# Firmware Inspector

> ファームウェアの.binをブラウザで開き、エントロピーマップ、埋め込み形式のシグネチャ、可読な文字列、uImage・TRX・ESPのヘッダを読む。アップロードなし。

- URL: https://bytescope.dev/ja/firmware-inspector/
- Locale: ja · Also: [en](https://bytescope.dev/en/firmware-inspector.md) · [zh-Hant](https://bytescope.dev/zh-Hant/firmware-inspector.md)
- Runs: イメージはこのタブで読み、Web Worker の中だけで解析する。どこにもアップロードしない。
- Requires: Any modern browser (Chrome, Edge, Firefox, Safari), desktop and mobile.
- Updated: 2026-09-08

## What it does

### 素性の分からないイメージと向き合う最初の十分

ルーターやカメラや開発ボードから吸い出した `.bin` が手元にあって、資料は何もない。最初に知りたいことはいつも同じ四つで、サイズはどれくらいか、どこが圧縮されているか、既知の形式が何か埋まっていないか、そして平文で自分のことを喋っていないか。`binwalk` を一回と `strings | grep` を二、三回、というあの作業をそのままこのページがやる — ブラウザの Web Worker の中で、ファイルはどこへも動かさずに。意図的にやらないのは展開のほうで、このツールは特定して切り出すところまで。持ち帰るのは展開済みのディレクトリではなく、オフセットと長さになる。

### エントロピーマップと、それだけでは分からないこと

エントロピーはブロックごとに一つの数字で、256 通りのバイト値がどれくらい均等にばらけているかを表す。同じバイトの繰り返しなら `0`、すべての値が同じ頻度で現れれば `8`。消去済みのフラッシュはゼロに張り付き、ブートローダーのコードやテーブルは中ほどに落ち、圧縮済みのペイロードは上端に貼り付いたまま動かない — だからセクションの境目は、探しにいく数値ではなく目に見える段差になる。ブロックサイズは選べて、4096 バイトなら全体の形が、256 バイトならおおよその位置が分かったあとの正確な境界が見える。帯は `padding`・`structured`・`mixed`・`compressed` の四つで、最後を「暗号化」と呼ばないのは意図的だ。バイトの頻度分布では圧縮と暗号化を区別できない。区別してみせるツールは、知らないことを言っている。

### シグネチャ、領域、そしてその隙間

走査はイメージを一度なめて、既知のマジックが座っている位置を全部挙げる。四種類のバイトオーダーの squashfs、JFFS2、CramFS、UBI と UBIFS、uImage、TRX、DfuSe、末尾の DFU サフィックス、Android ブートイメージ、デバイスツリー、Netgear CHK、PEM や SSH 鍵の目印 — さらにこのサイトがファイル種別判定に使っている共通カタログも丸ごと。重なった当たりも全部出す。gzip の塊の中にある squashfs のマジックは、片付けるべき誤りではなくファイルについての事実だから。当たった位置からは領域が始まり、次の当たりまで、あるいはヘッダ自身が長さを申告していればそこまで続く。申告するのは実質 uImage と TRX だけだ。申告された長さが次の当たりまでの距離より短ければ、地図には隙間が残る — その隙間はそのまま描く。どのシグネチャも名乗らなかったバイト列は発見であって、そこまで領域を伸ばすのは枠を付けた当て推量になる。

### 文字列、ヘッダ、そして「分からない」と言える推定

可読な並びは ASCII と UTF-16LE で取り出し、見た目でタグを付ける。`url`・`ip`・`email`・`path`・`version`・`private-key`・`ssh-key`・`certificate`・`credential`・`busybox`・`linux-version`・`u-boot` の十二種類。タグは形であって主張ではない — `1.2.3.4` に `ip` と `version` の両方が付くのは本当に両方の形をしているからで、片方に決めてやるよりも、どちらでありうるかを見せるほうが役に立つ。ヘッダが解ければフィールドはその形式自身の名前のまま出る。uImage のロードアドレスとエントリポイントと CRC、ESP の各セグメントのロードアドレスと長さ、TRX のオフセット、Android boot のページサイズとカーネルコマンドライン、そしてファイル末尾から読む DFU サフィックス。アーキテクチャがそのどれかから、あるいはターゲット名を含む Linux バージョン文字列から導けるなら、根拠を添えて出す。導けなければ `unknown` と出す — 既定値も、いちばんよくある答えも使わない。

### どこにもアップロードしない。ここではそれが特に効く

ファームウェア解析に持ち込まれるファイルは、いちばん外に出せない類のものだ。顧客の機器から吸い出したイメージ、まだ出荷していないビルド、他人の箱から取ったダンプ。このページは裏に API のない静的ファイルで、イメージは File API で読み、Web Worker へ転送し、そこで解析し、タブを閉じれば消える。確かめ方も直接的で、DevTools のネットワークタブを開いたまま 64 MB のイメージを解析すれば、そこが空のままなのが見える。

## Limits

Does not extract or decompress anything it finds (no gzip, LZMA or squashfs unpacking), does not disassemble code, and never labels a high-entropy region 'encrypted' — entropy cannot tell that apart from compressed. When no header or string states an architecture it reports unknown rather than guessing one.

## Machine output

Copy JSON copies an envelope `{ tool, url, generatedAt, userAgentHint, result }`. `tool` is `"firmware-inspector"`; `result` is the on-screen analysis below. The dropped file name, byte-view hex dump, and raw image bytes are omitted. Strings tagged `private-key` contribute offset / encoding / length only — never their text. High entropy is the band `"compressed"` (the histogram cannot tell compression from encryption apart).

- `byteLength` (number | null) — Image size in bytes. `null` = not supplied.
- `overallEntropy` (number | null) — Whole-input Shannon entropy in bits per byte (four decimals as on the overview card). `null` = not supplied.
- `band` (string | null) — Entropy band of the whole input: `"padding"` | `"structured"` | `"mixed"` | `"compressed"`. `null` = not supplied. Never `"encrypted"`.
- `architecture` (string | null) — ISA when a header/string rule fired (for example `"ARM"`). `null` = unknown (no rule fired); the page renders "unknown" and does not guess.
- `architectureEvidence` (array of strings) — Why `architecture` says what it says, on-screen order. Empty when `architecture` is `null`.
- `entropy` (object | null) — Chart facts: `overall`, `band` (`null` = the overview did not name a band; never inferred from the number), `blockSize` (or `null`), `blockCount` (or `null`). Per-block values are omitted. `null` = overall entropy was not supplied.
- `regions` (object) — Signature-derived regions in offset order, capped at 200. `rows`, `truncated`, `totalCount` (real region count, not the copied length).
- `hits` (object | null) — The signature-hit table as it was on screen, capped at 200 rows. `null` = hit page was not supplied. `rows` (`offset`, `id`, `name`, `source`, `confidence`, `size` or `null` if unknown, `detail`) are in offset order. `filtered` (boolean) — a filter was applied, so `rows` are matches from all over the image and NOT the first rows of the scan. `filter` — `null` when `filtered` is false; otherwise `{ minConfidence, source, textSearch }` in stable tokens (`minConfidence` `"low"` hides nothing; `source` `"all"` hides nothing; `textSearch` is only whether the search box held a term — the term itself is never copied). `matchedCount` — how many hits matched the filter; equal to `totalCount` when `filtered` is false. `totalCount` — how many hits the scan produced before any filter. `truncated` — more MATCHING hits exist than `rows` carries; it says nothing about the filter. `filterHidRows` — the filter excluded hits this copy does not carry. Empty `rows` with `totalCount: 0` = scanned, no hits.
- `hitsCapped` (boolean | null) — Scan stopped at the hit ceiling. `null` = not supplied.
- `strings` (object | null) — The string table as it was on screen, capped at 200 rows, excluding private-key material. `null` = string page was not supplied. Each row: `offset`, `encoding`, `text`, `tags`, `length`, in offset order. `filtered`, `filter`, `matchedCount`, `totalCount`, `truncated` and `filterHidRows` mean what they do for `hits`; the string `filter` is `{ tags, encoding, textSearch }` (empty `tags` and `encoding: "all"` hide nothing). `matchedCount` and `totalCount` both exclude private-key material and neither is the copied row count. Where the two counts contradict each other, `totalCount` is raised to `matchedCount` — a match is never dropped to make the numbers agree.
- `stringsCapped` (boolean | null) — Extraction stopped at the string ceiling. `null` = not supplied.
- `privateKeyCandidateCount` (number | null) — How many strings this tool tagged `private-key`. `null` = tags were not supplied; `0` = extraction ran, none found.
- `privateKeys` (object | null) — Private-key candidates: `offset`, `encoding`, `length` only — no text. Capped at 200. `null` = string page was not supplied. `totalCount` is the whole-image candidate count. Note that when `strings.filtered` is true these rows come off the same filtered table, so they are the candidates that matched that filter, not the first of `totalCount`.
- `tagCounts` (object | null) — Per-tag counts over the whole extracted list (the chip counts). `null` = not supplied.
- `headers` (object | null) — Parsed headers as shown: `uImage`, `esp`, `trx`, `android`, `dfu` (`null` = none parsed), `espHitCount`, `espCandidateCount`. `null` = headers were not supplied.

## FAQ

### これはブラウザ版の binwalk ですか？

前半だけです。エントロピー計測、シグネチャ走査、文字列抽出、ヘッダ解析 — `binwalk` の実行のうち「手元にあるのが何か」を教えてくれる部分は、ここに全部あります。展開はありません。何も伸長せず、squashfs をマウントもせず、gzip メンバーも展開しないので、返ってくるのは中身ではなくペイロードのオフセットと長さです。これは未完成ではなく引いた線で、`dd` で領域を切り出して本物のツールに渡せば、失うのは待ち時間だけです。

### アーキテクチャが `unknown` と出るのはなぜ？

そのイメージの中に、それを名乗るものがなかったからです。推定の根拠は三つだけで、順に試します。`arch` フィールドが U-Boot の定義値になっている uImage ヘッダ、ESP イメージヘッダ、`mips` や `armv7l` や `x86_64` のようなターゲット名を含む `linux-version` タグ付きの文字列。三つとも当たらなければ四つ目の規則もフォールバックもないので、`unknown` と出し、何を試したかを並べます。ファイルが裏付けていないアーキテクチャ名は、裏付けのある名前と画面上では見分けが付きません。だから正直な答えのほうが価値があります。

### あるブロックが 7.99 bits/byte です。暗号化されている？

エントロピーでは答えられませんし、バイト分布しか見ない他のどのツールでも答えられません。圧縮データも暗号化データも、設計上どちらもほぼ一様になります — 圧縮はそれが目的で、暗号化はそれが要件だからです。`7.99` は gzip とも LZMA とも JPEG とも AES とも矛盾しません。両者を分けうるのは端の構造のほうで、圧縮形式はたいてい領域の先頭付近にヘッダなりマジックなり長さフィールドなりを残し、シグネチャ走査がそれを拾います。高エントロピーなのに、その中にも手前にも形式のシグネチャが一つもない — それが疑ってよい形です。ただしそれは追いかける手掛かりであって、このページが下す判決ではありません。

### 当たりが一万件出ました。どれが本物？

確度の列と、どのテーブル由来かから見てください。確度は機械的で、マジックが 8 バイト以上、または 4 バイト以上でアラインメント条件付きなら `high`、4〜7 バイトの自由走査なら `medium`、それ未満は `low` です。洪水の正体はたいてい `esp-image` で、マジックが 1 バイトの `0xe9` しかないため偶然でも 256 バイトに一度ほど当たります。ESP のフラッシュダンプのオフセット `0` に出れば本物の情報、それ以外はノイズ — ヘッダのパネルが 4 KiB 境界でしか候補にしないのはそのためです。最低の確度を `high` にすれば、たいてい実際のレイアウトを説明する数件まで一気に減ります。

### どれくらい大きいファイルまで扱えますか？

64 MiB までで、上限はどれも黙って適用せず必ず表示します。シグネチャ走査は 10,000 件、文字列抽出は 200,000 件で打ち切り、どちらもオフセットの若いほうから残すので、欠けるのはファイルの後ろ側です。そのことをページ側で言うので、途中で切れた一覧が完走した一覧のふりをすることはありません。停止を押せば実行中の解析は取り消され、途中結果は出しません。半分で止まった走査は残りを見ていないのに、画面の上では完走したものと区別が付かないからです。

## Related tools

- [Binary Diff](https://bytescope.dev/ja/binary-diff.md) — 同じイメージの二つのビルドをバイト単位で比べる。
- [ELF Inspector](https://bytescope.dev/ja/elf-inspector.md) — ELF の位置が分かったら、セクションとシンボルを読む。
- [Hex Viewer / Editor](https://bytescope.dev/ja/hex-editor.md) — そのオフセットの生のバイトを開いて直す。
