目录
PE(Portable Executable File Format)可移植的执行体文件格式.
可执行文件
- 可执行文件(executable file)指的是可以由操作系统进行加载执行的文件.在操作系统存储空间中浮动定位的二进制可执行程序.它可以加载到内存中,由操作系统加载并执行.特定的CPU指令集(如X86指令集)对应的不同平台之间的可执行程序不可直接移植运行.
- 可执行文件格式:
- Windows PE(Portable Executable)文件结构(.EXE,.DLL,.SYS)
- Linux ELF(Executable and Linking Format)文件结构(.OUT,.o,.so)
识别PE文件
工具:16进制文本编辑器(WinHex / UltraEdit等).将文件拖入软件即可查看文件在硬盘中的数据.(涉及到IMAGE_DOS_HEADER以及IMAGE_NT_HEADERS知识点.后续章节详解).
如果是可执行文件前两个字节是0x5a4d(MZ标记 DOS系统中一个开发人员名字). 并且以文件头为基址 + (文件头 + 0x3C中存储的值)定位到的四个字节数据为0x00004550这就是一个可执行文件(PE文件).
//C语言中定义:
#define IMAGE_DOS_SIGNATURE 0x5A4D // MZ
#define IMAGE_NT_SIGNATURE 0x00004550 // PE00
1).EXE文件拖入效果
2).DLL文件拖入效果
3).SYS文件拖入效果
4).PDF文件拖入效果
仅仅通过MZ,PE标记判断是否为可执行文件是不准确的.文件被拉伸到内存后(即进程)此类信息均可抹除也不会影响程序正常运行.
PE文件结构
- PE文件头记录了PE文件中的所有数据的组织方式,它类似于一本书的目录,通过目录我们可以快速定位到某个具体的章节;通过PE文件头部分对某些数据结构的描述,我们也可以定位到那些不在文件头部的信息,比如导入表数据,导出表数据,资源表数据等.
- PE中有一个数据结构称为数据目录,其中记录了所有可能的数据类型.这些类型中,目前已定义的有15种,包括导出表、导入表、资源表、异常表、属性证书表、重定位表、调试数据、Architecture、Global Ptr、线程局部存储、加载配置表、绑定导入表、IAT、延迟导入表和CLR运行时头部.
- 节就是存放不同类型数据(比如代码、数据、常量、资源等)的地方,不同的节具有不同的访问权限.比如,代码段中的字节码在程序运行的时候,一般不允许用户进行修改,数据段则允许程序在运行过程中读写,常量只能读等.Windows操作系统在加载可执行程序时,会为这些具有不同属性的数据分别分配标记有不同属性的页面,以确保程序运行时的安全.节是PE文件种存放代码或数据的基本单元.
PE文件的两种状态
测试工具: WinHex(16进制编辑器), DbgView(被查看文件).
文件中测试: 将文件拖入软件即可
内存中测试: 运行目标进程,WinHex选择菜单栏Tools选择OpenRam找到目标进程即可
1).查看文件在内存和文件中的数据
文件中的数据:
内存中的数据:
通过上述比较可以看出内存中与文件中展示部分的数据相同,但是起始地址不同,文件中Base为0,内存中Base为0x00400000,文件运行后被加载的虚拟内存地址由(IMAGE_OPTIONAL_HEADER -> ImageBase)决定.
2).继续向下查找观察不同
文件中的数据:
内存中的数据:
向下查找一段空间后会发现一大堆为0数据,并且为0这段数据在内存中要比在文件中更大(也会存在相同情况涉及到文件与内存对齐概念).继续查找直到出现有效数据.
文件中在0x00000400指向地址中继续出现数据.
内存中在0x00401000指向地址中继续出现数据.
观察可以发现文件中0x00000400地址指向的数据与内存中0x00401000地址指向的数据相同.这种情况是由于文件对齐(IMAGE_OPTIONAL_HEADER->FileAlignment)与内存对齐(IMAGE_OPTIONAL_HEADER->SectionAlignment)不同所导致的.说明一个有效PE文件在内存和文件中将是两种不同状态.
内存对齐一般为1000h,由于Windows操作系统对内存属性的设置以页为单位,因此节在内存中的对齐单位必须至少是一个页的大小.
文件对齐一般为200h / 1000h,为了提高磁盘利用率,通常情况下,定义的节在文件中的对齐单位要远小于内存对齐的单位;通常会以一个物理扇区的大小作为对齐粒度的值,即512字节,十六进制表示为200h.
通过了解这种对齐后还得一些概念:
- 基地址(ImageBase)即文件按照PE格式拉伸到内存后的起始地址.
- 虚拟内存地址(Virtual Address, VA) = 相对虚拟地址(RVA) + 基地址(ImageBase).用户的PE文件被操作系统加载进内存后,PE对应的进程支配了自己独立的4GB虚拟空间.在这个空间中定位的地址称为虚拟内存地址(VA),所以虚拟内存地址的范围在x86下为00000000h ~ FFFFFFFFh.
- 相对虚拟地址(Relative Virtual Address, RVA) = 虚拟地址(VA) - 基地址(ImageBase).相对于基地址的偏移,即RVA是虚拟内存中用来定位某个特定位置的地址,该地址的值是这个特定位置距离某个模块基地址的偏移量.
- 文件偏移地址(File Offset Address, FOA)和内存无关,它是指某个位置距离文件头的偏移.
PE结构中出现的大部分地址都为偏移值,即文件或内存的起始地址加上FOA / RVA 才是指向的真正地址.