PE文件结构
- 格式:docx
- 大小:20.91 KB
- 文档页数:4
PE文件格式详解(一)基础知识什么是PE文件格式:我们知道所有文件都是一些连续(当然实际存储在磁盘上的时候不一定是连续的)的数据组织起来的,不同类型的文件肯定组织形式也各不相同;PE文件格式便是一种文件组织形式,它是32位Window系统中的可执行文件EXE以及动态连接库文件DLL的组织形式。
为什么我们双击一个EXE文件之后它就会被Window运行,而我们双击一个DOC文件就会被Word打开并显示其中的内容;这说明文件中肯定除了存在那些文件的主体内容(比如EXE文件中的代码,数据等,DOC 文件中的文件内容等)之外还存在其他一些重要的信息。
这些信息是给文件的使用者看的,比如说EXE文件的使用者就是Window,而DOC文件的使用者就是Word。
Window可以根据这些信息知道把文件加载到地址空间的那个位置,知道从哪个地址开始执行;加载到内存后如何修正一些指令中的地址等等。
那么PE文件中的这些重要信息都是由谁加入的呢?是由编译器和连接器完成的,针对不同的编译器和连接器通常会提供不同的选项让我们在编译和联结生成PE文件的时候对其中的那些Window需要的信息进行设定;当然也可以按照默认的方式编译连接生成Window中默认的信息。
例如:WindowNT默认的程序加载基址是0x40000;你可以在用VC连接生成EXE文件的时候使用选项更改这个地址值。
在不同的操作系统中可执行文件的格式是不同的,比如在Linux上就有一种流行的ELF格式;当然它是由在Linux上的编译器和连接器生成的,所以编译器、连接器是针对不同的CPU架构和不同的操作系统而涉及出来的。
在嵌入式领域中我们经常提到交叉编译器一词,它的作用就是在一种平台下编译出能在另一个平台下运行的程序;例如,我们可以使用交叉编译器在跑Linux的X86机器上编译出能在Arm上运行的程序。
程序是如何运行起来的:一个程序从编写出来到运行一共需要那些工具,他们都对程序作了些什么呢?里面都涉及哪些知识需要学习呢?先说工具:编辑器-》编译器-》连接器-》加载器;首先我们使用编辑器编辑源文件;然后使用编译器编译程目标文件OBJ,这里面涉及到编译原理的知识;连接器把OBJ文件和其他一些库文件和资源文件连接起来生成EXE文件,这里面涉及到不同的连接器的知识,连接器根据OS的需要生成EXE文件保存着磁盘上;当我们运行EXE文件的时候有Window的加载器负责把EXE文件加载到线性地址空间,加载的时候便是根据上一节中说到的PE文件格式中的哪些重要信息。
实验二PE文件结构分析一. 实验目的1.了解PE文件的输入表结构;2.手工解析PE文件的输入表;3.编程实现PE文件输入表的解析。
二. 实验内容1.第一步:手动解析输入表结构(1)使用工具箱中的工具e verything,寻找当前系统中任意一个e xe文件,文件名称是: actmovie.exe(2)使用LordPE“PE编辑器”打开exe文件,确定输入表的RVA,截图如下(图1):(3)点击PE编辑器右侧的“位置计算器”,得到文件偏移值,截图如下(图2):(4)使用16进制编辑工具,跳转到相应的输入文件偏移地址,输入表是每个IID对应一个DLL,根据IID大小,这里取20字节的数据进行分析,将输入表第一个IID结构的数据与IID结构体的成员一一对应,具体如下所示:IMAGE_IMPORT_DESCRIPTOR {OriginalFirstThunk = 000013C0TimeDateStamp = FFFFFFFFForwarderChain = FFFFFFFFName = 000014C0FirstThunk = 0000100C}(5)关注OriginalFirstThunk和Name两个成员,其中Name是一个RVA,用步骤(3)的方法得到其文件偏移值为 000008C0 ,在16进制编辑工具转到这个偏移地址,可见输入表的第一个D LL名为 msvcrt.dll ,截图如下(图3):(6)分析一下OriginalFirstThunk,它指向一个类型为IMAGE_THUNK_DATA的数组,上面已经分析出了它的值为000013C0 ,这是一个RVA,用步骤(3)的方法得到文件偏移地址 00007C0 。
在16进制编辑工具转到这个偏移地址,其中前面4个字节的数据为 63 5F 00 C8 ,截图如下(图4):(7)可以看出,这是以序号(填“以名字”或“以序号”)的方式输入函数;用与步骤(3)相同的方式在16进制编辑工具中对应IMAGE_IMPORT_BY_NAME结构的数据,可以看到函数的输入序号为 20 ,函数名为 cexit ,截图如下(图5):(8)验证:使用L ordPE单击“目录表”界面中输入表右侧的“…按钮”,打开输入表对话框,可以验证获取的DLL名和函数名是否正确。
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文件是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⽂件结构解析说明:本⽂件中各种⽂件头格式截图基本都来⾃看雪的《加密与解密》;本⽂相当《加密与解密》的阅读笔记。
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文件格式详解(一)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文件被称为可移植的执行体是Portable Execute的全称,常见的EXE、DLL、OCX、SYS、COM都是PE文件,PE文件是微软Windows操作系统上的程序文件(可能是间接被执行,如DLL),Portable 是指对于不同的Windows版本和不同的CPU类型上PE文件的格式是一样的,当然CPU不一样了,CPU指令的二进制编码是不一样的。
只是文件中各种东西的布局是一样的。
在下面关于结构的定义中,WORD 表示变量大小为2个字节,DWORD表示变量大小是4个字节。
1.1 PE文件的结构PE文件有着固定的结构,分为五个部分,如下:1:DOS MZ Header(DOS文件头) 一个IMAGE_DOS_HEADER结构,大小为64字节。
2:DOS Stub(DOS加载模块) 没有固定大小。
3:PE Header(PE文件头)一个IMAGE_NT_HEADERS结构,大小为248字节。
4:Section Table(节表)一个IMAGE_SECTION_HEADER结构数组,数组大小依据节而定,如果PE文件有5个节,则数组大小为5。
5:Sections(节或段)没有固定大小,可以有多个节。
1.2 DOS文件头和DOS加载模块PE文件的一二部分完全是为了程序能在DOS运行下时给出一个提示。
IMAGE_DOS_HEADER结构的定义如下:Typedef struct IMAGE_DOS_HEADER{WORD e_magic; // 魔术数字WORD e_cblp; // 文件最后页的字节数WORD e_cp; // 文件页数WORD e_crlc; // 重定义元素个数WORD e_cparhdr; // 头部尺寸,以段落为单位WORD e_minalloc; // 所需的最小附加段WORD e_maxalloc; // 所需的最大附加段WORD e_ss; // 初始的SS值(相对偏移量)WORD e_sp; // 初始的SP值WORD e_csum; // 校验和WORD e_ip; // 初始的IP值WORD e_cs; // 初始的CS值(相对偏移量)WORD e_lfarlc; // 重分配表文件地址WORD e_ovno; // 覆盖号WORD e_res[4]; // 保留字WORD e_oemid; // OEM标识符(相对e_oeminfo)WORD e_oeminfo; // OEM信息WORD e_res2[10]; // 保留字LONG e_lfanew; // 新exe头部的文件地址} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;DOS文件头和DOS加载模块在 Windows下几乎已经没有什么作用了。
PE文件格式详解(一)基础知识什么是PE文件格式:我们知道所有文件都是一些连续(当然实际存储在磁盘上的时候不一定是连续的)的数据组织起来的,不同类型的文件肯定组织形式也各不相同;PE文件格式便是一种文件组织形式,它是32位Window系统中的可执行文件EXE以及动态连接库文件DLL的组织形式。
为什么我们双击一个EXE文件之后它就会被Window运行,而我们双击一个DOC文件就会被Word打开并显示其中的内容;这说明文件中肯定除了存在那些文件的主体内容(比如EXE文件中的代码,数据等,DOC 文件中的文件内容等)之外还存在其他一些重要的信息。
这些信息是给文件的使用者看的,比如说EXE文件的使用者就是Window,而DOC文件的使用者就是Word。
Window可以根据这些信息知道把文件加载到地址空间的那个位置,知道从哪个地址开始执行;加载到内存后如何修正一些指令中的地址等等。
那么PE文件中的这些重要信息都是由谁加入的呢?是由编译器和连接器完成的,针对不同的编译器和连接器通常会提供不同的选项让我们在编译和联结生成PE文件的时候对其中的那些Window需要的信息进行设定;当然也可以按照默认的方式编译连接生成Window中默认的信息。
例如:WindowNT默认的程序加载基址是0x40000;你可以在用VC连接生成EXE文件的时候使用选项更改这个地址值。
在不同的操作系统中可执行文件的格式是不同的,比如在Linux上就有一种流行的ELF格式;当然它是由在Linux上的编译器和连接器生成的,所以编译器、连接器是针对不同的CPU架构和不同的操作系统而涉及出来的。
在嵌入式领域中我们经常提到交叉编译器一词,它的作用就是在一种平台下编译出能在另一个平台下运行的程序;例如,我们可以使用交叉编译器在跑Linux的X86机器上编译出能在Arm上运行的程序。
程序是如何运行起来的:一个程序从编写出来到运行一共需要那些工具,他们都对程序作了些什么呢?里面都涉及哪些知识需要学习呢?先说工具:编辑器-》编译器-》连接器-》加载器;首先我们使用编辑器编辑源文件;然后使用编译器编译程目标文件OBJ,这里面涉及到编译原理的知识;连接器把OBJ文件和其他一些库文件和资源文件连接起来生成EXE文件,这里面涉及到不同的连接器的知识,连接器根据OS的需要生成EXE文件保存着磁盘上;当我们运行EXE文件的时候有Window的加载器负责把EXE文件加载到线性地址空间,加载的时候便是根据上一节中说到的PE文件格式中的哪些重要信息。
pe文件格式总体结构
PE文件格式(Portable Executable)是Windows操作系统的可执行文件格式,其总体结构包括以下几个部分:
1.DOS头:PE文件的最前面是DOS头,用于兼容DOS系统。
它包
含了文件标识符、魔数(0x5A4D,即MZ)以及DOS头长度等
信息。
2.PE头:紧接着DOS头的是PE头,它包含了PE文件的元数据
信息,如文件标志、机器类型、节区数量、可选头大小等。
3.可选头:可选头包含了PE文件的属性、入口点地址、基址等
信息。
它是一个可选部分,某些PE文件可能没有这个部分。
4.节区:PE文件由多个节区组成,每个节区包含了一部分程序
代码或数据。
每个节区都有自己的头部信息,用于描述节区的属性。
以上是PE文件格式的总体结构,具体细节可能因不同的PE文件而有所差异。
我们这里将不依赖任何编译器,仅仅使用一个十六进制编辑器逐个字节的手工编写一个可执行程序。
以这种方式讲解PE结构,通过这个过程读者可以学习PE结构中的PE头、节表以及导入表相关方面的知识。
为了简单而又令所有学习程序开发的人感到亲切,我们将完成一个Hello World! 程序。
功能仅仅是运行后弹出一个消息框,消息框的内容是Hello World!。
首先了解一下Win32可执行程序的大体结构,就是通常所说的PE结构。
如图1所示PE结构示意图:图1 标准PE结构图由图中可以看出PE结构分为几个部分:MS-DOS MZ 头部:所有PE文件必须以一个简单的DOS MZ 头开始。
有了它,一旦程序在DOS下执行,DOS就能识别出这是有效的执行体,然后运行紧随MZ header 之后的DOS程序。
以此达到对Dos系统的兼容。
(通常情况DOS MZ header总共占用64byte)。
MS-DOS 实模式残余程序:实际上是个有效的EXE,在不支持PE文件格式的操作系统中,它将简单显示一个错误提示,大多数情况下它是由汇编编译器自动生成。
通常,它简单调用中断21h,服务9来显示字符串"This program cannot run in DOS mode"。
(在我们写的程序中,他不是必须的,可以不予以实现,但是要保留其大小,大小为112byte,为了简洁,可以使用00来填充。
)PE文件标志:是PE文件结构的起始标志。
(长度4byte, Windows程序此值必须为0x50450000)PE文件头:是PE相关结构 IMAGE_NT_HEADERS 的简称,其中包含了许多PE装载器用到的重要域。
执行体在支持PE文件结构的操作系统中执行时,PE装载器将从DOS MZ header中找到PE header的起始偏移量,跳过了MS-DOS 实模式残余程序,直接定位到真正的文件头PE header,长度20byte。
非托管PE文件资源结构剖析摘要简单介绍了PE文件的结构,介绍了怎样从PE中找到资源块,详细介绍了资源的三层结构,以及三层结构里面的一些细节。
关键词PE文件;资源段;资源结构1 PE文件结构简介PE(Portable Executable)文件是Windows操作系统上的程序文件,常见的EXE、DLL、SYS等都是PE文件。
PE文件有固定的结构,PE文件的结构如图1,所有PE文件都是由MS-DOS文件头,MS-DOS Stub,PE文件头,段表,以及一组段体组成。
段体包括代码段、数据段、输入表段、输出表段,以及资源段等。
MS-DOS文件头MS-DOS Stub PE文件头段表代码段数据段资源段输入表输出表…图1 PE文件结构图资源段是段表下的某个段。
我们可以通过MS-DOS文件头,PE文件头和段表的数据找到资源段的位置。
2找到PE文件资源段可以通过下面的步骤来查找PE资源段。
1)根据MS-DOS文件头结构的成员e_lfanew,得到PE文件头在文件中的位置;2)确定PE件头在文件中的位置之后,就可以确定PEHeader 中的成员FileHeader 和成员OptionalHeader 在文件中的位置。
根据FileHeader中的成员NumberOfSections 的值,就可以确定文件中段的数目,也就是段表数组中元素的个数;3)PE Header 在文件中的位置加上PE Header 结构的大小就可以得到段表在文件中的开始位置;4)取得PE Header 中的Optional Header 中的DataDirectory 数组中的第三项的成员VirtualAddress 的值,这个值就是在内存中资源段的RV A。
然后根据段的数目,遍历段表数组。
找到其中成员VirtualAddress 等于上面求得的RV A的数组项,该数组项就是资源段的段表,其成员PointerToRawData 就是资源段在文件中的位置。
附加数据其它节区.reloc节区.rsrc节区.data节区.text节区节表数据目录选项头文件头PE标志DOS stub DOS头PE文件格式--------------基本结构信息一)win32下PE文件格式的文件有:*.exe;*.dll;*.scr;*.fon;*.drv;*.sys二)pe文件基本结构格式说明:1)dos头:0000 0000:4D 5A0000 0010:0000 0020:0000 0030: 3C xx xx xxIMAGE_DOS_HEADER struc+00h e_magic: word ;dos标志MZ,常量表示IMAGE_DOS_SIGNATURE ...+3ch e_lfanew: dword;指向pe文件头IMAGE_DOS_HEADER ends2)dos stub在dos执行的程序代码。
方法是3)pe文件头定位:文件开始地址+IMAGE_DOS_HEADER.e_lfanew数据结构:IMAGE_NT_HEADER struc+00h Signature: dword ;pe标志50 45 00 00,IMAGE_NT_SIGNATURE+04h FileHead: IMAGE_FILE_HEADER+18h OptionalHead: IMAGE_OPTIONAL_HEADER32IMAGE_NT_HEADER ends4)pe标志0000 0D00: 50 45 00 00+00h Signature: dword ;PE标志,常量表示IMAGE_NT_SIGNATURE5)文件头0000 0D00: 50 45 00 00 04 xx 06 xx 08 xx xx xx0000 0D10: 14 xx 16 xx数据结构:IMAGE_FILE_HEADER struc+04h Machine: word ;运行平台+06h NumberOfSections: word ;节区数量+08h TimeDateStamp: dword ;从1969年12月31日下午4点以来的秒数...+14h SizeOfOptionalHeader: word ;选项头大小+16h Characteristics: word ;文件属性,exe为 dll为多少等IMAGE_FILE_HEADER ends6)选项头0000 0D00: 50 45 00 00 04 xx 06 xx 08 xx xx xx0000 0D10: 14 xx 16 xx0000 0D20: 28 xx xx xx0000 0D30: 34 xx xx xx 38 xx xx xx 3C xx xx xx0000 0D40:0000 0D50: 50 xx xx xx 54 xx xx xx 5C xx xx xx0000 0D60:0000 0D70: 78 xx xx xx xx xx xx xx数据结构:IMAGE_OPTIONAL_HEADER32 struc+28h AddressOfEntryPoint: dword ;入口地址+34h ImageBase: dword ;建议装载地址+38h SectionAlignMent: dword ;内存对齐大小+3ch FileAlignMent: dword ;文件对齐大小+50h SizeOfImage dword ;文件装入到内存后的总尺寸,即内存对齐后的总大小+54h SizeOfHeaders dword ;所有头 + 区块表的尺寸大小+5ch SubSystem: wrod ;子系统+78h DataDirectory: IMAGE_DATA_DIRECTORY ;数据目录IMAGE_OPTIONAL_HEADER32 ends界面子系统的取值和含义取值Windows.inc中的预定义值含义0IMAGE_SUBSYSTEM_UNKNOWN未知的子系统1IMAGE_SUBSYSTEM_NATIVE 不需要子系统(如驱动程序)2IMAGE_SUBSYSTEM_WINDOWS_GUI Windows图形界面3IMAGE_SUBSYSTEM_WINDOWS_CUI Windows控制台界面5IMAGE_SUBSYSTEM_OS2_CUI OS2控制台界面7IMAGE_SUBSYSTEM_POSIX_CUI POSIX控制台界面8IMAGE_SUBSYSTEM_NATIVE_WINDOWS不需要子系统9IMAGE_SUBSYSTEM_WINDOWS_CE_GUI Windows CE图形界面7)数据目录IMAGE_DATA_DIRECTORY strucVirtualAddress: dword ;RVA起始地址 VirtualSize: dword ;数据块大小IMAGE_DATA_DIRECTORY ends数据目录列表的含义索引索引值在Windows.inc中的预定义值对应的数据块0IMAGE_DIRECTORY_ENTRY_EXPORT导出表1IMAGE_DIRECTORY_ENTRY_IMPORT导入表2IMAGE_DIRECTORY_ENTRY_RESOURCE资源3IMAGE_DIRECTORY_ENTRY_EXCEPTION 异常(具体资料不详)4IMAGE_DIRECTORY_ENTRY_SECURITY 安全(具体资料不详)5IMAGE_DIRECTORY_ENTRY_BASERELOC重定位表6IMAGE_DIRECTORY_ENTRY_DEBUG调试信息7IMAGE_DIRECTORY_ENTRY_ARCHITECTURE版权信息8IMAGE_DIRECTORY_ENTRY_GLOBALPTR 具体资料不详9IMAGE_DIRECTORY_ENTRY_TLS Thread Local Storage10IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 具体资料不详11IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 具体资料不详12IMAGE_DIRECTORY_ENTRY_IAT 导入函数地址表13IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 具体资料不详14IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 具体资料不详15未使用15未使用8)节表多个IMAGE_SECTION_HEADER组成节表。
检验PE文件的有效性<1>首先检验文件头部第一个字的值是否等于IMAGE_DOS_SIGNATURE,是则表示DOS MZ header有效<2>一旦证明文件的Dos header 有效后,就可用e_lfanew来定位PE header<3>比较PE header 的第一个字的值是否等于IMAGE_NT_HEADER,如果前后两个值都匹配. PS.WinHex使用方法1.Alt+G跳到指定位置2.Ctrl+Shift+N放入新文件3.大文件扩容,新建一个扩容大小+1的文件,把这个文件的数据复制后写入整个文件的尾地址.4.文本搜索ctrl+F5.十六进制搜索ctrl+alt+x6.文本显示F77.打开内存alt+F98.进制转换器F89.分析选块F210.计算HASH ctrl+F211.收集文本信息ctrl+F1012.编辑模式F6一.IMAGE_DOS_HEADER<1>位置00H,WORD(2个字节)的e_magic为4D5A,即MZ<2>位置3CH,60,LONG(4个节节)的e_lfanew为64+112=176即B0H,二.IMAGE_NT_HEADERS<1>位置B0H,DWORD(4个字节),PE开始标记,写入50450000,即PE<2>位置B4H,WORD,PE所要求的CPU,对于Intel平台,为4C01<2>位置B6,WORD,PE中段总数,计划有3个段,.text代码段,.rdata只读数据段,.data全局变量数据段,所以值为0300,<3>位置C4,WORD,表示后面的PE文件可选头的占空间大小,即224字节(E0),值为E000<4>位置C6,WORD,表示文件是EXE还是DLL,如果是可执行文件写0200,如果是dll,写0020,<5>位置C8,WORD,表示文件格式,如果是0B01表示.exe,如果是0701表示ROM映像<6>位置D8,DWORD,表示代码入口的RVA地址,要代码写好后才能确定,这里先用AAAAAAAA 代替,后修改为00100000,<7>位置E4,DWORD,表示文件映射到内存的基地址,PE文件优先装载地址,这里值为00004000,<8>位置E8,DWORD,表示段加载后内存中的对齐方式,如果是1000h(即4096),每节的起始地址必须是4096的倍数,若第一节从401000h开始,大小是10个字节,下一个节并不是从401011开始,而是从402000h开始,一般情况下都为4KB.<9>位置EC,DWORD,表示段在文件中对齐方式,一般情况下,程序文件对齐粒度都为200h(512),将此值设为00020000,<10>位置F8,表示子系统主版本号,该子系统版本必定是4.0,那么此处值为04.<11>位置100,表示程序载入内存后占用内存的大小(单位字节),等于所有段的长度之和--PE 结构要占1000h,3个段每个占1000h,总大小为4000h,值为"00400000"<12>位置104,表示所有文件头的长度之和,PE文件头总大小为64+112+4+20+224=424,3个节表的总大小3*40=120,424+120=544 字节,转为十六进制为220h,实际上要占用400h空间,所以值为00040000,<13>位置10C,表示子系统,0300表示控制台程序,0200表示Windows程序,<14>位置124,表示IMAGE_DA TA_DIRECTORY结构数组大小,通常有16个元素,所以值为1000000,<15>位置130,目录表的起始RVA地址,目录表的长度,先填写AAAAAAAAAAAAAAAA,因为要文件对齐,全部添零直到1a7h处.(后改写为1020 0000 3C00 0000<16>位置1A8,表示该节的名称,该节的名字为.text,此值为2E74657874000000,<17>位置1B0,表示该节数据映射到内存后所占字节数,这里是指有效代码所占的字节数,也可以填写26h经过内存对齐后的值,即00100000,<18>位置1B4,表示.text段映射到内存中的起始地址,因为PE头本身结构小于1000h字节,经过内存对齐后便为1000h,那么此值便为00001000,因此此处填写00100000,<19>位置1B8,表.text段在文件中所占的大小,可以填写2600 0000,也可以填写此值经过文件对齐后的值即200h,即00020000,<20>位置1BC,表示.text段在文件中的起始地址,上面经计算PE文件的总长度为400h,实际上也就是.text的起始偏移地址,此值为00040000,<21>位置1CC,表示是代码段,bit5位要置1,含有初始化数据,bit6要置1,可执行bit29置1,所以此处应该填写6000 0020.<22>.text头编写完毕,按照上面方法分别填写.rdata段和.data段,后面的代码用零补齐,直到3ff<23>位置400,表示.text段的程序执行代码,push 0;MessageBoxA的第4个参数,即消息框的风格,这里传入0push 0x403000;第3个参数,消息框的标题字符所在的地址PE头1000h,.text段1000h,.data段1000h,则在后面的.rdata段应该在偏移为3000h处,push 0x403007;第2个参数,消息框的内容所在的地址,"wcc526"占7个字符,push 0;第1个参数,消息框所属窗口句柄0call 40101A;调用MessageBoxA,实际上是跳转到该函数的跳转指令所在地址,因为执行代码起始地址为.text段的起始地址为0x1000,总长度为2+5+5+2+5+2+5=26,转为十六进制是1A, push 0 ;ExitProcess函数的参数,程序退出码,传入0call 401020;调用ExitProcess,实际上是跳转到该函数的跳转指令所在地址,再加6个字节jmp dword ptr[0x402080];跳转到MessageBoxA的真正地址处,通过导入表得到其填充地址为0x2080,这是其RVA值,jmp dword ptr[0x402088];跳转到ExitProcess的真正地址处, 通过导入表得到其填充地址为0x2088,这是其RVA值,转成机器码为push 0 ->6A 00push 0x403000->68 00304000push 0x403007->68 07304000push 0 ->6A 00call 40101A->E8 07 00 00 00push 0->6A 00call 401020->E8 06 00 00 00jmp dword ptr[0x402080]->FF 2580204000jmp dword ptr[0x402088]->FF 2588204000其余部分用00填充,直到600h处.<24>位置61C,指向DLL名字的RVA,由导入函数名称表可知此user32.dll名称所在地址为66A,所以RVA地址为6A20 0000<25>位置630, 指向DLL名字的RVA,由导入函数名称表可知此kernel32.dlol名称所在地址为684,所以RVA地址为8420 0000<24>位置64C,库函数名称地址表,即RVA地址,文件偏移600h对应的RVA是2000h,所以MessageBoxA的地址为65C,所以写5C20 00 0000,<25>位置654, 库函数名称地址表,即RVA地址,文件偏移600h对应的RVA是2000h,所以ExitProcess的地址为676,所以写7620 00 0000导出表结构1.Characteristics 是DWORD 类型,4个字节,可以为零2.TimeDateStampe,DWORD,表示自1970年1月1日至今的秒数,输出表创建时间,可以为零3.MajorVersion,WORD,表示主版本号,可以是零4.MinorVersion,WORD,表示次版本号,可以是零,DWORD,指向模块的真实名称字符串的RVA值,若该值为0x2e0a6,在文件中找到偏移为0x2e0a6的位置,得到Dll.dll,得知导出模块的名字为Dll.dll6.Base,等于所有函数导出序号中最小的值7.NumberOfFunctions,DWORD,表示模块中导出函数/符号个数8.NumberofNames,DWORD,表示通过名字引出的函数/符号的数目,9.AddressofFunctions,DWORD,指向一个地址表,存放的是所有导出函数所在地址的RV A值10.AddressofNames,DWORD,指向一个地址表,存放的是所有导出函数名称字符串所在地址的RVA值.11.AddressOfNameOrdinals,DWORD,指向一个导出序号表,解析导出表的步骤1.找出导出目录,定位到导出表的文件偏移2.查看导出表的第7个成员,得知导出地址成员的个数,这里是43.查看导出表的第9个成员,得到RVA并且将其转成文件偏移,4.查看导出表的第8个成员,得到由函数名导出函数的个数5.查看导出表的第11个成员,得到导出序号表6.查看导出表第10个成员,得出导出表中导出函数名称字符串所在地址的函数名称地址表。