如何实现 AppStore App 的自动下载
這次的分享是關(guān)于如何在 AppStore 實(shí)現(xiàn) App 的自動(dòng)下載,理想中的目標(biāo)是只需要一部手機(jī),不需要人來干預(yù),就可以模擬用戶的真實(shí)下載,并在下載完成以后,可以自動(dòng)更改手機(jī)參數(shù),使之變?yōu)榱硗庖徊刻O果手機(jī),進(jìn)行周而復(fù)始的下載工作。但是呢,本文的內(nèi)容只包含如何去模擬用戶的操作來完成下載,并不涉及抹機(jī)、IP 更換等內(nèi)容。
最終效果見:https://pan.baidu.com/play/video#/video?path=%2F自動(dòng)下載效果視頻.mp4&t=-1
為什么做這個(gè)呢?
可能會(huì)有人問,為什么要做這么一個(gè)項(xiàng)目。主要是兩點(diǎn)原因吧,第一點(diǎn)呢,是出于個(gè)人興趣,逆向其實(shí)在開發(fā)中的用處還是蠻大的,比如幫助我們分析 Apple 操作系統(tǒng),幫我們做好安全防御。通過這么一個(gè)項(xiàng)目的實(shí)踐,可以加深自己對(duì)逆向開發(fā)的理解,第二點(diǎn)呢,就是 App Search Optimization 是一個(gè)一直比較熱門的話題,有白帽子和黑帽子 ASO 之分,通過關(guān)鍵字和標(biāo)題優(yōu)化等手段來進(jìn)行 ASO 的屬于白帽子 ASO,而通過刷榜程序來進(jìn)行 ASO 的屬于黑帽子 ASO,ASO 的刷榜腳本是價(jià)值不菲的,可能價(jià)值幾十萬甚至幾百萬。通過這個(gè)項(xiàng)目也是小試牛刀,了解下灰產(chǎn)的一些技術(shù)手段。
什么是 ASO
ASO 的全稱是 App Search Optimization,就是提升你 APP 在 AppStore 排行榜和搜索結(jié)果排名的過程。我們經(jīng)常可以看到 AppStore 有一些奇怪的五星好評(píng),也會(huì)遇到搜索關(guān)鍵字,排名第一的是一個(gè)看上去完全不相關(guān)的 App。這些都是 ASO 優(yōu)化的手段,幫助提升產(chǎn)品的曝光量。
白帽子 ASO 常用的手段就是通過數(shù)據(jù)分析,來優(yōu)化關(guān)鍵詞、標(biāo)題等,進(jìn)而提高 App 的排名和曝光率。而黑帽子的手段則是,通過刷榜程序來實(shí)現(xiàn) App 的大量搜索、下載、好評(píng)這一系列的過程來提升 App 的排名。
常見的刷榜手段主要有兩種,一種是機(jī)刷,就是通過觸動(dòng)精靈或者代碼注入的方式來實(shí)現(xiàn)模擬用戶的真實(shí)操作,進(jìn)而完成搜索、下載、評(píng)論等操作。再一種協(xié)議刷,就是破解 AppStore 的登陸、下載相關(guān)的網(wǎng)絡(luò)協(xié)議,通過模擬真實(shí)的網(wǎng)絡(luò)請(qǐng)求來實(shí)現(xiàn)登陸、下載等行為。據(jù)說在刷榜過程中,蘋果會(huì)校驗(yàn)?zāi)愕?Apple ID、IP 等信息,所以需要購買大量的 Apple ID 和不斷更換 IP 地址。
如何實(shí)現(xiàn) App 的自動(dòng)下載
想要的效果:
大概實(shí)現(xiàn)步驟:
砸殼
我們的 App 上傳到 AppStore 后,蘋果會(huì)對(duì) App 進(jìn)行加密,要想去分析可執(zhí)行文件,就必須要進(jìn)行脫殼解密的操作,dumpdecrypted 是一款出色的脫殼工具,它的原理是將 App 運(yùn)行起來,App 啟動(dòng)時(shí),系統(tǒng)會(huì)對(duì) Mach-O 文件進(jìn)行加載,并完成對(duì)應(yīng)的解密操作,dumpdecrypted 就可以在此時(shí)將解密后的 Mach-O dump 出來,從而達(dá)到解密的效果。
如果為了省事可以直接從 PP 助手、iTools 上下載對(duì)應(yīng)的 App,一般情況下是已經(jīng)經(jīng)過砸殼的。同時(shí),對(duì)于 AppStore 這樣的系統(tǒng)程序有些特殊,他們 并不需要進(jìn)行砸殼,可以直接拿來進(jìn)行分析。
獲取頭文件
拿到一個(gè)砸殼后的可執(zhí)行文件后,就可以使用 class-dump 來獲取可執(zhí)行文件的所有頭文件,class-dump 會(huì)對(duì) Mach-O 的格式進(jìn)行分析,并將信息提取出來形成我們想要的頭文件。
AppStore 的可執(zhí)行文件也略有特殊,class dump之后會(huì)發(fā)現(xiàn) AppStore 中包含的代碼極少。App Store 的很多關(guān)鍵代碼邏輯都不在 AppStore 這個(gè)可執(zhí)行文件當(dāng)中,而是在系統(tǒng)的動(dòng)態(tài)庫中,我們需要分析動(dòng)態(tài)庫的頭文件信息進(jìn)而定位到關(guān)鍵函數(shù)。可以獲取對(duì)應(yīng)系統(tǒng)dyld_cache 中的動(dòng)態(tài)庫,然后 dump 出頭文件。AppStore UI 有關(guān)的邏輯都在 StoreKitUI 動(dòng)態(tài)庫中,這個(gè)動(dòng)態(tài)庫是分析的重點(diǎn)。
Reveal
Reveal 是一款 UI 調(diào)試工具,官方的定義是:See your iOS application's view hierarchy at runtime with advanced 2D and 3D visualisations,當(dāng)然對(duì)于逆向安全人員,查看自己 App 的布局是完全不夠的,我們可以在 Cydia 中下載 Reveal Loader,在同一網(wǎng)段下,通過 Mac 的 Reveal 和 iOS 上的 Reveal Loader 就可以查看任意 App 的 UI 布局。
但是,有時(shí)候我們不僅想要去看這個(gè) UI 布局,還想要去動(dòng)態(tài)調(diào)試這個(gè)布局,去看它的 Controller 是誰,去挖掘界面下的真正的代碼邏輯。這個(gè)就涉及到 Cycript 這個(gè)工具。
Cycript
Cycript 是由 Cydia 創(chuàng)始人 Saurik 推出的一款腳本語言,它混合了Objective-C 與 JavaScript 兩種語法,很容易上手,我們可以通過 Cycript 來進(jìn)行動(dòng)態(tài)調(diào)試,比如查看函數(shù)運(yùn)行的效果,尋找 View 的 Controller 等。
就拿上面 Reveal 詳情頁為例, Reveal 可以看到獲取按鈕是 SKUIOfferView,列表頁是一個(gè) SKUICollectionView ,那么就通過 Cycript 來看看控制這個(gè) SKUICollectionView 的 Controller 是誰。首先通過 OpenSSH 來連接 iPhone,通過 cycript -p AppStore 來對(duì) AppStore 進(jìn)行注入調(diào)試,UIApp.keyWindow.recursiveDescription().toString() 來打印視圖層級(jí)。(注:此截圖和后面的地址對(duì)不上,因?yàn)椴皇峭淮未蛴?#xff0c;大家了解下大概意思就成)
可以發(fā)現(xiàn) SKUICollectionView,并且它的內(nèi)存地址是 0x13fa00e00,可以通過 cycript 腳本來找到它的 Controller 是哪一個(gè),有多種方案,比如通過它的 delegate 來找,或者通過 nextResponder 來找都可以。
cy# [#0x13fa00e00 delegate] #"<SKUIStorePageSectionsViewController: 0x140167e00>"cy# [#0x13fa00e00 nextResponder] #"<UIView: 0x140f5f540; frame = (0 0; 320 568); autoresize = W+H; layer = <CALayer: 0x140f771c0>>" cy# [#0x140f5f540 nextResponder] #"<SKUIStorePageSectionsViewController: 0x140167e00>" 復(fù)制代碼同時(shí)也可以借助一些私有 API 來實(shí)現(xiàn)快速查找 ViewController,使用[[[UIWindow keyWindow] rootViewController] _printHierarchy].toString(),可以發(fā)現(xiàn)打印結(jié)果中同樣可以找到 SKUIStorePageSectionsViewController
cy# [[[UIWindow keyWindow] rootViewController] _printHierarchy].toString() `<SKUITabBarController 0x157815400>, state: appeared, view: <UILayoutContainerView 0x156db38e0>| <UINavigationController 0x15784d200>, state: disappeared, view: <UILayoutContainerView 0x156e6b240> not in the window| | <SKUIDocumentContainerViewController 0x1578d3c00>, state: disappeared, view: <UIView 0x1580e1aa0> not in the window| | | <SKUIStackDocumentViewController 0x15812b740>, state: disappeared, view: <UIView 0x1580dc870> not in the window| | | | <SKUIStorePageSectionsViewController 0x1578ec000>, state: disappeared, view: <UIView 0x1580f1a30> not in the window| | | | | <SKUIAccountButtonsViewController 0x158654180>, state: disappeared, view: <SKUIAccountButtonsView 0x158654f60> not in the window| <UINavigationController 0x157849c00>, state: disappeared, view: <UILayoutContainerView 0x156ec4df0> not in the window| <UINavigationController 0x157803600>, state: disappeared, view: <UILayoutContainerView 0x156e80de0> not in the window| <UINavigationController 0x15703ea00>, state: appeared, view: <UILayoutContainerView 0x156f114a0>| | <SKUIDocumentContainerViewController 0x157ab2a00>, state: disappeared, view: <UIView 0x158a25930> not in the window| | | <SKUIStackDocumentViewController 0x158a50690>, state: disappeared, view: <UIView 0x158a2b360> not in the window| | | | <SKUIStorePageSectionsViewController 0x1578e6000>, state: disappeared, view: <UIView 0x158a2d4b0> not in the window| | <SKUIDocumentContainerViewController 0x157b5fa00>, state: appeared, view: <UIView 0x158cf70e0>| | | <SKUIStackDocumentViewController 0x158cf6690>, state: appeared, view: <UIView 0x158cf72b0>| | | | <SKUIStorePageSectionsViewController 0x157b4ae00>, state: appeared, view: <UIView 0x158cfb1e0>| <UINavigationController 0x157028000>, state: disappeared, view: <UILayoutContainerView 0x156ef1300> not in the window| | <ASUpdatesViewController 0x156f169e0>, state: disappeared, view: <UIView 0x156dbd590> not in the window`復(fù)制代碼從上面的分析可以知道,SKUICollectionView 的控制器是 SKUIStorePageSectionsViewController,「獲取」按鈕的類是 SKUIOfferView,下一步是分析頭文件,看看有沒有可以比較明顯的方法可以為我們所用。下載是最關(guān)鍵的一步,那么首先來看看 SKUIOfferView 類的情況,它的頭文件大致如此。
#import <StoreKitUI/SKUIItemOfferButtonDelegate-Protocol.h> #import <StoreKitUI/SKUIViewElementView-Protocol.h>@class NSMapTable, NSMutableArray, NSString; @protocol SKUIOfferViewDelegate;@interface SKUIOfferView : SKUIViewReuseView <SKUIItemOfferButtonDelegate, SKUIViewElementView> {unsigned long long _alignment;NSMapTable *_buttonElements;NSMapTable *_buyButtonDescriptorToButton;struct UIEdgeInsets _contentInset; } - (void)_buttonAction:(id)arg1;- (void)itemOfferButtonWillAnimateTransition:(id)arg1; - (void)itemOfferButtonDidAnimateTransition:(id)arg1; - (struct CGSize)sizeThatFits:(struct CGSize)arg1; 復(fù)制代碼可以從頭文件中看到一個(gè) _buttonAction 方法,感覺上是 「獲取」按鈕點(diǎn)擊后的響應(yīng)方法,對(duì)于這種猜測(cè),可以使用 Cycript 來進(jìn)行調(diào)試,測(cè)試一下這個(gè)函數(shù)執(zhí)行的效果到底如何 在終端執(zhí)行 [#0x156c69cc0 _buttonAction:#0x156cb4d20] 后查看效果如下,App 已經(jīng)開始進(jìn)行下載了,說明這個(gè)方法的效果我們猜對(duì)了,在調(diào)試過程中,可以多多使用 Cycript 提高效率。
lldb
上面我們使用 Cycript 測(cè)試了 _buttonAction 的效果,但是這個(gè)方法有一個(gè)參數(shù),我們要搞清楚它正確的參數(shù)類型,傳入正確的值。這時(shí)候可以借助 LLDB ,來幫助我們找到這個(gè)參數(shù)的正確類型。 可以使用 b function 來針對(duì) _buttonAction 方法打斷點(diǎn),然后打印它的參數(shù)。
傳統(tǒng)的做法是使用LLDB 和 IDA 等工具找到 ASLR 和 基地址等信息,然后計(jì)算出符號(hào)的地址,這樣做起來比較繁瑣,還是可以繼續(xù)使用一些私有方法快速定位 _buttonAction 的符號(hào)地址來進(jìn)行斷點(diǎn)。
我們想要斷點(diǎn)的方法是 _buttonAction,它所在的類是 SKUIOfferView,那么可以使用 LLDB 輸入 po [SKUIOfferView _shortMethodDescription] 來看下效果:(更多強(qiáng)大的黑科技私有函數(shù)可以參考這里:http://iosre.com/t/powerful-private-methods-for-debugging-in-cycript-lldb/3414)
(lldb) po [SKUIOfferView _shortMethodDescription] <SKUIOfferView: 0x1a096ddd8>: in SKUIOfferView:Class Methods:+ (void) requestLayoutForViewElement:(id)arg1 width:(double)arg2 context:(id)arg3; (0x194719470)+ (CGSize) sizeThatFitsWidth:(double)arg1 viewElement:(id)arg2 context:(id)arg3; (0x1947197a8)Properties:@property (weak, nonatomic) <SKUIOfferViewDelegate>* delegate; (@synthesize delegate = _delegate;)@property (nonatomic) long metadataPosition; (@synthesize metadataPosition = _metadataPosition;)@property (readonly, nonatomic, getter=isShowingConfirmation) BOOL showingConfirmation; (@synthesize showingConfirmation = _isShowingConfirmation;)Instance Methods:- (BOOL) setImage:(id)arg1 forArtworkRequest:(id)arg2 context:(id)arg3; (0x19471a8c8)- (BOOL) updateWithItemState:(id)arg1 context:(id)arg2 animated:(BOOL)arg3; (0x19471a8d0)- (void) _buttonAction:(id)arg1; (0x19471bb5c)- (BOOL) _shouldHideNoticesWithBuyButtonDescriptor:(id)arg1 context:(id)arg2; (0x19471c368)- (void) _positionNoticeForItemOfferButton:(id)arg1; (0x19471c234) (SKUIViewReuseView ...)復(fù)制代碼可以看到 - (void) _buttonAction:(id)arg1; (0x19471bb5c),那么直接使用 b 0x19471bb5c為 _buttonAction 加斷點(diǎn)即可。斷點(diǎn)到以后,再打印它的參數(shù),對(duì)于 Objective-C 來說消息有兩個(gè)隱含參數(shù),也就是 self 和 _cmd,那么我們想要的參數(shù)就在第三個(gè)位置,可以通過 po $x2 來查看它的具體信息(ARM64 下函數(shù)的參數(shù)是存放在 X0 到 X7 這 8 個(gè)寄存器里面的,如果超過8個(gè)參數(shù),就會(huì)入棧)。
Process 7839 stopped * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1 2.1 3.1frame #0: 0x000000019471bb5c StoreKitUI`-[SKUIOfferView _buttonAction:] StoreKitUI`-[SKUIOfferView _buttonAction:]: -> 0x19471bb5c <+0>: stp x24, x23, [sp, #-0x40]!0x19471bb60 <+4>: stp x22, x21, [sp, #0x10]0x19471bb64 <+8>: stp x20, x19, [sp, #0x20]0x19471bb68 <+12>: stp x29, x30, [sp, #0x30] Target 0: (AppStore) stopped. (lldb) po $x0 <SKUIOfferView: 0x1596aae00; frame = (279 74; 26 26); layer = <CALayer: 0x1596676b0>>(lldb) po $x2 <SKUIItemOfferButton: 0x1596ab260; baseClass = UIControl; frame = (0 0; 26 26); clipsToBounds = YES; alpha = 0.2; tintColor = UIDeviceRGBColorSpace 0.0862745 0.0156863 0.0156863 1; animations = { opacity=<CABasicAnimation: 0x1592e7b20>; }; layer = <CALayer: 0x15967d9c0>> 復(fù)制代碼由上可知,參數(shù)類型是 SKUIItemOfferButton,也就是 SKUIOfferView 的 subView,其實(shí)點(diǎn)擊的是 SKUIItemOfferButton,只是 SKUIItemOfferButton 將處理往上拋而已。
Tweak 注入
Cydia 創(chuàng)始人 Saurik 同時(shí)為我們提供了一個(gè) Cydia Substrate 這么一個(gè)工具,官方的定義是:The powerful code modification platform behind Cydia。我們可以基于 Cydia Substrate 來開發(fā)具有各種功能的代碼注入程序。
Cydia Substrate 由 MobileHooker、MobileLoader、Safe mode 三個(gè)模塊組成。MobileHooker 主要用來替換函數(shù)的實(shí)現(xiàn),可以想象成 Runtime 的 Method Swizzle。MobileLoader 是用來加載第三方 dylib 的,我們寫的破解程序會(huì)在目標(biāo)程序啟動(dòng)時(shí)注入到目標(biāo)程序。Safe mode 就是安全模式,我們寫 tweak 的時(shí)候可能會(huì)造成 Crash,比如萬一造成 SpringBoard 無限 Crash 手機(jī)豈不是就沒法用了,所以提供了這么一個(gè)安全模式。
MobileHooker 提供了一些函數(shù)來讓我們完成 Hook 的工作,但是我們不直接使用 它們,我們使用基于他們封裝的 Logos 工具,Logos 的語法很簡單直觀,易于上手。比如 %hook 可以指定要 Hook 的類、%orig 可以執(zhí)行被鉤住的函數(shù)的原始實(shí)現(xiàn)、%new 給一個(gè)現(xiàn)成的 class 添加新函數(shù)(效果與 class_addMethod 類似)。
Tweak AppStore
那我們來使用 Logos 實(shí)現(xiàn)下載的功能,當(dāng)進(jìn)入 SKUIStorePageSectionsViewController 頁面后,找到下載按鈕,然后點(diǎn)擊下載,當(dāng)下載按鈕的文字由「獲取」變?yōu)椤复蜷_」,代表下載已完成,然后繼續(xù)執(zhí)行后續(xù)操作。
%hook SKUIStorePageSectionsViewController - (void)viewDidAppear:(BOOL)animated { %log;%orig;// 遍歷所有子 View,找到 offerButton 、offerView[self findAllSubviews:self.view];if (offerButton && offerView) {// 執(zhí)行下載操作[offerView _buttonAction:offerButton];// 每秒去 check 一下,是否下載完成downloadTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerAction) userInfo:nil repeats:YES];} } %new -(void)timerAction {if ([offerButton.title isEqualToString:@"打開"]) {// 發(fā)送下載完成的通知[[NSNotificationCenter defaultCenter] postNotificationName:@"textChangedAction" object:nil];downloadTimer = nil;} } %new -(void)findAllSubviews:(UIView *)view{for (UIView *subView in view.subviews) {if (subView.subviews.count) {[self findAllSubviews:subView];}if ([subView isKindOfClass:NSClassFromString(@"SKUIOfferView")]) {offerView = (SKUIOfferView*)subView;}if ([subView isKindOfClass:NSClassFromString(@"SKUIItemOfferButton")]) {offerButton = (SKUIItemOfferButton*)subView;}} } %end 復(fù)制代碼其他的操作,與上述其實(shí)很類似,比如搜索、跳轉(zhuǎn)都是利用靜態(tài)或者動(dòng)態(tài)分析找到關(guān)鍵函數(shù),通過 tweak 來實(shí)現(xiàn)想要的效果即可。其中還有一個(gè)較難的點(diǎn),就是彈窗提示我們登陸怎么辦?如何實(shí)現(xiàn)自動(dòng)登錄功能?
Tweak SpringBoard
首先,想到的就是在 AppStore App 中注入代碼,Hook UIAlertAction 和 UIAlertController 的代碼,會(huì)發(fā)現(xiàn)并沒有產(chǎn)生作用。AppStore 中的彈窗不是它來控制的,而是另外一個(gè)進(jìn)程 SpringBoard,所以要想實(shí)現(xiàn) Hook AppStore 的彈窗,必須對(duì) SpringBoard 進(jìn)行代碼注入。
我們正常如果要實(shí)現(xiàn)一個(gè)這種彈窗,代碼一般是這么寫
UIAlertController *actionSheet = [UIAlertController alertControllerWithTitle:@"標(biāo)題" message:@"注釋信息" preferredStyle:UIAlertControllerStyleActionSheet]; UIAlertAction *action1 = [UIAlertAction actionWithTitle:@"標(biāo)題1" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { NSLog(@"點(diǎn)擊了按鈕 1"); }]; UIAlertAction *action2 = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { NSLog(@"點(diǎn)擊了按鈕 2"); }]; [actionSheet addAction:action1]; [actionSheet addAction:action2]; [self presentViewController:actionSheet animated:YES completion:nil]; 復(fù)制代碼基于上面的代碼分析可得,我們要想實(shí)現(xiàn)自動(dòng)登錄,就要實(shí)現(xiàn)自動(dòng)點(diǎn)擊「使用現(xiàn)有的 Apple ID」執(zhí)行系統(tǒng)的原 action 操作,然后在賬號(hào)和密碼的 TextField 中填入賬號(hào)密碼,點(diǎn)擊「好」執(zhí)行系統(tǒng)的原始 action 操作。其實(shí)可以發(fā)現(xiàn),要執(zhí)行的 action 其實(shí)是在初始化 UIAlertAction 過程中,handler block 中加入的邏輯。那么我們就可以 Hook actionWithTitle:style:handler: 然后將 handler 保存下來,當(dāng)填寫好賬號(hào)密碼后,主動(dòng)觸發(fā) handler 即可。
上面那種方法也可以奏效,但是需要自己額外處理下 alertView 的出現(xiàn)和消失, 為了簡單可以直接嘗試第二種方法,在分析 UIKit 框架中 UIAlertController 類的頭文件時(shí)發(fā)現(xiàn) _dismissWithAction:這個(gè)方法,然后我就試了一下發(fā)現(xiàn)可以完成 dismiss 和 執(zhí)行 handler 兩項(xiàng)功能,所以我就直接使用了這個(gè) API 來模擬點(diǎn)擊。核心代碼如下:
typedef void(^CDUnknownBlockType)(UIAlertAction *action); CDUnknownBlockType testBlock; static UIAlertAction *keepAction; static int atimers;%hook UIAlertController - (void)viewDidAppear:(BOOL)animated {%log;%orig;if ([keepAction.title isEqualToString:@"使用現(xiàn)有的 Apple ID"]) {dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{((void ( *)(id, SEL, UIAlertAction*))objc_msgSend)(self, NSSelectorFromString(@"_dismissWithAction:"),keepAction);});} if ([keepAction.title isEqualToString:@"好"]) {dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{if (self.textFields.count > 1) {self.textFields.firstObject.text = @"joyme0104@163.com";self.textFields.lastObject.text = @"Joyme0304&&&";((void ( *)(id, SEL, UIAlertAction*))objc_msgSend)(self, NSSelectorFromString(@"_dismissWithAction:"),keepAction);}});} } %end%hook UIAlertAction + (id)_actionWithTitle:(id)arg1 descriptiveText:(id)arg2 image:(id)arg3 style:(long long)arg4 handler:(CDUnknownBlockType)arg5 shouldDismissHandler:(CDUnknownBlockType)arg6 {id obj = %orig;UIAlertAction *action = (UIAlertAction *)obj;if ([action.title isEqualToString:@"使用現(xiàn)有的 Apple ID"]) {testBlock = arg6;keepAction = obj;} if ([action.title isEqualToString:@"好"]) {testBlock = arg6;keepAction = obj;}return obj; } %end 復(fù)制代碼從代碼可以看出我們?cè)?Hook UIAlertAction 的 _actionWithTitle 方法時(shí),并沒有 Hook actionWithTitle:style:handler: ,因?yàn)槲覝y(cè)試的時(shí)候發(fā)現(xiàn)在我操作過程中并沒有觸發(fā),懷疑是蘋果沒有使用這個(gè) API,直接使用了下面這個(gè)方法。
+ (id)_actionWithTitle:(id)arg1 descriptiveText:(id)arg2 image:(id)arg3 style:(long long)arg4 handler:(CDUnknownBlockType)arg5 shouldDismissHandler:(CDUnknownBlockType)arg6 { } 復(fù)制代碼Thinking About The Future
適當(dāng)增加對(duì) App 安全的精力的投入,像現(xiàn)在業(yè)界的很多 App 都處于被破解的狀態(tài),網(wǎng)上隨處可見各種 App 的破解版,比如愛奇藝會(huì)員破解、釘釘遠(yuǎn)程打卡等。從客戶端角度出發(fā),需要增加代碼混淆、反調(diào)試等手段保證運(yùn)行環(huán)境的安全,同時(shí)與后端人員合作增加保證網(wǎng)絡(luò)數(shù)據(jù)鏈路、反作弊的手段。
Summary
本文首先介紹了常見的攻擊手段:
然后介紹了 ASO 的影響因素都有哪些,以及黑帽子和白帽子都是怎么進(jìn)行 ASO 優(yōu)化的。最后重點(diǎn)寫了如何一步步通過代碼注入,實(shí)現(xiàn) AppStore App 的自動(dòng)登錄。
總結(jié)
以上是生活随笔為你收集整理的如何实现 AppStore App 的自动下载的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 腾讯云服务器和阿里云服务器新客选谁
- 下一篇: Chrome如何安装第三方扩展插件(cr