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

逆向block是一個稍微難點的活,因為你并不知道需要傳什么樣的參數(shù),也不知道是什么類型的返回。另外,Swift的block的內(nèi)存分布的實現(xiàn)與OC不一樣會帶你入坑。

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

crash?

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

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

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

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


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

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

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

這里的意思是你需要顯示的將Swift閉包轉(zhuǎn)換成OC的block。這是因為Swift編譯器沒有completionHandler的類型信息,所以它沒有幫我們自動轉(zhuǎn)換。

沒錯,在上篇的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)
}

我們增加了一個叫CompletionBlocktypealias,它將會把這個Swift閉包轉(zhuǎn)換成何時的OC對象。再次構(gòu)建并運行app,程序再不會crash了!

OC block的參數(shù)

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

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

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


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

在當(dāng)執(zhí)行停止時RSI寄存器沒有被重寫的前提下,我們可以打印出RSI的內(nèi)容。又因為我們剛好停在block執(zhí)行的最開始,所以RSI寄存器的內(nèi)容應(yīng)該是完好如初的。
跳回到frame 0并打印RSI的內(nèi)容:

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

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

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

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

 (lldb) register read rsi
     rsi = 0x0000000000000001

完美!現(xiàn)在它的值是1了。這意味著這個傳進(jìn)來的參數(shù)是個會根據(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)")
}

注意到我們再也不需要typealias了。這是因為我們已經(jīng)把completionHandler的類型從id改為了了一個block類型,于是編譯器便知道了應(yīng)該把這里的Swift閉包看待成OC的block。

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

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

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

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

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

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