前言

下文提及的相关数据结构,代码片段均为64位环境,32位环境会加以标注.

PE文件

PE(即Portable Executable)表示可移植的可执行文件,是Windows平台原生的可执行代码载体(此处要区别于NET的IL, JAVA的字节码等),Windows平台所有可以被加载执行的文件都遵从PE文件格式。

注:在linux平台下的可执行文件都遵从ELF文件格式,PE与ELF文件格式都继承于Unix的COFF文件格式,由此可见,虽然不同平台上的可执行文件不能兼容,但是从设计思想上有很多共通之处。

PE文件格式详解

文件数据结构布局

(网上有很多查看PE文件格式的工具,我常用010editor配合其EXE模版)

注:010editor模版所显示的在Section Data后还有其他内容,这只是模版的显示,相关内容其实还是在某个Section Data内部,但是由于其有特殊用途,故被模版单独展示。

由上图所示,一个PE文件有定长部分和不定长部分,这也体现了PE文件格式良好的可扩展性和兼容性。

定长部分:

DOS header + DOS Stub + NT header

不定长部分:

Section header[n] + Section data[n]

磁盘布局与内存布局

一个PE文件,在加载运行前后的布局这里我叫做磁盘布局和内存布局,这两种布局由于不同时期的对齐粒度不同导致有所差异:磁盘布局的对齐粒度是0x200,内存布局的对齐粒度是0x1000。

所以这里就有一个偏移地址转换的问题,PE文件中大体上有四类地址:

  • 虚拟内存地址(VA):RVA + 加载基地址

  • 相对虚拟地址(RVA):内存偏移

  • 文件偏移地址(FOA):文件偏移

  • 特殊地址:这中地址很少见,在资源节中有使用,其计算方式类似于汇编相对跳转的计算

相关数据结构(依次说明)

//DOS Header结构typedef struct _IMAGE_DOS_HEADER{     WORD e_magic;        //DOS文件签名 0x4d5a     WORD e_cblp;     WORD e_cp;     WORD e_crlc;     WORD e_cparhdr;     WORD e_minalloc;     WORD e_maxalloc;     WORD e_ss;     WORD e_sp;     WORD e_csum;     WORD e_ip;     WORD e_cs;     WORD e_lfarlc;     WORD e_ovno;     WORD e_res[4];     WORD e_oemid;     WORD e_oeminfo;     WORD e_res2[10];     LONG e_lfanew;        //NT头偏移} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

其中最重要的是e_magice_lfanew字段,e_magic字段是文件头,固定值0x4D5Ae_lfanew:NT头的文件偏移,通常是0xE8

//DOS Stub这是一小段Dos程序,当在Dos系统下运行时,会打印出"This program cannot be run in DOS mode",现在基本不会用到。
//NT header结构typedef struct _IMAGE_NT_HEADERS64 {  DWORD                   Signature;        //PE签名,0x4550  IMAGE_FILE_HEADER       FileHeader;        //文件头  IMAGE_OPTIONAL_HEADER64 OptionalHeader;    //可选头} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64;

//File Header结构typedef struct _IMAGE_FILE_HEADER {  WORD  Machine;                            //运行平台  WORD  NumberOfSections;                    //Section Header的数目  DWORD TimeDateStamp;                        //编译时间戳  DWORD PointerToSymbolTable;                //COFF符号表指针  DWORD NumberOfSymbols;                    //符号数目  WORD  SizeOfOptionalHeader;                //可选头大小,32位与64位大小不同  WORD  Characteristics;                    //属性,实际上是一个bitmap} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

运行平台:

value 平台
IMAGE_FILE_MACHINE_I386(0x014c) x86
IMAGE_FILE_MACHINE_IA64(0x0200) Intel Itanium
IMAGE_FILE_MACHINE_AMD64(0x8664) x64
//IMAGE_FILE_HEADER.Characteristics字段struct FILE_CHARACTERISTICS Characteristics {    WORD IMAGE_FILE_RELOCS_STRIPPED : 1                //在可执行文件中没有使用。    WORD IMAGE_FILE_EXECUTABLE_IMAGE : 1            //置1表示该文件为exe文件。    WORD IMAGE_FILE_LINE_NUMS_STRIPPED : 1          //在可执行文件中没有使用。    WORD IMAGE_FILE_LOCAL_SYMS_STRIPPED : 1         //在可执行文件中没有使用。    WORD IMAGE_FILE_AGGRESIVE_WS_TRIM : 1           //在可执行文件中没有使用。    WORD IMAGE_FILE_LARGE_ADDRESS_AWARE : 1         //该应用程序可以处理大于2G的地址。    WORD IMAGE_FILE_BYTES_REVERSED_LO : 1           //在可执行文件中没有使用。    WORD IMAGE_FILE_32BIT_MACHINE : 1               //置1表示希望是32位平台    WORD IMAGE_FILE_DEBUG_STRIPPED : 1              //在可执行文件中没有使用。    WORD IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP : 1     //置1表示该程序不能在可移动介质中运行。    WORD IMAGE_FILE_NET_RUN_FROM_SWAP : 1           //置1表示该程序不能在网络中运行。    WORD IMAGE_FILE_SYSTEM : 1                      //在可执行文件中没有使用。    WORD IMAGE_FILE_DLL : 1                         //置1表示文件是一个动态链接库。    WORD IMAGE_FILE_UP_SYSTEM_ONLY : 1              //置1该文件应仅在单处理器计算机上运行。    WORD IMAGE_FILE_BYTES_REVERSED_HI : 1           //在可执行文件中没有使用。}

//IMAGE_OPTIONAL_HEADER64结构typedef struct _IMAGE_OPTIONAL_HEADER64 {  WORD                 Magic;                        //判断是32还是64位可执行映像  BYTE                 MajorLinkerVersion;            //链接器主版本号  BYTE                 MinorLinkerVersion;            //链接器此版本号  DWORD                SizeOfCode;                    //多个代码段大小  DWORD                SizeOfInitializedData;        //多个初始化数据段大小  DWORD                SizeOfUninitializedData;        //多个为初始化数据段大小  DWORD                AddressOfEntryPoint;            //代码入口点的RVA  DWORD                BaseOfCode;                    //代码段基址  ULONGLONG            ImageBase;                    //加载基址  DWORD                SectionAlignment;            //内存对齐粒度,默认为页面大小4k  DWORD                FileAlignment;                //文件对齐粒度  WORD                 MajorOperatingSystemVersion;    //操作系统主版本号  WORD                 MinorOperatingSystemVersion;    //操作系统此次版本号  WORD                 MajorImageVersion;            //可执行文件主版本号  WORD                 MinorImageVersion;            //可执行文件次版本号  WORD                 MajorSubsystemVersion;        //子系统主版本号  WORD                 MinorSubsystemVersion;        //子系统此次版本号  DWORD                Win32VersionValue;            //保留,必须为0  DWORD                SizeOfImage;                    //占用内存的大小,需对齐  DWORD                SizeOfHeaders;            //定长部分+SectionHeader数组的大小,需对齐  DWORD                CheckSum;                    //校验和  WORD                 Subsystem;                    //所需子系统环境  WORD                 DllCharacteristics;            //映像属性  ULONGLONG            SizeOfStackReserve;            //栈保留大小  ULONGLONG            SizeOfStackCommit;            //栈提交大小  ULONGLONG            SizeOfHeapReserve;            //堆保留大小  ULONGLONG            SizeOfHeapCommit;            //堆提交大小  DWORD                LoaderFlags;                    //在可执行文件中没有使用。  DWORD                NumberOfRvaAndSizes;            //DataDirectory表表项的数目  IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];    //DataDirectory表} IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64;

可选头虽说名字叫可选头,但是确是PE⽂件中最不可缺少的部分,其中 DataDirectory 数组尤为重要,⾥⾯是系统加映像时,初始化资源的重要数据的索引,数组⼀共有16项,⼀项保留,其余15项分别对应15种资源索引,由于篇幅原因, DataDirectory 会在后⾯的⽂章中单独详细解读。

以上就是PE⽂件中的定⻓结构部分,下⾯是不定⻓的Section Header数组和各个Section Data

//Section Header数组就是由一项一项的IMAGE_SECTION_HEADER结构构成//IMAGE_SECTION_HEADER结构typedef struct _IMAGE_SECTION_HEADER {  BYTE  Name[IMAGE_SIZEOF_SHORT_NAME];    //char类型的八字节数组,保存section的名称  union {    DWORD PhysicalAddress;    DWORD VirtualSize;  } Misc;                                //section加载到内存的大小  DWORD VirtualAddress;                    //section起始地址的RVA  DWORD SizeOfRawData;                    //Misc.VirtualSize内存对齐后的大小  DWORD PointerToRawData;                //section起始地址的文件偏移  DWORD PointerToRelocations;            //指向该部分的重定位条目的文件偏移。为0则没有重定位。  DWORD PointerToLinenumbers;            //未使用  WORD  NumberOfRelocations;            //未使用  WORD  NumberOfLinenumbers;            //未使用  DWORD Characteristics;                //section属性} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

section常属性:

flag 属性
IMAGE_SCN_CNT_CODE(0x00000020) 本节包含可执行代码。
IMAGE_SCN_CNT_INITIALIZED_DATA(0x00000040) Section 含初始化数据。
IMAGE_SCN_CNT_UNINITIALIZED_DATA(0x00000080) Section 含未初始化的数据。
IMAGE_SCN_GPREL(0x00008000) 本节包含通过全局指针引用的数据。
IMAGE_SCN_LNK_NRELOC_OVFL(0x01000000) 本节包含扩展的重定位。
IMAGE_SCN_MEM_SHARED(0x10000000) 该部分可以在内存中共享。
IMAGE_SCN_MEM_EXECUTE(0x20000000) 该部分可以作为代码执行。
IMAGE_SCN_MEM_READ(0x40000000) 可读
IMAGE_SCN_MEM_WRITE0x80000000 可写

其他不常用标志见后面 MSDN 链接。

最后说明一下文件偏移和内存相对偏移的计算:FOA = RVA - VirtualAddress + PointerToRawData首先要判断的的RVA地址所落在的section,条件是VirtualAddress <= RVA < VirtualAddress + SizeOfRawData

附上我写的转换代码,⼀个壳的代码⽚段:

/*参数:RVA:内存相对偏移,pNT:指向NT头的指针返回:转换后的文件偏移*/

UINT32 RvaToOffset(UINT32 RVA, PIMAGE_NT_HEADERS pNT){    UINT32 NumberOfSections = pNT->FileHeader.NumberOfSections; //获取Section数量    UINT32 offset = 0;    PIMAGE_SECTION_HEADER pSH = (PIMAGE_SECTION_HEADER)((UINT64)pNT + sizeof(IMAGE_NT_HEADERS));    //获取Section header数组

    for (UINT32 i = 0; i     {        pSH->Characteristics |= IMAGE_SCN_MEM_WRITE;    //为每个section添加写属性        if (RVA >= pSH->VirtualAddress&& RVA VirtualAddress + pSH->SizeOfRawData)        {            offset = RVA - pSH->VirtualAddress + pSH->PointerToRawData;        }    }    return offset;}

常⻅section 

这些都是VS编译器⽣成的,⾥⾯的数据带有特定作⽤.

  • .bss:存放未初始化数据

  • .data:存放初始化数据

  • .edata:存放导出表

  • .idata:存放导⼊表

  • .pdata:存放异常信息

  • .rdata:存放初始化的只读数据

  • .reloc:存放重定位信息

  • .rsrc:存放资源数据

  • .text:存放可执⾏代码

  • .xdata:存放异常信息

⾃定义section

//在vs中可以通过以下方式自定一个section,命名为syclover#pragma data_seg("syclover") char WhoAmI[] = "syclover!"; #pragma data_seg()

注:SectionHeadr和SectionData并不是一一对应的关系,例如.bss段,占内存但是并不占文件空间,在文件中可以找到.bss段对应的SectionHeadr条目,但是并没有对应的SectionData存在

参考

https://blog.csdn.net/liuyez123/article/details/51281905

https://docs.microsoft.com/zh-cn/windows/win32/api/winnt/ns-winnt-image_nt_headers64

https://docs.microsoft.com/zh-cn/windows/win32/api/winnt/ns-winnt-image_section_header

三叶草小组公众号

微信号 : 三叶草小组Syclover

新浪微博:@三叶草小组Syclover

在与你相遇的路上马不停蹄~

rgb565和rgb555的文件头区别_Windows可执行文件格式相关推荐

  1. 通过文件头标识判断图片格式

    通过文件头标识判断图片格式 用Delphi从内存流中通过文件头标识判断图片格式 文件头标识大全: http://www.garykessler.net/library/file_sigs.html h ...

  2. 文件头标识判断图片格式

    同图片的文件头标志: 图片的格式很多,一个图片文件的后缀名并不能说明这个图片的真正格式什么,那么如何获取图片的格式呢?我想到了几个简单但有效的方法,那就是读取图片文件的文件头标识.我们知道各种格式的图 ...

  3. wav文件头修复_windows文件夹分析(xp版,网络整理)

    ├-WINDOWS │ ├-system32(存放Windows的系统文件和硬件驱动程序) │ │ ├-config(用户配置信息和密码信息) │ │ │ └-systemprofile(系统配置信息 ...

  4. 2014 ecb,_it’s_easy_as_123(修改bmp文件头) 攻防世界;

    这道题有两种方法,主要就是把bmp文件头,修改成正确格式,图片就可以查看了 拿到文件放入winhex里面分析 发现是个压缩包,修改文件后缀为zip打开, Somebody leaked a still ...

  5. web.xml文件头出错

    原先将web.xml文件头设置为如下格式 <?xml version="1.0" encoding="UTF-8"?> <web-app ve ...

  6. CTFHub笔记之WEB文件上传:无验证、前端验证、文件头检查

    小白一个,如有错误请指正! 一.无验证 我们这次来到了文件上传模块了,这是一个无验证的文件上传,那我们直接上传一个一句话木马即可. 代码: <?php@eval($_REQUEST[" ...

  7. 程序的本质之二ELF文件的文件头、section header和program header

    操作系统:CentOS Linux release 7.7.1908 内核版本:3.10.0-1062.1.1.el7.x86_64 运行平台:x86_64 参考文献:http://refspecs. ...

  8. java 判断文件类型(根据文件头)

    后缀判断的隐患: 对于判断前端(或网络)发送过来文件的类型,有些同学第一个想到的可能就是:根据其后缀名进行格式的判断... 正常情况下,是可以这样做.但实际上,任何文件的后缀都可以随意命名,因此仅通过 ...

  9. 视频文件头解析之---avi

    AVI格式是音频视频交错(Audio VideoInterleaved)的英文缩写,它是Microsoft公司开发的一种符合RIFF文件规范的数字音频与视频文件格式,原先用于Microsoft Vid ...

最新文章

  1. 4蓝图遍历actor_【虚幻4笔记15】浮动平台
  2. R语言临床预测模型的评价指标与验证指标实战:综合判别改善指数IDI(Integrated Discrimination Improvement, IDI)
  3. linux date
  4. 马斯克公布火星太空船最新照片:施工已达最后一步,10月有望正式推出
  5. 树莓派的linux系统安装,树莓派安装Linux操作系统
  6. 整数和小数的移码计算方法
  7. docker ps 和docker ps -a
  8. 福布斯中国2018年30位30岁以下精英榜单发布,为什么是他们?
  9. TensorFlow Seq2Seq
  10. 一次选中多个物体_经验之谈|Anchor Boxes:物体检测的关键
  11. 项目后台运行关闭_iOS到底有没有必要上滑强制关闭APP?
  12. java heapdump 分析工具_heapdump分析工具
  13. python写斗地主游戏_python编程斗地主 python编程入门
  14. php中的图片变名为8位用什么,CSS_详解PNG图片,1、PNG图片类型 PNG格式有8位、 - phpStudy...
  15. 中国行政区域经纬度(免费下载)
  16. No base URI; hope URI is absolute: http://
  17. Stata | 时间序列操作
  18. 洛谷 P1489 猫狗大战
  19. 服务器运维KPI指标,IT运维包括哪些内容,考核标准是什么
  20. html拖拽模态框,bootstrap模态框实现拖拽效果

热门文章

  1. 问题解决:vue dev模式没问题,dist之后页面not found
  2. nodejs 当前文件路径_NodeJs的几种文件路径
  3. html基础总结4-实现点击图片弹出放大图片--不用插件
  4. 2017-2018网络攻防第二周
  5. Bash:如何查看某个文件是那个程序/进程创建的?
  6. Understand one Simple Factory Pattern
  7. DataList 外部事件获取DataList内部值
  8. copy时候明细消失没有了
  9. C++ struct constructor
  10. Linux文件查看命令图解