应用安全 --- apk加固 之 全内存取证
核心原理
许多安卓应用,尤其是那些经过“加固”保护的,会动态地加载或解密关键的 DEX 文件。这些 DEX 文件在应用运行时必然存在于进程的内存空间中。通过转储整个进程的内存镜像,我们可以像“大海捞针”一样,从中扫描、提取并重组出完整的 DEX 文件。gcore 和 dd 是两种不同的内存转储工具。
方法一:使用 gcore 转储 (需要 Root)
gcore 是 NDK 调试工具链的一部分,它可以直接附加到进程并生成一个 core dump 文件(ELF 格式),其中包含了进程的完整内存布局。
操作步骤:
-
准备工作 (PC端)
-
下载并配置好 Android NDK。
-
从 NDK 的
prebuilt/[架构]/bin目录中找到gcore工具,或者使用ndk-build编译一个。 -
确保你的安卓设备已 Root(如 Magisk)。
-
将
gcore推送到设备的/data/local/tmp目录,并赋予可执行权限。
adb push /path/to/your/gcore /data/local/tmp/ adb shell chmod +x /data/local/tmp/gcore -
-
获取目标进程的 PID
-
在设备上运行你想要分析的目标应用。
-
通过
adb shell进入设备 shell,然后使用ps或pidof命令找到应用的进程 ID (PID)。
adb shell su pidof com.example.targetapp # 或者 ps -A | grep "com.example.targetapp" -
-
使用 gcore 转储内存
-
假设目标 PID 是
1234,运行gcore命令。这可能会花费几秒钟到几分钟,取决于应用的内存占用大小。
/data/local/tmp/gcore -o /sdcard/dump 1234-
执行成功后,你会在
/sdcard/目录下找到名为dump.<pid>(如dump.1234)的文件。
-
-
将 Core Dump 文件拉取到电脑进行分析
adb pull /sdcard/dump.1234 . -
从 Core Dump 中提取 Dex (Carving)
-
现在你有了一个 ELF 格式的内存镜像。你可以使用多种工具从其中扫描 Dex 文件:
-
DexExtractor (需编译): 专门用于此目的的工具。
-
LIEF (Python库): 一个强大的二进制文件解析和修改库,可以解析 ELF 并从其段和节中扫描 Dex 文件。
-
自定义脚本: 使用
hexdump,xxd,grep或编写 Python 脚本搜索 Dex 文件魔数64 65 78 0a 30 33 35 00(dex.035)。
-
-
使用 LIEF 的示例思路:
import lief dump = lief.parse("dump.1234") for segment in dump.segments: content = segment.content # 这是一个字节数组 # 在 content 中搜索 dex 魔数,并将找到的区域提取到文件
-
方法二:使用 dd 转储 (需要 Root)
dd 是 Linux 系统的“瑞士军刀”,可以直接读取设备文件。在 Linux/Android 中,每个进程的内存都映射在 /proc/[pid]/mem 这个虚拟文件中。我们可以用 dd 来读取它。
操作步骤:
-
获取目标进程的 PID (同方法一步骤 2)
-
确定内存映射范围
-
为了更高效地转储,我们通常只转储应用堆等可能包含 Dex 的区域,而不是整个 4GB 的虚拟地址空间。查看
/proc/[pid]/maps文件来了解内存布局。
adb shell su cat /proc/1234/maps > /sdcard/1234.maps-
拉取
maps文件到电脑分析:
adb pull /sdcard/1234.maps .-
分析
maps文件,寻找具有可读可执行 (r-xp) 权限的大内存块,这很可能是加载 Dex 或代码的地方。也关注[anon:libc_malloc]这样的堆区域。
-
-
使用 dd 转储特定内存区域
-
假设我们从
maps文件中发现一个区域:73a00000-73a8000 r-xp ... -
计算大小:
0x73a80000 - 0x73a00000 = 0x80000(524,288 bytes) -
使用
dd转储这个区域:
adb shell su dd if=/proc/1234/mem of=/sdcard/mem_dump_73a00000.bin bs=1 skip=$((0x73a00000)) count=524288-
注意:直接 dump 整个
/proc/pid/mem非常困难,因为地址空间中存在大量未映射的“空洞”,dd会遇到 I/O 错误而停止。因此分区域转储是更可行的方案。
-
-
从转储的 bin 文件中提取 Dex
-
现在你得到的是原始的内存块。你可以直接在这些
.bin文件上使用 carving 工具。 -
使用 DexExtractor (假设你已编译好):
./DexExtractor mem_dump_73a00000.bin
-
它会扫描文件,寻找 Dex 头,并尝试修复
checksum和signature,然后输出可被d2j-dex2jar或jadx反编译的 Dex 文件。
-
方法三:使用集成化工具 (推荐)
手动操作上述步骤非常繁琐。社区有一些强大的集成工具可以自动化整个过程:
-
Fridump (基于 Frida)
-
这是一个用 Python 编写的工具,利用 Frida 的 API 来转储进程内存。
-
优点: 不需要
gcore或dd,只需设备上运行 Frida-server 即可。可以转储整个地址空间并自动绕过一些空洞问题。 -
使用方法:
# 在电脑上运行 fridump -U -s com.example.targetapp -o output_dir/ -
它会在
output_dir生成很多内存块文件和一个maps文件,然后你可以用 carving 工具分析output_dir。
-
-
DexDump (基于 Frida)
-
同样是基于 Frida 的著名工具,它更智能:它不仅转储内存,还会主动遍历内存中的类加载器,寻找 DexFile 对象,并直接从内存中导出完整的 Dex 文件,成功率非常高。
-
使用方法:
frida -U -l dexdump.js -f com.example.targetapp --no-pause -
执行后,它通常会将导出的 Dex 文件保存到设备的
/sdcard目录。
-
总结与注意事项
| 特性 | gcore | dd | Fridump/DexDump |
|---|---|---|---|
| 易用性 | 中等 | 复杂 | 简单 |
| 可靠性 | 高 (生成标准ELF) | 高 (原始数据) | 高 |
| 完整性 | 完整进程镜像 | 需手动分块转储 | 完整或智能导出 |
| 需求 | Root, NDK工具 | Root | 只需Frida (通常需Root) |
| 输出 | ELF core dump | 原始内存块 | 内存块或直接得到Dex |
重要提示:
-
Root 权限:几乎所有方法都需要 Root 权限。在没有 Root 的设备上,几乎无法完成此操作。
-
对抗加固:高强度的加固方案会混淆 Dex 结构、阻止调试器附加(反制
gcore)、或抹去 Dex 头信息,这会增加 Carving 的难度。可能需要更高级的逆向技术来绕过。 -
合法性:请在获得授权的情况下对应用进行内存取证,切勿用于非法用途。
浙公网安备 33010602011771号