WAR の内部構造を確認する

WAR(Web Application Archive)は ZIP 形式のアーカイブで、 Java EE / Jakarta EE のデプロイメント仕様に従った固定のディレクトリ構造を持つ。

Shell — WAR の内容を一覧表示
# jar コマンドで内容一覧(展開せずに確認)
jar tf myapp.war | head -50

# クラスファイルのみ抽出
jar tf myapp.war | grep "\.class$"

# 内包 JAR の一覧
jar tf myapp.war | grep "WEB-INF/lib/"
jar tf の出力例
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⚠️ 必要なものだけ
*.jspJSP ファイル(テキスト)そのまま読める
static/HTML / CSS / JSそのまま読める
WEB-INF/web.xmlサーブレット設定XML のまま読める

WAR を展開する

Shell — 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/ を逆コンパイルする。

Shell — 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 などで公式ソースが入手可能なため、 逆コンパイルは自前コードのみに留めることが多い。 ただし独自カスタマイズされたライブラリを含む場合はこちらも対象になる。

Shell — WEB-INF/lib の JAR を個別処理
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 の展開から逆コンパイルまでを一括処理するシェルスクリプト例を示す。

Shell — decompile_war.sh
#!/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 がまとめてツリー表示される。

Shell — jadx で WAR 処理
# 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 が内包されるためさらに展開が必要

次の PART では…

PART 06 では逆コンパイルで頻出する Inner クラス・ラムダ式・難読化コードへの対処法を解説する。

→ PART 06 — 難読化コードへの対処へ