日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

如何实现 AppStore App 的自动下载

發布時間:2024/1/8 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 如何实现 AppStore App 的自动下载 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

這次的分享是關于如何在 AppStore 實現 App 的自動下載,理想中的目標是只需要一部手機,不需要人來干預,就可以模擬用戶的真實下載,并在下載完成以后,可以自動更改手機參數,使之變為另外一部蘋果手機,進行周而復始的下載工作。但是呢,本文的內容只包含如何去模擬用戶的操作來完成下載,并不涉及抹機、IP 更換等內容。

最終效果見:https://pan.baidu.com/play/video#/video?path=%2F自動下載效果視頻.mp4&t=-1

為什么做這個呢?

可能會有人問,為什么要做這么一個項目。主要是兩點原因吧,第一點呢,是出于個人興趣,逆向其實在開發中的用處還是蠻大的,比如幫助我們分析 Apple 操作系統,幫我們做好安全防御。通過這么一個項目的實踐,可以加深自己對逆向開發的理解,第二點呢,就是 App Search Optimization 是一個一直比較熱門的話題,有白帽子和黑帽子 ASO 之分,通過關鍵字和標題優化等手段來進行 ASO 的屬于白帽子 ASO,而通過刷榜程序來進行 ASO 的屬于黑帽子 ASO,ASO 的刷榜腳本是價值不菲的,可能價值幾十萬甚至幾百萬。通過這個項目也是小試牛刀,了解下灰產的一些技術手段。

什么是 ASO

ASO 的全稱是 App Search Optimization,就是提升你 APP 在 AppStore 排行榜和搜索結果排名的過程。我們經常可以看到 AppStore 有一些奇怪的五星好評,也會遇到搜索關鍵字,排名第一的是一個看上去完全不相關的 App。這些都是 ASO 優化的手段,幫助提升產品的曝光量。

白帽子 ASO 常用的手段就是通過數據分析,來優化關鍵詞、標題等,進而提高 App 的排名和曝光率。而黑帽子的手段則是,通過刷榜程序來實現 App 的大量搜索、下載、好評這一系列的過程來提升 App 的排名。

常見的刷榜手段主要有兩種,一種是機刷,就是通過觸動精靈或者代碼注入的方式來實現模擬用戶的真實操作,進而完成搜索、下載、評論等操作。再一種協議刷,就是破解 AppStore 的登陸、下載相關的網絡協議,通過模擬真實的網絡請求來實現登陸、下載等行為。據說在刷榜過程中,蘋果會校驗你的 Apple ID、IP 等信息,所以需要購買大量的 Apple ID 和不斷更換 IP 地址。

如何實現 App 的自動下載

想要的效果:

  • 進入 AppStore,切換 tab 到搜索界面
  • 設置搜索關鍵字、搜索
  • 進入列表頁后,點擊 App 進入詳情頁點擊下載
  • 根據提示完成登陸、下載,并在下載完成以后跳轉到推薦 Tab
  • 進入推薦 Tab 后,退出登陸
  • 大概實現步驟:

  • 準備越獄手機和 Mac 電腦
  • 砸殼 dumpdecrypted,通常 PP助手、iTools 下載的 App 是經過砸殼的,同時 AppStore App 不需要砸殼
  • 頭文件獲取:AppStore class-dump,系統庫的頭文件的獲取:dyld_cache class-dump
  • 定位關鍵函數:Reveal、Cycript、lldb
  • tweak 的注入
  • 砸殼

    我們的 App 上傳到 AppStore 后,蘋果會對 App 進行加密,要想去分析可執行文件,就必須要進行脫殼解密的操作,dumpdecrypted 是一款出色的脫殼工具,它的原理是將 App 運行起來,App 啟動時,系統會對 Mach-O 文件進行加載,并完成對應的解密操作,dumpdecrypted 就可以在此時將解密后的 Mach-O dump 出來,從而達到解密的效果。

    如果為了省事可以直接從 PP 助手、iTools 上下載對應的 App,一般情況下是已經經過砸殼的。同時,對于 AppStore 這樣的系統程序有些特殊,他們 并不需要進行砸殼,可以直接拿來進行分析。

    獲取頭文件

    拿到一個砸殼后的可執行文件后,就可以使用 class-dump 來獲取可執行文件的所有頭文件,class-dump 會對 Mach-O 的格式進行分析,并將信息提取出來形成我們想要的頭文件。

    AppStore 的可執行文件也略有特殊,class dump之后會發現 AppStore 中包含的代碼極少。App Store 的很多關鍵代碼邏輯都不在 AppStore 這個可執行文件當中,而是在系統的動態庫中,我們需要分析動態庫的頭文件信息進而定位到關鍵函數。可以獲取對應系統dyld_cache 中的動態庫,然后 dump 出頭文件。AppStore UI 有關的邏輯都在 StoreKitUI 動態庫中,這個動態庫是分析的重點。

    Reveal

    Reveal 是一款 UI 調試工具,官方的定義是:See your iOS application's view hierarchy at runtime with advanced 2D and 3D visualisations,當然對于逆向安全人員,查看自己 App 的布局是完全不夠的,我們可以在 Cydia 中下載 Reveal Loader,在同一網段下,通過 Mac 的 Reveal 和 iOS 上的 Reveal Loader 就可以查看任意 App 的 UI 布局。

    但是,有時候我們不僅想要去看這個 UI 布局,還想要去動態調試這個布局,去看它的 Controller 是誰,去挖掘界面下的真正的代碼邏輯。這個就涉及到 Cycript 這個工具。

    Cycript

    Cycript 是由 Cydia 創始人 Saurik 推出的一款腳本語言,它混合了Objective-C 與 JavaScript 兩種語法,很容易上手,我們可以通過 Cycript 來進行動態調試,比如查看函數運行的效果,尋找 View 的 Controller 等。

    就拿上面 Reveal 詳情頁為例, Reveal 可以看到獲取按鈕是 SKUIOfferView,列表頁是一個 SKUICollectionView ,那么就通過 Cycript 來看看控制這個 SKUICollectionView 的 Controller 是誰。首先通過 OpenSSH 來連接 iPhone,通過 cycript -p AppStore 來對 AppStore 進行注入調試,UIApp.keyWindow.recursiveDescription().toString() 來打印視圖層級。(注:此截圖和后面的地址對不上,因為不是同一次打印,大家了解下大概意思就成)

    可以發現 SKUICollectionView,并且它的內存地址是 0x13fa00e00,可以通過 cycript 腳本來找到它的 Controller 是哪一個,有多種方案,比如通過它的 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>" 復制代碼

    同時也可以借助一些私有 API 來實現快速查找 ViewController,使用[[[UIWindow keyWindow] rootViewController] _printHierarchy].toString(),可以發現打印結果中同樣可以找到 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`復制代碼

    從上面的分析可以知道,SKUICollectionView 的控制器是 SKUIStorePageSectionsViewController,「獲取」按鈕的類是 SKUIOfferView,下一步是分析頭文件,看看有沒有可以比較明顯的方法可以為我們所用。下載是最關鍵的一步,那么首先來看看 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; 復制代碼

    可以從頭文件中看到一個 _buttonAction 方法,感覺上是 「獲取」按鈕點擊后的響應方法,對于這種猜測,可以使用 Cycript 來進行調試,測試一下這個函數執行的效果到底如何 在終端執行 [#0x156c69cc0 _buttonAction:#0x156cb4d20] 后查看效果如下,App 已經開始進行下載了,說明這個方法的效果我們猜對了,在調試過程中,可以多多使用 Cycript 提高效率。

    lldb

    上面我們使用 Cycript 測試了 _buttonAction 的效果,但是這個方法有一個參數,我們要搞清楚它正確的參數類型,傳入正確的值。這時候可以借助 LLDB ,來幫助我們找到這個參數的正確類型。 可以使用 b function 來針對 _buttonAction 方法打斷點,然后打印它的參數。

    傳統的做法是使用LLDB 和 IDA 等工具找到 ASLR 和 基地址等信息,然后計算出符號的地址,這樣做起來比較繁瑣,還是可以繼續使用一些私有方法快速定位 _buttonAction 的符號地址來進行斷點。

    我們想要斷點的方法是 _buttonAction,它所在的類是 SKUIOfferView,那么可以使用 LLDB 輸入 po [SKUIOfferView _shortMethodDescription] 來看下效果:(更多強大的黑科技私有函數可以參考這里: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 ...)復制代碼

    可以看到 - (void) _buttonAction:(id)arg1; (0x19471bb5c),那么直接使用 b 0x19471bb5c為 _buttonAction 加斷點即可。斷點到以后,再打印它的參數,對于 Objective-C 來說消息有兩個隱含參數,也就是 self 和 _cmd,那么我們想要的參數就在第三個位置,可以通過 po $x2 來查看它的具體信息(ARM64 下函數的參數是存放在 X0 到 X7 這 8 個寄存器里面的,如果超過8個參數,就會入棧)。

    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>> 復制代碼

    由上可知,參數類型是 SKUIItemOfferButton,也就是 SKUIOfferView 的 subView,其實點擊的是 SKUIItemOfferButton,只是 SKUIItemOfferButton 將處理往上拋而已。

    Tweak 注入

    Cydia 創始人 Saurik 同時為我們提供了一個 Cydia Substrate 這么一個工具,官方的定義是:The powerful code modification platform behind Cydia。我們可以基于 Cydia Substrate 來開發具有各種功能的代碼注入程序。

    Cydia Substrate 由 MobileHooker、MobileLoader、Safe mode 三個模塊組成。MobileHooker 主要用來替換函數的實現,可以想象成 Runtime 的 Method Swizzle。MobileLoader 是用來加載第三方 dylib 的,我們寫的破解程序會在目標程序啟動時注入到目標程序。Safe mode 就是安全模式,我們寫 tweak 的時候可能會造成 Crash,比如萬一造成 SpringBoard 無限 Crash 手機豈不是就沒法用了,所以提供了這么一個安全模式。

    MobileHooker 提供了一些函數來讓我們完成 Hook 的工作,但是我們不直接使用 它們,我們使用基于他們封裝的 Logos 工具,Logos 的語法很簡單直觀,易于上手。比如 %hook 可以指定要 Hook 的類、%orig 可以執行被鉤住的函數的原始實現、%new 給一個現成的 class 添加新函數(效果與 class_addMethod 類似)。

    Tweak AppStore

    那我們來使用 Logos 實現下載的功能,當進入 SKUIStorePageSectionsViewController 頁面后,找到下載按鈕,然后點擊下載,當下載按鈕的文字由「獲取」變為「打開」,代表下載已完成,然后繼續執行后續操作。

    %hook SKUIStorePageSectionsViewController - (void)viewDidAppear:(BOOL)animated { %log;%orig;// 遍歷所有子 View,找到 offerButton 、offerView[self findAllSubviews:self.view];if (offerButton && offerView) {// 執行下載操作[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:@"打開"]) {// 發送下載完成的通知[[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 復制代碼

    其他的操作,與上述其實很類似,比如搜索、跳轉都是利用靜態或者動態分析找到關鍵函數,通過 tweak 來實現想要的效果即可。其中還有一個較難的點,就是彈窗提示我們登陸怎么辦?如何實現自動登錄功能?

    Tweak SpringBoard

    首先,想到的就是在 AppStore App 中注入代碼,Hook UIAlertAction 和 UIAlertController 的代碼,會發現并沒有產生作用。AppStore 中的彈窗不是它來控制的,而是另外一個進程 SpringBoard,所以要想實現 Hook AppStore 的彈窗,必須對 SpringBoard 進行代碼注入。

    我們正常如果要實現一個這種彈窗,代碼一般是這么寫

    UIAlertController *actionSheet = [UIAlertController alertControllerWithTitle:@"標題" message:@"注釋信息" preferredStyle:UIAlertControllerStyleActionSheet]; UIAlertAction *action1 = [UIAlertAction actionWithTitle:@"標題1" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { NSLog(@"點擊了按鈕 1"); }]; UIAlertAction *action2 = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { NSLog(@"點擊了按鈕 2"); }]; [actionSheet addAction:action1]; [actionSheet addAction:action2]; [self presentViewController:actionSheet animated:YES completion:nil]; 復制代碼

    基于上面的代碼分析可得,我們要想實現自動登錄,就要實現自動點擊「使用現有的 Apple ID」執行系統的原 action 操作,然后在賬號和密碼的 TextField 中填入賬號密碼,點擊「好」執行系統的原始 action 操作。其實可以發現,要執行的 action 其實是在初始化 UIAlertAction 過程中,handler block 中加入的邏輯。那么我們就可以 Hook actionWithTitle:style:handler: 然后將 handler 保存下來,當填寫好賬號密碼后,主動觸發 handler 即可。

    上面那種方法也可以奏效,但是需要自己額外處理下 alertView 的出現和消失, 為了簡單可以直接嘗試第二種方法,在分析 UIKit 框架中 UIAlertController 類的頭文件時發現 _dismissWithAction:這個方法,然后我就試了一下發現可以完成 dismiss 和 執行 handler 兩項功能,所以我就直接使用了這個 API 來模擬點擊。核心代碼如下:

    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:@"使用現有的 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:@"使用現有的 Apple ID"]) {testBlock = arg6;keepAction = obj;} if ([action.title isEqualToString:@"好"]) {testBlock = arg6;keepAction = obj;}return obj; } %end 復制代碼

    從代碼可以看出我們在 Hook UIAlertAction 的 _actionWithTitle 方法時,并沒有 Hook actionWithTitle:style:handler: ,因為我測試的時候發現在我操作過程中并沒有觸發,懷疑是蘋果沒有使用這個 API,直接使用了下面這個方法。

    + (id)_actionWithTitle:(id)arg1 descriptiveText:(id)arg2 image:(id)arg3 style:(long long)arg4 handler:(CDUnknownBlockType)arg5 shouldDismissHandler:(CDUnknownBlockType)arg6 { } 復制代碼

    Thinking About The Future

    適當增加對 App 安全的精力的投入,像現在業界的很多 App 都處于被破解的狀態,網上隨處可見各種 App 的破解版,比如愛奇藝會員破解、釘釘遠程打卡等。從客戶端角度出發,需要增加代碼混淆、反調試等手段保證運行環境的安全,同時與后端人員合作增加保證網絡數據鏈路、反作弊的手段。

    Summary

    本文首先介紹了常見的攻擊手段:

  • 通過靜態分析和動態分析掌握 App 的內部邏輯,通過代碼注入實現我們想要的功能,比如自動下載、自動跳轉等功能
  • 通過分析 App 的網絡請求,破解網絡協議,模擬真實的網絡請求來達到某種目的,比如批量下載,批量評論等功能。
  • 然后介紹了 ASO 的影響因素都有哪些,以及黑帽子和白帽子都是怎么進行 ASO 優化的。最后重點寫了如何一步步通過代碼注入,實現 AppStore App 的自動登錄。

    總結

    以上是生活随笔為你收集整理的如何实现 AppStore App 的自动下载的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。