# ELF Inspector

> 在瀏覽器裡打開 ELF：ELF 標頭、program header、節區、符號、重定位、notes 全部讀得到，點一列就對照 hex。檔案不會上傳，也不會離開這個分頁。

- URL: https://bytescope.dev/zh-Hant/elf-inspector/
- Locale: zh-Hant · Also: [en](https://bytescope.dev/en/elf-inspector.md) · [ja](https://bytescope.dev/ja/elf-inspector.md)
- Runs: 檔案不會離開你的瀏覽器 — 全部在本機處理。
- Requires: Any modern browser (Chrome, Edge, Firefox, Safari), desktop and mobile.
- Updated: 2026-09-08

## What it does

把 ELF 丟進來就好——執行檔、`.so`、核心模組、`.o`、CTF 題目、韌體映像檔都可以——你會拿到 `readelf -a` 會告訴你的東西，而且是兩個互相連動的面板。左邊是結構樹：ELF 標頭一個欄位一列、program headers、節區表、分開放的 `.dynsym` 與 `.symtab`、`.dynamic`、每一個重定位節區、notes，以及版本定義與版本需求。右邊是檔案的位元組。點任何一列，右邊就剛好標出這一列是從哪幾個位元組解出來的；點一個位元組，左邊就會找出蓋住它的最內層結構。

摘要卡先回答大家真正想知道的事，不用先翻表：這是 ELF32 還是 ELF64、什麼架構、什麼位元組序、EXEC / DYN / REL、進入點在哪；如果這個架構的 `e_flags` 有意義（RISC-V 和 MIPS 都有），也把它解出來。接著是推導出來的事實：靜態還是動態連結、要哪一個載入器、有沒有被 strip、是不是 PIE、`.note.gnu.build-id` 裡的 Build ID，以及 `PT_GNU_STACK` 有沒有帶 `PF_X`。最後這一項跟安全直接有關，所以直接寫成一句話，不要你自己去表裡面翻。

什麼都不會上傳，而對這群人來說，這是前提不是賣點。CTF 的執行檔是別人還沒解出來的題目，韌體通常是客戶的財產；為了看一個節區標頭就把它丟到別人的伺服器，很不划算。檔案由分頁讀進來，交給 Web Worker，在記憶體裡解析——沒有上傳端點，你可以開著 DevTools 自己確認：打開一個檔案完全不會發出網路請求。解析跑在 worker 裡，所以 60 MB 的檔案也不會把分頁卡死；超過 128 MiB 的則會直接說明理由拒收，而不是收下來然後當掉。

這頁印出來的每一個名稱——架構、類型、節區類型、符號的 type / bind / visibility、重定位類型、dynamic tag——都跟 GNU binutils 的 `readelf` 一字不差，因為這個解析器是拿真正的 `readelf` 輸出來對答案的，不是拿自己的期待值。1,900 個符號的檔案很常見，40 萬個的也存在，所以符號和重定位的過濾、排序、分頁都在資料所在的那一端做，不會一列一列畫進頁面裡。

## Limits

Does not disassemble or decompile code and does not read PE (.exe) or Mach-O files; it shows ELF headers, sections, segments and symbols.

## Machine output

Copy JSON copies an envelope `{ tool, url, generatedAt, userAgentHint, result }`. `tool` is `"elf-inspector"`; `result` is the on-screen ELF structure below. `runpath`, `interpreter`, note `descriptor` text, and dynamic `valueText` may contain absolute paths from the build machine (user names under `/home/...`). The dropped file name, hex-pane bytes, section payload bytes, and disassembly are omitted. Section and symbol names are included (capped).

- `byteLength` (number | null) — File size in bytes. `null` = not supplied.
- `summary` (object) — Summary-card facts: `className`, `endian`, `osAbiName`, `typeName`, `machineName`, `entryHex`, `flagsHex`, `flagsText`, `linkage` (`"dynamic"` | `"static"` | `"none"`, or `null`), `interpreter` (or `null` if absent), `pie` (or `null`), `stripped` (`true` = no `.symtab`; distinct from `.dynsym` still present), `symtabCount`, `dynsymCount`, `buildId` (or `null`), `execStack` (`"exec"` | `"noexec"` | `"absent"`, or `null`), `needed` (DT_NEEDED names), `soname` (or `null`), `runpath` (DT_RUNPATH or DT_RPATH string, or `null`; may be a build-machine path), `sectionCount`, `segmentCount`, `relocationCount`, `noteCount`, `debugSections` (names only).
- `header` (object) — ELF header as shown: class/endian/ABI/type/machine numbers and names, `entryHex`, `phoff` / `shoff` as decimal strings, flags, table sizes, and `fields` (`name` + `display` only; raw `value` bytes are omitted).
- `segments` (object) — Program headers in file order, capped at 200. `rows`, `truncated`, `totalCount` (real count). Each row matches the on-screen columns plus `interpreter` when that segment is PT_INTERP.
- `sections` (object) — Section header table in file order, capped at 200. Names and sizes only — no section contents. `totalCount` is the real section count.
- `dynamic` (object) — `.dynamic` entries in file order, capped at 200. Each: `tagName`, `tagHex`, `valueText`, `name` (resolved NEEDED/SONAME/RPATH/RUNPATH string, or `null`).
- `notes` (object) — Note records in on-screen order, capped at 200. `descriptor` is the interpreted text shown (build-id hex, printable comment, or hex preview) — not the raw descriptor bytes.
- `symbols` (object | null) — `null` = symbol rows were not supplied (paged table not copied). When present: rows capped at 200 from `.symtab` then `.dynsym` in table order; `truncated`; `totalCount` is the real symbol count across those tables, not the copied length. Empty `rows` with `totalCount: 0` = parsed, no symbol rows.
- `versionDefs` (object) — GNU version definitions, capped at 200.
- `versionNeeds` (object) — GNU version needs, capped at 200.
- `warnings` (array of strings) — Parser warnings shown on screen, in parse order.

## FAQ

### 我的檔案會被上傳嗎？

不會。檔案在這個分頁裡讀取，轉交給 Web Worker，在那裡解析。你在使用時這個頁面不會發出任何網路請求，所以你可以開著 DevTools 的 Network 分頁，看它一直是空的。這件事在這裡比在別的工具更重要：會被丟進 ELF 檢視器的，往往是還沒發布的韌體、客戶的建置產物，或別人還在解的 CTF 題目。

### 這是反組譯器或反編譯器嗎？

都不是，也不假裝是。它讀的是 ELF 的中繼資料：可以告訴你 `.text` 是從檔案位移 0x1000 開始、41 KiB 的可執行 PROGBITS，`puts` 是透過 `R_X86_64_JUMP_SLOT` 從 `libc.so.6` 來的，進入點在 0x1140；但沒辦法告訴你那些指令在做什麼。要那個就得用 objdump、Ghidra、Binary Ninja 或 IDA，而它們都不會跑在瀏覽器分頁裡。

### 支援哪些架構？

全部，因為 ELF 的結構不會因為目標平台而改變。x86-64、i386、ARM、AArch64、RISC-V、兩種位元組序的 MIPS、PowerPC、S390、SPARC、Xtensa、AVR 都是同一套讀法；跟架構有關的只有重定位類型的名稱和 `e_flags` 的意義，那是查每個機器各自的表得來的。表裡沒有的機器一樣可以完整解析，並且直接顯示原始的 `e_machine` 數字，不會替它掰一個名字。

### 最大能開多大的檔案？

128 MiB。解析時整個檔案都要放在分頁的記憶體裡，上面再加上每個符號、每筆重定位各一個物件，所以尖峰用量是檔案大小的好幾倍。超過這個上限，分頁會在解析途中被系統收掉，看起來就像頁面當掉，所以工具選擇直接說明理由拒收。會這麼大的通常是沒 strip 的除錯建置——在自己電腦上用 `readelf` 或 `objdump`，或先把除錯節區拿掉。

### 它說檔案被 strip 了，符號跑去哪了？

被 strip 的檔案沒有 `.symtab`，區域函式和 static 函式的名字就住在那裡，而 `strip` 會把整個節區砍掉。留下來的是 `.dynsym`，也就是動態符號表，因為載入器在執行時要靠它解出匯入與匯出——所以你還是看得到它呼叫了哪些函式庫的哪些函式、匯出了什麼，只是看不到它自己內部函式的名字。摘要卡會說明你現在看的是哪一種，以及各有幾個符號。

### 檔案壞掉了，它還是給了我東西，這是 bug 嗎？

這是刻意的。下載到一半斷掉、被人手工改過的標頭、指到檔案結尾外面的節區表——這些在 CTF 裡都是正常輸入，而一個遇到被改壞的 ELF 就給你一張空白錯誤頁的檢視器，正好在最需要它的時候沒有用。整份檔案層級的問題（沒有 ELF 魔術數字、class 或位元組序不可能成立）還是會中止解析，但單一節區、符號或重定位的問題會退化成警告，而每一則警告都會顯示在頁面上，不會被吞掉。

## Related tools

- [APK Analyzer](https://bytescope.dev/zh-Hant/apk-analyzer.md) — 從 APK 裡把原生的 .so 抽出來，再拿到這裡讀。
- [Hex Editor](https://bytescope.dev/zh-Hant/hex-editor.md) — 用同一個 hex 面板，打開任何檔案的原始位元組。
- [File Type Identifier](https://bytescope.dev/zh-Hant/file-type.md) — 不確定它到底是不是 ELF？用魔術位元組判斷檔案類型。
