逆向系列0x02-攻破OC的Block

逆向block是一個(gè)稍微難點(diǎn)的活,因?yàn)槟悴⒉恢佬枰獋魇裁礃拥膮?shù),也不知道是什么類型的返回。另外,Swift的block的內(nèi)存分布的實(shí)現(xiàn)與OC不一樣會(huì)帶你入坑。

接著上回逆向系列0x01-在Swift中使用Social框架,我們將實(shí)現(xiàn)SLComposeViewControllercompletion回調(diào)。

crash?

打開ViewController.swift把下面邏輯加入到sharingButtonTapped(_:)中:

...
vc.completionHandler = {
  print("SLComposeViewController completed")
}
present(vc, animated: true)
...

請(qǐng)確保你已經(jīng)在設(shè)備上登陸了Facebook賬號(hào)(假設(shè)我們使用的的serviceType是Facebook的)。構(gòu)建并運(yùn)行,點(diǎn)擊分享按鈕,然后在這個(gè)分享vc出現(xiàn)后點(diǎn)擊取消。

嗯,你又會(huì)遇到一個(gè)crash了。我們來(lái)用LLDB分析一下,選擇Frame 1:(lldb) frame select 1


仔細(xì)觀察這里的匯編,紅色線的上一個(gè)指令是個(gè)call指令,地址為對(duì)RDI內(nèi)容的偏移。那現(xiàn)在RDI里面存放的是個(gè)什么鬼呢?
跳回到第一個(gè)棧幀(你不能在frame 1訪問(wèn)寄存器RDI):(lldb) frame select 0
然后打印RDI:(lldb) po $rdi
輸出結(jié)果為:(Function)。有點(diǎn)意思,來(lái)看看它是否是個(gè)NSObject的子類:

(lldb) po [$rdi class]
_SwiftValue
(lldb) po [$rdi superclass]
NSObject

從我先前的文章常用lldb可以知道,這個(gè)類應(yīng)該繼承自一個(gè)叫NSBlock的私有類。于是編譯器對(duì)Swfit的閉包要轉(zhuǎn)換成的OC類型做了一個(gè)錯(cuò)誤的假設(shè)。

這里的意思是你需要顯示的將Swift閉包轉(zhuǎn)換成OC的block。這是因?yàn)镾wift編譯器沒(méi)有completionHandler的類型信息,所以它沒(méi)有幫我們自動(dòng)轉(zhuǎn)換。

沒(méi)錯(cuò),在上篇的NSObject+P_SLComposeViewController.h中我們的確聲明了completionHandler的類型為id。

打開ViewController.swift用以下內(nèi)容進(jìn)行替換:

@IBAction func sharingButtonTapped(_ sender: Any) {
  guard let vcClass =
    NSClassFromString("SLComposeViewController") else { return }
  let vc = vcClass.composeViewController(forServiceType:
    "com.apple.social.twitter") as! UIViewController
  vc.setInitialText("Yay! Doggie Love!")
  if let originalImage = imageView.image {
    vc.addImage(originalImage)
  }
  typealias CompletionBlock = @convention(block) () -> Void
  vc.completionHandler = {
    print("SLComposeViewController completed")
  } as CompletionBlock
  present(vc, animated: true)
}

我們?cè)黾恿艘粋€(gè)叫CompletionBlocktypealias,它將會(huì)把這個(gè)Swift閉包轉(zhuǎn)換成何時(shí)的OC對(duì)象。再次構(gòu)建并運(yùn)行app,程序再不會(huì)crash了!

OC block的參數(shù)

我們先了解以下調(diào)用OC block時(shí)的匯編指令。

在Xcode中創(chuàng)建一個(gè)GUI斷點(diǎn)在給completionHandler賦值的那一行。構(gòu)建并運(yùn)行app然后點(diǎn)擊分享按鈕。這個(gè)時(shí)候斷點(diǎn)就會(huì)命中一次,Continue忽略這一次繼續(xù)執(zhí)行。

接著按Cancel取消按鈕,這時(shí)候斷點(diǎn)會(huì)再一次被觸發(fā)。去到frame2:(lldb) frame select 2
仔細(xì)觀察這里的匯編,會(huì)發(fā)現(xiàn)一個(gè)有趣的指令。沒(méi)錯(cuò)了,就是下面紅框標(biāo)注的那一行。


在調(diào)用OC的block之前,一個(gè)參數(shù)會(huì)被保存到RSI寄存器中。這意味著這個(gè)OC block有一個(gè)參數(shù)。

在當(dāng)執(zhí)行停止時(shí)RSI寄存器沒(méi)有被重寫的前提下,我們可以打印出RSI的內(nèi)容。又因?yàn)槲覀儎偤猛T赽lock執(zhí)行的最開始,所以RSI寄存器的內(nèi)容應(yīng)該是完好如初的。
跳回到frame 0并打印RSI的內(nèi)容:

(lldb) frame select 0
(lldb) register read rsi
     rsi = 0x0000000000000000

那兒并沒(méi)有任何東西。這似乎有點(diǎn)悲劇。但它至少能告訴我們一些東西。在經(jīng)過(guò)一番研究折騰蘋果是如何實(shí)現(xiàn)他們的API之后,我們可以假設(shè)一個(gè)為nil的NSObject被傳進(jìn)去了,又或者它是某種東西。在這個(gè)情形里,它可能是個(gè)枚舉值enum來(lái)標(biāo)記vc是否正常結(jié)束。

只有一種途徑來(lái)驗(yàn)證這個(gè)猜想。我們需要一次成功的內(nèi)容推送然后觀察RSI寄存器有什么變化。

那就推送吧(反正圖片是兩只可愛(ài)的狗狗,非常河蟹),點(diǎn)擊Post之后斷點(diǎn)應(yīng)該再一次命中。此時(shí)再來(lái)檢驗(yàn)一下RSI的值:

 (lldb) register read rsi
     rsi = 0x0000000000000001

完美!現(xiàn)在它的值是1了。這意味著這個(gè)傳進(jìn)來(lái)的參數(shù)是個(gè)會(huì)根據(jù)操作結(jié)果是失敗還是成功而變化的枚舉值(或者布爾值,差不多的)。好了我們要更新一下completionHandlerblock的類型了。

打開NSObject+P_SLComposeViewController.h然后用以下內(nèi)容替換:

typedef enum : NSUInteger {
  P_SLComposePostFailed = 0,
  P_SLComposePostSuccess = 1
} P_SLComposePost;
@interface NSObject (P_SLComposeViewController)
+ (id)composeViewControllerForServiceType:(NSString *)serviceType;
- (BOOL)setInitialText:(id)text;
- (BOOL)addImage:(id)image;
@property (copy, nonatomic) void (^completionHandler)(P_SLComposePost);

稍微改一下ViewController.swift

vc.completionHandler = { result in
  let resultString = result == P_SLComposePostFailed ? "failed" :
"success"
  print("SLComposeViewController completed with result: \(resultString)")
}

注意到我們?cè)僖膊恍枰?code>typealias了。這是因?yàn)槲覀円呀?jīng)把completionHandler的類型從id改為了了一個(gè)block類型,于是編譯器便知道了應(yīng)該把這里的Swift閉包看待成OC的block。

大功告成!我們成功的探索并使用了別人的代碼來(lái)發(fā)送一個(gè)Facebook的post,這個(gè)過(guò)程中我們并沒(méi)有借助任何頭文件或者其他關(guān)于模塊的信息。

下篇預(yù)告:查找和執(zhí)行代碼只是逆向Framework時(shí)的挑戰(zhàn)之一。下一個(gè)挑戰(zhàn)便是修改Framework的函數(shù)的參數(shù)或邏輯來(lái)滿足我們自己的需求!暴走吧!逆向!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,692評(píng)論 4 61
  • 你是否曾經(jīng)苦惱于理解你的代碼,而去嘗試打印一個(gè)變量的值? NSLog(@"%@", whatIsInsideThi...
    木易林1閱讀 1,046評(píng)論 0 4
  • 你是否曾經(jīng)苦惱于理解你的代碼,而去嘗試打印一個(gè)變量的值? NSLog(@"%@", whatIsInsideThi...
    paraneaeee閱讀 1,339評(píng)論 0 7
  • 轉(zhuǎn)載 與調(diào)試器共舞 - LLDB 的華爾茲: https://objccn.io/issue-19-2/ 推薦:i...
    F麥子閱讀 3,463評(píng)論 0 10
  • 歸家的旅途路過(guò)大連,坐上輕軌去探望老同學(xué),從起點(diǎn)到終點(diǎn),以前從來(lái)沒(méi)有像這次一樣仔細(xì)的觀察那些行色匆匆的人,每站都會(huì)...
    微光_sweet閱讀 259評(píng)論 0 0

友情鏈接更多精彩內(nèi)容