共享文件原理_fishhook 的实现原理浅析
| 作者:小可長江,目前在?bilibili 做音視頻研發(fā),業(yè)余時(shí)間喜歡研究好的源碼和逆向
打開姿勢很重要
早些時(shí)候,iOS中一提到“黑魔法”、HOOK,很多人第一時(shí)間想到的就是 AOP RunTime MethodSwizzling 這些不明覺厲的東西,它們的基本用法其實(shí)都不難,真正難的是如何在合適的地方用好它們。
任何事物都有兩面性,越強(qiáng)大其可能帶來的隱患也越具有毀滅性。蘋果提供的運(yùn)行時(shí)機(jī)制固然大有用處,但如果在項(xiàng)目中濫用(更不是用來當(dāng)做面試提升逼格的),很多時(shí)候只會適得其反,詳細(xì)誤區(qū)請參考?iOS界的毒瘤-MethodSwizzling。
關(guān)于 MethodSwizzling 的用法在之前的文章中也有過講解,請參考?MethodSwizzling的幾種姿勢。該方式更多的用于性能監(jiān)測、 crash 的兼容和上報(bào)、反破解防護(hù)等一些工具的開發(fā)中,而在逆向中,在面對有相應(yīng)安全防護(hù)措施的應(yīng)用時(shí),其用武之地比較有限。
無獨(dú)有偶,“黑魔法”可不只有 RunTime ,今天我們來聊聊在逆向中常用的另一種HOOK方式:fishhook。
fishhook 背后的故事
(一)實(shí)現(xiàn)原理
fishhook 是 FaceBook 開源的可以動態(tài)修改 MachO 符號表的工具。fishhook 的強(qiáng)大之處在于它可以 HOOK 系統(tǒng)的靜態(tài) C 函數(shù)。
大家都知道 OC 的方法之所以可以 HOOK 是因?yàn)樗倪\(yùn)行時(shí)特性,OC 的方法調(diào)用在底層都是 msg_send(id,SEL) 的形式,這為我們提供了交換方法實(shí)現(xiàn)(IMP)的機(jī)會,但 C 函數(shù)在編譯鏈接時(shí)就確定了函數(shù)指針的地址偏移量(Offset),這個(gè)偏移量在編譯好的可執(zhí)行文件中是固定的,而可執(zhí)行文件每次被重新裝載到內(nèi)存中時(shí)被系統(tǒng)分配的起始地址(在 lldb 中用命令?image List?獲取)是不斷變化的。運(yùn)行中的靜態(tài)函數(shù)指針地址其實(shí)就等于上述 Offset + Mach0 文件在內(nèi)存中的首地址:
既然 C 函數(shù)的指針地址是相對固定且不可修改的,那么 fishhook 又是怎么實(shí)現(xiàn) 對 C 函數(shù)的 HOOK 呢?其實(shí)內(nèi)部/自定義的 C 函數(shù) fishhook 也 HOOK 不了,它只能HOOK Mach-O 外部(共享緩存庫中)的函數(shù)。fishhook 利用了 MachO 的動態(tài)綁定機(jī)制(不清楚的同學(xué)看這里:MachO 文件結(jié)構(gòu)詳解、dyld背后的故事&源碼分析
):蘋果的共享緩存庫不會被編譯進(jìn)我們的 MachO 文件,而是在動態(tài)鏈接時(shí)才去重新綁定。蘋果采用了?PIC(Position-independent code)技術(shù)成功讓 C 的底層也能有動態(tài)的表現(xiàn):
編譯時(shí)在 Mach-O 文件 _DATA 段的符號表中為每一個(gè)被引用的系統(tǒng) C 函數(shù)建立一個(gè)指針(8字節(jié)的數(shù)據(jù),放的全是0),這個(gè)指針用于動態(tài)綁定時(shí)重定位到共享庫中的函數(shù)實(shí)現(xiàn)。
在運(yùn)行時(shí)當(dāng)系統(tǒng) C 函數(shù)被第一次調(diào)用時(shí)會動態(tài)綁定一次,然后將 Mach-O 中的 _DATA 段符號表中對應(yīng)的指針,指向外部函數(shù)(其在共享庫中的實(shí)際內(nèi)存地址)。
fishhook 正是利用了 PIC 技術(shù)做了這么兩個(gè)操作:
將指向系統(tǒng)方法(外部函數(shù))的指針重新進(jìn)行綁定指向內(nèi)部函數(shù)/自定義 C 函數(shù)。
將內(nèi)部函數(shù)的指針在動態(tài)鏈接時(shí)指向系統(tǒng)方法的地址。
這樣就把系統(tǒng)方法與自己定義的方法進(jìn)行了交換,達(dá)到 HOOK 系統(tǒng) C 函數(shù)(共享庫中的)的目的。
(二)用匯編解析過程
為了更好的理解 fishhook 是如何 HOOK 系統(tǒng)的 C 函數(shù),我們以 HOOK NSLog 為例,從匯編著手來一步步去分析,為大家扒開 fishhook 實(shí)現(xiàn) HOOK 系統(tǒng) NSLog 的全過程。
注:對于非懶加載符號表,dyld 會在動態(tài)鏈接時(shí)就鏈接動態(tài)庫
對于懶加載符號表,dyld 會在運(yùn)行時(shí)函數(shù)第一次被調(diào)用時(shí)動態(tài)綁定一次
NSLog 在懶加載表中
1.驗(yàn)證系統(tǒng)的動態(tài)綁定:
新建一個(gè)空工程,寫下這兩行代碼:
編譯一下工程,在目錄 Products 下將 .app 內(nèi)的可執(zhí)行文件拷出用 MachOView 打開:
記下 0x3028 這個(gè)偏移值,這就是用于重定向到共享庫中的那個(gè)指針相對于 MachO文件的偏移量。
在兩個(gè) NSLog 處分別加上斷點(diǎn),將工程 Run 起來,把 Debug -> Debug Workflow -> Always Show Disassembly 勾選上,用于查看匯編信息,斷點(diǎn)斷住后獲取 MachO 在內(nèi)存中的首地址:
0x3028+0x000000010b0f7000 就是用于重定向到共享庫中的那個(gè)指針的內(nèi)存地址。此時(shí)我們查看該地址是否已經(jīng)被重定向:
拿到該指針當(dāng)前保存的值,iOS 的 CPU 是小端序,當(dāng)前機(jī)型為 64 位 CPU,所以倒序讀 8 個(gè)字節(jié)就是指針的值:0x010b0f89a0
dis -s 是反匯編命令,我們發(fā)現(xiàn)此時(shí)該指針指向的函數(shù)正在調(diào)用系統(tǒng)動態(tài)綁定的函數(shù)
進(jìn)一步查看調(diào)用函數(shù)詳細(xì)信息:libdyld.dylib`dyld_stub_binder
這是在干嘛?沒錯(cuò),這就是第一次調(diào)用 NSLog 時(shí)系統(tǒng)去重新綁定位懶加載符號表中 NSLog 對應(yīng)的指針?biāo)赶虻奈恢谩?/p>
接下來我們過掉第一次斷點(diǎn),讓斷點(diǎn)斷在第二個(gè) NSLog 處,再次查看符號表中該指針(依然是 0x3028+0x000000010b0f7000 這個(gè)地址)所指向的地址,
我們發(fā)現(xiàn),它指向的地址由之前的?0x010b0f89a0?變?yōu)?0x010b491276?了,對應(yīng)的函數(shù)也由之前的 dyld_stub_binder 變?yōu)?NSLog ,這意味著該函數(shù)的動態(tài)綁定已經(jīng)完成。以上,我們驗(yàn)證了 iOS 的動態(tài)綁定全過程。
2.驗(yàn)證 fishhook 的重綁定:
我們將 fishhook文件拖入工程,并添加一個(gè)簡單的綁定:
注意:修改文件后重新編譯的 MachO 文件,符號表里的指針偏移值可能會改變,重新運(yùn)行的程序內(nèi)存首地址也會發(fā)生變化,需要你重新拿到它們計(jì)算得出指針新的內(nèi)存地址。
我們運(yùn)行起來之后點(diǎn)擊屏幕進(jìn)入上圖所示斷點(diǎn),查看符號表中原本指向系統(tǒng) NSLog 的指針指向:
此時(shí)該指針的指向被修改為我們自定義的函數(shù) myNslog 了,而將系統(tǒng)重定位的外部函數(shù)地址保存到了另一個(gè)自定義函數(shù)指針 sys_nslog 中:
以上,我們通過斷點(diǎn)分析匯編信息,驗(yàn)證了 fishhook 實(shí)現(xiàn) HOOK 系統(tǒng)外部函數(shù)的思路。接下來我們結(jié)合 fishhook 的官方說明看它是如何根據(jù)字符串(方法名)找到對應(yīng)指針在符號表中的偏移值的。
(三)fishhook 是如何根據(jù)字符串找到對應(yīng)指針在符號表中的偏移值的?
fishhook 官方給了這張圖:
這張圖其實(shí)就是講根據(jù)一個(gè)字符串(比如 "NSLog") 如何一步步找到其在 MachO 文件里對應(yīng)指針的偏移值,大致步驟如下:
1) 在 String Table 中找到該字符串在 Symbols Table -> Symbols 中的位置:
用 0x4F9F-0x4F04 = 0x9B
2) 在 Symbols Table -> Symbols 中找到Data = 0x9B 的符號,其對應(yīng)的 offset 值 122 (0x7A) 就是該符號在 Dynamic Symbols Table -> Indirect Symbols 表中的 Data 值
3) 在 Dynamic Symbols Table -> Indirect Symbols 表中找到 Data 值為 0x7A 的符號,其位于該表中的位置(第一個(gè))就是它在懶加載表中對應(yīng)的位置。
4) 懶加載表中對應(yīng)位置的 Offset 值就是該指針最終的偏移量:
總結(jié)
今天我們結(jié)合 iOS 的共享緩存庫中采用的 PIC 技術(shù),介紹了 fishhook 對系統(tǒng)外部函數(shù)實(shí)現(xiàn) HOOK 基本原理和具體過程,并通過反匯編命令一一驗(yàn)證了 iOS 的動態(tài)綁定過程和 fishhook 的重新綁定機(jī)制,最后把 fishhook 在符號表中查找指針偏移量的步驟做了演示。 愿你有所收獲! 水平有限,請多指教~
文章鏈接
iOS界的毒瘤-MethodSwizzling
https://juejin.im/entry/5a1fceddf265da43310d9985MethodSwizzling的幾種姿勢?
https://juejin.im/post/5c616552f265da2dd53fa4e7#heading-3MachO--文件結(jié)構(gòu)詳解
https://juejin.im/post/5c67e7efe51d45164c75993bdyld背后的故事&源碼分析
https://juejin.im/post/5c727262e51d457139116208
推薦閱讀
移動開發(fā)唱衰,iOS開發(fā)者如何涅槃重生?
Cocoapod 1.6 概覽
看完這個(gè)你們團(tuán)隊(duì)的代碼也很規(guī)范
總結(jié)
以上是生活随笔為你收集整理的共享文件原理_fishhook 的实现原理浅析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 途牛java面试题_途牛java面试题.
- 下一篇: c语言简单选择对字符串数组排序,简单了解