现在的混淆越来越恶心了,平坦化+间接跳转已经是标配。传统的单步调试早已难以招架,去混淆在面对多层间接跳转+编译优化时也显得力不从心。于是逆向分析中很多时候需要借助trace工具来辅助。
目前主流的trace工具有三种:
Unidbg:使用范围广,上手简单。问题是需要补环境,有时候需要补充大量环境才能勉强跑通。就算跑通了,也因为是模拟执行,存在大量数据与真机数据不一致的问题。
stalker:frida配套的trace工具,稳定性较差,经常各种崩溃。不过最近有大佬开源了GumTrace,在稳定性上提升了许多。
qbdi:qbdi是基于动态重编译技术的插桩工具。具体原理可参考:QBDI原理详解
qbdi的优点是稳定+真机trace。缺点是上手复杂,需要自己引入编译qbdi相关的库。
QTrace封装了qbdi库,并修复了虚拟栈崩溃bug,优化了日志系统,引入了灵活的自定义监控框架等,可以方便逆向工作者更好的定制自己的trace工具。
QTrace为android studio项目,主要开发语言为C/C++。逆向工作者需要在使用中根据自己的实际需要来修改c++代码。
最终会编译生成libnativelib.so,注入这个so到目标进程将会自动实现hook和trace功能。
项目地址:e80K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6*7k6%4V1H3P5o6l9I4i4K6u0r3f1g2c8J5j5h3y4W2
将libnativelib.so注入目标进程后,他会自动根据config中的libname,去/data/local/tmp目录下寻找对应的so,然后遍历进程的maps文件,在内存中进行比对,找到要trace的目标so。
通过shadowhook,hook目标函数。当程序执行到目标函数时,可以选择将控制流转移给qbdi进行trace,也可以只过滤特定的参数进行trace,而对其他的参数则执行原函数。这对trace高并发,或vmp函数入口很有帮助。
例如,只有当X2为0x975dbf9a时,我们才进行trace,其他情况直接返回执行。
trace的过程中,会自动解析br,blr指令。如果目标地址命中自定义的libc函数,jni函数,或so本身的其他函数,则会根据你的定制规则进行解析。‘
内置了以下函数的监控:
你也可以根据自己的逆向需求,增添更多的函数监控。
所有的监控函数都符合以下格式:
例如libc_access:
会通过QBDI宏获取x0参数,然后在logcat中打印日志,同时写入log文件。
以小某书的libtiny.so为例,讲述QTrace的基本用法:
首先需要将libtiny.so push到/data/local/tmp目录。
然后修改native_main.cpp中的void config() 函数。指定目标so名称和trace的函数。我们先trace jni_onload函数
由于jni_onload函数有2个参数,所以我们选择hook_and_trace_arg2,修改void trace()函数:
执行android studio的make project,将生成的目标so,push到/data/local/tmp
使用项目自带的inject.js注入so,在logcat中过滤QTrace,可以看到实时的trace日志:

由于内置了RegisterNatives的解析,所以jni函数的注册自动打印了出来。函数名a,偏移0x173148
将trace文件pull到本地,用010editor查看:
有完整的指令trace和内存读写监控:

同时日志里搜索[log],也能看到jni注册信息:

a函数即其native入口函数:

这个函数有很多调用,会根据第一个参数的不同,执行不同的任务:

我们只关心其签名是如何生成的。
重新修改void config()函数,我们trace a函数:
由于这个函数在native有4个入参,我们需要修改trace函数,使用hook_and_trace_arg4
同时我们只关心签名,所以在hook_and_trace_arg4函数中,对X2进行过滤:
查看日志。可以看到核心的签名都已经出现:

将trace文件pull到本地,可以看到完整的指令,内存变化。并且所有日志也在trace文件中:

传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 1天前
被乐子人编辑
,原因: 格式