fishhook
fishhook是Facebook提供的用于hook系統(tǒng)c函數(shù)的庫。它能動態(tài)重新綁定運行在iOS模擬器和真機設(shè)備上的MachO文件中的符號表中的符號。

準備知識
- PIC(Position Independent Code)位置代碼獨立。
為了使多個進程能夠共享內(nèi)存中的同一份代碼拷貝,已達到節(jié)約內(nèi)存資源的目的。 - ASLR(Address Space Layout Randomization)地址空間布局隨機化技術(shù)。
它將進程的某些內(nèi)存空間地址進行隨機化來增大入侵者預(yù)測目的地址的難度,從而降低進程被成功入侵的風險。因而每次啟動程序時,程序在內(nèi)存中的地址都不一樣。 - DYLD(Dynamic Loader)動態(tài)加載器。在Darwin/OS X被叫做dyld,它用來加載所有的frameworks, dynamic libraries, and bundles (plug-ins)。
由于蘋果的動態(tài)庫遵循PIC,存在于共享緩存庫,同時采用了ASLR技術(shù)。每次重啟手機后,動態(tài)庫的地址都會隨機偏移,需要通過基地址加偏移地址來獲取代碼的真實地址。
MachO文件
MachO文件是蘋果系統(tǒng)的可執(zhí)行文件。通過DYLD動態(tài)加載。
我們的應(yīng)用程序為了能夠獲取動態(tài)庫的真實地址,由DYLD動態(tài)加載MachO中的符號表,將符號表中的指針指向動態(tài)庫地址。從而達到調(diào)用系統(tǒng)庫的目的。
而fishhook正是通過dyld來修改符號表中指向動態(tài)庫的指針來達到hook的目的。
MachO中,__DATA段中與動態(tài)符號綁定相關(guān)的有兩個section:__nl_symbol_ptr和__la_symbol_ptr。
-
__nl_symbol_ptr是一個非懶加載數(shù)據(jù)的指針數(shù)組。 -
__la_symbol_ptr在第一次調(diào)用前,dyld會去通過dyld_stub_binder填充指針數(shù)組。在第一次調(diào)用之后才會真正鏈接到動態(tài)庫的位置。

為了在MachO中找到想要hook的函數(shù)的名字,需要在幾個間接的層間進行跳轉(zhuǎn)。在這兩個section的section header提供了一個offset用來指向?qū)?yīng)的section。

而__la_symbol_ptr指針數(shù)組的下標和indirect_symbol_table中的下標是一一對應(yīng)的。

indirect_symbol_table中的每個元素的data中保存著一個十六進制的數(shù),這個數(shù)就是真正的符號表中的數(shù)組下標。比如NSStringFromClass這個函數(shù)的Data為0x54,對應(yīng)十進制84,在symbol_table中的第84位即可找到。

而symbol_table對應(yīng)的元素中的data同樣保存著一個十六進制的數(shù),這個數(shù)是字符串表中的偏移地址。比如NSStringFromClass在string_table中的偏移為0x92,找到string_table的首地址,為0xCA18,則該函數(shù)的字符串常量位于 0xCA18 + 0x92 = 0xCAAA 的位置。

借用fishhook的圖

用法
static void (*orig_NSLog)(NSString *format, ...);
void my_NSLog(NSString *format, ...)
{
orig_NSLog(@"hook成功:%@",format);
}
- (void)viewDidLoad {
[super viewDidLoad];
struct rebinding rebind;
rebind.name = "NSLog";
rebind.replacement = my_NSLog;
rebind.replaced = (void *)&orig_NSLog;
rebind_symbols((struct rebinding[1]){rebind}, 1);
NSLog(@"test");
}
總結(jié)
fishhook正是通過查找到需要hook的函數(shù)指針,然后替換成自己寫的函數(shù)指針,從而實現(xiàn)hook系統(tǒng)函數(shù)的目的。
fishhook不能hook自定義的函數(shù),是因為自定義的函數(shù)不在__la_symbol_ptr中。