pe文件开头固定为“mz”(0x4d5a),再通过dos头e_lfanew定位pe头,验证其signature是否为“pe\0\0”(0x00004550),两者均满足才判定为有效pe文件。

Windows下用前4字节判断是否为PE文件
PE文件开头固定是 MZ 字符(即十六进制 4D 5A),这是DOS头签名。只要文件足够大(至少64字节),且前两个字节是 0x4D 5A,就基本可判定为PE格式(包括EXE、DLL、SYS等)。
注意:不能只读2字节就下结论——有些资源文件或打包器生成的“伪PE”也保留了 MZ 头,但后续结构无效。稳妥做法是继续校验PE签名位置(偏移 0x3C 处的DWORD指向的地址是否为 "PE\0\0")。
实操建议:
- 用
std::ifstream以std::ios::binary模式打开文件,读取前64字节 - 检查
buf[0] == 0x4D && buf[1] == 0x5A - 读取
buf[0x3C]开始的4字节得到PE头偏移,再检查该偏移处是否为0x00004550(小端序的"PE\0\0") - 若任一条件失败,不认为是有效PE文件
Linux下用前4字节+第5字节判断是否为ELF文件
ELF文件开头固定为魔数 \x7fELF(即 0x7F 45 4C 46)。仅靠前4字节还不够——需结合第5字节(e_ident[EI_CLASS])确认是32位还是64位,否则可能误判某些嵌入式固件或混淆数据。
常见错误:把所有含 0x7F 45 4C 46 的文件都当ELF,但有些调试符号段、core dump片段、甚至恶意填充数据也会包含该序列。
实操建议:
- 读取前5字节,确认
buf[0]==0x7F && buf[1]=='E' && buf[2]=='L' && buf[3]=='F' - 检查
buf[4]是否为1(ELF32)或2(ELF64);其他值(如0)表示无效类 - 可选:进一步读取
buf[5](EI_DATA)验证字节序(1=小端,2=大端),增强鲁棒性
跨平台统一判断时别忽略文件权限和扩展名干扰
仅靠文件头无法100%断定“可执行性”——Linux下即使ELF头正确,若无 x 权限(如 chmod -x 后),execve() 仍会失败;Windows下则完全不看扩展名或权限位,只认PE结构+入口点有效性。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
所以“是否为可执行文件”实际分两层:格式合法(PE/ELF) + 系统允许执行。很多工具(如 file 命令)会同时检查这两者。
实操建议:
- 格式检测归格式检测,权限检测归权限检测,不要混在一起返回布尔值
- Windows上无需检查文件权限(ACL由系统运行时控制),但应尝试解析
OptionalHeader.AddressOfEntryPoint是否非零 - Linux上用
access(path, X_OK)辅助判断,但注意:普通用户对目录有X_OK表示可进入,对文件有X_OK才表示可执行 - 扩展名(如
.exe、.out)纯属误导,必须忽略
用 libmagic 替代手写解析更可靠
手动解析PE/ELF头容易漏掉边界情况:比如PE的NT头长度可变、ELF的section header数量可能为0、某些加壳文件会篡改魔数但保留可执行性。生产环境建议用成熟库。
libmagic(file 命令背后引擎)能识别上千种格式,且自带缓存和多级匹配逻辑,比自己写几行头校验鲁棒得多。
实操建议:
- 链接
-lmagic,调用magic_open(MAGIC_MIME_TYPE | MAGIC_SYMLINK) - 用
magic_load(magic, nullptr)加载默认数据库 -
magic_file(magic, path.c_str())返回类似"application/x-executable"或"application/x-pie-executable" - 注意:需处理
nullptr返回(文件不可读/过大/被拒绝)
真正难的不是读前几个字节,而是定义“可执行”的语义——是结构合法?有入口点?权限到位?被系统策略拦截?这些边界在不同场景下权重完全不同。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










