Fishhook 是否无法 hook 到所有的 mach_msg

需求:
我使用 fishhook hook 系统的 mach_msg 方法,但是我发现使用了 fishhook hook 之后,很多系统库(如:WebKit) 中使用的 mach_msg 并没有被 hook 到。

操作步骤:

  1. 在 main 函数中 hook mach_msg 方法
  2. 通过 Xcode 在系统原来的 mach_msg 打断点
  3. 在自己的 hook_mach_msg (在 hook_mach_msg 会调用原 mach_msg)方法处打断点
  4. 运行后发现很多调用 mach_msg 的时,并没有走 hook_mach_msg 方法。

以 WebKit 中的 mach_msg 为例:

由于 WebKit 的源码开源,所以我可以找到在 WebKit 的 IPC::Connection::receiveSourceEventHandler 方法中,有调用了 mach_msg 方法,我通过 Xcode 断点了该方法,如下图:


在上图红色方框部分,做了一层跳转

然后又做了一层跳转

在第二层跳转时,就找到了 mach_msg 方法。并没有跳转到我自己定义的 hook_mach_msg 中。

本人是新人,对于一些底层有点似懂非懂,在我的理解中,mach_msg 对于 WebKit 来说应该是属于外部符号,那么在调用时,应该会通过 la-symbol-ptr 去确定具体的 mach_msg 地址。那么我应该是可以同 fishhook 进行 hook 才对。想问下各位大佬,是不是由什么知识点是我没有了解到的? 导致了 WebKit 中的 mach_msg 无法进行 hook?

环境:
Xcode 10.3
iOS 12.4.1
macOS: 10.15 Beta (19A487m)

如果上面有什么描述不清楚的地方,请在下发留言,我尽力描述清楚,谢谢各位。

试一下在+load()方法中hook一下?

fishhook的原理是本执行文件的外部符号hook,并不是本进程的的这个外部符号hook。所以其他模块的mach_msg是无法进入你的hook代码的。

如果是这样,那么 fishhook 里面为什么需要遍历所有的 mach-o ,修改所有 mach-o 中的 la-symbol-ptr ,直接修改第一个 mach-o 中 la-symbol-ptr 不就好了吗?

刚刚试了下,也是不行的。

补充一些 hook 成功的调用路径:


成功的 hook 是会调用 symbol stub for: mach_msg

跳转到一个中转函数

然后就会跳转到我自己定义的 hook_mach_msg 里面了

嗯,这点我没注意。fishhook能指定当前的所有模块hook。你现在你不能hook的模块有多少?

这个没具体统计过,不过直接断点 mach_msg ,通过堆栈可以发现,挺多都没有经过 hook_mach_msg 的

图一:没有经过 hook_mach_msg

图二:经过 hook_mach_msg

我进入 fishhook 的源码打印过,确定了 WebKit 等相关模块的 la-symbol-ptr 是有被替换的

从你的执行路径上来看,WebKit这个模块里面的mach_msg函数好像是直接调用的,并不是像一般外部符号那样先跳到stub代码,然后根据la-symbol-ptr来跳转。所以这也是fishhook不能hook的原因。
至于为什么会出现这种情况,我猜测应该和dyld_cache有关?或者是由于这个模块针对这个函数并不是lazy的方式

我调试了下mach_msg函数,从我的结果来看dyld_cache里面的CoreFoundationIOKit都是直接调用的。

难道只要是 dyld_cache 里面的库,他们的之间的外部调用都会变成直接调用的吗?但是每次加载库进入内存的地址都是不一致的吧?怎么能做到直接调用呢?

dyld_cache是一个比较复杂的复合macho结构,也就不存在一般所说的外部调用,并不能按照一个模块来分析。不仅是mach_msg,像里面的objc_msgsend好像也是这样处理的。具体的话你可以用ida加载dyld_cache去分析一下。如果你单独加载dyld_cache里面的模块很多函数调用都是找不到的,原因就在与根本不是像外部符号那样用stub去调用的。所以在ida7.0之前有一个dsc_fix插件去恢复这些符号的原理就是创建一个外部调用的段,然后让ida能够去解析这些调用时外部的符号调用。当然在ida7.0以后自身就支持了对单个dyld模块的函数符号修复,原理应该和那个插件也差不多。

好的,谢谢,我去看下这方面的相关资料,这前我也怀疑过 dyld_cache 的原因,但还以为 dyld_cache 里面的各个模块是相互独立的,只是都提前先加载进内存中,现实看来不是这样的。 有关于 dyld_cache 详细结构或原理的资料的可以提供下吗?发现这方面网上很少资料。谢谢:laughing:
这样的话,对于这部分直接调用的,在非越狱机器上,应该是没办法 hook 到的吧?

资料确实很少,能够成功加载整个dyld_cache的人应该都不多。非越狱机器上应该很难实现hook,另外就算是越狱机器上,很多inlinehook框架都或许会失败。因为inlinehook自身也调用了mach_msg函数。

好的,谢谢大佬的解惑。

按照这样的原理,发现对于系统库来说,fishhook 的作用好像不大,里面的函数基本都 hook 不到。