fishhook
https://github.com/facebook/fishhook
fishhook
是一個facebook
的開源項目。
通過修改Mach-O
文件指向外部函數的指針
的值,來達到hook
的目的
hook原理
蘋果爲了能在 Mach-O
文件中訪問外部函數,採用了一個技術,叫做PIC(位置代碼獨立
)技術。
當你的應用程序想要調用 Mach-O 文件
外部的函數的時候,或者說如果 Mach-O
內部需要調用系統的庫函數時,Mach-O
文件會:
先在 Mach-O
文件的 _DATA
段中建立一個指針(8字節的數據,放的全是0)
,這個指針變量指向外部函數。
DYLD
會動態的進行綁定!將 Mach-O
中的 _DATA
段中的指針,指向外部函數。
所以說,C的底層也有動態的表現。C在內部函數的時候是靜態的,在編譯後,函數的內存地址就確定了。但是,外部的函數是不能確定的,也就是說C的底層也有動態的。fishhook
之所以能hook C函數
,是利用了 Mach-O
文件的 PIC(位置代碼獨立)
技術特點。也就造就了靜態語言C也有動態的部分,通過 DYLD
進行動態綁定的時候做了手腳。
原文鏈接:https://www.jianshu.com/p/4d86de908721
大概就是說:
app
是一個mach-o
文件,其內容中_DATA
字段有一個指針,指向外部框架(例如Fundation
),例如Fundation
框架的NSLog
,我們通過修改_DATA
字段的這個指針指向我們所寫的函數,然後在函數中指向原函數,就可以達到hook
的目的。
hook
通過 MachOView 這個軟件打開 app
的 Mach-O
文件
加載Mach-O
,NSLog
屬於lazy symbol Pointer
對於
非懶加載符號表 Non-lazy symbol Pointer
,DYLD會立刻馬上去鏈接動態庫
對於懶加載符號表 lazy symbol Pointer
,DYLD會在執行代碼的時候去動態的鏈接動態庫
通過fishhook
修改代碼如下:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
// 這裏由於NSLog是lazy pointer,所以需要調用一次,纔會被寫入_DATA 中的指針
NSLog(@"dylib link address");
struct rebinding nslogbind;
nslogbind.name = "NSLog";
nslogbind.replacement = newmethod;
nslogbind.replaced = (void*)&old_nslog;
struct rebinding rebinds[] = {nslogbind};
//這裏進行斷點
rebind_symbols(rebinds, 1);
}
static void (*old_nslog)(NSString *format,...);
void newmethod(NSString *format,...) {
va_list va;
va_start(va, format);
//改變下字符串,證明hook過了
format = [format stringByAppendingString:@" hook"];
old_nslog(format,va);
va_end(va);
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
NSLog(@"點擊屏幕");
}
@end
通過 rebind_symbols
斷點來檢測hook
前後的_DATA
中的符號表指針存儲
的值
是否改變。
找到NSLog的地址
找到NSLog對應包中的偏移量。
Offset
表示符號偏移量,當Mach-O
文件加載後,NSLog
的符號表綁定地址
應該是Mach-O
文件偏移量 + Offset
Mach-O
文件偏移量可以通過llvm
在xcode斷點時打印image list
獲取
(lldb) image list
[ 0] D6F7D258-D608-3ED0-A7D1-16215F661AEE 0x0000000100264000 /Users/sheng/Library/Developer/Xcode/DerivedData/hookTest-fdbgciwrddbskoafylnbaldfxzyr/Build/Products/Debug-iphoneos/hookTest.app/hookTest
[ 1] 3049BF50-A9B1-318C-8B2B-34774DE49438 0x0000000100434000 /Users/sheng/Library/Developer/Xcode/iOS DeviceSupport/12.4.5 (16G161)/Symbols/usr/lib/dyld
[ 2] 5D56F475-48DE-3DF0-955D-EBABE2A63DCF 0x000000019eea2000 /Users/sheng/Library/Developer/Xcode/iOS DeviceSupport/12.4.5 (16G161)/Symbols/System/Library/Frameworks/Foundation.framework/Foundation
...
hookTest
的偏移量是 0x0000000100264000
那麼NSLog
的符號綁定地址就是 0x0000000100264000+0x8018
(lldb) x 0x0000000100264000+0x8018
0x10026c018: 4c c4 fb 9e 01 00 00 00 58 24 fb 9e 01 00 00 00 L.......X$......
0x10026c028: 64 2b b8 ca 01 00 00 00 90 a5 26 00 01 00 00 00 d+........&.....
//這地址裏面的值,是指向外部函數的指針的地址,也就是動態緩存區裏面 NSLog 的真實函數地址。
// 地址從右往左,字節爲單位
4c c4 fb 9e 01 00 00 00
//得到地址爲
0x000000019efbc44c
通過dis -s
反彙編查看
(lldb) dis -s 0x000000019efbc44c
Foundation`NSLog:
0x19efbc44c <+0>: sub sp, sp, #0x20 ; =0x20
0x19efbc450 <+4>: stp x29, x30, [sp, #0x10]
0x19efbc454 <+8>: add x29, sp, #0x10 ; =0x10
0x19efbc458 <+12>: add x8, x29, #0x10 ; =0x10
0x19efbc45c <+16>: str x8, [sp, #0x8]
0x19efbc460 <+20>: add x1, x29, #0x10 ; =0x10
0x19efbc464 <+24>: mov x2, x30
0x19efbc468 <+28>: bl 0x19efbc3bc ; _NSLogv
hook後的地址
斷點過了之後,我們還是一樣查看(lldb) x 0x0000000100264000+0x8018
(lldb) x 0x0000000100264000+0x8018
0x10026c018: 00 94 26 00 01 00 00 00 58 24 fb 9e 01 00 00 00 ..&.....X$......
0x10026c028: 64 2b b8 ca 01 00 00 00 68 2a 06 9e 01 00 00 00 d+......h*......
//00 94 26 00 01 00 00 00
//0x0100269400
(lldb) dis -s 0x0100269400
hookTest`newmethod:
0x100269400 <+0>: sub sp, sp, #0x40 ; =0x40
0x100269404 <+4>: stp x29, x30, [sp, #0x30]
0x100269408 <+8>: add x29, sp, #0x30 ; =0x30
0x10026940c <+12>: mov x8, #0x0
0x100269410 <+16>: stur x8, [x29, #-0x8]
0x100269414 <+20>: sub x9, x29, #0x8 ; =0x8
0x100269418 <+24>: str x0, [sp, #0x18]
0x10026941c <+28>: mov x0, x9
已經替換成newmethod
點擊屏幕控制檯輸出
2020-03-28 20:43:31.938544+0800 hookTest[10749:1804880] 點擊屏幕 hook
這裏完成了NSLog
的hook
,接下來就想實現對objc_msgSend
方法的hook
,但是這個函數是彙編編寫的.s
文件 ,要實現hook
需要了解彙編,並重寫一個新的方法。後續…
參考文章: https://www.jianshu.com/p/4d86de908721