费力的 dex 字节码转换器

本贴最后更新于 1575 天前,其中的信息可能已经水流花落

闲话

三个多月磨磨唧唧,终于拿出了一版稍微有那么点样的开源工具--dedx。

链接 https://github.com/penguin-wwy/dedx

dedx 是一款将 apk 的 smali 字节码转换为 jvm 字节码的转换器。在此之前,我使用过的 dex2class 的工具主要有两种。

一种是 dex2jar(链接:https://github.com/pxb1988/dex2jar)。
这是一款完成度相当高的开源工具。尤其在 Android 逆向上基本是人手必备。

另一种是利用 soot(链接:https://github.com/Sable/soot)。
这是一款完成度也非常高、学术界较常使用的 Java optimization framework。soot 通过 dexlib2 将 dex 文件解析,编译为 jimple(soot 生成的一种 IR),再将 jimple 转回 jvm class,从而达到 dex2class 的目的。

dedx 的完成度自然是无法和上两个相比。而促使我做这么一个费力不讨好的东西的原因主要有两个:

1、dex2jar 和 soot 都有各自的不足。dex2jar 生成的 class 并是很不严格,不一定能 work。而 soot 本身过于重量级(我只是想得到一个 class 文件而已),运行过程会产生太多的分析和依赖;

2、闲的。

dedx 可能的好处在于,一来 dedx 使用 dx 作为 dex 的解析工具(dx 是 Google Android build tools 中的 dex 编译器,负责将 class 文件编译为单个 class 的 dex 文件)。从而带来指令集上的同步,后续的更新也会更方便。

二来使用 ASM 作为 jvm 的字节码生成,可以生成更高版本的 class 文件,使用更新的 jvm 指令。

除此之外,dedx 的 class 文件生成是以完全的严格的 class 文件为目标。简单来说,目标是生成可以执行的 class 文件。

使用方式

Build

git clone https://github.com/penguin-wwy/dedx.git
cd dedx
./gradlew distZip

Command

支持黑白名单设置

~$ ./dedx -help
usage: command [options] <dexfile>
    --black-classes <[class_name | @file]>   Specify classes which not to
                                             load (default none)
    --classes <[class_name | @file]>         Specify classes which to load
                                             (default all)
 -g,--debug                                  Print debug info
 -h,--help                                   Print help message
    --log <arg>                              Specify log file
 -o,--output <dirname>                       Specify output dirname
    --opt <[fast|normal]>                    Specify optimization level
 -v,--version                                Print version

TestCase

目前 dedx 自带有少量的测试集(非常少),方便冒烟测试

gradle -q runTest

使用生成的 class 文件

调用 dedx,会在指定的 output 位置生成对应的 class 文件

dedx -o /path/to/output /project_path/resource/Base.dex

然后可以通过反射调用

import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Paths;

public class ExampleLoader extends ClassLoader {
    public Class<?> defineClass(String name, byte[] bytes) {
        return defineClass(name, bytes, 0, bytes.length);
    }

    public static void main(String[] args) {
        ExampleLoader loader = new ExampleLoader();
        try {
            byte[] bytes = Files.readAllBytes(Paths.get("/path/to/Base.class"));
            Class baseClass = loader.defineClass("com.test.Base", bytes);
            Method addInt = baseClass.getMethod("addInt", int.class, int.class);
            assert (Integer) addInt.invoke(null, 1, 1) == 2;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

后续

目前会在 log 中统计生成的函数的成功数量

<record>
  <date>2019-12-04T22:54:23</date>
  <millis>1575471263304</millis>
  <sequence>19961</sequence>
  <logger>com.dedx.tools.MainKt</logger>
  <level>INFO</level>
  <class>com.dedx.tools.MainKt</class>
  <method>runMain</method>
  <thread>1</thread>
  <message>All method success/fail: 11826/1512</message>
</record>

测试效果,成功率超过 88%。这当中包含了包括 Android SDK 和一些常用的第三方 SDK(如 zxing)。

后续计划包含三部分:

  • 修复各种 bug;
  • 在生成的 JvmInst 结构上进行指令优化(类型推断、消除冗余指令);
  • 将 JvmInst 进行格式化 dump,便于调试使用。

当前稳定 tag 为 0.0.2,。master 分支不保证编译和冒烟测试成功。

欢迎感兴趣的童鞋一起参与。

  • 字节码
    3 引用 • 2 回帖
  • JVM

    JVM(Java Virtual Machine)Java 虚拟机是一个微型操作系统,有自己的硬件构架体系,还有相应的指令系统。能够识别 Java 独特的 .class 文件(字节码),能够将这些文件中的信息读取出来,使得 Java 程序只需要生成 Java 虚拟机上的字节码后就能在不同操作系统平台上进行运行。

    178 引用 • 120 回帖 • 3 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...