学习笔记:pe文件格式、pe部分信息解析程序代码(win32asm)

本文详细介绍了PE(Portable Executable)文件的整体结构,包括DOS MZ header、DOS stub、PE header、Section table等内容。通过解析DOS MZ header中的e_lfanew字段找到PE header,接着解释了PE header的Signature、FileHeader和OptionalHeader等关键信息,以及PE文件装载的主要步骤。此外,还探讨了DOS stub的作用。内容适合对PE文件格式感兴趣的读者和Windows程序开发者。
摘要由CSDN通过智能技术生成
 
一:PE整体结构
PE 的意思就是 Portable Executable (可移植的执行体)。
PE 文件的整体大概结构描述:
struct pe
{
DOS MZ header 所有 PE 文件 ( 甚至 32 位的 DLLs) 必须以一个简单的 DOS MZ header 开始。有了它,一旦程序在 DOS 下执行, DOS 就能识别出这是有效的执行体,然后运行紧随 MZ header 之后的 DOS stub
DOS stub           DOS stub 实际上是个有效的 EXE ,在不支持 PE 文件格式的操作系统中,它将简单显示一个错误提示,类似于字符串 "This program requires Windows" 或者程序员可根据自己的意图实现完整的 DOS 代码。大多数情况下它是由汇编器 / 编译器自动生成。通常,它简单调用中断 21h 服务 9 来显示字符串 "This program cannot run in DOS mode"
PE header     PE header PE 相关结构 IMAGE_NT_HEADERS 的简称,其中包含了许多 PE 装载器用到的重要域。执行体在支持 PE 文件结构的操作系统中执行时, PE 装载器将从 DOS MZ header 中找到 PE header 的起始偏移量(跳过了 DOS stub 直接定位到真正的文件头 PE header )。
Section table 节表。 每个结构包含对应节的属性、文件偏移量、虚拟偏移量等。如果 PE 文件里有 5 个节,那么此结构数组内就有 5 个成员。
Section 1 PE 文件的真正内容划分成块,称之为 sections (节)。每节是一块拥有共同属性的数据,比如代码 / 数据、读 / 写等。当 PE 装载器映射节内容时,它会检查相关节属性并置对应内存块为指定属性。
Section n
}
装载一个 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 (引入表)逻辑部分。
 
二:DOS MZ header解析
DOS MZ header 结构如下( asm 描述,来自汇编头文件 windows.ini ):
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      ?                          // 初始的 SS 值(相对偏移量)
 e_sp              WORD      ?               // 初始的 SP
 e_csum            WORD      ?                         // 校验和
 e_ip              WORD      ?                           // 初始的 IP
 e_cs              WORD      ?                          // 初始的 CS 值(相对偏移量)
 e_lfarlc          WORD      ?                    // 重分配表文件地址
 e_ovno            WORD      ?                         // 覆盖号
 e_res             WORD   4 dup(?)           // 保留字
 e_oemid           WORD      ?                         // OEM 标识符(相对 e_oeminfo
 e_oeminfo         WORD      ?                          // OEM 信息
 e_res2            WORD 10 dup(?)           // 保留字
 e_lfanew          DWORD      ?              // exe 头部的文件地址
IMAGE_DOS_HEADER ENDS                                      //sizeof(IMAGE_DOS_HEADER)==0x40
IMAGE_DOS_HEADER 结构体大小为 0x40 bytes
其中e_magic是标记,它应该等于IMAGE_DOS_SIGNATURE(字符为MZ),从而指明当前是DOS头部信息。 windows.ini 中定义的同类各种系统标记如下:
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 00004550h 
其中的 e_lfanew 是指向 PE header 的文件偏移,单位为 byte
 
三:DOS stub解析:
这个部分没有找到详细资料。基本上我们也不需要知道,因为这部分是为了在 MS-DOS 下运行本 exe 文件时显示“ This program cannot be run in DOS mode. ”,也就是说这是一段 MS-DOS 程序,似乎可以称为“ 实模式残余程序 ”。
此部分的起始偏移应该是由 IMAGE_DOS_HEADER .e_lfarlc 指出的,单位为 byte
 
四:PE header解析
PE header 正式命名是 IMAGE_NT_HEADERS ,结构如下
IMAGE_NT_HEADERS STRUCT
    Signature dd ?               // PE
标记,值为 50h, 45h, 00h, 00h PE/0/0 )。
    FileHeader IMAGE_FILE_HEADER <> // 包含了关于 PE 文件物理分布的一般信息
    OptionalHeader IMAGE_OPTIONAL_HEADER32 <> //
包含了关于 PE 文件逻辑分布的信息
IMAGE_NT_HEADERS ENDS
IMAGE_FILE_HEADER STRUCT
 Machine WORD    ?// 该文件运行所要求的 CPU 。对于 Intel 平台,该值是 IMAGE_FILE_MACHINE_I386 (14Ch)
 NumberOfSections      WORD    ?// 文件的节数目
 TimeDateStamp  DWORD   ?// 文件创建日期和时间,从 1970.1.1 00:00:00 以来的秒数
 PointerToSymbolTable DWORD   ?// 用于调试
 NumberOfSymbols       DWORD   ?// 用于调试
 SizeOfOptionalHeader WORD    ?// 指示紧随本结构之后的 OptionalHeader 结构大小,
必须为有效值
 Characteristics       WORD    ?// 关于文件信息的标记,比如文件是 exe 还是 dll
IMAGE_FILE_HEADER ENDS
Characteristics 详解如下:
Bit 0 (IMAGE_FILE_RELOCS_STRIPPED) :置 1 表示文件中没有重定向信息。每个段都
有它们自己的重定向信息。这个标志在可执行文件中没有使用,在可执行文件中是用一个叫做
基址重定向目录表来表示重定向信息的,这将在下面介绍。
    Bit 1 (IMAGE_FILE_EXECUTABLE_IMAGE)
:置 1 表示该文件是可执行文件(也就是说
不是一个目标文件或库文件)。
    Bit 2 (IMAGE_FILE_LINE_NUMS_STRIPPED)
:置 1 表示没有行数信息;在可执行文件
中没有使用。
    Bit 3 (IMAGE_FILE_LOCAL_SYMS_STRIPPED)
:置 1 表示没有局部符号信息;在可执行
文件中没有使用。
    Bit 4 (IMAGE_FILE_AGGRESIVE_WS_TRIM)

    Bit 7 (IMAGE_FILE_BYTES_REVERSED_LO)
    Bit 8 (IMAGE_FILE_32BIT_MACHINE)
:表示希望机器为 32 位机。这个值永远为 1
    Bit 9 (IMAGE_FILE_DEBUG_STRIPPED)
:表示没有调试信息,在可执行文件中没有使
用。
    Bit 10 (IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP)
:置 1 表示该程序不能运行于可移
动介质中(如软驱或 CD-ROM )。在这种情况下, OS 必须把文件拷贝到交换文件中执行。
    Bit 11 (IMAGE_FILE_NET_RUN_FROM_SWAP)
:置 1 表示程序不能在网上运行。在这种
情况下, OS 必须把文件拷贝到交换文件中执行。
    Bit 12 (IMAGE_FILE_SYSTEM)
:置 1 表示文件是一个系统文件例如驱动程序。在可执
行文件中没有使用。
    Bit 13 (IMAGE_FILE_DLL)
:置 1 表示文件是一个动态链接库( DLL )。
    Bit 14 (IMAGE_FILE_UP_SYSTEM_ONLY)
:表示文件被设计成不能运行于多处理器系
统中。
    Bit 15 (IMAGE_FILE_BYTES_REVERSED_HI)
:表示文件的字节顺序如果不是机器所期
望的,那么在读出之前要进行交换。在可执行文件中它们是不可信的(操作系统期望按正确的
字节顺序执行程序)。
IMAGE_OPTIONAL_HEADER32 STRUCT
 Magic                         WORD       ?// 标记
 MajorLinkerVersion            BYTE       ?// 链接器的版本号
 MinorLinkerVersion            BYTE       ?// 链接器的版本号
 SizeOfCode                    DWORD      ?// 可执行代码的长度
 SizeOfInitializedData         DWORD      ?// 初始化数据的长度(数据段)
 SizeOfUninitializedData       DWORD      ?// 未初始化数据的长度( bss 段)
 AddressOfEntryPoint   DWORD      ? // 代码的入口 RVA 地址,程序从这儿开始执行。 PE 装载器准备运行的 PE 文件的第一个指令的 RVA 。若您要改变整个执行的流程,可以将该值指定到新的 RVA ,这样新 RVA 处的指令首先被执行。
  BaseOfCode                    DWORD      ?// 可执行代码起始位置
 BaseOfData                    DWORD      ?// 初始化数据起始位置
 ImageBase  DWORD      ?// 载入程序首选的 RVA 地址。 PE 文件的优先装载地址。比如,如果该值是 400000h PE 装载器将尝试把文件装到虚拟地址空间的 400000h 处。字眼 " 优先 " 表示若该地址区域已被其他模块占用,那 PE 装载器会选用其他空闲地址。
 SectionAlignment              DWORD      ?// 段加载后在内存中的对齐方式。内存中节对齐的粒度。例如,如果该值是 4096 (1000h) ,那么每节的起始地址必须是 4096 的倍数。若第一节从 401000h 开始且大小是 10 个字节,则下一节必定从 402000h 开始,即使 401000h 402000h 之间还有很多空间没被使用。
 FileAlignment                 DWORD      ?// 段在文件中的对齐方式。文件中节对齐的粒度。例如,如果该值是 (200h), ,那么每节的起始地址必须是 512 的倍数。若第一节从文件偏移量 200h 开始且大小是 10 个字节,则下一节必定位于偏移量 400h: 即使偏移量 512 1024 之间还有很多空间没被使用 / 定义。
 MajorOperatingSystemVersion   WORD       ?// 操作系统版本
 MinorOperatingSystemVersion   WORD       ? // 操作系统版本
 MajorImageVersion             WORD       ?// 程序版本
 MinorImageVersion             WORD       ? // 程序版本
 MajorSubsystemVersion         WORD       ?// 子系统版本号。 win32 子系统版本。若 PE 文件是专门为 Win32 设计的,该子系统版本必定是 4.0 否则对话框不会有 3 维立体感。
 MinorSubsystemVersion         WORD       ?// 子系统版本号
 Win32VersionValue             DWORD      ?// 一般为 0
 SizeOfImage  DWORD      ?// 程序调入后占用内存大小(字节),等于所有段的长度之和。所有头和节经过节对齐处理后的大小。
 SizeOfHeaders DWORD ?// 所有文件头的长度之和 ( 从文件开始到第一个段之间的大小 ) 。所有头 + 节表的大小,也就等于文件尺寸减去文件中所有节的尺寸。可以以此值作为 PE 文件第一节的文件偏移量
 CheckSum DWORD ?// 校验和。它仅用在驱动程序中,在可执行文件中可能为 0 。它的计算方法 Microsoft 不公开,在 imagehelp.dll 中的 CheckSumMappedFile() 函数可以计算它
 Subsystem WORD ?// NT 子系统,可能是以下的值:
    IMAGE_SUBSYSTEM_NATIVE (1)
不需要子系统。用在驱动程序中。
    IMAGE_SUBSYSTEM_WINDOWS_GUI(2) WIN32 graphical
程序(它可用 AllocConsole() 来打开一个控制台,但是不能在 一开始自动得到)。
    IMAGE_SUBSYSTEM_WINDOWS_CUI(3) WIN32 console
程序(它可以一开始自动建立)。
    IMAGE_SUBSYSTEM_OS2_CUI(5) OS/2 console
程序(因为程序是 OS/2 格式,所以它很少用在 PE )。
    IMAGE_SUBSYSTEM_POSIX_CUI(7) POSIX console
程序。
    Windows95
程序总是用 WIN32 子系统,所以只有 2 3 是合法的值
 DllCharacteristics            WORD       ?// Dll 状态
 SizeOfStackRese
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值