vova7878/DexFile
GitHub: vova7878/DexFile
一个用于读取、生成和修改Android DEX文件的现代Java库,采用类似JDK Class-File API的流式设计。
Stars: 32 | Forks: 4

[](https://github.com/vova7878/DexFile/blob/main/LICENSE)
# 关于
该库专为读取、生成、修改和写入 dex 文件而设计。其灵感主要来源于 JDK 22 中引入的 [Class-File API](https://openjdk.org/jeps/484)。
### 安装
```
dependencies {
implementation("io.github.vova7878:DexFile:")
}
```
### 支持的 DEX 版本
| 版本 | 支持的 API | 读取 | 写入 | 指令集 |
|:---------------|:-------------:|:--------------:|:---------------:|:------------------------------------------------------|
| **041** | 35+ | ✅ 1 | ✅ 1 | 标准 2 |
| **040** | 30+ | ✅ | ✅ | 标准 + ODEX |
| **039** | 28+ | ✅ | ✅ | 标准 + ODEX |
| **038** | 26+ | ✅ | ✅ | 标准 + ODEX |
| **037** | 26+ | ✅ | ✅ | 标准 + ODEX |
| **037** | 24 - 25 | ✅ | ✅ | 标准 + ODEX + ⏳lambda opcodes 3 |
| **036** | 16 - 20 | ✅ | ✅ | 标准 + ODEX 4 |
| **036** | 14 - 15 | ✅ | ✅ | 标准 + ODEX + expanded jumbo opcodes 5 |
| **035** | 1+ | ✅ | ✅ | 标准 + ODEX |
| **013** | M5 | ✅ | ⏳ | 早期 Dalvik + ODEX 6 |
| **009** | M3 | ✅ | ⏳ | 早期 Dalvik + ODEX 6 |
| **cdex (001)** | 28 - 35 | ✅ 7 | ⚠️ 8 | 标准 + ODEX |
* 1 支持作为**容器**进行读写,该容器可将多个逻辑 DEX 文件合并为单个物理文件,以节省空间并共享数据(例如字符串)。
* 2 与所有其他 DEX 版本不同,版本 041 不包含任何特殊的 `odex` 指令。这是因为从 API 32 开始,`odex` 指令已从 Android runtime 中完全移除,而 `dex041` 容器格式直到 API 35 才被引入。
* 3 这是针对 Android 7.x 的早期实验性功能,从未发布过稳定版本。但是,存在一些样本,并且 ART 源码中也包含这些 opcodes。未来版本计划支持这些指令。
* 4 在这些 Android 版本中,此格式已回退至 `dex035` 状态,因此本库对其有完整的读写支持。
* 5 该格式从未被 Google 正式定稿,并在稳定版本发布前被废弃。然而它确实完整存在过,并且基于完整的文档和实际样本,本库为其提供了完整的读写支持,包括所有不寻常的指令族(如 expanded jumbo opcodes)。
* 6 历史性的预发布版本(Milestone 3 和 5)。完全支持读取,包括 `odex` 指令。写入功能计划在未来的版本中实现。
* 7 完全支持。特殊之处在于,`cdex` 通常总是被打包在 `vdex` 容器内。
* 8 仅支持作为 `vdex` 外部的单个文件进行写入。通过 API 28–35 中的原生代码使用特殊技术,或者在 API 34 和 35 中通过 `InMemoryClassLoader` 以标准方式(但**不能**通过 `PathClassLoader`),可以加载此类写入的文件。
### 示例
();
for (char op : bf.toCharArray()) {
switch (op) {
case '>' -> ib.binop_lit(ADD_INT, index_reg, index_reg, 1);
case '<' -> ib.binop_lit(ADD_INT, index_reg, index_reg, -1);
case '+' -> {
ib.aget('B', tmp_reg, tape_reg, index_reg);
ib.binop_lit(ADD_INT, tmp_reg, tmp_reg, 1);
ib.aput('B', tmp_reg, tape_reg, index_reg);
}
case '-' -> {
ib.aget('B', tmp_reg, tape_reg, index_reg);
ib.binop_lit(ADD_INT, tmp_reg, tmp_reg, -1);
ib.aput('B', tmp_reg, tape_reg, index_reg);
}
case '.' -> {
ib.aget('B', tmp_reg, tape_reg, index_reg);
ib.invoke(STATIC, write_byte_id, tmp_reg);
}
case ',' -> {
ib.invoke(STATIC, read_byte_id);
ib.move_result(tmp_reg);
ib.aput('B', tmp_reg, tape_reg, index_reg);
}
case '[' -> {
labels.add(depth);
int open_depth = depth++;
ib.label("label_open_" + open_depth);
ib.aget('B', tmp_reg, tape_reg, index_reg);
ib.if_testz(EQ, tmp_reg, "label_close_" + open_depth);
}
case ']' -> {
int close_depth = labels.pollLast();
ib.goto_("label_open_" + close_depth);
ib.label("label_close_" + close_depth);
}
default -> { /* nop */ }
}
}
ib.return_void();
})
)
);
return DexIO.write(Dex.of(impl_def));
}
```
BrainFuck 到 DEX 的编译器
这是一个将 [BrainFuck](https://en.wikipedia.org/wiki/Brainfuck) 语言编译为 dex 格式的简单编译器。 ``` public static byte[] compile(int tape_length, String bf) { var impl_name = "com.v7878.bf.Main"; var impl_id = TypeId.ofName(impl_name); var system_id = TypeId.of(System.class); var print_stream_id = TypeId.of(PrintStream.class); var system_out_id = FieldId.of(system_id, "out", print_stream_id); var system_write_id = MethodId.of(print_stream_id, "print", TypeId.V, TypeId.C); var input_stream_id = TypeId.of(InputStream.class); var system_in_id = FieldId.of(system_id, "in", input_stream_id); var system_read_id = MethodId.of(input_stream_id, "read", TypeId.I); var write_byte_id = MethodId.of(impl_id, "write", TypeId.V, TypeId.B); var read_byte_id = MethodId.of(impl_id, "read", TypeId.B); var impl_def = ClassBuilder.build(impl_id, cb -> cb .withSuperClass(TypeId.OBJECT) .withFlags(ACC_PUBLIC | ACC_FINAL) .withMethod(mb -> mb .of(write_byte_id) .withFlags(ACC_PRIVATE | ACC_STATIC) .withCode(/* locals */ 1, ib -> { ib.generate_lines(); int field_reg = ib.l(0); ib.sget(field_reg, system_out_id); ib.invoke(VIRTUAL, system_write_id, field_reg, ib.p(0)); ib.return_void(); }) ) .withMethod(mb -> mb .of(read_byte_id) .withFlags(ACC_PRIVATE | ACC_STATIC) .withCode(/* locals */ 2, ib -> { ib.generate_lines(); int field_reg = ib.l(0); ib.sget(field_reg, system_in_id); ib.invoke(VIRTUAL, system_read_id, field_reg); int data_reg = ib.l(1); ib.move_result(data_reg); ib.unop(INT_TO_BYTE, data_reg, data_reg); ib.return_(data_reg); }) ) .withMethod(mb -> mb .withFlags(ACC_PUBLIC | ACC_STATIC) .withName("main") .withReturnType(TypeId.V) .withParameterTypes(TypeId.of(String[].class)) .withCode(/* locals */ 3, ib -> { ib.generate_lines(); int tape_reg = ib.l(0); ib.const_(tape_reg, tape_length); ib.new_array(tape_reg, tape_reg, TypeId.B.array()); ib.local(tape_reg, "tape", TypeId.B.array()); int index_reg = ib.l(1); ib.const_(index_reg, /* start */ 0); ib.local(index_reg, "index", TypeId.I); int tmp_reg = ib.l(2); ib.local(tmp_reg, "tmp", TypeId.I); int depth = 0; var labels = new LinkedList标签:Android开发, Dalvik字节码, DEX文件, Java, 代码处理库, 域名枚举