(一)介绍PE 格式
PE格式(Portable Executable,可移植可执行文件)是原生的Win32文件格式,任何的32位应用程序(Executable)包括.NET、动态链接库(DLL)、控件(OCX)以及控制面板程序(CPL)均为PE格式,其实NT内核ntoskrnl.exe也是PE格式。
我们研究PE格式的目的有:一是给应用程序添加代码功能,比如注册机代码注入(Keygen Injecton);二是手动给EXE脱壳。
现在很多程序都加“壳”,一来是减少映像文件尺寸,二来可以保护程序文件不被轻易修改。被加壳的EXE一般它的输入表(import table)与数据区段(.data)都被加密过,并且被插入了一段脱壳的代码,当程序执行时会先执行这段代码给内存中的数据解密,接着会修复输入表和各个区段,然后再跳到原始入口点执行。
原文中使用的范例程序是delphi写的进制转换程序,本文中我使用的是VC9编写的一个很简单的数组求和程序,在debug模式下编译。这并不影响我们学习PE格式。
首先两个概念,PE文件被加载器加载到内存后被称为一个模块(module),存储模块首地址的称为模块句柄(handle of module),简写为HMODULE。
然后我们看一下PE格式的总体结构(这张图MS已经遍布全球了):
PICTURE MISSING
前4块我们后面会细讲,就区段来说(Section),一个PE文件至少要有2个区段,代码区段用来存储程序代码,以及数据区段用来存储各种数据。NT为PE预定义了9个区段:.text,.bss,.rdata,.data,.rsrc,.edata,.idata,.pdata,.debug。一个PE既可以选择其中的几个区段,也可以自己定义额外的区段满足特别需要。
一般来说,.text是代码区段,.data、.rdata、.bss是数据区段,.rsrc是资源区段,.edata与.idata分别是输出输入表区段,.debug是调试信息区段。这些名字(.text, .data…)实际上是给程序员看着方便的,执行程序时,系统会完全忽略它们。
最后一点,就是PE文件在硬盘中的结构顺序和当它们被加载到内存中后的顺序并非一样,记录硬盘上排列的是File Alignment域(域是含有特定信息的二进制片段),记录内存中排列的是Section Alignment域,这既是PE加载器调整的结果,也是虚拟内存机制的作用。关于Windows虚拟内存机制,请参考相关资料,或者我的另一篇文章。就是有一点要注意的是,PE文件中的每一个区段总是在一个新的页面中存放的。