objc_msgSend
是iOS里最重要的方法。
按道理来说,hook objc_msgSend就可以获取到app中所有调用的OC方法名和参数等。
所有的OC方法都需要经过objc_msgSend
方法消息转发寻找方法IMP。
参考hook objc_msgSend从而获得OC方法执行时间
objc_msgSend源码参考 苹果openSource。
此方法考虑效率,使用汇编编写,此方法大致上有4个关键步骤:
综上看,这个方法是一个"跳板"函数,并且它是线程安全的。
上述的4个步骤中,1,2,4是线程安全的,3步骤利用一些trick保障了线程安全,具体超出了本文的范围,可查看剖析objc_msgSend,其中对objc_msgSend方法的汇编实现,逐行讲解。
因为根据 #include <objc/message.h>
中objc_msgSend
函数的定义如下:
所以将它视为普通的C函数,利用 fishhook
/ cydia substrate
hook,结合va_list
分析传入的参数,应该就可以。
但是,不可以。通过据这里介绍,arm 64位上的va_list结构改变了,不能使用va_list了,因此此方法不能实现。
没有验证正确性。
既然利用va_list获取参数不可行。
参考objc_msgSend源码,因为在arm 64上,x0 - x8寄存器用于传参,所以结合汇编代码,应该可以hook,crx0715已经成功,具体可以查看它的讲解。
使用fishhook很好,因为fishhook是导出表hook,不会修改原方法的实现。
大概实现如下:
其中,保存寄存器,即 使用stp
命令,将寄存器保存到栈上。
提高栈帧SP,减小地址即可。因为iOS上内存栈帧由高地址向低地址,即后入的参数在内存低地址。
偏移寻址的标记后多了一个 !,在执行完上文的 stp 指令后,还会使 sp 寄存器也产生偏移。
同理,还原寄存器,使用ldp
命令降低栈帧SP。
既然fishhook可行,inline hook也可以。
区别在于inline hook会修改原始方法的前3条指令,备份函数会将这前3条指令备份下来,然后br跳转到原始方法的第四条指令。如此调用备份函数就好像调用原始objc_msgSend一样。具体inline hook的如何修改的原理就不赘述了。
具体流程和fishhook一致,不过经过实验有个坑,就是在before_objc_msgSend_inline
和after_objc_msgSend_inline
中暂时不能调用OC方法,因为会造成如下死循环。
由于参数保存在x0-x8寄存器中,那么在before_objc_msgSend
中对寄存器分析,就可以获取到入参。
这里有个坑,self参数,在一些app中会引入引用计数问题导致崩溃,还没有具体查询。作为TODO吧,现在
的printSpecificParam_fish参数入参需要修改一下,将self去除:
可以看到打印了所有的OC方法的方法名和参数。
完成代码可以参考github地址
id
_Nullable objc_msgSend(
id
_Nullable
self
, SEL _Nonnull op, ...)
id
_Nullable objc_msgSend(
id
_Nullable
self
, SEL _Nonnull op, ...)
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2022-1-25 11:13
被LeoW丨编辑
,原因: 更换图片