一直想做一个PE结构的总结,只是学的时候有很多东西就没搞懂,加上时间一长,很多知识也早忘了,也就一直没完成。这几天从头看了下,好不容易理清楚了,整理一下,以免又忘了

pe文件框架结构,图片贴过来太模糊了就画个表格代替一下

DOS文件头 PE文件头  区块表    各区块    调试信息  

  DOS文件头

DOS文件头包括DOS MZ头和DOS stub

MS-DOS头部是一个IMAGE_DOS_HEADER结构(代码来自windows.inc)

IMAGE_DOS_HEADER STRUCT
+0h e_magic WORD ? ;DOS可执行文件标记"MZ"
+2h e_cblp WORD ?
+4h e_cp WORD ?
+6h e_crlc WORD ?
+8h e_cparhdr WORD ?
+0a e_minalloc WORD ?
+0c e_maxalloc WORD ?
+0e e_ss WORD ?
+ e_sp WORD ?
+ e_csum WORD ?
+ e_ip WORD ?
+ e_cs WORD ?
+ e_lfarlc WORD ?
+1a e_ovno WORD ?
+1c e_res WORD dup(?)
+ e_oemid WORD ?
+ e_oeminfo WORD ?
+ e_res2 WORD dup(?)
+3c e_lfanew DWORD ? ;指向PE文件头
IMAGE_DOS_HEADER ENDS

此结构共64字节

这里面只有第一个和最后一个字段较为重要,e_magic字段被设置为5A4Dh,其ASCII值为"MZ"(4D5Ah)<小端存储>

偏移为3ch的e_lfanew字段指出真正的PE头的文件偏移位置

接下来的DOS stub(DOS块)实际上是一个有效的EXE,在DOS下显示一个错误提示

  PE文件头

PE文件头是一个IMAGE_NT_HEADERS结构

IMAGE_NT_HEADERS STRUCT
+0h Signature DWORD ?   ;PE文件表示
+4h FileHeader IMAGE_FILE_HEADER <>  ;映像文件头,包含了PE文件的一些基本信息
+18h OptionalHeader IMAGE_OPTIONAL_HEADER32 <>  ;可选映像头,补充PE文件属性
IMAGE_NT_HEADERS ENDS

对于32位PE文件,此结构通常为248字节

对于Signature的定义为

IMAGE_NT_SIGNATURE equ 00004550h

其ASCII值为:"PE00"(50450000h)

后面的两个结构最重要的就是偏移78h的数据目录表

IMAGE_FILE_HEADER结构

IMAGE_FILE_HEADER STRUCT
+04h  Machine WORD ? ;可执行文件的目标CPU类型,14Ch为Intel i386
+06h  NumberOfSections WORD ? ;区块的数目
+08h  TimeDateStamp DWORD ?  ;文件创建日期和时间
+0ch  PointerToSymbolTable DWORD ?  ;COFF符号表的文件偏移位置
+10h  NumberOfSymbols DWORD ?  ;符号个数
+14h  SizeOfOptionalHeader WORD ?  ;IMAGE_OPTIONAL_HEADER32结构的大小,32位PE文件通常是E0h,64位PE32+是F0h
+16h  Characteristics WORD ?  ;文件属性
IMAGE_FILE_HEADER ENDS

IMAGE_OPTIONAL_HEADER32结构

IMAGE_OPTIONAL_HEADER32 STRUCT
+18h  Magic WORD ?  ;标志字,ROM映像(0107h)普通可执行映像(010Bh)PE32+(020Bh)
+1Ah  MajorLinkerVersion BYTE ?  ;链接程序的主版本号
+1Bh  MinorLinkerVersion BYTE ?  ;链接程序的次版本号
+1Ch  SizeOfCode DWORD ?  ;所有带有IMAGE_SCN_CNT_CODE属性区块的总大小
+20h  SizeOfInitializedData DWORD ?  ;已初始化数据块的大小
+24h  SizeOfUninitializedData DWORD ?  ;未初始化数据块的大小(通常在.bss块中)
+28h  AddressOfEntryPoint DWORD ?  ;程序执行入口RVA(这个地址并不直接指向Main、WinMain、DllMain,而是指向运行库代码并由它来调用上述函数)
+2Ch  BaseOfCode DWORD ? ;代码段的起始RVA(Microsoft链接器生成的执行文件通常是1000h)<内存中代码段通常在PE文件头之后、数据块之前>
+30h  BaseOfData DWORD ?  ;数据段的起始RVA。这个域的值对于不同版本的微软链接器是不一致的,在64位可执行文件中是不出现的
+34h  ImageBase DWORD ?  ;文件在内存中的首选装入地址
+38h  SectionAlignment DWORD ?  ;内存中的区块对齐值
+3Ch  FileAlignment DWORD ?  ;磁盘上区块的对齐值
+40h  MajorOperatingSystemVersion WORD ?  ;操作系统最低主版本号
+42h  MinorOperatingSystemVersion WORD ?  ;操作系统最低次版本号
+44h  MajorImageVersion WORD ?  ;用户自定义主版本号
+46h  MinorImageVersion WORD ?  ;用户自定义次版本号
+48h  MajorSubsystemVersion WORD ?  ;要求最低子系统版本主版本号
+4Ah  MinorSubsystemVersion WORD ?  ;要求最低子系统版本次版本号
+4Ch  Win32VersionValue DWORD ?  ;不用字段,常设为0
+50h  SizeOfImage DWORD ?  ;映像装入内存后的总尺寸
+54h  SizeOfHeaders DWORD ?  ;MS-DOS头部、PE头部、区块表的组合尺寸。域值四舍五入至文件对齐的倍数
+58h  CheckSum DWORD ?  ;映像校验和,链接器的/RELEASE开关被使用时,校验和被置于文件中
+5Ch  Subsystem WORD ?  ;标明可执行文件所期望的子系统(用户界面类型)的枚举值
+5Eh  DllCharacteristics WORD ?  ;DLL特性旗标,DllMain()函数何时被调用,默认为0
+60h  SizeOfStackReserve DWORD ?  ;为线程保留的堆栈大小,它一开始只提交其中一部分,只有在必要时,才提交剩下的部分
+64h  SizeOfStackCommit DWORD ?  ;一开始即被委派给堆栈的内存数量。默认值是4KB
+68h  SizeOfHeapReserve DWORD ?  ;为进程的默认堆保留的内存。默认值是1MB,在当前Windows里,堆值在用户不干涉的情况下就能增长超过这个值
+6Ch  SizeOfHeapCommit DWORD ?  ;EXE文件里,委派给堆的内存大小。默认值是4KB
+70h  LoaderFlags DWORD ?  ;与调试有关,默认为0
+74h  NumberOfRvaAndSizes DWORD ?  ;数据目录的项数,从最早的Windows NT发布以来一直是16
+78h  DataDirectory IMAGE_DATA_DIRECTORY IMAGE_NUMBEROF_DIRECTORY_ENTRIES dup(<>)
IMAGE_OPTIONAL_HEADER32 ENDS

这两个结构中有很多常用到的信息,如IMAGE_FILE_HEADER结构中的区块数目, IMAGE_OPTIONAL_HEADER中的ImageBase、SectionAlignment和FileAlignment等字段

  区块表

对于数据目录表,最后再来总结

区块表是一个IMAGE_SECTION_HEADER结构数组,区块的个数由IMAGE_NT_HEADERS.FileHeader.NumberOfSections指出

区块表的后面与区块之间一般是大量的填充位

IMAGE_SECTION_HEADER结构:40字节

IMAGE_SECTION_HEADER STRUCT
Name1 db IMAGE_SIZEOF_SHORT_NAME dup(?)  ;区块名
union Misc
PhysicalAddress dd ?
VirtualSize dd ?  ;区块实际大小
ends
VirtualAddress dd ?  ;区块的RVA地址
SizeOfRawData dd ?  ;在文件中对齐后的尺寸
PointerToRawData dd ?  ;在文件中的偏移
PointerToRelocations dd ?  ;在OBJ文件中使用,重定位的偏移,指向一个IMAGE_RELOCATION结构数组
PointerToLinenumbers dd ?  ;行号表在文件中的偏移值,这是文件调试信息
NumberOfRelocations dw ?  ;重定位项数目,在EXE文件中无意义
NumberOfLinenumbers dw ?  ;该块在行号表中的行号数目
Characteristics dd ?  ;块属性
IMAGE_SECTION_HEADER ENDS

此结构常用VirtualAddress和PointerToRawData字段来定位区块,并实现文件偏移与虚拟地址的转换

  数据目录表

数据目录表位于IMAGE_NT_HEADER的78h偏移处,是一个IMAGE_DATA_DIRECTORY结构数组,从最早的Windows NT发布以来项数一直是16

IMAGE_DATA_DIRECTORY结构为:

IMAGE_DATA_DIRECTORY STRUCT
VirtualAddress DWORD ?  ;数据块的起始RVA
isize DWORD ?  ;数据块的大小
IMAGE_DATA_DIRECTORY ENDS

一、数组的第一项指向输出表

输出表是一个IMAGE_EXPORT_DIRECTORY结构

IMAGE_EXPORT_DIRECTORY STRUCT
Characteristics DWORD ?  ;输出属性,目前未定义,总是为0
TimeDateStamp DWORD ?  ;输出表创建时间(GMT时间)
MajorVersion WORD ?  ;输出表主版本号,未使用
MinorVersion WORD ?  ;次版本号,未使用
nName DWORD ?  ;指向一个ASCII字符串的RVA,这个字符串是与这些输出函数关联的DLL名字
nBase DWORD ?  ;基数,加上序数就是函数地址数组的索引值
NumberOfFunctions DWORD ?  ;EAT中条目数(输出函数地址表)
NumberOfNames DWORD ?  ;ENT中的条目数(输出函数名称表)
AddressOfFunctions DWORD ?  ;EAT的RVA
AddressOfNames DWORD ?  ;ENT的RVA
AddressOfNameOrdinals DWORD ?  ;输出序数表的RVA
IMAGE_EXPORT_DIRECTORY ENDS

输出表结构

AddressOfNameOrdinals指向的输出序数表的作用是把EAT和ENT联系起来,当PE装载器在ENT中找到对应函数的位置,再从输出序数表读取相应位置的值,这个值便是在EAT中的偏移,这个值作为进入EAT的索引(这里有一点不明白,看雪加密解密第三版书上说此值作为输出函数的输出序数并且要考虑Base阈值,但是实例中输出序数表中的值是函数在EAT中的偏移)。当通过序数来查询一个输出函数是,减去nBase阈值得到进入EAT的索引

二、数据目录表第二项输入表

输入表以一个IMAGE_IMPORT_DESCRIPTOR(IID)结构数组开始,数组以一个全为0的IID结构结束

IMAGE_IMPORT_DESCRIPTOR STRUCT
union
Characteristics dd ?
OriginalFirstThunk dd ?  ;指向输入名称表(INT)的RVA
ends
TimeDateStamp dd ?  ;时间标志
ForwarderChain dd ?  ;第一个被转向的API的索引,一般为0
Name1 dd ?  ;指向DLL的名字
FirstThunk dd ?  ;指向输入地址表(IAT)的RVA
IMAGE_IMPORT_DESCRIPTOR ENDS

INT和IAT都是一个IMAGE_THUNK_DATA结构数组,同样以一个全为0的IMAGE_THUNK_DATA结构作为结束

IMAGE_THUNK_DATA32 STRUCT
union u1
ForwarderString dd ?  ;指向一个转向者字符串的RVA
Function dd ?  ;被输入的函数的内存地址
Ordinal dd ?  ;被输入的API的序数值
AddressOfData dd ?  ;指向IMAGE_IMPORT_BY_NAME
ends
IMAGE_THUNK_DATA32 ENDS

INT中当IMAGE_THUNK_DATA值得最高位为1是,表示函数以序号方式输入;最高位为0是以函数名的方式输入,此时指向一个IMAGE_IMPORT_BY_NAME结构

IMAGE_IMPORT_BY_NAME结构为:

IMAGE_IMPORT_BY_NAME STRUCT
Hint dw ?  ;指出此函数在所驻留的DLL的输出表中的序号,有些连接器将此值设为0
Name1 db ?  ;输入函数的函数名,这是一个可变尺寸域
IMAGE_IMPORT_BY_NAME ENDS

PE加载器先同过INT找到相应的函数,再将对应函数的地址重写入IAT,此时程序仅依靠IAT提供的函数地址就可以正常运行了

因为一开始IAT跟INT是一样的,有些链接器就只用一个IAT结构就完成了输入(不知道在哪儿看到过这句话,大概意思就这个)

三、数据目录表第三项资源表

数据目录表中的IMAGE_DIRECTORY_ENTRY_RESOURCE条目包含资源的RVA和大小,资源目录每一节点包含一个IMAGE_RESOURCE_DIRECTORY结构和数个IMAGE_RESOURCE_DIRECTORY_ENTRY结构,IMAGE_RESOURCE_DIRECTORY指出紧跟在后面的IMAGE_RESOURCE_DIRECTORY_ENTRY结构的个数

从网上找了张图,这张图对资源目录结构的描述很清晰

IMAGE_RESOURCE_DIRECTORY STRUCT
Characteristics dd ?  ;理论上是资源的属性标志,但通常为0
TimeDateStamp dd ?  ;资源建立的时间
MajorVersion dw ?  ;理论上放置资源的版本,但通常为0
MinorVersion dw ?  ;
NumberOfNamedEntries dw ?  ;使用名字的资源条目的个数
NumberOfIdEntries dw ?  ;使用ID数字资源条目的个数
IMAGE_RESOURCE_DIRECTORY ENDS

NumberOfNamedEntries和NumberOfIdEntries加起来的和等于本目录IMAGE_RESOURCE_DIRECTORY_ENTRY的个数

IMAGE_RESOURCE_DIRECTORY_ENTRY STRUCT
union
rName RECORD NameIsString:,NameOffset:
Name1 dd ?
Id dw ?
ends
union
OffsetToData dd ?
rDirectory RECORD DataIsDirectory:,OffsetToDirectory:
ends
IMAGE_RESOURCE_DIRECTORY_ENTRY ENDS

  就先总结到这儿吧2015-07-02/17:11:08

PE文件结构整理的更多相关文章

  1. 再探.NET的PE文件结构(安全篇)

    一.开篇 首先写在前面,这篇文章源于个人的研究和探索,由于.NET有自己的反射机制,可以清楚的将源码反射出来,这样你的软件就很容易被破解,当然这篇文章不会说怎么样保护你的软件不被破解,相反是借用一个软 ...

  2. PE文件结构详解(六)重定位

    前面两篇 PE文件结构详解(四)PE导入表 和 PE文件结构详解(五)延迟导入表 介绍了PE文件中比较常用的两种导入方式,不知道大家有没有注意到,在调用导入函数时系统生成的代码是像下面这样的: 在这里 ...

  3. PE文件结构详解(五)延迟导入表

    PE文件结构详解(四)PE导入表讲 了一般的PE导入表,这次我们来看一下另外一种导入表:延迟导入(Delay Import).看名字就知道,这种导入机制导入其他DLL的时机比较“迟”,为什么要迟呢?因 ...

  4. PE文件结构详解(四)PE导入表

    PE文件结构详解(二)可执行文件头的最后展示了一个数组,PE文件结构详解(三)PE导出表中解释了其中第一项的格式,本篇文章来揭示这个数组中的第二项:IMAGE_DIRECTORY_ENTRY_IMPO ...

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

    上篇文章 PE文件结构详解(二)可执行文件头 的结尾出现了一个大数组,这个数组中的每一项都是一个特定的结构,通过函数获取数组中的项可以用RtlImageDirectoryEntryToData函数,D ...

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

    在PE文件结构详解(一)基本概念里,解释了一些PE文件的一些基本概念,从这篇开始,将详细讲解PE文件中的重要结构. 了解一个文件的格式,最应该首先了解的就是这个文件的文件头的含义,因为几乎所有的文件格 ...

  7. PE文件结构详解(一)基本概念

    PE(Portable Execute) 文件是Windows下可执行文件的总称,常见的有DLL,EXE,OCX,SYS等,事实上,一个文件是否是PE文件与其扩展名无关,PE文件可以是任 何扩展名.那 ...

  8. PE文件结构(四) 输出表

    PE文件结构(四) 參考 书:<加密与解密> 视频:小甲鱼 解密系列 视频 输出表 一般来说输出表存在于dll中.输出表提供了 文件里函数的名字跟这些函数的地址, PE装载器通过输出表来改 ...

  9. PE文件结构(五岁以下儿童)基地搬迁

    PE文件结构(五岁以下儿童) 參考 书:<加密与解密> 视频:小甲鱼 解密系列 视频 基址重定位 链接器生成一个PE文件时,它会如果程序被装入时使用的默认ImageBase基地址(VC默认 ...

随机推荐

  1. COJ 0343 WZJ的公司(二)

    传送门:http://oj.cnuschool.org.cn/oj/home/problem.htm?problemID=313 试题描述: WZJ的公司放假了!为了保证假期期间公司的安全,WZJ决定 ...

  2. 搭了个hexo博客

    上周六,气温还行,不想看书,开着电脑又想做点儿什么,于是就尝试了一把闻名已久的静态博客. 博客程序使用的是一位台湾小哥用node.js开发的hexo,传说页面生成神速.相对应的,大名鼎鼎的octopr ...

  3. HDOJ 2089 不要62(打表)

    Problem Description 杭州人称那些傻乎乎粘嗒嗒的人为62(音:laoer). 杭州交通管理局经常会扩充一些的士车牌照,新近出来一个好消息,以后上牌照,不再含有不吉利的数字了,这样一来 ...

  4. HDOJ 1098 Ignatius's puzzle

    Problem Description Ignatius is poor at math,he falls across a puzzle problem,so he has no choice bu ...

  5. selenium webdriver python 操作浏览器

    新建driver driver=webdriver.Firefox() driver=webdriver.Ie() driver=webdriver.Chrome()   打开一个链接 driver. ...

  6. Redis需要你来做的算法优化

    阅读一个优秀的Server内核实现,早期的代码比后期的代码要好得多.因为在早期的代码里,你可以学习到一个黑客级别的程序猿到底在思考什么.同时,你能看到他哪里写得差劲,以及后来是怎么优化的. 如果你一心 ...

  7. C++ —— 非常量引用不能指向临时对象

    目录 举例 分析 解决 1.举例 非常量引用 指向 临时对象 —— 即:将 临时对象 传递给 非常量引用类型. 如以下情况就会出现: 实现实数Rational类,实数可以使用+号相加,运算的结果要可以 ...

  8. android插件化-获取apkplug框架已安装插件-03

    上一篇文章成功的将apkplug框架嵌入了应用中而且启动 链接http://www.apkplug.com/blog/?post=10 这一篇文章实现怎样获取全部已安装插件 一 获取框架的System ...

  9. Lucene自定义扩展QueryParser

    Lucene版本:4.10.2 在使用lucene的时候,不可避免的需要扩展lucene的相关功能来实现业务的需要,比如搜索时,需要在满足一个特定范围内的document进行搜索,如年龄在20和30岁 ...

  10. Spring事务管理使用

    发现问题 最近,碰到一个问题,再用spring实现事务管理的时候,发现不起作用,在出异常时,并不会回滚数据库操作. 我想实现的功能如下: @Transactional(isolation=Isolat ...