dex-mem-dump · git:20261023.3fb7043 · 2026-10-23 · sha256 e13f1c4c38d19ace

dex-mem-dump git:20261023.3fb7043A

Immutable. This exact content never changes and is served at /api/v1/blob/e13f1c4c38d19ace.

---
name: dex-mem-dump
description: 运行时 DEX 内存 dump / 脱壳(root 直读 /proc/<pid>/mem,不注入、不 ptrace):常规解包拿不到、抽取壳未回填、或目标查 TracerPid 时用。流程:推工具 → 跑 → 拉回 → 清理;分诊与排障见 references/unpacking.md。
---

# dex-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,优先)」](../../../references/unpacking.md#step-1-root-内存-dump非-frida优先)。

## 前置

1. `adb devices` 有设备;多设备所有命令加 `-s <serial>`。
2. 目标进程在跑,且已过启动页(载荷解密后才在内存里)。
3. root 可用:`adb shell su -c 'id'` 返回 uid=0。
4. 二进制已推送(步骤 1)。本页路径以**项目根**为基准。

## 步骤

### 1. 推工具

```bash
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

```bash
adb shell pidof <包名>
```

- 为空 → 先拉起:`adb shell monkey -p <包名> -c android.intent.category.LAUNCHER 1`
- 多个 pid → 每个都要 dump,载荷可能只在其中一个。

### 3. 跑

```bash
# 主选: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. 拉回

```bash
adb pull /data/local/tmp/panda/ ./
adb pull /data/local/tmp/memdump/ ./
```

### 5. 清理

```bash
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,优先)」](../../../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 ›「工具机制与重建」](../../../references/unpacking.md#工具机制与重建)。