这是一个学习性质的项目,研究如何通过 Frida 的 CModule 实现高性能指令追踪。虽然 Frida 官方已经有了 frida-itrace 这个成熟方案,但直接用 CModule 手写可以更深入理解 Stalker 的工作原理和性能优化思路。
项目地址:c2aK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6$3N6%4N6%4i4K6u0V1k6s2u0G2K9h3c8Q4x3V1k6U0i4K6u0V1M7$3E0@1M7X3q4U0k6b7`.`.
原始的 JavaScript 实现在进行密集指令追踪时存在严重的卡顿问题,主要体现在:
通过 C 实现后,性能提升约两三个数量级(没细测,不过提升明显),实现了无卡顿的实时追踪。
示例:追踪一个执行 10,000 次的循环
每次切换需要:
还是很大的性能消耗
既然无法消除所有 JS 交互,那就:
通过不同的分隔符区分两种消息:
指令信息(tab 分隔):
寄存器信息(管道分隔,34 个字段):
性能提升:约两三个数量级(没细测,不过提升明显)
为什么慢?
怎么改?
效果?
function stalkerTraceRange(tid, base, size) {
Stalker.follow(tid, {
transform: (iterator) => {
do {
iterator.keep();
if (isModuleCode) {
iterator.putCallout((context) => {
send({...})
})
}
} while (iterator.next() !== null);
}
})
}
function stalkerTraceRange(tid, base, size) {
Stalker.follow(tid, {
transform: (iterator) => {
do {
iterator.keep();
if (isModuleCode) {
iterator.putCallout((context) => {
send({...})
})
}
} while (iterator.next() !== null);
}
})
}
目标代码执行到被 trace 的指令
↓
Callout 触发:Native 代码暂停
↓ 切换到 JS 引擎
执行 JS 回调函数
↓ JSON 序列化 context
调用 send()(进程间通信)
↓ 切回 Native
恢复目标代码执行
目标代码执行到被 trace 的指令
↓
Callout 触发:Native 代码暂停
↓ 切换到 JS 引擎
执行 JS 回调函数
↓ JSON 序列化 context
调用 send()(进程间通信)
↓ 切回 Native
恢复目标代码执行
void transform(GumStalkerIterator *iterator,
GumStalkerOutput *output,
gpointer user_data)
{
cs_insn *insn;
gpointer base = *(gpointer*)user_data;
gpointer end = *(gpointer*)(user_data + sizeof(gpointer));
while (gum_stalker_iterator_next(iterator, &insn))
{
gboolean in_target = (gpointer)insn->address >= base &&
(gpointer)insn->address < end;
if(in_target)
{
log("%p\t%s\t%s", (gpointer)insn->address,
insn->mnemonic, insn->op_str);
gum_stalker_iterator_put_callout(iterator,
on_arm64_before,
(gpointer)insn->address,
NULL);
}
gum_stalker_iterator_keep(iterator);
if(in_target)
{
gum_stalker_iterator_put_callout(iterator,
on_arm64_after,
(gpointer)insn->address,
NULL);
}
}
}
void transform(GumStalkerIterator *iterator,
GumStalkerOutput *output,
gpointer user_data)
{
cs_insn *insn;
gpointer base = *(gpointer*)user_data;
gpointer end = *(gpointer*)(user_data + sizeof(gpointer));
while (gum_stalker_iterator_next(iterator, &insn))
{
gboolean in_target = (gpointer)insn->address >= base &&
(gpointer)insn->address < end;
if(in_target)
{
log("%p\t%s\t%s", (gpointer)insn->address,
insn->mnemonic, insn->op_str);
gum_stalker_iterator_put_callout(iterator,
on_arm64_before,
(gpointer)insn->address,
NULL);
}
gum_stalker_iterator_keep(iterator);
if(in_target)
{
gum_stalker_iterator_put_callout(iterator,
on_arm64_after,
(gpointer)insn->address,
NULL);
}
}
}
static void
on_arm64_before(GumCpuContext *cpu_context, gpointer user_data)
{
}
static void
on_arm64_after(GumCpuContext *cpu_context, gpointer user_data)
{
if (cpu_context) {
log("%p|%llx|%llx|%llx|%llx|%llx|%llx|%llx|%llx|%llx|%llx|"
"%llx|%llx|%llx|%llx|%llx|%llx|%llx|%llx|%llx|%llx|"
"%llx|%llx|%llx|%llx|%llx|%llx|%llx|%llx|%llx|"
"%llx|%llx|%llx|%llx",
user_data,
cpu_context->x[0], cpu_context->x[1], cpu_context->x[2], cpu_context->x[3],
cpu_context->x[4], cpu_context->x[5], cpu_context->x[6], cpu_context->x[7],
cpu_context->x[8], cpu_context->x[9], cpu_context->x[10], cpu_context->x[11],
cpu_context->x[12], cpu_context->x[13], cpu_context->x[14], cpu_context->x[15],
cpu_context->x[16], cpu_context->x[17], cpu_context->x[18], cpu_context->x[19],
cpu_context->x[20], cpu_context->x[21], cpu_context->x[22], cpu_context->x[23],
cpu_context->x[24], cpu_context->x[25], cpu_context->x[26], cpu_context->x[27],
cpu_context->x[28],
cpu_context->fp, cpu_context->lr, cpu_context->sp, cpu_context->pc);
}
}
static void
on_arm64_before(GumCpuContext *cpu_context, gpointer user_data)
{
}
static void
on_arm64_after(GumCpuContext *cpu_context, gpointer user_data)
{
if (cpu_context) {
log("%p|%llx|%llx|%llx|%llx|%llx|%llx|%llx|%llx|%llx|%llx|"
"%llx|%llx|%llx|%llx|%llx|%llx|%llx|%llx|%llx|%llx|"
传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!