leibnitz27/cfr
GitHub: leibnitz27/cfr
CFR 是一款支持现代 Java 特性的反编译器,能将 Java 字节码还原为可读的源代码,支持单文件和整个 JAR 的反编译。
Stars: 2436 | Forks: 310
# CFR - 另一个 Java 反编译器 \o/
这是 CFR 反编译器的公开仓库,主站托管于 benf.org/other/cfr
CFR 能够反编译现代的 Java 特性 —— 包括 Java 9、12 和 14 的大部分特性,但它是完全用 Java 6 编写的,因此可以在任何地方运行!(FAQ) - 它甚至能很好地尝试将来自其他 JVM 语言的 class 文件还原为 java!
要使用它,只需运行特定版本的 jar,并传入你想要反编译的类名(可以是 class 文件的路径,或者是 classpath 上的全限定类名)。
(`--help` 用于列出参数)。
或者,要反编译整个 jar,只需提供 jar 路径,如果你想要输出文件(你可能想这么做!)请添加 `--outputdir /tmp/putithere`。
# 获取 CFR
CFR 的主站是 benf.org/other/cfr,那里有发布的版本以及作者的一堆碎碎念。
从 0.145 版本开始,二进制文件会连同 release 标签一起发布在 GitHub 上。
你也可以从你喜欢的 maven 仓库下载 CFR,不过发布通常会比主站晚几天,以便作者有“发布后悔”的时间。
# 问题
如果你有问题反馈,**_请不要_**包含受版权保护的材料。否则我将不得不删除你的 issue。
# 构建 CFR
非常简单!
只需确保你已安装 [Maven](https://maven.apache.org/)。然后在这个项目的根目录下执行 `mvn compile` 即可获得你所需的内容。
注意:如果你在尝试编译项目时遇到 `maven-compiler-plugin...: Compilation failure` 错误,那么你的 `JAVA_HOME` 环境变量可能指向了一个不支持 `source` 或 `target` 编译选项为 `6` 的 JDK 版本。
解决方法是将 `JAVA_HOME` 指向一个仍然支持编译到 Java 1.6 的 JDK 版本,例如 JDK 11。另外请注意,如果你使用的 Maven 版本 `>=3.3.1`(需要 Java 1.7),那么你 `PATH`(原文为 `JAVA`)上的 Java 版本可能需要高于 1.6。最好的解决方案是在 `PATH` 和 `JAVA_HOME` 中都使用 JDK 8、9、10 或 11。
主类是 `org.benf.cfr.reader.Main`,所以一旦构建完成,你就可以测试一下(从 `target/classes` 开始)
```
java org.benf.cfr.reader.Main java.lang.Object
```
让 CFR 反编译 `java.lang.Object`。
## 反编译测试
作为 Maven 构建的一部分,会执行自动反编译测试。它们验证 CFR 当前的反编译输出是否与预期的先前输出相匹配。测试数据(Java class 和 JAR 文件)是单独 Git 仓库的一部分;因此有必要使用 `git clone --recurse-submodules` 来克隆此仓库。然而,预期输出和 CFR 测试配置属于本仓库的一部分,以便允许在不修改相应测试数据的情况下对其进行更改。测试数据位于 `decompilation-test/test-data` 目录中,而相应的预期数据和自定义配置位于 `decompilation-test/test-data-expected-output` 目录中(具有类似的目录结构,见下文的 [预期数据结构](#expected-data-structure))。
反编译测试也会由 GitHub workflow 执行,如果测试失败,统一的 diff 文件可以在名为 "decompilation-test-failures-diff" 的 [workflow artifact](https://docs.github.com/en/actions/managing-workflow-runs/downloading-workflow-artifacts) 中找到。
**预期输出并非金标准**,它仅描述当前预期的输出。如果对反编译结果的更改是合理的,那么调整预期输出并没有什么问题。
测试类是 [`org.benf.cfr.test.DecompilationTest`](decompilation-test/src/org/benf/cfr/test/DecompilationTest.java)。可以修改它以调整测试目录,或忽略某些 class 文件或 JAR。此外,也可以直接从 IDE 中执行那里的测试。这通常比 Maven 显示的输出更好,并且允许使用 IDE 的内置功能来显示预期数据和实际数据之间的差异。
### 选项文件
可以通过添加选项文件来自定义反编译过程。其每一行指定一个 CFR 选项,键和值用空格分隔。空行和以 `#` 开头的行会被忽略,可用于注释。
示例:
```
# 启用 identifier renaming
renameillegalidents true
```
关于如何命名文件以及将其放置在何处,请参阅下文的 [预期数据结构](#expected-data-structure)。
### 预期数据结构
#### Class 文件
对于 class 文件,预期数据和自定义配置位于 `test-data-expected-output` 下的相应位置,文件名基于 class 文件名。
例如,对于 class 文件 `test-data/classes/subdir/MyClass.class`,可以使用以下文件:
- `test-data-expected-output/classes/subdir/`
- `MyClass.expected.java`
包含预期的反编译 Java 输出,可选带有 [反编译注释](#decompilation-note-comments)。
- `MyClass.options`
一个可选的 [选项文件](#options-file),用于自定义反编译。
- `MyClass.expected.summary`
包含 CFR API 报告的预期摘要。如果没有生成摘要,可以省略。
- `MyClass.expected.exceptions`
包含 CFR API 报告的预期异常。如果没有报告异常,可以省略。
#### JAR 文件
对于 JAR 文件,预期数据和自定义配置位于 `test-data-expected-output` 下相应位置的一个以 JAR 文件名命名的目录中。预期的 Java 输出文件在其文件名中包含包名,例如 `mypackage.MyClass.java`。选项文件以及预期的摘要和异常文件使用 "文件名" `_`。对于 [多版本 JAR](https://openjdk.java.net/jeps/238),该目录包含特定版本 class 的子目录。目录名格式为 `java-`。
例如,对于多版本 JAR 文件 `test-data/jars/subdir/MyJar.jar`,可以使用以下文件:
- `test-data-expected-output/jars/subdir/MyJar/`
- `mypackage.MyClass.java`
包含类 `mypackage.MyClass` 的预期反编译 Java 输出,可选带有 [反编译注释](#decompilation-note-comments)。
- `java-11/mypackage.MyClass.java`
包含特定于 Java 11 及更高版本的 class 文件的预期反编译 Java 输出(针对多版本 JAR)。
- `_.options`
一个可选的 [选项文件](#options-file),用于自定义反编译。
- `_.expected.summary`
包含 CFR API 报告的预期摘要。如果没有生成摘要,可以省略。
- `_.expected.exceptions`
包含 CFR API 报告的预期异常。如果没有报告异常,可以省略。
### 反编译注释
预期的 Java 输出文件支持代表*反编译注释*的注释。在与实际 Java 输出进行比较时,它们会被忽略,例如可用于指出不正确或可改进的 CFR 输出。有两种反编译注释:
- 行:以 `//#` 开头(可选地带有空白前缀)
- 行内:`/*# ... #*/`
应慎用行反编译注释,特别是在大文件中,因为它们会改变 diff 文件的行号(因为在比较过程中会被移除),这可能会令人困惑。
示例:
```
//# Line decompilation note
public class MyClass {
public static void main(String[] stringArray/*# Inline decompilation note #*/) {
...
}
}
```
### 更新 / 创建预期数据
当为反编译测试添加大量新 class 或 JAR 文件时,或者当 CFR 的更改影响大量 class 或 JAR 文件的输出时,手动创建或更新预期输出可能会相当繁琐。针对这些情况,存在以下系统属性可以帮助你。它们可以在运行测试时通过 `-D` 设置。但是,当设置这些系统属性时,相应的测试仍然会失败(但预期数据会被更新),以防止在常规测试执行中意外使用它们。
- `cfr.decompilation-test.create-expected`
根据当前的 CFR 输出生成所有缺失的预期测试数据。
- `cfr.decompilation-test.update-expected`
更新预期测试数据以匹配 CFR 生成的实际数据。请注意,这不适用于使用 [反编译注释](#decompilation-note-comments) 的预期 Java 输出,因为这些注释会丢失。受影响的测试必须手动更新。
标签:CFR, Class文件, DNS解析, Jar包反编译, Java 14, Java 9, Java反编译, JS文件枚举, JVM, Maven, 云安全监控, 云资产清单, 代码还原, 反编译器, 域名枚举, 开源项目, 漏洞验证, 编程语言, 软件开发, 逆向工程, 静态分析