return 0;
}
运行此段代码,则读者可以看到如下图所示的输出结果,程序会首先判断读入文件的pDosHead->e_magic
是否为IMAGE_DOS_SIGNATURE
用以验证是否为DOS头,接着通过IMAGE_DOS_HEADER
的e_lfanew
值得到NT头
部位置,并以此进一步判断是否为PE文件;
接下来则是读入PE文件中DOS头的重点部分,读者通过DosHeader
指针,即可依次遍历出IMAGE_DOS_HEADER
结构中的所有参数信息,这段代码可以总结为如下案例;
int main(int argc, char * argv[])
{
BOOL PE = IsPeFile(OpenPeFile("c://pe/x86.exe"), 0);
if (PE == TRUE)
{
printf("\t\t\t 十六进制 \t 十进制 \n");
printf("DOS标志: %08X \t %08d \n", DosHeader->e_magic, DosHeader->e_magic);
printf("文件最后一页的字节数: %08X \t %08d \n", DosHeader->e_cblp, DosHeader->e_cblp);
printf("文件中的页面: %08X \t %08d \n", DosHeader->e_cp, DosHeader->e_cp);
printf("重定位: %08X \t %08d \n", DosHeader->e_crlc, DosHeader->e_crlc);
printf("段落中标题的大小: %08X \t %08d \n", DosHeader->e_cparhdr, DosHeader->e_cparhdr);
printf("至少需要额外段落: %08X \t %08d \n", DosHeader->e_minalloc, DosHeader->e_minalloc);
printf("所需的最大额外段落数: %08X \t %08d \n", DosHeader->e_maxalloc, DosHeader->e_maxalloc);
printf("初始(相对)SS值: %08X \t %08d \n", DosHeader->e_ss, DosHeader->e_ss);
printf("初始SP值: %08X \t %08d \n", DosHeader->e_sp, DosHeader->e_sp);
printf("校验和: %08X \t %08d \n", DosHeader->e_csum, DosHeader->e_csum);
printf("初始IP值: %08X \t %08d \n", DosHeader->e_ip, DosHeader->e_ip);
printf("初始(相对)CS值: %08X \t %08d \n", DosHeader->e_cs, DosHeader->e_cs);
printf("重新定位表的文件地址: %08X \t %08d \n", DosHeader->e_lfarlc, DosHeader->e_lfarlc);
printf("叠加编号: %08X \t %08d \n", DosHeader->e_ovno, DosHeader->e_ovno);
printf("保留字: %08X \t %08d \n", DosHeader->e_res, DosHeader->e_res);
printf("OEM标识符 %08X \t %08d \n", DosHeader->e_oemid, DosHeader->e_oemid);
printf("OEM信息 %08X \t %08d \n", DosHeader->e_res2, DosHeader->e_res2);
printf("PE指针: %08X \t %08d \n", DosHeader->e_lfanew, DosHeader->e_lfanew);
}
else
{
printf("非标准程序 \n");
}
system("pause");
return 0;
}
编译并运行上述代码片段,则读者可看到如下图所示的输出效果,此时DOS头部数据将被全部完整的输出;
2.3 PE文件头详细解析
从DOS文件头IMAGE_DOS_HEADER
的e_lfanew
字段向下偏移003CH
的位置,就是真正的PE文件头的位置,该文件头是由IMAGE_NT_HEADERS
结构定义的,IMAGE_NT_HEADERS是PE文件格式的一部分,它包含了PE头和可选头的信息,用于描述PE文件的结构和属性。
typedef struct _IMAGE_NT_HEADERS
{
DWORD Signature; // PE文件标识字符
IMAGE_FILE_HEADER FileHeader; // 文件头
IMAGE_OPTIONAL_HEADER32 OptionalHeader; // 可选头
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
IMAGE_NT_HEADERS由IMAGE_NT_SIGNATURE
(标识符)和IMAGE_FILE_HEADER
(文件头)组成。其中,IMAGE_NT_SIGNATURE
用于标识该文件是否为有效的PE文件,IMAGE_FILE_HEADER
则用于描述可执行文件的基本结构信息,包括机器类型、段的数量、时间戳、符号表指针、符号表数量、可选头大小以及文件的各种标志和属性等。
如上_IMAGE_NT_HEADERS
文件头的第一个DWORD
是一个标志,默认情况下它被定义为00004550h
也就是P,E
两个字符另外加上两个零,而大部分的文件属性由标志后面的IMAGE_FILE_HEADER
和IMAGE_OPTIONAL_HEADER32
结构来定义。
我们跟进IMAGE_FILE_HEADER
这个结构,文件头结构体IMAGE_FILE_HEADER
是IMAGE_NT_HEADERS
结构体中的一个结构体,紧接在PE标识符的后面,IMAGE_FILE_HEADER
结构体的大小为20字节,起始位置为0x000000CC
结束位置在0x000000DF
,这个IMAEG_FILE_HEADER
结构体中包含了PE文件的大部分基础信息其结构的定义如下:
#define _IMAGE_FILE_HEADER 20
typedef struct _IMAGE_FILE_HEADER
{
WORD Machine; // 运行平台