有没有给我详细的讲一下PE有關的結構,我很想了解,但是感覺太難了!

jian_a0672 2004-08-11 10:28:53
有没有给我详细的讲一下PE有關的結構,我很想了解,但是感覺太難了!

有關PE的結構.

最好是有源代碼的,例孖最好了!!

謝謝,大家
...全文
50 点赞 收藏 9
写回复
9 条回复
切换为时间正序
当前发帖距今超过3年,不再开放新的回复
发表回复
bestbear 2004-08-12
不服不行,
我纯朴的心灵受到了极大的"鼓舞" :)
回复
李马 2004-08-11
完了完了,忘了楼主是来倒分的了
回复
huahao0672 2004-08-11
PE文件有效性校验。
定位到 PE header 的起始地址。
从 file header 的 NumberOfSections域获取节数。
通过两种方法定位节表: ImageBase+SizeOfHeaders 或者 PE header的起始地址+ PE header结构大小。 (节表紧随 PE header)。如果不是使用文件映射的方法,可以用SetFilePointer 直接将文件指针定位到节表。节表的文件偏移量存放在 SizeOfHeaders域里。(SizeOfHeaders 是 IMAGE_OPTIONAL_HEADER 的结构成员)
处理每个 IMAGE_SECTION_HEADER 结构。
回复
huahao0672 2004-08-11
这儿有个关于PE文件格式的常用术语: RVA
RVA 代表相对虚拟地址。 知道什么是虚拟地址吗?相对那些简单的概念而言,RVA有些晦涩。简言之,RVA是虚拟空间中到参考点的一段距离。我打赌您肯定熟悉文件偏移量: RVA就是类似文件偏移量的东西。当然它是相对虚拟空间里的一个地址,而不是文件头部。举例说明,如果PE文件装入虚拟地址(VA)空间的400000h处,且进程从虚址401000h开始执行,我们可以说进程执行起始地址在RVA 1000h。每个RVA都是相对于模块的起始VA的。
为什么PE文件格式要用到RVA呢? 这是为了减少PE装载器的负担。因为每个模块多有可能被重载到任何虚拟地址空间,如果让PE装载器修正每个重定位项,这肯定是个梦魇。相反,如果所有重定位项都使用RVA,那么PE装载器就不必操心那些东西了: 它只要将整个模块重定位到新的起始VA。这就象相对路径和绝对路径的概念: RVA类似相对路径,VA就象绝对路径。

Field Meanings
AddressOfEntryPoint PE装载器准备运行的PE文件的第一个指令的RVA。若您要改变整个执行的流程,可以将该值指定到新的RVA,这样新RVA处的指令首先被执行。
ImageBase PE文件的优先装载地址。比如,如果该值是400000h,PE装载器将尝试把文件装到虚拟地址空间的400000h处。字眼"优先"表示若该地址区域已被其他模块占用,那PE装载器会选用其他空闲地址。
SectionAlignment 内存中节对齐的粒度。例如,如果该值是4096 (1000h),那么每节的起始地址必须是4096的倍数。若第一节从401000h开始且大小是10个字节,则下一节必定从402000h开始,即使401000h和402000h之间还有很多空间没被使用。
FileAlignment 文件中节对齐的粒度。例如,如果该值是(200h),,那么每节的起始地址必须是512的倍数。若第一节从文件偏移量200h开始且大小是10个字节,则下一节必定位于偏移量400h: 即使偏移量512和1024之间还有很多空间没被使用/定义。

MajorSubsystemVersion
MinorSubsystemVersion win32子系统版本。若PE文件是专门为Win32设计的,该子系统版本必定是4.0否则对话框不会有3维立体感。
SizeOfImage 内存中整个PE映像体的尺寸。它是所有头和节经过节对齐处理后的大小。
SizeOfHeaders 所有头+节表的大小,也就等于文件尺寸减去文件中所有节的尺寸。可以以此值作为PE文件第一节的文件偏移量。
Subsystem NT用来识别PE文件属于哪个子系统。 对于大多数Win32程序,只有两类值: Windows GUI 和 Windows CUI (控制台)。
DataDirectory 一IMAGE_DATA_DIRECTORY 结构数组。每个结构给出一个重要数据结构的RVA,比如引入地址表等。
回复
huahao0672 2004-08-11
DOS MZ header 又命名为 IMAGE_DOS_HEADER.。其中只有两个域比较重要: e_magic 包含字符串"MZ",e_lfanew 包含PE header在文件中的偏移量。
比较e_magic 是否为IMAGE_DOS_SIGNATURE以验证是否是有效的DOS header。比对符合则认为文件拥有一个有效的DOS header。
为了定位PE header,移动文件指针到e_lfanew所指向的偏移。
PE header的第一个双字包含字符串"PE\0\0"。该双字与IMAGE_NT_SIGNATURE比对,符合则认为PE header有效。
本课我们继续探讨关于 PE header 的知识。 PE header 的正式命名是 IMAGE_NT_HEADERS。再来回忆一下这个结构。

IMAGE_NT_HEADERS STRUCT
Signature dd ?
FileHeader IMAGE_FILE_HEADER <>
OptionalHeader IMAGE_OPTIONAL_HEADER32 <>
IMAGE_NT_HEADERS ENDS

Signature PE标记,值为50h, 45h, 00h, 00h(PE\0\0)。
FileHeader 该结构域包含了关于PE文件物理分布的一般信息。
OptionalHeader 该结构域包含了关于PE文件逻辑分布的信息。

最有趣的东东在 OptionalHeader 里。不过,FileHeader 里的一些域也很重要。本课我们将学习FileHeader,下一课研究OptionalHeader。

IMAGE_FILE_HEADER STRUCT
Machine WORD ?
NumberOfSections WORD ?
TimeDateStamp dd ?
PointerToSymbolTable dd ?
NumberOfSymbols dd ?
SizeOfOptionalHeader WORD ?
Characteristics WORD ?
IMAGE_FILE_HEADER ENDS

Field name Meanings
Machine 该文件运行所要求的CPU。对于Intel平台,该值是IMAGE_FILE_MACHINE_I386 (14Ch)。我们尝试了LUEVELSMEYER的pe.txt声明的14Dh和14Eh,但Windows不能正确执行。看起来,除了禁止程序执行之外,本域对我们来说用处不大。
NumberOfSections 文件的节数目。如果我们要在文件中增加或删除一个节,就需要修改这个值。
TimeDateStamp 文件创建日期和时间。我们不感兴趣。
PointerToSymbolTable 用于调试。
NumberOfSymbols 用于调试。
SizeOfOptionalHeader 指示紧随本结构之后的 OptionalHeader 结构大小,必须为有效值。
Characteristics 关于文件信息的标记,比如文件是exe还是dll。

简言之,只有三个域对我们有一些用: Machine, NumberOfSections 和 Characteristics。通常不会改变 Machine 和Characteristics 的值,但如果要遍历节表就得使用 NumberOfSections。
为了更好阐述 NumberOfSections 的用处,这里简要介绍一下节表。

节表是一个结构数组,每个结构包含一个节的信息。因此若有3个节,数组就有3个成员。我们需要NumberOfSections值来了解该数组中到底有几个成员。 也许您会想检测结构中的全0成员起到同样效果。Windows确实采用了这种方法。为了证明这一点,可以增加NumberOfSections的值,Windows仍然可以正常执行文件。据我们的观察,Windows读取NumberOfSections的值然后检查节表里的每个结构,如果找到一个全0结构就结束搜索,否则一直处理完NumberOfSections指定数目的结构。 为什么我们不能忽略NumberOfSections的值? 有几个原因。PE说明中没有指定节表必须以全0结构结束。Thus there may be a situation where the last array member is contiguous to the first section, without empty space at all. Another reason has to do with bound imports. The new-style binding puts the information immediately following the section table's last structure array member. 因此您仍然需要NumberOfSections。


--------------------------------------------------------------------------------
回复
bestbear 2004-08-11
http://titilima.nease.net/article/pe.htm

www.baidu.com
www.google.com
pe文件格式详解
回复
huahao0672 2004-08-11
PE 的意思就是 Portable Executable(可移植的执行体)。它是 Win32环境自身所带的执行体文件格式。它的一些特性继承自 Unix的 Coff (common object file format)文件格式。"portable executable"(可移植的执行体)意味着此文件格式是跨win32平台的 : 即使Windows运行在非Intel的CPU上,任何win32平台的PE装载器都能识别和使用该文件格式。当然,移植到不同的CPU上PE执行体必然得有一些改变。所有 win32执行体 (除了VxD和16位的Dll)都使用PE文件格式,包括NT的内核模式驱动程序(kernel mode drivers)。因而研究PE文件格式给了我们洞悉Windows结构的良机。

本教程就让我们浏览一下 PE文件格式的概要。

DOS MZ header
DOS stub
PE header
Section table
Section 1
Section 2
Section ...
Section n

上图是 PE文件结构的总体层次分布。所有 PE文件(甚至32位的 DLLs) 必须以一个简单的 DOS MZ header 开始。我们通常对此结构没有太大兴趣。有了它,一旦程序在DOS下执行,DOS就能识别出这是有效的执行体,然后运行紧随 MZ header 之后的 DOS stub。DOS stub实际上是个有效的 EXE,在不支持 PE文件格式的操作系统中,它将简单显示一个错误提示,类似于字符串 "This program requires Windows" 或者程序员可根据自己的意图实现完整的 DOS代码。通常我们也不对 DOS stub 太感兴趣: 因为大多数情况下它是由汇编器/编译器自动生成。通常,它简单调用中断21h服务9来显示字符串"This program cannot run in DOS mode"。

紧接着 DOS stub 的是 PE header。 PE header 是PE相关结构 IMAGE_NT_HEADERS 的简称,其中包含了许多PE装载器用到的重要域。当我们更加深入研究PE文件格式后,将对这些重要域耳目能详。执行体在支持PE文件结构的操作系统中执行时,PE装载器将从 DOS MZ header 中找到 PE header 的起始偏移量。因而跳过了 DOS stub 直接定位到真正的文件头 PE header。

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

如果我们将PE文件格式视为一逻辑磁盘,PE header是boot扇区而sections是各种文件,但我们仍缺乏足够信息来定位磁盘上的不同文件,譬如,什么是PE文件格式中等价于目录的东东?别急,那就是 PE header 接下来的数组结构 section table(节表)。 每个结构包含对应节的属性、文件偏移量、虚拟偏移量等。如果PE文件里有5个节,那么此结构数组内就有5个成员。因此,我们便可以把节表视为逻辑磁盘中的根目录,每个数组成员等价于根目录中目录项。

以上就是PE文件格式的物理分布,下面将总结一下装载一PE文件的主要步骤:

当PE文件被执行,PE装载器检查 DOS MZ header 里的 PE header 偏移量。如果找到,则跳转到 PE header。
PE装载器检查 PE header 的有效性。如果有效,就跳转到PE header的尾部。
紧跟 PE header 的是节表。PE装载器读取其中的节信息,并采用文件映射方法将这些节映射到内存,同时付上节表里指定的节属性。
PE文件映射入内存后,PE装载器将处理PE文件中类似 import table(引入表)逻辑部分。

理论:
如何才能校验指定文件是否为一有效PE文件呢? 这个问题很难回答,完全取决于想要的精准程度。您可以检验PE文件格式里的各个数据结构,或者仅校验一些关键数据结构。大多数情况下,没有必要校验文件里的每一个数据结构,只要一些关键数据结构有效,我们就认为是有效的PE文件了。下面我们就来实现前面的假设。

我们要验证的重要数据结构就是 PE header。从编程角度看,PE header 实际就是一个 IMAGE_NT_HEADERS 结构。定义如下:

IMAGE_NT_HEADERS STRUCT
Signature dd ?
FileHeader IMAGE_FILE_HEADER <>
OptionalHeader IMAGE_OPTIONAL_HEADER32 <>
IMAGE_NT_HEADERS ENDS

Signature 一dword类型,值为50h, 45h, 00h, 00h(PE\0\0)。 本域为PE标记,我们可以此识别给定文件是否为有效PE文件。
FileHeader 该结构域包含了关于PE文件物理分布的信息, 比如节数目、文件执行机器等。
OptionalHeader 该结构域包含了关于PE文件逻辑分布的信息,虽然域名有"可选"字样,但实际上本结构总是存在的。

我们目的很明确。如果IMAGE_NT_HEADERS的signature域值等于"PE\0\0",那么就是有效的PE文件。实际上,为了比较方便,Microsoft已定义了常量IMAGE_NT_SIGNATURE供我们使用。

IMAGE_DOS_SIGNATURE equ 5A4Dh
IMAGE_OS2_SIGNATURE equ 454Eh
IMAGE_OS2_SIGNATURE_LE equ 454Ch
IMAGE_VXD_SIGNATURE equ 454Ch
IMAGE_NT_SIGNATURE equ 4550h

接下来的问题是: 如何定位 PE header? 答案很简单: DOS MZ header 已经包含了指向 PE header 的文件偏移量。DOS MZ header 又定义成结构 IMAGE_DOS_HEADER 。查询windows.inc,我们知道 IMAGE_DOS_HEADER 结构的e_lfanew成员就是指向 PE header 的文件偏移量。

现在将所有步骤总结如下:

首先检验文件头部第一个字的值是否等于 IMAGE_DOS_SIGNATURE,是则 DOS MZ header 有效。
一旦证明文件的 DOS header 有效后,就可用e_lfanew来定位 PE header 了。
比较 PE header 的第一个字的值是否等于 IMAGE_NT_HEADER。如果前后两个值都匹配,那我们就认为该文件是一个有效的PE文件。
回复
李马 2004-08-11
http://home.ncust.edu.cn/~titilima/readarticle.php?id=20
回复
相关推荐
发帖
VC/MFC
创建于2007-09-28

1.5w+

社区成员

VC/MFC相关问题讨论
申请成为版主
帖子事件
创建了帖子
2004-08-11 10:28
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……