Frida的Stalker功能介绍 – 作者:无情剑客Burning

Stalker是Frida的代码跟踪引擎.下面的内容来自Frida官网. 在加密解密,trace,代码定位方面用途还是挺大的.

Stalker is Frida’s code tracing engine. It allows threads to be followed, capturing every function, every block, even every instruction which is executed

类似Frida的工具还有QBDI.

QBDI

QuarkslaB Dynamic binary Instrumentation (QBDI) is a modular, cross-platform and cross-architecture DBI framework. It aims to support Linux, macOS, Android, iOS and Windows operating systems running on x86, x86-64, ARM and AArch64 architectures.

QBDI也能够和Frida完美结合.举个例子,打印导出函数aFunction对应的汇编代码.

varvm = newQBDI();
varstate = vm.getGPRState();
vm.allocateVirtualStack(state, 0x1000000);
varfuncPtr = Module.findExportByName(null, "aFunction");
vm.addInstrumentedModuleFromAddr(funcPtr);
varicbk = vm.newInstCallback(function(vm, gpr, fpr, data) {
varinst = vm.getInstAnalysis();
// Display instruction dissassembly
fmt= "0x"+ inst.address.toString(16) + " "+ inst.disassembly;
console.log(fmt);
returnVMAction.CONTINUE;
});
vm.addCodeCB(InstPosition.PREINST, icbk);
vm.call(funcPtr, [42]);

QBDI在Android平台下使用需要root以及关闭SELinux.

host$ adb root
host$ adb shell setenforce 0

参考

https://frida.re/news/2019/12/18/frida-12-8-released/ https://bbs.pediy.com/thread-264680.htm https://frida.re/docs/stalker/ Frida工作原理https://github.com/NMHai/frida-qbdi-fuzzer

Stalker实现

基本原理

Stalker是基于动态重新编译的代码跟踪器。 它将代码指令复制到内存中的另一个位置,在该位置对其进行调整以适应新位置并包括其他跟踪指令。 如果应用程序在原始位置检查其代码,则会发现该代码是完整无缺的,因为它是被篡改的代码的副本。

下图显示了函数如何在其新的内存位置进行transform以包括代码跟踪.在这里插入图片描述

练习一下

a.out程序源码:

#include<stdio.h>
#include<unistd.h>
static int fn(int n){
printf("number is %d\n",n);
}

int main(int argc, char ** argv){
intn = 0;
printf("fn is at %p\n",&fn);
while(1){
fn(n++);
sleep(3);
}
return0;
}

Frida脚本(重点在transform):

varapp = newModuleMap(isAppAddress);

Process.enumerateThreadsSync().forEach(function(thread){
Stalker.follow(thread.id,{
transform: function (iterator) {
varinstruction = iterator.next();
if(!app.has(instruction.address)) {
do{
iterator.keep();
} while((iterator.next()) !== null);
return;
}

do{
console.log(instruction.address+ ":"+ instruction);
iterator.keep();
} while((instruction = iterator.next()) !== null);

}
})
})

function isAppAddress(module) {
returnmodule.path.indexOf("a.out") != -1;
}

运行结果:在这里插入图片描述关闭系统的ASLR(地址随机化),对比指令和地址,显然是对应的。基于此我们可以定位到fn函数被执行了。在这里插入图片描述如果能够实施跟踪寄存器的值就更好了,这个也很容易实现,只需要添加下面的代码就可以了。基于此便可以实现类似IDA的trace功能。

iterator.putCallout((context) =>{
console.log(JSON.stringify(context))
})

stalker做了什么

从使用者的角度,每当执行一个基本快,stalker都会做以下事情:

1.对于方法调用,保存 lr 等必要信息2.重定位位置相关指令,例如:ADR Xd, label3.建立此块的索引,如果此块在达到可信阈值后,内容未曾变化,下次将不再重新编译(为了加快速度)4.根据 transform 函数,编译生成一个新的基本块 GumExecBlock ,保存到 GumSlab 。5.在上一过程中,可通过 void transform(GumStalkerIterator iterator, GumStalkerOutput output, gpointer user_data) 来读取,改动,写入指令。6.transform 过程中可通过 void gum_stalker_iterator_put_callout (GumStalkerIterator self,GumStalkerCallout callout, gpointer data, GDestroyNotify data_destroy) 来设置一个当此位置被执行到时的 callout。通过此 void callout(GumCpuContext cpu_context, gpointer user_data) 获取 cpu 信息。7.执行一个基本快 GumExecBlock,开始下一个基本快

trap功能

sleep函数定义:

相关函数:signal, alarm 头文件:#include <unistd.h> 定义函数:unsigned int sleep(unsigned int seconds); 函数说明:sleep()会令目前的进程暂停, 直到达到参数seconds 所指定的时间, 或是被信号所中断. 返回值:若进程/线程挂起到参数所指定的时间则返回0,若有信号中断则返回剩余秒数。

这里trap sleep函数, step into的时候不正是重新进入一个函数吗.

varsleep = newNativeFunction(
Module.getExportByName(null, 'sleep'),
'uint', ['int'],
{ traps: 'all'}
);

Stalker.follow({
events: {
call: true
},
onReceive: function (e) {
console.log(JSON.stringify(Stalker.parse(e)));
}
});

varresult = sleep(4);
console.log('sleep', result);

By setting the traps: ‘all’ option on the NativeFunction, it will re-activate Stalker when called from a thread where Stalker is temporarily paused because it’s calling out to an excluded range – which is the case here because all of frida-agent’s code is marked as excluded.

stalker实践


function start_stalker(mainThreadId, start, size){
Stalker.follow(mainThreadId, {
transform: function (iterator) {
varinstruction = iterator.next();
conststartAddress = instruction.address;
var isModule = startAddress.compare(start) >= 0&& startAddress.compare(start.add(size)) < 0;
do{
if(isModule){
console.log(instruction.address+ ":"+ instruction);
}
iterator.keep();
} while((instruction = iterator.next()) !== null);
}
});
}

写在最后

在接下来的文章中,会详细介绍Frida中Stalker的工作原理以及C模块。同时基于Frida的Stalker进行软件的破解。

有兴趣可以比较下下面这两段代码是否有区别。

do{
if(isModule){
console.log(instruction.address+ ":"+ instruction);
}
iterator.keep();
} while((instruction = iterator.next()) !== null);

if(!isModule) {
do{
iterator.keep();
} while((iterator.next()) !== null);
return;
}

do{
console.log(instruction.address+ ":"+ instruction);
iterator.keep();
} while((instruction = iterator.next()) !== null);

公众号

更多Frida相关的内容,欢迎关注我的微信公众号:无情剑客。在这里插入图片描述

来源:freebuf.com 2021-01-14 13:08:08 by: 无情剑客Burning

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享
评论 抢沙发

请登录后发表评论