可执行文件的格式是操作系统本身执行机制的反映
PE是Portable Executable,意为可移植的可执行的文件
PE文件中的数据结构有32位和64位之分
编辑
在黑白里温柔地爱彩色,在彩色里朝圣黑白。
——汪曾祺
PE各部分数据
Code View调试信息 | 调试信息 |
COFF符号表 | |
COFF行号 | |
...... | 块(Section) |
.reloc | |
.edata | |
.data | |
.text | |
IMAGE_SECTION_HEADER | 块表 |
IMAGE_SECTION_HEADER | |
IMAGE_SECTION_HEADER | |
IMAGE_SECTION_HEADER | |
数据目录表 |
PE文件头 |
IMAGE_OPTIONAL_HEADER32 | |
IMAGE_FILE_HEADER | |
"PE",0,0 | |
DOS stub | DOS部首 |
DOS 'MS' HEADER |
PE简介
编辑
PE是一个平面地址空间,所有代码和数据都合并在一起,组成一个很大的结构。
文件的内容被分割为不同的区块,又称区段,节
区块中包含代码或数据,这是一个二进制文件,我们可以用十六进制编辑器查看这个二进制文件
编辑
我们可以从地址,地址偏移量和各部分的作用来了解PE
基地址
PE文件文件载入内存后,内存中的版本称为模块(Module)
映射文件的起始地址称为模块句柄(hModule)
通过模块句柄可以访问内存中的其他数据结构,也称为基地址(ImageBase)
一个模块句柄在Windows CE下并不等于安装的起始地址
虚拟地址
PE文件被系统映射到内存中,每个程序都有自己的虚拟空间,这个虚拟空间的内存地址称为虚拟地址。(Vritual Address VA)
相对虚拟地址
相对虚拟地址(Relative Virtual Address RVA)
应为载入地址可能是进程空间的任意位置,访问PE文件中的全局变量就不能使用绝对地址,需要用与载入地址相关的相对虚拟地址
是相对于PE文件载入地址的偏移位置
目标地址-载入地址=相对虚拟地址
虚拟地址=基地址+相对虚拟地址
编辑
文件偏移地址
PE存储在磁盘中时
某个数据的位置相对于文件头的偏移量称为文件偏移地址(File Offset)
文件偏移地址是从PE文件的第一个字节开始计数,起始值为0
MS-DOS头部
每个PE文件都是以一个DOS程序开始的
程序在DOS中执行,DOS就能识别出这是一个有效的执行体
然后运行MZ header和DOS stub
DOS stub实际上是一个EXE,通常是汇编器和编译器自动生成的
MZ header和DOS stub合称DOS文件头
马克·茨柏克沃斯基(Mark Zbikowski,1956年3月21日-)出生于美国密西根州底特律,毕业于耶鲁大学与哈佛大学,是微软公司的元老之一。EXE执行档的档头“MZ”两字(16进制码为4D5A),即来自于马克·茨柏克沃斯基的姓名字首。
编辑
e_magic一个字的大小,16位,被设置为5A4Dh,h是十六进制的意思
这个值有一个#define,名为IMAGE_DOS_SIGNATURE
a=chr(0x5A)+chr(0x4D) print(a) #Output: MZ
在ASCLL表示法里是MZ,是MZ-DOS的创建者Mark Zbikowski
位、字节、字是计算机数据存储的单位。位是最小的存储单位,每一个位存储一个1位的二进制码,一个字节由8位组成。而字通常为16、32或64个位组成。
e_lfanew字段是真正的PE文件头的相对偏移RAV,位于文件的3Ch字节处,这个值是真正的PE文件头偏移量
PE文件头
DOS-stub之后是PE文件头,"PE Header"是PE相关结构的NT映像头
执行体在支持PE文件结构的操作系统中执行,PE装载器将从DOS头中的e_lfanew字段中找到PE Header的起始偏移量,再加上基址,得到PE文件头的指针
PNTHeader=ImageBase+dosHeader->e_lfanew
两个版本的IMAGE_DOS_SIGNATURE结构
- PE32(32位版本)
- PE32+(64位版本)
Signature
一个有效的PE文件里,Signtrue字段被设置为0x00004550,ASCLL码是“PE\0\0”
“PE\0\0”是PE文件头的开始,MS-DOS头部的e_lfanew指向的就是“PE\0\0”
IMAGE_FILE_HEADER结构
包含PE的一些基本信息,有一个域指明了IMAGE_OPTIONAL_HEADER
这个结构也可以在COFF格式的OBJ文件的开始处找到,称其为“COFF File Header”
machine字段:PE文件可以在多种机器上使用,不同平台上指令的机器码不同
机器类型标志
机器 | 标志 |
MIPS R3000 | 162h |
MIPS R4000 | 166h |
Intel i386 | 14Ch |
Alpha AXP | 184h |
Power PC | 1F0h |
TimeOfsection字段:区块数目
TimeDataStamp字段:表示文件的创建时间,自1970年1月1日用格林威治时间GMT计算的秒数
计算机元年
1970-01-01对于开发者来说都是不陌生的,有些系统对于时间的处理如果不够好的话,就可能把时间显示成1970-01-01,所以经常有用户看到1970-01-01这个时间。
1969年8月,贝尔实验室的程序员肯汤普逊利用妻儿离开一个月的机会,开始着手创造一个全新的革命性的操作系统,他使用B编译语言在老旧的PDP-7机器上开发出了Unix的一个版本。
随后,汤普逊和同事丹尼斯里奇改进了B语言,开发出了C语言,重写了Unix,新版于1971年发布。
在Unix被发明出来之后,需要在Unix上表示时间,就需要想办法定义一个能表示一份数据在某个特定时间之前已经存在的、完整的、可验证的数据来表示时间。
于是,Unix时间戳被定义出来,即通过当前时间和一个"纪元时间"进行对比,其间相差的秒数作为时间戳。
为了让Unix时间戳表示时间这种方式用的尽可能久,最初就把Unix诞生的时间1971-1-1定义成"纪元时间"。
后来为了方便记忆和使用,于是就把纪元时间从1971-01-01调整到1970-01-01了。
编辑
PointerToSymbolTable字段:COFF符号表的文件偏移位置
NumberOfSymbols字段:如果有COFF表,代表其中符号的数目,可以使用这个字段找到COFF的结束处
Characteristic字段:文件属性,有选择地通过几个值的运算得到,定义于winnt.h中的IMAGE_FILE_xxx值
IMAGE_OPTIONAL_HEADER结构
IMAGE_FILE_HEADER不足以定义PE文件属性,因此可选映像头中定义了更多的数据,完全不必考虑两个结构的区别在哪里
区块
在PE文件头于原始数据之间存在一个区块表,区块表中包含每个块在映像中的信息,分别指向不同的区块实体。
PE.exe中共有3个块的描述,分别是.text,.rdata,.data,每个块对应一个IMAGE_SECTION_HEADER结构
未完待续
编辑
Seeyounext illusion.