找回密码
 注册
搜索
热搜: 回贴

For菜鸟文章:PE文件格式,qduwg翻译,PE文件,系统底层

2010-1-22 18:41| 发布者: admin| 查看: 114| 评论: 0|原作者: 青鸾峰


For菜鸟文章:PE文件格式,qduwg翻译,PE文件,系统底层
2008年06月23日 星期一 下午 01:33
PE文件格式

(翻译:QduWg,原作LUEVELSMEYER)

说明:希望本文能够对初级入门CRACKER有一定帮助,翻译存在疏漏或者不准确,希望来信指出。感谢您的指导!感谢看雪为我们提供这个交流平台,让我们技术与时俱进!!

前言:

PE("portable executable")文件格式是针对MS windows NT, windows 95 and

win32s的可执行二进制代码(DLLs and programs) 。在windows NT内, 驱动程序也是这个格式。也可以用于对象文件和库。



这个格式是Microsoft设计的,并在1993经过TIS (tool interface standard)委员会(Microsoft, Intel, Borland, Watcom, IBM等)标准化了的。它基于在UNIX和VMS上运行的对象文件和可执行文件的COFF"common object file format"格式。



win32 SDK 包括一个头文件包括对PE格式的定义。我将提及成员名和定义。你也可能发现DLL文件"imagehelp.dll" 非常有用。它是NT的一部分,但文档很少。它的一些函数在"Developer Network"被描述。



总览:

在PE文件的开始,我们可以发现MSDOS执行部分("stub"); 这使得任何一个PE文件是有效的DOS执行文件。在DOS-stub之后是32位的魔数0x00004550(IMAGE_NT_SIGNATURE).然后是一个COFF格式的文件头,指明在何种机器上运行,多少个节在里面,连接的时间,是否是可执行文件或者DLL等。DLL和可执行文件的区别:DLL不能够启动,只可以被其他可执行文件使用,一个可执行文件不能够连接到另一个可执行文件。



接着,我们看到一个可选文件头optional header(虽然叫“可选”,它实际上一直存在)。

COFF把可选文件头用于库,不用于目标文件。这里告诉我们文件如何被调入:起始地址,预留堆栈数,数据段尺寸。



一个有趣的部分是尾巴上的数组数据目录data directories,这些目录包含指向节内数据的指针。例如,如果文件有输出目录,可以在数组成员IMAGE_DIRECTORY_ENTRY_EXPORT内发现一个指针指向那个目录(目录描述结构->THUNKDATA结构->BYNAME结构)。他将指向一个节。



在头的后面是节头,实际上,节的内容就是真正需要运行一个程序所需要的东西,所有的头和目录成员就是帮你找到它。每个节有几个标志:对齐,包含的数据类型(初始化数据等),是否可以共享等,及数据自身。多数节含有一个或多个通过“可选头”内的数据目录项引用的目录。没有目录类型的内容是初始化数据或者可执行代码。(节是物理意义上的内容组织,目录是逻辑意义上的内容组织,两者互相配合才能找到需要的东西。节是存储内容的地方,区域的安排,目录是如何对里面东西进行查找,目的是寻找里面的内容)

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

| DOS-stub |

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

| file-header |

- - - - - - - - - -

| optional header |

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

| |

| data directories |

| |

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

| |

| section headers |

| |

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

| |

| section 1 |

| |

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

| |

| section 2 |

| |

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

| |

| ... |

| |

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

| |

| section n |

| |

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

DOS-stub and Signature

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

DOS STUB的概念在16位WINDOWS可执行文件内就已经被熟知了,STUB是用于OS/2可执行文件,自解压文档和其他程序。对于PE文件,它是DOS2兼容可执行文件,总是包含100字节内容,输出一个错误信息:比如"this program needs windows NT".

你认识到一个DOSSTUB通过验证DOS-header,就是一个IMAGE_DOS_HEADER结构,前两个字节必须使"MZ"(有一个定义针对这个WORD,IMAGE_DOS_SIGNATURE )。你通过尾部的'e_lfanew' 给出的偏移量所确定的签名区别一个PE文件。对于PE文件,它是一个32位,按照8字节对齐边界。其值0x00004550由IMAGE_NT_SIGNATURE 定义.



IMAGE_NT_HEADERS STRUCT

Signature DWORD ?

FileHeader IMAGE_FILE_HEADER <>

OptionalHeader IMAGE_OPTIONAL_HEADER32 <>

IMAGE_NT_HEADERS ENDS



文件头File Header

-----------

IMAGE_FILE_HEADER STRUCT

Machine WORD ?

NumberOfSections WORD ?

TimeDateStamp DWORD ?

PointerToSymbolTable DWORD ?

NumberOfSymbols DWORD ?

SizeOfOptionalHeader WORD ?

Characteristics WORD ?

IMAGE_FILE_HEADER ENDS



要得到IMAGE_FILE_HEADER,确认DOS头的前2个字节"MZ",然后找到'e_lfanew'成员,然后从文件开始跳过许多字节,验证你找到的签名。文件头作为一个IMAGE_FILE_HEADER结构,就从它后面开始。从上到下描述其成员。



第1:Machine, 16位值,指明可执行文件所需要的系统。已知合法值如下:

IMAGE_FILE_MACHINE_I386

0x014c Intel 80386 处理器。

0x014d Intel 80486处理器

0x014e Pentium 处理器

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) DEC Alpha AXP处理器

IMAGE_FILE_MACHINE_POWERPC (0x1F0)IBM Power PC处理器

第2:NumberOfSections,16位值,它是跟随于头后面的节数。我们在后面讨论。

第3:TimeDateStamp,32位值,文件创建的时间。可以通过该值区分不同的文件版本。时间戳用于绑定输入目录,后面讲到。有些连接器设置该值为荒唐的值。

第3:PointerToSymbolTable和NumberOfSymbols,都是32位的。用于调试信息。一般都是0。

第4:SizeOfOptionalHeader,16位,是IMAGE_OPTIONAL_HEADER的尺寸.可以用它确认PE文件结构的正确性。

第5:Characteristics,16位值,包括一个标志集合,多数只对目标文件和库有效。

Bit 0 (IMAGE_FILE_RELOCS_STRIPPED) 如果文件内没有重定位信息该位置1。这里指的是每个节内的重定位信息。不用于可执行文件,可执行文件的重定位信息在后面提到的base relocation目录。

Bit 1 (IMAGE_FILE_EXECUTABLE_IMAGE) 如果文件是可执行的则置1,例如不是一个目标文件或者库文件。如果连接器试图创建可执行文件,但由于某种原因失败了,也置1。

Bit 2 (IMAGE_FILE_LINE_NUMS_STRIPPED)如果行数信息剥离,置1,对可执行文件无效。

Bit 3 (IMAGE_FILE_LOCAL_SYMS_STRIPPED) 如果没有本地符号信息该位置1。对可执行文件无效。

Bit 4 (IMAGE_FILE_AGGRESIVE_WS_TRIM) 如果操作系统被假定通过页换出抢占式修剪进程的工作集(进程使用的内存数),该位置1。

Bits 7 (IMAGE_FILE_BYTES_REVERSED_LO)和15(IMAGE_FILE_BYTES_REVERSED_HI) 如果文件的的endianess不是机器期望的,则置1,于是读之前必须交换字节。对可执行文件不可靠。

Bit 8 (IMAGE_FILE_32BIT_MACHINE) 如果机器被期望是32位机器,置1。

Bit 9 (IMAGE_FILE_DEBUG_STRIPPED)如果没有调试信息在文件内,置1。对可执行文件无效。

Bit 10 (IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP) 如果程序不能够从可移动媒体比如软盘或光驱,置1。操作系统建议拷贝文件到交换文件然后执行。

Bit 11 (IMAGE_FILE_NET_RUN_FROM_SWAP) 如果不能够才网络运行,置1。操作系统建议拷贝文件到交换文件然后执行。

Bit 12 (IMAGE_FILE_SYSTEM) 如果文件是类似驱动程序的系统文件,置1。对执行文件无效。

Bit 13 (IMAGE_FILE_DLL) 如果文件是DLL,置1.

Bit 14 (IMAGE_FILE_UP_SYSTEM_ONLY) 如果文件不是为多处理器设计的,置1 。



相对虚拟地址Relative Virtual Addresses

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

PE格式使用所谓的RVA。用于描述内存地址,如果你不知道基地址的话。需要你加上基地址得到线性地址。基地址是PE映像加载的地址。例如:假如可执行文件加载到0x400000,可执行文件的RVA是0x1560. 有效执行起始地址为0x401560.如果被加载到0x100000,则执行起始位置在0x101560.



情况变的复杂起来,由于节不必按照加载的映像那样对齐。例如,节一般按照512字节对齐。加载的映像可能按照4096字节对齐。参看'SectionAlignment' and 'FileAlignment' 。对齐的意思就是地址值=对齐长度的倍数。



于是为了找到一个特殊的RVA指向的信息,你必须计算偏移量好象文件被加载一样。假如知道执行起点在RVA 0x1560, 想从这反汇编代码。要找到文件内的地址,你必须找出在RAM内的按照4096对齐的节,".code"节自内存RVA 0x1000开始,16384字节长,你知道RVA 0x1560的偏移量在那个节内是0x560.找出节在文件内按照512字节对齐,且".code"从0x800开始,那么在文件内的代码执行起点是0x800 0x560=0xd60。

然后反汇编,并发现一个存取地址0x1051d0处的变量.线性地址在加载执行文件时重新分配,并给出优先加载地址。你发现优先加载地址是0x100000,于是我们处理RVA 0x51d0. 这是一个开始于RVA 0x5000的数据区,2048字节长。它开始于文件偏移量0x4800.变量可以在文件偏移量0x4800 0x51d0-0x5000=0x49d0处发现。



可选头Optional Header

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

紧跟在文件头的后面是IMAGE_OPTIONAL_HEADER,尽管名字是可选,实际一直存在。包含关于如何精确处理PE文件的信息。从上到下介绍成员。

IMAGE_OPTIONAL_HEADER32 STRUCT

Magic WORD ?

MajorLinkerVersion BYTE ?

MinorLinkerVersion BYTE ?

SizeOfCode DWORD ?

SizeOfInitializedData DWORD ?

SizeOfUninitializedData DWORD ?

AddressOfEntryPoint DWORD ?

BaseOfCode DWORD ?

BaseOfData DWORD ?

ImageBase DWORD ?

SectionAlignment DWORD ?

FileAlignment DWORD ?

MajorOperatingSystemVersion WORD ?

MinorOperatingSystemVersion WORD ?

MajorImageVersion WORD ?

MinorImageVersion WORD ?

MajorSubsystemVersion WORD ?

MinorSubsystemVersion WORD ?

Win32VersionValue DWORD ?

SizeOfImage DWORD ?

SizeOfHeaders DWORD ?

CheckSum DWORD ?

Subsystem WORD ?

DllCharacteristics WORD ?

SizeOfStackReserve DWORD ?

SizeOfStackCommit DWORD ?

SizeOfHeapReserve DWORD ?

SizeOfHeapCommit DWORD ?

LoaderFlags DWORD ?

NumberOfRvaAndSizes DWORD ?

DataDirectory IMAGE_DATA_DIRECTORY IMAGE_NUMBEROF_DIRECTORY_ENTRIES dup(<> )

IMAGE_OPTIONAL_HEADER32 ENDS

IMAGE_OPTIONAL_HEADER equ



第1个16位字是'Magic',总是0x010b.



下面2个字节是连接器的版本号'MajorLinkerVersion'和'MinorLinkerVersion',这些值都不可靠,不能总是妥当的反映连接器版本。有些连接器不设置该域。



下面3个longwords(32位)指定执行代码尺寸('SizeOfCode'),初始化数据尺寸

'SizeOfInitializedData', 所谓的数据段"data segment", 未初始化数据尺寸

'SizeOfUninitializedData',所谓的"bss segment".这些数值也不可靠。



往下一个32位的RVA.是入口点的偏移量。('AddressOfEntryPoint').执行从此开始。



下面2个32位是可执行代码('BaseOfCode')和初始化数据('BaseOfData')的RVAs 我们对它没有兴趣,因为可以通过节来查看更可靠的信息。非初始化数据没有RVA。



下面是一个32位值,ImageBase'作为整个文件的优先加载地址,包括所有头在内。该值总是

64KB的倍数,文件已经被连接器重定位,如果文件能够真正加载到这个地址,加载器不必重定位文件。如果另一个映像已经被加载到那个地址,则优先地址不可使用。这种情况下,映像被加载到其他地址,需要重定位。如果映像是DLL,还有更多结果,因为"bound imports"不再有效,需要对使用DLL的执行文件进行修正。参见'import directory' 。



下面2个32位是当映像文件加载后,PE文件的节在内存内的对齐,'SectionAlignment', 以及在文件内的对齐'FileAlignment'. 一般文件对齐是512,节对齐是4096.



下面2个16位的字是期望的操作系统版本,'MajorOperatingSystemVersion'和 'MinorOperatingSystemVersion'。



下面2个16位的字是期望的可执行文件版本,'MajorImageVersion'和

'MinorImageVersion'. 许多连接器不正确设置这些信息。



下面2个16位的字是期望的子系统版本,'MajorSubsystemVersion和MinorSubsystemVersion. 这个必须是Win32版本或者POSIX版本。该版本需要正确提供,因为它被检查并使用。如果程序是Win32-GUI并运行在NT4,子系统版本不是4.0,对话框不是3D效果。



然后是Win32VersionValue,32位。大部分情况下是0。



下面是32位的映像需要的内存数量'SizeOfImage'.是所有的头和节的总和,如果节已经对齐。它是给加载器的线索,需要多少页加载映像。



下面一个是32位的所有头的总和,包括数据目录和节头。'SizeOfHeaders'.它也是才文件开始到第一节的偏移量。



然后是32位的校验码'CheckSum'.对当前版本的NT,只校验映像是否是NT驱动程序。对于其他可执行文件类型,不必提供这个码,可能为0。



然后是16的子系统Subsystem'表明在什么系统上运行:

IMAGE_SUBSYSTEM_NATIVE (1)执行文件不需要子系统,用于驱动程序。

IMAGE_SUBSYSTEM_WINDOWS_GUI (2)映像是Win32图形程序可以打开控制台

IMAGE_SUBSYSTEM_WINDOWS_CUI (3)映像是Win32控制台程序,可以得到缺省控制台。

IMAGE_SUBSYSTEM_OS2_CUI (5)映像是OS/2 控制台,程序是OS/2格式。

IMAGE_SUBSYSTEM_POSIX_CUI (7)映像使用POSIX控制台子系统

Windows 95可执行文件总是使用Win32 subsystem,于是合法值是2和3。



下面是16位,DllCharacteristics,表明是否是DLL,如果0位置1,DLL被通知进程结合。位1置1,DLL被通知线程脱离。位2置1,DLL被通知线程结合。位3置1,DLL被通知进程脱离。



下面4个 32 位预留堆栈大小'SizeOfStackReserve',提交的堆栈大小'SizeOfStackCommit',预留的堆的大小'SizeOfHeapReserve'和提交的的堆的的大小'SizeOfHeapCommit'.



预留数量是地址空间不是真实的RAM,程序启动时,提交的数量是真正的分配的内存。这个值也是堆和栈根据需要增长的一个数量。

例如:一个程序预留1 MB的堆并提交的堆时64KB,该堆就从64KB开始,并保证可以加大到1MB.堆将以64KB块增长。该堆在这里是主要堆,默认堆。一个进程可以创建多个堆如果需要的话。栈是第一个线程的栈,进程可以创建许多线程,每个都有自己的堆栈,DLLs 没有栈或者堆,于是在其映像内该值被忽略。



下面是32位的LoaderFlags, 没有用。



然后是32位的NumberOfRvaAndSizes,在随后的目录内的有效项目数。最好使用

IMAGE_NUMBEROF_DIRECTORY_ENTRIES,即16。



下面是具有IMAGE_NUMBEROF_DIRECTORY_ENTRIES(16)个成员的IMAGE_DATA_DIRECTORYs结构数组.



IMAGE_DATA_DIRECTORY STRUCT

VirtualAddress DWORD ?

isize DWORD ?

IMAGE_DATA_DIRECTORY ENDS



每个目录描述了节内特定信息位置,32 bits RVA VirtualAddress 和尺寸32 bit,各个成员索引如下(括号内为索引值):

IMAGE_DIRECTORY_ENTRY_EXPORT (0)输出符号目录用于DLL

IMAGE_DIRECTORY_ENTRY_IMPORT (1)输入符号目录

IMAGE_DIRECTORY_ENTRY_RESOURCE (2)资源目录

IMAGE_DIRECTORY_ENTRY_EXCEPTION (3)异常目录

IMAGE_DIRECTORY_ENTRY_SECURITY (4)安全目录

IMAGE_DIRECTORY_ENTRY_BASERELOC (5)重定位表

IMAGE_DIRECTORY_ENTRY_DEBUG (6)调试目录

IMAGE_DIRECTORY_ENTRY_COPYRIGHT (7)描述版权串

IMAGE_DIRECTORY_ENTRY_GLOBALPTR (8)机器值

IMAGE_DIRECTORY_ENTRY_TLS (9)Thread local storage目录

IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG (10)Load configuration 目录

IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (11)Bound import directory目录

IMAGE_DIRECTORY_ENTRY_IAT (12)Import Address Table输入地址表目录



例如,如果我们找到索引7的2个值0x12000和33, 加载地址是0x10000, 我们知道版权数据是在 0x10000 0x12000,版权字数为33。如果一个特定类型的目录没有被使用,地址和尺寸都为0。



节目录Section directories

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



节包含2个部分:节头IMAGE_SECTION_HEADER,节数据。在数据目录之后,我们看到一个具有NumberOfSections个节头成员的数组,按RVA排序。



节头包括:

IMAGE_SECTION_HEADER STRUCT

Name1 db IMAGE_SIZEOF_SHORT_NAME dup(?)

union Misc

PhysicalAddress dd ?

VirtualSize dd ?

ends

VirtualAddress dd ?

SizeOfRawData dd ?

PointerToRawData dd ?

PointerToRelocations dd ?

PointerToLinenumbers dd ?

NumberOfRelocations dw ?

NumberOfLinenumbers dw ?

Characteristics dd ?

IMAGE_SECTION_HEADER ENDS





IMAGE_SIZEOF_SHORT_NAME(8)个字节的数组,组成节的名字。如果所有8个字节被用掉,没有0做为结尾。典型的名字如".data"或者".text"或者".bss". 没有必要前导'.',可以是是"CODE"或 "IAT" .注意名字不全部跟节内容有关。一个".code"节可能或没有可能包括可执行代码,可能只包括输入地址表。可能包含代码和地址表和初始化数据。要找到在节内的信息,必须通过“可选头”内的数据目录查找他们。不要依赖名字,不要假定节的原始数据起始于节的开始。


最新评论

QQ|小黑屋|最新主题|手机版|微赢网络技术论坛 ( 苏ICP备08020429号 )

GMT+8, 2024-9-30 01:35 , Processed in 0.226590 second(s), 12 queries , Gzip On, MemCache On.

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

返回顶部