📌 项目地址:MG1937/ASC | ⭐ 1,086 颗星 | 🔧 Python | 📜 未标注
代码
反编译大型 APK,先等半小时?
用 jadx 或 Ghidra 打开一个几百 MB 的商业 APK,流程都一样:先等。等工具吃掉几个 GB 内存,把整个文件解压出来,再花几十分钟建全局索引和交叉引用,然后你才能开始搜代码。这些预处理只为了之后的搜索快一点。
Droid ASC 的作者觉得这套流程不合理。APK 编译完之后本身就是高度结构化的数据,传统反编译器却在结构化数据之上再重建一个臃肿的关系数据库。既然能直接从 APK 里毫秒级提取任意代码关系,那预处理还有什么意义?
所以 ASC 换了个思路:把编译产物当成只读数据库,无状态、零预处理,需要什么就在毫秒级内查什么。
实测数据(352MB 商业 APK):
- 全局交叉引用搜索:1.79 秒
- 反编译目标类:177 毫秒
- 内存占用:141MB
这个项目来自 Black Hat Europe Arsenal 的议题 “Droid ASC: R8 Compiler Optimization as a DeCompiler Primitive”,仓库在 MG1937/ASC,Python 写的,1086 颗星。
快在哪:四个技术点
README 里讲的技术路线挺有意思,我逐条说一下。
直接探测 Deflate 比特流。 APK 本质是个 zip,解压整个文件是大头开销之一。ASC 不做完整解压,而是构建密集的 Huffman 查找表,在压缩比特流里直接探测,只提取核心元数据,不碰无关的数据块。
利用 R8 的编译器行为。 这是我觉得最有意思的一点。R8 编译器会做确定性的常量重 relocation 和指令去重,结果是代码在物理布局上高度集中。ASC 把这种编译器留下的“痕迹”当武器,实现跨 DEX 的快速代码搜索。也就是说,它不是在对抗混淆,而是顺着编译器的优化逻辑走。
O(1) 指令定位。 把原始字节码偏移映射回所属方法,通常要建映射表。ASC 做到了常数时间定位,不需要重型的映射结构。
按需重建最小 DEX。 命中目标之后,只提取目标字节码和它的依赖项,在内存里动态拼出一个最小但自洽的 DEX,直接丢给反编译器。这解释了为什么反编译单个类只要 177 毫秒:工作量本来就小。
一句话对比:传统工具是“先花几十分钟建索引,之后搜索快”;ASC 是根本不建索引,每次查询都够快。
怎么用
# 从 PyPI 安装
pip install droidasc
# 或从源码
pip install .
装完之后全局可用 droidasc 命令。README 给出的 CLI 结构:
usage: droidasc [-h] {getclass,getmanifest,findrefs} ...
ASC tooling entry.
三个子命令,从名字就能看出用途:
getclass:获取指定类getmanifest:获取 Manifestfindrefs:查找交叉引用
README 在这里截断了。各子命令的具体参数(比如怎么指定 APK 路径和类名),跑一下 droidasc <子命令> -h 看输出最可靠。
定位:前端,不是完整反编译器
要理解这个项目的边界。它是“前端”:负责快速定位、提取目标代码、重建最小 DEX,真正的反编译还是交给现有反编译器完成。
这个分工在两类场景下特别划算。一是安全审计里反复做定点查询,比如追踪某个 API 的所有调用方,用 jadx 每次都全量加载太浪费,findrefs 一条命令出结果。二是被自动化 Agent 调用:毫秒级响应加 141MB 内存占用,天然适合程序化集成。项目的定位也明确写了是给 Agent 和移动安全研究者用的。
用之前要知道的
- 仓库没标许可证,商用或二次分发前自己去仓库确认。
- 项目较新,命令行参数可能随版本变化,以仓库最新文档为准。
- 别期待图形化浏览整套代码。它的价值在快和省,深度阅读反编译结果还是要配合 jadx、Ghidra 这类工具。
- 想深入理解 Deflate 比特流探测和 R8 布局利用的原理,Black Hat Arsenal 的页面值得一读,README 开头附了链接。