
2026/10/09 23:18
古い Macromedia Director ゲームを現代のハードウェアで動作させる方法
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
このプロジェクトは、Macromedia Director の実行ファイルをリバースエンジニアリングすることで、1990 年代の古典的な Windows ゲーム(具体的には「Findus bei den Mucklas」シリーズ、例:「Findus4」など)のコピー保護を成功裏に回避しました。プロセスには、アーカイブからディスクイメージを取得し、実際のファイルと欺瞞用のエントリを選択するための複雑なファイル構造を解決する作業が含まれました。ImHex および Ghidra を使用した分析により、以下の 2 つの特定の保護メカニズムが明らかになりました:第一に、
.dxr セクションにある Lingo スクリプトが p3media で CD の存在を確認し、欠落していた場合は quit() を実行するもので、これを回避するにはアセンブリコードのジャンプオフセットを修正しました。第二に、optgraph.dll の initdisplay() 関数内部で行われるチェックで、物理ディスクの存在を検証するために特定の「マジック値」(26079537)を返すもので、これを回避するには Ghidra で戻り値をハードコーディングしました。これらバイト単位の修正を適用するための新しい Python オートメーションスクリプトが作成され、ユーザーが元のメディアなしでこれらのレガシータイトルをプレイし、データ破損のリスクを抑えることを可能にしました。この画期的な進展は、古いソフトウェアの歴史を保存する上で大きな一歩となり、macOS バージョンではしばしばこうした保護がない一方、Windows PC バージョンでは低レベルのドライブ検査に大きく依存していたことが示されています。本文
ヴィンディ(Findus)の CD 認証回避:20 年越しのリバイバルとリバースエンジニアリング
はじめに
2000 年代初頭、地域図書館で借りて遊んだドイツ製のパズルアドベンチャーゲーム(特に『ヴィンディ』シリーズ)への憧れ。当時流行っていた Windows 95 コンピュータ上の動作条件から、CD を再燃焼させる試みを繰り返してきました。
数日前、以前失敗した試みである「Findus bei den Mucklas」のディスクイメージが公開されたのを機に、ついに成功させようと考えました。
1. ディスクイメージの取得
当時の CD ブランナーではコピー保護を突破できず、かつ過去 10 年間 PC を持たなかったため、独自で焼くことは不可能でした。
- 幸運な出来事:
にシリーズの全タイトルを含む完全なディスクイメージがアップロードされていました。archive.org - ファイル構成: ゲームは
,.ccd
,.cue
の 3 ファイルセットとして提供。古い.img
で生成されたもので、ゲームデータに加えオーディオトラックも含まれています。CloneCD
テーブル:ゲームファイル(トラック 1)MODE1
テーブル:4 つの音源トラック(サウンドトラック用)AUDIO
FILE "CD01.img" BINARY TRACK 1 MODE1/2352 INDEX 1 00:00:00 TRACK 2 AUDIO INDEX 1 45:08:37 TRACK 3 AUDIO INDEX 1 46:38:44 TRACK 4 AUDIO INDEX 1 47:08:50 TRACK 5 AUDIO INDEX 1 47:11:58
2. イメージの展開とコピー保護 №1
古い
ccd2iso ツールを使用しましたが、最初の AUDIOTRACK で失敗しました。しかし、ファイル先頭部分をISO ファイルとして変換しマウントすることに成功しました。
ディレクトリ構造の改ざん対策
ファイルをマウントした際、重複ファイルが表示される奇妙な挙動が発生しました。
- 現象: 一部のファイルがディスクイメージ内で複数回存在します。
- 本物: 正しいサイズを持つファイル。
- 偽物(デコイ): 0 バイトまたは 256 バイトのみで、ランダムなゴミデータが含まれる。
- 意図: ツールが偽のデコイのみを表示するように、意図的に不適切なディレクトリエントリを作成したと思われます。
- Windows のネイティブ機能は正しいファイルを読み取りますが、他のツールでは異なる結果を返すことがあります。
解決策
複数のアーカイブソフト(PeaZip, Ark, Dolphin, 7z)の組み合わせで、手作業で正しいサイズのファイルを抽出することに成功しました。その後、以下のコマンドで Wine プレフィックス内にインストールし、データをコピーできるようにしました。
WINEPREFIX=$(pwd)/prefix wine "Installiere Findus4.exe"
3. コピー保護 №2:エレクトリック・ブーガルルー(Electric Boogaloo)
Findus4.exe を実行すると、CD が欠けているというエラーポップアップが表示されました。
バグの発見
- エラーメッセージには
という検索文字列が含まれていました。rg -a "Lege die CD" - 調査結果:このテキストは単に
から読み取られており、開発者が前のゲーム(Findus1/2/3)からのファイル名変更を考慮していなかったことが判明。Pettson3.ini
[CDROM]CDROM=? [GAME1]Vind=0 [ALERT1]MESSG=Lege die CD Findus4 ein und versuche es noch einmal. [SCREEN]FULLSCREEN=1
ディレクトリの実装(Macromedia Director)
- 実態: これらのゲームはすべて
形式の実行ファイルです。Macromedia Director Projector- 末尾に
ファイルが連結されており、これが実際の実行コードとなります。.dxr - CD チェックはランタイム環境の一部として埋め込まれています(設定ファイルからの読み取りを回避するため)。
- 末尾に
プロジェクトライズ(ProjectorRays)による解析
.dxr ファイルを解析するための専用ツール ProjectorRays を使用しました。この形式は以下のシグネチャを持ちます。
enum RifxType : u32 { MV93 = le u32("MV93"), // Director アプリラッパー MV95 = le u32("MV95"), FGDM = le u32("FGDM"), FGDC = le u32("FGDC"), APPL = le u32("APPL") // Director アプリケーションラッパー(今回はこれ) };
- 発見: アドレス
にある0x00222151
と、アドレスAPPL
にある0x002222F0
が有効なチャンクでした。MV93 - パッチの適用: オフセット情報を考慮し、
オプションを追加してデコンパイルを実行しました。--offset
./projectorrays decompile Findus4.exe -o output --dump-scripts --offset 0x002222F0
4. スクリプトの分析とロジック
デコンパイルされた Lingo スクリプト(約 150 行)は、ゲーム初期化ロジックを含んでいました。
CD 検出処理
.ini ファイルを素朴に読み取り、CD ドライブを探します。見つからなければエラーメッセージを表示して終了します。
on checkpccdrompath seperator = the last char in the applicationPath drivelista = ["d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"] Retvar = VOID repeat with Drive in drivelista iscdrom = baDiskInfo(Drive, "type") if iscdrom = "CD-ROM" then Path = Drive & ":" & seperator & "p3media" if checkoncd(Path) then Retvar = Drive exit repeat end if end if end repeat return Retvarend-- ...
- ロジック: 全 Windows ドライブをループし、
フォルダを含む CD-ROM を検索。p3media - 結果: 見つかった場合のみパスを設定し、ゲームを実行可能にします。
5. スクリプトのパッチング
CD パスをチェックする関数
setSearchPaths の if ステートメントを変更しました。
- 変更内容: CD がない場合のエラー分岐(
)ではなく、次の指示へ直接ジャンプさせる処理に修正。else - 推測: ゲームには「完全インストール」機能があり、データを HDD にコピーすれば動作する可能性があるため。
バイトレベルパッチ
| オフセット | 元のバイト () | パッチ後のバイト () |
|---|---|---|
| (JMP offset +60) | (JMP offset +3) |
- 手法:
はアセンブルの再エクスポートをサポートしていないため、ImHex で手動パッチを行いました。ProjectorRays - 処理: 元のバイト列
を95 00 60
に上書きし、ゲームをスキップするようにしました。95 00 03
これでイントロビデオが再生されるまで成功!🎉
6. コピー保護 №3:DLL による認証(Machine ID)
さらに深いコピー保護が存在しました。再度文字列検索を行いましたが、今度は
intro.dxr 以外に以下の結果が見つかりました。
➜ rg -a 'Disc Error !' P3media/intro.dxr... P3media/huset.dxr...
BehaviorScript による認証
スクリプト解析により、実行ファイル(
Findus4.exe)の隣にある optgraph.dll を読み込み、initdisplay() 関数を呼び出していることが分かりました。
- 処理: DLL から戻り値を確認し、特定のリターン値(マジックナンバー
)と一致しない場合、ゲームが強制終了します。26079537- Mac 版も考慮されていないため、PC 版のみの認証であることが判明。
on exitFrame me if the platform contains "Mac" then nothing() else if Ok = 0 then alert("Disc Error !") avslutaSpelet() else if getReturnVal(me) <> 26079537 then alert("Disc Error !") avslutaSpelet() end if end if end if end
DLL パッチングの決定
- 選択肢:
- プラットフォームを Mac に見せかける。
- マジック値比較をバイパスする。
- DLL をパッチして常に成功値を返す。
- 選択: ランタイムのリバースエンジニアリングは避け、
をパッチすることにしました。optgraph.dll
Ghidra による簡易化
Ghidra で
optgraph.dll を解析し、initdisplay() 関数を以下の通り簡略化しました。
FUNCTION initdisplay(void) 0x1060: MOV EAX, 26079537 ; 成功値を直接セット RET ; 即座に返す
これにより、CD ドライブの低レベルコマンドを実行する処理をすべて削除し、常に認証を通過するようにしました。
7. オートパッチャの作成
作業完了後、適用したパッチを自動化するための Python スクリプトを作成しました。 ※このスクリプトはドイツ語版(
Findus4.exe)に最適化されていますが、他言語版への適応も比較的容易です(ハッシュ値とオフセットの変更のみ)。
#!/usr/bin/env python3 import hashlib import sys from pathlib import Path game_dir = Path(sys.argv[1]) if len(sys.argv) > 1 else Path.cwd() patches = [ # Findus4.exe のパッチ ( game_dir / "Findus4.exe", "b33312ff9086c1cbc09d9cde6baa0eff4220a1ed354c9a3211ff6787fff46e39", # SHA256 0x2252E8, bytes.fromhex("95 00 60"), bytes.fromhex("95 00 03"), ), # optgraph.dll のパッチ ( game_dir / "optgraph.dll", "9897563fd6447976e2780ba0711f6e4e0a5a98a5c5cbecc4cb9bd608d856bd50", # SHA256 0x1060, bytes.fromhex("55 8B EC 81 EC F4"), # NOP 置換前 (簡易化前のコード片) bytes.fromhex("B8 31 F1 8D 01 C3"), # MOV EAX=SuccessValue, RET 後 ), ] verified = [] for path, expected_hash, offset, original, replacement in patches: data = bytearray(path.read_bytes()) actual_hash = hashlib.sha256(data).hexdigest() if actual_hash != expected_hash: raise SystemExit(f"Unexpected SHA-256 for {path}: {actual_hash}") if data[offset : offset + len(original)] != original: raise SystemExit(f"Unexpected bytes at 0x{offset:X} in {path}") data[offset : offset + len(replacement)] = replacement verified.append((path, data)) # ファイル書き込みと権限設定 for path, data in verified: path.chmod(path.stat().st_mode | 0o200) # Readable by all path.write_bytes(data) print(f"Patched {path}")
まとめ
- 成果: ゲームは CD を検出せず、HDD インストールデータを正常に読み込み、イントロビデオまで到達することができました。
- 類似タイトルの共通性: 多くの古いゲームが Macromedia Director を使用し、同様のコピー保護メカニズムを持っていたことが判明しました。
- 今後の展望: このプロセスは他のタイトルにも適用可能ですが、各ゲームごとのオフセット調整が必要です。
20 年以上経ってから再び愛したゲームをプレイでき、子供達も楽しめるかもしれないと考え、非常に嬉しく思います。同じ喜びを感じてくれる方がいれば幸いです!