dex-mem-dump
运行时 DEX 内存 dump / 脱壳(root 直读 /proc/<pid>/mem,不注入、不 ptrace):常规解包拿不到、抽取壳未回填、或目标查 TracerPid 时用。流程:推工具 → 跑 → 拉回 → 清理;分诊与排障见 references/unpacking.md。
SKILL.md.kilo/skill/dex-mem-dump/SKILL.md
GitHubdex-mem-dump - 运行时 DEX dump
从运行中进程的内存里抠 DEX。两个 arm64 工具,都不走 ptrace:
| 工具 | 访问方式 | 冻结目标 | 定位 |
|---|---|---|---|
panda-dex-dumper |
/proc/<pid>/mem |
是(SIGSTOP/SIGCONT) | 主选:快、快照一致 |
mem-dex-dumper |
/proc/<pid>/mem 或 process_vm_readv(2) |
否 | 备选 / 交叉校验 / 不能冻结时 |
二者都不用
ptrace。目标即使已用PTRACE_TRACEME占住 ptrace 槽(TracerPid != 0)也挡不住;能挡住的是真正基于 ptrace 的 dumper。二进制与源码在本 skill 目录;
mem-dex-dumper为本仓库自研。
何时用 / 何时不用
| 场景 | 走本卡 | 说明 |
|---|---|---|
| 有壳,想先低成本拿 DEX | ✅ | 不注入、无痕、比 Frida 快 |
| 一代壳(整体加密) | ✅ | 解密后的完整 DEX 就在内存里 |
| 抽取壳,只要类名 / 结构 | ✅ | 骨架够用 |
| 抽取壳,要完整方法体 | ❌ → tools/unpack.py |
被动扫描拿不到未执行过的方法体 |
| Frida 一挂就被杀 | ✅ 退路 | 只能拿已执行过的部分,产物必须标注不完整 |
| 无壳 | ❌ | 直接 unzip,不进任何 dump |
| 目标是 SO / native | ❌ | 走 native 分析 |
边界:本卡是被动取快照,tools/unpack.py 是主动触发回填——后者靠 loadClass 让壳把方法体写回 DEX,所以能补出前者原理上拿不到的内容;代价是必须过 Frida 检测。对"按方法执行粒度回填"的高级壳,两者都拿不到,转 native。升级路径:分诊 → 本卡(快照)→ 方法体齐全则结束 → 骨架则 tools/unpack.py(回填)→ 仍骨架 = 高级壳。
分诊与升级细节见 references/unpacking.md ›「Step 1 root 内存 dump(非 Frida,优先)」。
前置
adb devices有设备;多设备所有命令加-s <serial>。- 目标进程在跑,且已过启动页(载荷解密后才在内存里)。
- root 可用:
adb shell su -c 'id'返回 uid=0。 - 二进制已推送(步骤 1)。本页路径以项目根为基准。
步骤
1. 推工具
adb push .kilo/skill/dex-mem-dump/panda-dex-dumper /data/local/tmp/
adb shell chmod +x /data/local/tmp/panda-dex-dumper
mem-dex-dumper 同理,只换文件名。
2. 取 pid
adb shell pidof <包名>
- 为空 → 先拉起:
adb shell monkey -p <包名> -c android.intent.category.LAUNCHER 1 - 多个 pid → 每个都要 dump,载荷可能只在其中一个。
3. 跑
# 主选:panda(SIGSTOP 冻结快照)
adb shell "cd /data/local/tmp && ./panda-dex-dumper -p <pid>"
# 备选:mem(不冻结;-b mem 默认,-b vmreadv 换后端)
adb shell "su -c 'cd /data/local/tmp && ./mem-dex-dumper -p <pid> -o /data/local/tmp/memdump'"
4. 拉回
adb pull /data/local/tmp/panda/ ./
adb pull /data/local/tmp/memdump/ ./
5. 清理
adb shell "su -c 'rm -rf /data/local/tmp/panda /data/local/tmp/memdump /data/local/tmp/panda-dex-dumper /data/local/tmp/mem-dex-dumper'"
判据
| 现象 | 结论 |
|---|---|
每命中一行 find dex off: 0x… file_size: 0x… dumped: 0x… |
正常输出 |
末行 scanned N bytes, done. |
扫描结束 |
dumped < file_size |
读到一半失败,文件残缺 → 重跑或换 panda |
命中地址在 [anon:libc_malloc] 等匿名堆 |
载荷(业务 DEX) |
命中地址在 /system/framework/*.jar、/apex/*/javalib/*.jar、*.vdex |
系统噪音,忽略 |
| 两工具同地址 md5 相同 | 抓取一致,可信 |
有 class_defs 但方法体多为 return-void |
抽取壳骨架 → 升级 tools/unpack.py |
卡壳出口
| 症状 | 先查 |
|---|---|
open /proc/<pid>/mem: Permission denied |
非 root / SELinux → su -c、setenforce 0;仍失败换 -b vmreadv |
| 完全没命中,但 App 明明加固了 | 载荷未解密 → 停在功能页再跑;或换 panda 冻结 |
| 文件很大(几十 MB) | .vdex 内嵌 header,噪音;载荷在匿名堆 |
| 部分类解析失败 | 边扫边写的撕裂读 → 重跑;要一致快照用 panda |
| 两工具结果不一致 | 校验松紧不同,属正常;以严格校验的 mem 为准,再手工补 |
| 设备上跑不起来 | arm64-only、静态链接、API 29;按文末重建 |
更细的排障(PR_SET_DUMPABLE、hidepid、多进程)见 references/unpacking.md ›「Step 1 root 内存 dump(非 Frida,优先)」。
产出
- DEX 落到
<包名>/dex/(或用户指定目录),保留工具原始命名。 - 壳改过内存时 checksum 必失效:
python3 tools/dex_rebuilder.py --fix-checksum <dump.dex> -o fixed.dex,否则 jadx 报Bad dex file checksum。 REPORT.md记:工具、后端、命中地址 → maps 归属、md5 交叉校验。- 设备侧产物与二进制必须清掉。
工具机制
扫描/校验细节与重建步骤见 references/unpacking.md ›「工具机制与重建」。