软件破解入门教程和解密手册——PE文件格式

 

第8章 压缩与脱壳

第一节 PE文件格式

教程1: PE文件格式一览

考虑到早期写的PE教程1是自己所有教程中最糟糕的一篇,此番决心彻底重写一篇以飨读者。

PE 的意思就是 Portable Executable(可移植的执行体)。它是 Win32环境自身所带的执行体文件格式。它的一些特性继承自 Unix的 Coff (common object file format)文件格式。“portable executable”(可移植的执行体)意味着此文件格式是跨win32平台的 即使Windows运行在非IntelCPU上,任何win32平台的PE装载器都能识别和使用该文件格式。当然,移植到不同的CPUPE执行体必然得有一些改变。所有 win32执行体 (除了VxD16位的Dll)都使用PE文件格式,包括NT的内核模式驱动程序(kernel mode drivers)。因而研究PE文件格式给了我们洞悉Windows结构的良机。

本教程就让我们浏览一下 PE文件格式的概要。

DOS MZ header
DOS stub
PE header
Section table
Section 1
Section 2
Section …
Section n

上图是 PE文件结构的总体层次分布。所有 PE文件(甚至32位的 DLLs) 必须以一个简单的 DOS MZ header 开始。我们通常对此结构没有太大兴趣。有了它,一旦程序在DOS下执行,DOS就能识别出这是有效的执行体,然后运行紧随 MZ header 之后的 DOS stubDOS stub实际上是个有效的 EXE,在不支持 PE文件格式的操作系统中,它将简单显示一个错误提示,类似于字符串 “This program requires Windows” 或者程序员可根据自己的意图实现完整的DOS代码。通常我们也不对 DOS stub 太感兴趣因为大多数情况下它是由汇编器/编译器自动生成。通常,它简单调用中断21h服务9来显示字符串“This program cannot run in DOS mode”

紧接着 DOS stub 的是PE header。 PE header PE相关结构 IMAGE_NT_HEADERS 的简称,其中包含了许多PE装载器用到的重要域。当我们更加深入研究PE文件格式后,将对这些重要域耳目能详。执行体在支持PE文件结构的操作系统中执行时,PE装载器将从 DOS MZ header 中找到 PE header 的起始偏移量。因而跳过了 DOS stub 直接定位到真正的文件头 PE header

PE文件的真正内容划分成块,称之为sections(节)。每节是一块拥有共同属性的数据,比如代码/数据、读/写等。我们可以把PE文件想象成一逻辑磁盘,PE header 是磁盘的boot扇区,而sections就是各种文件,每种文件自然就有不同属性如只读、系统、隐藏、文档等等。 值得我们注意的是 —- 节的划分是基于各组数据的共同属性而不是逻辑概念。重要的不是数据/代码是如何使用的,如果PE文件中的数据/代码拥有相同属性,它们就能被归入同一节中。不必关心节中类似于“data”, “code”或其他的逻辑概念如果数据和代码拥有相同属性,它们就可以被归入同一个节中。(译者注:节名称仅仅是个区别不同节的符 而已,类似“data”, “code”的命名只为了便于识别,惟有节的属性设置决定了节的特性和功能)如果某块数据想付为只读属性,就可以将该块数据放入置为只读的节中,当PE装载器映射节内容时,它会检查相关节属性并置对应内存块为指定属性。

如果我们将PE文件格式视为一逻辑磁盘,PE headerboot扇区而sections是各种文件,但我们仍缺乏足够信息来定位磁盘上的不同文件,譬如,什么是PE文件格式中等价于目录的东东急,那就是 PE header 接下来的数组结构section table(节表)。 每个结构包含对应节的属性、文件偏移量、虚拟偏移量等。如果PE文件里有5个节,那么此结构数组内就有5个成员。因此,我们便可以把节表视为逻辑磁盘中的根目录,每个数组成员等价于根目录中目录项。

以上就是PE文件格式的物理分布,下面将总结一下装载一PE文件的主要步骤:

  1. PE文件被执行,PE装载器检查 DOS MZ header 里的 PE header 偏移量。如果找到,则跳转到 PE header
  2. PE装载器检查 PE header 的有效性。如果有效,就跳转到PE header的尾部。
  3. 紧跟 PE header 的是节表。PE装载器读取其中的节信息,并采用文件映射方法将这些节映射到内存,同时付上节表里指定的节属性。
  4. PE文件映射入内存后,PE装载器将处理PE文件中类似 import table(引入表)逻辑部分。

上述步骤是基于本人观察后的简述,显然还有一些不够精确的地方,但基本明晰了执行体被处理的过程。

你应该下载 LUEVELSMEYER的《PE文件格式》 该文的描述相当详细,可用作案头的参考手册。

本教程中我们将学习如何检测给定文件是一有效PE文件。
下载 
范例

如何才能校验指定文件是否为一有效PE文件呢nbsp;这个问题很难回答,完全取决于想要的精准程度。您可以检验PE文件格式里的各个数据结构,或者仅校验一些关键数据结构。大多数情况下,没有必要校验文件里的每一个数据结构,只要一些关键数据结构有效,我们就认为是有效的PE文件了。下面我们就来实现前面的假设。

我们要验证的重要数据结构就是 PE header。从编程角度看,PE header 实际就是一个 IMAGE_NT_HEADERS 结构。定义如下:

IMAGE_NT_HEADERS STRUCT 
   Signature dd nbsp;
   FileHeader IMAGE_FILE_HEADER <> 
   OptionalHeader IMAGE_OPTIONAL_HEADER32 <> 
IMAGE_NT_HEADERS ENDS

Signature dword类型,值为50h, 45h, 00h, 00hPE )。 本域为PE标记,我们可以此识别给定文件是否为有效PE文件。
FileHeader 
该结构域包含了关于PE文件物理分布的信息, 比如节数目、文件执行机器等。
OptionalHeader 
该结构域包含了关于PE文件逻辑分布的信息,虽然域名有可选字样,但实际上本结构总是存在的。

我们目的很明确。如果IMAGE_NT_HEADERSsignature域值等于“PE ”,那么就是有效的PE文件。实际上,为了比较方便,Microsoft已定义了常量IMAGE_NT_SIGNATURE供我们使用。

IMAGE_DOS_SIGNATURE equ 5A4Dh 
IMAGE_OS2_SIGNATURE equ 454Eh 
IMAGE_OS2_SIGNATURE_LE equ 454Ch 
IMAGE_VXD_SIGNATURE equ 454Ch 
IMAGE_NT_SIGNATURE equ 4550h

接下来的问题是如何定位 PE headernbsp;答案很简单: DOS MZ header 已经包含了指向 PE header 的文件偏移量。DOS MZ header 又定义成结构IMAGE_DOS_HEADER 。查询windows.inc,我们知道IMAGE_DOS_HEADER 结构的e_lfanew成员就是指向 PE header 的文件偏移量。

现在将所有步骤总结如下:

  1. 首先检验文件头部第一个字的值是否等于 IMAGE_DOS_SIGNATURE是则 DOS MZ header 有效。
  2. 一旦证明文件的 DOS header 有效后,就可用e_lfanew来定位 PE header 了。
  3. 比较 PE header 的第一个字的值是否等于IMAGE_NT_HEADER。如果前后两个值都匹配,那我们就认为该文件是一个有效的PE文件。

Example:

.386 
.model flat,stdcall 
option casemap:none 
include masm32includewindows.inc 
include masm32includekernel32.inc 
include masm32includecomdlg32.inc 
include masm32includeuser32.inc 
includelib masm32libuser32.lib 
includelib masm32libkernel32.lib 
includelib masm32libcomdlg32.lib 

SEH struct 
PrevLink dd    ; the address of the previous seh structure 
CurrentHandler dd nbsp;   ; the address of the exception handler 
SafeOffset dd    ; The offset where it’s safe to continue execution 
PrevEsp dd      ; the old value in esp 
PrevEbp dd     ; The old value in ebp 
SEH ends

.data 
AppName db “PE tutorial no.2”,0 
ofn OPENFILENAME <> 
FilterString db “Executable Files (*.exe, *.dll)”,0,”*.exe;*.dll”,0 
                 db “All Files”,0,”*.*”,0,0 
FileOpenError db “Cannot open the file for reading”,0 
FileOpenMappingError db “Cannot open the file for memory mapping”,0 
FileMappingError db “Cannot map the file into memory”,0 
FileValidPE db “This file is a valid PE”,0 
FileInValidPE db “This file is not a valid PE”,0 

.datanbsp;
buffer db 512 dup( 
hFile dd nbsp;
hMapping dd nbsp;
pMapping dd nbsp;
ValidPE dd nbsp;

声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2011年9月10日
下一篇 2011年9月11日

相关推荐