千家信息网

win32下PE文件分析之NT头

发表于:2026-11-01 作者:千家信息网编辑
千家信息网最后更新 2026年11月01日,接上一篇的win32下PE文件分析之DOS头(一)win32中PE的NT:NT头是PE文件中标准PE头和可选PE头的总体称谓,还包含一个PE标识.下面是它在Visual C++ 6.0中WINNT.h
千家信息网最后更新 2026年11月01日win32下PE文件分析之NT头

接上一篇的win32下PE文件分析之DOS头

(一)win32中PE的NT:

NT头是PE文件中标准PE头和可选PE头的总体称谓,还包含一个PE标识.下面是它在Visual C++ 6.0中WINNT.h中的定义:

typedef struct _IMAGE_NT_HEADERS64 {    DWORD Signature;    IMAGE_FILE_HEADER FileHeader;    IMAGE_OPTIONAL_HEADER64 OptionalHeader;} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64;typedef struct _IMAGE_NT_HEADERS {    DWORD Signature;                        //PE标识    IMAGE_FILE_HEADER FileHeader;           //标准PE头(也称文件头)    IMAGE_OPTIONAL_HEADER32 OptionalHeader; //可选PE头} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

第一个是64bit的NT头定义,第二个是32bit的.这里只探讨32bit的.标准PE头也叫文件头,这不重要,知道是那么个东西就行了,个人不太喜欢动不动就用高端名词,高端名词主要是为了严谨而取出来的,但是很多时候很晦涩,通俗易懂更易让人接受.

(二).NT头中的Signature:

这就是一个PE标识,说明这是PE的开始位置.它在PE文件中的偏移由DOS头中的最后一个成员e_lfanew决定,上一节解析了它的值为:0xE0,如图:

(三).NT头中的标准PE头:

(1).NT头中的标准PE头数据宽度是0x14个字节,在Visual C++ 6.0中的结构定义如下:

typedef struct _IMAGE_FILE_HEADER {    WORD    Machine;    WORD    NumberOfSections;    DWORD   TimeDateStamp;    DWORD   PointerToSymbolTable;    DWORD   NumberOfSymbols;    WORD    SizeOfOptionalHeader;    WORD    Characteristics;} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

(2).代码的文件结构如下图:每个解析头函数定义分别放在不同的头文件中,方便逐个头结构的观察,为了可以多熟悉即便各个头的数据结构,每个函数中都重新从头开始解析了一遍,这样效率会降低.后面如果有空,会提供优化了的代码.(优化思路:在解析一开始,就将各个头的地址放进一个unsigned long*的全局数组里面,这样在后面是用的时候就直接调用数组里的地址,而不用每次都重新定义DOS头结构再依计算出各个结构的偏移.)


(3).解析文件头的代码如下(代码中都只是输出部分重要的数据,博客中代码列数的限制,注释一行放不下,不美观):

file.h

void Output_File(void* buffer){        void* buf = buffer;        //计算偏移时有用        IMAGE_DOS_HEADER* pdos = (IMAGE_DOS_HEADER*)buf;                 //pnt存放NT头的地址.        IMAGE_NT_HEADERS32* pnt = (IMAGE_NT_HEADERS32*)((unsigned char*)buf + pdos->e_lfanew);         //pfile存放NT头中标准PE头结构所在地址.        IMAGE_FILE_HEADER* pfile = (IMAGE_FILE_HEADER*)&pnt->FileHeader;          printf("\nNT Header:\n");        //PE标识,值与PE的ascii码一一对应        printf("PE:     %#X\n", pnt->Signature);                printf("File Header:\n");        //输出程序能在哪种CPU平台上运行.        printf("Machine:        %#X\n", pfile->Machine);        //输出PE文件中节的数量        printf("NumberOfSec:    %#X\n", pfile->NumberOfSections);        //时间戳,文件的创建时间,一般有编译器填充,修改后不会影响程序运行        printf("TimeStamp:      %#X\n", pfile->TimeDateStamp);          //可选PE头的大小(32bit默认是0xE0,64bit默认是0xF0)           printf("SizeOfOpHdr:    %#X\n", pfile->SizeOfOptionalHeader);        //该文件的属性(标识给文件的类型,如是exe还是dll或其他)                printf("Characteristics:        %#X\n", pfile->Characteristics);        }

注释掉其他头的解析:运行结果如下:

(四).NT头中的可选PE头:

可选PE头结构挺复杂,也最重要,32bit的和64bit的有点不同.在Visual C++ 6.0中winnt.h中定义如下:

typedef struct _IMAGE_OPTIONAL_HEADER {    //    // Standard fields.    //    WORD    Magic;    BYTE    MajorLinkerVersion;    BYTE    MinorLinkerVersion;    DWORD   SizeOfCode;    DWORD   SizeOfInitializedData;    DWORD   SizeOfUninitializedData;    DWORD   AddressOfEntryPoint;    DWORD   BaseOfCode;    DWORD   BaseOfData;    //    // NT additional fields.    //    DWORD   ImageBase;    DWORD   SectionAlignment;    DWORD   FileAlignment;    WORD    MajorOperatingSystemVersion;    WORD    MinorOperatingSystemVersion;    WORD    MajorImageVersion;    WORD    MinorImageVersion;    WORD    MajorSubsystemVersion;    WORD    MinorSubsystemVersion;    DWORD   Win32VersionValue;    DWORD   SizeOfImage;    DWORD   SizeOfHeaders;    DWORD   CheckSum;    WORD    Subsystem;    WORD    DllCharacteristics;    DWORD   SizeOfStackReserve;    DWORD   SizeOfStackCommit;    DWORD   SizeOfHeapReserve;    DWORD   SizeOfHeapCommit;    DWORD   LoaderFlags;    DWORD   NumberOfRvaAndSizes;    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

解析文件头的代码如下:

void Output_Optional(void* buffer){        void* buf = buffer;        //计算偏移时使用        IMAGE_DOS_HEADER* pdos = (IMAGE_DOS_HEADER*)buf;         //pop中存放可选PE头的起始位置, 0x4是NT头中PE表示,0x14是NT头中的标准PE头,根据结构计算出可选PE头的偏移        IMAGE_OPTIONAL_HEADER32* pop = (IMAGE_OPTIONAL_HEADER32*)((unsigned char*)buf + pdos->e_lfanew + 0x4 + 0x14);        printf("Optional PE Header:\n");        //说明文件的类型,010B为32bit的PE文件,020B为64bit的PE文件        printf("Magic:                          %#X\n", pop->Magic);        //所有代码节的和,大小必须是FileAlignment的整数倍,有编译器填充,修改无影响.        printf("SizeOfCode:                     %#X\n",pop->SizeOfCode);             //已经初始化数据大小的和.        printf("SizeOfinitializedData:          %#X\n",pop->SizeOfInitializedData);        //未初始化数据大小的和.        printf("SizeOfuninitializedData:        %#X\n",pop->SizeOfUninitializedData);                //程序入口OEP        printf("AddressOfEntryPoint:            %#X\n",pop->AddressOfEntryPoint);        //内存镜像基址        printf("ImageBase:                      %#X\n", pop->ImageBase);                        //内存对齐                          printf("SectionAlignment:               %#X\n", pop->SectionAlignment);         //文件对齐        printf("FileAlignment:                  %#X\n", pop->FileAlignment);        //内存中整个PE文件的映射大小,可比实际的大,必须为内存对齐的整数倍                             printf("SizeOfImage:                    %#X\n", pop->SizeOfImage);        //所有头+节表文件对齐后的大小,严格按照文件对齐.        printf("SizeOfHeaders:                  %#X\n", pop->SizeOfHeaders);        //校验和,用来判断文件是否被篡改.比如系统的一些dll加载时会用到.                             printf("CheckSum:                       %#X\n", pop->CheckSum);                                                 }

注释掉其他解析部分,运行结果如下图:


(五).说明:

解析过程中最为麻烦的是计算偏移,多算几次,多看下结构图就好了.都是那么过来的.




文件 结构 标准 代码 大小 偏移 数据 标识 内存 地址 运行 重要 注释 程序 C++ 输出 不同 位置 函数 名词 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 小米移动互联网科技园 山东特亿宝互联网科技有限公司 江苏根荣互联网科技 科技互联网企业 互联网科技资迅 互联网公司 科技公司 唐信互联网科技 狮子座互联网科技有限公司 互联网金融 科技创新 顺网科技2021年互联网排名 海南购全球互联网科技 深圳市运趣互联网科技有限公司 成都玖崟互联网科技有限公司乐商圈怎么样 互联网校园科技培训 互联网科技和法律行业 南通创西澳互联网科技有限公司 云南省自由互联网科技有限公司 风投北京互联网科技 南京一网一红互联网科技有限公司 安徽九吞吴互联网科技有限公司 互联网科技海报 蓝思科技飞鸽互联网 互联网金融 科技金融 电子科技大学互联网 阿大互联网科技有限公司 滨湖区互联网智能科技产品有哪些 互联网对科技影响 10位科技互联网大佬 华人运通互联网科技公司 株洲百竞科技的互联网营销顾问
0