Skip to content
KitploitKITPLOIT
ツールエクスプロイトブログ
Log in
提出
ツールエクスプロイトブログ
提出

ハッキング、侵入テスト、サイバーセキュリティツールをあなたのセキュリティアーセナルに!

Kitploitはハッキング、サイバーセキュリティ、ペネトレーションテストのツールディレクトリです。最新のプロジェクトアップデートを見つけて、脆弱性の発見、システム分析、テストの自動化、セキュリティの強化を行いましょう。

··フィード·お問い合わせ·プライバシー·© 2026 Kitploit

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
ツール/GitHubGitHub/sjgallagher2/am335xbootrom
組み込みシステムセキュリティリバースエンジニアリングデバッガハードウェアハッキングバイナリ解析学習と教育ファームウェア解析
GitHubsjgallagher2/am335xbootrom

am335xbootrom

TI AM3358ブートROMのリバースエンジニアリング

リポジトリを見る
615182年前Kitploit レビュー済み

人気

すべて見る →

コミュニティで最も使われているツールを見つけましょう。

すべてのツールを探索

ツールコレクションを閲覧

すべてのツールを見る →
共有

AM335x ブートROMのリバースエンジニアリング

私が初めて何枚かのBeaglebone Blackボードを手にしてから、おそらく18ヶ月が経った。ゴミ箱から救い出したものだ。残念ながら、そのボードはすぐには動かなかった。さて、これが私にとってこれらのボードを扱う初めての経験であり、そもそもシングルボードコンピュータを扱うこと自体が初めてだったので、問題が自分のやり方にあるのか、それともボード自体に何か問題があるのか(おそらくそれがそもそもゴミ箱行きになった理由なのか)判断がつかなかった。これらのボードを実際に起動させるまでには、かなり長い時間と多大な労力がかかったが、とにかくやり遂げた。そして、その過程で学んだことをここに記す。

注: Ghidra XMLファイルの使い方

このリポジトリには、いくつかのユーティリティと、リバースエンジニアリングでこれまでに取得したすべてのシンボルを含むGhidraからエクスポートしたXMLファイルを同梱した。この投稿を使って、実際のファームウェアなしでエクスポートし、著作権問題を回避した。万一に備えてだ。自分でブートROMをデバッグしたいなら、JTAGをすでに接続しているはずなので、自分でブートROM(0x20000から0x2BFFFまで)をダンプできる。

シンボルを読み込むには:

  1. 新しいGhidraプロジェクトを作成する。バイナリ(XMLではなく)をGhidraにインポートする: ARMv7 Little Endianを選択し、Optionsでベースアドレスを0x20000に設定し、ブロック名をbootromに設定する。
  2. CodeBrowserでこのバイナリを開く。解析しないこと。
  3. File > Add program に移動し、XMLファイルを選択する。デフォルトで問題ない。リセットハンドラを辿るか、main()またはMMC/SDカードブートハンドラにジャンプできる。

問題

最初に、これらのボードは標準のBeaglebone Blackのカスタム版だと分かっていたので、早い段階で、ボード識別子のような、ボード自体に何か欠けている可能性があると判断した。balenaEtcherでフォーマットした標準SDカードを起動したときに見えたのは、何もなかった。ボードのLEDが点滅し始めることを期待していたし、UART-USBケーブルを接続すればU-Bootの過程が見えるはずだと思っていた。しかし、UARTは沈黙していた。SDカードを抜くと、Cという文字が繰り返し出力された。これはUART/シリアルブートの期待通りの動作だ。確かに起動しようとしており、SDカードがこの動作を変えていたのだが、それ以上は見えなかった。Web上のほとんどのトラブルシューティングは、U-Bootの出力を出発点として問題を診断していた。その余裕は私にはないようだ。

この時点で、デバッグプローブを接続する価値があると考えた。残念ながら、既存のフットプリントに合うヘッダを持っていなかったので、自分で作ることにした。

BeagleboneボードにはP2という指定のヘッダがあり、JTAG接続を引き出している。このヘッダにワイヤを接続し、メスヘッダにまとめてJ-Linkで通信できるようにした。

Ozone(Seggerデバッガ)を起動してJ-Linkを設定し、まずエントリポイントを見つけることから始めた。リセットホールトで目的の場所に到達できると思っていた。それがエントリポイントが0x2148aであるという(誤った)仮定に至った理由だが、これが一貫していないことには確かに気づいていた。後になって、AM335xボードはJ-Linkのリセットホールトとはあまり相性が良くないことが分かった。実際には数百クロックサイクルの遅延があり、非決定的にブートハンドラ内のどこかに着地していた。(最終的には、J-LinkデバッグをサポートするTIのCode Composer Studio用のGELファイルを書くことでこれを回避した。リセット時にPCレジスタをリセットハンドラに設定し、レジスタをクリアし、命令モードをARMに強制する。)

TIフォーラムのスレッド(AM335x: TI employees, where can I get the ROM Bootloader source code/symbols?)から、いくつかのデバッグシンボルを得た: 0x231e0のSPI Initialize、0x23230のSPI ReadSectors、そして0x24bfaはUART読み取りを行うルーチンだ。それは確かに少し助けになった。ブートは0x402f0440の無限ループ、つまりデッドループに陥って失敗していることに気づいた。ふむ、他のブートROMからはかなり離れている。RAMかどこかにあるに違いない。そろそろテクニカルリファレンスマニュアル(TRM)に目を通す時だろう!

TRMの第26章には、ブートに関する大量の情報が含まれている。ブートROMの次の図が得られる:

説明:

パブリックROMコードのアーキテクチャを図26-1に示す。これはトップダウン方式で3つの主要レイヤに分かれている: 高レベル、ドライバ、ハードウェア抽象化レイヤ(HAL)である。あるレイヤは、統一インターフェースを通じて下位レイヤと通信する。 高レベルレイヤは、パブリックROMコードの主なタスクを担当する: ウォッチドッグとクロックの設定、およびメインブートルーチンである。 ドライバレイヤは、インターフェース仕様に従って、任意のブートデバイスに対する論理プロトコルと通信プロトコルを実装する。 最後にHALは、ハードウェア基盤IPと相互作用するための最下位レベルのコードを実装する。エンドブートデバイスはデバイスIOパッドに接続される。

図26-2は、パブリックROMコードのブート手順の高レベルフローを示している。このデバイスでは、パブリックROMコードはセキュアスタートアップ(セキュアROMコードによって実行される)の完了時に開始する。その後、ROMコードはパブリックスタートアップ手順の一部としてプラットフォーム設定と初期化を実行する。ブートデバイスリストはSYSBOOTピンに基づいて作成される。ブートデバイスは、メモリブートデバイス(はんだ付けされたフラッシュメモリやメモリカードなどの一時的なブートデバイス)、またはホストに接続されたペリフェラルインターフェースである。 ブート手順のメインループはブートデバイスリストを順に調べ、現在選択されているブートデバイスからイメージを検索する。有効なブートイメージが見つかり正常に実行された場合、またはウォッチドッグの期限切れ時にこのループは終了する。イメージの認証手順は、HSデバイスでのイメージ実行の前に行われる。認証手順の失敗は、セキュアROM内の「デッドループ」(ウォッチドッグリセット待ち)への分岐につながる。

メモリマップ!例外ベクタ!フローチャート!このセクションには情報が盛りだくさんだ。私の仕事はずっと楽になった。

この時点でJTAGプローブを使ってファームウェアをいくつかのファイルにダウンロードし、Ghidraに読み込み始めた。便利な形式のSVDファイルやその他のレジスタマッピングは存在しないようで、これは本当に残念だ。なぜなら、メモリ領域やレジスタなどをすべて手動で定義する必要があるからだ。これは退屈な作業だったが、しばらくすると、AM3358用のシンボルをGhidraに読み込むためのPythonスクリプトを作成できた。心配事がひとつ減った!

リバースエンジニアリング

私が持っているファイルは次のようにマッピングできるようだ:

領域開始アドレス長さ
Boot ROM (Public)0x4002_00000xBFFF
Boot ROM (Public, alias)0x0002_00000xBFFF
SRAM Internal0x402F_04000xFC00
L3 OCM00x4030_00000x10000

0x402f_0440の無限ループが内部SRAM内の「ダウンロードされたイメージ」の先頭にある一方、例外ベクタは別の場所に格納されているのは興味深い。これは後で重要な手がかりになるかもしれない...

リセット時、プライベートブートROMはセキュリティ関連を処理し、リセットベクタを含む0x2 0000に分岐する。最初の命令は0x2 08d0への分岐で、これがエントリポイントであるはずだ。これはBX命令ではないので、おそらくその時点でもまだArmモードである。

リセットハンドラ

これは最初に実行されるコードであり、パラメータを持つ「関数」というよりは、コンパイラが生成したスタートアップスクリプトのようなものだ。最初の基本ブロック:```arm ldr r4,[->Peripherals::CM_PER] mov r0,#0x2c ldr r6,[r4,r0]=>CM_PER.CM_PER_OCMCRAM_CLKCTRL mov r6,#0x2 str r6,[r4,r0]=>CM_PER.CM_PER_OCMCRAM_CLKCTRL mov r0,#0x2c poll: ldr r6,[r4,r0]=>CM_PER.CM_PER_OCMCRAM_CLKCTRL cmp r6,#0x2 bne poll

このブロックはOCMC RAMクロックを有効に設定します:
1. `CM_PER_OCMCRAM_CLKCTRL=0x2` を設定します
2. レジスタが設定されたか確認し、設定されていない場合はポーリングを続けます

`CM_PER_OCMCRAM_CLKCTRL` レジスタは `MODULEMODE` フィールドにビット0と1を使用し、これを `=0x2` に設定するとOCMC RAMへのクロックが有効になります。

次の基本ブロック:```arm
	    ldr        r0,[PTR_control_status] 
	    ldr        r0,[r0,#0x0]=>control_status 
	    and        r0,r0,#0x700
	    mov        r0,r0, lsr #0x8
	    cmp        r0,#0x3
	    bne        skip
		ldr        r0,[PTR_control_status] 
	    ldr        r0,[r0,#0x0]=>control_status
	    cpy        r6,r0
	    and        r0,r0,#0x1f
	    cmp        r0,#0x1f
	    bleq       GPMIC_init 
skip:   ...

このブロックは以下の処理を行います:

  1. (control_status & 0x700) >> 8 == 0x3 かどうかを確認し、該当しない場合はスキップします
  2. control_status & 0x1f == 0x1f かどうかを確認し、該当する場合は control_status を r6 にロードした後、関数 GPMC_init を呼び出します
ツールをダウンロード