windows下通过解析pe头魔数(mz+pe)判断可执行文件,linux/macos通过elf或mach-o魔数识别,需校验文件长度、处理io错误,不可依赖扩展名或权限位。

Windows 下用 PeHeader 快速判断是否为 PE 可执行文件
直接读取文件前 64 字节,检查 DOS 头和 PE 签名即可排除绝大多数非可执行文件。这不是 100% 安全的检测(比如加壳后签名可能被改),但对常规场景足够快且可靠。
关键点:不调用 LoadLibrary 或 CreateProcess,纯字节解析;必须验证 e_magic == 0x5A4D(即 "MZ")且 Signature == 0x00004550(即 "PE\0\0")。
- 跳过前 60 字节定位
e_lfanew(DOS 头偏移,4 字节小端) - 从该偏移处读 4 字节,确认是否等于
0x00004550 - 注意:文件长度必须 ≥
e_lfanew + 4,否则是损坏或伪造头 - 示例片段:
FILE* f = fopen("test.exe", "rb"); if (!f) return false; fseek(f, 0x3C, SEEK_SET); uint32_t peHeaderOffset; fread(&peHeaderOffset, 1, 4, f); fseek(f, peHeaderOffset, SEEK_SET); uint32_t signature; fread(&signature, 1, 4, f); fclose(f); return signature == 0x00004550;
Linux/macOS 下检查 ELF 文件魔数(\x7fELF)
ELF 可执行文件开头固定 4 字节为 \x7fELF(十六进制是 7f 45 4c 46)。比 Windows 更简单——只要前 4 字节匹配,基本可认定为 ELF 格式(包括可执行、共享库、目标文件等)。
但要注意:不是所有 ELF 都能直接运行(比如缺少入口点或权限),这里只做格式识别,不保证可执行性。
- 打开文件只读,
fread前 4 字节到uint8_t buf[4] - 逐字节比对:
buf[0] == 0x7f && buf[1] == 'E' && buf[2] == 'L' && buf[3] == 'F' - 避免用
strcmp直接比字符串,因为首字节是不可见字符 - ARM64/MIPS/PPC 等架构的 ELF 魔数完全一致,无需区分平台
跨平台统一处理时,为什么不能只靠扩展名或 stat() 权限?
扩展名如 .exe 或 .out 完全不可信——用户可以随便改;stat() 返回的 st_mode & S_IXUSR 仅表示“有执行权限”,不代表文件内容合法。一个空文本文件 chmod +x 后也会被误判。
真正有效的是“格式特征码”:它由操作系统加载器强制要求,无法绕过(除非伪造头,但那已属恶意行为范畴)。
- Windows 的
MZ+PE是内核映射器硬编码识别逻辑 - Linux 的
\x7fELF是load_elf_binary()内核函数第一道校验 - macOS 同样依赖 Mach-O 魔数
\xce\xfa\xed\xfe(32 位)或\xcf\xfa\xed\xfe(64 位) - 若需支持 macOS,读前 4 字节后追加一次
memcmp(buf, "\xce\xfa\xed\xfe", 4)或对应 64 位魔数
实际项目中容易忽略的边界情况
特征码检测看似简单,但真实文件系统里常遇到这些坑:
- 文件被截断(如网络传输中断)→ 读取长度不足时必须提前返回 false,不能假设
fread总是成功 - 符号链接未解引用 → 用
stat()而非lstat(),否则可能检测到链接文件本身而非目标 - NTFS 重解析点或 WSL 中的跨文件系统路径 → 在 Windows 上建议用
CreateFile+FILE_FLAG_NO_BUFFERING替代fopen,避免缓存干扰 - 某些打包工具(如 PyInstaller)生成的 exe 会把 Python 字节码塞在资源段,但 PE 头依然完整 → 不影响检测,这是正常现象
最复杂的地方不在逻辑,而在 IO 错误处理的完备性:少一次 ferror() 检查,就可能把磁盘错误当成“不是可执行文件”。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











