文件扩展名是给人看的,魔数(magic number)才是给程序看的。前几字节就能识别一个文件是 PNG / JPEG / PDF / ZIP 还是别的什么——这是计算机识别文件类型的金标准。
为什么需要魔数
操作系统和大多数程序都不信任扩展名:
- macOS Finder 显示缩略图先看魔数
- Linux
file命令完全靠魔数(不看扩展名) - 浏览器 Content-Type sniffing 看响应体前几字节
- 上传文件的服务器要校验魔数防 webshell
把 shell.php 命名为 cat.jpg 上传,再通过 URL 执行 PHP——是经典漏洞。正确防御:服务端验证文件实际是不是 JPEG(看魔数 + 解码尝试),不光看扩展名。
30 个常见格式速查表
图片
| 格式 | 魔数(hex) | 魔数(ASCII) |
|---|---|---|
| PNG | 89 50 4E 47 0D 0A 1A 0A | ‰PNG\r\n\x1A\n |
| JPEG | FF D8 FF | - |
| GIF87a | 47 49 46 38 37 61 | GIF87a |
| GIF89a | 47 49 46 38 39 61 | GIF89a |
| BMP | 42 4D | BM |
| WebP | 52 49 46 46 ?? ?? ?? ?? 57 45 42 50 | RIFF....WEBP |
| TIFF (LE) | 49 49 2A 00 | II*\0 |
| TIFF (BE) | 4D 4D 00 2A | MM\0* |
| HEIC | ?? ?? ?? ?? 66 74 79 70 68 65 69 63 | ....ftypheic |
| AVIF | ?? ?? ?? ?? 66 74 79 70 61 76 69 66 | ....ftypavif |
| ICO | 00 00 01 00 | - |
| PSD | 38 42 50 53 | 8BPS |
文档 / 容器
| 格式 | 魔数 | ASCII |
|---|---|---|
25 50 44 46 2D | %PDF- | |
| ZIP / DOCX / JAR / APK | 50 4B 03 04 | PK\x03\x04 |
| RAR (v1.5+) | 52 61 72 21 1A 07 00 | Rar!\x1A\x07\x00 |
| RAR5 | 52 61 72 21 1A 07 01 00 | Rar!\x1A\x07\x01\x00 |
| 7Z | 37 7A BC AF 27 1C | 7z¼¯'\x1C |
| TAR (POSIX) | offset 257: 75 73 74 61 72 | ustar |
| GZIP | 1F 8B | - |
| BZIP2 | 42 5A 68 | BZh |
| XZ | FD 37 7A 58 5A 00 | \xFD7zXZ\0 |
| ZSTD | 28 B5 2F FD | - |
音频 / 视频
| 格式 | 魔数 | ASCII |
|---|---|---|
| MP3 (with ID3v2) | 49 44 33 | ID3 |
| MP3 (no ID3) | FF FB / FF F3 / FF F2 | - |
| FLAC | 66 4C 61 43 | fLaC |
| OGG | 4F 67 67 53 | OggS |
| WAV | 52 49 46 46 ?? ?? ?? ?? 57 41 56 45 | RIFF....WAVE |
| MP4 / HEIF | ?? ?? ?? ?? 66 74 79 70 | ....ftyp |
| MKV / WebM | 1A 45 DF A3 | EBML header |
可执行 / 库
| 格式 | 魔数 | ASCII |
|---|---|---|
| Windows PE (EXE/DLL) | 4D 5A | MZ |
| Linux ELF | 7F 45 4C 46 | \x7FELF |
| macOS Mach-O 64 (LE) | CF FA ED FE | - |
| macOS Universal Binary | CA FE BA BE | - |
| Java class | CA FE BA BE | 同 Universal Binary(按字节长度区分) |
| WebAssembly | 00 61 73 6D | \0asm |
数据库 / 数据
| 格式 | 魔数 | ASCII |
|---|---|---|
| SQLite | 53 51 4C 69 74 65 20 66 6F 72 6D 61 74 20 33 00 | SQLite format 3\0 |
| Parquet | 末尾 50 41 52 31 | 末尾 PAR1 |
| HDF5 | 89 48 44 46 0D 0A 1A 0A | - |
字体
| 格式 | 魔数 | ASCII |
|---|---|---|
| TTF | 00 01 00 00 | - |
| OTF | 4F 54 54 4F | OTTO |
| WOFF | 77 4F 46 46 | wOFF |
| WOFF2 | 77 4F 46 32 | wOF2 |
为什么 PNG 魔数这么诡异
89 50 4E 47 0D 0A 1A 0A 看起来像随机字节,实际是精心设计:
89—— 高位 1 的非 ASCII 字节,让 7-bit 文本传输(如老 SMTP)无法保留,触发明确错误而不是悄悄破坏50 4E 47—— “PNG” 的 ASCII,便于人眼识别0D 0A—— Windows 换行 CRLF,FTP 文本模式会把它转换成0A1A—— DOS 文件结束符(Ctrl+Z)—— 在 Windowstype命令下会让输出停止0A—— Unix 换行 LF —— FTP 文本模式会把它转换成0D 0A
整套设计的目标——传输过程中任何一种字节级损坏都会破坏魔数,立即被检测到。这是 1996 年 PNG 设计的远见——比同期 GIF / JPEG 的简单魔数好得多。
怎么用 Hex 工具识别陌生文件
打开 Hex 二进制查看,拖入文件:
- 看第一行第一组 —— 16 个字节,对照上面表格
- 工具自动识别 + 展开结构树 —— 主流格式直接给出 chunk / box / segment 列表
- 滚到末尾验证 —— 看格式必需的尾标记是不是完整
实战例子 1:文件没扩展名
拖入后第一行 89 50 4E 47 0D 0A 1A 0A —— PNG。后面工具展开 IHDR/IDAT/IEND chunk,能看到尺寸、位深度、颜色类型。
实战例子 2:扩展名是 .docx 但打不开
拖入后看到 50 4B 03 04 —— ZIP。尝试解压看结构——如果有 [Content_Types].xml + word/document.xml 是正常 DOCX;只有 META-INF/MANIFEST.MF 是 JAR;都没有就是普通 ZIP 文件被改了扩展名。
实战例子 3:怀疑图片里嵌了别的东西
拖入 JPEG 后跳到末尾。正常应当以 FF D9 结束。如果 FF D9 后面还有几 KB / 几 MB 字节——很可能是 polyglot 隐写。搜 50 4B 03 04(ZIP 魔数)能直接发现嵌入的 ZIP 附件。
实战例子 4:可执行文件来源识别
拖入 .exe 看到 4D 5A —— Windows PE。继续看 0x3C 偏移处的 PE header 偏移,跳过去看 machine type 字段——4C 01 = i386,64 86 = AMD64,AA 64 = ARM64——能立刻知道是哪个架构编译的。
真实世界的安全意义
魔数是文件类型识别的硬基础:
- 杀毒软件扫描时按魔数走对应解析器
- 沙箱判断是不是动态可执行
- CDN的 Content-Type 自动判断
- 邮件附件过滤真正的 EXE 即使改成 .txt 也能拦下
- 取证分析恢复无扩展名的删除文件靠魔数
你也应该这样做 —— 上传文件的服务端别只看扩展名,前几字节比对魔数白名单是基本卫生。
配套工具
- Protobuf 解码 —— 看二进制 protobuf
- PCAP 抓包查看 —— 看抓包文件结构
- 证书解析 —— PEM/DER 证书内容
下次拿到一个陌生二进制文件,第一步永远是看魔数。八字节定一切。