最详细PE文件格式讲解!!!!!
每个PE文件都是以一个DOS程序开始的,有了它,一旦程序在DOS下执行,DOS就能执行识别出这是一个有效的执行体,然后运行紧随MZ header的DOS stub(DOS块)。e_lfanew 字段是真正的PE文件头的相对偏移(RVA),指向真正的PE头的文件偏移位置,占用4字节,位于文件开始偏移3ch字节处。当PE文件通过加载器载入内存后,内存中的版本称为模块(Module),映射文件的起始地址
一:简单介绍和必须知道的知识点:
在介绍PE文件格式的细节前,仔细看一下下面图,该图展示了PE文件格式的大概布局,学习时建议配合使用PE工具——stud_PE。
1.1PE的基本概念
PE文件使用的是一个平面地址空间,所以代码和数据都合并在一起,组成了一个很大的结构。文件的内容被分割为不同的区块(Section,也叫区段,节等)区块中包含代码或者数据,各个区块按照页边界对齐。区块木有大小限制,是一个连续结构。每个块都有它自己的内存中的一套属性,例如此块是否含有代码,是否可读或者写等。
知道PE文件不是作为单一内存映射文件被载入内存是很重要的。PE装载器遍历PE文件并决定文件的那一部分被映射,这种映射方式是将文件较高的偏移位置映射到较高的内存地址中。磁盘文件一旦被载入内存,磁盘上的数据结构布局和内存中的数据结构布局就是一致的。这样,如果在磁盘的数据结构中找一些内容,就和被载入的内存文件找到的内容一样。但是数据之间的相对位置可能会改变,某项的偏移地址可能区别于原始的偏移位置。
1.1.1 基地址
当PE文件通过加载器载入内存后,内存中的版本称为模块(Module),映射文件的起始地址称为模块句柄(hModule),可以通过模块句柄访问内存中的其他数据结构。这个起始内存地址也称为基地址(ImageBase)。
基地址的值是由PE文件本身设定的。按照默认设置,用 VsuaC++建立的EXE文件的基地址是400000h(6位),DLL文件的基地址是10000000h(8位)。
1.1.2 虚拟地址
在Windows系统中,PE文件被系统加载器映射到内存中。每个程序都有自己的虚拟空间,这个虚拟空间的内存地址被称为虚拟地址(Virtual Address,VA )。
1.1.3 相对虚拟地址
为了避免出现绝对内存地址引入了相对虚拟地址(RVA)的概念。RVA只是内存中的一个简单的,相对于PE文件载入地址的偏移量,它是一个”相对“地址(或者叫 偏移量)
例如:一个文件从400000h处载入,而且它的代码区块开始于401000h处,代码区块的RVA计算方法如下:
目标地址(401000h)- 载入地址(400000h)= RVA (1000h)
相对的知道了:
虚拟地址(VA) = 基地址(ImageBase) + 相对虚拟地址(RVA)
1.1.4 文件偏移地址
当PE文件存储在磁盘中时,某个数据的位置相当于文件头的偏移量称为文件偏移地址(FileOffset)或者物理地址(RAW Offset)。文件偏移地址从PE文件的第一个字节开始计数,起始值为0.
二:MS-DOS头部
每个PE文件都是以一个DOS程序开始的,有了它,一旦程序在DOS下执行,DOS就能执行识别出这是一个有效的执行体,然后运行紧随MZ header的DOS stub(DOS块)。DOS stub实际上是一个有效的EXE ,在不支持PE文件格式的操作系统上它将显示一个错误提示,类似于字符串”This program cannot be run in MS-DOS mode“。程序员也会根据自己的意图实现完整的DOS代码。这个stub部分不重要,我们通常把DOS MZ头与DOS stub合称为DOS文件头。
IMAGE_DOS_HEADER的结构体定义如下:
其中有俩个字段比较重要,分别是e_magic 和 e_lfanew。e_magic字段(1字节大小)的值需要被设置为 5A4D。这个值有一个#define,名为IMAGE_DOS_SIGNATURE,在ASII表示法里它的值为”MZ“,是MS-DOS的创建者之一Mark Zbikowski名字的缩写。e_lfanew 字段是真正的PE文件头的相对偏移(RVA),指向真正的PE头的文件偏移位置,占用4字节,位于文件开始偏移3ch字节处。
CPU属于小端类,字符存储时低位在低位,及在前,高位在后。将次序恢复后,e_lfanew的值为0000000Bh,这个值就是真正的偏移量。
三:PE文件头
紧跟DOS stub的是PE文件头(PE Header)。”PE Header“是PE相关结构NT映像头的简称(IMAGE_NT_HEADERS),其中包含许多重要字段。当执行体在执行PE文件结构时,装载器将会从IMAGE_DOS_HEADER结构的e_lfanew字段里找到PE Heard的起始偏移量,用其加上基地址,得到PE文件头的指针。
PNTHeard = ImageBase + dosHeader->e_lfanew
3.1 Signature字段
3.2 IMAGE_FILE_HEADER 结构
其中最重要的是指出IMAGE_OPTIONAL_HEADER的大小。下面介绍IMAGE_FILE_HEADER 结构的各个字段,并进行说明。
typedef struct _IMAGE_FILE_HEADER {
WORD Machine; ** 机器号 相对该结构的偏移0x00**
WORD NumberOfSections; **重要成员 节区数量 相对该结构的偏移0x02**
DWORD TimeDateStamp; ** 时间戳 相对该结构的偏移0x04**
DWORD PointerToSymbolTable; ** 符号表偏移 相对该结构的偏移0x08**
DWORD NumberOfSymbols; ** 符号表数量 相对该结构的偏移0x0C**
WORD SizeOfOptionalHeader; **重要成员 可选头大小 相对该结构的偏移0x10**
WORD Characteristics; **重要成员 PE文件属性 相对该结构的偏移0x12**
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
用16进制工具查看结构情况:
1.Machine
所表示的是计算机的体系结构类型,也就是说这个成员可以指定该PE文件能够在32位还是在64位CPU上执行。如果强行更改该数值程序就会报错。该成员可以是以下的数值:
2.NumherOlSections:区块(Section)的数目,块表紧跟在IMAGE NT HEADERS 后面。
3.TimeDateStamp:表示文件的创建时间。这个值是自1970年1月1日以来用格林威治时间(GMT)计算的秒数,是一个比文件系统的日期/时间更精确的文件创建时间指示器。将这个值翻译为易读的字符串需要使用_cime 函数(它是时区敏感型的)。另一个对此字段计算有用的函数是 gmtime。
4.PointerToSymbolTable:COFF 符号表的文件偏移位置(参见 Microsoft 规范的 5.4节)。因为采用了较新的 debug格式,所以 COF 符号表在 PE 文件中较为少见。在 Visual Studio.NET 出现之前COFF 符号表可以通过设置链接器开关(/DEBUGTYPE:COFF)来创建。COFF 符号表几乎总能在目标文件中找到,若没有符号表存在,将此值设置为0。
5.NumberOrSymhols:如果有 COFF 符号表,它代表其中的符号数目。COFF 符号是一个大小固定的结构,如果想找到 COFF符号表的结束处,需要使用这个域。
6.Size0f0ptionalHeader:紧跟 IMAGE_FILE HEADER,表示数据的大小。在 PE 文件中,这个数据结构叫作 IMAGE OPTIONAL, HEADER,其大小依赖于当前文件是 32 位还是 64 位文件。对 32位 PE 文件,这个域通常是 00F0h;对 64位 PE32+ 文件,这个域是 00F0h。不管怎样,这些是要求
的最小值,较大的值也可能会出现。
7. Characterislics:文件属性,有选择地通过几个值的运算得到。这些标志的有效值是定义于winnt.h 内的 IMAGE FILE xxx值,具体如表 11.2所示。普通 EXE 文件的这个字段的值一般是 010fh.DLL文件的这个字段的值一般是 2102h。
3.3IMAGE_OPTIONAL_HEARDER结构
typedef struct _IMAGE_OPTIONAL_HEADER {
WORD Magic; **魔术字 偏移0x00
BYTE MajorLinkerVersion; **链接器主版本 偏移0x02
BYTE MinorLinkerVersion; **链接器副版本 偏移0x03
DWORD SizeOfCode; **所有含代码的节的总大小 偏移0x04
DWORD SizeOfInitializedData; **所有含初始数据的节的总大小 偏移0x08
DWORD SizeOfUninitializedData; **所有含未初始数据的节的总大小 偏移0x0C
DWORD AddressOfEntryPoint; **程序执行入口地址 偏移0x10 重要
DWORD BaseOfCode; **代码节的起始地址 偏移0x14
DWORD BaseOfData; **数据节的起始地址 偏移0x18
DWORD ImageBase; **程序首选装载地址 偏移0x1C 重要
DWORD SectionAlignment; **内存中节区对齐大小 偏移0x20 重要
DWORD FileAlignment; **文件中节区对齐大小 偏移0x24 重要
WORD MajorOperatingSystemVersion; **操作系统的主版本号 偏移0x28
WORD MinorOperatingSystemVersion; **操作系统的副版本号 偏移0x2A
WORD MajorImageVersion; **镜像的主版本号 偏移0x2C
WORD MinorImageVersion; **镜像的副版本号 偏移0x2E
WORD MajorSubsystemVersion; **子系统的主版本号 偏移0x30
WORD MinorSubsystemVersion; **子系统的副版本号 偏移0x32
DWORD Win32VersionValue; **保留,必须为0 偏移0x34
DWORD SizeOfImage; **镜像大小 偏移0x38 重要
DWORD SizeOfHeaders; **PE头大小 偏移0x3C 重要
DWORD CheckSum; **校验和 偏移0x40
WORD Subsystem; **子系统类型 偏移0x44
WORD DllCharacteristics; **DLL文件特征 偏移0x46
DWORD SizeOfStackReserve; **栈的保留大小 偏移0x48
DWORD SizeOfStackCommit; **栈的提交大小 偏移0x4C
DWORD SizeOfHeapReserve; **堆的保留大小 偏移0x50
DWORD SizeOfHeapCommit; **堆的提交大小 偏移0x54
DWORD LoaderFlags; **保留,必须为0 偏移0x58
DWORD NumberOfRvaAndSizes; **数据目录的项数 偏移0x5C
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
① Magic:这是一个标记字,说明文件是 ROM 映像(0107h)还是普通可执行的映像(010Bh),一般是 010Bh。如果是 PE32+,则是 020Bh。
④ Size0fCode:有 IMAGE SCN_CNT_CODE 属性的区块的总大小(只人不舍 ),这个值是向上对齐某一个值的整数倍。例如,本例是 200h,即对齐的是一个磁盘扇区字节数(200h)的整数倍。在通常情况下,多数文件只有1个 Code 块,所以这个字段和.text 块的大小匹配。
⑤ SizeOfInitializedData:已初始化数据块的大小,即在编译时所构成的块的大小(不包括代码段 )。但这个数据不太准确。
⑥ SizeOfUninitializedData:未初始化数据块的大小,装载程序要在虚拟地址空间中为这些数据约定空间。这些块在磁盘文件中不占空间,就像“UninitializedData”这一术语所暗示的一样,这些块在程序开始运行时没有指定值。未初始化数据通常在.bss块中。
⑦ Address0fEntnyPoint:程序执行人口 RVA。对于 DLL,这个入口点在进程初始化和关闭时及线程创建和毁灭时被调用。在大多数可执行文件中,这个地址不直接指向 Main、WinMain 或 DlMain函数,而指向运行时的库代码并由它来调用上述函数。在 DLL中,这个域能被设置为 0,此时前面
提到的通知消息都无法收到。链接器的 /OENTRY 开关可以设置这个域为 0。⑧ BaseOfCode:代码段的起始 RVA。在内存中,代码段通常在 PE 文件头之后,数据块之前。在 Microsof 链接器生成的可执行文件中,RVA 的值通常是1000h。Borland的 Tlink32 用 ImageBase加第1个 Code Section 的 RVA,并将结果存入该字段。
⑧ BaseOfCode: 代码段的起始RVA。在内存中,代码段通常在 PE文件头之后,数据块之前。 在 Microsoft链接器生成的可执行文件中, RVA的值通常是1000h 。Borland 的 Tlink32 用 ImageBase 加第1个 Code Section的 RVA, 并将结果存入该字段。
⑨BaseOfData:数据段的起始 RVA。数据段通常在内存的末尾,即 PE 文件头和 Code Section之后。可是,这个域的值对于不同版本的 Microso 链接器是不一致的,在 64 位可执行文件中是不会出现的。
⑩ ImageBase:文件在内存中的首选载入地址。如果有可能(也就是说,如果目前没有其他文件占据这块地址,它就是正确对齐的并且是一个合法的地址),加载器会试图在这个地址载入 PE 文件。如果 PE 文件是在这个地址载人的,那么加载器将跳过应用基址重定位的步骤。
⑪SectionAlignment: 载入内存时的区块对齐大小。每个区块被载人的地址必定是本字段指定数值的整数倍。默认的对齐尺寸是目标 CPU的页尺寸。对运行在Windows 9x/Me下的用户模式可执行 文件,最小的对齐尺寸是每页1000h(4KB) 。 这个字段可以通过链接器的/ALIGN 开关来设置。在 IA-64 上,这个字段是按8KB排列的。
⑫ FileAlignment: 磁盘上 PE 文件内的区块对齐大小,组成块的原始数据必须保证从本字段的 倍数地址开始。对于x86 可执行文件,这个值通常是200h 或1000h, 这是为了保证块总是从磁盘的 扇区开始,这个字段的功能等价于NE 格式文件中的段/资源对齐因子。使用不同版本的 Microsoft链 接器,默认值会改变。这个值必须是2的幂,其最小值为200h。而且,如果 SectionAlignment 小于 CPU的页尺寸,这个域就必须与SectionAlignment 匹配。链接器开关/OPT:WIN98设置x86 可执行文 件的对齐值为1000h,/OPT:NOWIN98 设置对齐值为200h。
⑳ SizeOflmage: 映像载入内存后的总尺寸,是指载入文件从 ImageBase 到最后一个块的大小。 最后一个块根据其大小向上取整。
⑳ SizeOfHeaders:MS-DOS 头部、PE 文件头、区块表的总尺寸。这些项目出现在 PE 文件中的 所有代码或数据区块之前,域值四舍五入至文件对齐值的倍数。
SizeOfHeaders = (e_lfanew/*DOS头部*/ + 4/*PE签名*/ +
sizeof(IMAGE_FILE_HEADER) +
SizeOfOptionalHeader + /*NT头*/
sizeof(IMAGE_SECTION_HEADER) * NumberOfSections) / /*节表*/
FileAlignment *
FileAlignment +
FileAlignment; /*向上舍入 一般该结果不可能是FileAlignment的整数倍,所以直接加上FileAlignment还是没问题的 */
!!NumberOfRvaAndSizes: 数据目录的项数。这个字段的值从Windows NT 发布以来 一 直是16。
!! DataDirectory[16]:数据目录表,由数个相同的IMAGE_DATA_DIRECTORY 结构组成,指向
输出表、输入表、资源块等数据。IMACE_DATA_DIRECTORY的结构定义如下。
以上太繁琐,下面是简化版:
AddressOfEntryPoint
该成员保存着文件被执行时的入口地址,它是一个RVA。如果想要在一个可执行文件中附加了一段代码并且要让这段代码首先被执行,就可以通过更改入口地址到目标代码上,然后再跳转回原有的入口地址。
ImageBase
该成员指定了文件被执行时优先被装入的地址,如果这个地址已经被占用,那么程序装载器就会将它载入其他地址。当文件被载入其他地址后,就必须通过重定位表进行资源的重定位,这就会变慢文件的载入速度。而装载到ImageBase指定的地址就不会进行资源重定位。
对于EXE文件来说,由于每个文件总是使用独立的虚拟地址空间,优先装入地址不可能被其他模块占据,所以EXE总是能够按照这个地址装入,这也意味着EXE文件不再需要重定位信息。对于DLL文件来说,由于多个DLL文件全部使用宿主EXE文件的地址空间,不能保证优先装入地址没有被其他的DLL使用,所以DLL文件中必须包含重定位信息以防万一。因此,在前面介绍的 IMAGE_FILE_HEADER 结构的 Characteristics 成员中,DLL 文件对应的IMAGE_FILE_RELOCS_STRIPPED
位总是为0,而EXE文件的这个标志位总是为1。
SectionAlignment
该成员指定了文件被装入内存时,节区的对齐单位。节区被装入内存的虚拟地址必须是该成员的整数倍,以字节为单位,并且该成员的值必须大于等于FileAlignment的值。该成员的默认大小为系统的页面大小。
FileAlignment
该成员指定了文件在硬盘上时,节区的对齐单位。节区在硬盘上的地址必须是该成员的整数倍,以字节为单位,并且该成员的值必须大于等于FileAlignment的值。该值应为200h到10000h(含)之间的2的幂。默认为200h。如果SectionAlignment的值小于系统页面大小,则FileAlignment的值必须等于SectionAlignment的值。
SizeOfImage
该成员指定了文件载入内存后的总体大小,包含所有的头部信息。并且它的值必须是SectionAlignment
的整数倍。
PE文件在定位输出表,输入表和资源表等重要数据时,就是从IMAGE_DATA_DIRECTORY结构开始的。
3.4区块
在PE文件头与原始数据之间存在一个区块表(Section Table)。区块表中包含每块在映像中的信息,分别指向不同的区块实体。
3.4.1 区块表
紧跟IMAGE_NT_HEARDERS的就是区块表,它是一个IMAGE_SECTION_HEARDER结构数组。每个IMAGE_SECTION_HEARDER结构包含了它所关联的区块信息,例如位置,长度,属性。该数组的数目由IMAGE_NT_HEARDER.FileHearder.NumberSection指出。
IMAGE_SECTION_HEARDER结构定义如下:
#define IMAGE_SIZEOF_SHORT_NAME 8
typedef struct _IMAGE_SECTION_HEADER {
BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; **节区名 偏移0x00
union {
DWORD PhysicalAddress;
DWORD VirtualSize; **节区的虚拟大小 偏移0x08 重要
} Misc;
DWORD VirtualAddress; **节区的虚拟地址 偏移0x0C 重要
DWORD SizeOfRawData; **节区在硬盘上的大小 偏移0x10 重要
DWORD PointerToRawData; **节区在硬盘上的地址 偏移0x14 重要
DWORD PointerToRelocations; **指向重定位项开头的地址 偏移0x18
DWORD PointerToLinenumbers; **指向行号项开头的地址 偏移0x1C
WORD NumberOfRelocations; **节区的重定位项数 偏移0x20
WORD NumberOfLinenumbers; **节区的行号数 偏移0x22
DWORD Characteristics; **节区的属性 偏移0x24 重要
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
Name
这是一个8字节的ASCII字符串,长度不足8字节时用0x00填充,该名称并不遵守必须以"\0"结尾的规律,如果不是以"\0"结尾,系统会截取8个字节的长度进行处理。可执行文件不支持长度超过8字节的节名。对于支持超过字节长度的文件来说,此成员会包含斜杠(/),并在后面跟随一个用ASCII表示的十进制数字,该数字是字符串表的偏移量。
Misc.VirtualSize
这个成员在一个共用体中,这个共用体中还有另外一个成员,由于用处不大我们就不讲解了,主要讲解VirtualSize的含义。这个成员指定了该节区装入内存后的总大小,以字节为单位,如果此值大于SizeOfRawData的值,那么大出的部分将用0x00填充。这个成员只对可执行文件有效,如果是obj文件此成员的值为0。
VirtualAddress
指定了该节区装入内存虚拟空间后的地址,这个地址是一个相对虚拟地址(RVA),它的值一般是SectionAlignment的整数倍。它加上ImageBase后才是真正的虚拟地址。
SizeOfRawData
指定了该节区在硬盘上初始化数据的大小,以字节为单位。它的值必须是FileAlignment的整数倍,如果小于Misc.VirtualSize,那么该部分的其余部分将用0x00填充。如果该部分仅包含未初始化的数据,那么这个值将会为零。
PointerToRawData
指出零该节区在硬盘文件中的地址,这个数值是从文件头开始算起的偏移量,也就是说这个地址是一个文件偏移地址(FOA)。它的值必须是FileAlignment的整数倍。如果这个部分仅包含未初始化的数据,则将此成员设置为零。
Characteristics
该成员指出了该节区的属性特征。其中的不同数据位代表了不同的属性,这些数据位组合起来就是这个节的属性特征
3.4.2 常见区块与区块合并
区块中的数据逻辑通常是关联的。PE文件一般至少有俩个区块,一个是代码块,另一个是数据块。每个区块都有特定的名字,这个名字用于表示区块的用途。例如,一个区块叫做.rdata,表名它是一个只读区块。区块在映像中是按起始地址(RVA)排列的,而不是按照字母顺序排列。
常见区块:
注意:在编程过程中,读取文件相关内容时,不能将区块名称作为参考。正确的方法是按照 数据目录表中的字段进行定位。
3.4.3 区块的对齐值
区块的大小是要对齐的。有俩种对齐值,一种用于磁盘文件内,另一种用于内存中。PE文件头指出了俩个值,它们可以不同。
在PE文件头里,FileAlignmment定义了磁盘区块的对齐值。每一个区块从对齐值的倍数的偏移位置开始,而区块的实践代码或者数据的大小不一定刚好是那么多,不足的地方以00h来填充,这就是区块的间隙。
例如,在PE 文件中, 一个典型的对齐值是200h, 这样每个区块从200h 的倍数的文件偏移位置开始。假设区块的第1个节在400h 处,长度为90h, 那么400h~490h 为这 一区块的内容,而文件对齐值是200h, 为了使这一节的长度为 FileAlignment 的整数倍,490h~600h 会被0填充,这段空间称为区块间隙,下一个区块的开始地址为600h。
在PE 文件头里,SectionAlignment 定义了内存中区块的对齐值。当PE 文件被映射到内存中时, 区块总是至少从一个页边界处开始。也就是说,当一个 PE 文件被映射到内存中时,每个区块的第1 个字节对应于某个内存页。在x86 系列CPU 中,内存页是按4KB(1000h) 排列的;在x64 中,内 存页是按8KB(2000h) 排列的。所以,在x86 系统中,PE 文件区块的内存对齐值一般为1000h, 每个区块从1000h 的倍数的内存偏移位置开始。
回顾一下 .text 区块在磁盘文件中的偏移位置是400h, 在内存中将是其载人地址之上 的1000h 字节处。 .rdata 区块在磁盘文件偏移的600h 处,在内存中将是载入地址之上的2000h 字 节处。
3.4.4文件偏移与虚拟地址的转换
一些 PE 文件为减小体积,磁盘对齐值不是一个内存页1000h, 而是200h。当这类文件被映射 到内存中后,同一数据相对于文件头的偏移量在内存中和磁盘文件中是不同的,这样就出现了文件。
偏移地址与虚拟地址的转换问题。而那些磁盘对齐值(1000h) 与内存页相同的区块,同 一 数据在 磁盘文件中的偏移与在内存中的偏移相同,因此不需要转换。
可以看出,文件被映射到内存中时, MS-DOS 头 部 、PE 文件头和块表的偏移位置与大小均没有 变化,而当各区块被映射到内存中后,其偏移位置就发生了变化。例如,磁盘文件中,text 块起始端 与文件头的偏移量为 add1, 映射到内存后, .text块起始端与文件头(基地址)的偏移量为add2。
同时.text块与块表之间形成了 一大段空隙,这部分数据全是以0填充的。在这里, add1 的值就是文件偏移地址(FileOffset),add2的值就是相对虚拟地址 (RVA) 。 假设它们的差值为△k, 则文件偏移地址与虚拟地址的关系如下。
FileOffset =RVA-△k
FileOffset =VA -ImageBase - △k
在同一 区块中,各地址的偏移量是相等的,可用上面的公式对此区块中的任意FileOffset与 VA 进行转换。但请不要错误地认为在整个文件里File Offset 与 VA 的差值是△k, 因为各区块在内存中 是以 一 个页边界开始的,从第1个区块的结束到第2个区块的开始(1000h 对 齐 处 ) 全 以 数 据 0 填 充,所以不同区块在磁盘与内存中的差值不同。如表11.7所示是该实例文件各区块在磁盘与内存中的起始地址差值。
区 块 | 虚拟偏移量(Virtual Oifset)=RVA | 文件偏移量(Raw Offset) | 差值(△k) |
.text | 1000h | 400h | 0C00h |
.rdata | 2000h | 600h | 1A00h |
.data | 3000h | 800h | 2800h |
例如,此实例中某一虚拟地址 (VA) 为401112h, 要计算它的文件偏移地址。401112h 在 .text
块中,此时△k=0C00h, 故
File Offset =VA-ImageBase - △k=401112h-400000h-C00h =512h
再来看一看虚拟地址4020D2h的转换。4020D2h在 .rdata块中,此时Ak=1A00h, 故
File Offset =VA-ImageBase - △k=4020D2h-400000h-1A00h =6D2h
欧克,上半部分介绍结束,希望大家认真阅读理解,然后看下半部分就会有更好的理解
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)