1 ポイント 投稿者 GN⁺ 2024-01-23 | 1件のコメント | WhatsAppで共有
  • TheZZAZZGlitchは Game Boy Advanceのクラッシュ音 を録音して、カートリッジ内のゲームデータを識別し、最終的に同じROMを復元できることを示した
  • 核心はクラッシュ後に出力されるオーディオを ROMデータ として解釈することだが、ソース形式ごとの調整が多く必要で、汎用ダンプツールとして使うのは難しい
  • 4時間を超える録音のうち約 1時間50分付近 で特徴的な波形が現れ、その後はゲームの楽器音やオーディオサンプルが順に聞こえる
  • Pythonスクリプトと整列補正により 99.76%の精度 に到達したが起動には失敗し、3回の録音を多数決アルゴリズムで統合すると99.979%まで改善した
  • 7本の録音の統合と空白領域のフィルタリングの末に 100%一致 を達成し、クラッシュ音だけでGBA ROMを復元できるという実験的可能性を確認した

クラッシュ音からROMデータを読み出した実験

  • TheZZAZZGlitchは、GBAがソフトウェアクラッシュ後に出す音にゲームデータが含まれうることを実演した
  • クラッシュしたGBAはカートリッジ内部のデータに基づく音を出し続けることがあり、特殊なハードウェアとコードでこのオーディオを解析すれば、どのゲームかを識別できる
  • ただしこの方法は、カートリッジデータを簡単にダンプする手段ではなく、すぐに使える解決策 でもない
    • ソース形式ごとに多くのチューニングが必要になる

長時間録音で現れた波形

  • GBAをクラッシュさせたあと 4時間以上 録音した結果、約1時間50分付近で特徴的な波形が現れた
  • その後の区間では、ゲームに含まれる実際の 楽器音やオーディオサンプル が順番に聞こえる
  • 残りのデータは13,100Hzの 8ビットデータ のように聞こえ、一部の区間は非常に奇妙に聞こえる

Pythonスクリプトと最初の復元の試み

  • TheZZAZZGlitchは「2日間バグを修正したあと」、きれいなGBAクラッシュダンプ録音を読み取る Pythonスクリプト を用意した
  • ROMデータには0バイトの領域が大きく存在し、この部分は無音のように見えるため、オーディオからの解析が難しい
  • 元のROM内の位置を基準にセクションを再整列する別スクリプトを実行したあと、復元ROMは 99.76%の精度 に達した
  • このROMは依然として起動せず、既知のROMデータを使って未知のデータを明らかにする方法だったため、技術的には「チート」に当たる
    • 完全にブラインドで進めたとしても、適用できる仮定や推定は残っている

複数の録音を統合して精度を向上

  • 次の段階では 録音品質の改善 に集中した
  • 3回録音した結果を「多数決」アルゴリズムで統合すると、精度は 99.979% まで上がった
  • この出力ROMは起動には成功したが、テキストが文字化けし、タイトル画面でクラッシュが発生した
  • その後、7本の録音を結合して空白領域をフィルタリングすると、100%一致 を達成した

物理ハードウェアと追加実験

  • 動画の後半では、この方法が 物理ハードウェア 上でどのように動作するかも確認している
  • 他のゲームでも実験し、複製カートリッジ内のARMコードに関する謎もあわせて追跡した
  • より良い録音を得る方法も試された
    • そのひとつは、1チャンネルに粗くミックスダウンする「cursed adapter」を使う方法だった

1件のコメント

 
GN⁺ 2024-01-23
Hacker News のコメント
  • 0x00 が長く続くときの問題は、**クロックリカバリ(clock recovery)に関係している
    一部のデジタルデータストリーム、特にディスクドライブの磁気ヘッドからの生データや Ethernet のような高速シリアル通信は、別個のクロック信号なしで送信される
    受信側はおおよその周波数基準からクロックを生成し、位相同期ループ(PLL)でデータストリームの遷移にクロック位相を合わせる
    この方式が機能するには、PLL 発振器のドリフトを補正できるだけのデータ遷移が十分頻繁に現れる必要があり、遷移なしでどれだけ長く耐えられるかを
    最大連続同一数字(CID)**仕様と呼ぶ
    https://en.wikipedia.org/wiki/Clock_recovery

    • 昔は 8b/10b のように、短いビット区間を注意深く扱う賢いコードブック方式でクロックリカバリの可能性を保証し、線路容量の問題を避けていたのが良かった
      その後は 64/66b のように大きなビットの塊に短いヘッダーを付けてクロック遷移を保証し、全体を疑似乱数スクランブラに通す方式へ移行した
    • もう一つの懸念は、波形のどこかが**交流結合(AC coupling)**されていて、直流が通過できない場合
      クロックが完全に同期していても、長い区間ではほぼすべて 1 の信号とほぼすべて 0 の信号が最終的に同じになってしまう
    • このケースではあまり関係なさそう
      オーディオはアナログで、ビットストリームとしてデコードするには DAC が必要であり、特別な同期なしに 44.1kHz や 48kHz のような固定周波数で送信される
    • このページで Wireless Set Number 10 というとても興味深い記事を見つけた
      https://en.wikipedia.org/wiki/Wireless_Set_Number_10
  • ほぼ20年前に、第4世代 iPod のファームウェアをピエゾスピーカーでダンプした元祖 iPodLinux ハックを思い出す
    https://web.archive.org/web/20140810083116/http://www.newscientist.com/article/dn7085
    偶然にもそのおかげで iPod 上で GBA ゲームも動かせるようになったが、クリックホイールで Doom を遊んだり白黒動画を見たりするだけでも十分満足だった

    • 良い思い出だ
      まだ iPod Classic を持っていて、バッテリーをアップグレードしたあと 256GB の MicroSD カードを4枚入れた
      最近 Bluetooth を追加する改造も見かけたが、背面ケースを交換する必要があるので、そこまではやらなさそう
      デザインにも、音楽ファイルのコピーを自分で所有しているという点にも、時が経っても色あせない感覚がある
  • ここでさらに注目されてうれしい
    数日前にも投稿されていたが埋もれていた: https://news.ycombinator.com/item?id=39037104
    元動画にはこの短い記事にはない内容が多く、ハッカーが DS から適切なオーディオ品質を得るために自分で切り貼りして作ったカスタムアダプターも含まれている

  • Zzazz は毎年エイプリルフールのコンテスト/イベントを開いていて、通常はある程度のレトロハックやリバースエンジニアリングが入る
    参加をおすすめする
    過去のコンテストは GitHub にある

  • Nintendo のオーディオアーキテクチャはいつも興味深かった
    もともと NES には任意波形を作れるサンプルジェネレータがあり、2つの方法で駆動できた
    1つはメモリアドレスを渡すとビットを読み取り、非常に単純な波形として処理する方式で、1 は「値を1つ上げる」、0 は「値を1つ下げる」なので、平らな波形を作るには 10101010 のように繰り返す必要があった
    もう1つは CPU から「初期値」を継続的に直接投入してチップを直接駆動する方式で、実際にはオーディオドライバが RAM からビットを読むより速かった
    問題は、この方式が CPU サイクルをすべて食い尽くすため、ほかの処理をしていないときにしか使えなかったこと
    Battletoads のようなゲームはこれを活用し、アクションが止まっている間により高音質のドラムヒットを再生し、タイトル画面、敵に最後の一撃を与えるときに一瞬すべてのアクションが止まる「パリッとした」効果、記憶に残るポーズ音楽に使っていた
    ゲームが直接駆動モードと、チップがサンプルを読み取るモードを切り替えるデモがここにある。Retro Game Audio がエミュレータで修正した動画を投稿し、直接駆動サブルーチンに入った瞬間を示している: https://www.youtube.com/watch?v=JGT0FM3yh-w

  • そもそも、なぜこんなことが起きるのかが気になる
    こういうゲームが状態をオーディオとしてダンプするのはよくあることなのか? ゲーム開発者向けの意図的なデバッグツールなのか?

    • 投稿者の以前の動画[1]が、この動作の技術的な詳細を説明している
      基本的にGBAのサウンドはRAM内のバッファからオーディオをストリーミングし、割り込みがハードウェアにバッファの先頭から再度読み込むよう知らせる必要がある
      しかし、ゲームがクラッシュした場合のように割り込みが発生しないと、オーディオストリームはバッファを越えてメモリの別領域を読むようになる
      [1] https://www.youtube.com/watch?v=wSWNkpqjtQY&t=361s
    • かなり珍しいことで、意図的なデバッグツールでもない
      理論上、GBAはゲームがクラッシュしたときにアーキテクチャを「停止」させることもできたし、定期的に実行されるべき状態更新をマスク不能割り込みに結び付け、その更新が止まったら再起動するウォッチドッグを置くこともできた
      しかしそうした機能はコストがかかるため、GBAには搭載されていない
      Nintendoは古いゲームカートリッジメーカーの伝統的なアプローチ、つまり「自分たちのゲームにバグがなければ、未定義のハードウェア状態の動作を心配する必要もない」を選んだわけだ
      そのため、GBAゲームが割り込み無効の無限ループのようなクラッシュ状態に陥ると、オーディオチップはシステムがクラッシュしたことを知らず、RAMの連続したビットを読み取って音に変換するという単純な作業を続ける
      通常動作中なら読み取り処理を管理していた後始末ルーチンがなくなるため、そのまま読み続け、最終的にはカートリッジROMの値を表すビットにまで到達する
  • 本当にとんでもなく印象的だ
    ここで使われている多数決アルゴリズムのような手法は、さまざまな産業でまだ十分に活用されていないのではないかと思う

    • 興味があるなら、高度なノイズ信号復元の手法は、もうほぼ100年近く蓄積されている
      磁気記憶媒体は基本的にこのハックと同じ原理で動いている
      https://en.wikipedia.org/wiki/Partial-response_maximum-likelihood
      ここにも同じ発想を適用できる。GBA ROMの内容には強い偏りがあるはずだからだ
      多数決は多くの情報を無駄にしている
    • 多数派の値を選ぶ、あるいはより一般的に中央値を選ぶ興味深い応用として、同じ時点で撮った複数の写真からノイズや人を取り除く方法がある
      すべての写真を重ね合わせ、各ピクセルごとに中央値だけを残せばよい[1]
      [1]: https://patdavid.net/2013/05/noise-removal-in-photos-with-median_6/
    • データ復旧ではかなり一般的だ
      ディスクイメージを繰り返し読み取り、クォーラム判定を回し、チェックサムを通る結果が出たら正しく動くか確認する、という具合だ
      ファイルシグネチャのようなより高レベルのチェックサムがあれば、追加検証にさらに役立つ
      一部の航空宇宙用途でも使われている
    • フィルムスキャン、特に4K77のようなファンプロジェクトで使えるのではないかと思った
      pristineなマスターではなく、損傷している可能性のある劇場上映用プリントを扱うため、複数本を使って傷などを除去できるなら、ポストプロダクションでの手作業修正の時間を大幅に減らせるはずだ
  • 0xFFが0x00に変わるのは、直流遮断コンデンサやハイパスフィルタリングが原因かもしれない
    オーディオ回路は非可聴コンテンツにはあまり向いていない
    運がよければ、チップのオーディオ出力にデジタルオシロスコープを直接つなぐことでキャプチャ精度が上がるかもしれないが、それでも起動可能なイメージを得られたのはかなり印象的だ

    • YouTube動画のコメントで読んだ記憶では、できるだけ基本的な機材だけでやってみるのが目的だった
      だから本格的なデータロギング用オシロスコープの代わりに、何時間もかけて繰り返しキャプチャし、誤差を平均化していたのだ
    • その通り、直流バイアスのない信号を作る必要がある
      Manchester符号化のような単純な方式だけでも大きな助けになる
      それで十分でなければ、NRZや、場合によっては畳み込み符号化も可能だ
      また、正弦波を送るか、それが無理なら少なくとも矩形波の周波数を十分高くして、AC結合コンデンサに食われないようにする必要がある
  • この件全体で一番印象的なのは、個人的には海賊版業者が、ROM+揮発性保存メモリの代わりに、書き込み可能なフラッシュからゲームを実行するようコードを改変した方法

    • 海賊版のGame BoyやGame Boy Advanceゲームで、バッテリー代の数セントを節約するために使われる非常に一般的な手法だ
      実のところ、こうしたパッチを作るのもそれほど難しくない
      海賊版業者はゲームコードが保存する場所を少しリバースエンジニアリングし、ゲームごとのパッチを書いて保存内容を書き込み可能なフラッシュへフラッシュする
      ただし公式のGBAゲームは常にNintendo SDKの関数を使って保存するため、その関数をフックすれば、バッテリーなしの複製カートリッジでもどんなGBAゲームでも保存できるようにする汎用パッチはかなり簡単になる
      これを行うパッチャーを書いており、ここで見られる
      https://github.com/metroid-maniac/gba-auto-batteryless-patcher
  • TheZZAZZGlitchのエミュレータが、ゲームが不正なアドレスにジャンプしようとしていると報告するとき、内部で何が起きているのか知っている人はいる?
    GameBoy Advanceで使われているARM7プロセッサには詳しくないが、不正な値でジャンプ呼び出しを構成することがどう可能なのか、あまり想像がつかない
    それと、TheZZAZZGlitchが誤って復元したROMの一つを実機のGameBoyで動かしたらどうなるのかも気になる

    • bx命令を使えば、レジスタに保存された任意のアドレスへジャンプできる
      そして誤って復元されたROMを実機のGameBoyで実行するとクラッシュし、最終的にはROMをスピーカーで再生し始めるだろう
      それが動画の肝だ :)
    • エミュレータがGBAメモリマップ上の有効な領域へのアクセスだけをエミュレートしていて、無効な領域にアクセスされるとそのエラーを投げている可能性が高い
      実ハードウェアではどうなるか……誰にも分からない :)