https://github.com/facebook/fishhook
fishhook
是一个facebook
的开源项目。
通过修改Mach-O
文件指向外部函数的指针
的值,来达到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
的目的。
通过 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对应包中的偏移量。
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
断点过了之后,我们还是一样查看(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