方法 1:CFR に JAR を直接渡す
CFR は JAR ファイルをそのまま引数に渡せる。これが最も簡単な方法だ。
# JAR を直接指定(全クラスを逆コンパイル)
java -jar cfr.jar myapp.jar --outputdir decompiled/
# ファイル数を確認
find decompiled/ -name "*.java" | wc -l
# 特定のクラスだけ取り出す場合
java -jar cfr.jar myapp.jar \
--classname com.example.MyService \
--outputdir decompiled/
# 複数 JAR をまとめて処理(シェルスクリプト例)
for jar in lib/*.jar; do
name=$(basename "$jar" .jar)
java -jar cfr.jar "$jar" --outputdir "decompiled/${name}/"
done
decompiled/
└── com/
└── example/
├── Main.java
├── Service.java
├── Service$1.java ← 匿名クラス
├── util/
│ ├── StringUtil.java
│ └── DateUtil.java
└── model/
├── User.java
└── Order.java
方法 2:JAR を展開してバッチ処理
JAR を展開してから処理することで、特定ディレクトリのクラスのみを対象にしたり、 前処理(ファイルリスト確認など)を挟んだりできる。
# JAR の内容を一覧表示(展開前の確認)
jar tf myapp.jar | head -30
# 展開(jarコマンド)
mkdir extracted
jar xf myapp.jar -C extracted/
# または unzip でも同じ
unzip myapp.jar -d extracted/
# 展開後の .class ファイル数を確認
find extracted/ -name "*.class" | wc -l
# 自社コードのみを逆コンパイル(com/example 以下だけ)
find extracted/com/example -name "*.class" | while read f; do
java -jar cfr.jar "$f" --outputdir decompiled/
done
# ディレクトリ全体を一括指定(CFR はディレクトリも受け付ける)
java -jar cfr.jar extracted/com/example/ --outputdir decompiled/
⚠️ 内部クラスを含む場合の注意
Outer$Inner.class は Outer.class と同じディレクトリに存在する必要がある。
単体ファイルを指定すると内部クラスが解決できず、/* MISSING */ コメントが出力されることがある。
ディレクトリごと指定するか JAR を直接渡す方法(方法 1)を使うと自動的に解決される。
方法 3:jadx GUI で開く
jadx-gui を使えばコマンドを書かずに JAR をブラウズしながら逆コンパイルできる。
# 1. jadx-gui を起動
./bin/jadx-gui # macOS/Linux
bin\jadx-gui.bat # Windows
# 2. File → Open File → myapp.jar を選択
# 3. 左ペインにクラスツリーが表示される
# Source code タブでソースを確認
# Smali タブでバイトコードを確認
# 4. 特定クラスを右クリック → Save as Java file
# 5. 全体を保存する場合
# File → Save All → 出力先ディレクトリを選択
# CLI でエクスポートする場合
./bin/jadx -d output/ myapp.jar
💡 jadx の検索機能
jadx-gui には 全文検索(Ctrl+Shift+F)がある。「どのクラスに特定のメソッドがあるか」を探す際に非常に便利。クラス名・メソッド名の検索もできるため、大規模 JAR の解析起点として重宝する。
内部クラス($)の扱い
Java の内部クラス・匿名クラス・ラムダ由来の合成クラスは
Outer$Inner.class や Outer$1.class などのファイルとして存在する。
| ファイル名パターン | 意味 |
|---|---|
Service$1.class | 匿名クラス(番号付き) |
Service$Builder.class | 名前付き内部クラス |
Service$$Lambda$14.class | ラムダ由来の合成クラス(JVM 実装による) |
package-info.class | パッケージ情報(Javadoc など) |
CFR や jadx はこれらを自動的に外部クラスと統合して復元する。
手動で .class を個別指定する場合は、外部クラスと内部クラスを同じ引数に含めるか、ディレクトリごと指定すること。
出力ディレクトリの整理
#!/bin/bash
# decompile_jar.sh — JAR を逆コンパイルして整理する
JAR_FILE="$1"
OUTPUT_DIR="${2:-decompiled}"
CFR_JAR="./cfr.jar"
if [ -z "$JAR_FILE" ]; then
echo "Usage: $0 <target.jar> [output_dir]"
exit 1
fi
echo "▶ 逆コンパイル開始: $JAR_FILE → $OUTPUT_DIR"
mkdir -p "$OUTPUT_DIR"
java -jar "$CFR_JAR" "$JAR_FILE" \
--outputdir "$OUTPUT_DIR" \
--comments false \
--showversion false
JAVA_COUNT=$(find "$OUTPUT_DIR" -name "*.java" | wc -l)
echo "✅ 完了: $JAVA_COUNT ファイルを出力"
# 匿名クラスファイル($数字.java)を別ディレクトリに移動
mkdir -p "$OUTPUT_DIR/_anonymous"
find "$OUTPUT_DIR" -name '*$[0-9]*.java' \
-exec mv {} "$OUTPUT_DIR/_anonymous/" \;
逆コンパイル結果の確認ポイント
| 確認項目 | 方法 |
|---|---|
| エラーや警告コメントがないか | grep -r "MISSING\|ERROR\|// This method" decompiled/ |
| すべてのクラスが出力されているか | .class 数と .java 数を比較(内部クラスは統合されるため少なくなる) |
| 再コンパイルできるか | javac -cp . decompiled/com/example/*.java を試す |
| 文字化けがないか | 日本語文字列を grep して確認 |
✅ 次の PART では…
PART 05 では WAR ファイルからの抽出を解説する。WAR の構造(WEB-INF/classes と WEB-INF/lib の内包 JAR)を理解し、再帰的に処理する手順を説明する。