符号绑定的另一种打开方式

懒加载和非懒加载

iOS对于引用的外部符号,分为 Lazy SymbolNon-Lazy Symbol ,分别存储在 __DATA,__got 节和 __DATA,__la_symbol_ptr 节。


Non-Lazy Symbol 符号在dyld加载模块的时候,就会将真实的函数地址写入到对应的地址中,实现绑定。而 Non-Lazy Symbol 则会在第一次调用该函数的时候,为其动态寻找真实函数地址并进行绑定。

facebook基于符号绑定机制,写出了hook神器 fishhook ,通过查找符号指针并替换,从而达到hook效果!!!

然而,基于模块检测反hook,却甚是烦人。你可能会有反反hook来应付,但是它也有可能会有反反反hook来对付你~~~

那么,怎么才能终结这场hook与反hook的心理战呢?

Mach-O View 分析动态符号绑定过程

简单分析一下 Lazy Symbol 的绑定过程:



这里以 NSLog 为例:

可以看出,符号 NSLog 所指向的地址为:0x0000000100006474。

转化为文件偏移为:0x0000000100006474 - 0x100008078 + 32888 = 0x6474;

到文件偏移为 0x6474 的位置查看:


这是一段可执行代码,地址0x6474处的意思是:读取0x647c位置处的四个字节的数据(0x1d),保存到w16寄存器。然后无条件跳转到0x645c(这里的地址,全部都是指文件偏移)。

这段代码,光这么看其实看不出什么,但是如果去调试的话,就会发现,这段代码实际上是在调用 dyld_stub_binder 为懒加载符号绑定真实地址。而刚刚在0x6474处的代码获取到的四字节的数据,实际上是符号绑定信息的偏移:

0xc428+0x1d = 0xc445

也就是说,动态绑定NSLog所需要的数据,就存储在0xc445处。

那么,理论上来说,如果我们尝试着修改这里的数据,是不是就会改变符号的查找的过程呢?

实践

想的再多,都不如动手操作!!!

新建一个工程,书写如下代码(main.m):

__attribute__((constructor)) static void entry(int argc,char *argv[],char **apple,char **executablepath,struct mach_header_64 **mh_ptr){
    
    if (!strncmp(argv[0], "aaa", 3)) {
        printf("the same!!");
    }
}

并按照如上方式,查找到函数 strncmp 的Lazy Binding Info,做如下修改:


修改后

编写动态库并注入到可执行文件:

__attribute__((visibility("default"))) int strncmq(const char *__s1, const char *__s2, size_t __n);

int strncmq(const char *__s1, const char *__s2, size_t __n){
    printf("hook:%s\nhook:%s",__s1,__s2);
    return strncmp(__s1, __s2, __n);
}

重签名运行!!


发现已经替换成功了!!!

但是,用ida或者hopper分析一下二进制文件,会发现调用的还是原来的strncmp符号:
10
说明如果进行模块检测的话,还是可以检测出来的~因为虽然符号查找替换了,但是实际上"外套"还是strncmp。所以,继续把外套也修改了!!!

修改这两个处:


修改后:
12

总结

对于动态绑定的外部引用符号,能动手脚的地方确实很多!!!

4 个赞

终究对__LINKEDIT段下手了

看不大懂,能不能用简单的一两句话介绍一下这是干什么的?

简单来说,就是直接修改Lazy Binding Info 的信息,使程序在绑定懒加载符号的时候,往自己写的动态库库里去查找,而自己写的动态库库,只需要实现该函数,并将该符号导出。从而实现hook~

3 个赞

:joy:没办法,原理不能违背~

是这个意思吗?
normal : caller → xx_func
fishhook : caller → xxfunc_hooked(in main executable) → xxfunc
yourhook: caller → xxfunc_hooked(in xx dylib) – xxfunc

normal : caller → xx_func
fishhook : caller → xxfunc_hooked(in main executable) → xxfunc
yourhook: caller → xx_func(in xx dylib)

2 个赞

以前有干过修改dylib的符号,然后再在自己executable重新实现该函数达到hook的目的

应该跟楼主类似

:+1::+1::+1:

image
哇~ 太强了~
学到了

图3是什么工具能分享一下不

010 Editor

楼主,你好,有一点没想明白,这种方式是不是最终要查找偏移,然后计算最后替换。

1 个赞

肯定要查找啊,不查找,修改的地方不对,就没有效果,而且还有可能会引起不适。

1 个赞