感受是要搞個《逆向分析及修復app》系列的節奏啊javascript
你們好,我又來了。上次給你們分享了《逆向及修復最新iOS版少數派客戶端的閃退bug》,@了一些iOS界的大神,沒想到受到了你們的轉發和關注,還真有點小激動。同時也在微博的評論下收到了稀土掘金的歡迎,還給我開了專欄權限,但願能夠到稀土掘金上分享寫做。備感榮幸的同時,忽然發現,一直在稀土掘金上面看文章的我,一直沒有註冊帳戶(好像註冊了可是忘了密碼,在1Password中也沒有記錄,看來是好久以前登錄過了)。這也是此次故事的開始,收到稀土掘金的評論後,立刻打開了早已不知道何時下載的 掘金 app。果真,沒有登錄記錄,做爲開發者立刻用 GitHub 受權進行登錄,尷尬的事情發生了,在受權成功後,app閃退了。好尷尬啊,好吧稀土掘金上的處女座就獻給你了。java
跟以前寫《逆向及修復最新iOS版少數派客戶端的閃退bug》文章時不一樣,在文章寫到這裏的時候,我其實尚未徹底分析完崩潰的緣由。我是分析途中決定開始寫這篇文章的,決定一邊分析一遍寫,這樣纔會儘量的保留分析的全過程,固然最後能不能分析到關鍵點,且看下去吧,由於我也不知道。ios
###找到崩潰緣由
分析一個bug的開始,固然是從崩潰緣由找起,前一篇文章,咱們使用了lldb調試,在觸發 app 崩潰的時候,從 Terminal 找到了關鍵詞:EXE_BAD_ACCESS
。此次,咱們使用最簡單的,查看系統日誌來定位崩潰緣由,使用最新的 Mac,打開控制檯,鏈接手機並清空多餘的系統日誌打印,而後觸發崩潰,觀察控制檯中輸出的內容(若是輸出日誌過多,可使用進程名Xitu
做爲關鍵詞,進行過濾)。若是能夠仍是不要使用關鍵詞過濾,由於不是全部的崩潰都是因爲 app 自己的問題致使的,正如簽名破壞的 app 在沒有越獄的手機上會安裝失敗,這個安裝失敗的緣由查找就須要在日誌中的 SpringBoard
進程打印中進行查找
(SpringBoard
是Apple
用來管理咱們iPhone應用的一個應用,能夠理解爲咱們 PC 端的桌面,它還負責應用的桌面排列、管理通知中心等)。git
正如咱們但願的同樣,崩潰的緣由最多見不過了:
github
這個崩潰就頗有意思了,開發者對NSNull
調用了length
方法,由於找不到該方法而崩潰!固然開發者確定是不會主動對NSNull
類型調用length
方法,猜測:swift
NSNull
類型,說明這個對象多是在運行時肯定的length
,說明開發者認爲這個類應該是屬於NSString
類型的大膽猜想:結合這個崩潰是發生在登錄的時候,須要從網絡獲取登錄信息,那麼會不會是程序在對獲取到的登錄信息進行處理的時候致使的崩潰(好比登錄用戶的一些信息沒有設置,因此程序從登錄信息裏取到的是空值)。網絡
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
全部的target
和action
app
使用class-dump
等工具,導出app
的全部類頭文件,使用logify.pl
工具生成XTGithubLoginViewController
類的全部hook
函數,使用theos
編寫安裝插件後,觸發崩潰,而後在控制檯查找該類的函數調用邏輯。以下:
函數
能夠發現,程序是在-[XTGithubLoginViewController viewWillDisappear:]
調用以後才收到的崩潰的通知。因此頗有可能崩潰的緣由不是XTGithubLoginViewController
致使的,而是在XTGithubLoginViewController
類返回登錄信息後。工具
仔細研究XTGithubLoginViewController
的調用過程,發現了幾個有意思的方法,調用順序以下:
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:
方法確實是登陸的入口
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];複製代碼
在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
,並傳入callback
,LoginCloud
根據傳入的callback
初始化登陸控制器XTGithubLoginViewController
,並設置另外的一個callback
回調,XTGithubLoginViewController
在登錄成功後,經過該callback
進行回調。
block有點特殊,它也是一個對象類型。在這裏咱們涉及到了兩個block的傳遞,爲了方便以後的分析,咱們能夠先將兩個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 *)
。
block
的具體傳回參數值知道了 block 的參數的類型,那麼咱們能夠寫個 tweaks
來 hook
這個 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複製代碼
打印結果以下:
果真發現第二個字典中,出現了多個值爲null
的value
,頗有可能就是咱們要找的點。那麼這個字典是從哪裏來的呢?想起當時打印XTGithubLoginViewController
類的調用順序的時候,發現的一個函數onAuthCompleted:
,它返回的就是一個這樣的字典,那麼咱們hook
一下方法,過濾掉這些值爲null
的value
看看:
-(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 點。
在第二個回調上下斷點,觸發後,進入實現內部,根據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的內容,根據Xitu
image的偏移獲得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
下一步後,程序不當心退出了!!,因此應該不是不當心退出了,而是這個函數的內部實現致使了崩潰。分析到了這個方法-[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是由於經過key
:self.description
從user
中獲取了一個沒有的值,並對他調用了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就業形勢,不提也罷,興趣所致,跪着走完。