ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

vdexExtractor 实战:从 Vdex 到 Dex 的完整转换指南

2026/10/2 5:49:20 拓冰建站 浏览量
vdexExtractor 实战:从 Vdex 到 Dex 的完整转换指南 1. 为什么需要把 Vdex 转换回 Dex做安卓应用分析和系统调试的朋友应该都有过这种经历从设备或者系统镜像里捞出一个.vdex后缀的文件打开看一眼全是二进制乱码用file命令一看显示的是Android dex file或者干脆是未知格式。这时候如果想把里面的代码逻辑拿出来用jadx或者jeb看看直接喂给工具大概率会报错或者提示文件格式不支持。其实.vdex就是 ART 虚拟机在安装应用时生成的一种打包文件里面装着优化过的 dex 字节码和一堆验证数据。从 Android 7.0Nougat开始系统把 dex、验证结果、快速编译产生的辅助信息统一塞进了 vdex 文件里用来加快应用启动速度、减少运行时开销。但这个格式对分析工具很不友好——jadx、jeb、baksmali这些反编译工具认的是标准 dex 文件碰到 vdex 基本不认。vdexExtractor 就是干这个的输入一个 vdex 文件经过解析、校验、抽取输出标准的 dex 文件。拿到 dex 之后后面怎么反编译、怎么分析就完全回到你熟悉的流程上了。这篇文章我会从 vdex 的结构原理讲起覆盖 vdexExtractor 的编译部署、完整命令行操作、常见报错排查方法最后再分享几个实战中才用得上的细节。内容面向的是有安卓开发基础、想深入做应用逆向分析或系统研究的同学纯小白的建议先补一下 dex 和 ART 虚拟机的基础知识再来读。2. Vdex 文件结构拆解搞懂格式才能玩得转2.1 一个 Vdex 文件里到底装了什么要理解 vdexExtractor 为什么能还原 dex得先搞清楚 vdex 的文件布局。一个标准的 vdex 文件由三个部分组成依次排列vdex 文件头Header记录了魔数、版本号、dex 文件数量、dex 文件在 vdex 中的偏移量和大小以及验证数据、快速编译数据的偏移和大小。dex 文件区Dex Section包含一个或多个 dex 文件。这里可能是完整未压缩的 dex也可能是经过压缩存储的 dex 数据。辅助数据区Auxiliary Data存放 dex 文件对应的校验注解Verifier Dependencies和快速编译数据Quickening Data这部分是 ART 为了运行时优化额外生成的分析代码逻辑时通常用不上。用十六进制编辑器打开 vdex 文件头部最开始四个字节如果是76 64 65 78也就是 ASCII 码的vdex那基本可以确认这是一个 vdex 文件。再往后读取 12 字节处的版本号常见的版本有019、021、027、028、029等分别对应不同的 Android 版本和编译器实现。vdexExtractor 能做的就是解析文件头拿到 dex 文件的偏移和大小然后根据这些元数据把 dex 区域的数据完整抠出来同时绕过或修正校验机制最终输出标准的.dex文件。2.2 为什么不能直接把 dex 部分复制出来这就得提到 vdex 文件里的 dex 数据状态了。Android 编译器在生成 vdex 时对 dex 文件区做了特殊处理如果 dex 文件的校验和checksum与文件头记录的 checksum 不一致ART 会触发重新验证机制把 dex 重新解析一遍而在部分 Android 版本上vdex 里的 dex 是以已进行快速编译的形态存在的里面的某些指令可能已经被替换或者打上了补丁。还有一个典型坑在 Android 8.0 到 9.0 时期speed-profile编译模式下的 vdex其内嵌 dex 的 checksum 是被人为修改过的。直接用dd之类的命令把 dex 区块扣出来得到的 dex 文件往往打不开jadx会报Dex checksum mismatch之类的错误。所以真正靠谱的方式是走 vdexExtractor 这类工具它会自动处理 checksum 修复和 dex 重建工作。vdexExtractor 在抽取 dex 文件后会自动重算 dex 头的 checksum把文件修正为标准合法的 dex 格式。这也是我强烈建议用它而不是自己写脚本去抠数据的原因——省事而且处理边界场景的能力比野路子脚本强得多。3. vdexExtractor 的编译部署从源码到可执行文件3.1 环境准备与依赖关系vdexExtractor 是 C 语言项目依赖系统工具链编译没有特别复杂的外部库依赖。官方推荐在 Linux 或 macOS 环境下编译Windows 环境下需要用 WSL 或者 Cygwin但我实测下来还是 WSL 最省心。编译环境需要以下基础工具git拉取源码make执行编译脚本gcc或clangC 编译器zlib开发库用于解压部分压缩存储的 dex 数据以 Ubuntu 22.04 为例安装依赖就三条命令sudo apt update sudo apt install git make gcc zlib1g-devmacOS 需要确认安装了 Xcode Command Line Toolsxcode-select --install跑一下即可。zlib 在 macOS 上是自带的不需要额外装。这里有个细节值得注意vdexExtractor 支持交叉编译可以编译出在 Windows 上运行的.exe版本但需要先装好 mingw-w64 工具链。比如用以下命令在 Linux 上交叉编译 Windows 版本make mingw如果只是日常分析用我更推荐直接在有 vdex 文件的平台上原生编译少一层交叉编译的兼容性折腾。3.2 完整编译流程与验证源码仓库在 GitHub 上搜索 vdexExtractor 就能找到。克隆到本地后直接make编译git clone https://github.com/anestisb/vdexExtractor.git cd vdexExtractor make编译完成后在bin目录下会生成vdexExtractor可执行文件。先验证一下版本信息和帮助文档是否正常./bin/vdexExtractor -h如果能看到类似vdexExtractor 0.5.3的版本输出和参数说明就说明编译成功了。建议顺手把二进制文件复制到一个单独的目录里配合后续分析工作统一管理。编译过程中如果遇到make: command not found那说明系统没装构建工具Ubuntu 上执行sudo apt install build-essential即可。遇到zlib.h: No such file or directory说明 zlib 开发包没装重新安装zlib1g-dev然后重新编译。整个编译过程通常在一两分钟内完成没有任何魔法跑完就能拿到可执行文件。这一款工具这么轻量也是我优先推荐它的原因之一——对比那些一堆依赖的 Python 脚本和需要配置环境的 IDA 插件它真的是一键搞定。4. 实操全流程一行命令完成 Vdex 转 Dex4.1 基本转换命令与参数说明拿到编译好的 vdexExtractor使用场景非常直接。基础命令长这样./bin/vdexExtractor -i input.vdex -o output_dir参数含义如下-i指定输入的 vdex 文件路径-o指定输出目录转换得到的 dex 文件会放在这个目录下跑完之后去 output_dir 里看一眼里面应该会出现classes.dex、classes2.dex之类的文件名称取决于原始 vdex 里封装了几个 dex。如果是多 dex 应用vdex 里会包含多个 dex 文件工具会依次导出并自动命名。实际分析时我习惯加一个-f参数./bin/vdexExtractor -i input.vdex -o output_dir -f-f表示输出 dex 文件时用 unzip 格式重新压缩这样输出的文件体积更小后续传给别人分析或者归档保存都比较方便。不加-f时输出的是不压缩的 dex虽然文件更大但个别反编译工具对未压缩 dex 的兼容性反而更好这取决于你后续工具链的偏好。还有一个值得关注的参数是--ignore-crc-error./bin/vdexExtractor -i input.vdex -o output_dir --ignore-crc-error这个参数的意思是忽略 dex 文件的 CRC 校验错误。前面提过部分 vdex 里的 dex checksum 是被人为改过的正常情况下 vdexExtractor 会尝试自动修复但如果遇到修复失败或者特殊变体加这个参数可以直接跳过校验强行抽取。优先级排序是先不加参数跑一遍报 CRC 错误了再加--ignore-crc-error重试不要一上来就跳过校验否则抽出来的 dex 可能有隐患。4.2 验证转换结果是否可用转换完成后不要急着关终端先验证一下输出文件是不是合法 dex。第一步用file命令检查格式file output_dir/classes.dex正常输出应该类似classes.dex: Dalvik dex file version 035看到Dalvik dex file字样就基本没问题了。如果输出是data或者Java archive (JAR)说明格式不对需要返回上一步检查参数。第二步用dexdump或者jadx验证可分析性。以jadx为例jadx output_dir/classes.dex -d output_src如果能够成功输出反编译后的 Java 代码那这个 dex 就完全可用了。这一步是最终验收标准——你自己写的脚本说格式合法不算反编译工具认了才算真能用。实战中我还习惯用baksmali做二次验证把 dex 转成 smali 指令看看整体结构是否完整。如果 smali 解析顺利顺手还能对比一下 vdex 原始文件里的快速编译指令是不是被正常还原成了标准指令。这一层验证对后面代码审计或者去做深度分析的可靠性很重要。4.3 批量转换多个 Vdex 文件实际项目里经常碰到一个目录下有几十个 vdex 文件比如系统镜像里/system/framework目录下成堆的中间框架包一个一个跑命令太原始了。我一般用for循环批量处理mkdir -p dex_output for vdex in $(find . -name *.vdex); do name$(basename $vdex .vdex) mkdir -p dex_output/$name ./bin/vdexExtractor -i $vdex -o dex_output/$name -f done这样每个 vdex 都会有一个独立输出目录不会被混到一起。批量跑的时候要注意输出信息里是否有大量[ERROR]行如果是特定文件反复报错单独拎出来手动调试别让报错在脚本里被淹没了。我用这个方式处理过一整个系统镜像的 framework 目录大概 40 多个 vdex 文件全程没用超过两分钟——这也是 vdexExtractor 相比其他要跑虚拟机或 Python 模拟器的方案最大的优势纯 C 编译出的原生二进制效率确实没得说。5. 分版本适配Android 8.0 到 12.0 的 Vdex 差异与处理5.1 不同版本的文件头与编译器差异vdexExtractor 之所以要不断更新版本根本原因是 ART 编译器在 Android 各版本间变动太频繁vdex 的文件头格式、辅助数据结构、dex 存储形态都在跟着变。拿版本号来说早期 Android 8.0 的 vdex 版本是006Android 8.1 是010Android 9.0 有012和017Android 10 用019Android 11 升级到021Android 12 则是027和028不同版本之间 dex 区的 metadata 布局和校验逻辑都有差异。如果你的 vdexExtractor 版本比较旧碰到新版 vdex 文件时会报Header check failed或者Unsupported vdex version之类的错误。破解办法有两个一是升级 vdexExtractor 到最新版本这是首选二是老项目里如果必须用旧工具仔细阅读报错信息里提示的版本范围看是否有兼容模式可以强行解析。这里就得提醒一句平时注意留意 vdexExtractor 仓库的更新动态新手机发布或者 Android 大版本更新之后最好立刻拉一下源码重新编译因为大概率那个版本的新 vdex 变体会被社区在几周内修复并合入主分支。5.2 特殊变体处理压缩 dex 与快速编译指令Android 9 及之后的版本中vdex 文件里的 dex 出于节省空间考虑可能会以storage mode为kDexStorageModeJar的方式存储也就是把 dex 文件压缩进 jar 容器里。vdexExtractor 对这类变体是支持的会在解析时自动解压前提是你的编译环境里有 zlib 支持。这也就是为什么 3.1 里特意强调要装zlib1g-dev。还有一种变体是 dex 里包含快速编译指令。ART 的 quickening 过程会把部分 dex 操作码替换为优化过的快速指令这些指令对标准 dex 格式来说是不合法的。vdexExtractor 在抽取时会把受影响的操作码还原回标准 dex 指令保证输出文件能正常通过 dex 文件格式校验。如果抽出来的 dex 在 jadx 里打开后大量方法体是空的或者CodeItem解析异常十有八九就是快速编译指令还原不彻底。这时候建议用最新版工具重新抽取并配合baksmali --use-locals之类的去混淆参数辅助分析。6. 典型报错与排查记录照着速查表解决问题6.1 Header check failed 和其他解析类错误我整理了一张速查表覆盖 vdexExtractor 最常见的几类报错。报错信息触发场景解决办法Header check failed文件头魔数或版本号不匹配确认文件是否为有效 vdex升级工具到最新版Unsupported vdex versionvdex 版本超出工具支持范围更新源码重新编译CRC check faileddex 校验和与文件头不一致加--ignore-crc-error参数尝试强行抽取Failed to open file输入路径不对或权限不足检查文件路径和设备权限用 chmod 或 sudozlib decompression failed压缩 dex 解压失败确认 zlib 开发库安装正确文件可能损坏No dex files found in input输入文件不是标准 vdex 结构用十六进制编辑器检查文件头魔数遇到Header check failed时我优先怀疑文件版本过新先看看文件头的版本号再到工具源码的vdex_versions.h里比对一下支持列表。如果确实不在支持范围内就直接升级工具不用浪费时间在旧版本上变魔法。6.2 输出 dex 不可用时的排查方向有一种情况是命令执行没有任何报错输出的 dex 也能用file识别但jadx反编译时大量类缺失、方法体为空白。这种假成功的现场我的排查顺序是第一确认 vdexExtractor 输出的 dex 文件大小是否正常。如果 dex 文件只有几 KB而原始 vdex 有几十 MB那肯定是抽取环节出了问题。第二试试不加-f参数重新输出未压缩 dex个别反编译工具对压缩过的 dex 解析支持不佳。第三如果还是不行用dexdump直接 dump dex 的 method 数量和一些关键偏移量和原始 apk 的 dex 做交叉比对看数据是否完整。大部分假成功本质还是工具版本和文件变体不匹配造成的升级工具版本能解决绝大多数问题。7. 实战中的几个细节来自一线折腾的经验7.1 优先从系统目录批量收集 Vdex 文件分析一台设备上的应用时vdex 文件不只局限在/data/app目录/system/framework和/system/app下也会有大量系统应用和框架包的 vdex。尤其在分析系统级问题、搞框架层调试或者对比系统内置组件行为时系统目录里的 vdex 价值反而更大——系统应用的 dex 是系统镜像的一部分很多厂商改过的 API 实现就藏在这些包里。我自己比较常用的收集方式是 adb pull 整个相关目录adb pull /system/framework framework_vdex adb pull /data/app app_vdex/data/app目录需要 root 权限才能读取这一点要先确认。如果你做的是非 root 设备上的应用分析vdex 文件基本拿不到那 vdexExtractor 就不是对口工具了建议换思路从 apk 本身的 dex 入手。7.2 和 jadx、frida 配合的完整分析工作流vdexExtractor 通常不是终点做完转换之后还得跟后续分析工具串起来才完整。我个人比较顺手的流程是这样第一步用 vdexExtractor 把 vdex 转成 dex得到干净的 dex 文件集。第二步把 dex 重新打包进一个只有 dex 的 apk 或者直接喂给 jadxjadx -d output_src output_dir/classes.dex第三步如果反编译出来的代码太乱配合baksmali转成 smali然后用 smali 级别动态调试baksmali d output_dir/classes.dex -o smali_out第四步需要动态验证的时候结合frida通过 hook 关键方法观察运行时行为和参数变化形成静态分析和动态分析的闭环。这套流程我用在很多次应用行为分析、崩溃问题排查和兼容性调查中基本覆盖了日常遇到的绝大多数需求场景。值得一提的是vdex 里既有 dex 也有 ART 的验证信息某些场景下这些验证信息能间接反映应用在设备上的真实运行方式做 frida hook 时对照着 vdex 里的版本信息选择匹配的 hook 点成功率会明显提升。7.3 输出文件的管理与归档建议处理大量 vdex 文件时输出结果的命名和归档混乱是一个容易被忽略但很费时间的问题。vdexExtractor 对输出文件会自动按原始名字命名但多个 apk 产生的 dex 可能都是classes.dex放在同一个目录下直接互相覆盖。我习惯按包名或 apk 名建立两级目录结构类似这样analysis_output/ ├── com.example.app1/ │ ├── classes.dex │ └── classes2.dex ├── com.example.app2/ │ └── classes.dex └── framework_jar/ ├── classes.dex └── classes2.dex配合批量转换脚本里的mkdir -p就能实现这个结构成本很低。等到要做横向对比或者回去翻旧项目的时候你就会发现这个分类方式能帮你省出大量时间。还有一个小建议转换成功后顺手生成一个 MD5 校验文件把原始 vdex 和输出 dex 的哈希值都记下来。设备上拿到的 vdex 可能随时会被系统重新编译替换留一份哈希记录后续做版本对比或者判断系统是不是重新优化过会非常有用。命令行一行就搞定md5sum input.vdex output_dir/classes.dex checksums.txt7.4 版本兼容性的坑要提前踩最后重点说一个我自己被坑过很多次的问题系统镜像里的 vdex 和当前设备系统版本并不总是一一对应的。厂商 OTA 升级后/system/framework下的 vdex 可能还是旧系统版本编译出来的而/data/app下的 vdex 已经被新系统重新编译过了。这时候同一台设备上可能存在两个不同版本的 vdex 文件用固定版本的 vdexExtractor 必然有一半会解析失败。我的处理方法是写一个 wrapper 脚本先自动识别 vdex 头部版本号再根据版本选择已编译好的对应版本工具version$(xxd -p -l 4 -s 12 input.vdex | xxd -r -p) echo vdex version: $version版本确认后手动指定工具路径去处理单个文件。虽然不是完全自动化但能明显降低排查成本。如果你工作环境里需要频繁处理不同 Android 版本的 vdex建议在本地一次性编译好几个主流版本的 vdexExtractor 备用。另外提一句vdexExtractor 项目本身维护频率不算高隔一段时间就会有新版本或者新分支出现遇到新版 vdex 解析不了就去看一下仓库的 recent commits一般能找到答案。这工具整体已经比较成熟核心功能这么多年没有大变化掌握本文的内容之后应付日常分析已经足够了。我在实际项目里用它处理过的 vdex 文件数量至少上千了从 Android 8 到 Android 14 的设备都有覆盖可以说每一轮都是先转 dex、再丢 jadx、配合 frida 动态验证这套组合拳几乎没让我失望过。希望这篇文章的细节能帮你把 vdexExtractor 这条工具体系完整跑通少踩几个我当年踩过的坑。