fishhook

315 阅读4分钟

fishhook的使用

下载fishhook源码,新建工程,拖到工程中。使用fishhook来hookNSLog

image.png 创建一个rebinding结构体,设置3个参数。分别是:函数名,函数指针,新函数。按照原函数的格式定义一个新函数用来替换。定义函数指针用来接收被hook的函数,方便之后调用原函数。调用rebind_symbols传入结构体,这样就hook完成了。

struct rebinding rebing;
rebing.name = "NSLog";
rebing.replaced = (void *)&orig_NSLog;
rebing.replacement = sww_NSLog;

struct rebinding bds[] = {rebing};
rebind_symbols(bds, 1);
//函数指针
static void (*orig_NSLog)(NSString *format, ...);

void sww_NSLog(NSString *format, ...){
    NSString *str = [@"hook到了NSLog:" stringByAppendingString:format];
    orig_NSLog(str);
}

在touchesBegin中调用NSLog方法,可以看到已经hook成功了。 image.png

符号绑定

当我们调用系统的C函数时,比如NSLog,这个函数在Foundation中,Foundation存在手机里,我们并不知道函数的真实地址。NSLog是外部符号,编译生成MachO文件时,把它保存在间接符号表中,他也在懒加载符号表中。第一次调用时,dyld会对NSLog进行符号重绑定,指向真正的函数地址,第二次包括以后再调用,就会跳转到真正的函数地址了。

汇编验证

通过查看两个NSLog的汇编,验证绑定过程 image.png 进入汇编,看到两个bl 0x1043824f0的指令,调用NSLog Snipaste_2021-05-07_00-14-33.png

进去第一个NSLog Snipaste_2021-05-07_00-15-12.png 进去br x16 Snipaste_2021-05-07_00-15-53.png 这里看到要调用dyld_stub_binder函数,这个就是dyld的绑定函数。 Snipaste_2021-05-07_00-17-08.png

Snipaste_2021-05-07_00-17-58.png

过掉dyld_stub_binder函数,进入第二个NSLog的汇编 Snipaste_2021-05-07_00-19-14.png 进入下一步 Snipaste_2021-05-07_00-19-48.png 这里看到直接进入NSLog的函数实现。

汇编+MachO

image.png 在汇编代码中,看到NSLog函数都会跳转到0x1005565a8地址去执行,读出0x1005565a8中的值为图中所示。通过image list拿出ASLR为0x0000000100550000,0x1005565a8在MachO中的偏移量为65a8,此时我们去MachO文件中查看。

image.png 进入到第一个NSLog里面,查看汇编,打印x16中的值0x0000000100556638,减去ASLR0x0000000100550000,结果为6638

image.png 去MachO中找偏移量为6638的位置,是一段汇编代码,最后会执行到6620

图:重绑定 image.png

image.png 这段代码,最后就是走到了dyld_stub_binder函数,进行重绑定。

image.png 看一下懒加载符号表,所有的外部函数都在这里,他们的data就是偏移量,NSLog6638,NSStringFromClass6644,UIApplicationMain66a4,去看图:重绑定,这个函数都会调用到6620,走到dyld_stub_binder函数,进行重绑定。

总结 符号绑定的过程:

  1. 外部函数执行首先调用代码 Text Stubs(执行懒加载符号表中对应的函数)
  2. 懒加载符号表里默认保存的是寻找binder函数(绑定之后,修改懒加载符号表中的值,改为函数的真实地址)

fishhook原理

fishhook只能hook系统的C函数。原因是:系统的C函数在MachO中的符号对应一个函数地址,fishhook所做的事情就是,修改了MachO文件中这个符号与地址的对应关系,把系统C函数绑定一个自定义的函数地址。修改了懒加载和非懒加载符号表。

下面为fishhook官方原理图: 687474703a2f2f692e696d6775722e636f6d2f4856587148437a2e706e67.png

  1. 懒加载符号表中拿到一个符号的index.
  2. 通过index,找到间接符号表中对应的符号,拿到其在Symbol Table中的index
  3. 在Symbol Table中找到对应符号,拿到其在String Table中的偏移量
  4. 在String Table中找到字符,与给定的名字对比,相同则修改其懒加载符号表中的函数地址为自定函数

去符号与恢复符号

我们的app在打包上架的时候。默认都会选择脱掉所有符号,包括本地符号,全局符号。这样既能减少包的体积,也能增加app的安全性。

image.png 脱掉符号的可执行文件,只有间接符号。 image.png 当我们遇到脱掉符号的app时,调试起来就比较困难了。但是我们可以恢复符号,原理就是根据OC的runtime,类名和方法名这些是不会被脱掉的,我们可以使用工具恢复OC的方法。

image.png 使用retore-symbol工具可以直接回复符号。恢复之后,会在Symbols里。之后还需要重新打包生成ipa,重签名等。

image.png