PE文件(二)

来源:互联网 发布:sql中replace的用法 编辑:程序博客网 时间:2024/06/12 19:06

上篇讲到的PE文件DOS头中的两个重要成员,一个是e_magic 用来判段是否为PE文件,另一个 e_lfanew用来找出NT头的位置。

在DOS头与NT头 之间还有一部分区域,这里储存着一些被DOS头使用的数据,包括一些字符串等等,这部分的大小不太确定,所以,NT头的具体位置要由DOS头的最后一个成员来确定 。 我们可以利用这一部分空间实现一些特殊用途。

NT头的定义

typedef struct _IMAGE_NT_HEADERS {    DWORD Signature;    IMAGE_FILE_HEADER FileHeader;    IMAGE_OPTIONAL_HEADER32 OptionalHeader;} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

其中第一个成员我们已经使用过了 可以用来判断是否为PE文件,在系统中定义为

#define IMAGE_NT_SIGNATURE                  0x00004550  // PE00

另外两个成员都是结构体,里面存储的信息非常有用,解析这两个结构体才可以说是解析PE文件的开始。
NT头的第二个成员是文件头
这个文件头储存着关于这个PE文件的信息

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;

Machine: 标识这个文件可以运行在哪一个运行平台,并没有什么卵用,
NumberOfSections: 是区段的个数,也就是PE文件的主体被分为多少个部分
TimeDateStamp :表示文件是何时被创建的
PointerToSymbolTable: 符号表偏移,据说已经没什么用0.0
NumberOfSymbols: 符号个数,也没什么用
SizeOfOptionalHeader:扩展头的大小,32位和64位是不一样的
Characteristics:PE文件属性值,这个属性很重要,具体的值可以查询

如果在上篇文章中的代码里加上

    PIMAGE_FILE_HEADER pFile = &pNt->FileHeader;

就可以遍历文件头的信息了。想要显示什么直接用pFile指出来就行。

NT头的第三个成员扩展头

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;

Magic:表示可选头的类型。

MajorLinkerVersion和MinorLinkerVersion:链接器的版本号。

SizeOfCode:代码段的长度,如果有多个代码段,则是代码段长度的总和。

SizeOfInitializedData:初始化的数据长度。

SizeOfUninitializedData:未初始化的数据长度。

AddressOfEntryPoint:程序入口的RVA,对于exe这个地址可以理解为WinMain的RVA。对于DLL,这个地址可以理解为DllMain的RVA,如果是驱动程序,可以理解为DriverEntry的RVA。当然,实际上入口点并非是WinMain,DllMain和DriverEntry,在这些函数之前还有一系列初始化要完成,当然,这些不是本文的重点。

BaseOfCode:代码段起始地址的RVA。

BaseOfData:数据段起始地址的RVA。

ImageBase:映象(加载到内存中的PE文件)的基地址,这个基地址是建议,对于DLL来说,如果无法加载到这个地址,系统会自动为其选择地址。

SectionAlignment:节对齐,PE中的节被加载到内存时会按照这个域指定的值来对齐,比如这个值是0x1000,那么每个节的起始地址的低12位都为0。

FileAlignment:节在文件中按此值对齐,SectionAlignment必须大于或等于FileAlignment。

MajorOperatingSystemVersion、MinorOperatingSystemVersion:所需操作系统的版本号,随着操作系统版本越来越多,这个好像不是那么重要了。

MajorImageVersion、MinorImageVersion:映象的版本号,这个是开发者自己指定的,由连接器填写。

MajorSubsystemVersion、MinorSubsystemVersion:所需子系统版本号。

Win32VersionValue:保留,必须为0。

SizeOfImage:映象的大小,PE文件加载到内存中空间是连续的,这个值指定占用虚拟空间的大小。

SizeOfHeaders:所有文件头(包括节表)的大小,这个值是以FileAlignment对齐的。

CheckSum:映象文件的校验和。

Subsystem:运行该PE文件所需的子系统

DllCharacteristics:DLL的文件属性

SizeOfStackReserve:运行时为每个线程栈保留内存的大小。

SizeOfStackCommit:运行时每个线程栈初始占用内存大小。

SizeOfHeapReserve:运行时为进程堆保留内存大小。

SizeOfHeapCommit:运行时进程堆初始占用内存大小。

LoaderFlags:保留,必须为0。

NumberOfRvaAndSizes:数据目录的项数,即下面这个数组的项数。

DataDirectory:数据目录(非常重要)

下面是数据目录的定义

typedef struct _IMAGE_DATA_DIRECTORY {    DWORD   VirtualAddress;    DWORD   Size;} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

VirtualAddress:是一个RVA。

Size:是一个大小。

这两个数一个是地址,一个是大小,可以看出这个数据目录项定义的是一个区域。这个区域包括导入表,导出表等等,根据不同的索引取出来的是不同的结构,这个等后面再讲。
我们可以在上篇的代码中加入

PIMAGE_OPTIONAL_HEADER pOptional = &pNt->OptionalHeader;

以此来解析NT头的扩展头,想要显示什么直接用pOptional指出来就行。

0 0
原创粉丝点击