扫二维码与项目经理沟通
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流
1)节:PE文件的真正内容划分成块,称之为sections(节)。每节是一块拥有共同属性的数据,比如代码/数据、读/写等。我们可以把PE文件想象成一逻辑磁盘,PE header 是磁盘的boot扇区,而sections就是各种文件,每种文件自然就有不同属性如只读、系统、隐藏、文档等等。值得我们注意的是 ---- 节的划分是基于各组数据的共同属性: 而不是逻辑概念。重要的不是数据/代码是如何使用的,如果PE文件中的数据/代码拥有相同属性,它们就能被归入同一节中。不必关心节中类似于"data", "code"或其他的逻辑概念: 如果数据和代码拥有相同属性,它们就可以被归入同一个节中。(节名称仅仅是个区别不同节的符号而已,类似"data", "code"的命名只为了便于识别,惟有节的属性设置决定了节的特性和功能)如果某块数据想付为只读属性,就可以将该块数据放入置为只读的节中,当PE装载器映射节内容时,它会检查相关节属性并置对应内存块为指定属性。下面是常见的节名及作用:
创新互联是一家专注于网站设计制作、成都网站建设与策划设计,虎丘网站建设哪家好?创新互联做网站,专注于网站建设十多年,网设计领域的专业建站公司;建站业务涵盖:虎丘等地区。虎丘做网站价格咨询:18982081108节名 | 作用 |
.arch | 最初的构建信息(Alpha Architecture Information) |
.bss | 未经初始化的数据 |
.CRT | C运行期只读数据 |
.data | 已经初始化的数据 |
.debug | 调试信息 |
.didata | 延迟输入文件名表 |
.edata | 导出文件名表 |
.idata | 导入文件名表 |
.pdata | 异常信息(Exception Information) |
.rdata | 只读的初始化数据 |
.reloc | 重定位表信息 |
.rsrc | 资源 |
.text | .exe或.dll文件的可执行代码 |
.tls | 线程的本地存储器 |
.xdata | 异常处理表 |
注意:上面已经说过了“节的划分是基于各组数据的共同属性: 而不是逻辑概念。重要的不是数据/代码是如何使用的,如果PE文件中的数据/代码拥有相同属性,它们就能被归入同一节中” 所以上面表中列出的节并不一定单独成节,也就是说即使存在上面表中的某一节,在节表(section table)(后面会讲到)中也不一定就有于之对应的项,因为它可能和别的具有共同属性的节共同组成了一节。比如 .idata 可以和 .text 合成一节而命名为 .text,而在节表中只有和 .text 对应的项。这也就是后面的optional header中数据目录(DataDirectory)存在的作用,因为很多有用的节被合并了,因此加载器无法通过节表来定位它们,所以这就是数据目录(DataDirectory)发挥作用的时候了(具体作用后面会讲到)。
2)虚拟地址:虚拟地址即程序中使用的地址,也就是从程序员的角度看到的地址,有时也叫逻辑地址;通常使用段地址:偏移量的形式表示,不过在32位系统中使用的是平坦(Flat)内存模式,所以我们可以不用管段地址,只考虑32位的偏移量即可,认为32位的偏移量就是虚拟地址,这样一来程序员就可以认为他是在一个段中写程序,这个段的大小是232 = 4G的容量,当然这部分地址空间是程序和OS共享的,程序员可以利用的大约有2G(具体可以参考Win98和WinNT的内存布局);所以我们平时在写程序申请内存的时候实际上申请的就是这2G的线性地基空间,由于所有的4G线性地址空间都被OS作为资源来管理(这4G的线性地址空间是通过页表来表现出来的,OS分配线性地址空间給进程也就是分配相应的页表給进程),所以我们无论用什么方式使用内存最终都是转换为OS为我们分配线性地址空间,至于分配的线性地址空间又如何被映射为真正的物理内存完全是有OS负责的(更详细资料参见“Windows 内存管理”),程序员不必操心。
3)相对虚拟地址:「相对虚拟地址(Relative VirtualAddress,RVA)」即相对于上面的基地址的偏移量。PE 文件中的许多字段内容都是以RVA 表示,一个RVA 是某一资料项的offset(偏移)值-- 从文件被映像进来的起点(即基地址)算起。举个例子,我们说Windows加载器把一个PE 文件映像到虚拟地址空间的0x400000 处,如果此image 有一个表格开始于0x401464,那么这个表格的RVA 就是0x1464:虚拟地址0x401464 - 基地址0x400000 = RVA 0x1464只要把RVA 加上基地址,RVA 就可以被转换为一个有用的指针。在PE文件中大多数地址多是RVA 而 RVA只有当PE文件被PE装载器装入内存后才有意义。如果我们直接将文件映射到内存而不是通过PE装载器载入,那么我们就不能直接使用那些RVA。必须先将那些RVA转换成文件偏移量,RVAToOffset函数就起到这个作用。
4)基地址:「基地址(base address)」是一个重要概念,用来描述被映像到内存中的EXE 或DLL 的起始地址。为了方便,Windows NT 和Windows 95 都以模块的基地址做为模块的instance handle(HINSTANCE,实例句柄)。Windows95加载器把一个PE 文件映像到虚拟地址空间的0x400000 处;而WindowNT加载器把一个PE 文件映像到虚拟地址空间的0x10000 处 。
5)文件偏移量:文件中的地址与内存中表示不同,它是用偏移量(File offset)来表示的,文件中的第一个字节的偏移量是0,后面的字节依次递增。在SoftICE和W32Dasm下显示的地址值是内存线性地址,或称之为虚拟地址(Virual Address,VA)。而十六进制工具里,如:Hiew、Hex Workshop等显示的地址就是文件地址,称之为偏移量(File offset) 或物理地址(RAW offset,注意这个物理地址不是内存寻址中说到的物理地址 )。
6)模块:「模块(module)」一词表示一个EXE 或DLL 被加载内存后的程序代码、数据和资源(就是被加载到内存后的EXE或DLL整体,包括代码、数据和资源,而不是说代码、数据、资源分别都是模块)。除了程序代码和数据是你的程序直接使用的之外,模块还内含一些支持性数据,Windows 用它来决定程序代码和数据放在内存的什么地方,在Win32,这些信息保留在PE头部(即图1中的PE header,实际上它是一个IMAGE_NT_HEADERS 结构)中。
7)逻辑地址:见“虚拟地址”
8)线性地址:线性地址是由虚拟地址(逻辑地址)转换来的,转换需要CPU和OS共同合作来完成;里面涉及到全局描述符表GDT和局部描述符表LDT;不过由于32位的Window系统采用flat内存模式,所以我们可以认为虚拟地址就是线性地址,即我们可以认为逻辑地址中的32位偏移量就是线性地址。
9)物理地址:即最终发往地址总线上的地址,它对应着实际的物理内存,在32位的Window存储管理中它是通过页表由线性地址转换出来的。
10)实际地址:即“物理地址”。
其中前面的6个概念是学习PE文件格式需要知道的
不登高山,怎知天高;不临深溪,焉知地厚!站在坚实的土地上,做着生命中最真实的事情;像一棵挺拔的大树,认可自己的命运并敢于迎接属于这一方天空的风风雨雨。我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流