如何检测被Cydia Substrate的MSHookFunction挂钩的Android原生函数
检测Android平台MSHookFunction挂钩行为的实用方法
针对你遇到的「用MSHookFunction挂钩后,对比内存.text段与SO文件一致,但确认真实已被挂钩」的问题,以下是具体的检测思路和方法:
1. 排查PLT/GOT表的异常修改
MSHookFunction在处理动态链接函数时,常通过修改PLT/GOT表项实现挂钩,而非直接改写原函数的.text段——这就是你对比.text段无差异的核心原因。
- 操作方式:解析目标SO文件的ELF结构,提取PLT段的原始表项;再通过Frida读取内存中对应模块的PLT区域,逐一对比表项地址。若某条PLT条目指向的地址不在原模块的.text段范围内,大概率是被hook了。
- 示例Frida代码片段:
const moduleName = "libyour.so"; const mod = Process.getModuleByName(moduleName); const pltStart = mod.plt; const pltSize = mod.pltSize; for (let i = 0; i < pltSize; i += Process.pointerSize) { const pltAddr = pltStart.add(i); const targetAddr = pltAddr.readPointer(); if (!mod.contains(targetAddr)) { console.log(`可疑PLT条目:${pltAddr} -> ${targetAddr}`); } }
2. 追踪函数调用的实际跳转地址
直接监控目标函数的调用行为,看实际执行的地址是否与原函数起始地址一致:
- 用Frida拦截函数调用,打印当前执行的指令地址:
const targetFunc = ptr("0x12345678"); // 替换为原函数实际地址 Interceptor.attach(targetFunc, { onEnter: function(args) { const currentPc = this.context.pc; if (currentPc !== targetFunc) { console.log(`函数被hook,实际执行地址:${currentPc}`); } } });
若currentPc不等于原函数地址,说明调用被重定向到了trampoline或hook函数。
3. 扫描内存中的trampoline特征
MSHookFunction生成的trampoline通常包含「原函数前几条指令 + 跳转回原函数剩余部分」的结构,且所在内存页多为**可写可执行(RWX)**权限:
- 操作步骤:
- 枚举进程中所有RWX权限的内存页;
- 在每个页内搜索包含原函数开头指令的内存块;
- 检查块末尾是否存在跳转回原函数偏移位置的指令。
- 示例Frida代码片段:
const targetFunc = ptr("0x12345678"); const originalCode = Instruction.parse(targetFunc).toString(); Process.enumerateRanges('rwx').forEach(range => { const scan = Memory.scan(range.base, range.size, originalCode); while (scan.next()) { const matchAddr = scan.address; const jumpInst = Instruction.parse(matchAddr.add(4)); if (jumpInst.mnemonic === 'bx' && jumpInst.op1.toString() === targetFunc.add(4).toString()) { console.log(`找到trampoline:${matchAddr}`); } } });
4. 枚举Substrate的内部hook链表
Cydia Substrate会维护一个全局的hook链表,记录所有被MSHookFunction挂钩的函数:
- 通过Frida查找Substrate模块中的相关符号(比如内部链表结构变量),遍历链表即可获取所有被hook的函数信息。
内容的提问来源于stack exchange,提问作者Ken Kem
相关产品推荐
相关产品推荐

