win32下PE文件分析之NT头

win32下PE文件分析之NT头

创新互联自2013年起,是专业互联网技术服务公司,拥有项目成都网站设计、成都做网站网站策划,项目实施与项目整合能力。我们以让每一个梦想脱颖而出为使命,1280元三穗做网站,已为上家服务,为三穗各地企业和个人服务,联系电话:18982081108

接上一篇的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,如图:

win32下PE文件分析之NT头

(三).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头结构再依计算出各个结构的偏移.)

 win32下PE文件分析之NT头


    (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);	
}

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

win32下PE文件分析之NT头(四).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);							
}

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

win32下PE文件分析之NT头

(五).说明:

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


本文题目:win32下PE文件分析之NT头
URL分享:http://scyanting.com/article/ppphjp.html