一、目的
在分析so中的算法时,Trace和Debug是常用的本事。
相识一些调试器原理的同学都知道,Trace和Debug必要修改原始代码加上个int 3,来激活调试器。
如许有些App可以依赖检测关键代码来判定是否被调试。
大概你会说,我们可以patch掉检测代码,前次飞哥遇到一个狠人app,B去检测A处的代码,C去检测B处的代码,D去检测C处的代码,…… 反正套娃了 5-6次,我就瓦解了。
还好有 Stalker
Stalker是基于动态重新编译的代码跟踪器。 它将代码指令复制到内存中的另一个位置,在该位置对其举行调解以顺应新位置并包罗其他跟踪指令。 假如应用步伐在原始位置查抄其代码,则会发现该代码是完备无缺的,由于它是被窜改的代码的副本。
二、步调
Stalker Trace Code
我们还是从前次分析的某小说App来演示 Stalker 的用法, http://91fans.com.cn/post/readbookone/
Stalker在ARM32下貌似不大好使,这里我们在ARM64下做的测试
function trace_entry(baseAddr,tatgetAddr){ Interceptor.attach(tatgetAddr, { onEnter: function(args){ console.log("enter tatgetAddr===================================================================="); this.pid = Process.getCurrentThreadId(); Stalker.follow(this.pid,{ events:{ // 临时不必要这些 events call:false, ret:false, exec:false, block:false, compile:false }, onReceive:function(events){ }, transform: function (iterator) { var instruction = iterator.next(); const startAddress = instruction.address; // 从ida内里 找到 Java_com_baidu_searchbox_NativeBds_dae1 函数的 代码 在 0xE84 和 0x126C 之间 var isModule = startAddress.compare(baseAddr.add(0xE84)) >= 0 && startAddress.compare(baseAddr.add(0x126C)) < 0; do{ if (isModule){ console.log(instruction.address.sub(baseAddr) + "\t:\t" + instruction); } iterator.keep(); } while ((instruction = iterator.next()) !== null); }, onCallSummary:function(summary){ } }); },onLeave: function(retval){ Stalker.unfollow(this.pid); console.log("retval:"+retval); console.log("leave tatgetAddr===================================================================="); } });}必要表明的是 transform 函数, 其他的 events、 onReceive 和 onCallSummary 现在我们还用不到,它们可以做block 和 call之类的跟踪分析。
transform 遍历执行了当前的每一行汇编指令,默认体现的地址是实际内存地址,我们 instruction.address.sub(baseAddr) 减去了一个so的基地址,得到的就和ida中体现的地址同等了
加Hook
光体现指令没啥意思,指令看上去还没有ida悦目呢。
但是这个是活的执行,我们可以加个hook,通过ida分析,感觉差不多 0xFB8的时间是计算key的长度。
以是我们在这里加个打印
do{ if (isModule){ console.log(instruction.address.sub(baseAddr) + "\t:\t" + instruction); if(instruction.address.sub(baseAddr) == 0xfb8){ iterator.putCallout((context) => { var string = Memory.readCString(context["x21"]); console.log("#### key = " + string) }) } } iterator.keep();} while ((instruction = iterator.next()) !== null);如许就可以顺遂的打印出本次aes算法中用的key了
0xfb4 : add x21, x21, x0, lsr #10xfb8 : mov x0, x210xfbc : ldr x22, [x8, #0x580]0xfc0 : bl #0x7a751c55f0#### key = D0CD8B760CE07BC30xfc4 : mov x1, x00xfc8 : mov x0, x200xfcc : blr x220xfd0 : ldr x8, [x20]更帅的Trace
我们如今把握了打印指令和寄存器值的方法。不外如许打出来太不帅了,IDA的Trace只会打印被修改的寄存器的值。
还好有大佬已经写好了
https://github.com/bmax121/sktrace
先 把我们代码中 Stalker 部门解释掉,由于要干掉登录和vip,以是先启动我们的js
frida -U -l bqgst.js -f com.bqg.ddnoverl --no-pause然后启动 sktrace
python sktrace.py -m attach -l libTxtFormatter.so -i Java_com_baidu_searchbox_NativeBds_dae1 com.bqg.ddnoverl-l 指定so名称
-i 指定要Trace的函数名
末了跟的是包名大概app名称
0x7a2e289e9c add x29, sp, #0x50 ; x29=0x7a7bdb9d48->0x7a7bdb9c500x7a2e289ea0 mov x20, x0 ; x20=0x0->0x7c02192b700x7a2e289ea4 ldr x8, [x20] ; x8=0x79a1c49061b6ff43->0x7b3fe0e1980x7a2e289ea8 mov x1, x2 ; x1=0x9e30a688->0x7a7bdb9d180x7a2e289eac mov x2, xzr ; x2=0x7a7bdb9d18->0x00x7a2e289eb0 mov x19, x3 ; x19=0x7cb23102a0->0x7a7bdb9d1c0x7a2e289eb4 ldr x8, [x8, #0x548] ; x8=0x7b3fe0e198->0x7b3fbe20e00x7a2e289ebc ldr x8, [x20] ; x0=0x7c02192b70->0x7b522130f0, x1=0x7a7bdb9d18->0x7dee5ef7cc, x3=0x7a7bdb9d1c->0x10, x4=0x7a7bdb9d58->0x0, x8=0x7b3fbe20e0->0x7b3fe0e198, x11=0x40->0x522130f7, x12=0xb->0xffff00000eff, x13=0x380->0xafba2fb8, x16=0x7a2e289e94->0x7dd27ad7f8, x17=0x7cb23102a0->0x7dee554c78, x30=0x7deaefdc0c->0x7a2e289ebc0x7a2e289ec0 mov x21, x0 ; x21=0x0->0x7b522130f00x7a2e289ec4 ldr x22, [x8, #0x580] ; x22=0x7abbab01b8->0x7b3fbe55040x7a2e2895f0 adrp x16, #0x7a2e29a000 ; x16=0x7dd27ad7f8->0x7a2e29a000, x30=0x7a2e289ebc->0x7a2e289ecc0x7a2e2895f4 ldr x17, [x16, #0xfe8] ; x17=0x7dee554c78->0x7dee5619800x7a2e2895f8 add x16, x16, #0xfe8 ; x16=0x7a2e29a000->0x7a2e29afe8如今打出来的,就有ida Trace内味了。
同学们还可以模仿下sktrace的代码,搞个更趁手的兵刃出来。
三、总结
矛与盾的战斗是永不绝息的,无痛hook,硬件断点应该是趋势。
一个趁手的调试器非常难过,一个能被Debug的App是藏不住秘密的。
万事纷纷一笑中,渊明把菊对金风抽丰 |