PE文件(二)

上篇讲到的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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值