ilylty/android_eBPF_dump_dex

GitHub: ilylty/android_eBPF_dump_dex

一款利用 Android ARM64 eBPF uprobe 技术在运行时动态转储受保护应用 DEX 文件的逆向分析工具。

Stars: 2 | Forks: 1

# android_eBPF_dump_dex Android ARM64 eBPF uprobe DEX 转储工具。 ## 快速开始 在设备上使用 `run_dexdump_full.sh`。将以下文件放在同一目录下: ``` /data/local/tmp/dex_dump_bin /data/local/tmp/dex_dump.bpf.o /data/local/tmp/run_dexdump_full.sh ``` 部署: ``` adb push dex_dump.bpf.o /data/local/tmp/ adb push dex_dump_bin /data/local/tmp/ adb push run_dexdump_full.sh /data/local/tmp/ adb shell chmod +x /data/local/tmp/dex_dump_bin /data/local/tmp/run_dexdump_full.sh ``` 从具有完整权限的设备端 root shell 运行: ``` adb shell su cd /data/local/tmp ./run_dexdump_full.sh run ``` 在脚本运行并等待事件后,打开目标受保护的应用程序并进行交互,直到触发其解包或 DEX 加载逻辑。 后台模式: ``` cd /data/local/tmp ./run_dexdump_full.sh start ./run_dexdump_full.sh logs ./run_dexdump_full.sh stop ``` 该脚本会自动: - 检查 `dex_dump_bin` 和 `dex_dump.bpf.o` - 将诊断信息写入 `dexdump.log` - 清理旧的 `dump_pid_*.dex` 文件 - 携带 `-scan` 参数运行 - 默认使用 `MAX_DUMP=0` 转储完整的 DEX 文件 实用的脚本环境变量: - `EXTRA_ARGS`:传递给 `dex_dump_bin` 的额外参数,例如 `-symbol ...` 或 `-offset 0x...` - `MAX_DUMP`:每个 DEX 区域转储的最大字节数。默认为 `0`,表示完整转储 - `LIB`:ART DEX 库路径。默认为 `/apex/com.android.art/lib64/libdexfile.so` - `OUT`:转储输出目录。默认为脚本所在目录 - `WORKDIR`:包含 `dex_dump_bin` 和 `dex_dump.bpf.o` 的目录。默认为脚本所在目录 示例: ``` ./run_dexdump_full.sh run MAX_DUMP=65536 ./run_dexdump_full.sh run EXTRA_ARGS="-symbol '_ZNK3art16ArtDexFileLoader4OpenEPKhmRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEjPKNS_10OatDexFileEbbPS9_NS3_10unique_ptrINS_16DexFileContainerENS3_14default_deleteISH_EEEE'" ./run_dexdump_full.sh run EXTRA_ARGS="-offset 0x19a90" ./run_dexdump_full.sh run ``` 拉取结果: ``` adb pull /data/local/tmp . adb pull /data/local/tmp/dexdump.log . ``` ## 构建 GitHub Actions 构建两个产物: - `dex_dump.bpf.o`:eBPF 字节码 - `dex_dump_bin`:Android ARM64 用户空间加载器/转储器 从 Actions 手动运行 `Build Android eBPF Dex Dumper` 工作流,或者推送到 `main` 分支。 ## 部署 ``` adb push dex_dump.bpf.o /data/local/tmp/ adb push dex_dump_bin /data/local/tmp/ adb shell chmod +x /data/local/tmp/dex_dump_bin ``` ## 运行 从具有完整权限的设备端 root shell 运行。在某些设备上,`adb shell su -c` 会继承 `adbd` 受限的 capability bounding set,无法加载 eBPF 程序。 基本运行: ``` adb shell su cd /data/local/tmp ./dex_dump_bin -lib /apex/com.android.art/lib64/libdexfile.so -package com.example.target -max-dump 65536 ``` 如果包已经在运行,`-package` 会解析当前的 PID 并将事件过滤到该 PID。如果应用程序在转储器之后启动,请在不带 `-package` 的情况下启动转储器,或者先启动应用程序,然后再使用 `-package` 运行转储器。 如果不使用 `-package`,将观察所有的 DEX 加载事件: ``` ./dex_dump_bin -lib /apex/com.android.art/lib64/libdexfile.so -max-dump 65536 ``` 测试使用 `readelf` 找到的符号时,请使用显式符号: ``` ./dex_dump_bin \ -lib /apex/com.android.art/lib64/libdexfile.so \ -symbol '_ZNK3art16ArtDexFileLoader4OpenEPKhmRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEjPKNS_10OatDexFileEbbPS9_NS3_10unique_ptrINS_16DexFileContainerENS3_14default_deleteISH_EEEE' \ -package com.example.target \ -max-dump 65536 ``` 如果符号查找失败或符号被剥离,请使用文件偏移量: ``` ./dex_dump_bin \ -lib /apex/com.android.art/lib64/libdexfile.so \ -offset 0x19a90 \ -package com.example.target \ -max-dump 65536 ``` 启用头部扫描作为后备方案。这将在启动后和 DEX 事件发生后扫描可读内存范围以查找 DEX 头部: ``` ./dex_dump_bin -lib /apex/com.android.art/lib64/libdexfile.so -package com.example.target -scan -max-dump 65536 ``` 如果从 ADB root 加载 eBPF 失败,请从真实的 root 上下文(例如通过 Magisk 服务或本地终端 root shell)创建并运行设备端脚本: ``` cd /data/local/tmp ./dex_dump_bin -lib /apex/com.android.art/lib64/libdexfile.so -package com.example.target -scan -max-dump 65536 ``` 转储的文件写入 `/data/local/tmp//` 下。 文件名格式: ``` dump_pid__0x__.dex ``` 拉取结果: ``` adb pull /data/local/tmp . adb pull /data/local/tmp/dexdump.log . ``` 实用标志: - `-obj`:`dex_dump.bpf.o` 的路径 - `-lib`:ART DEX 库的路径,默认为 `/apex/com.android.art/lib64/libdexfile.so` - `-symbol`:要进行 uprobe 的可选符号名称。如果未设置,程序将尝试使用 `main.go` 中的内置符号列表 - `-offset`:要进行 uprobe 的可选文件偏移量。如果非零,它将覆盖符号查找 - `-out`:转储输出目录,默认为 `/data/local/tmp` - `-max-dump`:每个 DEX 区域转储的最大字节数,默认为 `65536`。使用 `0` 转储报告的完整大小 - `-package`:可选的 Android 包名过滤器 - `-scan`:在事件发生后扫描目标进程内存以查找 DEX 头部。需要 `-package` 才能进行初始扫描 ## 查找 Hook 符号 不同的 Android 版本和 ROM 将 ART DEX 加载代码放在不同的库中。不要想当然地认为 `libart.so` 总是包含您需要的函数。 本项目目前 hook 的是 DEX 内存打开函数,其中第一个有用的参数是: ``` x0 = this x1 = const uint8_t* base x2 = size_t size ``` 这与当前的 eBPF 程序相匹配,该程序从寄存器 `x1` 读取 DEX 基址,从 `x2` 读取大小。 ### 1. 检查哪个库包含符号 从 `libdexfile.so` 开始,如果需要,再检查 `libart.so`: ``` adb shell "readelf -Ws /apex/com.android.art/lib64/libdexfile.so | grep -iE 'ArtDexFileLoader|DexFileLoader|DexFileC|OpenCommon|OpenWithDataSection'" adb shell "readelf -Ws /apex/com.android.art/lib64/libart.so | grep -iE 'ArtDexFileLoader|DexFileLoader|DexFileC|OpenCommon|OpenWithDataSection'" ``` 有用的符号通常如下所示: ``` art::ArtDexFileLoader::Open(const uint8_t*, size_t, ...) art::DexFileLoader::Open(const uint8_t*, size_t, ...) art::DexFileLoader::OpenWithDataSection(const uint8_t*, size_t, ...) art::DexFile::DexFile(const uint8_t*, size_t, ...) ``` 在修饰(mangled)形式中,来自一台测试设备的示例是: ``` _ZNK3art16ArtDexFileLoader4OpenEPKhmRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEjPKNS_10OatDexFileEbbPS9_NS3_10unique_ptrINS_16DexFileContainerENS3_14default_deleteISH_EEEE _ZNK3art13DexFileLoader4OpenEPKhmRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEjPKNS_10OatDexFileEbbPS9_NS3_10unique_ptrINS_16DexFileContainerENS3_14default_deleteISH_EEEE _ZN3art7DexFileC2EPKhmS2_mRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEjPKNS_10OatDexFileENS3_10unique_ptrINS_16DexFileContainerENS3_14default_deleteISG_EEEEb ``` 重要的部分是修饰名中的 `EPKhm`。它表示该函数具有 `const uint8_t*` 和 `size_t` 参数,即 DEX 基址和 DEX 大小。 ### 2. 忽略未定义的符号 `readelf` 可能会显示 `UND` 条目。这些是导入的,而不是实现。Hook 包含实际地址的库。 好的情况: ``` 0000000000019a90 FUNC GLOBAL PROTECTED ... _ZNK3art16ArtDexFileLoader4OpenEPKhm... ``` 坏的情况: ``` 0000000000000000 FUNC GLOBAL DEFAULT UND _ZNK3art16ArtDexFileLoader4OpenEPKhm... ``` 如果该符号在 `libart.so` 中是 `UND`,请检查 `libdexfile.so`。 ### 3. 无需重新构建测试符号 如果您在 `libdexfile.so` 中找到了一个符号,请使用 `-symbol` 传递它: ``` ./dex_dump_bin \ -lib /apex/com.android.art/lib64/libdexfile.so \ -symbol '_ZNK3art16ArtDexFileLoader4OpenEPKhmRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEjPKNS_10OatDexFileEbbPS9_NS3_10unique_ptrINS_16DexFileContainerENS3_14default_deleteISH_EEEE' \ -package com.example.target \ -max-dump 65536 ``` 如果符号名被剥离或 `link.Uprobe` 无法解析它们,请使用 `readelf` 中的符号值作为文件偏移量: ``` ./dex_dump_bin \ -lib /apex/com.android.art/lib64/libdexfile.so \ -offset 0x19a90 \ -package com.example.target \ -max-dump 65536 ``` ### 4. 在 `main.go` 中需要更改什么 在大多数情况下,您只需要更改默认库或内置符号列表。 默认库路径位于 `main.go` 中: ``` libPath := flag.String("lib", "/apex/com.android.art/lib64/libdexfile.so", "path to target ART dex library") ``` 内置符号位于 `attachDexOpen()` 中: ``` symbols := []string{ "_ZNK3art16ArtDexFileLoader4OpenEPKhm...", "_ZNK3art13DexFileLoader4OpenEPKhm...", "_ZN3art7DexFileC2EPKhmS2_m...", } ``` 将您找到的符号添加到此列表中,如果它最适合您的 ROM,最好放在顶部附近。 如果您从命令行使用 `-symbol` 或 `-offset`,则不需要编辑 `main.go`。 ### 5. 何时必须更改 `dex_dump.bpf.c` 仅当函数签名不同时,才更改 eBPF 参数寄存器。 当前预期的布局: ``` x1 = DEX base x2 = DEX size ``` 如果您 hook 的函数中 DEX 指针和大小位于不同的参数中,请相应地更新 `dex_dump.bpf.c`。例如: ``` event.base = ctx->regs[1]; event.size = ctx->regs[2]; ``` 将 `regs[1]` 和 `regs[2]` 更改为您选择的函数正确的 ARM64 参数寄存器。 ### 6. 实用工作流程 1. 在 `libdexfile.so` 和 `libart.so` 上运行 `readelf -Ws`。 2. 选择一个包含 `EPKhm` 或等效的 `const uint8_t*, size_t` 签名的已定义符号。 3. 首先使用 `-symbol` 进行测试。 4. 如果符号查找失败,请使用 `readelf` 中的地址通过 `-offset` 进行测试。 5. 如果有效,可选择将该符号添加到 `main.go` 中的 `attachDexOpen()`。 6. 重新构建并重新部署。 ## Root 注意事项 在某些 Magisk/root 设备上,`adb shell su -c` 仍然缺乏加载 eBPF 程序所需的 capabilities,因为它继承了 `adbd` 受限的 capability bounding set。 如果加载失败并提示 `operation not permitted` 或 memlock/capability 错误,请从真实的设备端 root 上下文(例如 Magisk 服务脚本或具有完整权限的本地 root shell)运行转储器。
标签:Android, ARM64, Docker镜像, DSL, EVTX分析, uprobe, 云资产清单, 日志审计, 脱壳, 逆向工程