fishhook的注意点

有关fishhook的原理如果看过之前的MachO链接过程,那其实就应该很简单了,他就是讲懒加载符号表和非懒加载符号表的值改版了,使它指向我们自己的地址,然后又把原函数的真实地址保存在了一个指针上, 让外部可以继续调用原来的函数。关于源码解析的文章,可以阅读下面的两篇。
http://ios.jobbole.com/92918/
http://m.desgard.com/2017/12/17/fishook-1/index.html
这里想说的是一个注意点。刚刚说到fishhook把原函数的真实地址保存在了一个指针上, 让外部可以继续调用原来的函数。考虑一种情况,我们在某个系统的A函数首次调用前,使用fishhook去hook它,在我们的hook函数里面去调用了原来的函数。然后在hook之后,连续调用两次刚刚我们hook的函数。伪代码如下,请问会输出几个"hook method"?

static void (*ori_system_method_A)(void);
void hook_system_method_A() {
    NSLog(@"hook method");
    ori_system_method_A();
}

- (void)viewDidLoad {
    rebind_symbols((struct rebinding[]){
        {"system_method_A", (void *)&hook_system_method_A, (void **)&ori_system_method_A}
    }, 1);
    system_method_A();
    system_method_A();
}

正确答案是可能1次,也可能2次,这取决于system_method_A具体是什么函数。造成这种差别的原因在于,该函数是不是有被其他库调用,且其他库已经绑定了真实的函数地址。看了fishhook的源码之后应该知道,fishhook是注册了_dyld_register_func_for_add_image函数来监听image的加载,当加载一个每次这个callback回调之后,就会对传入的image进行处理,拿到image的懒加载表和非懒加载表,对其中每一项进行遍历,和我们需要hook的函数名称做比较,如果找到了名字一样的函数,就将我们传入的原函数的指针指向该项所指向的函数地址。这里面需要注意,我们只传入了一个原函数的指针,所以旧的值会被新的值所覆盖。
那么考虑一种情况:如果我需要hook的函数没有其他image调用了,那么就只能在自己的image里面找到匹配,然后进行替换,但是这个时候,所替换的原函数地址值是最终调用stub_helper的。所以当我们调用了原函数指针对应的函数之后,就会触发stub_helper,然后stub_helper又会把懒加载表里面的值改为真实的函数地址。所以就会造成我们的hook方法只能执行一遍。
考虑第二种情况:我们需要hook的函数被其他image调用了,且该image的懒加载表或非懒加载表里面已经是真实的函数地址,那么fishhook在处理的时候,虽然第一次是拿到了我们image内指向stub_helper的地址,但是后面会被其他image里面的真实的函数地址所覆盖。所以调用原函数指针对应的函数并不影响我们image里面的懒加载表,故我们的hook方法可以执行2次。
总结一下就是:调用fishhook之前,最好把这个需要hook函数调用一下,让其绑定真实的地址。

你可能感兴趣的:(fishhook的注意点)