目录
2. FileHeader(IMAGE_FILE_HEADER)
3.OptionalHeader(IMAGE_OPTIONAL_HEADER32)
一、NT Headers的结构
NT Headers的结构被定义在"winnt.h"中,结构名为"_IMAGE_NT_HEADERS"。它内部有三个成员,具体请看下图:
接下来我们将具体分析这三个成员:
1.Signature(DWORD)
它占据了四个字节,是一个固定的十六进制值为"0x50450000",按照ASCII码编码,值为"PE\0\0",这是一个标签,它的作用是告诉OS loader这是个PE文件。
2. FileHeader(IMAGE_FILE_HEADER)
这个我暂且叫他文件头,里面有一些和这个PE文件有关的信息。
接下来我们来分析一下它的结构:
首先,我们先用PE-Bear来解析一下这个exe文件的File Header:
1.Machine
它占两个字节,镜像文件只能运行于指定处理器或者能够模拟该处理器的系统上。我们来看看微软的专用文档对这一部分的描述:
下面是相关的值与其对应的机器型号:
接下来我们用一个解析软件"PE-Bear"来解析一下eclipse.exe,来看看它这个Machine值是多少
这个Meaning是AMD64(K8),这是一个由AMD公司研发的64位处理器。
2.NumberofSections
它是一个count,它占两个字节,代表在之后的区域表中有几个区域段。
3.TimeDataStamp
存的是这个文件创建的时间。
4.PointerToSymbolTable 和 NumberOfSymbols
这个我目前也知道一个概念,不过它是已经被抛弃了,官方文档里面狠心地说了一句"...is deprecated",所以说这两个的value都是0。
5.SizeOfOptionalHeader
它存有可选择头的大小,它的大小是不同的。对于32位文件来说大小是224,对于64位文件来说大小是240。
6.Characteristics
用来标识说明这个PE文件的一些属性与特征,比如说这个文件是不是可执行的等等。这里每一个value都有一些特定的信息,具体想看的话可以看上面那个传送门。
3.OptionalHeader(IMAGE_OPTIONAL_HEADER32)
前言:可选头有两个版本分别为32位版本(IMAGE_OPTIONAL_HEADER32)和64位版本(IMAGE_OPTIONAL_HEADER64)这两个结构分别在_IMAGE_NT_HEADERS和_IMAGE_NT-HEADER64结构中。可选择头是这个结构中最重要的一部分,当文件被执行时这里的属性会被加载到内存中并且执行。
可选择头有两个版本,这两个版本之间有一些区别:
1.32位的可选择头,它一共有31个成员而64位的可选择头有30个成员。多了一个DWORD BaseOfData,它存的是镜像被加载到内存中的数据节的RVA。
2.有五个成员,在32位的可选择头和64位的可选择头结构中的数据类型是不同的,这个就不一一列举了。
下面是可选择头的具体结构:
//
// Optional header format.
//
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;
typedef struct _IMAGE_OPTIONAL_HEADER64 {
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
ULONGLONG 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;
ULONGLONG SizeOfStackReserve;
ULONGLONG SizeOfStackCommit;
ULONGLONG SizeOfHeapReserve;
ULONGLONG SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64;
八个标准成员:
1.Magic:表示这是一个32-bit或者64-bit的可执行文件。对于Windows的PE Loader,它会忽视文件头中的Machine。
2.MajorLinkerVersion:链接器的主版本号。
3.MinorLinkerVersion:链接器的次版本号。
4.SizeOfCode:代码块的大小,代码是放在.text文件里的。
5.SizeOfInitializedData:初始化的数据的数据块大小。这些初始化的数据放在.data的文件中。
6.SizeOfUninitializedData:未初始化的数据的数据块的大小。这些未初始化的数据放在后缀为.dss的区块下。
7.AdressOfEntryPoint:镜像文件被加载到内存中时,它代表了一个入口点的RVA(Relative Virtual Address)。
8.BaseOfCode:代码基址,表示的是镜像文件被加载到内存中的代码节的RVA。
9.BaseOfData(32-bit):数据基址,表示镜像文件被加载到内存中的数据节的RVA。(偷偷加一个应该没人看见【滑稽】)
下面就是一些sections。说实话,看英文比中文更好理解。但是有一点我今天才弄懂,在英文文章中我们总能看见一个词"image",比如"image file",这个是个镜像文件。那么什么是镜像文件呢?当一个可执行文件run之前,它会由OS loader加载进内存中,分配虚拟内存空间 。这种行为类似于复制该文见到机器给它分配到的空间中去,所以用"image"来修饰这个文件,这就显得很形象了。
拓展内容:
1.ImageBase:镜像文件被加载进入内存时的第一个字节的首选地址,它必须是64k的倍数。它实际上是用来定为的,因为镜像文件存储的内存空间实际上是和实际空间不一样的,需要通过这个基址来给“头”和“节”进行重定位。
2.SectionAlignment:字节对齐,这里面存的值是用来使字节对齐的值(以字节计)。
3.FileAlignment:文件对齐,用来对齐文件的节中的原始数据因子(以字节计)。
注:这两个对齐我没怎么理解。
4.MajorOperatingSystemVersion,
MinorOperatingSystemVersion,
MajorImageVersion, MinorImageVersion,
MajorSubsystemVersion,
MinorSubsystemVersion:我来按照顺序翻译一下。主操作系统版本号,次操作系统版本号,镜像文件主版本号,镜像文件次版本号,子系统主版本号,主系统次版本号。
5.Win32VersionValue:这是一个保留值,必须为0.
6.SizeOfImage:镜像的大小(以字节计),它的大小是为SectionAlignment整数倍。因为可执行文件要被加载到内存中,而机器分配给它空间方式是连续的整块。实际上这样会使文件加载后所占内存空间比实际空间更大。
7.SizeOfHaeders:所有头的总大小(以字节计算),它的大小为FileAlignment的整数倍,向上取整(显然,上面那个也是)。
8.CheckSum:翻译过来是叫验效和。在加载时检测这个文件是否合法。
9.SubSystem:运行镜像所需要的子系统,对应的值可以去microslft的官网里面去看,传送门在上边。
10.DLLcharacteristic:定义一些属性,普通文件都有这个。比如说,0x0040代表着DLL能够被重定位,0x0100代表着这个PE文件被提供了一种内存保护机制——NX(Non-Executable)位是一种硬件支持的内存保护技术,可以防止缓冲区溢出等攻击。
11.SizeOfStackReserve,SizeOfStackCommit: 栈保留大小和初始的栈提交大小。这里的栈是CPU的栈。
12.SizeOfHeapReserve,SizeOfHeapCommit:堆保留大小和初始的堆提交大小。
13.LoaderFlags:保留区域,set为0。
14.NumberOfRvaAndSizes:是数据目录表的数组大小。
15.DataDirectory:数据目录表,一个结构体数组。
看一下最后一个数据目录表,它的结构里面存的是表大小和表的虚拟地址。
这些是本次的学习笔记,等完全学完后再完整地整理一下优化一下这些笔记(先立一个flag)其实还想写一下解析文件地C程序(已经写了一部分了),等有之后全写了(再立一个flag)。