没有扩展名的文件、CTF 里一个叫 chall 的附件、从 flash 芯片里读出来的固件、厂商支持页面上下载的 .bin——光看名字什么都判断不了,直接双击更不是第一步该做的事。你真正需要的,是 file 和 xxd 能告诉你的那些信息,再顺便看看里面还埋着什么,然后才去挑真正能打开它的工具。
这个页面在浏览器里把这件事做完。文件按块读取,所以 2 GB 的磁盘镜像和 2 KB 的 .pyc 处理方式一样,而且不会上传。
能看到什么
它是什么文件。 先看偏移 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 镜像(带展开后的大小)、OTApayload.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,按
ftypbrand 区分的 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,三个文件头都在正确的偏移被找到。
文件如何保持私密
文件不会上传:
- 识别、内嵌扫描、熵和字符串都在你设备上的 Web Worker 里完成;十六进制视图在页面里直接按块读取文件。
- 从文件里取出的一切内容(名称、标签、字符串、字节)都以纯文本显示,不会被当作 HTML 解析或执行。
- 唯一的统计事件
filetype_identify只包含结果状态、首个结果的粗粒度类别和可信度(例如archive和high)、大小区间和耗时区间。不会包含文件名、格式名称、字节、字符串或偏移。完整列表见隐私页。 - 工具页不展示广告。
如何自己验证
- 在选择文件之前,打开浏览器开发者工具,切到 Network(网络) 面板。识别一个文件,滚动十六进制视图,再改一下字符串的最短长度。你只会看到页面资源和 worker 脚本
/_astro/filetype-worker-*.js的请求,没有任何请求携带你的文件。 - 或者先打开页面,再断网(关掉 Wi-Fi,或者在 Network 面板勾选 Offline),然后识别文件,依然可以正常工作。
- worker 运行在内容安全策略
default-src 'none'; script-src 'self'; connect-src 'none'之下,浏览器不允许它发出任何网络请求。
局限
- 签名只是一种声明。 被截断或损坏的文件开头照样是正确的字节,有些格式也只能靠很短的特征认出来。只有 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找到的一样。
常见问题
为什么我的 .pyc 带着“预发布版”的备注?
前两个字节的魔数在每个 CPython 版本的开发周期里都会变。工具把整个区间都映射到对应版本,只要不是该版本的最终值就加上这条备注,意思是文件由 alpha、beta 或 rc 版本的解释器写出。
文件明明是 APK,结果却显示 ZIP。
APK 和其他 ZIP 文件的区别,是中央目录里有 AndroidManifest.xml,并且有 classes.dex 或 resources.arsc。如果中央目录读不出来(ZIP64、文件损坏或大于 1 GiB),结果就停留在 ZIP,并附上说明。
熵等于 8 能说明什么?
只能说明这些字节看起来是随机的。压缩数据、加密数据和随机密钥都会到这个值。固件镜像通常是混合的:bootloader 和填充部分熵很低,从压缩内核和根文件系统开始出现接近 8 的平台,而内嵌扫描一般也正是在这些位置找到它们的文件头。
和 file、binwalk 有什么区别?
file 根据文件开头判断格式,binwalk 扫描内嵌签名并负责提取。这个页面不用安装任何东西就能完成前两件事,还多了十六进制视图、熵图和字符串,但什么都不提取。签名表是我们自己整理的,覆盖范围和两者都不完全相同,上面的对比列出了结果不一致的地方。
合规
请只识别你自己生成的文件、开源软件、CTF 题目或你获得授权评估的文件。尊重所分析软件的许可证(包括其中限制逆向工程的条款)以及适用的法律。参见免责声明。
相关教程
- 不认识的文件:用魔数识别文件类型,再选对分析工具:用
file、xxd和 PowerShell 看同样的签名,以及 REA 的inspect-artifact怎么报告它们。 - file 命令认错文件类型的那些情况:JAR、APK、DOCX 与残缺文件:25 个样本分别过一遍
file5.41 和本工具的代码,记录两边各自认错的地方。 - Binwalk 3 使用指南:把内嵌扫描找到的东西切出来。
- 从固件 .bin 里提取 SquashFS 根文件系统:从扫描里的 SquashFS 偏移到解开的根文件系统。
- 用 REA 做固件分析:Binwalk、Unblob 解包再交给 Ghidra:完整的固件分析流程。
- CTF 逆向入门:从 file、strings 到拿到第一个 flag:把
file和strings当作做题的前两步。