PE速查

来源:互联网 发布:成功广告案例知乎 编辑:程序博客网 时间:2024/06/02 23:25

RVA的含义:相对虚拟地址 = 该数据的虚拟地址 - 映象的装载基址的虚拟地址.

IMAGE_DOS_HEADER ;DOS头:
00000000: DB e_magic   "MZ"
0000003C: DD e_lfanew ;PE头基址,最后一个字段

IMAGE_NT_HEADERS ;PE头:
00000000: DB Signature "PE",0,0

IMAGE_FILE_HEADER ;文件头
00000004: DW Machine    ;运行平台(1)
00000006: DW NumberOfSection   ;文件节数目
00000008: DD TimeDateStamp   ;创建文件的时间   (usually usless)
0000000c: DD PointerToSymbolTable ;指向符号表   (usless)
00000010: DD NumberOfSymbols   ;符号表中符号的数量 (usless)
00000014: DW SizeOfOptionalHeader ;可选头大小,一般为E0h
00000016: DW Characteristics   ;文件属性(2)

IMAGE_OPTIONAL_HEADER32 ;可选头(其实是必须的)
00000018: DW Magic    ;107h=ROM Image,10bh=exe Image
0000001a: DB MajorLinkerVersion ;连接器主版本号    (usless)
0000001b: DB MinorLinkerVersion ;连接器次版本号    (usless)
0000001c: DD SizeOfCode   ;所有含代码的节的总大小   (usless)
00000020: DD SizeOfInitializedData ;所有含已初始化节的总大小 (usless)
00000024: DD SizeOfUninitializedData ;所有含未初始化节的总大小 (usless)
00000028: DD AddressOfEntryPoint ;程序执行入口RVA
0000002c: DD BaseOfCode   ;代码节起始RVA    (usless)
00000030: DD BaseOfData   ;数据节起始RVA    (usless)
00000034: DD ImageBase   ;程序建议装载地址(虚拟地址)
00000038: DD SectionAlignment ;内存中节对齐粒度,一般是4KB,64位系统是8KB
0000003c: DD FileAlignment   ;文件中节对齐粒度,一般是200h
00000040: DW MajorOperatingSystemVersion ;操作系统主版本号 (usless)
00000042: DW MinorOperatingSystemVersion ;操作系统次版本号 (usless)
00000044: DW MajorImageVersion ;可运行的操作系统的最小版本号,主版本号 (usless)
00000046: DW MinorImageVersion ;可运行的操作系统的最小版本号,次版本号 (usless)
00000048: DW MajorSubsystemVersion ;可运行的操作系统的最小子系统主版本号 (usless)
0000004a: DW MinorSubsystemVersion ;可运行的操作系统的最小子系统次版本号 (usless)
0000004c: DD Win32VersionValue ;未用,Win32VersionValue   (usless)
00000050: DD SizeOfImage   ;内存中整个PE映象的大小
00000054: DD SizeOfHeaders   ;所有头+节表的大小
00000058: DD CheckSum   ;checksum,在可执行文件中为0,系统DLL和驱动中为校验和,就是将文件的所有内容按单字大小带进位累加,最后去掉进位加上文件大小.就是校验和
0000005c: DW Subsystem   ;文件的子系统(3)
0000005e: DW DllCharacteristics
00000060: DD SizeOfStackReserve ;初始化时堆栈大小
00000064: DD SizeOfStackCommit ;初始化时实际提交的堆栈大小
00000068: DD SizeOfHeapReserve ;初始化时堆大小
0000006c: DD SizeOfHeapCommit ;初始化时实际提交的堆大小
00000070: DD LoaderFlags
00000074: DD NumberOfRvaAndSizes ;数据目录的大小
00000078: QD DataDirectory   ;数据目录,一般是16个目录项,每个目录项8字节(4)
000000f0: QD     ;最后一个目录项

IMAGE_DATA_DIRECTORY ;数据目录结构
00000000: DD VirtualAddress   ;数据起始RVA
00000004: DD isize    ;数据块长度

IMAGE_SECTION_HEADER ;节表
00000000: DB 8 DUP (?) Name1    ;节名称
00000008: DD   VirtualSize   ;节未对齐时的大小
0000000c: DD   VirtualAddress   ;节在内存中的RVA
00000010: DD   SizeOfRawData   ;节在文件中对齐后的大小
00000014: DD   PointerToRawData ;节在文件中的偏移
00000018: DD   PointerToRelocations ;usless
0000001c: DD   PointerToLinenumbers ;usless
00000020: DW   NumberOfRelocations ;usless
00000022: DW   NumberOfLinenumbers ;usless
00000024: DD   Characteristics   ;节属性(5)

每个节被加载到内存后,节基址是按页对齐的,因为系统管理内存数据是以页为单位,而节保存在磁盘中时,节基址是以扇区为对齐单位的,这是系统管理磁盘数据的普遍规定.
往往一个页要大于一个扇区的大小,又有在内存中节基址偏移是相对于映象基址的,在磁盘中节基址的偏移是相对于文件基址的.
所以为了避免当节被加载到内存时要进行对齐转换,在节表中加入了该节被实际加载到内存后相对于映象基址的按页对齐的偏移,同时保留该节在磁盘上相对于文件基址的按扇区对齐的偏移.
这样既方便加载又节省磁盘空间.

虽然方便了加载,但是通过数据的RVA来得到数据在磁盘文件基址的偏移就比较麻烦.
方法是:
先通过该数据的RVA比较各节基址的RVA和各节的大小,看该数据是否落在某个节中,如果落在某个节中,则算出数据相对于节基址的偏移(数据的RVA-节基址RVA)
然后,因为该数据相对于节基址的偏移在内存中和在文件中都是相同的,所以找到该数据所处的节在文件中的基址,
然后用算出的偏移加上该节在文件中的节基址,就是该数据在文件中的偏移.

节表紧靠在可选头后面.

;导入表
00000000: DD   ;指向一个双字的RVA,该双字描述一个导入函数.若指向的双字的最高位为1,则指向的双字的剩余数据是该函数的导出编号,否则是一个RVA,这个RVA 指向一个结构,该结构最低字是一个编号,然后是一个以0结尾的字符串,该字符串是导入的函数名.
00000004: DD   ;创建时间,dump一下一般都是0?
00000008: DD   ;dump了一下都是0?
0000000c: DD   ;指向导入库文件名称字符串的RVA,该字符串以0结尾.
00000010: DD   ;指向一个双字的RVA,当映象被加载后,该字段指向的双字被加载器动态修改为对应的导入函数首指令所在字节地址.当在程序中动态加载函数后,在调用的时候是通过call,最后转到一个jmp dword ptr [xxxxxxxx]间接寻址的指令所在的位置,而这个间接地址xxxxxxxx=该RVA+映象基址.

导入表最后要有一个全0表项,指示导入表的结束,00000000指向的偏移处的结构也需要一个全0双字表示结束.

;导出表
00000000: DD   ;全0
00000004: DD   ;文件产生时间
00000008: DD   ;全0
0000000c: DD   ;指向文件名字符串的RVA,该字符串以0结尾.
00000010: DD   ;导出函数的起始序号
00000014: DD   ;导出函数的总数
00000018: DD   ;以名称导出的函数的总数
0000001c: DD   ;指向导出函数地址表的RVA,指向的是一个双字表,该双字表每个双字保存函数入口RVA.个数由00000014字段指定
00000020: DD   ;指向导出函数名地址表的RVA,指向的是一个双字表,该双字表每个双字保存函数名字符串.个数由00000018字段指定
00000024: DD   ;指向对照表的RVA,指向的是一个单字表,该单字表每个单字的索引对应导出函数名表,每个单字的值对应导出函数表的索引.个数由00000018字段指定,该表将导出函数表和导出函数名表对应起来.

函数的导出序号等于该函数所在的导出函数表索引加00000010字段.
索引都是从0记数.

;重定位表
重定位只是对直接寻址指令进行重定位,积存器寻址不需要.只有在映象基址和建议加载不符时才需要重定位,而实际加载地址是加载时动态确定的,所以重定位表只需保存需要重定位的数值地址.
重定位表是按页分块的,这样不用保存高端地址,只保存页偏移,当页内有多个重定位地址时节省空间.
00000000: DD   ;页起始RVA.
00000004: DD   ;重定位块长度
通过重定位长度算出该页内重定位个数,每个重定位项用一个字表示,该字的低12位指定页内偏移,高4位含义如下:
0:该双字地址不重定位
1:该双字地址高16位重定位
2:该双字地址低16位重定位
3:该双字地址全部重定位
其他值含义不详
最后以一个全0的8字节结束

;资源

;调试信息

注释:
1.运行平台(WORD):
0 未知平台
14ch Intel 386
14dh Intel 486
14eh Intel 586
160h R3000 大尾方式
162h R3000 小尾方式
166h R4000 小尾方式
168h R10000 小尾方式
184h Dec Alpha AXP
1f0h IBM Power PC 小尾方式
284h Dec Alpha AXP64

2.文件属性(WORD):
位数 含义
0 文件中不存在重定位信息
1 文件是可执行的
2 不存在行信息(?)
3 不存在符号信息
7 小尾方式
8 只在32位平台运行
9 不包含调试信息
10 不能从可移动盘(如软盘,光盘)运行
11 不能从网络运行
12 系统文件,不能直接运行
13 这是一个DLL文件
14 文件不能在多处理器机上运行
15 大尾方式

3.使用界面的子系统(WORD):
0 未知子系统
1 不需要子系统(?)
2 Windows图形界面
3 Windows控制台界面
5 OS2控制台界面
7 POSIX控制台界面
8 不需要子系统(?)
9 WinCE图形界面

4.数据目录项的含义:(每个表项占8字节,低双字是一个实际数据的RVA,高双字是实际数据的大小)
0 导出表
1 导入表
2 资源
3 异常(?)
4 安全(?)
5 重定位表
6 调试信息(?)
7 0
8 全局指针(?)
9 Thread Local Storage(?)
10 加载配置表(?)
11 绑定输入表(?)
12 IAT,导入函数地址表
13 延迟加载的输入表(?)
14 CLR运行时头地址
15 0

5.节属性(DWORD):
位 含义
5 (00000020h)节中包含代码
6 (00000040h)节中包含已初始化的数据
7 (00000080h)节中包含未初始化的数据
25 (02000000h)节在初始化以后将被丢弃,如重定位节
26 (04000000h)节中的数据不会经过缓存
27 (08000000h)节中的数据不会交到磁盘
28 (10000000h)节中的数据将被不同进程共享
29 (20000000h)映射到内存中页面包含可执行属性
30 (40000000h)映射到内存中页面包含可读属性
31 (80000000h)映射到内存中页面包含可写属性

0 0
原创粉丝点击