peexplorer1.9: PE文件格式 1.9版 完整译文(附注释)(1)

  原著:Bernd.Luevelsmeyer               

  翻译:ah007

  [注意:本译文所有大小标题序号都是译者添加以方便大家阅读圆圈内数字是注释编号其中注释②译自微软PECOFF规范标准其它译自网络----译者]

  、前言(Preface)

  ------------------

  PE(“portableexecutable”可移植可执行文件)文件格式是微软WindwosNT,Windows95和Win32子集①中可执行 2进制文件格式;在WindowsNT中驱动也是这种格式它还能被应用于各种目标文件②和库文件中

  这种文件格式是由微软设计并于1993年被TIS(toolerfacestandard,工具接口标准)委员会(由Microsoft,Intel,Borland,Watcom,IBM,等等组成)所批准它明显基于COFF文件格式许多知识COFF(“commonobjectfilefromat”,通用目标文件格式)是应用于好几种UNIX系统③和VMS④系统中目标文件和可执行文件格式

  Win32SDK⑤中包含个名叫<winnt.h>头文件其中含有很多用于PE格式#和typedef定义我将逐步地提到其中很多结构成员名字和#定义

  你也可能发现DLL文件“imagehelp.dll”很有用途它是WindowNT部分但其书面文件却很缺乏些功用在“DeveloperNetwork”(开发者网络)中有所描述

   2、总览(GeneralLayout)

  -------------------------

  在个PE文件开始处我们会看到个MS-DOS可执行体(英语叫“stub”,意为“根存根”);它使任何PE文件都是个有效MS-DOS可执行文件

  在DOS-根的后是个32位签名以及魔数0x00004550(IMAGE_NT_SIGNATURE)(意为“NT签名”也就是PE签名;十 6进制数45和50分别代表ASCII码字母E和P----译者注)

  的后是文件头(按COFF格式)用来介绍说明该 2进制文件将运行在何种机器的上、分几个区段、链接时间、是可执行文件还是DLL、等等(本文中可执行文件和DLL文件区别在于:DLL文件不能被启动但能被别 2进制文件使用个 2进制文件则不能链接到另个可执行文件)

  那些的后是可选头(尽管它直都存在却仍被称作“可选”----COFF文件格式仅为库文件使用个“可选头”却不为目标文件使用个“可选头”这就是为什么它被称为“可选”原因)它会告诉我们该 2进制文件怎样被载入更多信息:开始地址呀、保留堆栈数呀、数据段大小呀、等等

  可选头个有趣部分是尾部“数据目录”;这些目录包含许多指向各“节”数据指针例如:如果个 2进制文件拥有个输出目录那么你就会在成员“IMAGE_DIRECTORY_ENTRY_EXPORT”(输出目录项)中找到个指向那个目录指针而该指针指向文件中某节

  跟在各种头后面我们就发现各个“节”了它们都由“节头”引导本质上讲各节中内容才是你执行真正需要东西所有头和目录这些东西只是为了帮助你找到它们

  每节都含有和对齐、包含什么样数据(如“已化数据”等等)、是否能共享等有关些标记还有就是数据本身大多数(并非所有)节都含有个或多个可通过可选头“数据目录”项来参见目录如输出目录和基址重定位目录等无目录形式内容有:例如“可执行代码”或“已化数据”等

  +-------------------+

  |DOS-stub     |  --DOS-头

  +-------------------+

  |file-header   |  --文件头

  +-------------------+

  |optionalheader |  --可选头

  |----------|

  |         |

  |datadirectories |  --数据目录

  |         |

  +-------------------+

  |         |

  |sectionheaders |  --节头

  |         |

  +-------------------+

  |         |

  |section1    |  --节1

  |         |

  +-------------------+

  |         |

  |section2    |  --节2

  |         |

  +-------------------+

  |         |

  |...       |

  |         |

  +-------------------+

  |         |

  |sectionn    |  --节n

  |         |

  +-------------------+

   3、DOS-根和签名(DOS-stubandSignature)

  -----------------------------------------

  DOS-根概念很早从16位windows可执行文件(当时是“NE”格式⑥)时就广为人知了根原来是用于OS/2⑦系统可执行文件也用于自解压档案文件和其它应用对于PE文件来说它是个总是由大约100个字节所组成和MS-DOS2.0兼容可执行体用来输出象“thisprogramneedswindowsNT”的类信息

  你可以通过确认DOS-头部分是否为个IMAGE_DOS_HEADER(DOS头)结构来认出DOS-根前两个字节必须为连续两个字母“MZ”(有个#IMAGE_DOS_SIGNATURE定义是针对这个WORD单元)

  你可以通过跟在后面签名来将个PE 2进制文件和其它含有根 2进制文件区分开来跟在后面签名可由头成员'e_lfa'(它是从字节偏移地址60处开始有32字节长)所设定偏移地址找到对于OS/2系统和Windows系统 2进制文件来说签名是个16位word单元;对于PE文件来说它是个按照8位字节边界对齐32位longword单元并且IMAGE_NT_SIGNATURE(NT签名)值已由#d定义为0x00004550(即字母“PE/0/0”----译者)

   4、文件头(FileHeader)

  -------------------------

  要到达IMAGE_FILE_HEADER(文件头)结构请先确认DOS-头“MZ”(起始2个字节)然后找出DOS-根头部成员“e_lfa并从文件开始处跳过那么多字节在核实你在那里找到签名后IMAGE_FILE_HEADER(文件头)结构文件头就紧跟其后开始了下面我们将从头至尾介绍其成员

  1)第个成员是“Machine(机器)”个16位用来指出该 2进制文件预定运行于什么样系统已知合法值有:

  IMAGE_FILE_MACHINE_I386(0x14c)

  Intel80386处理器或更高

  0x014d

  Intel80386处理器或更高

  0x014e

  Intel80386处理器或更高

  0x0160     

  R3000(MIPS⑧)处理器大尾⑨

  IMAGE_FILE_MACHINE_R3000(0x162)

  R3000(MIPS)处理器小尾

  IMAGE_FILE_MACHINE_R4000(0x166)

  R4000(MIPS)处理器小尾

  IMAGE_FILE_MACHINE_R10000(0x168)

  R10000(MIPS)处理器小尾

  IMAGE_FILE_MACHINE_ALPHA(0x184)

  DECAlphaAXP⑩处理器

  IMAGE_FILE_MACHINE_POWERPC(0x1F0)

  IBMPowerPC,小尾

  2)然后是“NumberOfSections(节数)”成员16位它是紧跟在头后面数目我们以后将讨论节问题

  3)下个成员是时间戳“TimeDateStamp”(32位)用来给出文件建立时间即使它“官方”版本号没有改变你也可通过这个值来区分同个文件区别版本(除了同个文件区别版本的间必须唯时间戳格式没有明文规定但似乎是按照UTC时间“从1970年1月1日00:00:00算起秒数值”----也就是大多数C语言编译器给time_t标志使用格式)

  这个时间戳是用来绑定各个输入目录我们稍后再讨论它

  警告:有些链接器往往将时间戳设为荒唐而不是如前所述time_t格式链接时间

  4-5)成员“PoerToSymbolTable(符号表指针)”和成员“NumberOfSymbols(符号数)”(都是32位)都用于调试信息我不知道该怎样去解读它并且我发现该指针值总为0

  6)成员“SizeOfOptionalHeader(可选头大小)”(16位)只是“IMAGE_OPTIONAL_HEADER(可选头)”项大小你能用它去验证PE文件结构正确性

  7)成员“Characteristics(特性)”是个16位由许多标志位形成集合组成但大多数标志位只对目标文件和库文件有效具体如下:

  位0IMAGE_FILE_RELOCS_STRIPPED(重定位被剥离文件)表示如果文件中没有重定位信息该位置1这就表明各节重定位信息都在它们各自节中;可执行文件不使用该位它们重定位信息放在下面将要描述“baserelocation”(基址重定位)目录中

  位1IMAGE_FILE_EXECUTABLE_IMAGE(可执行映象文件)表示如果文件是个可执行文件也即不是目标文件或者库文件时置1如果链接器尝试创建个可执行文件些原因失败了并保存映像以便下次例如增量链接时使用此时此标志位也可能置1

  位2IMAGE_FILE_LINE_NUMS_STRIPPED(行数被剥离文件)表示如果行数信息被剥除此位置1;此位也不用于可执行文件

  位3IMAGE_FILE_LOCAL_SYMS_STRIPPED(本地符号被剥离文件)表示如果文件中没有有关本地符号信息时此位置1(此位也不用于可执行文件)

  位4IMAGE_FILE_AGGRESIVE_WS_TRIM(强行工作集修剪文件)表示如果操作系统被假定为:通过将正在运行进程(它所使用内存数量)强行页清除来修剪它工作集时此位置1如果进程是大部分时间处于等待天中仅被唤醒演示性应用的类时此位也应该被置1

  位7IMAGE_FILE_BYTES_REVERSED_LO(低字节变换文件)和位15IMAGE_FILE_BYTES_REVERSED_HI(高字节变换文件)表示如果文件字节序不是机器所预期形式因此它在读入前必须调换字节时此位置1这样做对可执行文件是不可靠(操作系统期望可执行文件都已经被正确地按字节排整齐了)

  位8IMAGE_FILE_32BIT_MACHINE(32位机器文件)表示如果使用机器被期望为32位机器时此位置1现在应用总将此位置1;NT5系统可能工作区别

  位9IMAGE_FILE_DEBUG_STRIPPED(调试信息被剥离文件)表示如果文件中没有调试信息此位置1此位可执行文件不用按照其它信息([6])(这里指是参考书目中第[6]种----译者注)此位被称作“恒定”并且当个映象文件只有在被装入优先装入地址才能运行(亦即:此文件不可重定位)时此位置1

  位10IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP(移动介质文件从交换文件运行)表示如果个应用不可以从可移动介质如软盘或CD-ROM上运行时此位置1在这种情况下建议操作系统将文件复制到交换文件并从那里执行

  位11IMAGE_FILE_NET_RUN_FROM_SWAP(网络文件从交换文件运行)表示如果个应用不可以从网络上运行时此位置1在这种情况下建议操作系统将文件复制到交换文件并从那里执行

  位12IMAGE_FILE_SYSTEM(系统文件)表示如果文件是个象驱动那样系统文件此位置1此位可执行文件不用;我所见过所有NT系统驱动也不用

  位13IMAGE_FILE_DLL(DLL文件)表示如果文件是个DLL文件时此位置1

  位14IMAGE_FILE_UP_SYSTEM_ONLY(仅但处理器系统文件)表示如果文件不设计运行在多处理器系统上(也就是说此文件严格地依赖单处理器些方式工作所以它会发生冲突)时此位置1

   5、相对虚拟地址(RelativeVirtualAddresses)

  ---------------------------------------------

  PE格式大量地使用所谓RVA(相对虚拟地址)个RVA亦即个“RelativeVirtualAddresses(相对虚拟地址)”是在你不知道基地址时被用来描述个内存地址它是需要加上基地址才能获得线性地址数值基地址就是PE映象文件被装入内存地址并且可能会随着次又而变化

  例如:假若个可执行文件被装入地址是0x400000并且从RVA0x1560处开始执行那么有效执行开始处将位于0x401560地址处假若它被装入地址为0x100000那么执行开始处就位于0x101560地址处

  PE-文件中各部分(各节)不需要像已载入映象文件那样对齐事情变得复杂起来例如文件中各节常按照512(十 6进制0x200----译者注)字节边界对齐而已载入映象文件则可能按照4096(十 6进制0x1000----译者注)字节边界对齐参见下面“SectionAlignment(节对齐)”和“FileAlignment(文件对齐)”

  因此为了在PE文件中找到个特定RVA地址信息你得按照文件已被载入时那样来计算偏移量但要按照文件偏移量来跳过

  试举假若你已知道执行开始处位于RVA0x1560地址处并且想从那里开始代码处反汇编为了从文件中找到这个地址你得先查明在RAM(内存)中各节是按照4096字节对齐并且“.code”节是从RVA0x1000地址处开始有16384字节长;然后你才知道RVA0x1560地址位于此节偏移量0x560处你还要查明在文件中那节是按512字节边界对齐且“.code”节在文件中从偏移量0x800处开始然后你就知道在文件中代码执行开始处就在0x800+0x560=0xd60字节处

  然后你反汇编它并发现访问个变量线性地址位于0x1051d0处 2进制文件线性地址在装入时将被重定位并常被假定使用是优先载入地址你已查明优先载入地址为0x100000因此我们可开始处理RVA0x51d0了因数据节开始于RVA0x5000处,且有2048字节长所以它处于数据节中又因数据节在文件中开始于偏移量0x4800处所以该变量就可以在文件中0x4800+0x51d0-0x5000=0x49d0处找到

   6、可选头(OptionalHeader)

  ----------------------------

  紧跟在文件头后面就是IMAGE_OPTIONAL_HEADER(尽管它名叫“可选头”它却直都在那里)它包含有怎样去准确处理PE文件信息我们也将从头至尾介绍其成员

  1)第个16位word单元叫“Magic(魔数)”就我目前所观察过PE文件而言值总是0x010b

  2-3)下面2个字节是创建此文件链接器版本(‘MajorLinkerVersion’“链接器主版本号”和‘MinorLinkerVersion’“链接器小版本号”)这两个值又是不可靠并不能总是正确地反映链接器版本号(有好几个链接器根本就不设置这个值)况且你可想象你连使用是“什么”链接器都不知道知道它版本号又有什么作用呢?

  4-6)下面3个longword(每个32位)分别用来设定可执行代码大小(“SizeOfCode”)、已化数据大小(“SizeOfInitializedData”所谓“数据段”)、以及未化数据大小(“SizeOfUninitializedData”所谓“bss段”)这些值也是不可靠(例如:数据段实际上可能会被编译器或者链接器分成好几段)并且你可以通过查看可选头后面各个“节”来获得更准确大小

  7)下个32位值是RVA这个RVA是代码入口点偏移量(‘AddressOfEntryPo“入口点地址”)执行将从这里开始它可以是:例如DLL文件LibMain地址或者开始代码(这里相应)地址或者驱动DriverEntry地址如果你敢于“手工”装载映象文件那么在你完成所有修正和重定位后你可以从这个地址开始执行你进程

  8-9)下两个32位值分别是可执行代码偏移值(‘BaseOfCode’“代码基址”)和已化数据偏移值(‘BaseOfData’“数据基址”)两个都是RVA并且两个对我们来说都没有多少意义你可以通过查看可选头后面各个“节”来获得更可靠信息

  未数据没有偏移量它没有所以在映象文件中提供这些数据是没有用处

  10)下项是个32位值提供整个 2进制文件包括所有头优先(线性)载入地址(‘ImageBase’“映象文件基址”)这是个文件已被链接器重定位后地址(总是64KB倍数)如果 2进制文件事实上能被载入这个地址那么加载器就不用再重定位文件了也就节省了些载入时间

  优先载入地址在另个映象文件已被先载入那个地址(“地址冲突”在当你载入好几个全部按照链接器缺省值重定位DLL文件时经常发生)时或者该内存已被用于其它目(堆栈、malloc、未化数据、或不管是什么)时就不能用了在这些情况下映象文件必须被载人其它地址并且需要重定位(参见下面“重定位目录”)如果是个DLL文件这么做还会产生其它问题此时“绑定输入”已不再有效所以使用DLL 2进制文件必须被修正----参见下面“输入目录”

  11-12)下两个32位值分别是RAM中“SectionAlignment”(当映象文件已被载入后意为“节对齐”)和文件中“FileAlignment”(文件对齐)它们都是PE文件各节对齐值这两个值通常都是32或者是:FileAlignment为512SectionAlignment为4096节会在以后讨论

  13-14)下2个16位word单元都是预期操作系统版本信息(MajorOperatingVersion,“操作系统主版本号”)和(MinorOperatingVersion“操作系统小版本号”)[它们都使用微软自己书面确定名字]这个版本信息应该为操作系统版本号(如NT或Win95)而不是子系统版本信息(如Win32)版本信息常常被不提供或者提供很明显加载器并不使用它们

  15-16)下2个16位word单元都是本 2进制文件版本信息('MajorImageVersion'“映象文件主版本号”和

  'MinorImageVersion'“映象文件小版本号”)很多链接器不正确地设定这个信息许多员也懒得提供这些因此即便存在这样信息你最好也不要信赖它

Tags:  pe文件格式 peexplorer1.9

延伸阅读

最新评论

发表评论