PE(Portable Executable),即可移植的执行体。
Linux平台:ELF(Executable and Linking Format)文件结构。
一般在Windows平台下,所有的可执行文件诸如:exe、dll、sys、ocx、com等均适用PE文件结构。这些使用PE文件结构也被称为PE文件。
PE 结构是由若干个复杂的结构体组合而成的,不是单单的一个结构体那么简单,它的结构就像文件系统的结构是由多个结构体组成的。
PE 结构包含的结构体有 DOS 头、PE 标识、文件头、可选头、目录结构、节表等。
1.通过导入文件到c32asm等工具,观察MZ头。
2.通过lordpe等工具。
从 数据管理的角度来看,可以把 PE 文件大致分为两部分,DOS 头、PE 头和节表属于 PE 文件的数据管理结构或数据组织结构部分,而节表数据才是 PE 文件真正的数据部分,其中包含着代码、数据、资源等内容。
从PE结构图中可以看出,PE 结构主要分为 4 大部分(DOS头、PE头、节表、节表数据),其中每个部分又进行了细分,存在若干个小的部分。
无论是32位或64位可执行文件,其文件的头部必定是IMAGE_DOS_HEADER.
IMAGE_DOS_HEADER 数据结构定义如下:
而DOS头又分两部分:
MZ文件头和Dos Stub。
MZ文件头:IMAGE_DOS_HEADER 结构体,其大小占64个字节,并且该结构中的最后一个LONG类型e_lfanew成员指向PE文件头的位置为中的PE文件头标志的地址。
这里有两个比较有用的成员信息:
1、e_magic,用于判断PE文件的标识。如果不是MZ即不是十六进制值:0x5A4D。计算机存储顺序是低位在前高位在后,所以存储为:0x4D5A。
2、e_lfanew,这里是指pe的偏移量,用于找到pe头的位置。
如下阴影区域:
DOS stub:dos存根,在IMAGE_DOS_HEADER和IMAGE_NT_HEADERS之间存在一DOS存根,这其实是一段汇编代码:
PE文件是运行在32位或64位操作系统下的。
其功能是当该EXE运行在16位环境下,输出一段文字:“This program cannot be run in DOS mode”,然后并退出该进程。
在pe文件利用的时候,我们可以把payload写入到当前区域,诸如存放我们的shellcode,在读取时,获取dos头字节数,减去MZ头字节数,即为dos存根字节大小。然后拿去操作加载shellcode等。
IMAGE_NT_HEADER数据结构定义:
参数具体含义:
将文件标识为 PE 映像的 4 字节签名。字节为“PE\0\0”。这个字段是PE文件的标志字段,通常设置成00004550h,其ASCII码为PE00,这个字段是PE文件头的开始,前面的DOS_HEADER结构中的字段e_lfanew字段就是指向这里。
这个字段也是包含几个字段结构,它包含了PE文件的一些基本信息,最重要的是其中一个域指出了IMAGE_OPTIONAL_HEADER的大小。
在PE文件头的后面,1234567个框分别对应_IMAGE_FILE_HEADER结构的七个参数位置以及各自的值。我们需要判断运行平台,就可以通过第一个参数位置的值来判断。
上述e_lfanew中,可以在下图中看到,e_lfanew的值为0080,这里可以看到PE头就在0080h。
常见标识如下,比如这里的014c,就是在Intel I386机器上运行。
机器 标识
Intel I386 14ch
MIPS R3000 162h
Alpha AXP 184h
Power PC 1F0h
MIPS R4000 184h
2)NumberOfSection,标识区块的数目,关于区块后面会详细讲。
3)TimeDateStamp
4)PointerToSymbolTable。这个字段用的比较少,略
5)NumberOfSymbol。这个字段也用得很少,略
6)SizeOfOptionalHeader:紧跟着IMAGE_FILE_HEADER后面的数据大小,这也是一个数据结构,它叫做IMAGE_OPTIONAL_HEADER,其大小依赖于是64位还是32位文件。32位文件值通常是00EOh,对于64位值通常为00F0h。
7)Characteristics:文件属性,普通EXE文件这个字段值为010fh,DLL文件这个字段一般是0210h。
这个结构是IMAGE_FILE_HEADER结构的补充。这两个结构合起来才能对整个PE文件头进行描述。左边的16位字符表示相对于文件头的偏移量。
这里总共31个字段。通常用的就是加*字段。
这里可以知道我们确定的PE文件头在0080h处,通过偏移计算,我们可以得到IMAGE_OPTIONAL_HEADER结构的的首个字段在80h+18h=98h的地方。这里直接通过c32asm跳转到对应位置。如图所示:
然后比较重要的就是最后一个成员,即数据目录表,大小为16,每个元素都是一个IMAGE_DATA_DIRECTORY结构体,这里看到是一个数组类型。
它的定义如下:
在winnt.h中的定义:
在这个数据目录结构体中只有两个成员VirtualAddress和Size,这两个成员的含义比较简单,VirtualAddress指定了数据块的相对虚拟地址(RVA)。Size则指定了该数据块的大小,有时并不是该类型数据的总大小,可能只是该类型数据一个数据项的大小。这两个成员(主要是VirtualAddress)成为了定位各种表的关键,所以一定要知道每个数组元素所指向的数据块类型,以下表格就是它的对应关系:
下面是DataDirctory[16]即数据目录表的各个成员
对于安全人员来说,通常需要了解比较重要的导出表和导入表。这里放在下篇学习。