在Windows系统中,16位和32位可执行文件的存储格式存在本质差异,16位程序采用NE(New Executable)格式,32位程序采用PE(Portable Executable)格式,我们可以通过读取文件的头部特征信息来区分两者,不需要加载文件执行。

核心判断原理
所有Windows可执行文件的开头两个字节都是MZ,这是DOS头部的标志,由Mark Zbikowski的名字缩写而来。在DOS头结构中,偏移0x3C的位置存储了PE头或者NE头的偏移地址:如果是32位PE程序,该偏移指向的位置会存在PE 的标志;如果是16位NE程序,该偏移指向的位置会存在NE的标志。
实现步骤
- 打开目标可执行文件,以二进制只读模式读取内容
- 先校验文件开头是否为
MZ标志,不是则不是合法Windows可执行文件 - 读取偏移0x3C处的4字节内容,得到后续头部的偏移地址
- 跳转到该偏移地址,读取前两个字节,判断是
PE还是NE标志 - 根据标志返回对应的程序位数
完整C++实现代码
以下是可直接使用的判断函数,包含错误处理和边界校验:
#include <iostream>
#include <fstream>
#include <string>
#include <cstdint>
// 返回值:0-非Windows可执行文件,1-16位程序,2-32位程序
int checkProgramBits(const std::string& filePath) {
std::ifstream file(filePath, std::ios::binary);
if (!file.is_open()) {
std::cerr << "无法打开文件" << std::endl;
return 0;
}
// 校验MZ头
uint8_t mzFlag[2];
file.read(reinterpret_cast<char*>(mzFlag), 2);
if (file.gcount() != 2 || mzFlag[0] != 'M' || mzFlag[1] != 'Z') {
file.close();
return 0;
}
// 读取PE/NE头偏移
file.seekg(0x3C, std::ios::beg);
uint32_t headerOffset = 0;
file.read(reinterpret_cast<char*>(&headerOffset), 4);
if (file.gcount() != 4) {
file.close();
return 0;
}
// 跳转到对应头部位置
file.seekg(headerOffset, std::ios::beg);
uint8_t headerFlag[2];
file.read(reinterpret_cast<char*>(headerFlag), 2);
file.close();
if (file.gcount() != 2) {
return 0;
}
// 判断标志
if (headerFlag[0] == 'P' && headerFlag[1] == 'E') {
return 2; // 32位PE程序
} else if (headerFlag[0] == 'N' && headerFlag[1] == 'E') {
return 1; // 16位NE程序
} else {
return 0; // 其他格式
}
}
int main() {
std::string testPath = "test.exe";
int result = checkProgramBits(testPath);
if (result == 0) {
std::cout << "该文件不是合法的Windows可执行文件" << std::endl;
} else if (result == 1) {
std::cout << "该文件是16位程序" << std::endl;
} else {
std::cout << "该文件是32位程序" << std::endl;
}
return 0;
}
注意事项
- 该方法仅适用于Windows平台的.exe、.dll等可执行文件,不适用于其他系统的可执行格式
- 部分16位程序可能同时包含DOS stub和NE头,上述逻辑已经覆盖这种场景
- 64位PE程序的头部标志同样是
PE,如果需要区分32位和64位,还需要进一步读取PE可选头中的Magic字段,0x10B代表32位,0x20B代表64位 - 读取文件时需要确保文件存在且有读取权限,避免程序异常退出
扩展说明
如果需要进一步判断32位PE程序的具体子系统类型,可以读取PE头后续的可选头内容,其中偏移0x44的位置存储了子系统信息,0x2代表Windows GUI程序,0x3代表控制台程序。如果还需要判断是否为64位程序,可以在确认PE标志后,读取可选头的Magic字段,根据值区分32位和64位即可。