1.Windows的二进制文件格式PE/COFF

在32位Windows平台下,微软引入了一种叫PE(Protable Executable)的可执行格式。PE文件格式和ELF都是由COFF格式发展而来的。而对于VISUALC++编译器产生的目标文件仍然使用COFF格式。由于PE是COFF的一种扩展,所以它们的结构在很大程度上相同,甚至跟ELF文件的基本结构也相同。即Windows下目标文件默认为COFF格式,而可执行文件为PE格式。可以将它们统称为PE/COFF格式。

随着64位Windows的发布,PE文件结构对应的新的文件格式叫做PE32+。新的PE32+并没有添加任何结构,最大的变化就是把那些原来32位的字段变成了64位。绝大部分情况下,PE32+与PE的格式一致。

在VISUALC++中可以使用“#pragma”这个编译器指示,将变量或函数放到自定义的段。

#pragma data_seg("FOO")
int global=1;
#pragma data_seg(".data")

表示将把全局变量“global”放到“FOO”段里面去,然后再使用“#pragma”将这个编译器指示换回来,恢复到“.data”。

2.PE的前身——COFF

使用SimpleSection.c为例子。

int printf(const char * format, ...);int global_init_var=84;
int global_uninit_var;void func1(int i) {printf("%d\n",i);
}int main() {static int static_var=85;static int static_var2;int a=1;int b;func1(static_var+static_var2+a+b);return a;
}

在Windows平台下下载Visual studio后,Visual C++编译器等也就安装好了。之后在控制台就可以直接使用,“cl“是Visual C++的编译器。使用以下编译命令:

cl /c /Za SimpleSection.c

/c参数表示只编译,不链接。VISUAL C++有一些C和C++语言的专有扩展,这些扩展并没有定义ANSI C标准或ANSI C++标准。/Za参数表示禁用这些扩展,使得我们的程序跟标准的C/C++兼容。使用/Za参数时,编译器自动定义了__STDC__这个宏,可以在程序中通过判断这个宏是否被定义而确定编译器是否禁用了Microsoft C/C++语法扩展。

该命令执行后会生成一个同名的.obj目标文件。使用dumpbin查看目标文件的结构:

dumpbin /ALL SimpleSection.obj > SimpleSection.txt

/ALL参数表示打印目标文件的所有相关信息,我们把输出信息重定向了一个文本文件中。/SUMMARY参数可以查看整个文件的基本信息,它只输出所有段的段名和长度:

COFF几乎和ELF一样,也是由文件头及后面的若干个段组成,再加上文件末尾的符号表,调试信息的内容,就构成了COFF文件的基本结构。COFF文件的文件头包括了两部分,一个是描述文件总体结构和属性的映像头(Image Header),另外一个是描述该文件中包含的段属性的段表(Section Table)。如图所示:

描述文件总体结构和属性的映像头的结构是IMAGE_FILE_HEADER,如下所示:

在SimpleSection.txt中的输出信息中可以看到,开始一段”FILE HEADER VALUES“中的内容和COFF映像头中的成员是一一对应的。

字段含义从字面意思很好理解,依次是机器类型,段的数量,PE或COFF文件创建的时间,符号表在PE/COFF中的位置,Optional Header的大小(这个结构只存在于PE可执行文件,COFF目标文件中该结构不存在,所以这里是0),属性。

映像头后面紧跟着的是COFF文件的段表,它是一个类型位”IMAGE_SECTION_HEADER“结构的数组,数组里面每个元素代表一个段,这个数组元素的个数就文件头中NumberOfSections的值。IMAGE_SECTION_HEADER这个结构是用来描述段的属性的,如下所示:

typedef struct _IMAGE_SECTION_HEADER {BYTE Name[8];   //段名union {DWORD PhysicalAddress;  //物理地址DWORD VirtualSize;  }Misc;DWORD VirtualAddress;   //虚拟地址DWORD SizeOfRawData;    //原始数据大小DWORD PointerToRawData;     //段在文件中的位置DWORD PointerToRelocations;     //该段的重定位表在文件中的位置DWORD PointerToLinenumbers;     //该段的行号表在文件中的位置WORD NumberOfRelocations;WORD NumberOfLinenumbers;DWORD Characteristics;  //标志位
}IMAGE_SECTION_HEADER,*PIMAGE_SECTION_HEADER;

其中几个比较重要的字段:、

(1)VirtualSize:该段被加载至内存后的大小。

(2)VirtualAddress:该段被加载至内存后的虚拟地址。

(3)SizeOfRawData:该段在文件中的大小,这个值可能和VirtualSize不一样,比如.bss段的SizeOfRawData是0,而VirtualSize值是.bss段的大小,另外涉及一些内存对齐等问题,导致SizeOfRawData一般比VirtualSize小。

(4)Characteristics:段的属性。属性里主要包括段的类型(代码,数据,bss),对齐方式及可读可写可执行等权限。段的属性是一些标志位的组合。

段表之后就是一个个段的实际内容了,如代码段,数据段和BSS段,它们的内容和存储方式和ELF文件几乎一样。这里主要介绍两个ELF文件中不存在的段:“.drectve”和“.debug$S”。

3.链接指示信息

SimpleSection.txt中关于“.drectve”段的内容如下:

.drectve的内容是编译器传递给链接器的指令。段名后面是段的属性,注意到最后一个段的属性是标志位flags,标志位为“0x100A00”,含义如下:

dumpin也把这三个组合属性打印出来了,紧跟在flags之后。

紧跟着属性的就是该段在文件中的原始数据,用十六进制显示的原始数据及相应的ASCII字符。从图可知,表示的内容是“/DEFAULTLIB:'LIBCMT'”的链接指令。这个参数表示编译器希望告诉链接器,该目标文件需要LIBCMT这个默认库。所以在链接过程中,链接器看到输入文件中有这个段,会将”/DEFAULTLIB:'LIBCMT'”参数添加到链接参数中,即将libcmt.lib加入链接输入文件中。

我们可以在cl编译器参数里面加入/Zl来关闭默认C库的链接指令。

4.调试信息

COFF文件中所有以“.debug”开始的段都包含着调试信息。如,“.debug$S”表示包含的是符号相关的调试信息段,“.debug$P”表示包含预编译头文件相关的调试信息段,“.debug$T”表示包含类型相关的调试信息段。可以从该段的文本信息中看到目标文件的原始路径,编译器信息等。内容如下:

5.符号表

“SimpleSection.txt”的最后部分是COFF符号表,和ELF文件的符号表一样,主要就是符号名,符号类型,所在的位置等。内容如下:

最左列是符号的编号,然后是符号的大小,即符号所表示的对象所占用的空间,第三列是符号所在的位置,ABS表示符号是个常量,不在任何段中,SECT1表示符号定义在第一个段中,依次类推,UNDEF表示符号未定义,即该符号定义在其他目标文件中。第四列是符号类型,对于C语言的符号,COFF只区分两种,一种是变量和其他符号,类型为notype,另外一种是函数,类型为notype()。第五列是符号的可见范围,Static表示符号是局部变量,只有目标文件内部可见,External表示符号是全局变量,可以被其他目标文件引用。最后一列是符号名,会把修饰前后的名字都打印出来,后面括号里面的是未修饰的符号名。

可以看到有个比较特殊的符号$SG4198,它表示的其实是“%d\n”字符串常量。对比之下,ELF文件并没有为字符串常量自动生成符号。另外所有的段名都是一个符号,dumpbin如果碰到某个符号是一个段的段名,那么它还会解析该符号所表示的段的基本属性,可以看到每个段名符号后紧跟着一行段的基本属性,分别是段长,重定位数,行号数和校验和。

6.Windows下的ELF——PE

PE文件是基于COFF的扩展,它比COFF文件多了几个结构。主要有两个变化:

(1)文件最开始的部分不是COFF文件头,而是DOS MZ可执行文件格式的文件头和桩代码。

(2)原来的COFF文件头中的“IMAGE_FILE_HEADER”部分扩展成了PE文件文件头结构“IMAGE_NT_HEADERS”,这个结构包括了原来的“Image Header”及新增的PE扩展头部结构(PE Optional Header)。

PE文件的结构如下所示:

DOS下的执行文件的扩展名和Windows下的可执行文件扩展名一样,都是“.exe”。但DOS下的可执行文件格式是MZ,和Windows下的PE完全不同,虽然使用的是相同的扩展名。实际上,在Windows发展的早期,DOS系统还如日中天,Windows还不能脱离DOS环境独立运行,为Windows编写的程序应尽量兼容于DOS系统,

PE文件中的“Image DOS Header”和“DOS Stub”这两个结构就是为了兼容DOS系统而设计的。其中IMAGE_DOS_HEADER”结构其实和DOS的“MZ”可执行结构的头部完全一样。所以PE文件也算是一个“MZ”文件。“IMAGE_DOS_HEADER”的结构中前两个字节是“e_magic”结构,里面包含的“MZ”这两个字母的ASCII码,“e_cs”和“e_ip”两个成员指向程序的入口地址。

当PE可执行映像在DOS下被加载时,DOS系统检测该文件的前两个字节是“MZ”,于是认为它是一个“MZ”可执行文件。然后DOS将这个PE文件当作正常的MZ文件执行。接着DOS读取e_cs和e_ip这两个值,跳转到程序的入口地址。然而在PE文件中,e_cs和e_ip并不指向程序真正的入口地址,而是指向文件中的DOS Stub。DOS Stub是一段可以在DOS下运行的一小段代码,这段代码作用只是在终端输出“This program cannot be run in DOS”,然后退出程序。所以如果在DOS下运行Windows的程序,会看到这段话,这是因为PE要兼容DOS的缘故。

IMAGE_DOS_HEADER结构中,我们主要关心e_lfanew成员,它表明了PE文件头IMAGE_NT_HEADERS在PE文件中的偏移,需要用这个值来定位到PE文件头。这个成员在DOS的MZ格式中为0。因此挡Windows执行一个.exe时,会判断e_lfanew是否为0,如果为0,则会启动DOS子系统来执行它。

IMAGE_NT_HEADERS是PE真正的文件头,包含了一个标记和两个结构体。标记是一个常量,对于一个合法的PE文件来说,它的值是0x00004550,在小端序中对应的是'P','E','\0','\0'这四个字符的ASCII码。

IMAGE_FILE_HEADER前面已介绍。IMAGE_OPTIONAL_HEADER对于PE可执行文件(包括DLL)来说,是必需的。定义如下:

在64位Windows下,结构名位IMAGE_OPTIONAL_HEADER64。但可以直接使用IMAGE_OPTIONAL_HEADER作为Optional Image Header的定义。在64位下,Visual C++编译器编译时会自动把IMAGE_OPTIONAL_HEADER定义成IMAGE_OPTIONAL_HEADER64,在32位下同理。

这里主要说一下结构里最后一个字段DataDirectory。

6.1 PE数据目录

Windows系统装载PE可执行文件时,需要很快找到一些装载所需要的数据结构,比如导入表,导出表,资源,重定位表等。这些常用的数据的位置和长度都被保存在了数据目录DataDirectory成员中。这个成员是IMAGE_DATA_DIRECTORY结构的一个数组,IMAGE_DATA_DIRECTORY定义如下:

数组大小为16,结构里两个成员分别是虚拟地址和长度。DataDirectory数组里每个元素对应一个包含一定含义的表。

其中还定义了一些以“IMAGE_DIRECTORY_ENTRY_”开头的宏,数值从0到15。实际含义是相关的表的宏定义在数组中的下标。比如IMAGE_DIRECTORY_ENTRY_EXPORT被定义为0,表示这个数组的第一个元素所包含的地址和长度就是导出表(Export Table)所在的地址和长度。

Windows PE/COFF相关推荐

  1. 程序员的自我修养--链接、装载与库笔记:Windows PE/COFF

    1. Windows的二进制文件格式PE/COFF 在32位Windows平台下,微软引入了一种叫PE(Portable Executable)的可执行格式.作为Win32平台的标准可执行文件格式,P ...

  2. 基于windows PE文件的恶意代码分析;使用SystemInternal工具与内核调试器研究windows用户空间与内核空间...

    基于windows PE文件的恶意代码分析:使用SystemInternal工具与内核调试器研究windows用户空间与内核空间 ******************** 既然本篇的主角是PE文件,那 ...

  3. CLR探索系列:托管PE/COFF文件格式侧窥

    一直都想写篇文章来说说那些尘封在PE/Coff文件格式下的那些事,还有Metadata和EEClass是如何表现了一个静态的PE格式文件在内存中的映射结构.    在这篇文章里,我不去介绍window ...

  4. [读书][笔记]WINDOWS PE权威指南《一》PE的原理和基础 之 第一章 环境搭建及简单破解

    文章目录 前言 前期准备 1.1 开发语言MASM32 1.1.1 设置开发环境 下载安装masm 环境变量配置 测试是否配置成功 1.1.2 开发第一个源程序HelloWorld.asm 配置 代码 ...

  5. Windows PE入门基础知识:Windows PE的作用、命名规则、启动方式、启动原理

    Windows PE的全名是WindowsPreinstallationEnvironment(WinPE)直接从字面上翻译就 是"Windows预安装环境".微软的本意是:Win ...

  6. Windows PE变形练手2-开发一套自己的PE嵌入模板

    PE嵌入模板 编写一段代码,生成一个已经处理过重定位信息,同时所有的内容都在代码段里,并且没有导入表的PE程序,把这个程序嵌入到其他PE的相关位置,能够独立的运行,接下来是整理了2个模板,一个是Hel ...

  7. Windows Pe 第三章 PE头文件(上)

    第三章  PE头文件 本章是全书重点,所以要好好理解,概念比较多,但是非常重要. PE头文件记录了PE文件中所有的数据的组织方式,它类似于一本书的目录,通过目录我们可以快速定位到某个具体的章节:通过P ...

  8. 菜鸟教程终极篇之Microsoft Windows Pre-installation Environment (Windows PE) 2.0

        大家好啊.通过以前的菜鸟日记大家感觉咋样?今天给大家带来新的知识.学完本文所讲的内容,谁敢在说你是菜鸟.你就拿砖块拍它!HOHO!!Go on!              平时我们在使用电脑中 ...

  9. Windows PE 背景知识

    1.什么是 Windows PE? 1.1 简要介绍 Windows Preinstallation Environment (Windows PE) 是一个为 Windows 安装而设计的最小操作系 ...

  10. 【虚拟机里测试Windows PE的方法】

    有时候我们需要在虚拟机里测试Windows PE镜像文件.下面就以VMWare Workstation为例,步骤如下: 1.运行虚拟机软件VMWare Workstation并新建虚拟机(如xp). ...

最新文章

  1. Window.Show()和Window.ShowDialog()区别
  2. 宝塔Linux, 反向代理服务器, 开启WSS
  3. unity 在图片的指定位置上添加按钮_Unity-利用免费资源快捷实现第三人称角色控制...
  4. Docker中容器数据持久化-数据卷的简单使用
  5. Flutter之window系统下配置开发环境以及在Android Studio里面运行hello word
  6. mysql 从库状态_大神教你自动发现监控mysql从库状态
  7. 跨境网上收款 找PayPal没错(获取Client ID 和 secret)
  8. 应用安全 - 操作系统安全 - Linux系统加固
  9. Elastic Search 学习笔记
  10. 字典制作、在线密码破解
  11. Silverlight4 多页打印实现
  12. 怎么给批处理(bat)添加注释
  13. 石墨烯——结构、制备方法与性能表征(1)
  14. 【安卓Android】VibratorService分析
  15. DNW的详细配置及使用过程
  16. Redis:集合SADD、SISMEMBER、SPOP、SRANDMEMBER、SREM、SMOVE、SCARD、SMEMBERS、SSCAN命令介绍
  17. 从网课安全到多场景挑战,网易云信构建全方位安全合规屏障
  18. 以管理员身份在当前目录打开命令行窗口
  19. Ubuntu 图文安装教程------2020年最新版
  20. 用vim编辑器在行首添加行号、序列号

热门文章

  1. win10关闭触摸板和键盘
  2. Java NIO?看这一篇就够了!
  3. PyGame实现小游戏案例[小甲鱼 PlayTheBall]
  4. 网络游戏植入营销的成功案例
  5. win10如何删除微软拼音输入法
  6. 数据库查看内存,数据大小
  7. EasyCVR人脸识别框在播放器上显示及消失的机制设定
  8. 产品开发都应该知道的8个网站,增强工作体验
  9. WEB前端开发学习5大网站,你用过几个?
  10. vr视频制作软件哪个好?Nibiru creator是企业的标配