pe文件结构 入门 教程
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文件详解(教程1-7)=========================================PE教程1: PE文件格式一览PE 的意思就是Portable Executable(可移植的执行体)。
它是Win32环境自身所带的执行体文件格式。
它的一些特性继承自Unix的Coff (common object file format)文件格式。
"portable executable"(可移植的执行体)意味着此文件格式是跨win32平台的: 即使Windows运行在非Intel的CPU上,任何win32平台的PE装载器都能识别和使用该文件格式。
当然,移植到不同的CPU上PE执行体必然得有一些改变。
所有win32执行体(除了VxD和16位的Dll)都使用PE文件格式,包括NT的内核模式驱动程序(kernel mode drivers)。
因而研究PE文件格式给了我们洞悉Windows结构的良机。
本教程就让我们浏览一下PE文件格式的概要。
上图是PE文件结构的总体层次分布。
所有PE文件(甚至32位的DLLs) 必须以一个简单的DOS MZ header 开始。
我们通常对此结构没有太大兴趣。
有了它,一旦程序在DOS下执行,DOS就能识别出这是有效的执行体,然后运行紧随MZ header 之后的DOS stub。
DOS stub实际上是个有效的EXE,在不支持PE文件格式的操作系统中,它将简单显示一个错误提示,类似于字符串"This program requires Windows" 或者程序员可根据自己的意图实现完整的DOS代码。
通常我们也不对DOS stub 太感兴趣: 因为大多数情况下它是由汇编器/编译器自动生成。
通常,它简单调用中断21h服务9来显示字符串"This program cannot run in DOS mode"。
逆向分析实验2PE文件结构分析

实验二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文件结构

检验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个成员,得出导出表中导出函数名称字符串所在地址的函数名称地址表。
PE文件结构详细说明

PE文件结构详细说明今天看这个PE文件结构真是好晕,不过还好,勉强看得明白。
就是这里面参数太多了,越看越悬乎了,哎,郁闷哦,这不,干脆来总结一下。
帮助自己理解。
不要急,慢慢来,先去喝杯水,现在整点报时,嘀嘀嘀嘀嘟。
现在是重庆时间2-14号4:21。
啊!冒视是情人节!再郁闷一下。
好了,水喝好了。
不吹废话了,言归正传。
说到PE文件结构,其实两天前我自己只是知道这么个东西,知道里面装了一些我不知道的但是很重要的东西。
这几天看Windows核心编程,看到这一节,看了3遍,晕,第一次看了觉得有那么个印象了,第二次看了就发现有些问题还不大明白,开始模糊了,第3次看了把一些疑虑搞定了,但是还是有些问题仍然模糊中。
哎!都怪自己太菜。
(可能是菜吃的太多的缘故:))。
又跑题了,再次言归正传。
先看看PE文件结构总体层次分布吧,我这里没有网络,本想在网上找个粘贴过来的,哎,只能是梦想,来自己画一个吧,建议你也自己画一下,加深印象。
(甜菜们跳过。
)。
画画中。
终于画完了,嘿嘿,现在时间5:09分。
看看这个图吧,什么感觉,是不是有点晕。
呵呵,反正我刚开始看这玩意儿的时候是这么个感觉,多看几次也就习惯了,嘿嘿。
先看看吧,有个印象。
现在继续深层次的解剖,就从DOS MZ Header 开始吧。
DOS MZ Header是一个IMAGE_DOS_HEADER类型的数据结构。
下面这就是IMAGE_DOS_HEADER数据结构的C语言定义,这个定义可以在winnt.h中找到。
结构大小=30*sizeof(WORD)+sizeof(LONG),sizeof(WORD)为2字节,sizeof(LONG)=4字节,所以整个IMAGE_DOS_HEADER大小为64字节。
typedef struct _IMAGE_DOS_HEADER { // DOS .EXE headerWORD e_magic; // Magic numberWORD e_cblp; // Bytes on last page of fileWORD e_cp; // Pages in fileWORD e_crlc; // RelocationsWORD e_cparhdr; // Size of header in paragraphsWORD e_minalloc; // Minimum extra paragraphs neededWORD e_maxalloc; // Maximum extra paragraphs neededWORD e_ss; // Initial (relative) SS valueWORD e_sp; // Initial SP valueWORD e_csum; // ChecksumWORD e_ip; // Initial IP valueWORD e_cs; // Initial (relative) CS valueWORD e_lfarlc; // File address of relocation tableWORD e_ovno; // Overlay numberWORD e_res[4]; // Reserved words注意这里是8字节WORD e_oemid; // OEM identifier (for e_oeminfo)WORD e_oeminfo; // OEM information; e_oemid specificWORD e_res2[10]; // Reserved words注意这里是20字节LONG e_lfanew; // File address of new exe header} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;其中第一个域e_magic成为魔术数字,它用于表示一个MS-DOS兼容的文件类型,所有MS-DOS兼容的可执行文件都将这个值设置为0x5A4D,表示ASCII字符MZ。
PE结构基础

+-------------------+
IMAGE_FILE_MACHINE_R4000 (0x166)
R4000 (MIPS)处理器,小尾
IMAGE_FILE_MACHINE_R10000 (0x168)
R10000 (MIPS)处理器,小尾
IMAGE_FILE_MACHINE_ALPHA (0x184)
PE文件格式系列译文之一----
【翻译】“PE文件格式”1.9版 完整译文(附注释)
=========================================================
原著:Bernd. Luevelsmeyer
| ... |
| |
+-------------------+
| |
| section n | --节n
| |
翻译:ah007
[注意:本译文的所有大小标题序号都是译者添加,以方便大家阅读。圆圈内的数字是注释的编号,其中注释②译自微软的《PECOFF规范》,其它译自网络。----译者]
一、前言(Preface)
------------------
PE(“portable executable”,可移植的可执行文件)文件格式,是微软WindwosNT,Windows95和Win32子集①中的可执行的二进制文件的格式;在WindowsNT中,驱动程序也是这种格式。它还能被应用于各种目标文件②和库文件中。
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结构中的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⽂件结构解析说明:本⽂件中各种⽂件头格式截图基本都来⾃看雪的《加密与解密》;本⽂相当《加密与解密》的阅读笔记。
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文件结构)思绪燃星火2009-02-09 17:07:26 阅读4216 评论30 字号:大中小订阅题记:本文发表于《黑客X档案》08年第9期,这是在其中分离出的第二段。
转载请注明版权:/ 作者:A1Pass黑客反病毒论坛不知道通过上一节的学习与大家自己的努力,许多以前曾困惑不解的问题现在是否已经有了满意的答案。
如果自己认为理解的还不够,就多实践一下,多思考一会,多搜索一次,相信你会因此而变得不再平凡。
这一节就让我带领大家走进文件系统的底层——PE文件结构的学习与探究。
其实PE文件就是指Windows里的DLL与EXE文件,PE的意思就是Portable Executable,即可移植的执行体。
大家也可以将其与JPG文件或MP3文件等对应理解,也许这样就会揭开它的神秘面纱,显得更为亲近一些。
当然,PE文件要远比MP3等文件复杂得多,但是作为Windows操作系统里特有的一种可执行文件格式,只要我们能对其有一个大体的了解,就会使我们了解更多的免杀技术的本质,从而更加有效与正确的利用这些技术。
一、PE文件入门PE文件总的来说是由DOS文件头、DOS加载模块、PE文件头、区段表与区段5部分构成。
其实,如果在纯Windows 环境下运行,DOS文件头、DOS加载模块根本是用不上的,加上两个DOS相关的结构完全是为了兼容性问题。
为了方便观察与理解,我们可以通过观察图1大体了解PE文件的结构。
如图可知,整个程序就是以DOS文件头“MZ”开始的,接下来就是DOS加载模块“This program cannot be run in DOS mode”,几乎每个Windows程序的前面都是这样一些信息!下面有一个以字母“PE”为开头的文件块,这就是大名鼎鼎的PE文件头了,PE文件头的标准大小为224个字节,由图可见,里面有一个画了横线标记的问号与左面的十六进制信息“E0”相对应,这便是PE文件头体积的描述标记,十六进制的“E0”等于十进制的“224”由此也不难看出PE文件头的大小为224个字节。
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文件结构

导入表
• • • • • • • • • • IMAGE_IMPORT_DESCRIPTOR STRUCT union Characteristics DWORD OriginalFirstThunk DWORD ends TimeDateStamp DWORD ForwarderChain DWORD Name1 DWORD FirstThunk DWORD IMAGE_IMPORT_DESCRIPTOR ENDS ? ? ;指向被调函数名的指针数组的指针 ? ? ? ? ;时间日期记录,无实际意义,可忽略 ;正向连接索引 ;指向被调用dll的名字指针数组的指针 ;指向被调函数地址的指针数组的指针
• • • • • •
//定位到PE HEADER //基址hMod加上IMAGE_DOS_HEADER结构的e_lfanew成员到达 IMAGE_NT_HEADERS //NT文件头的前4字节是文件签名("PE00" 字符串),然后是20字节的 IMAGE_FILE_HEADER结构 //即到达IMAGE_OPTIONAL_HEADER结构的地址,获取了一个指向 IMAGE_OPTIONAL_HEADER结构体的指针 IMAGE_OPTIONAL_HEADER * pOptHeader = (IMAGE_OPTIONAL_HEADER *)((BYTE*)hMod + pDosHeader->e_lfanew + 24);
• 指向DOS可执行程序部分 • 指向PE结构部分
– DOS块
• DOSபைடு நூலகம்可执行程序部分
DOS头结构定义
• • • • • • • • • • • • • • • • • • • • • IMAGE_DOS_HEADER STRUCT e_magic 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 WORD 4 dup(?) e_oemid WORD ? e_oeminfo WORD ? e_res2 WORD 10 dup(?) e_lfanew DWORD ? IMAGE_DOS_HEADER ENDS ;DOS可执行文件标记,固定为“MZ”
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文件框架结构PE文件是指Windows操作系统平台下的可执行文件,它是一种二进制程序文件,在Windows操作系统中扮演着非常重要的角色。
因此,了解PE文件的框架结构对于了解Windows操作系统的关键机制和安全性方面有很大的帮助。
本文将分步骤阐述PE文件框架结构。
一、DOS头DOS头是PE文件结构的开始部分,用于在早期版本的Windows操作系统上运行应用程序。
DOS头包含了一些用于MS-DOS的信息和可执行程序的信息,比如可执行程序的起始地址和执行入口点。
二、PE文件头PE文件头是指非常关键的PE文件头部区域,包含了PE文件的组织结构信息和代码在内存中的布局信息。
PE文件头由四部分组成,分别是文件标识头(Signature),文件头部(File Header),可选头部(Optional Header),数据目录(Data Directory)。
三、节表在PE文件头部区域中,还有一块重要的信息是节表。
节表用于存储PE 文件中的区段信息,包括代码段、数据段、资源段和导出等信息。
在代码段、数据段和资源段中,存储着PE文件的程序和数据,节表为了对各个节进行可控制的访问,便于程序的控制和调试。
四、数据目录PE文件头部区域中还包含了一个数据目录,在32位PE中,数据目录通常有16个数据目录,用于存储PE文件中的各种信息,如导入表、导出表、资源表、重定位表等等。
五、代码段代码段是PE文件的主要部分,它包含了PE文件的执行代码和程序逻辑,是PE文件的核心内容。
代码段的格式主要是二进制代码,对程序的执行有很大的影响。
六、数据段数据段主要用来存放程序中要用到的各种数据,包括全局变量、局部变量和常量等。
数据段会经常开辟空间以存储函数中的局部变量和运行时生成的各种实例对象。
综上,PE文件是Windows操作系统平台下的可执行文件,其框架结构主要由DOS头、PE文件头、节表、数据目录、代码段和数据段组成。
了解PE文件的结构以及各个部分之间的关联是非常重要的,既有助于我们更好的理解操作系统机制,又有助于我们在软件开发和安全领域中进行有效的分析和优化。
PE文件结构解析PPT教学课件

Subsystem
NT用来识别PE文件属于哪个子系统。
DataDirectory
一个IMAGE_DATA_DIRECTORY 结构数组。 每个结构给出一个重要数据结构的RVA,比 如引入地址表等。
17
几个常用术语
• RVA:Relative Virtual Address,表示相对虚拟地址。它是相对内 存中ImageBase的偏移位置。
在文件中时,每个双字中存放着对应引入函数的函数名字符串的 RVA 在内存中时,每个双字中存放着对应引入函数的地址。
请问:ExitProcess函数的地址可能在不同的操作系统中各不相同,但为什么 我们写的程序可以在不同的系统中正常运行?
40
IAT( IMPORT Address Table)
41
IMPORT Directory Table (核心)
OriginalFirstThunk dd ? //指向引入函数名列表或序号列表
Ends
TimeDateStamp dd ?
ForwarderChain dd ?
Name1 dd
?
//指向dll函数名
FirstThunk dd ?
//指向IAT表
IMAGE_IMPORT_DESCRIPTOR ENDS
IMAGE_DATA_DIRECTORY ENDS
21
22
3.4hello-2.5.exe的IAT与引入表
23
节表
节表其实就是紧挨着 PE header 的一结构数组。
该数组成员的数目由 File header (IMAGE_FILE_HEADER) 结 构中 NumberOfSections 域的域值来决定。
18
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文件格式:我们知道所有文件都是一些连续(当然实际存储在磁盘上的时候不一定是连续的)的数据组织起来的,不同类型的文件肯定组织形式也各不相同;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文件结构详解(六)重定位前面两篇PE文件结构详解(四)PE导入表和PE文件结构详解(五)延迟导入表介绍了PE文件中比较常用的两种导入方式,不知道大家有没有注意到,在调用导入函数时系统生成的代码是像下面这样的:在这里,IE的iexplorer.exe导入了Kernel32.dll的GetCommandLineA函数,可以看到这是个间接call,00401004这个地址的内存里保存了目的地址,根据图中显示的符号信息可知,00401004这个地址是存在于iexplorer.exe模块中的,实际上也就是一项IAT的地址。
这个是IE6的exe中的例子,当然在dll 中如果导入其他dll中的函数,结果也是一样的。
这样就有一个问题,代码里call 的地址是一个模块内的地址,而且是一个VA,那么如果模块基地址发生了变化,这个地址岂不是就无效了?这个问题如何解决?答案是:Windows使用重定位机制保证以上代码无论模块加载到哪个基址都能正确被调用。
听起来很神奇,是怎么做到的呢?其实原理并不很复杂,这个过程分三步:1.编译的时候由编译器识别出哪些项使用了模块内的直接VA,比如push一个全局变量、函数地址,这些指令的操作数在模块加载的时候就需要被重定位。
2.链接器生成PE文件的时候将编译器识别的重定位的项纪录在一张表里,这张表就是重定位表,保存在DataDirectory中,序号是IMAGE_DIRECTORY_ENTRY_BASERELOC。
3.PE文件加载时,PE 加载器分析重定位表,将其中每一项按照现在的模块基址进行重定位。
以上三步,前两部涉及到了编译和链接的知识,跟本文的关系不大,我们直接看第三步,这一步符合本系列的特征。
在查看重定位表的定义前,我们先了解一下他的存储方式,有助于后面的理解。
按照常规思路,每个重定位项应该是一个DWORD,里面保存需要重定位的RVA,这样只需要简单操作便能找到需要重定位的项。
pe文件格式标准

pe文件格式标准PE文件是Windows操作系统中常见的可执行文件格式,它具有一定的规范和结构。
本文将介绍PE文件格式的标准,以帮助读者更好地理解和应用该文件格式。
一、文件类型PE文件是一种可执行文件格式,其后缀名通常为.exe、.dll或.sys。
根据文件头中的标识,可以确定文件是否为PE文件。
二、文件结构1. DOS头PE文件的第一个部分是DOS头,用于向操作系统提供兼容性支持。
它包含了DOS头标识、指向PE文件的偏移地址等信息。
PE头是PE文件的关键部分,包含了各种信息,如文件类型、入口点地址、导入表、导出表等。
其中,导入表和导出表记录了文件中使用的外部函数和数据。
节表用于描述PE文件的各个节(Sections),每个节都包含了特定的代码、数据或资源。
每个节在节表中都有一个条目,记录了节的名称、在文件中的偏移地址、大小等。
4. 数据目录数据目录记录了PE文件中存储重要信息的位置和大小,如导入表、导出表、资源表等。
每个数据目录都有一个条目,包含了相应信息的位置和大小。
三、文件解析PE文件可以通过解析文件头部和节表来获取所需的信息。
通过解析PE头的入口点地址,可以定位文件的入口点,从而启动程序。
1. 解析DOS头首先,解析DOS头,获取PE头的文件偏移地址。
通过该地址,可以定位到PE头的起始位置。
2. 解析PE头接下来,解析PE头,获取文件的相关信息,如文件类型、入口点地址等。
可以根据需要进一步解析导入表、导出表等信息。
3. 解析节表通过解析节表,可以获取PE文件中各个节的详细信息。
可以根据节的名称或索引来定位到相应的节,并获取节的起始地址、大小等。
四、常见问题与处理方法在处理PE文件时,可能会遇到一些常见的问题,以下列举几个常见问题,并提供相应的处理方法:1. 处理导入表若PE文件中存在外部函数调用,需要解析导入表,并将相应的函数地址重新映射到实际的函数地址上。
2. 处理资源表若PE文件中包含资源,需要解析资源表,并提取所需的资源。
自己构建PE文件

自己构建PE文件现在我将演示一个简单且具有一般性的标准PE文件(畸形的PE将来做壳开发的时候再做研究,现在我们只关心标准PE)的构造过程。
为简单起见,我们只关心必不可缺的结构和成员(eg。
),其他成员一律定义为0,具体含义请自己参考微软官方提供的PECOFF 规范(英文)。
个人认为PE文件结构的结构太复杂成员太多,这样才比较容易接受。
首先还是老生常谈的PE的结构,如下图所示:1.DOS 头typedef struct _IMAGE_DOS_HEADER { // DOS .EXE headerWORD e_magic; // Magic numberWORD e_cblp; // Bytes on last page of fileWORD e_cp; // Pages in fileWORD e_crlc; // RelocationsWORD e_cparhdr; // Size of header in paragraphsWORD e_minalloc; // Minimum extra paragraphs neededWORD e_maxalloc; // Maximum extra paragraphs neededWORD e_ss; // Initial (relative) SS valueWORD e_sp; // Initial SP valueWORD e_csum; // ChecksumWORD e_ip; // Initial IP valueWORD e_cs; // Initial (relative) CS valueWORD e_lfarlc; // File address of relocation tableWORD e_ovno; // Overlay numberWORD e_res[4]; // Reserved wordsWORD e_oemid; // OEM identifier (for e_oeminfo)WORD e_oeminfo; // OEM information; e_oemid specificWORD e_res2[10]; // Reserved wordsLONG e_lfanew; // File address of new exe header} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;我们只关心第一个和最后一个这2个变量e_magic(WORD)必须等于0x5A4D。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
三年前,我曾经写了一个手工打造可执行程序的文章,可是因为时间关系,我的那篇文章还是有很多模糊的地方,我一直惦记着什么时候再写一篇完美的,没想到一等就等了三年。
因为各种原因直到三年后的今天我终于完成了它。
现在把它分享给大家,希望大家批评指正。
我们这里将不依赖任何编译器,仅仅使用一个十六进制编辑器逐个字节的手工编写一个可执行程序。
以这种方式讲解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文件可选头:虽然它的名字是“可选头部”,但是请确信:这个头部并非“可选”,而是“必需”的。
(长度 224byte )。
各段头部:又称节头部,一个Windows NT的应用程序典型地拥有9个预定义段(节),它们是“.text”、“.bss”、“.rdata”、“.data”、“.rsrc”、“.edata”、“.idata”、“.pdata”和“.debug”。
一些应用程序不需要所有的这些段,同样还有些应用程序为了自己特殊的需要而定义了更多的段。
(每个段头部占40byte,我们这里也不需要所有的段,仅需3个段。
)通常我们是将PE整个结构分成四个部分,把MS-DOS MZ 头部和MS-DOS 实模式残余程序作为第一部分,可以称他为DOS部分,而PE文件标志、PE文件头、PE文件可选头三个部分作为第二部分,称之为PE头部分,因为这部分才是Windows下真正需要的部分,所以从PE文件标志开始才是真正的PE部分。
各段头部是第三部分,称之为节表。
它详细描述了PE文件中各个节的详细信息。
最后就是各个节的实体部分了,称为节数据。
以上仅仅是对PE结构各部分的大体讲解。
接下来再手写这个Hello World!程序过程中,我将详细介绍每个部分的含义。
首先准备一下工具,一个十六进制编辑器足以。
我们这里使用VC++ 6.0所携带的十六进制编辑器,您也可以使用如WinHex等十六进制编辑工具。
打开VC,选择文件,新建菜单项,然后选择一个二进制文件,单击确定。
一切就绪了,下面就开始手写可执行程序,如图2所示:图2 VC6.0下的十六进制编辑器首先来完成“DOS MZ header”部分。
“DOS MZ header”的功能前面已经讲过,在这里不再重述,直接实现他。
“DOS MZ header”总共64byte,他对应的结构是IMAGE_DOS_HEADER ,在WINNT.H文件中有定义。
通过这个结构我们可以看到,这64字节被分成19个成员,每个成员都有特殊的含义,与其说我们是在逐字节的手写可执行程序,倒不如说我们是在逐个成员的写。
因为单独的一个字节并不一定具有什么意义。
我们在学习过程中,就是要按照官方的定义,将整个部分拆分成若干个成员,然后逐个成员的去学习。
(提示: 如果安装有VC开发环境,那么在其安装目录下有一个头文件WINNT.H,在这个头文件中定义了所有PE结构相关的各部分结构体。
如图3所示:)图3 VC安装目录下的WINNT.H头文件使用VC开发环境打开此文件,然后按快捷键Ctrl+F输入IMAGE_DOS_HEADER进行搜索,如图4所示:图4 VC下查找文件单击Find Next按钮即可得到如下搜索结果,如图5所示:图5可以看出IMAGE_DOS_HEADER,结构体的定义如下:typedef struct _IMAGE_DOS_HEADER { // DOS .EXE headerWORD e_magic; // Magic numberWORD e_cblp; // Bytes on last page of fileWORD e_cp; // Pages in fileWORD e_crlc; // RelocationsWORD e_cparhdr; // Size of header in paragraphsWORD e_minalloc; // Minimum extra paragraphs needed WORD e_maxalloc; // Maximum extra paragraphs needed WORD e_ss; // Initial (relative) SS valueWORD e_sp; // Initial SP valueWORD e_csum; // ChecksumWORD e_ip; // Initial IP valueWORD e_cs; // Initial (relative) CS valueWORD e_lfarlc; // File address of relocation tableWORD e_ovno; // Overlay numberWORD e_res[4]; // Reserved wordsWORD e_oemid; // OEM identifier (for e_oeminfo)WORD e_oeminfo; // OEM information; e_oemid specificWORD e_res2[10]; // Reserved wordsLONG e_lfanew; // File address of new exe header} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;按照它的定义,我们分别完成各个成员。
第一个成员(e_magic)是个WORD类型,占2个字节,它被用于表示一个MS-DOS兼容的文件类型,他的值是固定的0x5A4D,所以在十六进制编辑器中输入“4D5A”。
(注意:因为我们是在十六进制编辑器下写数据,所以所有的数据格式都是十六进制式的。
但是我们在开发环境中通常在数据前添加“0x”用来表示十六进制数 ,即:0x5A4D。
而在十六进制编辑器中,直接写成“4D5A”即可。
后面内容都照此规定书写。
有一点需要说明,为什么十六进制值0x5A4D输入到十六进制编辑器中是4D5A呢?这是因为一个内存值,无论是占两个字节的WORD类型,还是占四个字节的DWORD类型等,如同我们学习数学中的十进制数值一样,都是有高低位之分的,从右向左位越来越高。
然而在十六进制编辑器中,十六进制位是自左向右依次增高。
因此按照高低位对齐的原则,值0x5A4D中,低位0x4D应该应该放到左边,0x5A应该放到右边。
也就得到了编辑器中的4D5A。
)第2个成员到第18个成员总共58个字节,是对DOS程序环境的初始化等操作,对于我们这个程序来说,没什么影响,我们通通用“00”来填充。
(如果您想对其进行详细了解,请查阅相关书籍。
)(提示:我们在此不可能把PE结构所有的知识点都面面俱到,因为他十分的庞大。
当然也没有必要对他作完全彻底的掌握,只需掌握关键的地方就可以了。
以后我们都将把不影响程序执行的成员填充为零,这样做,一方面使程序看起来简洁,另一方面可以使您快速定位PE结构中要重点掌握的地方。
)第19(e_lfanew)个成员非常重要,他是一个LONG类型,占4个字节,用来表示“PE文件标志”在文件中的偏移,单位是byte。
而从图5-1中可以看到“PE文件标志”紧随“MS-DOS 实模式残余程序”其后。
知道这一点,我们就可以计算一下,我们的“DOS MZ header”总共64 byte,后面的“MS-DOS 实模式残余程序”占112 byte, 64 + 112 = 176 byte。
但是要注意,我们这里的176是十进制的,转化成十六进制是0xB0。
因为是4个字节,其余三位字节应该以00补齐,所以最终的值为0x000000B0。
所以在我们的十六进制编辑器中按照高低对齐的原则应该填写“B0000000”。
接下来完成“MS-DOS 实模式残余程序”,笔者已经介绍,他是用在DOS下执行的,而我们所完成的HelloWorld程序是在win32下执行的。
所以这里的内容并不影响我们程序的执行。
因此这里直接用“00”来填充,注意总共112 byte。
这两部分完成之后代码如图6所示:图6 完成PE结构中Dos部分的编写接下来便进入真正主题,开始写真正的PE结构部分:微软将“PE文件标志”,“PE文件头”,“PE文件可选头”这三个部分用一个结构来定义,即:IMAGE_NT_HEADERS32在WINNT.H中可以搜索其定义,定义如下:typedef struct _IMAGE_NT_HEADERS {DWORD Signature;IMAGE_FILE_HEADER FileHeader;IMAGE_OPTIONAL_HEADER32 OptionalHeader;} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;可以看出这个结构含有3个成员:第一个成员(Signature)表示“PE文件标志”,是一个DWORD类型,占4个字节,它是PE开始的标记,对于Windows程序这个值必须为0x00004550,所以编辑器中填写“50450000”。