2万字长文,写了给我累死了,本来可以直接随便写写就发的,但是还是写好一些吧,唉,我这算是完美主义者么?
本文分三大部分:
新手老手都能看,希望对你有用。如果是 星球 用户可以直接移步第三部分,当然建议前面也看看;
我知道大家最想看啥,所以把对比放到前面来了;我把5个trace工具在同一个目标函数上跑一遍最直观。bench 用的 demo:
当然有人会杠说我测试不够多啥的,对此我只能说事实胜于雄辩,不信就自己测试。
哦因为介绍了我自己的trace工具,可能还会有人说软广啥的,随便说,好的工具值得拿出来宣传,而且我自己也把优化点分享出来了(我觉得做到这一步已经很好了),有动手能力的都能自己写一个trace工具。
结果:
注:AndroidPrybar 崩溃位置在 libtrace.so 偏移 0x510a2c–0x5666c8 之间 8 帧(SIGSEGV / SEGV_MAPERR / fault addr 0x0),作者 README 也直接写了"代码让ai改崩溃了,一些功能不太稳定"。
vmtrace 接口和其他几家不同:它要求"Interceptor.replace 目标函数 → callback 里调 qbdi_vm_call(targetAddr, argsPtr, argNum, logPath, dumpMem) → 返回结果",等于把"切入 trace VM"和"调用目标"绑成了一次调用。某种程度上更直观(一行 trace 一个函数),但只能从函数边界进入,且接入方需要自己组装参数指针数组(参数个数填错会崩)。
几个观察点:
当然这就是 demo 上的单点数据,实战中不同样本(深递归、大量子线程、自定义 linker、JNI 调用密集型等)相对差距会变。bench 脚本和 demo 已经放在仓库 trace_compare/bench/ 下,欢迎自己换样本复现。
为什么会有这么大的差距?这就涉及指令级 trace 到底在做什么、各家选了什么底层引擎、还有 xfQTrace 在 QBDI 之上做了哪些工程化改动 —— 接下来一层层说清楚。
先来一大段背景,可跳过:
最近一两个月,先是休息了半个月,打打游戏啥的,然后实在是压力太大了,一天不逆向浑身难受啊,跟蚂蚁在爬一样,然后就开始逆向了。最近一个月在学习密码学经典算法的设计原理相关的内容,感觉学起来还是有点难度的,很有意思。后续会用remotion来创作高质量视频,然后发布到b站给大家补点相关基础,放心讲的会非常非常详细,设计原理/手搓实现/汇编特征/魔改怎么逆等等;
然后平时偶尔逆向一下实战案例,一些安全SDK那种,过程中发现很多案例用unidbg来补会有一些问题,虽然中等难度的app大多都可以解决,但是稍微再难的就不方便处理了,与其耗时在深度魔改unidbg让其更像一个android,不如直接写一个优秀的真机trace工具;反正一个信息很全的trace日志,ai只需要使用ripgrep就可以暴力分析了,然后我们拿到分析结果之后就可以开始复现,这样可以利用ai当老师然后不断追问来进行学习,哪怕是vmp的案例也是照样逆完学习;
所以就会有明显痛点了,开源的真机trace好像有很多问题,而且直接用别人开源的成品好像原理啥的我也掌握不到位呀,俺是来学习的又不是来直接用成品的。所以一周前我开始学习qbdi,然后顺便开始开发我自己用的真机trace工具,连续3天睡眠加起来不超过14小时吧,也是非常抽象了,也是压根睡不着*几发还是一样哈哈哈哈;好在最近几天已经完工了,可以美美休息几天了。
然后感觉自己做的比开源的工具对比下来会有很多明显优点吧,自己也倾注了很多心血,而且后续还会支持俺的ttd;然后我认为单独收费卖感觉不太好,因为其实技术含金量不是很高,重要的是怎么思考到这些优化点,优化的点都是我一步步思考出来的; 那为啥我不打算免费发出来呢?
基本上就是这三点,寒了多少愿意开源的朋友的心。所以仔细考虑下来,就打算把工具免费丢到 星球 了。当然你也可以认为我是为了引流吧,我承认有这一部分原因,但更多的是心确实已经寒了。(另外其实我在b站已经送出去很多朋友 星球 了,都是愿意分析文章或者工具或者都愿意点拨我的)
那发到 星球 还有什么好处?
因为诚实来说俺也没啥稳定收入,平时也不接点单,也没工作,现在就靠 星球 赚点生活费,能靠这样能引流几个人进去也算不错了;我个人认为就这样处理最妥当,如果还是被喷的话那就随他吧,反正这圈子遇到的神人确实还挺多的哈哈哈哈;
但是!!有很多朋友仍然非常支持我,我觉得我也要做点什么!所以我会把这个工具的实现以及优化思考全都放出来吧,这样即使各位不愿意进 星球 的朋友依旧可以拿本文喂给ai让他对开源的真机trace进行改造,具体见后文介绍(点击跳转);
安卓逆向里,trace 是绕不开的环节。先说啥是 trace。
trace 就是"跟踪"。我对 trace 的抽象理解是:站在不同角度去观察一件事的发展。
举个现实生活的例子:你看到一条新闻,它就一定是正确的吗?我个人认为现在的网络环境下,很多人会拿着单方或者疑似权威的说明就当作绝对正确。
总结就是:视角越深、维度越多,你看到的事实就越接近真实。
trace 在逆向里也是这个道理 —— 你站在哪一层看,决定了你能看到的事实有多深。
但也不是越底层越好的,要综合评估,如性能开销还有实际效果,最底层的trace并不是适用于任何场景。
trace 其实有非常多种,只是现在大家提到 trace 默认指的是"指令级 trace"。下面我会从最浅到最深列一遍各级 trace,希望能帮大家拓宽一点思维 —— 这对于理解整个安卓逆向工具链的作用和发展史会很有帮助。
OK,假设你拿到一个 app,目标是分析清楚它"登录"按钮背后到底做了什么,从最浅到最深一层一层下去:
点击登录按钮 → loading 弹窗 → 界面跳转 → 中间可能藏着各种日志上报、设备信息收集、风控埋点。这一层我们关心"app 对外露出来什么"——不进 app 内部,只看它在外部留下的痕迹。
下面是一些常见的"黑盒观测 trace":
logcat:app 自身打的日志,很多 SDK / app 默认会留一堆 tag,可以选择一些常见的进行尝试,运气好可以抓到关键点,不过意义不大(一般只有在报错时会有error日志有点用)
抓包:登录涉及把账号密码上传服务器,所以一定走网络协议。
直接跑下来你会看到一堆 HTTP / TLS over TCP 的包,少数 app 会上 HTTP/3(也就是 QUIC over UDP)。这里就不展开抓包对抗的东西了。总之这一步价值非常的高,比如如果是http协议,你就能看到他的关键报文了;
文件 IO trace:很多 app 会把 token / device id / 缓存 / dex 副本 / so 副本写到 /data/data/<pkg>/ 或 sdcard,看它读写哪些文件能直接定位关键流程(特别是脱壳、找加密本地存储的密钥)
系统调用 / 内核 trace:
UI 行为分析:
进程 / 服务 状态:
UI 层用的是 Java / Kotlin(绝大部分情况),那就把 apk / dex 拿出来反编译看,常见工具如下:
加壳的话需要先脱壳,方案很多,常见的有:
上面的话推荐直接用网站,不行的话就得自己找脱壳机了,实在不行就找一个类似f利姬的热心 脱壳姬哈哈哈哈(俗称人脉脱壳),还是不行的话就得自己魔改咯。这里不过多展开。
拿到代码之后一般直接看 Java / Kotlin 源码 —— 高级语言可读性高,定位关键函数很快,如果遇到一些特殊情况,比如反编译失败或者混淆严重可以选择查看更底层的语言:smali汇编,在这里不多赘述;
总之通过这一步很容易找到关键函数,亦或者你用前面黑盒观测 trace 中分析出来的关键信息比如报文 header、xml 文件名等等定位也可以!
知道哪个函数是关键之后,基本都需要做动态校验:到底有没有真的被调用?参数 / 返回值长什么样?主流两种方案:调试 / hook,本人基本不用调试,因为太麻烦啦!下面只聊 hook。
hook 思路:注入进程 → 改写函数入口出口 → 打印参数返回值 / 改写参数返回值。下面给一些我个人认为值得尝试的 hook工具:
这里我觉得很有必要提醒一点:很多没列出的工具也很好用,但大多本质都是基于 frida / lsposed 的封装,目的都是省时间。这类封装对新手是降维打击,无脑使用即可;但对老手来说,遇到新样本时往往会感到束手束脚 —— 封装给你省了入门时间,但也屏蔽了底层细节。所以新手如果一上来就把大量时间花在二次封装工具上,那就错失了学习底层工具的窗口。
每个时代都会涌现大量二次封装工具,那些出名且能长期存活下来的项目,而且一直有人时不时维护,我认为这种才值得长期使用,或者你有改源码的能力;如果没有,此类项目一旦时间拉长,最终也基本只剩下源码学习的意义(比如学习如何封装)。对作者本人来说,很多项目只是为了解决自己在某一时期的需求,时间一拉长,就会有更好的解决方案了。
正所谓: 器本自通流,制器有来由,他铸合他求,探源得真枢,握得本根在,随心任去留。
综合下来,这里建议直接学 frida / lsp 就行了。但是有开发能力的朋友建议多思考多动手,万一你能做出更优秀的工具呢!!
我自己更喜欢灵活性高的方案,直接 frida cli + JS。哪怕是为了省时间,我也会给 ai 链上 jadx 的 mcp,让它自行查找关键函数,然后让它快速使用 frida cli + JS 验证:通过 hook 快速验证是否触发,以及打印获取调用栈。
到这一步大部分中等强度的 app 已经能搞定,比如本场景:抓密码 → hash → 拼报文header如sign → 发包模拟登录。但深度混淆(dex 加固 / Java 转 native)的 app 就没这么轻松了。
Java 反编译失败、或者 jadx 出来全是 <error: failed to decompile>,亦或者需要做混淆验证 —— 那就深入本质,降到 ART 虚拟机字节码层面,也就是 smali。这时候要么静态硬啃 smali(jadx 直接切到 smali 视图即可),要么动态分析。
如果静态分析压力大,或者需要动态验证,那就要么动态调试 smali,要么对解释器插桩把"真实执行的 smali 汇编trace 出来:
这一步建议熟读各类工具源码,会有很深的体会;
另一个常被忽略的视角是 binder trace,这部分高可用的工具也少。Android 应用与系统服务(SystemServer / mediaserver / surfaceflinger 等)的所有 IPC 都走 binder,位置 / 传感器 / 设备 ID / 通讯录 / 短信 / 包管理 / 网络状态 …… 这些数据无一例外都要跨进程拿。在 Java 函数 trace 看到的 TelephonyManager.getDeviceId() 之类,在系统框架层就是一次 IPCThreadState::transact,能抓到完整的 Parcel 序列化数据。
这一层 trace 的核心价值在于:
常见工具:
实战里 binder trace 经常和 Java/native trace 配合着看:先在 Java 层 frida-trace 找到可疑调用 → 在 binder 层确认到底走的哪个系统服务接口 → 必要时下到 native 层抓 Parcel 详细字段。
如果 Java 函数最终调进 native(JNI 桥),SO 在 lib/arm64-v8a/ 下。拿出来反编译:
常用插件(拿来辅助识别加解密 / 哈希):
由于反编译工具编写难度很大,所以推荐闭源的IDA/Ninja;
接下
...(内容过长,已截断)
---
来源: 看雪论坛
原文链接: https://bbs.kanxue.com/thread-291372.htm
[原创]不同真机 trace 工具性能对比、优化点分享、xfQtrace使用说明
1 浏览
0 回复
暂无回复,快来抢沙发吧!