re-fw-extract について
re-fw-extractは、ファームウェアイメージやアップデートパッケージを展開するリバースエンジニアリングスキルです。自動抽出に加え、手動のシグネチャ検索、入れ子コンテナやMCUイメージの分析も扱います。
主な機能
- 01コンテナの自動識別と展開
- 02バイナリシグネチャとオフセットの検索
- 03入れ子構造や付加データへの対応
- 04バイトオーダーとアーキテクチャの判定
- 05MCUイメージ分析の手順
活用シーン
- 01IoTファームウェアイメージの展開
- 02自動抽出で漏れたデータの回収
- 03埋め込み・付加アーカイブの分離
- 04MCUイメージの逆アセンブル準備
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ dslsdzc/ rev-skills --skill re-fw-extractインストール後、Claude Code で/re-fw-extractを入力して読み込まれたか確認します。
詳細
ファームウェアの抽出と展開
使うとき/使わないとき
- 使う:ファームウェアファイル(.bin / .img / アップグレードパッケージ / IoT イメージ)を入手し、内容を展開して確認する必要がある場合
- 使う:ファームウェアのパッケージ形式、アーキテクチャ、バイトオーダーを判定する必要がある場合
- 使わない:すでにファイルシステムイメージ/rootfs の場合(直接 [[re-fw-rootfs]] へ進みます)
- 使わない:ファームウェアを実行して動作を観察する必要がある場合([[re-fw-emulate]] へ進みます)
- 使わない:実機の基板からハードウェア抽出する必要がある場合([[re-hardware-io]] へ進みます)
ツールの準備
すべてのツールは使用前に検証してください。展開と magic スキャンは純粋な静的解析のため、サンドボックスなしで実行できます([[re-analyze/platform-tips]] の最優先原則)。展開して得られた成果物を実行する場合は [[re-fw-emulate]] に進みます。
binwalk —— 自動展開の主力
binwalk には並行して存在する製品ラインが 2 つあります。インストール前に見分けてください。v3(Rust による書き換え版)が upstream の現行メインラインで、v2(従来の Python 実装)は legacyです。両者ではパラメーターの種類とモジュールの対応状況が異なります。
- v3(現行メインライン):公式 README に記載された方法でインストールします——Docker イメージ /
cargo install binwalk/ ソースからビルド。upstream では pip をインストール方法として扱わなくなっています - v2(legacy):PyPI の
binwalkパッケージは 2.1.0(2015-01)で更新が止まっており、各ディストリビューションのリポジトリにあるパッケージも 2.x 系です(Debian trixie 2.4.3、bookworm 2.3.4)- Linux:
apt install binwalk(Debian/Ubuntu)/dnf install binwalk/pacman -S binwalk - pip:
pip install binwalk - macOS:
brew install binwalk - Windows/WSL:Windows ネイティブ版に公式パッケージはないため、WSL 内の Linux 版を使用します
- Linux:
- 検証:
binwalk --version——使用前に major を確認してください。このスキルのコマンド例(-e/-M/-E)は v2 と v3 の両方で引き続き使用できます。v3 固有のその他のパラメーターが必要な場合は、実際のバージョンに合わせてbinwalk --helpを確認してください
unblob —— より精度の高い自動展開(主力として推奨)
- クロスプラットフォーム:
pip install unblob(依存関係が多いため、venv を推奨します:python3 -m venv venv && venv/bin/pip install unblob) - ほとんどのディストリビューションに公式パッケージがないため、pip を使用します
- 検証:
unblob --version
dd —— オフセットで切り分け
- Linux/macOS:coreutils に付属(macOS には /usr/bin/dd が標準搭載)
- Windows/WSL:WSL 内の Linux 版を使用します。Windows ネイティブでは Git Bash に付属する dd または WSL を使用します
- 検証:
dd --version
hexdump —— 16 進数表示と手動 magic スキャン
- Linux:
apt install bsdmainutils(Debian/Ubuntu)/dnf install util-linux(Fedora/RHEL、hexdump を含む) - macOS:/usr/bin/hexdump が標準搭載されています
- Windows/WSL:WSL 内の Linux 版を使用します
- 検証:
hexdump -C /dev/null(エラーが出なければ使用できます。macOS 版には--versionがありません)
sasquatch —— 古い形式/メーカー独自に改変された squashfs の展開
- Linux:ソースからビルドします(
git clone https://github.com/devttys0/sasquatchを実行し、apt install zlib1g-dev liblzma-dev build-essentialの後にmakeを実行) - macOS:同様にソースからビルドします(Xcode Command Line Tools が必要です)
- Windows/WSL:WSL 内でビルドします
- 検証:
sasquatch -hで使い方を表示します(ビルド成果物はリポジトリのサブディレクトリにあるため、PATH に追加する必要があります)
作業手順
順番に実行し、各ステップの結果を記録してください。
-
自動展開(unblob を優先し、binwalk を予備として使用):
unblob firmware.bin # 更准,自动识别 50+ 容器格式并递归解包 # 或 binwalk: binwalk firmware.bin # 先列出签名与偏移 binwalk -Me firmware.bin # -M 递归 -e 提取成果物:
firmware.bin/(unblob)または_firmware.bin.extracted/(binwalk)ディレクトリ。展開されたファイルシステムは [[re-fw-rootfs]] へ、ELF は [[re-binary-core]] へ進みます。 -
magic の手動スキャン(自動展開が不完全な場合):
hexdump -C firmware.bin | head -40 # 或按 binwalk 报告的偏移核对: dd if=firmware.bin bs=1 skip=<偏移> count=16 | hexdump -Cよくあるマジックナンバー:JPEG
FF D8 FF、gzip1F 8B、squashfshsqs、cramfs45 3D CD 28(リトルエンディアン、ビッグエンディアンでは逆順)、U-Boot27 05 19 56、jffs285 19、ELF7F 45 4C 46。識別できた形式に応じて処理してください。 -
バイトオーダーの判定:
file firmware.bin # 输出含 LSB/MSB 提示 readelf -h <解出的ELF> # Machine 字段 + Data 字段(大小端) strings firmware.bin | head # 可读串确认字节序ビッグエンディアンの ARM/MIPS ファームウェアでは、マジックナンバーや文字列がビッグエンディアンでエンコードされていることがよくあります(例:
hsqsが逆順で現れる)。確認後は、以降の処理をすべてそのバイトオーダーで行ってください。 -
入れ子になったコンテナーを段階的に展開:
- unblob /
binwalk -Meは自動で再帰展開しますが、入れ子(tar の中に zip がある、自作ヘッダーで gzip を包んでいるなど)の場合、途中で止まることがよくあります - 1 層ずつ手動で展開します。外側を先に展開し、
fileで内側の形式を確認してから、対応するツール(tar/gzip はシステム標準のもの、squashfs は sasquatch、その他は [[re-fw-rootfs]] で準備した 7z/unsquashfs)を使って再度展開し、ファイルシステムまたは ELF が現れるまで続けます - 終端マーカーの後にある追加データ:画像(PNG
IEND、JPEGFFD9)などの形式では、終端マーカーの後にコンテナーや圧縮ストリームが追加されていることがよくあります(パーサーは終端マーカーを読み取ると処理を止めるため、追加データは通常の表示では見えません)。fileはファイル全体を画像として報告します。終端マーカーの位置を決める際、バイト列の先頭/末尾をそのまま検索してはいけません(IENDの文字列は tEXt chunk のペイロード内に現れる場合があり、FF D9は JPEG の APP/COM セグメント内に現れる場合があります。最初または最後に見つかったものが偽のマーカーである可能性があります)。PNG は 8 バイトのシグネチャからLength/Type/Data/CRCを順にたどり、構造が正しく、長さが 0 のIENDまで解析します。JPEG はSOIから marker/segment を解析し、SOSの後はFF00のエスケープ処理とRSTnを考慮して、構文上正しい位置にあるEOIを特定します。解析で得られた実際の終端オフセットを使って追加データを切り出してください(単純検索の結果は候補としてのみ使用します) - シグネチャが欠けた zip も修復可能:追加された zip では、ローカルファイルヘッダー先頭の
PK\x03\x04(4 バイト)が削られている場合があります。フィールド間の整合性を使って検証します。シグネチャを補った後、version(よくある値は 20/45)、mod date(年が 1980 以上)、compressed/uncompressed size(EOCD/中央ディレクトリのエントリーと一致)がすべて妥当で、EOCD が末尾に完全な状態で残っている場合は、PK\x03\x04のプレフィックスを追加するだけで完全に展開できます(unzipの "missing 4 bytes" や zipfile のOSError: Invalid argumentは典型的な兆候です) - 入れ子構造の自動処理:同じパターンが繰り返される場合(各層が「画像+末尾の zip に次の層が含まれる」など)は、ループを使って自動的に剥がしてください——末尾部分の抽出 → 修復 → 展開 → 次の層の特定を、追加データがなくなるまで繰り返します。中間成果物は各層の名前を残しておきます(ロールバック可能です)
- unblob /
-
自動展開に失敗した場合の手動切り分け(dd でオフセット指定):
binwalk firmware.bin # 找内嵌文件偏移 dd if=firmware.bin of=part1.bin bs=1 skip=<偏移1> count=<长度1> dd if=firmware.bin of=part2.bin bs=1 skip=<偏移2> file part*.bin # 每块验证メーカー独自ヘッダーはよくある形式です。ヘッダーの N バイトをスキップした位置から標準形式が始まります(まず hexdump で目視してヘッダー長を見積もってから切り出します)。
MCU イメージの分析(8051 / AVR / PIC / MSP430)
この節に進むタイミング
- 前提:ステップ 1〜5(自動展開、magic スキャン、バイトオーダーの判定、入れ子の展開、dd での切り分け)を完了し、イメージ内にファイルシステム/ELF/実行可能コンテナーがないことを確認している
- 判定条件(いずれかを満たせばこの節に進めます):
- ファイルシステムがない:binwalk/unblob の検出結果がゼロ、または少数の誤検出のみで、squashfs/cramfs/jffs2 などのマジックナンバーがない
- 単一イメージ:サイズが小さい(数十 KB 程度)、パーティションテーブルがない、ブートローダーのブートヘッダーがない、複数セクション構造がない
- MCU の特徴:
fileが data/unknown と報告する。strings にオペレーティングシステムやライブラリの特徴的な文字列がない("libc" や RTOS カーネル名がない)。イメージの先頭付近の低アドレスに、読み取り可能なヘッダーではなく短いジャンプ命令/ベクターデータがある
- 例外時の振り分け:イメージ内に RTOS の特徴(スケジューラー/タスク作成の文字列、TCB のような構造)が現れる場合は、リセットベクターと起動コードを特定してから [[re-rtos]] へ進みます
镜像格式:Intel HEX 与二进制
- Intel HEX(.hex):ASCII 文本,逐行记录
:LLAAAATT<数据><校验和>——LL 数据长度、AAAA 段内地址、TT 记录类型(00 数据 / 01 结束 / 02·04 扩展地址 / 03·05 起始地址)、末字节校验和- 地址可分段不连续:多段记录之间留空,整文件按 bin 反汇编必然错位
- 先转内存映像:按记录把数据落位到实际地址(02/04 扩展地址记录决定地址高 16 位),再按 flash 起始基址导入反汇编器
- 二进制(.bin):原始内存映像,无地址信息,导入时手动给基址
- 程序内存布局(泛化,具体值因芯片而异):
- flash 起始:0x0000 或芯片映射基址(如 0x8000/0x10000 量级),以复位向量所在位置反推更可靠
- 中断向量表:低地址固定区,复位向量位于表内固定偏移(见下节)
向量表定位
- 复位向量 → 启动代码:从复位向量位置取入口地址,沿此反汇编即为启动序列(禁中断、初始化堆栈/时钟/外设、跳 main)
- 复位向量位置各架构不同:8051 在 0x0000、AVR 在 0x0000、MSP430 在 0xFFFE(高地址端)、PIC 多为 0x0000(个别系列另有固定偏移)——按架构查位置,不猜
- 跑 RTOS 的固件:复位向量 → 启动代码 → 内核初始化/任务创建 → 调度器启动,衔接 [[re-rtos]] 按任务表拆分
ISA 识别与反汇编
- 识别要点(结合指令字宽、寻址特征、字节序判断):
- 8051:哈佛架构,程序/数据空间分离(MOVC 读程序区、MOVX 访问外部数据区);SFR 特殊功能寄存器(0x80-0xFF 直接寻址);0x20-0x2F 区可位寻址;累加器 A 与 DPTR 主导数据搬运
- AVR:RISC,32 个通用寄存器(R0-R31)寄存器文件,指令大多单周期;哈佛架构
- PIC:哈佛架构,寄存器分 bank、靠状态位切换(bank 切换);指令字宽随系列不同(12/14/16 位),导入反汇编器时按对应宽度
- MSP430:冯诺依曼(统一地址空间),16 位;中断向量表集中在高地址区(0xFFE0-0xFFFF)
- 字节序:AVR/PIC/MSP430 为小端;8051 无原生端序指令、多字节数据惯例大端存放(Keil C51 默认大端),与其余三者相反——导入反汇编器时以实际数据确认(多字节常量、跳转目标地址的字节排列为判断依据)
- 反汇编:选对应架构的处理器模块导入(Ghidra 含 8051/AVR/PIC/MSP430 处理器支持,[[re-ghidra]];其他反汇编器同样有对应处理器,工具可替换,方法为核心)
- 熔丝位/配置位(泛化概念):烧录期配置项(各架构叫法不同:熔丝位/配置位/安全位),烧录时一次性写入,影响时钟源选择、看门狗、代码/读保护等。它不体现在镜像内容里,但决定芯片后续硬件行为——镜像读不到时要先怀疑它
本节常见坑与陷阱
- Intel HEX 多段当 bin 直接反汇编:现象——反汇编错位,地址与代码对不上,全是无效指令;原因——HEX 分段记录地址不连续,且忽略 02/04 扩展地址记录会把高地址段数据落到低地址;对策——先按记录类型把数据落到实际地址(扩展地址记录参与地址合成),生成连续内存视图,再按 flash 基址导入
- 向量表偏移猜错:现象——从文件头直接反汇编得到一堆数据/无效指令,找不到入口;原因——各架构复位向量位置不同(8051/AVR 在 0x0000、MSP430 在 0xFFFE、PIC 多为 0x0000 或系列固定偏移),猜错就从数据开始反汇编;对策——先确认架构,再按该架构复位向量固定位置取入口,从复位向量跟启动代码
- 熔丝位锁定读保护,镜像不可得:现象——芯片读保护开启,读取操作返回全 F/拒绝响应,拿不到完整镜像;原因——烧录期配置位开启了读保护(代码保护),正常读取接口被锁;对策——走替代路径:芯片自带 UART bootloader 交互(若未禁用)、逻辑分析仪采集固件升级/烧录流量([[re-hardware-io]])按协议还原;镜像不可读时不强求,改从行为侧分析(串口输出/协议交互)
- 数据 EEPROM 与程序 flash 混淆:现象——反汇编出大量无效指令,或分析半天发现是数据;原因——镜像含数据 EEPROM/校准表/配置表映像(非易失数据区,独立于程序区),被当成代码;对策——用复位向量/向量表定位程序区起点,程序区之外的高熵/全 F/重复模式段先排除为数据区,再反汇编
跨域联合
- [[re-firmware]]:工作流第 2 步固定调用本技能
- 解出的文件系统 → [[re-fw-rootfs]];解出的可运行程序 → [[re-fw-emulate]]
- 解出的 ELF → [[re-binary-core]]([[re-format-elf]] / [[re-ghidra]]);发现恶意样本 → [[re-malware]]
- 本技能被 [[re-analyze]] 的 triage「分析固件 / IoT 设备」路径调用(re-firmware → re-fw-extract)
よくある落とし穴と注意点
- 自動展開に失敗 ≠ 中身がない:症状——binwalk の出力が空白、または 0 バイトのファイルが展開される。原因——ベンダー独自のヘッダー/暗号化レイヤーによりマジックナンバーが一致せず、シグネチャライブラリが検出できない。対策——hexdump で独自ヘッダーを手動で探し(手順 2)、ヘッダーのオフセットをスキップしてから dd で切り出します(手順 5)。
- エンディアンの誤り → すべて誤り:症状——展開したファイル/ELF/文字列がすべて文字化けする。原因——ビッグエンディアンの ARM/MIPS ファームウェアをリトルエンディアンとして処理している。対策——まず
fileとreadelf -hでエンディアンを確認し(手順 3)、以降の展開/解析でも一貫して同じエンディアンを使用します。 - ビッグエンディアンの MIPS ヘッダーが binwalk で見落とされる:症状——binwalk が既知のファイルシステム(squashfs など)を識別できない。原因——シグネチャがリトルエンディアンの特徴に基づいて照合されるため、ビッグエンディアンのマジックナンバーはバイト順が逆になる。対策——hexdump でバイト順が逆のマジックナンバーを手動で探し、オフセットを指定して dd で切り出し、sasquatch で展開します。
- 展開物にゴミが混入する:症状——展開された「ファイル」がランダムデータで、
fileが data と報告する。原因——シグネチャの誤検出、または切り出しオフセットのずれ。対策——各展開物に対してfileを実行し、先頭 16 バイトを確認してマジックナンバーを検証します。無効な場合はオフセットを変えて再試行します。 - 拡張子と内容が一致しない(偽装拡張子):症状——
.jpgが PNG、.soが tar、.cが XZ と報告される。原因——作成者が意図的に無関係な拡張子を付けている(ステガノグラフィー/多重ラッピング/検査回避でよく見られる)。対策——常にfile/マジックナンバーを基準にし、拡張子は手がかりとしてのみ扱います。fileの出力に "with extra data prepended" などのヒントがあれば、その指示に従って処理します。 - zip がエラーを報告してもファイル一覧は表示できる:症状——
unzipが "missing 4 bytes"/"invalid zip with overlapped components"(zip bomb の誤検出)を報告する、または Python zipfile がOSError: [Errno 22] Invalid argumentを出す。原因——ローカルファイルヘッダーにPK\x03\x04シグネチャがない、またはデータの前にゴミのプレフィックスがある。対策——まずunzip -lで一覧を表示できるか確認します(表示できれば EOCD/中央ディレクトリは正常です)。シグネチャを補う(手順 4)か、UNZIP_DISABLE_ZIPBOMB_DETECTION=TRUEで強制します。 - ME ファームウェア(Intel ME / CSME)は起動チェーンから調べる:症状——ME ファームウェアのパーティション(FTPR/RBE/BUP)を展開した後、どこから手を付ければよいかわからず、コード内の ROM_SVC_* 呼び出しも理解できない。原因——ME は独立したプロセッサーのファームウェアで、起動チェーンが通常のファームウェアと異なります。ROM(PCH 内に固定され、抽出不可)が FTPR の署名を検証 → RBE を展開(PCH/SPI の初期化、UTOK の検証、boot_cfg)→ kernel(ThreadX RTOS)→ BUP(ハードウェアの bringup、boot_cfg を読み DCI を有効にするか判断)→ pm/vfs/heci/crypto など 38 個の user processes。対策——まず FPT(Flash Partition Table:ME 領域の固定オフセットにあるパーティションルーティングテーブル。各パーティションの位置/サイズ/種類を記録します。ROM/RBE にはファイルシステムがないため、パーティションの特定にはこれを使います。解析の参考として UEFIExtract のソースコードを参照してください)を解析し、次に RBE→kernel→BUP の順に追います。デバッグインターフェイス/ハードウェア初期化ロジックは BUP にあります。
- UTOK は ME のデバッグロックを解除する OEM のバックドア:症状——ME のデバッグインターフェイス(DCI/DFX)を有効にするスイッチが見つからない。原因——Intel が OEM 向けに残したロック解除の仕組みです。SPI Flash の FPT パーティションテーブルに独立した UTOK パーティションがあり、データを書き込むと DFX デバッグインターフェイスをロック解除できます(ハードウェアヒューズは不要です)。対策——RBE の
rbe_utok_checkをリバースエンジニアリングして検証ロジックを特定します。デバッグ機能が起動するのはフラグ UTOK[0x298]==1 の場合のみです(メーカーのデフォルト値 FF では起動しません)。また、DCI の有効化は複数レイヤーの検証チェーン(boot_mode==2、boot_cfg の解析、CT handler による制御、FPF Debug Auth など)によって制御されるため、フラグを変更するだけでは不十分です。特定のヒント:ME の実行ベースアドレスは 0x4000 なので、文字列オフセットはこれを基準に換算します。 - ME ファームウェアの脆弱性悪用は ROM cookie に制限される:症状——ME ファームウェア内でバッファーオーバーフロー(Intel-SA-00086 など:CT ファイルの
num_records(オフセット +0x06 の uint16)に検証がなく、record 数が 100 を超えるとスタック cookie/戻りアドレスが上書きされる)を見つけても、ROP が成功しない。原因——ME の cookie は ROM(抽出不可の mask ROM)が SRAM にあらかじめ書き込んだプロセスコンテキストテーブルから生成されるため、TXE プラットフォームのように TLS で回避できません。対策——まず対象プラットフォームの cookie の由来を確認します(ROM 生成の場合、オーバーフローを利用した攻撃には制限があります)。認証チェーン全体(UTOK→boot_cfg→CT handler の複数レイヤーの検証)をリバースエンジニアリングしてから、オーバーフロー以外の経路を探します。 - ブロック単位で周期的に変換される:症状——ファイル全体に対する単一の XOR/ローテーションが先頭部分でしか有効にならない。原因——変換がブロックサイズ(よく使われるのは 256/512/1024/2048/4096)ごとにリセットされる。対策——まず、一般的な Flash/転送ブロックサイズに基づき、変換周期がブロックごとにリセットされるか確認し、明示的にブロック単位でデコードします。
- ブロック先頭にマスクが含まれる難読化:症状——高エントロピーのファームウェアが強力に暗号化されていると判断される。原因——ブロックごとのマスクによる難読化(例:
mask = block[0]、plain[i] = ROR8(packed[i], n) XOR mask(マスクのバイト自体は変換の対象外で、i は 1 から始まります))。対策——まず強い crib(ベクターテーブルの SRAM スタックポインター/Thumb Reset Vector)で先頭ブロックの復元を制約し、その後モデルを検証します。 - 最頻値による crib は信頼できない:症状——テキスト密度の高いブロックで、最頻バイトをマスクにしても文字化けが解消されない。原因——最も多い平文バイトが必ずしもゼロとは限らない。対策——ブロック先頭バイトのモデルに切り替えます。検証基準:文字化けが解消されること、同系列の 2 つ目のファームウェアでも再現すること、round-trip の結果がバイト単位で一致すること。
- CRC 文字列 ≠ CRC フィールド:症状——CRC という文字列を見て、末尾に標準 CRC があると思い込む。原因——その文字列はメタデータキーにすぎません。対策——一般的な CRC/ハードウェア CRC/Adler/累算系を系統的に除外した後、未知のフィールドとして残し、無理に名称を付けないようにします。 (出典:reverse-skill field-journal、MIT)