hook objc_msgSend方法以获取OC方法入参
2022-12-28 18:2:24
Author: 看雪学苑(查看原文)
阅读量:12
收藏
本文为看雪论坛优秀文章
看雪论坛作者ID:LeoW丨
按道理来说,hook objc_msgSend就可以获取到app中所有调用的OC方法名和参数等。所有的OC方法都需要经过objc_msgSend方法消息转发寻找方法IMP。参考hook objc_msgSend从而获得OC方法执行时间(https://github.com/cxr0715/hook_objc_msgSend)简说objc_msgSend方法
objc_msgSend源码参考 苹果openSource(https://opensource.apple.com/source/objc4/objc4-723/runtime/Messengers.subproj/objc-msg-arm64.s.auto.html)。此方法考虑效率,使用汇编编写,此方法大致上有4个关键步骤:① 获取传入的target的class。
② 找到class对应方法的缓存。
③ 尝试在缓存bukets中找到方法的具体实现。如果没有在cache中找到,则进入消息转发,深层查找,找到后,将方法和对应的IMP写入cache中。
④ 直接调用方法的实现。
综上看,这个方法是一个"跳板"函数,并且它是线程安全的。上述的4个步骤中,1,2,4是线程安全的,3步骤利用一些trick保障了线程安全,具体超出了本文的范围,可查看剖析objc_msgSend,其中对objc_msgSend方法的汇编实现,逐行讲解。想法1 视为普通C函数hook
因为根据 #include <objc/message.h> 中objc_msgSend函数的定义如下:id _Nullable objc_msgSend(id _Nullable self, SEL _Nonnull op, ...)
所以将它视为普通的C函数,利用 fishhook / cydia substrate hook,结合va_list分析传入的参数,应该就可以。但是,不可以。通过据这里介绍,arm 64位上的va_list结构改变了,不能使用va_list了,因此此方法不能实现。想法2 利用fish Hook+汇编 hook
参考objc_msgSend源码(https://opensource.apple.com/source/objc4/objc4-723/runtime/Messengers.subproj/objc-msg-arm64.s.auto.html),因为在arm 64上,x0 - x8寄存器用于传参,所以结合汇编代码,应该可以hook,crx0715已经成功,具体可以查看它的讲解。使用fishhook很好,因为fishhook是导出表hook,不会修改原方法的实现。其中,保存寄存器,即 使用stp命令,将寄存器保存到栈上。提高栈帧SP,减小地址即可。因为iOS上内存栈帧由高地址向低地址,即后入的参数在内存低地址。"stp x8, x9, [sp, #-16]!\n" \
偏移寻址的标记后多了一个 !,在执行完上文的 stp 指令后,还会使 sp 寄存器也产生偏移。想法3 利用inline hook + 汇编hook
既然fishhook可行,inline hook也可以。区别在于inline hook会修改原始方法的前3条指令,备份函数会将这前3条指令备份下来,然后br跳转到原始方法的第四条指令。如此调用备份函数就好像调用原始objc_msgSend一样。具体inline hook的如何修改的原理就不赘述了。具体流程和fishhook一致,不过经过实验有个坑,就是在before_objc_msgSend_inline和after_objc_msgSend_inline中暂时不能调用OC方法,因为会造成如下死循环。得到objc_msgSend的参数
由于参数保存在x0-x8寄存器中,那么在before_objc_msgSend中对寄存器分析,就可以获取到入参。// 打印调用的OC方法的对象和方法名,和最多两个参数。
void printSpecificParam_fish(id self, SEL _cmd, uintptr_t param1, uintptr_t param2,uintptr_t lr)
{
// 此方法中暂只能使用C方法,使用OC方法可能会导致寄存器异常导致崩溃,经测试,发生在相同方法调用相同方法时崩溃
// NSlog可以用
const char * className = object_getClassName(self);
const char * selector = sel_getName(_cmd);
//NSLog(@"class : %s, methodname : %s",className,selector);
if ( strcmp( selector, "fileExistsAtPath:" ) == 0) {
NSLog(@"class : %s, methodname : %s, param1 : %@",className,selector,param1);
}else {
NSLog(@"class : %s, methodname : %s",className,selector);
}
}
这里有个坑,self参数,在一些app中会引入引用计数问题导致崩溃,还没有具体查询。作为TODO吧,现在的printSpecificParam_fish参数入参需要修改一下,将self去除:void printSpecificParam_fish( SEL _cmd, uintptr_t param1, uintptr_t param2,uintptr_t lr)
{
const char * selector = sel_getName(_cmd);
NSLog(@"methodname : %s",selector);
}
最后
看雪ID:LeoW丨
https://bbs.pediy.com/user-home-889798.htm
*本文由看雪论坛 LeoW丨 原创,转载请注明来自看雪社区
文章来源: http://mp.weixin.qq.com/s?__biz=MjM5NTc2MDYxMw==&mid=2458489490&idx=2&sn=fa567d47b2b7a65289b6eb64b65d54c0&chksm=b18ea21886f92b0eaeb664cee226dce4676fdbc5ada6c6dda1aa5117f92176555b425ade987c#rd
如有侵权请联系:admin#unsafe.sh