rea-reverse

文件类型识别与十六进制查看器

把任意文件拖进来——没有扩展名的、几个 GB 的都行——按魔数判断它是什么格式,找出里面嵌着哪些文件头,看每个数据块的熵怎么变化、能提取出哪些字符串,再用十六进制视图逐字节核对。

文件不会离开你的浏览器. 文件由一个没有网络权限的 Web Worker 分块读取,不会上传到任何地方。 如何自己验证

在单独页面打开工具(无广告、无统计)

没有扩展名的文件、CTF 里一个叫 chall 的附件、从 flash 芯片里读出来的固件、厂商支持页面上下载的 .bin——光看名字什么都判断不了,直接双击更不是第一步该做的事。你真正需要的,是 file 和 xxd 能告诉你的那些信息,再顺便看看里面还埋着什么,然后才去挑真正能打开它的工具。

这个页面在浏览器里把这件事做完。文件按块读取,所以 2 GB 的磁盘镜像和 2 KB 的 .pyc 处理方式一样,而且不会上传。

使用步骤

  1. 拿出那个认不出来的文件:没有扩展名、扩展名不对、CTF 附件或者固件 .bin 都行。多大都可以,文件是分块读取的。
  2. 把它拖到上方的拖放区,或者点“选择文件”。文件不会上传。
  3. 先看“这是什么文件”(格式、可信度和依据偏移处的字节),再看“内嵌签名”里装了什么、熵图和字符串。任何偏移都可以在十六进制视图里核对。

真实输出示例

我们下载了一个 OpenWrt 官方路由器固件,用发布目录里的 sha256sums 核对过,然后拖进本页,把页面显示的结果复制下来。排版做了压缩(标签和值放在同一行,十六进制按行合并,长列表做了截断),所有值都原样保留。

样本: openwrt-25.12.6-ath79-generic-tplink_archer-c7-v5-squashfs-sysupgrade.bin(OpenWrt 25.12.6,GPL-2.0),2026-11-02 从 downloads.openwrt.org 下载,SHA-256 0ec9643b…b0be9d (downloads.openwrt.org/releases/25.12.6/)

这是什么文件
U-Boot legacy image (uImage) .uimg 可信度:高 固件
格式                          可信度  依据
U-Boot legacy image (uImage)  高      0x0 27 05 19 56
名称                  MIPS OpenWrt Linux-6.12.112
操作系统 / 子系统     Linux
架构                  MIPS
类型                  kernel
压缩算法              lzma
声明的大小            2751070 bytes
文件大小              7.1 MB (7405862 B)

十六进制视图
00000000  27 05 19 56 4b 16 06 4c 6a d3 ec 82 00 29 fa 5e  '..VK..Lj....).^
00000010  80 06 00 00 80 06 00 00 1c 8f 41 47 05 05 02 03  ..........AG....
00000020  4d 49 50 53 20 4f 70 65 6e 57 72 74 20 4c 69 6e  MIPS OpenWrt Lin
…

内嵌签名
偏移      签名                          类别         备注
0x0       U-Boot legacy image (uImage)  固件
0x29fa9e  SquashFS file system          固件
0x29fafe  xz                            归档 / 压缩  位于 0x29fa9e 处的 SquashFS file system 内部
0x2c64e6  xz                            归档 / 压缩  位于 0x29fa9e 处的 SquashFS file system 内部
…
0x709d36  xz                            归档 / 压缩  位于 0x29fa9e 处的 SquashFS file system 内部

熵
共 1024 个块,每块 7.1 KB;平均每字节 7.95 比特;1020 个块高于 7.2。

字符串
共 12114 个长度不少于 6 个字符的字符串(ASCII 和 UTF-16LE)。
0x20   ASCII  MIPS OpenWrt Linux-6.12.112
…

能看到什么

它是什么文件。 先看偏移 0 处的签名,以及某些格式固定使用的偏移:tar 在 257,ISO 9660 在 0x8001,ext2/3/4 在 0x438,GPT 头在 512,DMG 的 koly 尾部在距文件末尾 512 字节处。每条结果都会给出匹配到的字节和它所在的偏移、一个可信度,以及文件头里读出的信息:

  • 高:签名匹配,并且结构校验也对得上。例如 ELF 的 class 和 version 字节、tar 头校验和、U-Boot 头的 CRC32、SquashFS 的块大小与 block_log 一致、Java class 版本号在合理范围内。
  • 中:签名匹配,但没法进一步校验,或者校验没通过(U-Boot CRC 不对、声明的大小比文件还大、PNG 不以 IEND 块结尾、gzip 压缩流没到结尾就断了)。
  • 低:签名很短或很常见,比如 MP3 帧同步、zlib 头、第 510 字节处的 55 AA。只能当线索。

覆盖的格式包括:

  • 可执行文件:ELF(架构、32/64 位、字节序、类型、OS/ABI)、PE(PE32 还是 PE32+、机器类型、EXE 还是 DLL、子系统;带 CLI 头的标为 .NET 程序集)、Mach-O(CPU、文件类型),以及通用 Mach-O 里每个切片的架构。CA FE BA BE 会根据后面 4 个字节区分是通用 Mach-O 还是 Java class,和 file(1) 的规则相同。
  • 字节码:Java class(带 Java 版本)、DEX/ODEX/VDEX/ART、WebAssembly 模块和组件、.pyc(根据魔数读出 Python 版本,从 2.x 到 3.15 开发版;预发布版的魔数会单独标出)、Lua 5.1–5.5 和 LuaJIT、Hermes(React Native)及其字节码版本、V8 代码缓存(bytenode 的 .jsc)、Erlang BEAM、Dart kernel。
  • 归档与压缩:ZIP、gzip(原文件名和操作系统)、xz、Zstandard、bzip2、7z、RAR 4 和 5、LZ4、lzip、LZMA、Unix compress、CAB、ar 和 .deb、RPM、XAR(.pkg)、CRX、tar(POSIX 和 GNU)、cpio(newc、CRC、odc、二进制)、Electron ASAR。
  • 基于 ZIP 的格式,根据中央目录里的文件名区分:APK、AAB、APKS、XAPK、AAR、IPA、JAR、WAR、DOCX、XLSX、PPTX、EPUB、ODT/ODS/ODP、MSIX、VSIX、NuGet、Python wheel 和 egg、3MF、KMZ、Fiddler SAZ、Firefox XPI 及其他 WebExtension 扩展包。
  • 固件与文件系统:SquashFS(版本、压缩算法、块大小、镜像大小,也认厂商改过字节序的魔数)、U-Boot uImage(名称、操作系统、架构、镜像类型、压缩算法、头 CRC)、Broadcom TRX、UBI 和 UBIFS、两种字节序的 JFFS2、cramfs、romfs、设备树、Android boot.img 和 vendor_boot.img、Android sparse 镜像(带展开后的大小)、OTA payload.bin、DTBO、ext2/3/4、FAT、NTFS、exFAT、GPT 和 MBR、ISO 9660、DMG、qcow、VMDK、VHDX、VDI、WIM、LUKS、Intel HEX 和 S-record。
  • 图片与音视频:PNG、JPEG、GIF、BMP、WebP、TIFF 及基于它的 RAW 格式、ICO/CUR、PSD、QOI、ICNS、JPEG XL、JPEG 2000、HEIF/AVIF,按 ftyp brand 区分的 MP4/MOV/M4A/3GP,WAV(编码、声道、采样率)、AVI、Ogg(及其中的编码)、FLAC、MP3、AAC、Matroska 和 WebM、FLV、MIDI、AIFF、MPEG-TS 和 PS、ASF。
  • 数据、密钥与文本:PDF、OLE2 复合文档、RTF、PostScript、二进制和 XML 格式的 plist、SQLite 及其 WAL、pcap 和 pcapng、Windows 快捷方式、注册表配置单元、事件日志和 minidump、字体、Git pack、Android 二进制 XML 和 resources.arsc、DER 编码的 X.509 证书、DER 或 BER 编码的 CMS / PKCS#7 SignedData(被签名的内容是带描述文件字段的 plist 时报为 iOS 描述文件)、按标签识别的 PEM 块、OpenSSH 密钥、Java keystore,以及按内容识别的文本格式:HAR、source map、JSON、HTML、SVG、XML、带 shebang 的脚本、十六进制文本形式的 EVM 字节码。

Protocol Buffers 认不出来。 序列化后的 protobuf 消息和 FlatBuffer、裸 C 结构体、Brotli 流以及任何加密数据一样,没有魔数:第一个字节只是第一个字段的 tag。工具会直接说明这一点,不会瞎猜;这时能依靠的只有下面的熵图和字符串。

用其他工具打开。 如果本站有对应格式的工具,结果里会给出链接:APK/AAB 分析器、处理 ELF/PE/Mach-O 的原生二进制概览、.NET 元数据查看器、WASM 转 WAT、ASAR 解包、HAR / SAZ 接口归纳、source map 还原、EVM 选择器提取,描述文件和 IPA 则链接到 iOS 描述文件与 IPA 查看器。页面之间不会传递文件,需要在那边重新选择同一个文件。

十六进制视图。 整个文件的偏移、十六进制和 ASCII 三栏。只读取当前可见的行,所以滚动几个 GB 的镜像和滚动小文件一样轻。用来识别文件的那几个字节会高亮。可以按十六进制(0x8001)或十进制跳转到任意偏移;点击一个字节选中它,拖动或按住 Shift 点击扩展选区。下方面板会把选区开头的字节分别按 8/16/32/64 位整数(有符号和无符号、大小端)、32/64 位浮点数和 Unix 时间戳解读,并把选区按 ASCII、UTF-8 和 UTF-16LE 显示成文本。

内嵌签名。 类似 binwalk 的签名扫描:逐字节扫一遍文件,列出每个出现已知文件头并通过校验的偏移,比如固件里的 gzip 流、uImage 内核后面的 SquashFS 根文件系统、PDF 里的 PNG。落在声明了长度的容器(SquashFS、uImage、TRX、设备树、cramfs、DEX、SQLite)内部的命中会标为嵌套。重复出现的结构(JFFS2 节点、UBI 擦除块、cpio 和 ZIP 成员)每段只报告一次。最多保留 1,000 个命中。

熵图。 把文件分成大约 1,024 个块(每块至少 1 KiB),画出每块的香农熵。贴着 0 的是填充或擦除过的 flash,代码和文本一般在 4.5 到 6.5 之间,接近 8 的平台则是压缩或加密数据。点击图表可以跳到十六进制视图里对应的块。

字符串。 长度不少于 6 个字符(可在 2 到 64 之间调整)的可打印 ASCII 和 UTF-16LE 串,每条都带偏移,点偏移就能在十六进制视图里看到它。保留前 20,000 条并可筛选;大文件找到这么多条后就停止扫描,并说明扫到了哪里。

原理

页面把文件交给一个 Web Worker。Worker 先用 Blob.slice 读出前 64 KiB,跑一遍签名表:每一项包含魔数、魔数所在的偏移,以及格式允许时对周围头字段的校验;匹配上的再去读详细信息。遇到 ZIP 时,Worker 从文件末尾读出中央目录,根据里面列出的条目判断具体格式。如果没有任何二进制签名匹配,就把前 8 KiB 当文本来判断。

接着 Worker 再按 4 MiB 一块把整个文件读一遍,同时做内嵌签名扫描、计算各块的熵并提取字符串,边扫边报告进度。签名表上建了一个两字节的索引,所以扫描时每个字节只需查一次表。熵和字符串的代码与原生二进制概览用的是同一套。无论文件多大,内存峰值基本就是一块的大小。

我们拿 macOS 自带的 file 5.41 和 binwalk 3.1.0 做了对比,样本是专门准备的 98 个文件:用 zip、tar、gzip、xz、zstd、7zz、bzip2、lz4、cpio 和 mksquashfs(gzip、xz、lz4、zstd 四种压缩)生成的归档,CPython 3.11、3.12、3.13、3.14 编译出的 .pyc,一个 SQLite 数据库,用 sips 转换的图片,ffmpeg 生成的音视频,openssl 生成的证书,hdiutil 和 pkgbuild 生成的 DMG、ISO 和 .pkg,按各自打包工具的目录结构做的 ZIP 类安装包,以及本站自己的 ELF、PE、Mach-O、APK、AAB、WASM、ASAR 和 HAR 测试文件。每个样本的首个结果都和我们生成它时的格式一致。file 对其中 90 个给出了相同的格式;剩下 8 个是 file 5.41 没覆盖到的情况:4 个 .pyc(Python 3.11 到 3.14)、ASAR 归档和 MPEG-TS 它都报成 data,PEM 私钥报成 ASCII text,UDZO 格式的 DMG 报成 zlib compressed data(它不看 koly 尾部)。整个样本集上 binwalk 一共报告了 53 个偏移,其中 51 个这里也能找到:偏移 0 的由识别结果覆盖,其余由内嵌扫描找到。另外 2 个是 binwalk 在 TIFF 和 JPEG 2000 图片的色彩配置里标出的 “Copyright text” 字符串,在这里会出现在字符串列表中。在一个人为拼接了 gzip 文件、SquashFS 镜像和 PNG 的样本上,两个工具报告的三个偏移完全一致;这里还会列出 SquashFS 镜像内部的 xz 压缩块,并标为嵌套。

大文件方面,我们测了一个 2.5 GB 的稀疏镜像,在偏移 0 放了 gzip 头、1 GiB 处放了 SquashFS 镜像、2 GiB 处放了 PNG:在 Node.js 22 里完整扫描用时 10.4 秒,常驻内存峰值 133 MiB,三个文件头都在正确的偏移被找到。

文件如何保持私密

文件不会上传:

  • 识别工具运行在一个单独的框架(iframe)里,来源是 tools.rea-reverse.com。下面说的处理都在这个框架里、在你的设备上完成。
  • 这个页面在正文之后展示一个广告。广告脚本运行在 rea-reverse.com 上,而框架属于另一个源(origin),浏览器不允许广告脚本或本页的任何其他脚本读取你拖入的文件或结果。
  • 框架的内容安全策略拦截它的脚本能发起的所有请求(connect-src 'none',也不加载任何第三方资源)。框架不能自行打开窗口或让本页跳转;只有你点击报告里的链接时才会打开页面。它传给本页的只有自身高度和下面这些统计事件。
  • 识别、内嵌扫描、熵和字符串都在你设备上的 Web Worker 里完成;十六进制视图在框架里直接按块读取文件。
  • 从文件里取出的一切内容(名称、标签、字符串、字节)都以纯文本显示,不会被当作 HTML 解析或执行。
  • 唯一的统计事件 filetype_identify 只包含结果状态、首个结果的粗粒度类别和可信度(例如 archive 和 high)、大小区间和耗时区间。不会包含文件名、格式名称、字节、字符串或偏移。框架把事件交给本页,由本页发送。完整列表见隐私页。
  • 不想要任何广告和统计?在单独页面打开识别工具。

如何自己验证

  1. 打开浏览器开发者工具的 Network(网络) 面板,加载本页。发往 tools.rea-reverse.com 的请求就是框架:它的 HTML、样式、字体和脚本,以及 Worker 脚本 /_astro/filetype-worker-*.js。发往 Google 域名的请求是本页的广告。
  2. 清空日志,然后识别一个文件,滚动十六进制视图,再改一下字符串的最短长度。在过滤框里输入 domain:tools.rea-reverse.com:最多只会再出现一次 Worker 脚本 /_astro/filetype-worker-*.js(为下一次运行准备的新 Worker),没有任何请求带着你的数据。广告请求可能还会继续,但它们来自本页,不带框架里的任何内容。
  3. 点开来自 tools.rea-reverse.com 的 file-type-identifier/ 文档,查看 Headers:它的 Content-Security-Policy 里有 connect-src 'none' 和 frame-ancestors https://rea-reverse.com。在 Chrome 里,Application(应用)> Frames 也能看到框架的源和策略。Worker 运行在 default-src 'none'; script-src 'self'; connect-src 'none' 这条策略下,浏览器不允许它发出任何网络请求。
  4. 检查隔离:在 Console(控制台) 里,上下文选 top,运行 document.getElementById('tool-frame').contentDocument,结果是 null;frames[0].document 会抛出 SecurityError。广告脚本拿到的也是这个结果。
  5. 或者先打开页面再断网(关掉 Wi-Fi,或在 Network 面板勾选 Offline),然后识别文件,照样能用。

局限

  • 签名只是一种声明。 被截断或损坏的文件开头照样是正确的字节,有些格式也只能靠很短的特征认出来。只有 PNG(看结尾是不是 IEND 块)和不超过 8 MiB 的 gzip(试解压到压缩流结尾)会检查结尾是否缺失;截断的 ZIP 因为中央目录没了也能看出来。其他格式不检查。每条结果的可信度和备注说明了到底校验了多少。
  • 不解包。 不会提取、解密或执行任何东西;除了对不超过 8 MiB 的 gzip 做上面那次结尾检查(解压结果直接丢弃),也不解压任何东西。内嵌扫描只告诉你东西从哪里开始;要切出来,请用 binwalk -e、unblob 或 dd,Binwalk 3 使用指南里有完整步骤。
  • ZIP 细分依赖中央目录:ZIP64 归档和超过 1 GiB 的 ZIP 文件只会报成普通 ZIP,并附上说明。
  • 文本格式只根据前 8 KiB 判断,所以一个到后面才看得出是 HAR 的 JSON 文件会被报成 JSON。
  • 内嵌命中可能是巧合,尤其是在压缩数据里,任何字节组合都可能出现。每个扫描签名都要求至少 4 个字节或通过头部校验,这让误报很少,但不是不可能。
  • V8 启动快照(snapshot_blob.bin)没有稳定的签名,识别不了;能识别的只有 V8 代码缓存。
  • 字符串只包括可打印 ASCII(0x20–0x7e)的单字节串和 UTF-16LE 串,和 strings、strings -e l 找到的一样。

合规

请只识别你自己生成的文件、开源软件、CTF 题目或你获得授权评估的文件。尊重所分析软件的许可证(包括其中限制逆向工程的条款)以及适用的法律。参见免责声明。

对应的命令行

file 读的是同一个 uImage 头(macOS 自带的 file 5.41):

$ file openwrt-25.12.6-ath79-generic-tplink_archer-c7-v5-squashfs-sysupgrade.bin
openwrt-25.12.6-ath79-generic-tplink_archer-c7-v5-squashfs-sysupgrade.bin: u-boot legacy uImage, MIPS OpenWrt Linux-6.12.112, Linux/MIPS, OS Kernel Image (lzma), 2751070 bytes, Sat Oct 17 21:45:38 2026, Load Address: 0X80060000, Entry Point: 0X80060000, Header CRC: 0X4B16064C, Data CRC: 0X1C8F4147

魔数本身:偏移 0 处的 27 05 19 56:

$ xxd -l 64 openwrt-25.12.6-ath79-generic-tplink_archer-c7-v5-squashfs-sysupgrade.bin
00000000: 2705 1956 4b16 064c 6ad3 ec82 0029 fa5e  '..VK..Lj....).^
00000010: 8006 0000 8006 0000 1c8f 4147 0505 0203  ..........AG....
00000020: 4d49 5053 204f 7065 6e57 7274 204c 696e  MIPS OpenWrt Lin
00000030: 7578 2d36 2e31 322e 3131 3200 0000 0000  ux-6.12.112.....

binwalk 3.1.0 在同样的偏移找到同样的两个顶层部分(过宽的输出做了截断),但没有列出 SquashFS 镜像内部的 xz 数据块:

$ binwalk openwrt-25.12.6-ath79-generic-tplink_archer-c7-v5-squashfs-sysupgrade.bin
…
DECIMAL                            HEXADECIMAL                        DESCRIPTION
…
0                                  0x0                                uImage firmware image, header size: 64 bytes, data size: 2751070 bytes, compression: lzma, CPU: MIPS32, OS: Linux, image type: OS
                                                                      Kernel Image, load address: 0x80060000, entry point: 0x80060000, creation time: 2026-10-17 21:45:38, image name: "MIPS OpenWrt
                                                                      Linux-6.12.112"
2751134                            0x29FA9E                           SquashFS file system, little endian, version: 4.0, compression: xz, inode count: 1213, block size: 262144, image size: 4629526
                                                                      bytes, created: 2026-10-17 21:45:38
…

常见问题

怎么在线识别文件类型,又不用上传文件?

把文件拖到这里。浏览器里的 Web Worker 分块读取它,不会上传,所以几个 GB 的镜像和小文件处理方式一样。结果会给出格式、匹配到的字节所在偏移,以及可信度。

没有扩展名的文件,怎么知道它是什么?

看开头几个字节通常就够了。大多数格式以固定的签名(魔数)开头,比如 U-Boot 镜像是 27 05 19 56,ELF 是 7F 45 4C 46,ZIP 以及所有基于 ZIP 的格式是 50 4B 03 04。少数格式的签名在固定偏移处,例如 tar 在 257。本页两种都查,并显示匹配到的是哪些字节。

能当在线十六进制查看器用吗?

能。十六进制视图覆盖整个文件,只读取可见的行。可以按十六进制或十进制跳到某个偏移,选中一段字节,下方面板会把它解读成整数、浮点数、Unix 时间戳和文本。

和 file、binwalk 有什么区别?

file 根据文件开头判断格式,binwalk 扫描内嵌签名并负责提取。这个页面不用安装任何东西就能完成前两件事,还多了十六进制视图、熵图和字符串,但什么都不提取。在上面的 OpenWrt 固件上,三者对 uImage 头和 0x29fa9e 处的 SquashFS 镜像结论一致。

熵等于 8 能说明什么?

只能说明这些字节看起来是随机的。压缩数据、加密数据和随机密钥都会到这个值。上面的 OpenWrt 固件平均 7.95,因为 LZMA 压缩的内核和 xz 压缩的 SquashFS 根文件系统都是压缩数据。

文件明明是 APK,结果却显示 ZIP。

APK 和其他 ZIP 文件的区别,是中央目录里有 AndroidManifest.xml,并且有 classes.dex 或 resources.arsc。如果中央目录读不出来(ZIP64、文件损坏或大于 1 GiB),结果就停留在 ZIP,并附上说明。

更新于

相关教程