关于hook
- OC方法可hook,得益运行时特性,方法调用底层都是
msg_send(id,SEL)
的形式,这为我们提供了交换IMP的机会
函数地址偏移量
- C函数在编译连接时就确定了函数指针的地址偏移量offset,offset在编译好的可执行文件是固定的,而可执行文件每次被重新装载到内存中时被系统分配的起始地址是不断变化的(lldb用imagelist可以获取
- 运行中的今天静态函数指针地址等于offset+MachO文件在内存中的首地址
- 这一块说明,如果了解过逆向或者app可执行文件相关的知识的话,会相对更容易理解。更白话一点说,拿打包完的app举例,装载代码的可执行文件已经固定不变了,函数在该文件相对位置已经确定了。只要确定整个可执行文件被加载时的起点位置,就能知道该函数的具体位置
fishhook
- 应用范围:无法hook内部/自定义的C函数,只能hook Mach-O外部(共享缓存库中)的函数
- 原理:利用MachO的动态绑定机制,苹果的共享缓存库不会被编译进MachO文件,而是在动态链接时重新绑定。
PIC
- 苹果采用PIC(position-independent code)技术成功让C的底层能有动态的表现:
- 编译时,Mach-O中的_DATA段的符号表中,为每一个被引用的系统C函数建立一个指针(8字节数据,放的是0),这个指针用于动态绑定时重定位到共享库中的函数实现
- 运行时,当系统C函数第一次调用时,会动态绑定一次,然后将Mach-O中的_DATA段符号表中对应的指针,指向外部函数(其在共享库中的实际内存地址)
fishhook利用PIC做了两个操作
- 将指向系统方法(外部函数)的指针重新进行绑定指向内部函数/自定义C函数
- 将内部函数的指针在动态链接时指向系统方法的地址
- 交换指针指向
fishhook的适用场景:
- 主要应用在安全防护领域
- 基础的动态调试逆向中,最常见的就是定位到目标方法之后,通过runtime中的几种方法交换方法,实现代码注入的目的
- fishhook可以拦截系统外部的C函数,自然可以hook到runtime中的所有方法
- 将所有可能用来篡改OC方法实现的runtime API,都用fishhook拦截掉,使其无法用代码注入的方式成功hook进行防护
- 实际引用中,自己的项目可能是要用到这些函数的,需要设计相应的白名单方案,并且在检测到是被第三方非法hoo时,直接调用exit(0)这类接口终止掉进程
源码摘要
-
一些说明,都注释进去了
-
补充:
- rcd_rebindings_entry:可以看出内部存在链表结构
- 对于非懒加载符号表,dyld会在动态链接时就链接动态库。对于懒加载符号表,dyld在运行时函数第一次被调用时动态绑定一次
- Debug -> Debug Workflow -> Always Show Disassembly:查看汇编断点
- 根据字符串 MachOView,查找该字符串在String Table->symbols的位置,……经过一系列查找,获得该指针最终的偏移量
参考
- 本文主要是源码学习及参考以下文章的提炼总结以及白话补充:
- 1、fishhook 的实现原理浅析
- 2、fishhook 使用场景&源码分析
- 3、objc_msgSend Hook 精简学习过程