早些時候,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 在懶加載表中
複製代碼
新建一個空工程,寫下這兩行代碼:
編譯一下工程,在目錄Products下將 .app內的可執行文件拷出用 MachOView 打開: 記下0x3028這個偏移值,這就是用於重定向到共享庫中的那個指針相對於 MachO文件的偏移量。在兩個 NSLog 處分別加上斷點,將工程 Run 起來,把 Debug -> Debug Workflow -> Always Show Disassembly 勾選上,用於查看彙編信息,斷點斷住後獲取 MachO 在內存中的首地址:
0x3028+0x000000010b0f7000 就是用於重定向到共享庫中的那個指針的內存地址。此時咱們查看該地址是否已經被重定向:這是在幹嗎?沒錯,這就是第一次調用 NSLog 時系統去從新綁定位懶加載符號表中 NSLog 對應的指針所指向的位置。
接下來咱們過掉第一次斷點,讓斷點斷在第二個 NSLog 處,再次查看符號表中該指針(依然是 0x3028+0x000000010b0f7000 這個地址)所指向的地址,
咱們發現,它指向的地址由以前的 0x010b0f89a0 變爲 0x010b491276 了,對應的函數也由以前的 dyld_stub_binder 變爲 NSLog ,這意味着該函數的動態綁定已經完成。以上,咱們驗證了 iOS 的動態綁定全過程。咱們將 fishhook文件拖入工程,並添加一個簡單的綁定:
注意:修改文件後從新編譯的 MachO 文件,符號表裏的指針偏移值可能會改變,從新運行的程序內存首地址也會發生變化,須要你從新拿到它們計算得出指針新的內存地址。咱們運行起來以後點擊屏幕進入上圖所示斷點,查看符號表中本來指向系統 NSLog 的指針指向:
此時該指針的指向被修改成咱們自定義的函數 myNslog 了,而將系統重定位的外部函數地址保存到了另外一個自定義函數指針 sys_nslog 中: 以上,咱們經過斷點分析彙編信息,驗證了 fishhook 實現 HOOK 系統外部函數的思路。接下來咱們結合 fishhook 的官方說明看它是如何根據字符串(方法名)找到對應指針在符號表中的偏移值的。fishhook 官方給了這張圖:
這張圖主要在描述如何由一個字符串(好比 "NSLog"),跟着它在 MachO 文件的懶加載表中對應的指針,一步步的找到該指針指向的函數實現地址,大體步驟以下:今天咱們結合 iOS 的共享緩存庫中採用的 PIC 技術,介紹了 fishhook 對系統外部函數實現 HOOK 基本原理和具體過程,並經過反彙編命令一一驗證了 iOS 的動態綁定過程和 fishhook 的從新綁定機制,最後把 fishhook 在符號表中根據符號指針尋找函數實現的步驟作了演示。 願你有所收穫! 水平有限,請多指教~
鑑於篇幅過長會影響你們的閱讀體驗,fishhook 的源碼分析與應用場景以及安全防禦的分享,咱們這裏繼續。