版权归作者所有,如有转发,请注明文章出处:https://cyrus-studio.github.io/blog/
前言
在移动应用的安全加固中,OLLVM(Obfuscator-LLVM) 是一种常见的代码混淆与保护手段。它通过控制流平坦化、虚假控制流、指令替换等方式,使逆向分析者很难直接还原出原始算法逻辑。
然而,通过 Frida 提供的 Stalker 模块去动态分析让我们有机会对 OLLVM 的“黑盒逻辑”进行还原。
通过 Stalker 的指令级追踪能力,我们不仅能捕获 函数调用 、记录 调用参数 ,还可以打印 调用堆栈 ,最终逐步揭开 OLLVM 加固算法的真实运行流程。
比如,分析某个 so 中偏移为 0x23AD0 的加密函数。使用 IDA 反汇编 so,可以看到 so 中该函数做了混淆

使用了控制流平坦化混淆

Frida Stalker
Frida Stalker 是 Frida 提供的一个强大的指令级追踪引擎,它能够在目标进程运行时,动态捕获每一条指令的执行情况。与传统的函数级 hook 不同,Stalker 可以深入到 原生汇编层面 ,追踪寄存器变化、内存访问、函数调用关系等底层细节。
相关链接:
-
Stalker 介绍:https://frida.re/docs/stalker/
目前 Stalker 对于 arm64 支持比较好,但是 arm32 并不是很完善。

onCallSummary(函数调用摘要)
onCallSummary 是 Frida Stalker 提供的一个回调方法,用于在 函数调用层面 对收集到的执行数据进行归纳和统计。
它会将某一段追踪区间内的 调用信息进行汇总 ,例如:
-
哪些函数被调用了
-
每个函数被调用了多少次
-
调用分布和频率
简而言之,onCallSummary 像是 函数调用的统计报表 ,让你能在混淆代码的“噪音”中看清主干逻辑。
返回数据的结构(summary)通常类似于以下格式:
{
"函数地址": 调用次数
}
hook 目标函数,跟踪 call 事件并打印 call summary
function onCallSummary() {
// 目标 so
var soName = "libaes.so"
// 目标 so 基址
var baseAddress = Module.findBaseAddress(soName);
// 目标函数地址 = 基址 + 偏移
var targetAddr = baseAddress.add(0x23AD0);
console.log('Target function found at:', targetAddr);
Interceptor.attach(targetAddr, {
onEnter: function (args) {
console.log('Entering target function');
Stalker.follow(Process.getCurrentThreadId(), {
events: {
call: true, // 捕获函数调用
ret: false, // 捕获函数返回
exec: false, // 捕获指令执行
block: false, // 捕获基本块
compile: false // 捕获编译事件
},
onCallSummary: function (summary) {
console.log('Call Summary:');
Object.keys(summary).forEach(function (addr) {
var module = Process.getModuleByAddress(ptr(addr));
// 判断地址是否属于目标 so
if (module && module.name === soName) {
var offset = ptr(addr).sub(module.base);
console.log(`调用函数地址: ${ptr(addr)} | 模块: ${module.name} | 偏移: ${offset} | 次数: ${summary[addr]}`);
}
});
}
});
},
onLeave: function (retval) {
console.log('Leaving target function');
Stalker.unfollow(Process.getCurrentThreadId());
}
});
}
// setImmediate():确保代码在 Frida 环境准备好后执行。
setImmediate(onCallSummary)
启动 frida-server,附加到当前 app 并执行脚本
frida -H 127.0.0.1:1234 -F -l onCallSummary.js
输出如下:
Target function found at: 0x77fdf2ead0
[Remote::AndroidExample]-> Entering target function
Leaving target function
Call Summary:
调用函数地址: 0x77fdf62d50 | 模块: libaes.so | 偏移: 0x57d50 | 次数: 4
调用函数地址: 0x77fdf62d00 | 模块: libaes.so | 偏移: 0x57d00 | 次数: 1
调用函数地址: 0x77fdf62dd0 | 模块: libaes.so | 偏移: 0x57dd0 | 次数: 1
调用函数地址: 0x77fdf62f70 | 模块: libaes.so | 偏移: 0x57f70 | 次数: 3
调用函数地址: 0x77fdf62d80 | 模块: libaes.so | 偏移: 0x57d80 | 次数: 1
调用函数地址: 0x77fdf30f60 | 模块: libaes.so | 偏移: 0x25f60 | 次数: 1
调用函数地址: 0x77fdf62d30 | 模块: libaes.so | 偏移: 0x57d30 | 次数: 1
调用函数地址: 0x77fdf62e00 | 模块: libaes.so | 偏移: 0x57e00 | 次数: 1
调用函数地址: 0x77fdf30b8c | 模块: libaes.so | 偏移: 0x25b8c | 次数: 1
调用函数地址: 0x77fdf34580 | 模块: libaes.so | 偏移: 0x29580 | 次数: 10
调用函数地址: 0x77fdf62ce0 | 模块: libaes.so | 偏移: 0x57ce0 | 次数: 1
调用函数地址: 0x77fdf62db0 | 模块: libaes.so | 偏移: 0x57db0 | 次数: 1
调用函数地址: 0x77fdf62d60 | 模块: libaes.so | 偏移: 0x57d60 | 次数: 13
调用函数地址: 0x77fdf303cc | 模块: libaes.so | 偏移: 0x253cc | 次数: 1
调用函数地址: 0x77fdf62d10 | 模块: libaes.so | 偏移: 0x57d10 | 次数: 1
调用函数地址: 0x77fdf62de0 | 模块: libaes.so | 偏移: 0x57de0 | 次数: 1
调用函数地址: 0x77fdf30bc8 | 模块: libaes.so | 偏移: 0x25bc8 | 次数: 1
调用函数地址: 0x77fdf62eb0 | 模块: libaes.so | 偏移: 0x57eb0 | 次数: 1
调用函数地址: 0x77fdf62d40 | 模块: libaes.so | 偏移: 0x57d40 | 次数: 1
调用函数地址: 0x77fdf62cf0 | 模块: libaes.so | 偏移: 0x57cf0 | 次数: 2
调用函数地址: 0x77fdf62dc0 | 模块: libaes.so | 偏移: 0x57dc0 | 次数: 1
调用函数地址: 0x77fdf62d70 | 模块: libaes.so | 偏移: 0x57d70 | 次数: 1
调用函数地址: 0x77fdf62df0 | 模块: libaes.so | 偏移: 0x57df0 | 次数: 2
调用函数地址: 0x77fdf62ec0 | 模块: libaes.so | 偏移: 0x57ec0 | 次数: 1
调用函数地址: 0x77fdf62da0 | 模块: libaes.so | 偏移: 0x57da0 | 次数: 2
onReceive(接收捕获的事件)
onReceive 是 Frida Stalker 的另一个重要回调方法,用于 逐条接收捕获到的事件 。与 onCallSummary 不同,它不会进行统计汇总,而是将底层指令级别的执行轨迹实时发送到回调中。
在 onReceive 中,你能拿到最原始的 执行事件数据 ,例如:
-
每条指令的执行地址
-
寄存器变化

最低0.47元/天 解锁文章
5195

被折叠的 条评论
为什么被折叠?



