PE文件头解析大全

合集下载

PE文件结构详解 超详细 C代码

PE文件结构详解 超详细 C代码

翻译:Jason Sun(木水鱼).2004年5月10日[译注:仅供大家学习使用,您在复制或使用此文档时请保留这个文件头]Peering Inside the PE: A Tour of the Win32 Portable Executable File FormatMatt Pietrek1994 年3月Matt Pietrek 是Windows Internals (Addison-Wesley, 1993)的作者。

他就职于Nu-Mega 技术有限公司,可通过CompuServe: 71774,362联系到他。

这篇文章出自1994年3月发行的Microsoft系统期刊。

版权所有﹫1994 Miller Freeman, Inc.保留所有权利。

未经Miller Freeman同意,这篇文章的任何部分不得以任何形式被复制(除了在评论文章里以摘要引用)。

一个操作系统的可执行文件的格式在很多方面是这个操作系统的一面镜子。

虽然学习一个可执行文件格式不是大多数程序员的首要任务,但是从中你可学到大量的知识。

这篇文章中,我将给出Microsoft为他们的基于Win32的系统所设计的PE文件格式的详细说明。

可以预知在未来,PE文件格式在Microsoft的所有操作系统包括Windows 2000中都将扮演着很重要的角色。

如果你在使用Win32s或WinNT,那么你已经在使用PE文件了。

甚至你只是在Windows3.1下用Visual C++编程,你也已在使用PE文件了(Visual C++的32位DOS扩展组件使用此格式)。

简而言之,PE格式已得到普遍应用并且在不短的将来也不会取消。

现在是时间找出这种新的可执行文件格式为操作系统所带来的影响了。

我不会让你盯住无穷无尽的16进制Dumps和详细讨论页面中每个单独位的重要性。

代替的,我将介绍PE文件格式中内含的概念并且把它们和你每天都会遇到的东西联系起来。

例如,线程局部变量的概念,比如declspec(thread) int i;它使我快要发疯了,直到我明白它是怎样在可执行文件里优雅而简单的实现的。

PE格式揭秘

PE格式揭秘

这是我在我的电脑上随便找了个exe文件,下面我将会仔细的分析一下这种文件的格式下面我依次截图,仔细分析,让你看清楚:首先4D 5A 90 这三个一般是在一起的,我们知道windows文件是从DOS兼容过来的,这三个数其实就是M Z 和一个分隔符是为了兼容以前DOS下的MZ文件好:我找了两个文件通过比较发现,从0x 00 00 00 80处开始不一样,也就是在这之前从0x 00 00 00 00到0x00 00 00 7F是相同的DOS头和DOS桩程序,其实,MZ_DOS在PE 文件中占64个字节,就是我们看到的前4行数据(每行16个字节),而在微软给我们提供的DOS头结构体中有各个部分的结构:Typedef struct _IMAGE_DOS_HEADER{USHORT e_magic; //00H魔术数字就是0x 4D 5A = MZ;USHORT e_cblp; // 02H 表示的是文件最后页(page)中的字节数;USHORT e_cb ; / 04H 文件的页数(每页大小4KB);USHORT e_crlc ; //06H重新定向的元素个数;USHORT e_parhdr //08H 头部大小,以段(paragraph)为单位;USHORT e_minalloc //0AH 所需要的最小附加段;USHORT e_maxalloc //0CH 所需要的最大附加段;USHORT e_ss //0EH 初始的ss值;USHORT e_sp //10H 初始的sp值;USHORT e_csum //12H 校验和或者0USHORT e_ip //14H 初始的IP值USHORT e_cs //16H 初始的CS值(相对偏移量)USHORT e_lfarlc //18H 重定向表文件地址USHORT e_ovno //1AH 覆盖号USHORT e_res[4] 1CH 保留字USHORT e_oemid //24H OEM标示符(for e_oeminfo)USHORT e_oeminfo //26H OEM信息USHORT e_res2[10] //28H 保留LONG e_lfanew; //3CH PE头位置//这个位置很重要,做病毒要这个东西} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER上边就是我的一个exe文件的DOS头(不包括DOS桩,紧跟在它的下面),下面是我的分析0x4D 5A 魔术字符MZ0x00 90(144字节) 文件最后一页的字节数地址为20x00 03 文件总页数用这两个计算出文件总大小(3-1)*4028+144=82000x00 00 重定向元素个数0x00 04 头部大小,以段为单位0x00 00 所需要的最小附加段0xFF FF 所需要的最大附加段0x00 00 加载时ss段寄存器的值0x00 B8 加载时sp的值0x00 00 校验和或者为00x00 00 初始IP值0x00 00 初始CS值0x00 40 重定位表文件地址0x00 00 覆盖号0x0000 0000 0000 0000保留的四个字0x00 00 OEM标识0x00 00 OEM信息0x0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 保留10个字0x00 00 00 D0 PE头开始的地址地址为3C上边指需要记住两处,但是别的地方最好是也记住。

PE文件解析-PE头解析-1-文件头,PE头

PE文件解析-PE头解析-1-文件头,PE头

PE⽂件解析-PE头解析-1-⽂件头,PE头PE⽂件解析-PE头解析PE头⼜叫NT头,是PE⽂件真正的头部,DOS头只是⽤来为了兼容以前的DOS系统。

PE头位于DOS Stub后⾯,以PE00作为起始标记类似于DOS头的MZDOS StubDOS Stub就是DOS头结束到PE头的开始中间的区域,基本上是垃圾区域没啥⽤,但是加壳的时候可以⽤到。

NT/PE头typedef struct _IMAGE_NT_HEADERS {DWORD Signature;//PE标志,就是PE00IMAGE_FILE_HEADER FileHeader;//⽂件头,是另⼀个⽂件的结构体IMAGE_OPTIONAL_HEADER32 OptionalHeader;//可选PE头,也叫扩展头} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;⽂件头typedef struct _IMAGE_FILE_HEADER {WORD Machine;//程序允许的CPU型号,如果为0表⽰能在任何CPU上运⾏,如果是0x14c表⽰的是386以及后续的型号WORD NumberOfSections;//⽂件中存在的区段的数量DWORD TimeDateStamp;//时间戳DWORD PointerToSymbolTable;DWORD NumberOfSymbols;WORD SizeOfOptionalHeader;//可选PE头的⼤⼩32位默认是E0,64位默认是F0WORD Characteristics;//⽂件属性,每个位有不同的含义,} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;代码解析。

PE文件头简析及输入表、输出表的分析

PE文件头简析及输入表、输出表的分析

PE文件头简析及输入表、输出表的分析2008年01月13日星期日 22:50PE文件头简析及输入表、输出表的分析前两天由于做个免杀,需要对输入表做些改变,所以又重新看了pe文件的结构尤其是输入表及输出表的一些细节方面,做个笔记,方便自己以后翻看,也给大家提个方便,呵呵!1、PE文件格式文件尾___________________________________________________________________Code View调试信息 |COFF 符号表 | 调试信息COFF 行号 |------------------------------------------------------------------.reloc |.edata | 块(Section) .data |.text |---------------------------------------------------------------------------------------------------IMAGE_SECTION_HEADER |IMAGE_SECTION_HEADER | 块表(Section Table)IMAGE_SECTION_HEADER |IMAGE_SECTION_HEADER | //对应.text块---------------------------------------------------------------------------------------------------------数据目录表 | //包含在可选映像头中,包含输出\入表信息 IMAGE_OPTIONAL_HEADER32 | 可选映像头IMAGE_FILE_HEADER | 文件映像头"PE",0,0 | pe文件标识(pe文件头包含三个部分)---------------------------------------------------------------------------------------------------------DOS stub |DOS 'MZ' HEADER | 该字段中的e_lfanew字段指向pe头 ---------------------------------------------------------------------------------------------------------文件头说明:01、输入表、输出表的位置:在pe头中可选映像头字段中数据目录表字段中,相对于pe文件标识处的偏移分别为:+80h、+78h;(其中pe文件标识的位置在DOS 'MZ' HEADER字段中的e_lfanew指出,该字段相对于文件开始的偏移为+3Ch,4个字节);02、在用例子说明时用c32asm打开,其中的偏移均为文件在磁盘上存储的物理偏移,而查看的值均为给出的各个地址的RVA,转化方法入下:查到的RVA值-VOffset(可由lordpe查看)+ROffset 得到的就是可以c32asm中的偏移2、上面对pe文件格式做了简要介绍,下面主要分析输入表和输出表:输入表篇:概念不做介绍,位置上面已经给出,呵呵输入表的结构:输入表是以一个IMAGE_IMPORT_DESCRIPTOR(IID)数组开始,一个程序要调用几个dll就会有几个IID项,即每个IID对应于一个dllIID结构:IMAGE_IMPORT_DESCRIPTOR structunion{DWORD Characteristics ; ;00hDWORD OriginalFirstThunk; // 注释1};TimeDateStamp DWORD ;04h // 时间标志,可以忽略;ForwarderChain DWORD ;08h // 正向链接索引,一般为0,当程序引用一个dll中的api,而这个api又引用其它dll中的api时用;Name DWORD ;0Ch //DLL名字的指针,以00结尾的ASCII字符的RVA地址;FirstThunk DWORD ;10h // 注释2IMAGE_IMPORT_DESCRIPTOR ends注释 1:该值为一个 IMAGE_THUNK_DATA数组的RVA,其中的每个指针都指向IMAGE_IMPORT_BY_NAME结构。

深入剖析PE文件

深入剖析PE文件

深入剖析PE文件PE文件是Win32的原生文件格式.每一个Win32可执行文件都遵循PE文件格式.对PE文件格式的了解可以加深你对Win32系统的深入理解.一、基本结构。

上图便是PE文件的基本结构。

(注意:DOS MZ Header和部分PE header的大小是不变的;DOS stub部分的大小是可变的。

)一个PE文件至少需要两个Section,一个是存放代码,一个存放数据。

NT上的PE文件基本上有9个预定义的Section。

分别是:.text, .bss, .rdata, .data, .rsrc, .edata, .idata, .pdata, 和.debug。

一些PE文件中只需要其中的一部分Section.以下是通常的分类:l 执行代码Section , 通常命名为:.text (MS) or CODE (Borland)l 数据Section, 通常命名为:.data, .rdata, 或.bss(MS) 或DATA(Borland).资源Section, 通常命名为:.edatal 输入数据Section, 通常命名为:.idatal 调试信息Section,通常命名为:.debug这些只是命名方式,便于识别。

通常与系统并无直接关系。

通常,一个PE文件在磁盘上的映像跟内存中的基本一致。

但并不是完全的拷贝。

Windows加载器会决定加载哪些部分,哪些部分不需要加载。

而且由于磁盘对齐与内存对齐的不一致,加载到内存的PE文件与磁盘上的PE文件各个部分的分布都会有差异。

当一个PE文件被加载到内存后,便是我们常说的模块(Module),其起始地址就是所谓的HModule.二、DOS头结构。

所有的PE文件都是以一个64字节的DOS头开始。

这个DOS头只是为了兼容早期的DOS操作系统。

这里不做详细讲解。

只需要了解一下其中几个有用的数据。

1. e_magic:DOS头的标识,为4Dh和5Ah。

分别为字母MZ。

pe文件简单解释

pe文件简单解释
IMAGE_FILE_AGGRESIVE_WS_TRIM = $0010; // Agressively trim working set
IMAGE_FILE_LARGE_ADDRESS_AWARE = $0020; // App can handle >2gb addresses
IMAGE_FILE_BYTES_REVERSED_LO = $0080; // Bytes of machine word are reversed.
IMAGE_FILE_MACHINE_WCEMIPSV2 = $0169; // MIPS little-endian WCE v2
IMAGE_FILE_MACHINE_ALPHA = $0184; // Alpha_AXP
IMAGE_FILE_MACHINE_SH3 = $01a2; // SH3 little-endian
IMAGE_FILE_MACHINE_SH3E = $01a4; // SH3E little-endian
IMAGE_FILE_MACHINE_SH4 = $01a6; // SH4 little-endian
IMAGE_FILE_MACHINE_SH5 = $01a8; // SH5
IMAGE_FILE_MACHINE_MIPSFPU16 = $0466; // MIPS
// IMAGE_FILE_MACHINE_AXP64 IMAGE_FILE_MACHINE_ALPHA64
IMAGE_FILE_MACHINE_AMD64 = $0500; // AMD K8
IMAGE_FILE_MACHINE_TRICORE = $0520; // Infineon
// If Image is on removable media, copy and run from the swap file.

PE文件结构解析

PE文件结构解析

PE⽂件结构解析说明:本⽂件中各种⽂件头格式截图基本都来⾃看雪的《加密与解密》;本⽂相当《加密与解密》的阅读笔记。

1.PE⽂件总体结构PE⽂件框架结构,就是exe⽂件的排版结构。

也就是说我们以⼗六进制打开⼀个.exe⽂件,开头的那些内容就是DOS头内容,下来是PE头内容,依次类推。

如果能认识到这样的内含,那么“exe开头的内容是不是就直接是我们编写的代码”(不是,开头是DOS头内容)以及“我们编写的代码被编排到了exe⽂件的哪⾥”(在.text段,.text具体地址由其相应的IMAGE_SECTION_HRADER指出)此类的问题答案就显⽽易见了。

exe⽂件从磁盘加载到内存,各部份的先后顺序是保持不变的,但由于磁盘(⼀般200H)和内存(⼀般1000H)区块的对齐⼤⼩不⼀样,所以同⼀内容在磁盘和在内存中的地址是不⼀样的。

换⾔之你在磁盘上看到⼀段内容⼀内容要到在内存中找到它--假设它是能映射到内容的部份--那么要做相应的地址转换。

(⽐如你在Ultraedit 中看到某⼏个字节⽽想在OllyDbg中找到这⼏个字节那么需要进⾏地址转换)另外要注意,PE⽂件中存放的地址值都是内存中的地址,这些地址在OllyDbg中不需要转换到其指定的位置就能找到其指向的内容;这要根据这个地址找到内容在Ultraedit的地址,需要将此RVA址转换成⽂件偏移地址。

还要注意DOS头/PE头/块表,映射到内存时属同⼀区块⽽且是第⼀区块,所以此三者上的RVA和⽂件偏移地址是相等的。

2.DOS头部2.1MS-DOS头部(IMAGE_DOS_HEADER)最后的e_lfanew即是PE⽂件的RVA地址我们在前边已经提过,对于DOS头/PE头/区块表三部分RVA和⽂件偏移地址是相等的,所以上边在⼗六进制⽂本编缉器中,直接转向e_lfanew指向的000000B0可以正好找到PE头。

2.2DOS stubDOS stub是当操作系统不⽀持PE⽂件时执⾏的部分,⼀般由编译器⾃⼰⽣成内容是输出“This program cannot be run in MS-DOS mode”等提⽰。

PE文件格式详解(一)

PE文件格式详解(一)

PE文件格式详解(一)0x00 前言PE文件是portable File Format(可移植文件)的简写,我们比较熟悉的DLL和exe文件都是PE文件。

了解PE文件格式有助于加深对操作系统的理解,掌握可执行文件的数据结构机器运行机制,对于逆向破解,加壳等安全方面方面的同学极其重要。

接下来我将通过接下来几篇详细介绍PE文件的格式。

0x01 基本概念PE文件使用的是一个平面地址空间,所有代码和数据都被合并在一起,组成一个很大的组织结构。

文件的内容分割为不同的区块(Setion,又称区段,节等),区段中包含代码数据,各个区块按照页边界来对齐,区块没有限制大小,是一个连续的结构。

每块都有他自己在内存中的属性,比如:这个块是否可读可写,或者只读等等。

认识PE文件不是作为单一内存映射文件被装入内存是很重要的,windows加载器(PE加载器)便利PE文件并决定文件的哪个部分被映射,这种映射方式是将文件较高的偏移位置映射到较高的内存地址中。

当磁盘的数据结构中寻找一些内容,那么几乎能在被装入到内存映射文件中找到相同的信息。

但是数据之间的位置可能改变,其某项的偏移地址可能区别于原始的偏移位置,不管怎么样,所表现出来的信息都允许从磁盘文件到内存偏移的转换,如下图:PS:PE文件头以下的地址无论在内存映射中还是在磁盘映射中都是一样的,当内存分页和磁盘分页一致时无需进行地址转换,只有当磁盘分页和内存分页不一样时才要进行地址转化,这点很重要,拿到PE文件是首先查看分页是否一致。

前两天一直没碰到内存和磁盘分页不一样的,所以这个点一直没发现,今天特来补上。

下面要介绍几个重要概念,分别是基地址(ImageBase),相对虚拟地址(Relative Virtual Address),文件偏移地址(File Offset)。

1)基地址定义:当PE文件通过Windows加载器被装入内存后,内存中的版本被称作模块(Module)。

映射文件的起始地址被称作模块句柄(hMoudule),可以通过模块句柄访问其他的数据结构。

PE文件解析-各种头解析代码实现

PE文件解析-各种头解析代码实现

PE⽂件解析-各种头解析代码实现 1 #ifndef __CPEUTIL_H__2#define __CPEUTIL_H__3 #include<Windows.h>4 #include<iostream>5class CPeUtil6 {7public:8 CPeUtil();9 ~CPeUtil();10//加载⽂件11 BOOL LoadFile(const char* patch);12//初始化PE信息13 BOOL InitPeInfo();14//遍历区段头15void PrintSectionHeaders();16private:17//创建缓冲区来存储⽂件信息18char* FileBuff;19//获取⽂件⼤⼩20 DWORD FileSize;21 PIMAGE_DOS_HEADER pDosHeader;22 PIMAGE_NT_HEADERS pNtHeader;23 PIMAGE_FILE_HEADER pFileHeader;24 PIMAGE_OPTIONAL_HEADER pOptionalHeader;25 };26#endif1 #include"CpeUtil.h"2 CPeUtil::CPeUtil()3 {4this->FileBuff = NULL;5this->FileSize = 0;6this->pDosHeader = NULL;7this->pFileHeader = NULL;8this->pOptionalHeader = NULL;9this->pNtHeader = NULL;10 }1112 CPeUtil::~CPeUtil()13 {14if (FileBuff != NULL)15 {16delete[]FileBuff;17 FileBuff = NULL;18 }19 }2021 BOOL CPeUtil::LoadFile(const char* patch)22 {23//拿到⽂件句柄24 HANDLE hFile = CreateFileA(patch,25 GENERIC_READ,260x00000000,27 NULL,28 OPEN_EXISTING,29 FILE_ATTRIBUTE_NORMAL,30 NULL31 );32if (hFile == 0)33 {34 std::cout << "打开⽂件失败" << std::endl;35return false;36 }37//获取⽂件⼤⼩38this->FileSize = GetFileSize(hFile, NULL);3940//创建缓冲区41this->FileBuff = new char[this->FileSize]{ 0 };4243 DWORD RealSize = 0;44//复制⽂件内容给缓冲区45 BOOL ret = ReadFile(hFile,46this->FileBuff,47this->FileSize,48 &RealSize,49 NULL50 );51if (ret == 0)52 {53 std::cout << "读取⽂件失败" << std::endl;54return false;55 }5657 std::cout << "实际读取的⽂件⼤⼩为:" << RealSize << std::endl;58//初始化PE⽂件结构体59if (InitPeInfo())60 {61//读取完⽂件后关闭掉⽂件句柄62 CloseHandle(hFile);63return true;64 }6566return false;67 }686970 BOOL CPeUtil::InitPeInfo()71 {72this->pDosHeader = (PIMAGE_DOS_HEADER)this->FileBuff;73if(this->pDosHeader->e_magic != 0x5A4D)74 {75 std::cout << "不是有效的PE⽂件" << std::endl;76return false;77 }78this->pNtHeader = (PIMAGE_NT_HEADERS)(this->FileBuff + this->pDosHeader->e_lfanew); 79if (this->pNtHeader->Signature != IMAGE_NT_SIGNATURE)80 {81 std::cout << "不是有效的PE⽂件" << std::endl;82return false;83 }84this->pFileHeader = (PIMAGE_FILE_HEADER)&this->pNtHeader->FileHeader;85this->pOptionalHeader = (PIMAGE_OPTIONAL_HEADER)&this->pNtHeader->OptionalHeader; 86return true;87 }88void CPeUtil::PrintSectionHeaders()89 {90 PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(pNtHeader);91for (int i = 0; i < this->pFileHeader->NumberOfSections; i++)92 {93char name[9]{ 0 };94for (int i = 0; i < 8; i++)95 {96 name[i] = pSectionHeader->Name[i];97 }98 std::cout << "区段名称:" << name<<std::endl;99 pSectionHeader++;100 }101102103 }1 #include"CpeUtil.h"23int main()4 {5 CPeUtil peUtil;6 BOOL ifSuccess = peUtil.LoadFile("E:\\Project_Sum\\CC++\\test\\test\\Thread_syn.exe");7if (ifSuccess)8 {9 peUtil.PrintSectionHeaders();10return0;11 }12 std::cout << "加载PE⽂件失败" << std::endl;13return0;14 }。

PE文件详解1——PE文件头部解析

PE文件详解1——PE文件头部解析

PE⽂件详解1——PE⽂件头部解析参考书籍:《WindowsPE⽂件权威指南》MSDN中winnt.h是PE⽂件定义的最终决定者。

EXE⽂件与DLL⽂件之间的区别完全是语义上的,⼆者PE结构完全相同。

唯⼀区别⽤⼀个字段标⽰处这个⽂件是exe还是dll。

许多DLL扩展,如OCX控件,控制⾯板等都是DLL,它们有⼀样的实体。

64位的Windows只是对PE格式做了⼀些简单的修饰,新格式叫PE32+。

没有新的结构加进去,其余的改变只是简单地将以前的32位字段扩展为64位字段。

1.PE⽂件基本结构:PE⽂件的头分为DOS头、NT头、节头。

注意,这是本⼈的分法。

这样分法会更加合理,更易理解。

因为这三个部分正好构成SizeOfHeaders所指的范围,所以将它们合为“头”。

2.⽂件头2.1 DOS头⽤记事本打开任何⼀个镜像⽂件,其头2个字节必为字符串“MZ”,这是Mark Zbikowski的姓名缩写,他是最初的MS-DOS设计者之⼀。

然后是⼀些在MS-DOS下的⼀些参数,这些参数是在MS-DOS下运⾏该程序时要⽤到的。

在这些参数的末尾也就是⽂件的偏移0x3C(第60字节)处是是⼀个4字节的PE⽂件签名的偏移地址。

该地址有⼀个专⽤名称叫做“E_lfanew”。

这个签名是“PE00”(字母“P”和“E”后跟着两个空字节)。

紧跟着E_lfanew的是⼀个MS-DOS程序。

那是⼀个运⾏于MS-DOS下的合法应⽤程序。

当可执⾏⽂件(⼀般指exe、com⽂件)运⾏于MS-DOS下时,这个程序显⽰“This program cannot be run in DOS mode(此程序不能在DOS模式下运⾏)”这条消息。

⽤户也可以⾃⼰更改该程序,有些还原软件就是这么⼲的。

同时,有些程序既能运⾏于DOS⼜能运⾏于Windows下就是这个原因。

Notepad.exe整个DOS头⼤⼩为224个字节,⼤部分不能在DOS下运⾏的Win32⽂件都是这个值。

PE文件结构详解--输出表--对照《加密与破解》第十章

PE文件结构详解--输出表--对照《加密与破解》第十章

MajorOp MinorOp MajorIm MinorIma MajorSub MinorSu Win32VersionVal
IMAGE_OPTIONAL_HEADER32
00000150
IMAGE_OPTIONAL_HEADER32
PE文件头Leabharlann IMAGE_OL_HEADER32
IMAGE_NT_HEADERS 区块表头部
PointerToRawData PointerToRelocat PointerToLinenu IMAGE_SECTION_HEADER
00000290
00 00 00 00 40 00 00 40
NumberO NumberO Characteristics IMAGE_SECTION_HEADER
区块表
00000250
00 10 00 00 00 30 00 00
VirtualSize
00 02 00 00 00 0A 00 00
PointerToRawData
VirtualAddress SizeOfRawData IMAGE_SECTION_HEADER
00000260
00 00 00 00 00 00 00 00 2E 65 64 61 74 61 00 00
000001C0
IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG IMAGE_DIRECTORY_ENTRY_TLS DataDirectory
000001D0
IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT IMAGE_DIRECTORY_ENTRY_IAT DataDirectory
PointerToRowData Roffset C00h 从IMAGE_FILE_HEADER的B6h处NumberOfSections可知知道有5个块表: 从IMAGE_OPTIONAL_HEADER32的E8h处SectionAlignment可知块对齐大小为1000h 块表位于目录表之后:PE头100h+目录表最后偏移F7h=1F7

pe文件

pe文件
文件层次解释
A.
谢谢观看
Hale Waihona Puke pe文件可移植的可执行的文件
01 定义
03 PE首部
目录
02 相关概念 04 文件格式
PE文件的全称是Portable Executable,意为可移植的可执行的文件,常见的EXE、DLL、OCX、SYS、COM都 是PE文件,PE文件是微软Windows操作系统上的程序文件(可能是间接被执行,如DLL)
定义
一个操作系统的可执行文件格式在很多方面是这个系统的一面镜子。虽然学习一个可执行文件格式通常不是 一个程序员的首要任务,但是你可以从这其中学到大量的知识。在这篇文章中,我会给出 Microsoft的所有基于 win32系统(如winnt,win9x)的可移植可执行(PE)文件格式的详细介绍。在可预知的未来,包括Windows2000, PE文件格式在 MicroSoft的操作系统中扮演一个重要的角色。如果你在使用 Win32或 Winnt,那么你已经在使 用 PE文件了。甚至你只是在 Windows3.1下使用 Visual C++编程,你使用的仍然是 PE文件(Visual C++的 32位MS-DOS扩展组件用这个格式)。简而言之,PE格式已经普遍应用,并且在不短的将来仍是不可避免的。
和微软的其它可执行格式一样,你可以通过查找它的起始偏移来得到真实首部,这个偏移放在DOS残留首部 中。WINNT.H头文件包含了DOS残留程序的数据结构定义,使得很容易找到PE首部的起始位置。e_lfanew域是PE 真实首部的偏移。为了得到PE首部在内存中的指针,只需要把这个值加到映像的基址上即可。
线程局部变量
我最后不会让你盯住无穷无尽的十六进制Dump,也不会详细讨论页面的每一个单独的位的重要性。代替的, 我会向你介绍包含在 PE文件中的概念,并且将他们和你每天都遇到的东西联系起来。比如,线程局部变量的概 念,如下所述:

PE文件结构详解(三)PE导出表

PE文件结构详解(三)PE导出表

PE文件结构详解(三)PE导出表上篇文章PE文件结构详解(二)可执行文件头的结尾出现了一个大数组,这个数组中的每一项都是一个特定的结构,通过函数获取数组中的项可以用RtlImageDirectoryEntryToData函数,DataDirectory中的每一项都可以用这个函数获取,函数原型如下:PVOID NTAPI RtlImageDirectoryEntryToData(PVOID Base, BOOLEAN MappedAsImage, USHORT Directory, PULONG Size);Base:模块基地址。

MappedAsImage:是否映射为映象。

Directory:数据目录项的索引。

Size:对应数据目录项的大小,比如Directory为0,则表示导出表的大小。

返回值表示数据目录项的起始地址。

这次来看看第一项:导出表。

导出表是用来描述模块中的导出函数的结构,如果一个模块导出了函数,那么这个函数会被记录在导出表中,这样通过GetProcAddress函数就能动态获取到函数的地址。

函数导出的方式有两种,一种是按名字导出,一种是按序号导出。

这两种导出方式在导出表中的描述方式也不相同。

模块的导出函数可以通过Dependency walker工具来查看:上图中红框位置显示的就是模块的导出函数,有时候显示的导出函数名字中有一些符号,像 ??0CP2PDownloadUIInterface@@QAE@ABV0@@Z,这种是导出了C++的函数名,编译器将名字进行了修饰。

下面看一下导出表的定义吧:结构还算比较简单,具体每一项的含义如下:Characteristics:现在没有用到,一般为0。

TimeDateStamp:导出表生成的时间戳,由连接器生成。

MajorVersion,MinorVersion:看名字是版本,实际貌似没有用,都是0。

Name:模块的名字。

Base:序号的基数,按序号导出函数的序号值从Base开始递增。

PE文件学习笔记(一):DOS头与PE头解析

PE文件学习笔记(一):DOS头与PE头解析

PE⽂件学习笔记(⼀):DOS头与PE头解析在Windows下所谓PE⽂件即Portable Executable,意为可移植的可执⾏的⽂件。

常见的.EXE、.DLL、.OCX、.SYS、.COM都是PE⽂件。

PE⽂件有⼀个共同特点:前两个字节为4D 5A(MZ)。

如果⼀个⽂件前两个字节不是4D 5A则其肯定不是可执⾏⽂件。

⽐如⽤16进制⽂本编辑器打开⼀个“.xls”⽂件其前两个字节为:0XD0 0XCF;打开⼀个“.pdf”其前两个字节为:0X25 0X50。

PE⽂件结构:DOS头+PE头+节表+.data/.rdata/.text。

⽽今天我们就来具体了解⼀下PE⽂件的DOS头和PE头的结构成员与部分成员的作⽤。

注意:⼀个exe⽂件本⾝是⼀个PE⽂件,但是由于包含dll库,所以⼀个exe⽂件也是许多PE⽂件组成的(包含多个dll)⼀个PE⽂件。

1、DOS头:共40H(64字节)DOS头中声明⽤的寄存器(我们可以看到e_ss、e_sp、e_ip、e_cs还是16位的寄存器),所以在32位/64为系统中⽤到的只有两个成员了(第⼀个和最后⼀个):①e_magic:判断⼀个⽂件是不是PE⽂件;②e_lfanew:相对于⽂件⾸的偏移量,⽤于找到PE头;具体结构如下(前⾯的⼗六进制数表⽰该成员相对于结构的偏移量,WORD2字节变量、DWORD4字节变量)://注释掉的不需要重点分析struct _IMAGE_DOS_HEADER{0X00 WORD e_magic; //※Magic DOS signature MZ(4Dh 5Ah):MZ标记:⽤于标记是否是可执⾏⽂件//0X02 WORD e_cblp; //Bytes on last page of file//0X04 WORD e_cp; //Pages in file//0X06 WORD e_crlc; //Relocations//0X08 WORD e_cparhdr; //Size of header in paragraphs//0X0A WORD e_minalloc; //Minimun extra paragraphs needs//0X0C WORD e_maxalloc; //Maximun extra paragraphs needs//0X0E WORD e_ss; //intial(relative)SS value//0X10 WORD e_sp; //intial SP value//0X12 WORD e_csum; //Checksum//0X14 WORD e_ip; //intial IP value//0X16 WORD e_cs; //intial(relative)CS value//0X18 WORD e_lfarlc; //File Address of relocation table//0X1A WORD e_ovno; //Overlay number//0x1C WORD e_res[4]; //Reserved words//0x24 WORD e_oemid; //OEM identifier(for e_oeminfo)//0x26 WORD e_oeminfo; //OEM information;e_oemid specific//0x28 WORD e_res2[10]; //Reserved words0x3C DWORD e_lfanew; //※Offset to start of PE header:定位PE⽂件,PE头相对于⽂件的偏移量};我们查看下⾯所⽰unins000.exe⽂件的结构信息:64字节(共4⾏)的DOS头,第⼀个成员2个字节是可执⾏⽂件的标志信息;最后⼀个成员4字节是PE头的偏移地址为00000100H,我们可以根据00000100H来获取PE头的地址。

PE文件结构详解(二)可执行文件头

PE文件结构详解(二)可执行文件头

PE文件结构详解(二)可执行文件头在PE文件结构详解(一)基本概念里,解释了一些PE文件的一些基本概念,从这篇开始,将详细讲解PE文件中的重要结构。

了解一个文件的格式,最应该首先了解的就是这个文件的文件头的含义,因为几乎所有的文件格式,重要的信息都包含在头部,顺着头部的信息,可以引导系统解析整个文件。

所以,我们先来认识一下PE文件的头部格式。

还记得上篇里的那个图吗?DOS头和NT头就是PE文件中两个重要的文件头。

一、DOS头DOS头的作用是兼容MS-DOS操作系统中的可执行文件,对于32位PE文件来说,DOS所起的作用就是显示一行文字,提示用户:我需要在32位windows上才可以运行。

我认为这是个善意的玩笑,因为他并不像显示的那样不能运行,其实已经运行了,只是在DOS上没有干用户希望看到的工作而已,好吧,我承认这不是重点。

但是,至少我们看一下这个头是如何定义的:我们只需要关注两个域:e_magic:一个WORD类型,值是一个常数0x4D5A,用文本编辑器查看该值位‘MZ’,可执行文件必须都是'MZ'开头。

e_lfanew:为32位可执行文件扩展的域,用来表示DOS头之后的NT头相对文件起始地址的偏移。

二、NT头顺着DOS头中的e_lfanew,我们很容易可以找到NT头,这个才是32位PE文件中最有用的头,定义如下:下图是一张真实的PE文件头结构以及其各个域的取值:Signature:类似于DOS头中的e_magic,其高16位是0,低16是0x4550,用字符表示是 ‘ PE’。

IMAGE_FILE_HEADER是PE文件头,C语言的定义是这样的:每个域的具体含义如下:Machine:该文件的运行平台,是x86、x64还是I64等等,可以是下面值里的某一个。

umberOfSections:该PE文件中有多少个节,也就是节表中的项数。

TimeDateStamp:PE文件的创建时间,一般有连接器填写。

PE文件结构详解

PE文件结构详解

PE文件结构详解1 摘要Windows NT 3.1引入了一种名为PE文件格式的新可执行文件格式。

PE文件格式的规范包含在了MSDN的CD中(Specs and Strategy, Specifications, Windows NT File Format Specifications),但是它非常之晦涩。

然而这一的文档并未提供足够的信息,所以开发者们无法很好地弄懂PE格式。

本文旨在解决这一问题,它会对整个的PE文件格式作一个十分彻底的解释,另外,本文中还带有对所有必需结构的描述以及示范如何使用这些信息的源码示例。

为了获得PE文件中所包含的重要信息,我编写了一个名为PEFILE.DLL的动态链接库,本文中所有出现的源码示例亦均摘自于此。

这个DLL和它的源代码都作为PEFile示例程序的一部分包含在了CD中(译注:示例程序请在MSDN中寻找,本站恕不提供),你可以在你自己的应用程序中使用这个DLL;同样,你亦可以依你所愿地使用并构建它的源码。

在本文末尾,你会找到PEFILE.DLL的函数导出列表和一个如何使用它们的说明。

我觉得你会发现这些函数会让你从容应付PE文件格式的。

2 介绍Windows操作系统家族最近增加的Windows NT为开发环境和应用程序本身带来了很大的改变,这之中一个最为重大的当属PE文件格式了。

新的PE文件格式主要来自于UNIX操作系统所通用的COFF规范,同时为了保证与旧版本MS-DOS及Windows操作系统的兼容,PE文件格式也保留了MS-DOS中那熟悉的MZ头部。

在本文之中,PE文件格式是以自顶而下的顺序解释的。

在你从头开始研究文件内容的过程之中,本文会详细讨论PE文件的每一个组成部分。

很多解决PE文件格式的工作和直接观看数据有关。

例如,要弄懂导入地址名称表是如何构成的,我就得同时查看.idata段头部、导入映像数据目录、可选头部以及当前的.idata段实体,而EXEVIEW.EXE就是查看这些信息的最佳示例。

PE文件结构详解

PE文件结构详解
PE(Portable Execute)文件是Windows下可执行文件的总称,常见的有DLL,EXE,OCX,SYS等,事 实上,一个文件是否是PE文件与其扩展名无关,PE文件可以是任何扩展名。那Windows是怎么区分可执行 文件和非可执行文件的呢?我们调用LoadLibrary传递了一个文件名,系统是如何判断这个文件是一个合法 的动态库呢?这就涉及到PE文件结构了。 PE文件的结构一般来说如下图所示:从起始位置开始依次是DOS头,NT头,节表以及具体的节。
NumberOfSections:该PE文件中有多少个节,也就是节表中的项数。 TimeDateStamp:PE文件的创建时间,一般有连接器填写。 PointerToSymbolTable:COFF文件符号表在文件中的偏移。 NumberOfSymbols:符号表的数量。 SizeOfOptionalHeader:紧随其后的可选头的大小。 Characteristics:可执行文件的属性,可以是下面这些值按位相或。
这里写图片描述
这种表示方式叫做虚拟地址(VA)。也许有人要问,既然有VA这么简单的表示方式为什么还要有前面的 RVA呢?因为虽然PE文件为自己指定加载的基地址,但是windows有茫茫多的DLL,而且每个软件也有自己 的DLL,如果指定的地址已经被别的DLL占了怎么办?如果PE文件无法加载到预期的地址,那么系统会帮 他重新选择一个合适的基地址将他加载到此处,这时原有的VA就全部失效了,NT头保存了PE文件加载所需 的信息,在不知道PE会加载到哪个基地址之前,VA是无效的,所以在PE文件头中大部分是使用RVA来表示 地址的,而在代码中是用VA表示全局变量和函数地址的。那又有人要问了,既然加载基址变了以后VA都失 效了,那存在于代码中的那些VA怎么办呢?答案是:重定位。系统有自己的办法修正这些值,到后续重定 位表的文章中会详细描述。既然有重定位,为什么NT头不能依靠重定位采用VA表示地址呢(十万个为什 么)?因为不是所有的PE都有重定位,早期的EXE就是没有重定位的。我们都知道PE文件可以导出函数让 其他的PE文件使用,也可以从其他PE文件导入函数,这些是如何做到的?PE文件通过导出表指明自己导 出那些函数,通过导入表指明需要从哪些模块导入哪些函数。导入和导出表的具体结构会在单独的文章中 详细解释。

PE文件头部与标志(转)

PE文件头部与标志(转)

PE⽂件头部与标志(转)PE⽂件头部与标志 PE⽂件头部是由MS-DOS头部的e_lfanew域定位的,这个域只是给出了⽂件的偏移量,所以要确定PE头部的实际内存映射地址,就需要添加⽂件的内存映射基地址。

例如,以下的宏是包含在PEFILE.H源⽂件之中的://PEFILE.H#define NTSIGNATURE(a) ((LPVOID)((BYTE *)a + \((PIMAGE_DOS_HEADER)a)->e_lfanew))在处理PE⽂件信息的时候,我发现⽂件之中有些位置需要经常查阅。

既然这些位置仅仅是对⽂件的偏移量,那么⽤宏来实现这些定位就⽐较容易,因为它们较之函数有更好的表现。

请注意这个宏所获得的是PE⽂件标志,⽽并⾮PE⽂件头部的偏移量。

那是由于⾃Windows与OS/2的可执⾏⽂件开始,.EXE⽂件都被赋予了⽬标操作系统的标志。

对于Windows NT的PE⽂件格式⽽⾔,这⼀标志在PE⽂件头部结构之前。

在Windows和OS/2的某些版本中,这⼀标志是⽂件头的第⼀个字。

同样,对于PE⽂件格式,Windows NT使⽤了⼀个DWORD值。

以上的宏返回了⽂件标志的偏移量,⽽不管它是哪种类型的可执⾏⽂件。

所以,⽂件头部是在DWORD标志之后,还是在WORD标志处,是由这个标志是否Windows NT⽂件标志所决定的。

要解决这个问题,我编写了ImageFileType函数(如下),它返回了映像⽂件的类型://PEFILE.CDWORD WINAPI ImageFileType (LPVOID lpFile){/* ⾸先出现的是DOS⽂件标志 */if (*(USHORT *)lpFile == IMAGE_DOS_SIGNATURE){/* 由DOS头部决定PE⽂件头部的位置 */if (LOWORD (*(DWORD *)NTSIGNATURE (lpFile)) ==IMAGE_OS2_SIGNATURE ||LOWORD (*(DWORD *)NTSIGNATURE (lpFile)) ==IMAGE_OS2_SIGNATURE_LE)return (DWORD)LOWORD(*(DWORD *)NTSIGNATURE (lpFile));else if (*(DWORD *)NTSIGNATURE (lpFile) ==IMAGE_NT_SIGNATURE)return IMAGE_NT_SIGNATURE;elsereturn IMAGE_DOS_SIGNATURE;}else/* 不明⽂件种类 */return 0;}以上列出的代码⽴即告诉了你NTSIGNATURE宏有多么有⽤。

PE文件各区段说明

PE文件各区段说明

PE文件各节所包含的内容2008-11-13 13:34关于 sections 的意义以及它如何定位,相信你已有个概念。

现在我们要看看在EXE 和OBJ 档中的一些常见的 sections 。

虽然我所列的并不是全部,但已经涵盖了你每天会接触到(但也许你自己并不知道)的 sections 。

排列次序是根据其重要性以及遭遇它们的频繁度。

.text section.text 内含所有一般性的程序代码。

由于 PE 文件在 32 位模式下跑,并且不受约束于 16 位元节区,所以没有理由把程序代码分开放到不同的 sections 中。

联结器把所有来自 .OBJ的 .text 集合到一个大的 .text 中。

如果你使用Borland C++,其编译器制作出来的 code section 名为 CODE 而不是 .text 。

请看稍后「Borland CODE 以及 .icode sections 」一节。

我很惊讶地发现,在 .text 中除了编译器制作出来的码,以及runtime library 的码之外,还有一些其它东西。

在 PE 文件中,当你呼叫另一模块中的函数(例如USER32.DLL 中的 GetMessage ),编译器制造出来的 CALL 指令并不会把控制权直接传给 DLL 中的函数,而是传给一个JMP DWORD PTR [XXXXXXXX]指令,后者也位于 .text 中。

JMP 指令跳到一个地址去,此地址储存在 .idata 的一个 DWORD 之中。

这个DWORD 内含该函数的真正地址(函数进入点),如图8-4 所示。

图8-4 一个 PE 档呼叫 imported function 。

沉思良久,我终于了解为什么 DLL 的呼叫需要以这种方式实现。

把对同一个DLL 函数的所有呼叫都集中到一处,加载器就不再需要修补每一个呼叫 DLL 的指令。

PE 加载器需要做的,就只是把 DLL 函数的真实地址放到 .idata 的那个 DWORD 之中,根本就没有程序代码需要修补。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

PE可选头部PE可执行文件中接下来的224个字节组成了PE可选头部。

虽然它的名字是“可选头部”,但是请确信:这个头部并非“可选”,而是“必需”的。

OPTHDROFFSET宏可以获得指向可选头部的指针:PEFILE.H#define OPTHDROFFSET(a) ((LPVOID)((BYTE *)a + \((PIMAGE_DOS_HEADER)a)->e_lfanew + \SIZE_OF_NT_SIGNATURE + \sizeof(IMAGE_FILE_HEADER)))可选头部包含了很多关于可执行映像的重要信息,例如初始的堆栈大小、程序入口点的位置、首选基地址、操作系统版本、段对齐的信息等等。

IMAGE_OPTIONAL_HEADER结构如下:WINNT.Htypedef struct _IMAGE_OPTIONAL_HEADER {//// 标准域//USHORT Magic;UCHAR MajorLinkerVersion;UCHAR MinorLinkerVersion;ULONG SizeOfCode;ULONG SizeOfInitializedData;ULONG SizeOfUninitializedData;ULONG AddressOfEntryPoint;ULONG BaseOfCode;ULONG BaseOfData;//// NT附加域//ULONG ImageBase;ULONG SectionAlignment;ULONG FileAlignment;USHORT MajorOperatingSystemVersion;USHORT MinorOperatingSystemVersion;USHORT MajorImageVersion;USHORT MinorImageVersion;USHORT MajorSubsystemVersion;USHORT MinorSubsystemVersion;ULONG Reserved1;ULONG SizeOfImage;ULONG SizeOfHeaders;ULONG CheckSum;USHORT Subsystem;USHORT DllCharacteristics;ULONG SizeOfStackReserve;ULONG SizeOfStackCommit;ULONG SizeOfHeapReserve;ULONG SizeOfHeapCommit;ULONG LoaderFlags;ULONG NumberOfRvaAndSizes;IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];} IMAGE_OPTIONAL_HEADER, *PIMAGE_OPTIONAL_HEADER;如你所见,这个结构中所列出的域实在是冗长得过分。

为了不让你对所有这些域感到厌烦,我会仅仅讨论有用的——就是说,对于探究PE文件格式而言有用的。

标准域首先,请注意这个结构被划分为“标准域”和“NT附加域”。

所谓标准域,就是和UNIX可执行文件的COFF 格式所公共的部分。

虽然标准域保留了COFF中定义的名字,但是Windows NT仍然将它们用作了不同的目的——尽管换个名字更好一些。

·Magic。

我不知道这个域是干什么的,对于示例程序EXEVIEW.EXE示例程序而言,这个值是0x010B或267(译注:0x010B为.EXE,0x0107为ROM映像,这个信息我是从eXeScope上得来的)。

·MajorLinkerVersion、MinorLinkerVersion。

表示链接此映像的链接器版本。

随Window NT build 438配套的Windows NT SDK包含的链接器版本是2.39(十六进制为2.27)。

·SizeOfCode。

可执行代码尺寸。

·SizeOfInitializedData。

已初始化的数据尺寸。

·SizeOfUninitializedData。

未初始化的数据尺寸。

·AddressOfEntryPoint。

在标准域中,AddressOfEntryPoint域是对PE文件格式来说最为有趣的了。

这个域表示应用程序入口点的位置。

并且,对于系统黑客来说,这个位置就是导入地址表(IAT)的末尾。

以下的函数示范了如何从可选头部获得Windows NT可执行映像的入口点。

PEFILE.CLPVOID WINAPI GetModuleEntryPoint(LPVOID lpFile){PIMAGE_OPTIONAL_HEADER poh;poh = (PIMAGE_OPTIONAL_HEADER)OPTHDROFFSET(lpFile);if (poh != NULL)return (LPVOID)poh->AddressOfEntryPoint;elsereturn NULL;}·BaseOfCode。

已载入映像的代码(“.text”段)的相对偏移量。

·BaseOfData。

已载入映像的未初始化数据(“.bss”段)的相对偏移量。

Windows NT附加域添加到Windows NT PE文件格式中的附加域为Windows NT特定的进程行为提供了装载器的支持,以下为这些域的概述。

·ImageBase。

进程映像地址空间中的首选基地址。

Windows NT的Microsoft Win32 SDK链接器将这个值默认设为0x00400000,但是你可以使用-BASE:linker开关改变这个值。

·SectionAlignment。

从ImageBase开始,每个段都被相继的装入进程的地址空间中。

SectionAlignment 则规定了装载时段能够占据的最小空间数量——就是说,段是关于SectionAlignment对齐的。

Windows NT虚拟内存管理器规定,段对齐不能少于页尺寸(当前的x86平台是4096字节),并且必须是成倍的页尺寸。

4096字节是x86链接器的默认值,但是它可以通过-ALIGN: linker开关来设置。

·FileAlignment。

映像文件首先装载的最小的信息块间隔。

例如,链接器将一个段实体(段的原始数据)加零扩展为文件中最接近的FileAlignment边界。

早先提及的2.39版链接器将映像文件以0x200字节的边界对齐,这个值可以被强制改为512到65535这么多。

·MajorOperatingSystemVersion。

表示Windows NT操作系统的主版本号;通常对Windows NT 1.0而言,这个值被设为1。

·MinorOperatingSystemVersion。

表示Windows NT操作系统的次版本号;通常对Windows NT 1.0而言,这个值被设为0。

·MajorImageVersion。

用来表示应用程序的主版本号;对于Microsoft Excel 4.0而言,这个值是4。

·MinorImageVersion。

用来表示应用程序的次版本号;对于Microsoft Excel 4.0而言,这个值是0。

·MajorSubsystemVersion。

表示Windows NT Win32子系统的主版本号;通常对于Windows NT 3.10而言,这个值被设为3。

·MinorSubsystemVersion。

表示Windows NT Win32子系统的次版本号;通常对于Windows NT 3.10而言,这个值被设为10。

·Reserved1。

未知目的,通常不被系统使用,并被链接器设为0。

·SizeOfImage。

表示载入的可执行映像的地址空间中要保留的地址空间大小,这个数字很大程度上受SectionAlignment的影响。

例如,考虑一个拥有固定页尺寸4096字节的系统,如果你有一个11个段的可执行文件,它的每个段都少于4096字节,并且关于65536字节边界对齐,那么SizeOfImage域将会被设为11 * 65536 = 720896(176页)。

而如果一个相同的文件关于4096字节对齐的话,那么SizeOfImage 域的结果将是11 * 4096 = 45056(11页)。

这只是个简单的例子,它说明每个段需要少于一个页面的内存。

在现实中,链接器通过个别地计算每个段的方法来决定SizeOfImage确切的值。

它首先决定每个段需要多少字节,并且最后将页面总数向上取整至最接近的SectionAlignment边界,然后总数就是每个段个别需求之和了。

·SizeOfHeaders。

这个域表示文件中有多少空间用来保存所有的文件头部,包括MS-DOS头部、PE文件头部、PE可选头部以及PE段头部。

文件中所有的段实体就开始于这个位置。

·CheckSum。

校验和是用来在装载时验证可执行文件的,它是由链接器设置并检验的。

由于创建这些校验和的算法是私有信息,所以在此不进行讨论。

·Subsystem。

用于标识该可执行文件目标子系统的域。

每个可能的子系统取值列于WINNT.H的IMAGE_OPTIONAL_HEADER结构之后。

·DllCharacteristics。

用来表示一个DLL映像是否为进程和线程的初始化及终止包含入口点的标记。

·SizeOfStackReserve、SizeOfStackCommit、SizeOfHeapReserve、SizeOfHeapCommit。

这些域控制要保留的地址空间数量,并且负责栈和默认堆的申请。

在默认情况下,栈和堆都拥有1个页面的申请值以及16个页面的保留值。

这些值可以使用链接器开关-STACKSIZE:与-HEAPSIZE:来设置。

·LoaderFlags。

告知装载器是否在装载时中止和调试,或者默认地正常运行。

·NumberOfRvaAndSizes。

这个域标识了接下来的DataDirectory数组。

请注意它被用来标识这个数组,而不是数组中的各个入口数字,这一点非常重要。

·DataDirectory。

数据目录表示文件中其它可执行信息重要组成部分的位置。

它事实上就是一个IMAGE_DATA_DIRECTORY结构的数组,位于可选头部结构的末尾。

当前的PE文件格式定义了16种可能的数据目录,这之中的11种现在在使用中。

数据目录WINNT.H之中所定义的数据目录为:WINNT.H// 目录入口// 导出目录#define IMAGE_DIRECTORY_ENTRY_EXPORT 0// 导入目录#define IMAGE_DIRECTORY_ENTRY_IMPORT 1// 资源目录#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2// 异常目录#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3// 安全目录#define IMAGE_DIRECTORY_ENTRY_SECURITY 4// 重定位基本表#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5// 调试目录#define IMAGE_DIRECTORY_ENTRY_DEBUG 6// 描述字串#define IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7// 机器值(MIPS GP)#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8// TLS目录#define IMAGE_DIRECTORY_ENTRY_TLS 9// 载入配置目录#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10基本上,每个数据目录都是一个被定义为IMAGE_DATA_DIRECTORY的结构。

相关文档
最新文档