バイナリHACK


binwalkで解析できない場合などの、firmwareイメージを自力で解析する方法のメモです。Mac OS X El Capitanで実行しています。

stringsコマンド

バイナリに含まれる文字列を確認します。文字列のオフセットを確認することもできます。-8などを指定して長めの文字列を確認するとよいです。文字列が出てこない場合は圧縮などのエンコードされている可能性が高いです。

$ strings -t x original.bin | grep config.har
330000 config.har

オプションはmanページを確認してください。

hexdumpコマンド

バイナリを確認します。

$ hexdump original.bin
0000000 10 00 01 03 24 1a 00 00 10 00 02 9c 24 1a 00 01
0000010 10 00 02 d1 24 1a 00 02 10 00 02 cf 24 1a 00 03
0000020 10 00 02 cd 24 1a 00 04 10 00 02 cb 24 1a 00 05
0000030 10 00 02 c9 24 1a 00 06 10 00 02 c7 24 1a 00 07

grepしてgzipのヘッダーの"1f 8b 08"などを探します(binwalkでもやってくれますが)。またブロックの終端は0xffなどで埋まっていて、塊の見当がつけられます。

データの固まりの確認はこんな感じでできます。

$ hexdump original.bin | grep -1 '^*'

FreeBSDではhexdumpに-Cオプションをつけるとバイトでの表示になりこれはhdというコマンドと同等になります。この表示はなぜかオフセットが1桁多く表示されます。

% hd Softbank_E-WMTA22.zimage | head
00000000  27 05 19 56 19 7e 4d bc  5d bf 60 d6 00 15 84 de  |'..V.~M.].`.....|
00000010  80 05 01 80 80 05 01 80  20 91 70 29 05 02 02 01  |........ .p)....|
00000020  46 72 65 65 42 53 44 20  4b 65 72 6e 65 6c 20 49  |FreeBSD Kernel I|
00000030  6d 61 67 65 00 00 00 00  00 00 00 00 00 00 00 00  |mage............|
00000040  1f 8b 08 08 d6 60 bf 5d  02 03 53 6f 66 74 62 61  |.....`.]..Softba|
00000050  6e 6b 5f 45 2d 57 4d 54  41 32 32 5f 6b 65 72 6e  |nk_E-WMTA22_kern|
00000060  65 6c 2e 6b 62 69 6e 00  dc bd 0b 98 5c 55 99 2e  |el.kbin.....\U..|
00000070  bc 76 5d 77 77 2a c9 ee  a4 03 6d d3 c2 4e 68 34  |.v]ww*....m..Nh4|
00000080  66 5a 53 b9 a0 31 87 81  0a 20 32 80 52 b9 71 9b  |fZS..1... 2.R.q.|
00000090  88 85 97 19 e7 8c 33 16  0c 9e 71 1c 1d ab fa 9e  |......3...q.....|

ddコマンド

バイナリの一部分を抜き出すことができます。

$ dd if=original.bin of=tp.gz bs=1 skip=262660 count=48702
48702+0 records in
48702+0 records out
48702 bytes transferred in 0.225961 secs (215533 bytes/sec)
$ file tp.gz
tp.gz: gzip compressed data, was "welkin-tp.b", from Unix, last modified: Fri Apr  9 01:01:29 2010, max compression

バラしたものを連結するときはcatコマンドで。

xxdコマンド

bootのコーンソールなどでアスキーダンプしたバイナリを元に戻せます。

$ head -1 hexdump.asc 
bfc40000:46 69 72 6d 77 61 72 65-00 ff ff ff ff ff ff ff Firmware........
$ sed 's/^.\{9\}//;s/.\{17\}$//' hexdump.asc | xxd -r -p > hexdump.bin

これらのコマンドとbootのコンソールを駆使して某ルータのflashイメージを解析したところこんな感じになってました。

ここまで解析するのはbinwalkだけでは無理なんで、職人の感と技でやるしかありません。

bootは64Kの2ブロックでhexdumpで見ると二つの部分に分かれているのがわかる。最初の部分は通常のインストラクションで、後半は圧縮された実行形式と思われるがgzipではなさそう。最初の部分を実行して後半をRAMに展開して実行を移していると思われる。

展開しているアドレスはカーネルのロードアドレスが0x80040000となっているので、それより前のアドレスになるようだ。

bootが管理するFirmwareなどのファイルの64K毎のセクタのヘッダー構造。tftpでpushするとできるので、直接操作することはないかもしれません。

タイプ バイト数 備考
ファイル名 16 NULLターミネートの文字列
ブロック番号 4
ファイルサイズ 4
? 4
? 36

Versionブロックはただの文字列。welkin-tp.bはテストプログラムで起動時やbootのコンソールでtpコマンドを実行すると読み込まれて処理を行うものと思われる。実際のfirmwareのnetbsdの部分だと想像される。

welkin-tp.bはgzip形式だがnetbsdは不明の形式。

Firmwareの中のブロックのヘッダー構造は調べた方がいてこんな感じのよう

タイプ バイト数 備考
フラグ 4 おそらく31ビットがgzipフラグ 17ビット目が実行可能フラグ 16ビットがENDフラグ
データ長 4
データ開始位置 4 0x18固定
チェックサム 2 データー長以降の16ビット単位のIPヘッダーと同様のチェックサム。と思ったのですがカーネルの部分だけ上位バイトが微妙にあいません。いくつかのファームウエアを調べていたところ、差がデータサイズに比例していることがわかりました。カーネル以外はサイズが小さいので、ちゃんと一致するというのもうなずけます。試しにデータ長を250000くらいで割った数字を足しこんでみたところうまくいくことがありました。
ダミー 2
ロードアドレス 4 実行可能な場合のロードアドレス
エントリーポイント 4 実行可能な場合のエントリーポイント

チェックサムの範囲は、短いデータのブロックで確認して見当をつけるのが良いです。

このターゲットのチェックサムは謎なので上記のロジックで適当にいじるとあたりがでます。

チェックサムがNGな場合

boot> load Firmware
Firmware system: load fail

チェックサムがOKな場合

boot> load Firmware
begin  : 0x80040100
length : 1807018
startup: 0x80040100

このターゲットにはrootfs部分が無いが、kernel内にufsとして入っているようだ。現在のCPUやメモリのスペックを考えると、あまり良い方法ではないきがする。

rubyなどで、構造をチェックするスクリプトを書くことも有効です。(例:EZ-USB FX2のfirmware抜き取りスクリプト)

objdumpでのアセンブラの解析も可能ですが、手間がかかるので本当に最後の手です。逆アセンブラの解析は現実的ではないのでAIを使った逆コンパイラがあると良いのですが。

$ objdump -b binary -m mips -D hoge.bin

エンディアンがどちらか分からない時はELとEBオプションを付けて、それっぽい命令が並ぶか確認します。

解析にはQUEM上でバイナリを実行する方法もあるかもしれません。

バイナリを編集したいときはMacだとHex FiendというアプリがApp Storeにあって定番のようです。