WAR の内部構造を確認する
WAR(Web Application Archive)は ZIP 形式のアーカイブで、 Java EE / Jakarta EE のデプロイメント仕様に従った固定のディレクトリ構造を持つ。
# jar コマンドで内容一覧(展開せずに確認)
jar tf myapp.war | head -50
# クラスファイルのみ抽出
jar tf myapp.war | grep "\.class$"
# 内包 JAR の一覧
jar tf myapp.war | grep "WEB-INF/lib/"
META-INF/ META-INF/MANIFEST.MF WEB-INF/ WEB-INF/web.xml WEB-INF/classes/ WEB-INF/classes/com/example/HelloServlet.class WEB-INF/classes/com/example/service/UserService.class WEB-INF/classes/com/example/model/User.class WEB-INF/lib/ WEB-INF/lib/spring-webmvc-6.0.11.jar WEB-INF/lib/jackson-databind-2.15.2.jar WEB-INF/lib/logback-classic-1.4.8.jar index.jsp static/css/style.css static/js/app.js
| パス | 内容 | 逆コンパイル対象 |
|---|---|---|
WEB-INF/classes/ | 自前の Java クラス | ✅ 主な対象 |
WEB-INF/lib/*.jar | 依存ライブラリ JAR | ⚠️ 必要なものだけ |
*.jsp | JSP ファイル(テキスト) | そのまま読める |
static/ | HTML / CSS / JS | そのまま読める |
WEB-INF/web.xml | サーブレット設定 | XML のまま読める |
WAR を展開する
mkdir war_extracted
cd war_extracted
# jar コマンドで展開
jar xf ../myapp.war
# 展開後の構造確認
ls -la WEB-INF/
# → classes/ lib/ web.xml
ls WEB-INF/classes/com/example/
ls WEB-INF/lib/ | wc -l # 内包 JAR 数
WEB-INF/classes の逆コンパイル
自前コードが格納された WEB-INF/classes/ を逆コンパイルする。
cd war_extracted
# CFR でディレクトリごと指定
java -jar ../cfr.jar WEB-INF/classes/ \
--outputdir ../decompiled_src/ \
--comments false
# 出力確認
find ../decompiled_src/ -name "*.java" | wc -l
WEB-INF/lib の内包 JAR を処理する
サードパーティ依存は通常 Maven Central などで公式ソースが入手可能なため、 逆コンパイルは自前コードのみに留めることが多い。 ただし独自カスタマイズされたライブラリを含む場合はこちらも対象になる。
cd war_extracted
# 内包 JAR 一覧を確認
ls WEB-INF/lib/
# 特定 JAR のみ逆コンパイル(例:独自ライブラリ)
java -jar ../cfr.jar WEB-INF/lib/custom-lib-1.0.jar \
--outputdir ../decompiled_lib/custom-lib/
# 全 JAR を一括処理(注意:数百 MB になることがある)
for jar in WEB-INF/lib/*.jar; do
name=$(basename "$jar" .jar)
java -jar ../cfr.jar "$jar" --outputdir "../decompiled_lib/${name}/"
done
⚠️ ライブラリ JAR の逆コンパイルに関する注意
Spring・Jackson などの OSS ライブラリは 公式ソースコードをそのまま参照する方が正確で早い。
Maven の依存設定に <classifier>sources</classifier> を追加するか、
mvn dependency:sources を実行してソース JAR を取得することを先に検討すること。
再帰処理スクリプト
WAR の展開から逆コンパイルまでを一括処理するシェルスクリプト例を示す。
#!/bin/bash
# decompile_war.sh
# Usage: ./decompile_war.sh myapp.war [output_base_dir]
WAR_FILE="$1"
BASE_DIR="${2:-war_output}"
CFR_JAR="./cfr.jar"
if [ -z "$WAR_FILE" ]; then
echo "Usage: $0 <target.war> [output_dir]"
exit 1
fi
WORK_DIR="${BASE_DIR}/extracted"
SRC_DIR="${BASE_DIR}/src"
LIB_DIR="${BASE_DIR}/lib"
echo "▶ WAR 展開: $WAR_FILE"
mkdir -p "$WORK_DIR" "$SRC_DIR" "$LIB_DIR"
(cd "$WORK_DIR" && jar xf "$(realpath "$WAR_FILE")")
# 1. WEB-INF/classes を逆コンパイル
if [ -d "$WORK_DIR/WEB-INF/classes" ]; then
echo "▶ WEB-INF/classes を逆コンパイル中..."
java -jar "$CFR_JAR" "$WORK_DIR/WEB-INF/classes" \
--outputdir "$SRC_DIR" \
--comments false \
--showversion false
echo " → $(find "$SRC_DIR" -name '*.java' | wc -l) ファイル出力"
fi
# 2. WEB-INF/lib の内包 JAR を処理
if [ -d "$WORK_DIR/WEB-INF/lib" ]; then
echo "▶ WEB-INF/lib の JAR を処理中..."
for jar in "$WORK_DIR/WEB-INF/lib"/*.jar; do
name=$(basename "$jar" .jar)
echo " 処理中: $name"
java -jar "$CFR_JAR" "$jar" \
--outputdir "$LIB_DIR/$name" \
--comments false \
--showversion false 2>/dev/null
done
fi
echo "✅ 完了"
echo " 自前コード: $SRC_DIR"
echo " ライブラリ: $LIB_DIR"
jadx で WAR を直接開く
jadx は WAR ファイルをそのまま開ける。自前クラスと内包 JAR がまとめてツリー表示される。
# CLI で一括エクスポート
./bin/jadx -d output/ myapp.war
# GUI で開く
./bin/jadx-gui
# → File → Open → myapp.war
# → 左ペインに「WEB-INF/classes」と「WEB-INF/lib/xxx.jar」がツリー表示
💡 jadx で WAR を扱う場合の特徴
jadx-gui で WAR を開くと、左ペインに自前クラスと内包 JAR が同列で表示される。 「どのクラスがどの JAR に含まれるか」が視覚的に分かるため、大規模 WAR の全体像を把握する起点として使いやすい。
よくある注意点
| 問題 | 対処 |
|---|---|
| Spring Boot fat JAR が WAR に入っている | fat JAR は内部に Tomcat を内包。BOOT-INF/classes/ を探す |
JSP がコンパイルされて .class になっている |
クラス名に jsp_servlet や _jsp が含まれる。元の JSP ファイルが別途存在するか確認 |
WEB-INF/classes が空 |
Maven の war プラグインで JAR にまとめられている場合あり。WEB-INF/lib/ の内包 JAR を確認 |
| EAR ファイル(企業アプリ) | EAR も ZIP 形式。中に WAR・JAR が内包されるためさらに展開が必要 |