关于 dex2oat 以及 vdex、cdex、dex 格式转换
版权归作者所有,如有转发,请注明文章出处:https://cyrus-studio.github.io/blog/
dex2oat
dex2oat 是 Android 系统中的一个核心工具,负责将应用中的 .dex(Dalvik Executable)字节码编译为本地机器代码(native code),以提高运行效率。它的全称是 DEX to OAT(Optimized Android Transport)。
dex2oat 的作用
在 Android 系统中,Java/Kotlin 编写的应用在编译时会被转换为 .dex 文件。为了提高性能,Android 会将 .dex 文件进一步编译为 .oat 文件(或 .odex/.vdex 文件),以便设备可以直接执行本地代码而不是解释执行 .dex 字节码。
工作原理(简要流程)
输入:一个或多个 .dex 文件(通常来自 APK 中的 classes.dex)。
输出:生成 .oat(Optimized Android Executable)、.vdex(Verified DEX)和 .art(Android Runtime)文件。
过程:
-
验证并优化 .dex。
-
使用 AOT(Ahead-Of-Time)方式将 .dex 转为本地机器码。
-
生成平台相关的可执行文件,用于加快应用启动速度。
编译模式:
-
AOT(Ahead-Of-Time):安装或开机前就编译好,提高运行速度。
-
JIT(Just-In-Time):运行时动态编译,节省空间但牺牲性能。
-
Hybrid(混合):Android 7.0+ 默认模式,结合 AOT 与 JIT。
执行时机
-
第一次开机:系统会对预装的应用使用 dex2oat 编译。
-
App 安装时:根据编译策略(speed, quicken, interpret-only 等)选择是否编译。
-
系统升级/补丁后:部分 DEX 文件会重新编译。
文件说明
-
.dex:Dalvik 字节码文件,APK 中的代码格式。
-
.oat / .odex:包含从 dex 编译来的 优化后的中间表示代码。
-
.vdex:包含验证后的 dex 和优化信息。
-
.art:Android Runtime 中的预编译执行数据
vdexExtractor
vdexExtractor 是一个用于从 .vdex 文件中提取 DEX 文件的工具,它由 Google 安全团队成员 anestisb 开发。它支持将 Android 9 及以后版本中引入的 CompactDex (cdex) 格式转换为标准的 DEX 格式,方便我们使用常规反编译工具(如 jadx、dex2jar等)进行分析。
开源地址:https://github.com/anestisb/vdexExtractor
编译 vdexExtractor
先把 vdexExtractor clone 到本地。
由于 是在 Windows 的 WSL 中编译,所有要转换换行符
cyrus@cyrus:/mnt/d/Python/anti-app/vdexExtractor$ ./tools/deodex/run.sh -i /mnt/d/Python/anti-app/frida_dex_dump/dumped_dex -o /mnt/d/Python/anti-app/frida_dex_dump/dumped_dex_out
/usr/bin/env: ‘bash\r’: No such file or directory
执行下面命令转换 .sh 文件中的换行符为 Unix 格式的换行符
dos2unix make.sh
dos2unix ./tools/deodex/run.sh
dos2unix ./tools/deodex/constants.sh
执行 make.sh 开始编译,报错如下:
cyrus@cyrus:/mnt/d/Python/anti-app/vdexExtractor$ ./make.sh
make: Entering directory '/mnt/d/Python/anti-app/vdexExtractor/src'
rm -f *.o
rm -f */*.o
rm -f vdexExtractor
make: Leaving directory '/mnt/d/Python/anti-app/vdexExtractor/src'
make: Entering directory '/mnt/d/Python/anti-app/vdexExtractor/src'
gcc -c -std=c11 -D_GNU_SOURCE -Wall -Wextra -Werror -DVERSION=\"dev-78f283b\" -c dex.c -o dex.o
gcc -c -std=c11 -D_GNU_SOURCE -Wall -Wextra -Werror -DVERSION=\"dev-78f283b\" -c dex_instruction.c -o dex_instruction.o
dex_instruction.c:655:43: error: argument 2 of type ‘u4[kMaxVarArgRegs]’ {aka ‘unsigned int[kMaxVarArgRegs]’} declared as a variable length array [-Werror=vla-parameter]655 | void dexInstr_getVarArgs(u2 *code_ptr, u4 arg[kMaxVarArgRegs]) {| ~~~^~~~~~~~~~~~~~~~~~~
In file included from dex_instruction.c:23:
dex_instruction.h:278:32: note: previously declared as an ordinary array ‘u4[]’ {aka ‘unsigned int[]’}278 | void dexInstr_getVarArgs(u2 *, u4[]);| ^~~~
cc1: all warnings being treated as errors
make: *** [Makefile:56: dex_instruction.o] Error 1
make: Leaving directory '/mnt/d/Python/anti-app/vdexExtractor/src'
[-] build failed
改为通过 make 命令编译:
-
-Wno-error=vla-parameter:关闭 vla-parameter 警告(某些编译器会默认把这个警告当作错误)。
-
${CFLAGS}:保留已有的 CFLAGS(确保不覆盖其他选项)。
make CFLAGS="${CFLAGS} -Wno-error=vla-parameter" -C src
参考:https://github.com/anestisb/vdexExtractor/issues/82
编译成功
cyrus@cyrus:/mnt/d/Python/anti-app/vdexExtractor$ make CFLAGS="${CFLAGS} -Wno-error=vla-parameter" -C src
make: Entering directory '/mnt/d/Python/anti-app/vdexExtractor/src'
cc -Wno-error=vla-parameter -c dex.c -o dex.o
cc -Wno-error=vla-parameter -c dex_instruction.c -o dex_instruction.o
cc -Wno-error=vla-parameter -c log.c -o log.o
cc -Wno-error=vla-parameter -c out_writer.c -o out_writer.o
cc -Wno-error=vla-parameter -c utils.c -o utils.o
cc -Wno-error=vla-parameter -c vdexExtractor.c -o vdexExtractor.o
cc -Wno-error=vla-parameter -c vdex_api.c -o vdex_api.o
cc -Wno-error=vla-parameter -c hashset/hashset.c -o hashset/hashset.o
cc -Wno-error=vla-parameter -c vdex/vdex_006.c -o vdex/vdex_006.o
cc -Wno-error=vla-parameter -c vdex/vdex_010.c -o vdex/vdex_010.o
cc -Wno-error=vla-parameter -c vdex/vdex_019.c -o vdex/vdex_019.o
cc -Wno-error=vla-parameter -c vdex/vdex_021.c -o vdex/vdex_021.o
cc -Wno-error=vla-parameter -c vdex/vdex_backend_006.c -o vdex/vdex_backend_006.o
cc -Wno-error=vla-parameter -c vdex/vdex_backend_010.c -o vdex/vdex_backend_010.o
cc -Wno-error=vla-parameter -c vdex/vdex_backend_019.c -o vdex/vdex_backend_019.o
cc -Wno-error=vla-parameter -c vdex/vdex_backend_021.c -o vdex/vdex_backend_021.o
cc -Wno-error=vla-parameter -c vdex/vdex_decompiler_006.c -o vdex/vdex_decompiler_006.o
cc -Wno-error=vla-parameter -c vdex/vdex_decompiler_010.c -o vdex/vdex_decompiler_010.o
cc -Wno-error=vla-parameter -c vdex/vdex_decompiler_019.c -o vdex/vdex_decompiler_019.o
cc -Wno-error=vla-parameter -c vdex/vdex_decompiler_021.c -o vdex/vdex_decompiler_021.o
cc dex.o dex_instruction.o log.o out_writer.o utils.o vdexExtractor.o vdex_api.o hashset/hashset.o vdex/vdex_006.o vdex/vdex_010.o vdex/vdex_019.o vdex/vdex_021.o vdex/vdex_backend_006.o vdex/vdex_backend_010.o vdex/vdex_backend_019.o vdex/vdex_backend_021.o vdex/vdex_decompiler_006.o vdex/vdex_decompiler_010.o vdex/vdex_decompiler_019.o vdex/vdex_decompiler_021.o -lm -lz -o vdexExtractor
cp vdexExtractor ../bin/vdexExtractor
make: Leaving directory '/mnt/d/Python/anti-app/vdexExtractor/src'
编译成功后可以看到 bin 目录下多了一个 vdexExtractor 文件
vdexExtractor 的作用就是从 vdex 中抽取 cdex 文件。还需要 compact_dex_converter 把 cdex 转换成标准的 dex。
cdex
Android 9 引入 CompactDex(.cdex,magic 为 cdex001),是 DEX 的压缩优化版本。
优化后的 dex/cdex 通常存放在:
/data/app/package_name/oat/arm64/base.odex
/data/app/package_name/oat/arm64/base.vdex
在 Android 9(Pie)中,APP 的 .cdex 文件 是由 dex2oat 优化生成的,通常以 odex, vdex 或直接优化后的 .art 文件形式存在。
compact_dex_converter
下载 compact_dex_converter (把 CompactDexFile 转换为标准 Dex)
https://github.com/anestisb/vdexExtractor?#compact-dex-converter
由于 constants.sh 中下载链接已经失效了。我们手动把下载的 compact_dex_converter 放到下面路径:
vdexExtractor\tools\deodex\hostTools\Linux\api-API_29\bin\compact_dex_converter
注释掉 /tools/deodex/run.sh 中 deps_prepare_env “$apiLevel” 的方调用,去掉自动下载 compact_dex_converter
vdex 转换 dex
执行 run.sh 开始转换
cyrus@cyrus:/mnt/d/Python/anti-app/vdexExtractor$ ./tools/deodex/run.sh -i ./test -o ./test
[INFO]: Processing 1 input Vdex files
[INFO]: 1 binaries have been successfully deodexed
cdex 转换 dex
通过 compact_dex_converter 把 cdex 转换 dex
./tools/deodex/hostTools/Linux/api-API_29/bin/compact_dex_converter -w ./test ./test/base.cdex
调用示例
进入 /data/app 目录下找到目标 app 的 oat 文件存放路径
wayne:/data/app # ls
com.android.chrome-b1d3YEy1eVrwwjPOa1oq5A== com.iflytek.inputmethod-s1r9JFv0-eKNskzHyrh_vQ==
com.cyrus.example-uIsySv7lFm21qMVPnPJ-pw== com.shizhuang.duapp-fTxemmnM8l6298xbBELksQ==
com.cyrus.example.plugin-YsXrxPvfWYdsWHxFKjcusw== com.tencent.mm-ql7ajyK9JqKXli5pgu88nw==
com.cyrus.example.test-R06ZNyf5doqJFOcZ6EaYHQ== com.xingin.xhs-HeYr1dfB-rU7NjxJiLiDeg==
进入目标目录,找到 base.art base.odex base.vdex
wayne:/data/app # cd com.shizhuang.duapp-fTxemmnM8l6298xbBELksQ==
wayne:/data/app/com.shizhuang.duapp-fTxemmnM8l6298xbBELksQ== # ls
base.apk lib oat
wayne:/data/app/com.shizhuang.duapp-fTxemmnM8l6298xbBELksQ== # cd oat
wayne:/data/app/com.shizhuang.duapp-fTxemmnM8l6298xbBELksQ==/oat # ls
arm64
wayne:/data/app/com.shizhuang.duapp-fTxemmnM8l6298xbBELksQ==/oat # cd arm64/
wayne:/data/app/com.shizhuang.duapp-fTxemmnM8l6298xbBELksQ==/oat/arm64 # ls
base.art base.odex base.vdex
拉取到本地
adb pull /data/app/com.shizhuang.duapp-fTxemmnM8l6298xbBELksQ==/oat/arm64 ./test
执行 run.sh 把 vdex 转换为 dex
./tools/deodex/run.sh -i ./test -o ./test
输出如下:
cyrus@cyrus:/mnt/d/Python/anti-app/vdexExtractor$ ./tools/deodex/run.sh -i ./test -o ./test
[INFO]: Processing 1 input Vdex files
[INFO]: 1 binaries have been successfully deodexed
转换完成后的 dex 文件
拖入 jadx 能正常识别