odex 是 Optimized dex 的缩写,是优化后的dex文件,通过将 apk 中的 dex 文件进行 odex 优化,可以提升程序的启动速度,同时减小空间的占用
odex 文件依赖系统中已经编译好的系统模块,一般是 /system/framwork 目录下的 jar 包,目的也是为了提高虚拟机的运行速度,可以说从 class 到 dex 是针对 Android 平台的一种通用优化,odex 是 dex 文件在不同手机上做的特殊优化

为了反编译 odex 我们需要准备以下工具

  • smali/baksmali
    baksmali-xxx.jar 将 odex 转换为 smali 文件 (有兴趣的可以了解一下 smali 语法)
    smali-xxx.jar 将 smali 文件打包成 dex 文件
  • dex2jar
    dex2jar 是将 dex 文件转换为 jar
  • Java Decompiler
    这个工具是将 jar 里面你的 class 转换为 java 文件,当然也可以不转换,直接使用 jd-gui 程序打开 classes.jar 看里面的内容

这里下载的 smali 版本是 baksmali-2.1.3.jar 和 smali-2.1.3.jar,不同版本使用方法大同小异
开工,我们先建立一个工作目录,我这里是创建了一个 deodex 的目录,然后把相关工具都放进去,因为反编译 odex 需要依赖 /system/framework 目录下的一些东西,所以这里直接把整个 framewrok 目录拉出来

$ mkdir deodex
$ cd deodex
$ adb pull /system/framework .

当然这一步不是必须的,如果在反编译过程中没有报缺失依赖文件的错误是不需要的
然后从系统目录中拉出一个程序的 odex 如下

$ adb pull /system/app/LeEcoCircle/oat/arm/LeEcoCircle.odex .

目录结构如下 (mac 下,目录结构生成使用 tree 命令,安装:brew install tree,然后在当前目录执行 tree 即可)

deodex
├── LeEcoCircle.odex
├── baksmali-2.1.3.jar
├── dex2jar-2.0
│   ├── ...
├── framework
│   ├── ...
├── jd-gui-osx-1.4.0
│   ├── ...
└── smali-2.1.3.jar

接下来开始反编译,先使用如下命令

$ java -jar baksmali-2.1.3.jar -x LeEcoCircle.odex

-x 参数是指定 odex 文件
上面命令通常会报错

Error occurred while loading boot class path files. Aborting.
org.jf.util.ExceptionWithContext: Cannot locate boot class path file /system/framework/core.jar
...

意思是:找不到 /system/framework/core.jar ,这个 jar 是在 /system/framework/arm/boot.oat 中,当然 boot.oat 不只包含 core.jar ,还有 framework.jar ,core-libart.jar 等一系列 jar所以我们将命令改变一下

$ java -jar baksmali-2.1.3.jar -x LeEcoCircle.odex -d framework/arm/ -c framework/arm/boot.oat

-d 参数 指定 bootclasspath 查找的目录,如果不加这个参数,默认是当前目录
-c 参数 指定 bootclasspath 中具体的 jar/oat 文件
此时,命令不会报 Aborting 的错,可能会出现一些异常,某些函数不能被解析,但是还是能够顺利完成 odex 反编译,生成的 smali 文件放在了当前目录的 out 目录中
下一步就是将 smali 文件打包为 dex,使用以下命令

$ java -jar smali-2.1.3.jar out -o classes.dex

会在当前目录生成 classes.dex 文件,然后执行下面命令来反编译 dex 文件

$ cp classes.dex dex2jar-2.0
$ cd dex2jar-2.0
$ ./dex2jar.sh classes.dex

完成后,会在 dex2jar-2.0 目录生成 classes-dex2jar.jar 接下来就可以使用 jd-gui 打开直接查看里面的内容了

上面反编译 odex 是单 dex 的情形,如果这个 odex 是由多个 dex 优化合成的产物的话,在 baksmali 命令中还需要加入 -e 参数来指定反编译哪一个 dex,然后 -o 指定不同的输出目录,不同的目录是为了生成对应的 dex 文件,如果把他们都输出到同一个目录,在执行 smali 命令时,可能会失败,比如:方法数超过 65k 等

参考资料
深入理解Android之Java虚拟机Dalvik 邓凡平老师之作
convert odex file to dex file
DeodexInstructions