一次失败的无痕hook尝试

无痕hook

前段时间网上冲浪,发现了PC逆向中的一种无痕hook,基于VT的读写 执行分离的技术。
可以看下面的介绍

VT中,host通过exit事件监控guest的一举一动,稍微“大”一点的动作(进程切换、读写msr、执行cpuid等)都会在guest触发exit,回到host的handle函数处理,在VT框架中,host对guest有绝对的监控和处理的全力,所以业界通常把VT框架下的程序称为-1环,比操作系统的0环都低,很形象地说明了host的权限范围;VT中非常重要的一个模块EPT,gtest中任何读写实际物理内存的操作都需要通过EPT转换一遍,这个转换环节一旦出任何问题,导致转换到了错误的物理地址,都会导致guest读写物理内存失败;本文利用这个原理,让guest对host同一块物理地址的读和写分离:第三方程序(比如CE、PChunter、某些程序自带的CRC检测功能、windows自带的patch guard等)读物理页的时候返回一个结果,执行的时候又返回一个结果,以此骗过第三方程序对物理页内容的检查,这就是业界俗称的shadow walker。此技术可用于无痕hook!

  • 简单来说,就是针对内容的读写操作和执行操作进行监控,分别返回不同的内容。那么,在移动端,能否通过信号捕获的机制去实现次功能呢。比如将text段的代码权限设置为“只执行”,此时运行的是hook代码,而当有程序去读取text段内容的时候(比如做CRC校验),因为hook函数被设置成了“只执行”,那么此时将触发异常,通过捕获该异常信号,把text段内容进行还原并将权限设置为“只读”。这样当读取text段内容的时候只能获得原来text段的内容,当后续执行代码的时候,又将触发“只读”权限的访问异常,再次捕获此信号,将text段内容改为hook后的内容,并将权限置为“只执行”。

Dobby Plugin

有了大致思路,下面开始实现此类hook。这里选择对DobbyHook框架进行改造。之前读过Dobby的源码,感觉写的很不错,又花些时间重温了一下,两个字,优秀!另外Dobby是支持Plugin拓展的,无痕hook可以以一种plugin的方式加入到框架中。


Demo测试

先快速实现一个demo验证思路的可行性。

DobbyHook(dladdr, dladdr_fake, &dladdr_org);
kern_return_t kr = mach_vm_protect(mach_task_self(), dladdr, 1, true, VM_PROT_EXECUTE);

修改dladdr的内容权限,将其置为只执行,但其实压根不行 :sweat_smile:这里有2个坑。

  1. mach_vm_protect修改权限估计是以内存页为单位,这种粒度对于函数hook来说还是太大了,只想修改dladdr的内存权限,但是连同跟dladdr在同一内存页的其他函数的权限也一起被修改了。。
  2. mac压根不支持“只执行”权限的text段,这种权限的text段代码无法正常执行,会报错EXC_BAD_ACCESS。查看头文件可以发现如下说明
    image
    也就是说,想让代码执行,权限必须是VM_PROT_READ|VM_PROT_EXECUTE,对于这种权限的text段,那也必然是可读的。。

但是,依然可以将函数的内容设置为只读,让它可以正常被读取,而执行的时候,通过捕获权限异常,去替换成hook代码再执行。(但是想想就知道这方案效率太低,而且执行的时候也是可读的。。执行完还得把权限改回只读)
对于实现无痕hook,貌似还有一丝机会,继续探索。直到后面的信号捕获环节。


信号捕获

开始进行关键的信号捕获,如果使用调试器进行调试,需要注意调试器会将信号拦截,导致调试的时候信号捕获逻辑不执行。因为我使用的是xcode,可以通过命令pro hand -p true -s false SIGSEGV对特定的信号进行配置,使自定义的信号捕获逻辑生效。

结果,拦截了官方的31个信号,没一个能捕获这种异常。。一开始以为这属于SIGSEGV异常,可压根就不生成这种信号,也就不从捕获了。。至此,无痕hook方案探索以失败告终。

至于为什么会想到用信号捕获去实现hook,那是因为之前看见别人用信号捕获实现过单指令hook,不愁短函数啦。dobby虽然也有单指令hook的选项,但实现的原理是利用b指令跳转,而b指令的跳转范围太小了。。

3 个赞