re-java について
JAR、WAR、classファイルを調べるためのJavaリバースエンジニアリング向けスキルです。CFR、JD-GUI、javapによるバイトコード調査と逆コンパイルに加え、難読化の確認や任意の動的検証を扱います。
主な機能
- 01JARとWARの展開
- 02javapによるクラス構造とバイトコードの確認
- 03CFRとJD-GUIによる逆コンパイル
- 04一般的な難読化と文字列暗号化の識別
- 05JDBによる任意の動的検証
活用シーン
- 01Javaサンプルのロジック復元
- 02登録コード、鍵、ネットワーク処理の特定
- 03ProGuardやAllatoriで処理されたプログラムの分析
- 04Javaマルウェアの静的調査
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ dslsdzc/ rev-skills --skill re-javaインストール後、Claude Code で/re-javaを入力して読み込まれたか確認します。
詳細
Java 字节码逆向(CFR / JD-GUI / javap)
何时使用 / 何时不用
- 用:jar/war/class 样本还原 Java 逻辑;定位注册码/密钥/网络逻辑;处理 ProGuard/Allatori 混淆与 Java 加固
- 用:恶意 Java 样本([[re-malware]] → [[re-managed]] 路径)静态还原
- 不用:Android DEX 直接分析([[re-apk]];转成 jar 后可回本技能)
- 不用:非 Java(.NET 走 [[re-dotnet]]、脚本走 [[re-script-deob]]、native 走 [[re-binary-core]])
- 注意:动态步骤默认沙箱([[re-analyze/platform-tips]] 最高原则);解包产物先备份
工具准备
参考 [[re-analyze/platform-tips]]——反编译/解包为静态步骤,免沙箱;动态验证按最高原则进沙箱。
JDK(javap / jar / java 运行时)
- Debian/Ubuntu:
apt install openjdk-17-jdk - Fedora:
dnf install java-17-openjdk;Arch:pacman -S jdk17-openjdk - macOS:
brew install openjdk(或brew install --cask temurin) - Windows:
choco install temurin或官方安装器 - 验证:
java -version && javap -version
unzip(jar = zip 容器)
- Linux:
apt install unzip/dnf install unzip/pacman -S unzip;macOS:brew install unzip - 验证:
unzip -v
CFR(jar CLI 反编译器,零依赖)
- 下载(无依赖,只需 JRE):
curl -L -o cfr.jar https://www.benf.org/other/cfr/cfr-0.152.jar - 验证:
java -jar cfr.jar --help
JD-GUI(GUI 反编译器)
- GitHub
java-decompiler/jd-guirelease zip → 解压,Linux/macOS:java -jar jd-gui-1.6.6.jar(zip 内含各平台可执行) - 验证: GUI 启动并能
File > Open打开 jar
procyon(备选反编译器)
- Maven Central:
curl -L -o procyon-decompiler.jar https://repo1.maven.org/maven2/org/bitbucket/mstrobel/procyon-decompiler/0.6.0/procyon-decompiler-0.6.0.jar - 验证:
java -jar procyon-decompiler.jar --help
操作步骤
按顺序执行,每步记录证据(路径 + sha256,见 [[re-triage]])。
-
jar/war 解包:
jar tf app.jar # 先看清单(JDK 自带 jar 工具) unzip -o app.jar -d unpacked/ unzip -p app.jar META-INF/MANIFEST.MF # Main-Class / 加固标记- war: 类在
WEB-INF/classes/,依赖在WEB-INF/lib/ - fat jar(Spring Boot): 类在
BOOT-INF/classes/,嵌套依赖BOOT-INF/lib/*.jar需逐个解 - aar(Android): 内含
classes.jar,解出后再按本技能处理 - Multi-Release jar: 同路径多版本类在
META-INF/versions/N/(N=9/11/17…)下覆盖——运行时按 JDK 选版加载,逆向按目标 JDK 看对应层,别只看顶层
- war: 类在
-
类结构识别:
javap -c -p unpacked/com/example/Main.class # 字节码(-c)+ 私有成员(-p) javap -v unpacked/com/example/Main.class # 常量池/元数据- 找入口:MANIFEST.MF 的
Main-Class→javap -c -p <入口类>看 main 逻辑 - 混淆程序集先看类名是否可读(a/b/c → 步骤 4)
- class 文件头速查:
xxd -l 8 <类>.class→CA FE BA BE+ minor(2) + major(2);Java 版本 = major − 44(52=Java 8、55=11、61=17、65=21,实测 JDK 21 产物 major=65);javap -v首行直接打印 minor/major - 常量池:计数在 major 之后(u16,索引 0 占位),
javap -v列条目类型(String/Class/NameAndType/Methodref/Utf8 等)——字符串字面量、类名、签名全在常量池,混淆样本的明文串先在这里找
- 找入口:MANIFEST.MF 的
-
逻辑还原(CFR / JD-GUI):
java -jar cfr.jar app.jar --outputdir cfr_out/ # 整包还原为 Java 源码 java -jar cfr.jar unpacked/com/example/Main.class # 单类还原 java -jar procyon-decompiler.jar -jar app.jar -o procyon_out/ # 备选- JD-GUI:
File > Open→ 左侧树浏览 →File > Save All Sources导出 - 关键类/方法用两个反编译器交叉验证(CFR 对 lambda/现代字节码还原更好,JD-GUI 对老代码更顺)
- JD-GUI:
-
混淆识别与字符串解密:
- ProGuard: 类/方法名全变
a/b/c、javap -l无 LineNumberTable/LocalVariableTable(调试信息被剥) - Allatori: 反编译产物出现
com.allatori.*水印类、StringEncryptor调用(字符串加密) - ZKM(Zelix KlassMaster):
com.zelix.*类特征 - 检测:
grep -rn 'StringEncryptor\|decrypt(' cfr_out/ | head - 字符串加密处理:定位解密类与算法(key/变换方式)→ python3 复刻批量还原;或动态取明文(步骤 5,沙箱内)——先静态还原,静态卡住再动态
- ProGuard: 类/方法名全变
-
动态(可选;沙箱内执行 [[re-sandbox]]):
- JDB(桌面 JVM):
jdb -classpath app.jar com.example.Main > stop in com.example.Main.checkKey # 下断点 > print key # 取变量 > eval new com.example.Util().decrypt("密文") # 直接调用解密方法取明文 - Frida(Android Java 应用,转 [[re-mobile]]/[[re-apk]] 域):
Java.perform(function () { var c = Java.use("com.example.Main"); c.checkKey.implementation = function (k) { console.log("key = " + k); return this.checkKey(k); }; }); - 桌面 JVM 无 Frida Java API → 用 JDB / 自写 Java agent
- JDB(桌面 JVM):
跨域联合
- [[re-managed]]:网关工作流步骤②(反编译)③(去混淆)固定调用本技能
- [[re-malware]]:Java 恶意样本路径(re-malware → re-managed → 本技能静态还原)
- Android 侧:[[re-apk]] / [[re-mobile]](DEX 转 jar 后可回本技能);动态 [[re-frida]](Android)
- 底座 [[re-binary-core]]:初勘([[re-triage]]);native/JNI 部分
よくある落とし穴
- ProGuard による名前変更後は文字列を手がかりに相互参照する:症状——逆コンパイルするとすべてのクラスが
a/b/c、メソッドがa(...)となり、目的のロジックを特定できません。原因——ProGuard の shrink+obfuscate による名前変更で意味情報が失われます。対策——文字列を手がかりにします。平文の URL/メッセージを grep → 逆コンパイル結果からその文字列を参照しているクラスを探す(grep -rn "提示语" cfr_out/)→ 呼び出しチェーンをたどって意味を復元します。 - Allatori/文字列暗号化は先に復号する必要があります:症状——逆コンパイル結果に
StringEncryptor.decrypt("...")の呼び出ししかなく、平文を確認できません。原因——文字列が実行時に初めて復号されます。対策——復号アルゴリズムと key を静的に特定 → python3 で再現して一括復元します。または復号呼び出しの直後に平文を取得します(JDB のeval/Frida)。サンドボックス内で実行してください。 - 逆コンパイル結果が完全に正確とは限りません:症状——CFR/JD-GUI の出力に構文エラーがある、goto/label の構造が混乱している、try-catch の構造が不自然、lambda の復元に失敗するといった問題が発生します。原因——バイトコードから Java への変換は、完全に元どおりに復元できるものではありません。対策——
javap -c -pのバイトコードと照合して手動で修正し、複数の逆コンパイラーで相互検証します。 - Virbox などの Java 保護は、シェルと同様に先に解除する必要があります:症状——JD-GUI で開くとエラー/空白になり、
javapの出力が破損している、またはファイルヘッダーが標準形式ではありません。原因——保護ツールが class バイトコードを暗号化し、実行時に初めて復号します(本質的にはシェルです)。対策——先に保護を解除します。実行時に class を dump します(主に attach agent/専用のシェル解除ツールを使用します。-Xbootclasspathは JDK 8 でのみ使用でき、JDK 9 以降では削除されています)→ 解除した標準形式の class を逆コンパイルします。考え方は [[re-anti-analysis]] の「先にシェルを解除してから分析する」と同じです。 - javap で unsupported class file version が表示される:症状——
javapでmajor version 65などの未対応エラーが表示されます。原因——class ファイルのバージョンが、ローカルの JDK ツールのバージョンより新しい状態です。対策——major−44 で対象の Java バージョンを算出し、対応する JDK または新しい JDK をインストールします。バージョンと定数プールだけを読み取る場合は、ツールを待たずにxxdでヘッダーフィールドを手動確認できます。 - Multi-Release jar の分析対象レイヤーを間違える:症状——
unzip -oで最上位のクラスを展開して逆コンパイルすると、ロジックが実行時の動作と一致しません(バージョン分岐がなくなっています)。原因——複数バージョンのクラスはMETA-INF/versions/N/にあり、JDK によって読み込むレイヤーが異なります。対策——展開後にMETA-INF/versions/を確認し、対象の JDK に応じたレイヤーを選んで分析します。最上位レイヤーと versions レイヤーの差分が、バージョン条件付きロジックです。 - lambda の逆コンパイルで合成メソッドが現れる:症状——CFR/JD-GUI の出力に
lambda$xxx$nメソッドやinvokedynamic呼び出しが現れます。原因——lambda 本体は、合成された private メソッドと invokedynamic のブートストラップとしてコンパイルされます。対策——javap -v -pの BootstrapMethods テーブルで lambda 本体のメソッドを特定します(キャプチャされた変数は合成メソッドの引数に含まれます)。あとは通常のメソッドと同様に分析できます。
コメント
Markdown 対応