zoukankan      html  css  js  c++  java
  • 几个重要的基本概念 天高地厚

    几个重要的基本概念:

    1)节:PE文件的真正内容划分成块,称之为sections(节)。每节是一块拥有共同属性的数据,比如代码/数据、读/写等。我们可以把PE文件想象成一逻辑磁盘,PE header 是磁盘的boot扇区,而sections就是各种文件,每种文件自然就有不同属性如只读、系统、隐藏、文档等等。值得我们注意的是 ---- 节的划分是基于各组数据的共同属性: 而不是逻辑概念。重要的不是数据/代码是如何使用的,如果PE文件中的数据/代码拥有相同属性,它们就能被归入同一节中。不必关心节中类似于"data", "code"或其他的逻辑概念: 如果数据和代码拥有相同属性,它们就可以被归入同一个节中。(节名称仅仅是个区别不同节的符号而已,类似"data", "code"的命名只为了便于识别,惟有节的属性设置决定了节的特性和功能)如果某块数据想付为只读属性,就可以将该块数据放入置为只读的节中,当PE装载器映射节内容时,它会检查相关节属性并置对应内存块为指定属性。下面是常见的节名及作用:

    节名

    作用

    .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文件格式需要知道的

     

     

  • 相关阅读:
    线程安全及Python中的GIL
    八个我最常用的AS3开源类库(转)
    Mac 10.6与Win7 文件共享
    linux下erlang之OTP编译安装
    位图和矢量图区别
    让你的AIR程序脱离AIR环境运行
    outlook 0x80040201 错误
    NOSQL
    RLE压缩及优化图片压缩
    改正6种常见HTML5错误用法
  • 原文地址:https://www.cnblogs.com/net2012/p/2874664.html
Copyright © 2011-2022 走看看