# Dex 编辑

一个 Android 应用的绝大部分代码(使用 Java/Kotlin 编写)都会编译打包到 dex 文件中,通过 MT 强大的 Dex 编辑器++ 功能你可以将其反编译为 Smali 代码并进行修改。

# 多 Dex 编辑

打开 dex 文件,选择 Dex 编辑器++,如果同目录下还有其它 dex 文件,你将会看到以下对话框,可以选择将它们一起打开进行编辑:

alt

如果一次打开多个 dex 文件时出现了类名重复的情况,那么将根据 dex 文件名排序移除后面的类。

例如 classes.dex 和 classes2.dex 中均包含了类 androidx.annotation.Keep,那么 classes2.dex 中的该类会被移除,并看到以下提示:

alt

# Smali 编辑

在浏览界面中点击一个类,即可进入它的 Smali 代码编辑界面:

alt

关于 Smali 的相关知识,可以在网上找到非常多的资料,这边不过多展开。

# 代码导航

在点击左上角的指南针图标可打开导航对话框,这里提供了当前代码中所有字段和方法的导航(也可以切换为字符串导航),直接点击可跳转到对应的代码位置,长按则可以弹出菜单进行相关操作。

alt

相关内容:关于重写方法

# 代码跳转

# 跳转到类

选中一个类名,在弹出菜单中就会出现「跳转」,点击即可跳转到该类的 Smali 代码中。

alt

# 跳转到方法

选中一个方法名,在弹出菜单中就会出现「跳转」,点击即可跳转到该方法的 Smali 代码处。

alt

# 跳转到字段

选中一个字段名,在弹出菜单中就会出现「跳转」,点击即可跳转到该字段的 Smali 代码处。

alt

# 跳转到标签

选中一个标签,在弹出菜单中就会出现「跳转」,点击即可看到该标签的定义和调用点。

alt

# 指令查询

Smali 拥有两百多条指令,你可以点击右上角菜单的「指令查询」调用出查询窗口,查看各个指令的功能介绍和调用格式。如果当前光标所在行存在 Smali 指令,还会自动将其填入到搜索框,无需手动输入。

alt

# 寄存器分析与扩充

Smali 是基于寄存器的语言,寄存器存放着运行时的各个变量值,在修改时若稍有不慎,错误覆盖了一个后续代码需要用到的寄存器的值,将会产生意想不到的错误。

在修改 Smali 代码时,特别是在插入调用方法指令的情况下,经常需要借用几个寄存器来存放参数值,为了避免产生上面提到的错误,你可以使用 MT 的「寄存器分析」功能来查找出指定位置中可用寄存器

可用寄存器:你可以随意对其赋值,完全不会影响到下文的运行。

只需要将光标放在需用插入指令的位置,然后点击右上角的笔图标,选择「寄存器分析」:

alt

MT 会通过控制流分析代码上下文,找出所有可使用的寄存器,以注释方式插入到代码中:

alt

有时会出现所有寄存器都被占用,找不到可用寄存器的情况,这时候就需要增加寄存器数量。

寄存器数量在方法代码的开头以 .registers N 的形式指定,例如原先有 8 个寄存器,你需要额外使用 1 个寄存器,那就需要扩充为 9 个寄存器,即修改为 .registers 9,这样再使用「寄存器分析」就可以找到可用寄存器了。

但是直接修改 .registers N 的方法仅在修改后的 N 不大于 16 的情况下推荐使用,因为有些指令只能使用 v0 .. v15 的寄存器,而修改 N 会导致参数寄存器变大,一旦参数寄存器超过 v15,又刚好被这些指令用到,将会导致编译失败。

对于 N 大于 16 的情况,可以使用 MT 的「寄存器扩充」功能:

alt

# 反编译为 Java

VIP 功能

  • 点击右上角菜单的「转成 Java」即可将当前编辑的整个 Smali 代码反编译为 Java 代码
  • 在代码导航的方法长按菜单中点击「转成 Java」可将选中的方法单独反编译为 Java 代码

alt

反编译后的 Java 代码仅供参考,无法修改!

Java 是高级语言,Smali 是低级语言,Java 代码到 Smali 代码之间经过编译器优化,一些代码流程逻辑已无法使用 Java 代码来描述,只要代码逻辑复杂点,反编译出来的 Java 代码就可能会存在一些错误,甚至一些反编译器因为自身 bug 生成了逻辑完全相反的代码。

因此反编译出来的 Java 代码只能参考,用于帮助你更快的理解 Smali 代码,而无法用于修改再转回 Smali 代码。

# 切换引擎

如果你觉得当前反编译出来的 Java 代码不理想,那么可以点击右上角菜单切换引擎,当前支持的反编译引擎有:

  • Jadx
  • Jadx (Simple)
  • Jadx (Fallback)
  • FernFlower
  • JD-Core
  • Procyon
  • CFR

一般推荐使用 Jadx,其次 FernFlower,如果都不理想再尝试其它的。

Jadx (Simple) 和 Jadx (Fallback) 反编译出来的代码介于 Smali 代码与 Java 代码之间,比 Smali 可读性高,比 Java 错误少。

# 重新反编译

当你反编译为 Java 代码后再去修改 Smali 代码,前面的 Java 代码并不会随之刷新,你需要点击右上角菜单的「重新加载」,它会读取最新的 Smali 代码并进行反编译。

# 搜索与替换

Dex编辑器++ 支持搜索代码、类名、方法名、字段名、字符串、整数,可指定搜索路径、是否搜索子目录、是否区分大小写等,支持正则表达式,支持在当前搜索结果中搜索,支持撤销搜索。

alt

发起搜索的方式有两种,一是切换到搜索界面,直接点击对应选项,二是在浏览界面长按文件夹,点击弹出菜单中的搜索。

# 搜索字符串和代码的区别

字符串包含于代码。代码就是 Smali 代码,而字符串是 Smali 代码中的 “xxxxxx”,即引号中的那部分内容(经过反转义)。

# 为什么搜不到内容?

  1. 检查下「搜索子目录」是否没有勾选;
  2. 正则表达式、完全匹配、区分大小写等选项是否正确勾选;
  3. 搜索内容是否输入正确。

如果以上都没有问题,那就是确实找不到你要搜索的内容了。

# 替换

要使用替换功能,需要先进行搜索,成功搜索到内容后,才可以看到「在当前结果中替换」按钮,替换功能的搜索类型仅支持代码与字符串

alt

# 工程说明

对于新打开的文件都会为其创建一个全新的临时工程,所有的工程数据(正在编辑的文件、历史记录、搜索记录等)都会在返回到主界面时被删除,当然你也可以选择将当前工程数据保存。

点击左上角菜单的「保存为工程」,输入工程名字后即可:

alt

保存为工程后,你可以在主界面侧拉栏的工程分组中找到该工程并打开:

alt

还可以向右滑动展开菜单进行删除、重命名、打开工程目录的操作:

alt

# 临时工程与正式工程的主要区别

  • 临时工程在编译时会覆盖原文件,正式工程则会将文件输出到工程目录;
  • 临时工程退出时会删除所有数据,正式工程会保留数据并可以重新进入;
  • 临时工程适合快速修改保存的场景,正式工程适合分析复杂文件的场景。

# 补充说明

# 关于重写方法

在代码导航的方法长按菜单中,「查找调用处」与「查找重写方法」均和重写方法有关。

重写方法的完整说法应该是重写父类中的方法,有 Java 开发经验的人肯定对 @Override 这个注解十分熟悉,其实它就是标注了这是一个重写方法,如果不是则编译器会报错。

先让我们看看以下代码:

class Parent {
    public void run() {
        System.out.println("I am parent");
    }
}

class Child extends Parent {
    @Override
    public void run() {
        System.out.println("I am child");
    }
}

其中 Child 重写了 Parent 的 run() 方法,再来看看以下代码:

static void print(Parent obj) {
    obj.run();
}

上面的 obj.run() 是会输出 I am parent 还是 I am child 呢?

答案是都有可能:

print(new Parent()); // 输出 I am parent
print(new Child());  // 输出 I am child

如果转到 Smali 代码的视角,那就是 invoke-virtual {v0}, Lparent;->run()V 不一定执行 parent.run() 方法,也可能执行 Child.run() 方法,主要就在于 v0 参数究竟是 Parent 的实例还是 Child 的实例。

因此如果你看到了 invoke-virtual {v0}, Lparent;->run()V,然后跳转到 parent.run() 方法看了下代码,就觉得运行时会输出 I am parent,这显然是不对的,你还需要去它的重写方法 Child.run() 看看。

alt

而在方法的「查找调用处」功能中,如果选择了同时搜索此方法的重写方法的调用,假设你搜索的是 parent.run() 的调用处,那么 MT 还会同时搜索 Child.run() 方法的调用处,避免遗漏。

# 关于旧版 Dex 编辑器

在打开 dex 文件时除了 Dex 编辑器++ 外还有一个 Dex 编辑器,这是早期开发的功能,停更多年,技术已过时,除了特殊需求外,不再推荐使用,并且在未来版本中可能会将其移除。