早些時候,iOS中一提到「黑魔法」、HOOK,不少人第一時間想到的就是 AOP RunTime MethodSwizzling 這些不明覺厲的東西,它們的基本用法其實都不難,真正難的是如何在合適的地方用好它們。git
任何事物都有兩面性,越強大其可能帶來的隱患也越具備毀滅性。蘋果提供的運行時機制當然大有用處,但若是在項目中濫用(更不是用來當作面試提高逼格的),不少時候只會拔苗助長,詳細誤區請參考iOS界的毒瘤-MethodSwizzling。github
關於 MethodSwizzling 的用法在以前的文章中也有過講解,請參考MethodSwizzling的幾種姿式。 該方式更多的用於性能監測、 crash 的兼容和上報、反破解防禦等一些工具的開發中,而在逆向中,在面對有相應安全防禦措施的應用時,其用武之地比較有限。面試
無獨有偶,「黑魔法」可不僅有 RunTime ,今天咱們來聊聊在逆向中經常使用的另外一種HOOK方式:fishhook。緩存
fishhook 是 FaceBook 開源的能夠動態修改 MachO 符號表的工具。fishhook 的強大之處在於它能夠 HOOK 系統的靜態 C 函數。sass
你們都知道 OC 的方法之因此能夠 HOOK 是由於它的運行時特性,OC 的方法調用在底層都是 msg_send(id,SEL)的形式,這爲咱們提供了交換方法實現(IMP)的機會,但 C 函數在編譯連接時就肯定了函數指針的地址偏移量(Offset),這個偏移量在編譯好的可執行文件中是固定的,而可執行文件每次被從新裝載到內存中時被系統分配的起始地址(在 lldb 中用命令image List
獲取)是不斷變化的。運行中的靜態函數指針地址其實就等於上述 Offset + Mach0 文件在內存中的首地址: 安全
既然 C 函數的指針地址是相對固定且不可修改的,那麼 fishhook 又是怎麼實現 對 C 函數的 HOOK 呢?其實內部/自定義的 C 函數 fishhook 也 HOOK 不了,它只能HOOK Mach-O 外部(共享緩存庫中)的函數。fishhook 利用了 MachO 的動態綁定機制(不清楚的同窗看這裏:MachO 文件結構詳解、dyld背後的故事&源碼分析 ):蘋果的共享緩存庫不會被編譯進咱們的 MachO 文件,而是在動態連接時纔去從新綁定。蘋果採用了PIC(Position-independent code)技術成功讓 C 的底層也能有動態的表現:bash
fishhook 正是利用了 PIC 技術作了這麼兩個操做:app
這樣就把系統方法與本身定義的方法進行了交換,達到 HOOK 系統 C 函數(共享庫中的)的目的。函數
爲了更好的理解 fishhook 是如何 HOOK 系統的 C 函數,咱們以 HOOK NSLog 爲例,從彙編着手來一步步去分析,爲你們扒開 fishhook 實現 HOOK 系統 NSLog 的全過程。工具
注:對於非懶加載符號表,dyld 會在動態連接時就連接動態庫
對於懶加載符號表,dyld 會在運行時函數第一次被調用時動態綁定一次
NSLog 在懶加載表中
複製代碼
新建一個空工程,寫下這兩行代碼:
在兩個 NSLog 處分別加上斷點,將工程 Run 起來,把 Debug -> Debug Workflow -> Always Show Disassembly 勾選上,用於查看彙編信息,斷點斷住後獲取 MachO 在內存中的首地址:
這是在幹嗎?沒錯,這就是第一次調用 NSLog 時系統去從新綁定位懶加載符號表中 NSLog 對應的指針所指向的位置。
接下來咱們過掉第一次斷點,讓斷點斷在第二個 NSLog 處,再次查看符號表中該指針(依然是 0x3028+0x000000010b0f7000 這個地址)所指向的地址,
咱們將 fishhook文件拖入工程,並添加一個簡單的綁定:
咱們運行起來以後點擊屏幕進入上圖所示斷點,查看符號表中本來指向系統 NSLog 的指針指向:
fishhook 官方給了這張圖:
今天咱們結合 iOS 的共享緩存庫中採用的 PIC 技術,介紹了 fishhook 對系統外部函數實現 HOOK 基本原理和具體過程,並經過反彙編命令一一驗證了 iOS 的動態綁定過程和 fishhook 的從新綁定機制,最後把 fishhook 在符號表中根據符號指針尋找函數實現的步驟作了演示。 願你有所收穫! 水平有限,請多指教~
鑑於篇幅過長會影響你們的閱讀體驗,fishhook 的源碼分析與應用場景以及安全防禦的分享,咱們這裏繼續。