逆向分析及修復稀土掘金iOS版客戶端閃退bug

感受是要搞個《逆向分析及修復app》系列的節奏啊javascript

故事概要

你們好,我又來了。上次給你們分享了《逆向及修復最新iOS版少數派客戶端的閃退bug》,@了一些iOS界的大神,沒想到受到了你們的轉發和關注,還真有點小激動。同時也在微博的評論下收到了稀土掘金的歡迎,還給我開了專欄權限,但願能夠到稀土掘金上分享寫做。備感榮幸的同時,忽然發現,一直在稀土掘金上面看文章的我,一直沒有註冊帳戶(好像註冊了可是忘了密碼,在1Password中也沒有記錄,看來是好久以前登錄過了)。這也是此次故事的開始,收到稀土掘金的評論後,立刻打開了早已不知道何時下載的 掘金 app。果真,沒有登錄記錄,做爲開發者立刻用 GitHub 受權進行登錄,尷尬的事情發生了,在受權成功後,app閃退了。好尷尬啊,好吧稀土掘金上的處女座就獻給你了。java

跟以前寫《逆向及修復最新iOS版少數派客戶端的閃退bug》文章時不一樣,在文章寫到這裏的時候,我其實尚未徹底分析完崩潰的緣由。我是分析途中決定開始寫這篇文章的,決定一邊分析一遍寫,這樣纔會儘量的保留分析的全過程,固然最後能不能分析到關鍵點,且看下去吧,由於我也不知道。ios

問題描述

  • 最新版 4.1.3 稀土掘金 app 在使用 github 登錄成功後會 crash
  • 是在登錄成功後才 crash,由於再次進入後會發現已經登錄成功
  • 可能不是全部人都會遇到這個bug,根據後來的分析,多是Github的某個我的信息中缺乏某些設置致使的受權後crash

着手分析

###找到崩潰緣由
分析一個bug的開始,固然是從崩潰緣由找起,前一篇文章,咱們使用了lldb調試,在觸發 app 崩潰的時候,從 Terminal 找到了關鍵詞:EXE_BAD_ACCESS。此次,咱們使用最簡單的,查看系統日誌來定位崩潰緣由,使用最新的 Mac,打開控制檯,鏈接手機並清空多餘的系統日誌打印,而後觸發崩潰,觀察控制檯中輸出的內容(若是輸出日誌過多,可使用進程名Xitu做爲關鍵詞,進行過濾)。若是能夠仍是不要使用關鍵詞過濾,由於不是全部的崩潰都是因爲 app 自己的問題致使的,正如簽名破壞的 app 在沒有越獄的手機上會安裝失敗,這個安裝失敗的緣由查找就須要在日誌中的 SpringBoard 進程打印中進行查找
SpringBoardApple用來管理咱們iPhone應用的一個應用,能夠理解爲咱們 PC 端的桌面,它還負責應用的桌面排列、管理通知中心等)。git

正如咱們但願的同樣,崩潰的緣由最多見不過了:
github

這個崩潰就頗有意思了,開發者對NSNull調用了length方法,由於找不到該方法而崩潰!固然開發者確定是不會主動對NSNull類型調用length方法,猜測:swift

  • 開發不知道對象會變成NSNull類型,說明這個對象多是在運行時肯定的
  • 調用length,說明開發者認爲這個類應該是屬於NSString類型的

大膽猜想:結合這個崩潰是發生在登錄的時候,須要從網絡獲取登錄信息,那麼會不會是程序在對獲取到的登錄信息進行處理的時候致使的崩潰(好比登錄用戶的一些信息沒有設置,因此程序從登錄信息裏取到的是空值)。網絡

分析入口

1.找到登錄界面的控制類


分析從該登錄界面出發,使用cycript注入app後,打印找到該界面的控制器:

susnms-iPhone:~ root# ps -e | grep Xitu
24240 ??         0:00.00 (Xitu.Widget)
24241 ??         0:00.00 (XituShare)
24242 ??         0:00.00 (Xitu)
24283 ??         0:02.03 /var/containers/Bundle/Application/994C4217-88DD-4F55-A016-55BDD5998C49/Xitu.app/Xitu
24288 ttys000    0:00.01 grep Xitu
susnms-iPhone:~ root# cycript -p 24283 common.cy ; cycript -p 24283
cy# currentVCWithKeyWindow ()
#"<XTGithubLoginViewController: 0x15e0ef340>"
cy#複製代碼

currentVCWithKeyWindow() 來自我本身寫的一個腳本,你能夠前往common.cy下載。下載以後,將其放到/var/root目錄下便可。
使用時,只須要在cycript注入的時候加上便可。
其中還有一些好用的函數,好比快速打印UIControl全部的targetactionapp

2.查看登錄界面的調用流程

使用class-dump等工具,導出app的全部類頭文件,使用logify.pl工具生成XTGithubLoginViewController類的全部hook函數,使用theos編寫安裝插件後,觸發崩潰,而後在控制檯查找該類的函數調用邏輯。以下:
函數

能夠發現,程序是在-[XTGithubLoginViewController viewWillDisappear:]調用以後才收到的崩潰的通知。因此頗有可能崩潰的緣由不是XTGithubLoginViewController致使的,而是在XTGithubLoginViewController類返回登錄信息後。工具

仔細研究XTGithubLoginViewController的調用過程,發現了幾個有意思的方法,調用順序以下:

  • -(void)setCallBack:
  • -(id)onAuthCompleted:
  • -(id)callBack

callback!太熟悉不過了,這不就是咱們常常在開發中使用的設置回調block的時候使用的參數名嗎!!而且,該callback在類初始化的時候被設置,類方法-(id)onAuthCompleted:調用以後才被回調。結合這個方法名onAuthCompleted,這個邏輯是否是很像:咱們在登錄成功以後,使用block傳回了咱們的登錄信息進行處理。

分析到這裏,其實咱們已經可使用lldb調試,打印該block的內存地址,而後去掉內存地址偏移後,在Hopper中查看block的實現,看是否該block致使的崩潰。可是做爲逆向的學習,咱們能夠先繼續深刻,查看一下這個block的傳遞調用過程。

繼續深刻callback的回調過程

既然該回調是在XTGithubLoginViewController被初始化的時候被設置的,那麼咱們先找到是誰初始化的該Github登錄控制器:

使用cycript注入打印該控制器類名,並嘗試github的受權登陸入口:

susnms-iPhone:~ root# cycript -p Xitu common.cy ; cycript -p Xitu
cy# currentVCWithKeyWindow ()
#"<XTLoginViewController: 0x12e43f740>"
cy# [#0x12e43f740 github
githubBt      githubLogin:
cy# [#0x12e43f740 githubLogin: nil]複製代碼

發現成功觸發登陸,因此能夠肯定githubLogin:方法確實是登陸的入口

1.githubLogin:實現

Hopper中查看該方法的實現:

-[XTLoginViewController githubLogin:]:
0000000100194c24         sub        sp, sp, #0x50                               ; Objective C Implementation defined at 0x1009d2708 (instance method), DATA XREF=0x1009d2708
0000000100194c28         stp        x20, x19, [sp, #0x30]
0000000100194c2c         stp        x29, x30, [sp, #0x40]
0000000100194c30         add        x29, sp, #0x40
0000000100194c34         mov        x19, x0
0000000100194c38         adrp       x8, #0x100ab4000                            ; @selector(detectSocketStatus)
0000000100194c3c         ldr        x0, [x8, #0x878]                            ; objc_cls_ref_LoginCloud,__objc_class_LoginCloud_class
0000000100194c40         adrp       x8, #0x100aa0000                            ; @selector(computeTime:)
0000000100194c44         ldr        x1, [x8, #0xd0]                             ; "singleClass",@selector(singleClass)
0000000100194c48         bl         imp___stubs__objc_msgSend                   ; login = [LoginCloud singleClass]
0000000100194c4c         mov        x29, x29
0000000100194c50         bl         imp___stubs__objc_retainAutoreleasedReturnValue
0000000100194c54         mov        x20, x0
0000000100194c58         adrp       x8, #0x100914000
0000000100194c5c         ldr        x8, [x8, #0x448]                            ; __NSConcreteStackBlock_100914448,__NSConcreteStackBlock
0000000100194c60         str        x8, [sp, #0x8]
0000000100194c64         movz       w8, #0xc200
0000000100194c68         stp        w8, wzr, [sp, #0x10]
0000000100194c6c         adr        x8, #0x100194cc4
0000000100194c70         nop
0000000100194c74         str        x8, [sp, #0x18]
0000000100194c78         adrp       x8, #0x100923000
0000000100194c7c         add        x8, x8, #0x9d0                              ; 0x1009239d0
0000000100194c80         str        x8, [sp, #0x20]
0000000100194c84         mov        x0, x19
0000000100194c88         bl         imp___stubs__objc_retain
0000000100194c8c         str        x0, [sp, #0x28]
0000000100194c90         adrp       x8, #0x100aa7000                            ; @selector(isTableViewScrolledToBottom)
0000000100194c94         ldr        x1, [x8, #0x848]                            ; "githubLoginCallback:",@selector(githubLoginCallback:)
0000000100194c98         add        x2, sp, #0x8
0000000100194c9c         mov        x0, x20
0000000100194ca0         bl         imp___stubs__objc_msgSend                   ; [login githubLoginCallback: block]
0000000100194ca4         mov        x0, x20
0000000100194ca8         bl         imp___stubs__objc_release
0000000100194cac         ldr        x0, [sp, #0x28]
0000000100194cb0         bl         imp___stubs__objc_release
0000000100194cb4         ldp        x29, x30, [sp, #0x40]
0000000100194cb8         ldp        x20, x19, [sp, #0x30]
0000000100194cbc         add        sp, sp, #0x50
0000000100194cc0         ret複製代碼

內容很簡單,調用了單例類,並傳入回調callback參數:

LoginCloud *login = [LoginCloud singleClass];
[login githubLoginCallBack: callback];複製代碼

2.[LoginCloud singleClass]實現

Hopper查看以下:

-[LoginCloud githubLoginCallback:]:
sub        sp, sp, #0x80 ; Objective C Implementation defined at 0x1009c7cc8 (instance method), DATA XREF=0x1009c7cc8
stp        x26, x25, [sp, #0x30]
stp        x24, x23, [sp, #0x40]
stp        x22, x21, [sp, #0x50]
stp        x20, x19, [sp, #0x60]
stp        x29, x30, [sp, #0x70]
add        x29, sp, #0x70
mov        x22, x0
mov        x0, x2
bl         imp___stubs__objc_retain
mov        x21, x0
adrp       x8, #0x100ab4000 ; @selector(detectSocketStatus)
ldr        x0, [x8, #0x6a0] ; objc_cls_ref_UIStoryboard,_OBJC_CLASS_$_UIStoryboard
adrp       x8, #0x100aa0000 ; @selector(computeTime:)
ldr        x1, [x8, #0x788] ; "storyboardWithName:bundle:",@selector(storyboardWithName:bundle:)
adrp       x2, #0x100949000 ; @"%@ %@ %@"
add        x2, x2, #0x500 ; @"Login"
movz       x3, #0x0
bl         imp___stubs__objc_msgSend ; loginSb = [UIStoryboard storyboardWithName: @"Login" bundle: nil]
mov        x29, x29
bl         imp___stubs__objc_retainAutoreleasedReturnValue
mov        x19, x0
adrp       x8, #0x100aa0000 ; @selector(computeTime:)
ldr        x1, [x8, #0x790] ; "instantiateViewControllerWithIdentifier:",@selector(instantiateViewControllerWithIdentifier:)
adrp       x2, #0x10094f000 ; @"nickname"
add        x2, x2, #0xd00 ; @"githubVC"
bl         imp___stubs__objc_msgSend ; githubVC = [loginSb instantiateViewControllerWithIdentifier: @"githubVC"]
mov        x29, x29
bl         imp___stubs__objc_retainAutoreleasedReturnValue
mov        x20, x0
adrp       x8, #0x100914000
ldr        x8, [x8, #0x448] ; __NSConcreteStackBlock_100914448,__NSConcreteStackBlock
str        x8, sp
movz       w8, #0xc200
stp        w8, wzr, [sp, #0x8]
adr        x8, #0x10015ccbc
nop
str        x8, [sp, #0x10]
adrp       x8, #0x100921000
add        x8, x8, #0x6f0 ; 0x1009216f0
stp        x8, x21, [sp, #0x18]
mov        x0, x21
bl         imp___stubs__objc_retain
mov        x21, x0
mov        x0, x22
bl         imp___stubs__objc_retain
str        x0, [sp, #0x28]
adrp       x8, #0x100aa6000 ; @selector(hasText)
ldr        x1, [x8, #0x440] ; "setCallBack:",@selector(setCallBack:)
mov        x2, sp
mov        x0, x20      ; [githubVC setCallBack: black]
bl         imp___stubs__objc_msgSend
adrp       x8, #0x100ab4000 ; @selector(detectSocketStatus)
ldr        x0, [x8, #0x938] ; objc_cls_ref_UINavigationController,_OBJC_CLASS_$_UINavigationController
adrp       x8, #0x100a9f000
ldr        x1, [x8, #0xd78] ; "alloc",@selector(alloc)
bl         imp___stubs__objc_msgSend
adrp       x8, #0x100aa1000 ; @selector(setUpdatedAt:)
ldr        x1, [x8, #0x948] ; "initWithRootViewController:",@selector(initWithRootViewController:)
mov        x2, x20
bl         imp___stubs__objc_msgSend
mov        x22, x0
adrp       x8, #0x100ab4000 ; @selector(detectSocketStatus)
ldr        x0, [x8, #0x820] ; objc_cls_ref_UIApplication,_OBJC_CLASS_$_UIApplication
adrp       x8, #0x100aa1000 ; @selector(setUpdatedAt:)
ldr        x1, [x8, #0x2d8] ; "sharedApplication",@selector(sharedApplication)
bl         imp___stubs__objc_msgSend
mov        x29, x29
bl         imp___stubs__objc_retainAutoreleasedReturnValue
mov        x23, x0
adrp       x8, #0x100a9f000
ldr        x1, [x8, #0xf88] ; "delegate",@selector(delegate)
bl         imp___stubs__objc_msgSend
mov        x29, x29
bl         imp___stubs__objc_retainAutoreleasedReturnValue
mov        x24, x0
adrp       x8, #0x100aa1000 ; @selector(setUpdatedAt:)
ldr        x1, [x8, #0x930] ; "window",@selector(window)
bl         imp___stubs__objc_msgSend
mov        x29, x29
bl         imp___stubs__objc_retainAutoreleasedReturnValue
mov        x25, x0
adrp       x8, #0x100aa1000 ; @selector(setUpdatedAt:)
ldr        x1, [x8, #0x938] ; "rootViewController",@selector(rootViewController)
bl         imp___stubs__objc_msgSend
mov        x29, x29
bl         imp___stubs__objc_retainAutoreleasedReturnValue
mov        x26, x0
mov        x0, x25
bl         imp___stubs__objc_release
mov        x0, x24
bl         imp___stubs__objc_release
mov        x0, x23
bl         imp___stubs__objc_release
adrp       x8, #0x100aa0000 ; @selector(computeTime:)
ldr        x1, [x8, #0x220] ; "presentViewController:animated:completion:",@selector(presentViewController:animated:completion:)
orr        w3, wzr, #0x1
mov        x0, x26
mov        x2, x22
movz       x4, #0x0
bl         imp___stubs__objc_msgSend
mov        x0, x26
bl         imp___stubs__objc_release
mov        x0, x22
bl         imp___stubs__objc_release
ldr        x0, [sp, #0x28]
bl         imp___stubs__objc_release
ldr        x0, [sp, #0x20]
bl         imp___stubs__objc_release
mov        x0, x21
bl         imp___stubs__objc_release
mov        x0, x20
bl         imp___stubs__objc_release
mov        x0, x19
bl         imp___stubs__objc_release
ldp        x29, x30, [sp, #0x70]
ldp        x20, x19, [sp, #0x60]
ldp        x22, x21, [sp, #0x50]
ldp        x24, x23, [sp, #0x40]
ldp        x26, x25, [sp, #0x30]
add        sp, sp, #0x80
ret複製代碼

解釋以下:

UIStoryboard *loginSb = [UIStoryboard storyboardWithName: @"Login" bundle: nil]
UIViewController *githubVC = [loginSb instantiateViewControllerWithIdentifier: @"githubVC"]
[githubVC setCallBack: black]
UINavigatinController *nav = [[UINavigatinController alloc] initWithRootViewControler: githubVC];
UIViewController *vc = [UIApplication sharedApplication].delegate.window.rootViewController;
[vc presentViewController: nav animaed: YES completion: nil];複製代碼
  • 初始化UIStoryBoard,並從中初始化控制器githubVC
  • githubVC控制器設置回調block(另外一個callback參數回調)
  • 顯示控制器

其實這裏的githubVC應該就是XTGithubLoginViewController類型,咱們可使用cycript確認一下:

cy# var sb = [UIStoryboard storyboardWithName: @"Login" bundle: nil]
#"<UIStoryboard: 0x12e0ebf00>"
cy# [#0x12e0ebf00 instantiateViewControllerWithIdentifier: @"githubVC"]
#"<XTGithubLoginViewController: 0x12e382e00>"複製代碼

如今callback的傳遞應該很清楚了:點擊 github 登陸按鈕時,調用單例LoginCloud,並傳入callbackLoginCloud根據傳入的callback初始化登陸控制器XTGithubLoginViewController,並設置另外的一個callback回調,XTGithubLoginViewController在登錄成功後,經過該callback進行回調。

分析callback實現

block有點特殊,它也是一個對象類型。在這裏咱們涉及到了兩個block的傳遞,爲了方便以後的分析,咱們能夠先將兩個block的實現地址和傳遞的參數類型都打印出來。接下來的操做中可能會由於程序屢次重啓致使文中上下顯示的內存地址不一致的狀況,我每次都會進行說明。

1.獲取block的函數地址

由於咱們知道這個callback做爲參數傳給了-[LoginCloud githubLoginCallback:]方法,因此lldb鏈接服務(如何lldb能夠看個人第一篇文章)後,咱們在這個方法上打個斷點,同理設置-[XTGithubLoginViewController setCallBack:]斷點。
根據Block的內存結構能夠查找block的函數實現地址和參數返回值類型,具體能夠看這篇文章。開源就是好,節省步驟,咱們可使用facebook開源的chisel。觸發斷點後,獲取到block的內存地址後,使用pblock打印block:

獲取到第一個 block 的函數實現地址:0x0000000100194cc4
參數及返回值:void ^(bool, NSString *)

獲取到第二個 block 的函數實現地址:0x000000010015ccbc
參數及返回值:void ^(NSDictionary *, ThirdLoginModel *, NSDictionary *)

2.獲取block的具體傳回參數值

知道了 block 的參數的類型,那麼咱們能夠寫個 tweakshook 這個 block 而後打印一下,傳遞的這些參數內容,根據回傳順序,咱們先打印第二個block

%hook XTGithubLoginViewController

-(callBackType)callBack {
  callBackType block = ^(NSDictionary *dic1, ThirdLoginModel *model, NSDictionary *dic2) {
    HBLogInfo(@"dic1: %@, model: %@, dic2: %@", dic1, [model debugDescription], dic2);
  };
  return block;
}
%end // end hook複製代碼

打印結果以下:

果真發現第二個字典中,出現了多個值爲nullvalue,頗有可能就是咱們要找的點。那麼這個字典是從哪裏來的呢?想起當時打印XTGithubLoginViewController類的調用順序的時候,發現的一個函數onAuthCompleted:,它返回的就是一個這樣的字典,那麼咱們hook一下方法,過濾掉這些值爲nullvalue看看:

-(NSDictionary *)onAuthCompleted:(id)arg1 {
  HBLogInfo(@"%s", __func__);
  NSDictionary *result = %orig;
  NSMutableDictionary *dict = [result mutableCopy];
  for (NSString *key in result.allKeys) {
    if ([result[key] isKindOfClass:[NSNull class]]) {
      HBLogInfo(@"%s key: %@", __func__, key);
      [dict removeObjectForKey:key];
    }
  }
  HBLogInfo(@"result: %@", dict);
  return dict;
}複製代碼

打包,安裝後發現,並無修復這個bug,仍是報找不到length方法的日誌。看來咱們尚未找到 bug 點。

3.得到 block 的具體實現過程

在第二個回調上下斷點,觸發後,進入實現內部,根據lldb打印出實現內部的每一個方法的方法調用者和函數名、參數值。過程以下(主要是找到了這個block的實現地址後,在Hopper中找到這個代碼塊,能夠發現對於這個 block 的內部方法,Hopper是沒有註釋調用者、selector、參數的,咱們能夠對Hopper中的這些代碼中的全部顯示爲objc_msgSend的地方下一個斷點,一一觸發,分別打印每一個方法的調用者和調用方法,傳遞參數等信息),主要過程以下:

解釋後主要是如下過程:

// swift class
ZEHud *hud = [Xitu.ZEHud sharedInstance];
[hud showHud];
[AVUser loginWithAuthData:  dict platform: @"github" block: block] // block Imp: 0x000000010015cdf8 Signature: void ^(AVUser *, NSError *);複製代碼

能夠看到,其中也有一個block,打印一下block的內容,根據Xituimage的偏移獲得block實現的地址:0x100208df8-0x00000000000ac000=0x10015CDF8,在Hopper中找到:

在這個block上下斷點,c運行後來到該斷點出:si進入該實現內部,斷點定位到如圖中的惟一一個objc_msgSend方法上,獲取其調用信息:

能夠看到該方法-[LoginCloud thirdLogRefreshCurrentUserDatatype:ThirdData:Error:CallBack:]中還有一個block參數,繼續打印:

能夠發現獲得的block:
0x0000000100194cc4,void ^(bool, NSString *)
和咱們獲得的第一個block是同一個。

既然第二個block參數尚未分析完,第一個block已經火燒眉毛的回來了,那麼咱們先在Hopper中查看一下這個block的實現:

分析到這裏ni後app不當心退出了,那麼咱們在這裏從新啓動,既然已經知道這個block的地址,能夠直接經過查看Hopper內的每一個objc_msgSend的地址下斷點:


能夠知道該block的調用以下:

login = [LoginCloud singleClass];
[login refreshGithubLogin];
[XTLoginViewController callback];複製代碼

既然分析到了這裏,程序尚未崩潰,那麼說程序這以前都沒有問題,那麼問題可能出在了XTLoginViewController這個callback中,那麼咱們打印一下這個block

獲得:0x00000001001935d0,void ^(bool, NSString *)

那麼咱們繼續深刻,打印一下這個block的實現,步驟如上,若是不想每一個objc_msgSend都手動打斷點,能夠在這個block上打斷點,而後si,進入後,一步一步向下執行,等到運行到objc_msgSend的時候,打印這個方法的內容。可是在這裏下到的斷點都沒有執行程序就已經崩潰了!!!說明什麼?說明這個傳入的callback尚未執行,程序已經crash了。

  • 從正向開發的角度,傳入這個block,多是在程序知足必定條件的時候纔會回調這個block,來傳遞信息或處理一些事情。
  • 那麼程序應該是崩潰在這個block被執行前,咱們須要查看一下這個函數的內部實現。
  • 可能後知後覺了,如今仔細想一想,我前面說的分析到這個函數時,使用ni下一步後,程序不當心退出了!!,因此應該不是不當心退出了,而是這個函數的內部實現致使了崩潰。

肯定bug點

分析到了這個方法-[LoginCloud thirdLogRefreshCurrentUserDatatype:ThirdData:Error:CallBack:],那麼咱們去Hopper中看看它的內部實現:

果真,如圖看到的,咱們看到了以前在控制檯輸出的崩潰的關鍵詞length,既然找到了這裏,咱們利用這個調用函數的地址:0x10015d228來下一個斷點,看看調用者是誰,是否是真的是null,從而致使的bug。

從新啓動,lldb下斷點:

如圖,調用者爲null,而且ni後,完美的崩潰了。文章寫了這麼多,終於找到這個bug點了!!!激動啊。

找到了bug點,那麼咱們應該研究一下如何修復這個bug,首先咱們須要知道爲何開發者會用這個null,調用了length,程序發生了什麼致使了這個調用者爲null

咱們來仔細看一下,這個函數調用所在的代碼塊的彙編代碼:

loc_10015d1a8:
add        x8, sp, #0xb0 ; CODE XREF=-[LoginCloud thirdLogRefreshCurrentUserDatatype:ThirdData:Error:CallBack:]+660
stp        xzr, x8, [sp, #0xb0]
orr        w8, wzr, #0x30
stp        w28, w8, [sp, #0xc0]
adr        x8, #0x10015d84c
nop
fmov       d0, x8
adr        x8, #0x10015d85c
nop
ins        v0, x8
add        x8, sp, #0xb0
stur       q0, [x8, #0x18]
ldr        x0, [x25, #0x618]
mov        x24, x27
mov        x1, x24
bl         imp___stubs__objc_msgSend
mov        x29, x29
bl         imp___stubs__objc_retainAutoreleasedReturnValue
mov        x23, x0
adrp       x2, #0x10094b000 ; @"Z"
add        x2, x2, #0xc60 ; @"self_description"
mov        x1, x26
bl         imp___stubs__objc_msgSend
mov        x29, x29
bl         imp___stubs__objc_retainAutoreleasedReturnValue
str        x0, [sp, #0xd8]
mov        x0, x23
bl         imp___stubs__objc_release
ldr        x8, [sp, #0xb8]
ldr        x0, [x8, #0x28]
adrp       x8, #0x100aa0000 ; @selector(computeTime:)
ldr        x1, [x8, #0x340] ; "length",@selector(length)
bl         imp___stubs__objc_msgSend
ldr        x20, [sp, #0x18]
cmp        x0, #0x24
b.lo       loc_10015d24c複製代碼

能夠看到,ldr x0, [x8, #0x28]。這個null是從[x8, #0x28]加載的。可是咱們在這個代碼塊中(包括整個-[LoginCloud thirdLogRefreshCurrentUserDatatype:ThirdData:Error:CallBack:]方法內)都沒有找到。這個狀況就比較特殊了,自從學習逆向以來,仍是第一次碰到這種狀況。該調用者是從一個歷來沒有被寫入過內容的寄存器中讀取的。應該也是由於這個緣由才致使讀取的內容爲null吧。

換個思路,咱們結合上下文找線索。咱們翻譯一下上下文方法調用的彙編代碼:

AVUser *user = [AVUser currentUser];
NSString *name = [user valueForKey: @"username"];
NSString *headImg = [user ValueForKey: @"avatar_large"];
NSString *descrip = [user ValueForKey: @"self.description"];
//...複製代碼

咱們可使用cycript來打印一下這個user是什麼:

而後順便把彙編中的內容,打印一下:

果真,其中有一個打印是null。並且正好出如今length調用的上方:

因此頗有可能,這個bug是由於經過keyself.descriptionuser中獲取了一個沒有的值,並對他調用了length致使的。

那麼咱們經過theos建立一個插件,而後hook一下這個方法,返回一個咱們設置了值的AVUser對象,

%hook AVUser
+(id)currentUser {
  AVUser *user = %orig;
  id descri = [user valueForKey: @"self_description"];
  if ([descri isKindOfClass: [NSNull class]]) {
    HBLogWarn(@"the value for key: self_description is null");
    [user updateValue: @"" forKey: @"self_description"];
  }
  return user;
}

%end複製代碼

安裝後,再次登錄發現登錄成功,並無crash,終於修復了這個bug,發現已經寫了很多字了。

總結

最後的修復bug的tweaks能夠在這裏下載XituHook

其實在我本身分析的時候,分析的內容還要多,也比較雜。逆向分析正是這樣,咱們須要順着程序執行的順序分析,可是事情每每不如咱們想的這樣簡單,分析的岔路不少,特別是這裏,涉及到了多個block的回調,須要對block的實現原理及其內存結果比較熟悉才行。

在分析的過程當中,分析到的還有一些文中沒有寫到的block和方法,而且在其中發現了length關鍵詞,激動不已啊,可是當我深刻分析的時候發現,開發者都作了防禦

if ([obj isKindOfClass:[NSNull class]]) {
    obj = @"";
}複製代碼

預防出現NSNull的狀況,因此都不是 crash 的罪魁禍首。我猜想self_description是獲取的 github 上的某個我的信息(根據名字推測是自我介紹?)。若是想分析的話也能夠,須要從AVUser這個類若是得到這些信息開始分析。可是我想本文分享到這裏已經差很少了。

寫在最後

求工做,求工做,求工做,重要的事情說三遍。如今的iOS就業形勢,不提也罷,興趣所致,跪着走完。

相關文章
相關標籤/搜索