在没有能力自己写cydia hook 框架的情况下,想实现在某个函数任意地址(目标12字节+)hook,怎么办呢。
下面分享个小玩具,可以实现在函数中部hook。
步骤1:
首先找到你要hook那条语句地址,比如0x493B12
步骤2:
创建2个函数
其中$warp函数是构造好的函数。
static void $wap(){
asm volatile(
"stmfd sp!,{r0}\n" // 多压入一个空位,存放old_fun
"stmfd sp!,{r0-r12,r14}\n" // 保存现场
"bl %0\n" // 调用函数,返回值r0是old fun地址
"str r0,[sp,#0x38]\n" // 直接将r0放入上面的空位
"ldmfd sp!,{r0-r12,r14}\n" // 恢复现场
"ldmfd sp!,{pc}\n" // 将old_fun弹到pc
:
:"g"(realFun) //g 使用任何可用的寄存器和内存位置
:
);
}
realFun是hook业务函数,funRet为函数返回地址。
realFun的参数根据自己需要设置。
如果hook目标地址的有效数据不是r0-r3,比如r6, 那么可以在
“bl %0\n” 这句之前加一句
"mov r0, r6\n"
也可以访问栈变量 偏移自己估算。
"ldr r1, [sp,#0x48] \n"
然后是实际的hook业务代码。
static int funRet;
static int realFun(int r0, int r1, const char* r2, int r3) {
@autoreleasepool {
/* your code */
return funRet;
}
}
最后用MSHookFunction hook该地址,并将返回地址保存在funRet里。
void* addr = (void*)((dyldget_image_vmaddr_slide(0) + 0x493B12)|1);
MSHookFunction((void *)addr, (void *)$wrap, (void **)& funRet);
注意标志位寄存器未保存,你可以自己加上,64位代码同理。
另外的实例:
有很多人想hook stringWithFormat: 其实上面方法就可以
static int funRet;
static int realFun(id self, SEL _cmd, NSString* fmt, va_list argp) {
void* pre = __builtin_return_address(1); /* 第0层为warp地址 */
void* base = (void*)_dyld_get_image_header(0);
Dl_info info = {0};
if (dladdr(pre, &info) && info.dli_fbase == base) {
NSString* res = [[NSString alloc] initWithFormat:fmt locale:nil arguments:argp];
//printf("call_addr:%p\n \t fmt:%s\n \t res:%s\n", pre, [fmt UTF8String], [res UTF8String]);
//fflush(stdout);
NSLog(@"\n \t call_addr:%p\n \t fmt:%@ \n \t res:%@", pre, fmt, res);
[res release];
}
return funRet;
}
static void $warp(){
asm volatile(
"stmfd sp!, {r3} \n"
"stmfd sp!, {r0-r12, r14} \n"
"add r3, sp, #0x38 \n"
"bl %0 \n"
"str r0, [sp, #0x38] \n"
"ldmfd sp!, {r0-r12, r14} \n"
"ldmfd sp!, {pc} \n"
:
:"g"(realFun) //g 使用任何可用的寄存器和内存位置
:
);
}
MSHookMessageEx(objc_getMetaClass("NSString"), @selector(stringWithFormat:), (IMP)&$warp, (IMP*)&funRet);
注释的地方有点小问题需要花点时间修改一下,我没需求,懒得改了 :)。
理论上也可以hook objc_msgSend, 但是一定要注意循环调用。