---
name: re-mobile-pack
description: >
  Android 加固脱壳专项：乐固/360/梆梆/爱加密、DEX 恢复。
  触发词：脱壳、Android加固、DEX脱壳、乐固、梆梆、360加固、爱加密
capabilities: [unpack]
---

# Android 加固脱壳（DEX 恢复）

## 何时使用 / 何时不用

- 用：拿到 Android 加固（乐固/360/梆梆/爱加密等）App，需要还原真实 DEX 继续静态分析
- 用：[[re-apk]] 已识别出加固，需要选脱壳路径
- 用：脱壳产物方法体空壳 / 指令抽取，需要修复
- 不用：未确认加固——先 [[re-apk]] 静态确认；普通 App 直接 jadx 即可
- 不用：VMP 虚拟化为主的强壳——转 [[re-anti-analysis]]（[[re-unpack-advanced]] 思路）
- 不用：需要运行时 hook / 绕过检测（那是 [[re-frida]]）

## 工具准备

动态脱壳默认在受控设备 / 模拟器快照内执行（[[re-analyze/platform-tips]] 最高原则——默认沙箱）。所有工具先验证再使用。

### frida + frida-dexdump —— 运行脱壳主力

- frida-tools: 全平台 `pip install frida-tools`（Python 3.8+；建议 venv）；frida-server 版本必须与主机一致并推送设备（安装与反检测完整流程见 [[re-frida]] 工具准备）
- frida-dexdump: 全平台 `pip install frida-dexdump`（Python 3）——运行时搜索内存中的 DEX 魔数并 dump
- 验证: `frida --version`；`frida-dexdump -h`（能看到 `-U`/`-f`/`-d` 参数）
- 用法: `frida-dexdump -U -f <包名>`（spawn 起步脱壳）、`frida-dexdump -U <pid>`（attach）、`frida-dexdump -U -f <包名> -d`（深度搜索，覆盖多 DEX / 部分抽取）

### BlackDex —— 静态脱壳机（免 frida，普通加固首选替代）

- 安装: GitHub release 下载 APK——`https://github.com/CodingGay/BlackDex/releases`；32 位与 64 位是两个独立 APK（目标 App 不出现在列表就换另一个架构版本）
- 环境: 普通 Android 手机或模拟器，Android 5.0~12，无需 root / 定制环境
- 能力: 覆盖落地加载 / 内存加载 / 指令抽取三类壳；深度模式回填指令抽取的方法体（实验性，可能耗时数分钟或失败）
- 产物: `hook_*.dex`（hook 系统 API 得到）与 `cookie_*.dex`（DexFile cookie 技术）
- 验证: 安装后能列出目标 App 并完成脱壳

### Youpk —— 主动调用脱壳机（抽取加固最强路径）

- 源码/ROM: `https://github.com/Youlor/Youpk`——基于 Android 7.1.2_r33 二次开发，**仅支持 Google Pixel 1**：Android Studio 构建后 `flash-all.sh` 刷入定制 ROM
- 用法: `adb shell "echo <包名> >> /data/local/tmp/unpacker.config"` → 启动目标 App 自动主动调用脱壳（日志见 "unpack end"）→ `adb pull /data/data/<包名>/unpacker`
- 修复: `java -jar dexfixer.jar ./unpacker ./out`（配套 DEX 修复，回填方法指令）
- 验证: `adb shell ls /data/data/<包名>/unpacker` 能看到 dex/method 产物

### jadx —— 脱壳后反编译（复用 [[re-apk]]）

- 跨 OS 安装与验证见 [[re-apk]] 工具准备；jadx 支持多 DEX 直接分析，避免手工合并

### adb —— 设备交互

- Android SDK platform-tools 自带；验证 `adb --version`；模拟器场景 `adb shell` 即可
- dexdump: Android SDK build-tools 自带（步骤 4 DEX 完整性检查用）；验证 `dexdump -h`

### 内存转储（[[re-memdump]] 思路）

- frida/objection `memory dump` 或按 [[re-memdump]] 从转储 grep DEX 魔数 `dex\n0xx` 提取——frida-dexdump 不可用时的兜底路径

## 操作步骤

按顺序执行，每步产物（DEX 路径 + sha256）存档（见 [[re-triage]]）。

1. **加固识别（先确认再动手）**：
   - 类名特征: jadx 反编译只见壳类——`com.stub.StubApp`（爱加密）、`com.secneo.apkwrapper` / `com.bangcle.*`（梆梆）等；入口 Application 被替换成壳类
   - 壳 so 特征: `ls out/lib/`——`libjiagu.so` / `libprotectClass.so`（360 加固）、`libDexHelper.so` / `libexec.so`（爱加密）、`libsecexe.so` / `libsecmain.so`（梆梆）
   - DEX 体积: `ls -la out/classes*.dex`——真实 DEX 加密存放（assets/ 或运行时解密），壳内 classes.dex 体积异常小
   - 记录识别出的壳名决定路径: 常规整体/抽取加固 → 本技能；虚拟化为主 → [[re-anti-analysis]]

2. **运行脱壳（frida-dexdump，默认路径）**：
   ```sh
   adb shell monkey -p <包名> 1            # 先启动 App，让壳把真 dex 加载进内存
   frida-dexdump -U -f <包名>              # 或 spawn 起步
   frida-dexdump -U <pid>                  # 已运行 attach
   frida-dexdump -U -f <包名> -d           # 深度搜索（多 DEX / 部分抽取场景）
   ```
   产物在当前目录 `<包名>/<时间戳>/*.dex`。**时机**：等 App 进到业务页面再 dump（[[re-analyze/platform-tips]] 关键经验——转储时机），一启动就 dump 拿到的是壳初始状态。

3. **静态脱壳（BlackDex / Youpk，frida 被检测时的替代路径）**：
   - BlackDex: 安装对应架构 APK → 选择目标 App → 脱壳（普通壳几秒；抽取壳开深度模式，数分钟且有失败风险）→ 导出产物
   - Youpk（抽取加固优先）: 刷 Pixel 1 ROM → 写包名配置 → 启动 App 等待自动脱壳 → pull `unpacker/` 目录 → `java -jar dexfixer.jar ./unpacker ./out`
   - 交叉验证: 两条路径产物对比（同一方法体应一致），不一致取完整者

4. **DEX 修复与完整性检查**：
   ```sh
   file out/*.dex                          # 应见 "Dalvik dex file version 035"（magic: dex\n035\0）
   dexdump out/classes.dex 2>/dev/null | head -30
   jadx -d java-out out/                   # 反编译验证：业务类是否齐全
   ```
   - 方法体空壳（CodeItem 全 nop / 直接 throw）→ 指令抽取壳未回填：用 dexfixer（Youpk 产物配套）或 BlackDex 深度模式重脱；仍不行 → 主动调用触发方法执行后再转储（[[re-memdump]] 思路）
   - 多 DEX: 检查 classes.dex / classes2.dex / ... 是否齐全；frida-dexdump `-d` 深度搜索兜底；jadx 多 DEX 直接分析
   - 修复后必须反编译抽检再进业务分析（见坑 3）

5. **脱壳后分析（走 [[re-apk]] 全流程）**：
   - manifest 入口/权限 → jadx 反编译业务代码 → 定位目标逻辑（密钥/协议/校验）
   - 需要改行为 → smali 补丁 + 重打包签名（[[re-apk]] 步骤 4）
   - 原生 so（壳 so / JNI 业务 so）→ [[re-binary-core]]（[[re-format-elf]] + [[re-ghidra]]）；壳自身反调试/校验对抗 → [[re-anti-analysis]]

## 跨域联合

- [[re-mobile]]: 工作流第 5 步加固分支固定调用本技能（[[re-apk]] 识别后转入）
- [[re-apk]]: 加固识别（步骤 5）与脱壳后静态分析（manifest / jadx / smali）
- [[re-frida]]: 运行脱壳执行环境 + 反 frida 检测对抗（改名 / 换端口 / gadget）
- [[re-memdump]]: 内存 DEX 提取思路（dump 时机、magic 扫描、多 DEX 兜底）
- [[re-anti-analysis]]: VMP / 虚拟化加固对抗（抽取+虚拟化组合壳转强壳流程）
- [[re-binary-core]]: 壳 so / JNI 原生逻辑分析（[[re-format-elf]] / [[re-ghidra]]）
- [[re-analyze/platform-tips]]: 默认沙箱（模拟器快照）、转储时机关键经验
- 本技能被 [[re-analyze]] 的 triage「移动 App 分析」路径引用（re-mobile → re-apk 识别加固 → re-mobile-pack）

## 常见坑与陷阱

- **VMP 级加固（抽取+指令虚拟化）脱壳难度大**：现象——frida-dexdump / BlackDex 出 DEX 后方法体大量 nop / 空壳，部分方法逻辑在 dex 里永远找不到；原因——壳除抽取指令外把关键方法虚拟化进 so（自定义 VM 解释执行），DEX 层还原不出这部分；对策——识别为抽取+虚拟化组合时：抽取部分用 Youpk 主动调用修复，虚拟化部分放弃 DEX 还原、改为动态 [[re-frida]] hook 观察输入输出，或还原 VM 解释器（成本高，标注范围并转 [[re-anti-analysis]] 强壳思路）
- **反 frida 检测**：现象——spawn 即闪退、frida-dexdump 连不上或 dump 出空结果；原因——壳检测 frida-server 端口 27042 / 路径 / 特征线程（gum-js-loop）等指纹；对策——按 [[re-frida]] 反检测章节改名 + 换端口；仍被检测 → 换 BlackDex / Youpk（不依赖 frida）或 frida-gadget 注入
- **DEX 修复不完整（方法体空壳）**：现象——jadx 打开脱壳 DEX，半数方法无反编译内容或直接 throw；原因——指令抽取壳的 CodeItem 未回填（dump 时机早于方法执行 / 修复器未处理）；对策——dexfixer 修复（Youpk 产物配套）或 BlackDex 深度模式重脱；主动调用触发方法执行后再 [[re-memdump]] 转储；修复后 dexdump 抽查 + jadx 反编译验证再进分析
- **多 DEX 合并/丢失**：现象——只 dump 到单个 classes.dex，jadx 报类缺失 / 引用不存在；原因——加固后多 DEX 由壳运行时加载，frida-dexdump 默认搜索可能漏；对策——`-d` 深度搜索、hook ClassLoader 枚举已加载 dex（[[re-frida]] 枚举）、内存转储全部提取；jadx 直接加载全部 dex 分析
- **转储时机过早**：现象——dump 出的 DEX 解不开 / 是壳初始数据；原因——真 dex 尚未解密加载；对策——等 App 进入业务页面再 dump（[[re-analyze/platform-tips]] 转储时机关键经验，与"解密数据看到立刻保存"相反的另一端）
- **PairIP（Google Play 自动保护）三道门**：现象——JADX 拉开关键方法只剩 `VMRunner.invoke("...")` 空壳，模拟器上跑几秒就退；原因——Play 上架自动套的加固三件套：①代码虚拟化：逻辑抽进加密 VM blob（assets 随机名无扩展名文件，`\x00IAP` 头为解密判据）+ native 解释器 `libpairipcore.so`（`executeVM` 等符号名运行时从 XOR 表解码，strings 搜不到）；②TEE 密钥：blob 是 AES/GCM 密文，wrapping key 存在设备 TEE（AndroidKeyStore），Play 经 `importWrappedKey`（SecureKeyWrapper ASN.1）下发——三个硬前提：Play 安装（`gpdeku` split 载体）、过 Play Integrity、有登录 Google 账号，缺一即 Finsky 日志 FAILURE → 本地伪造 `isEncryptionKeyPresent()` 也没用；③运行时反篡改：`dl_iterate_phdr`/opendir 扫 maps 发现注入 so → 故意给 std::vector 喂非法 length → `std::__throw_length_error` abort（tombstone 指纹 `length_error was thrown in -fno-exceptions mode`）→ abort 被中和则执行流落 abort 下一条指令踩内存 SIGSEGV + `SIG_DFL` 复位——**信号层 hook 拦不住，根因在检测端（maps 不露脸）**；对策——先过 Integrity（三绿真机或 PlayIntegrityFix）+ 清掉可见注入模块（Zygisk disable 后重启）
- **PairIP 脱明文：调用而非 hook**：现象——hook `VMRunner` 任何方法（getVmByteCode/executeVM/readByteCode）进程立刻自毁；原因——PairIP 盯 ArtMethod 完整性：frida hook 改写 `entry_point_from_quick_compiled_code` 即命中检测；对策——**反射直接调用**解密函数（`getVmByteCode` 是 private static，frida 反射可调；blob 名带不带 `assets/` 前缀版本间不一致，两种都试），零 hook 零 ArtMethod 改动即可解出全部 blob 明文——脱 PairIP 不需要内核级隐身，只有必须动态跟解释器时才需要
- **加固 APK 直接重打包 → 真机黑屏**：现象——对加固 APK 走 apktool 重打包，签名安装后真机黑屏（进程存活、无崩溃），同一 APK 在模拟器上却「看起来正常」；原因——重打包从 smali 重建 DEX，破坏壳的布局与自校验假设，壳定位/验证不了加密载荷 → 不解密 → 真实 DEX 里平台通道 handler 全部抛 MissingPluginException → 未捕获异常拆掉渲染；模拟器上壳校验被放松，「模拟器能跑」不是证据；对策——重打包前先做加固检测（assets 里壳运行时 so（`lib*sec*`/`lib*shell*` 风格）、动态 DEX loader so、壳 stub 入口类、单一超大 classes.dex 且无业务字符串、smali→dex 往返拆出多个空 stub DEX）；确认加固即放弃 apktool 重打包；转储内存必须卡解密窗口——壳瞬态自卸载，`/proc/self/maps` 里可能都看不到壳，过早/过晚都拿不到解密 DEX；若应用自身还自检「壳是否存在」，脱壳需一并中和该检查（来源：reverse-skills（inliver233），MIT）
- **加固目标交付路径：脱壳重打包 / 免重打包注入 / 常驻 gadget**：现象——带壳目标直接脱壳重打包后运行异常，或不知选哪条修改交付路径；原因——脱壳重打包后壳工件未清干净，壳运行时与明文 DEX 冲突；对策——按目标状态选：路径 a 脱壳重打包（保真度高）：blackdex / FART / frida-dexdump 在解密窗口从活进程取回真实 DEX 再重打包（壳已不需要），重打包前清掉全部壳工件（壳运行时 so、壳目录、DEX loader）；路径 b 免重打包注入（不重装 DEX，壳与自校验完全不动，对壳风险低，优先）：原位替换 libapp.so + 用 LSPatch 类工具注入 Java hook，注意目标可能检测注入框架本身（检测字符串常见），属需一并处理的完整性信号；路径 c 常驻 gadget（零文件改动）：注入 frida-gadget + 开机自启脚本做运行时签名适配与 libapp 内存 patch，注意 gadget 可被检测、运行时翻转检查耦合标志可能反噬；不推荐逆向壳校验逻辑硬过 apktool 重打包——难度高收益低（来源：reverse-skills（inliver233），MIT）
