ニンテンドーサウンドクロックAlarmo(アラーモ)の解析が進んでいる件

記事の方法を試す前や質問をするまえに必ず下記リンクを見てからにしてください!
このブログについて
http://yyoossk.blogspot.jp/1970/11/blog-post_24.html
https://garyodernichts.blogspot.com/2024/10/looking-into-nintendo-alarmo.html
ただの目覚まし時計?
Alarmoは、前面に2.8インチの小型LCD、上部に戻るボタンと通知ボタン、上部にダイヤルがあり、回して押すと確認ボタンとして機能する。
ダイヤルにはRGB LEDも搭載されている。

他の目覚まし時計との違いは?
ソフトウェア・アップデートや追加テーマをダウンロードできる2.4GHz Wi-Fiを搭載し、あなたの動きに反応する24GHz mmWaveプレゼンス・センサーを搭載している。

中身は?
基板上には、STM32H730ZBI6 MCUとKIOXIA 4GB eMMCが搭載されている。
SWDピンにワイヤーをハンダ付けし、自分のPiでAlarmoに接続した後、OpenOCDを使ってAlarmoのメモリとレジスタを覗き見ることができた。
残念ながら、システムがファームウェアの実行を開始するSTMの内蔵フラッシュは、読み出し保護(RDP)と呼ばれるメカニズムのため、読み出すことができなかった。
このメカニズムにより、デバッガが検出されると、内部フラッシュへのアクセスができなくなる。
RDPを破るには、デバッガを取り付けずにコードを実行する方法を見つける必要がある。
STM32H7が素晴らしいのは、リファレンス・マニュアルや多くのサンプル・コード、ライブラリが自由に利用できることだ。デバッグ・ポートでは、ペイロードをメモリにロードして実行することもできる。

これはフラッシュ?
メモリの0x70000000の範囲をダンプしてみると、たくさんのARM命令が見つかった。
この領域にはファームウェアのほとんどが含まれているようだ!
リファレンス・マニュアルによると、これはいわゆるOCTOSPI2の範囲だ。
OCTOSPIは、シングル/デュアル/クアッド/オクタルのSPI通信に使用される低レベル・インターフェースである。
MCUの隣に、何のマークもない小さなチップがあるようです。
ファームウェアのリバース・エンジニアリングを始めたところ、OCTOSPIレンジをRAMとして扱っているようだ。
そして実際、この領域の未使用部分に値を書き込んでシステムをリセットすると、値はリセットされて0x55に戻る。
OCTOSPIレジスタ・コンフィギュレーションを調べた結果、これは外部RAMとして使用される32MiBのHYPERRAMのようだ。
外部RAMに永続的なペイロードを持つことはできないので、残念ながらこれはRDPを破る助けにはならない。

コンテンツの暗号化解除
では、このRAM上のファームウェアはどこから来ているのだろうか?
起動時にeMMCからロードされている。
Spindaはこの時点ですでにeMMCをダンプし、RAM内のファームウェアが提供するeMMCの機能を使用していた。
eMMCには、各ゲームテーマのファイルが入ったコンテンツフォルダ、システムファイル、ファクトリーファイル、2ndloader.binというファイルが含まれています。
残念ながら、コンテンツファイルはすべて暗号化されている。
しかし、システムはどうやって暗号化を解除するのでしょうか?
STM32H7にはCRYPと呼ばれる暗号化プロセッサが搭載されている。
このペリフェラルはコンフィギュレーションが可能で、メモリ内のコンテンツを復号化するために使用される。外部RAMからダンプされたファームウェアはCRYPをコンフィギュレーションしていないため、起動時に実行される内蔵フラッシュ上のコードによってコンフィギュレーションされている可能性が高い。
しかし、ファームウェアはCRYPインターフェイスを使ってコンテンツを復号している!CRYPインターフェイスはAES-128-CTR用に設定されている。
CTRモードではキーストリームを作成し、それを平文と組み合わせてファイルを暗号化・復号化するので、CRYPインターフェイスを使ってこのキーストリームを大量に作成し、暗号化されたファイルと組み合わせて復号化すればよい。
デバッグ・インターフェースを使用して約100MBのキーストリームをダンプした後、フラッシュ上のすべてのファイルを復号化することができた。

おまけ:鍵の取得
CRYPインターフェースを設定する際、鍵は4つの32ビット・レジスタに格納される。
これらのレジスタは書き込み専用なので、残念ながら鍵の読み出しは不可能である。
また、2128通りの組み合わせが考えられるため、ブルートフォースも実行可能なオプションではない。
hexkyzはCRYPインターフェースが部分的な上書き攻撃に対して脆弱であることに気づいた。キーが4つの異なるレジスタに分割されているため、キーの32ビットを更新するだけで、暗号プロセッサから一致する出力が生成されるまで、232通りの可能性を試すことができる。
これは鍵の4つの部分すべてに対して行う必要があるため、合計4×232通りの組み合わせをテストする必要がある。
これを実行するための小さなペイロードを書いた後、一晩実行させた。翌朝、進捗状況を確認すると、Alarmoコンテンツ・ファイルの暗号化と復号化に使用されるAES-128-CTRキーの取得に成功していた。
sha256(alarmo_content_key)=47238c47d21165fdb2f9a26c128e4b620a39139f6514588f5edb8a16397a9201
IVレジスタは書き込み専用ではないため、初期化ベクターは単純にIVレジスタから読み出すことができる。

ファイル形式の概要
eMMC上のすべてのコンテンツファイルは暗号化され、CIPHファイル署名マジックが先頭に付きます。この記事では、これらのファイルをCIPHファイルまたは暗号ファイルと呼ぶことにする。
これらのCIPHファイルの本体はAES-128-CTRで暗号化されている。
暗号化された本体の最後の256バイトがRSA-2048署名(PKCS#1 v1.5 with SHA256)を構成します。
すべてのテーマとシステムファイル、ファクトリーファイルは .shpac ファイルです。これらのファイルは単にCIPHファイルの中にZIPファイルを包んだものです。
すべてのshpacアーカイブには、あらゆる種類のアセットとファームウェアバイナリが含まれています。
これらのファームウェア・バイナリは、BINFファイルのシグネチャ・マジックで始まるので、BINFファイルと呼ぼう。
すべてのBINFファイルにはヘッダーがあり、ファイルがメモリ上にロードされるべきアドレス、ベクターテーブルのアドレス、メモリ上のファイルの合計サイズが含まれています。

2ndローダー
eMMC上のコンテンツの中で最も興味深いのは2ndloaderである。
その名の通り、これはeMMCからロードされる2次ローダーである。
2ndローダーは、shpacファイルではない唯一のコンテンツファイルです。
代わりに、内蔵フラッシュ上のローダーによって、SRAM(@0x24000000)に直接ロードされ、復号化されます。
通常のブート中、2ndローダーはeMMC上の暗号化されたsystem.shpacファイルからファームウェアをロードし、それを外部RAMにコピーし、それにジャンプする。
驚くべきことに、system.shpacの署名は通常のブート時にはチェックされず、ファームウェアのアップデートを実行する時にのみチェックされます。

ファームウェアのアップデート
システムファームウェアは、デバイス固有の証明書を使用して、最新のファームウェアバージョンをエンドポイントに問い合わせ、エンドポイントは更新されたsystem.shpacへのCDNリンクで応答する。
このファイルは、ダウンロードされ、eMMC 上の system.update.shpac として保存され、デバイスは、2nd ローダーにリブートする。
2nn ローダーは更新されたファイルの署名を確認し、既存のsystem.shpacにコピーします。
このコピープロセス中、小さなプログレスバーが画面に表示されます。
その後、新しいsystem.shpacの署名がチェックされます。
 
USBローダー
2ndローダーの最も興味深い部分はUSBモードです。
ブート中にAlarmoの上部にある3つのボタンを全て押したままにすると、2ndローダーは外部RAMにFAT32フォーマットされたバッファを持つUSBマスストレージデバイスをセットアップします。
そして、MarkFileがデバイスに置かれるのを待ちます。
そのファイルが見つかると、BINFファームウェアを含むCIPHファイルをデバイスから読み込む。
他の CIPH ファイルと同様、このファイルは暗号化され、署名されている。
ファイルを復号化し、外部 RAM にロードした後、2nd ローダーは、通常のシステム・ファームウェアに 対して行うように、新しくロードされたリセット・ベクターにジャンプする。
if (!IsSignatureValid("2:/a.bin")) {
// Do nothing
}

// Continue with loading the firmware

これにより、Alarmoを開くことなく、USB経由で任意のファームウェア・バイナリをロードすることができる。
ファイルはまだ暗号化されている必要があるが、キーや十分な大きさのキーストリームがあれば簡単です。
Alarmo 上で実行され、猫の写真を表示しているカスタム ペイロードの画像。

RDPを破る
デバッガーを接続しなくても任意のコードを実行できるようになったので、フラッシュ読み出し保護のトリガーを回避することが可能になった。
そこで、内蔵フラッシュの内容をRAMにコピーしようとする小さなペイロードを書くことにした。
残念ながらこれはうまくいかず、ゼロしか読み取れなかった。
読み出し保護エラーがセットされなくなった一方で、別のエラーが発生した: セキュア・エラー・フラグがセットされていたのだ。
STM32H7には「セキュア・アクセス・モード」と呼ばれる別の保護メカニズムがあることがわかった。

セキュア・アクセス・モード
セキュア・アクセス・モードでは、MCUは常にSTM製のセキュア・ブートローダで起動します。
このブートローダは、セキュア・ユーザー・コードを含むセキュア・エリアの設定をサポートし、セキュア・ユーザー・コードは様々なタスクを実行することができます。
セキュア・ユーザー・コードが終了すると、通常のアプリケーションにジャンプし、ロックアウトされる。
Alarmoの場合、セキュア・ユーザー・コードが具体的に何をするのかは不明だが、CRYPインターフェースのセットアップを担当し、ロード、復号化、2ndローダーへのジャンプを行うはずだ。
2ndローダーにジャンプする直前に、セキュアユーザーエリアはロックアウトされ、読めなくなります。
残念ながら、セキュアブートローダーもセキュアユーザーエリアもダンプできていない。そのため、そこで一体何が起きているのかは当分謎のままだ。

2ndloaderの悪用
2ndローダーをリバースエンジニアリングしているときに、RSS_exitSecureAreaを使ってロードされたファームウェアにジャンプしていることに気づいた。
これは、ロックアウトしてセキュア・ユーザー・エリアから出るために使われる関数だ。
ということは、2ndローダーはまだセキュアモードで動作しているのではないか?
そこで私は、2ndローダーがロードされたコードにジャンプしてセキュア・エリアを出る前に、2ndローダーでコードを実行する方法を探し始めた。
USBモードでロードされるBINFファイルを覚えているだろうか?
BINFファイルには、ヘッダーにロード先のアドレスがあります。
外部RAM内にある必要があるのはベクターテーブルのアドレスだけでした。
ファイル自体はどこにでもロードできる。
これにより、2ndローダーからの命令を上書きすることができ、RSS_exitSecureAreaが呼び出される前にカスタムコードにジャンプすることができます。
残念ながら、内蔵フラッシュを読み出してもセキュアエラーが発生した。
つまり、2ndローダーはセキュアエリアから抜ける関数を使っているにもかかわらず、実際にはまだセキュアモードで動いていないようだ。

次はどうなる?
さて、次は何だろう?Alarmoを開かずにカスタムコードを実行する方法がある。これは現在まだソフトウェアバージョン2.0.0で動作しており、2ndローダーをアップデートするシステムはまだないようだ。もちろん、技術的にはeMMC上で2ndローダーをアップデートすることは可能なので、どうなるかはこれからです。
セキュアなユーザーエリアについてはどうですか?今のところ、それを捨てる方法についてはアイデアがない。キーはすでに持っているのだから、それを捨てることで得られる面白いことは、もちろんどのように機能するか確認すること以外に、あまりないだろう。

このブログ記事が公開されたら、ディスプレイの初期化を行い、猫の絵を表示するテスト用のUSBペイロードとコンテンツ・キーを取得するために使用したペイロードも公開。
もしかしたら、もっと多くの人がAlarmoのカスタムコードを書くことに目を向けるかもしれない。

コメント