吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 7444|回复: 43
收起左侧

[调试逆向] PE文件笔记五 PE文件头之扩展PE头

  [复制链接]
lyl610abc 发表于 2021-3-30 17:05
本帖最后由 lyl610abc 于 2021-4-3 21:27 编辑

继续PE系列笔记的更新

PE其它笔记索引可前往:
PE文件笔记一 PE介绍


继续具体学习PE的各个结构细节,前面学完了标准PE头,接着学习扩展PE头

由于PE文件头的内容较多,故要拆分为多个笔记,此笔记主要为扩展PE头

PS:扩展PE头的成员较多,可以先看个大概后结合下面的实战分析来学习扩展PE头的成员

扩展PE头

扩展PE头所属

扩展PE头是PE文件头中的一个成员



32位所属

typedef struct _IMAGE_NT_HEADERS {
    DWORD Signature;                                            //PE文件头标识
    IMAGE_FILE_HEADER FileHeader;                               //标准PE头
    IMAGE_OPTIONAL_HEADER32 OptionalHeader;                                //扩展PE头 32位
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

64位所属

typedef struct _IMAGE_NT_HEADERS64 {
    DWORD Signature;                                       //PE文件头标识
    IMAGE_FILE_HEADER FileHeader;                          //标准PE头        
    IMAGE_OPTIONAL_HEADER64 OptionalHeader;                //扩展PE头 64位
} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64;

两种扩展PE头差异

两种结构

扩展PE头的结构根据程序是32位或64位而分成了两种结构,而PE文件头则因扩展PE头的差异也被分成了两种结构

PE文件头结构 说明
_IMAGE_NT_HEADERS 32位程序对应的PE文件头结构
_IMAGE_NT_HEADERS64 64位程序对应的PE文件头结构
_IMAGE_NT_HEADERS 对应C中的结构体(类型) 说明
"PE",0,0 DOWRD PE标识
IMAGE_FILE_HEADER IMAGE_FILE_HEADER 标准PE头
IMAGE_OPTIONAL_HEADER32 IMAGE_OPTIONAL_HEADER32 扩展PE头 32位
_IMAGE_NT_HEADERS64 对应C中的结构体(类型) 说明
"PE",0,0 DOWRD PE标识,固定值不可变
IMAGE_FILE_HEADER IMAGE_FILE_HEADER 标准PE头
IMAGE_OPTIONAL_HEADER64 IMAGE_OPTIONAL_HEADER64 扩展PE头 64位

结构体比较

32位结构体

typedef struct _IMAGE_OPTIONAL_HEADER {
    WORD    Magic;
    BYTE    MajorLinkerVersion;
    BYTE    MinorLinkerVersion;
    DWORD   SizeOfCode;
    DWORD   SizeOfInitializedData;
    DWORD   SizeOfUninitializedData;
    DWORD   AddressOfEntryPoint;
    DWORD   BaseOfCode;
    DWORD   BaseOfData;
    DWORD   ImageBase;
    DWORD   SectionAlignment;
    DWORD   FileAlignment;
    WORD    MajorOperatingSystemVersion;
    WORD    MinorOperatingSystemVersion;
    WORD    MajorImageVersion;
    WORD    MinorImageVersion;
    WORD    MajorSubsystemVersion;
    WORD    MinorSubsystemVersion;
    DWORD   Win32VersionValue;
    DWORD   SizeOfImage;
    DWORD   SizeOfHeaders;
    DWORD   CheckSum;
    WORD    Subsystem;
    WORD    DllCharacteristics;
    DWORD   SizeOfStackReserve;
    DWORD   SizeOfStackCommit;
    DWORD   SizeOfHeapReserve;
    DWORD   SizeOfHeapCommit;
    DWORD   LoaderFlags;
    DWORD   NumberOfRvaAndSizes;
    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

64位结构体

typedef struct _IMAGE_OPTIONAL_HEADER64 {
    WORD        Magic;
    BYTE        MajorLinkerVersion;
    BYTE        MinorLinkerVersion;
    DWORD       SizeOfCode;
    DWORD       SizeOfInitializedData;
    DWORD       SizeOfUninitializedData;
    DWORD       AddressOfEntryPoint;
    DWORD       BaseOfCode;
    ULONGLONG   ImageBase;
    DWORD       SectionAlignment;
    DWORD       FileAlignment;
    WORD        MajorOperatingSystemVersion;
    WORD        MinorOperatingSystemVersion;
    WORD        MajorImageVersion;
    WORD        MinorImageVersion;
    WORD        MajorSubsystemVersion;
    WORD        MinorSubsystemVersion;
    DWORD       Win32VersionValue;
    DWORD       SizeOfImage;
    DWORD       SizeOfHeaders;
    DWORD       CheckSum;
    WORD        Subsystem;
    WORD        DllCharacteristics;
    ULONGLONG   SizeOfStackReserve;
    ULONGLONG   SizeOfStackCommit;
    ULONGLONG   SizeOfHeapReserve;
    ULONGLONG   SizeOfHeapCommit;
    DWORD       LoaderFlags;
    DWORD       NumberOfRvaAndSizes;
    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64;

差异

成员\扩展PE头 _IMAGE_OPTIONAL_HEADER _IMAGE_OPTIONAL_HEADER64
BaseOfData DWORD 无此成员
ImageBase DWORD ULONGLONG
SizeOfStackReserve DWORD ULONGLONG
SizeOfStackCommit DWORD ULONGLONG
SizeOfHeapReserve DWORD ULONGLONG
SizeOfHeapCommit DWORD ULONGLONG

可以看到,64位相比于32位其实并没有太大的区别,只是删去了一个成员 以及 五个成员的数据类型由DWORD变为ULONGLONG

因为其区别并不明显,因此以32位结构体作为例子分析,64位结构体也近似相同


32位扩展PE头分析

扩展PE头中的成员较多,一般情况只需掌握部分重点即可(重点为表格中加黑的成员)

下面的分析参考自官方文档:ns-winnt-image_optional_header32和《Windows PE 权威指南》

PS:下面的分析较为冗长,类似参考文档,可以先跳过,先看下面的实战分析根据实战里的数据到对应的成员参考其含义

成员 数据宽度 说明
Magic WORD(2字节) 镜像文件的状态,可用于判断程序是32位还是64位
MajorLinkerVersion BYTE(字节) 链接器的主要版本号
MinorLinkerVersion BYTE(字节) 链接器的次要版本号
SizeOfCode DWORD(4字节) 代码段的大小
SizeOfInitializedData DWORD(4字节) 初始化数据段的大小
SizeOfUninitializedData DWORD(4字节) 未初始化数据段的大小
AddressOfEntryPoint DWORD(4字节) 程序入口
BaseOfCode DWORD(4字节) 代码开始的基址
BaseOfData DWORD(4字节) 数据开始的基址
ImageBase DWORD(4字节) 内存镜像基址
SectionAlignment DWORD(4字节) 内存对齐
FileAlignment WORD(2字节) 文件对齐
MajorOperatingSystemVersion WORD(2字节) 标识操作系统版本号 主版本号
MinorOperatingSystemVersion WORD(2字节) 标识操作系统版本号 次版本号
MajorImageVersion WORD(2字节) PE文件自身的版本号
MinorImageVersion WORD(2字节) PE文件自身的版本号
MajorSubsystemVersion WORD(2字节) 运行所需子系统版本号
MinorSubsystemVersion WORD(2字节) 运行所需子系统版本号
Win32VersionValue DWORD(4字节) 子系统版本的值,必须为0
SizeOfImage DWORD(4字节) Image大小
SizeOfHeaders DWORD(4字节) 所有头+节表按照文件对齐后的大小
CheckSum DWORD(4字节) 校验和
Subsystem WORD(2字节) 子系统
DllCharacteristics WORD(2字节) 文件特性 不只是针对DLL文件的
SizeOfStackReserve DWORD(4字节) 初始化时保留的栈大小
SizeOfStackCommit DWORD(4字节) 初始化时实际提交的大小
SizeOfHeapReserve DWORD(4字节) 初始化时保留的堆大小
SizeOfHeapCommit DWORD(4字节) 初始化时实践提交的大小
LoaderFlags DWORD(4字节) 调试相关
NumberOfRvaAndSizes DWORD(4字节) 目录项数目
DataDirectory[16] IMAGE_DATA_DIRECTORY[16]=128字节 指向数据目录中第一个IMAGE_DATA_DIRECTORY结构的指针(数据目录项)

Magic

镜像文件的状态。该成员可以是以下值之一

含义
IMAGE_NT_OPTIONAL_HDR_MAGIC 该文件是一个可执行的映像。这个值在32位应用程序中定义为IMAGE_NT_OPTIONAL_HDR32_MAGIC,在64位应用程序中定义为IMAGE_NT_OPTIONAL_HDR64_MAGIC
IMAGE_NT_OPTIONAL_HDR32_MAGIC=0x10b 该文件是一个可执行的映像(32位)
IMAGE_NT_OPTIONAL_HDR64_MAGIC=0x20b 该文件是一个可执行的映像(64位)
IMAGE_ROM_OPTIONAL_HDR_MAGIC=0x107 该文件是ROM镜像

MajorLinkerVersion

链接器版本号


MinorLinkerVersion

链接器次要版本号


SizeOfCode

代码段的大小(以字节为单位),如果有多个代码段,则为所有这些代码段的总和。是文件对齐后的大小 编译器填的 没用(不一定准确)


SizeOfInitializedData

初始化数据段的大小(以字节为单位),如果有多个初始化数据段,则为所有这些数据段的总和。是文件对齐后的大小 编译器填的 没用(不一定准确)


SizeOfUninitializedData

未初始化数据段的大小(以字节为单位),如果有多个未初始化数据段,则为所有这些数据段的总和。是文件对齐后的大小 编译器填的 没用(不一定准确)


AddressOfEntryPoint

一个指向入口点函数的指针,相对于Image的基址。

  1. 对于可执行文件,这是起始地址
  2. 对于设备驱动程序,这是初始化函数的地址
  3. 入口点函数对于dll是可选的。当没有入口点存在时,该成员为零

BaseOfCode

指向代码段开头的指针,相对于ImageBase。编译器填的  没用(不一定准确)


BaseOfData

指向数据段开头的指针,相对于ImageBase。编译器填的  没用(不一定准确)


ImageBase

Image(PE文件)载入内存时第一个字节的首选地址。该值是64K字节的倍数

  • dll的默认值是0x10000000
  • 应用程序的默认值为0x00400000,
  • Windows CE上的默认值为0x00010000

SectionAlignment

加载到内存中的节的对齐方式,以字节为单位。该值必须大于或等于FileAlignment(文件对齐)成员。默认值是系统的页面大小


FileAlignment

Image(PE文件)中各节的原始数据(以字节为单位)的对齐方式。该值应该是512到64K(包括)之间2的幂。缺省值是512。如果SectionAlignment成员小于系统页面大小,则该成员必须与SectionAlignment相同


MajorOperatingSystemVersion

所需操作系统的主要版本号


MinorOperatingSystemVersion

所需操作系统的次要版本号


MajorImageVersion

镜像(PE文件)的主版本号


MinorImageVersion

镜像(PE文件)的次要版本号


MajorSubsystemVersion

子系统的主要版本号


Win32VersionValue

该成员是保留的,并且必须为0


SizeOfImage

Image的大小,以字节为单位,包括所有头。必须是多个SectionAlignment

内存中整个PE文件的映射的尺寸,可比实际的值大,必须是SectionAlignment的整数倍


SizeOfHeaders

下列项的组合大小,舍入为“文件对齐”成员中指定值的倍数

  • IMAGE_DOS_HEADER(DOS MZ头) 中的最后一个成员e_lfanew  
  • PE文件头标志 signature 的 大小 4字节
  • IMAGE_FILE_HEADER(标准PE头)的大小
  • 扩展PE头的大小
  • 所有节头(节表)的大小

SizeOfHeaders=

{

e_lfanew

+sizeof(signature)

+sizeof(_IMAGE_FILE_HEADER)

+sizeof(_IMAGE_OPTIONAL_HEADER)

+sizeof(_IMAGE_SECTION_HEADER)

}(文件对齐)

即 DOS部首+PE文件头+节表 按照文件对齐后的大小


CheckSum

Image(PE文件)校验和。以下文件在加载时进行验证:所有驱动程序,在引导时加载的任何DLL,以及加载到关键系统进程中的任何DLL


Subsystem

运行此映像所需的子系统。定义了以下值:

宏定义 含义
IMAGE_SUBSYSTEM_UNKNOWN 0 未知的子系统
IMAGE_SUBSYSTEM_NATIVE 1 不需要子系统(设备驱动程序和本机系统进程)
IMAGE_SUBSYSTEM_WINDOWS_GUI 2 Windows图形用户界面子系统
IMAGE_SUBSYSTEM_WINDOWS_CUI 3 Windows字符模式用户界面(CUI)子系统
IMAGE_SUBSYSTEM_OS2_CUI 5 OS/2 CUI子系统
IMAGE_SUBSYSTEM_POSIX_CUI 7 POSIX CUI子系统
IMAGE_SUBSYSTEM_WINDOWS_CE_GUI 9 Windows CE系统
IMAGE_SUBSYSTEM_EFI_APPLICATION 10 可扩展固件接口(EFI)应用程序
IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER 11 带有引导服务的EFI驱动程序
IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER 12 带有运行时服务的EFI驱动程序
IMAGE_SUBSYSTEM_EFI_ROM 13 EFI ROM镜像
IMAGE_SUBSYSTEM_XBOX 14 Xbox系统
IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION 16 启动应用程序

DllCharacteristics

官方文档版本

Image的DLL特性,定义了以下值

宏定义 含义
0x0001 保留,必须为0
0x0002 保留,必须为0
0x0004 保留,必须为0
0x0008 保留,必须为0
IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA 0x0020 具有64位地址空间的ASLR
IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE 0x0040 DLL可以在加载时重新定位
IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY 0x0080 强制进行代码完整性检查。如果你设置了这个标志,并且section只包含未初始化的数据,那么将该section的IMAGE_SECTION_HEADER的PointerToRawData成员设置为0;否则,由于无法验证数字签名,Image将无法加载
IMAGE_DLLCHARACTERISTICS_NX_COMPAT 0x0100 该映像与数据执行预防(DEP)兼容
IMAGE_DLLCHARACTERISTICS_NO_ISOLATION 0x0200 映像可以被隔离,但不应该被隔离
IMAGE_DLLCHARACTERISTICS_NO_SEH 0x0400 该映像不使用结构化异常处理(SEH)。在此映像中不能调用任何处理程序
IMAGE_DLLCHARACTERISTICS_NO_BIND 0x0800 不要绑定映像
IMAGE_DLL_CHARACTERISTICS_APPCONTAINER 0x1000 映像应该在AppContainer中执行
IMAGE_DLLCHARACTERISTICS_WDM_DRIVER 0x2000 一个WDM驱动
IMAGE_DLL_CHARACTERISTICS_GUARD_CF 0x4000 映像支持控制流保护(Control Flow Guard)
IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE 0x8000 该映像是终端服务器感知的

PE权威指南版本

image-20210330140944224

image-20210330141007450


SizeOfStackReserve

为堆栈保留的字节数。只有SizeOfStackCommit成员指定的内存在加载时被提交;其余的页面每次只提供一个页面,直到达到这个预留大小


SizeOfStackCommit

要提交给堆栈的字节数


SizeOfHeapReserve

为本地堆保留的字节数。只有SizeOfHeapCommit成员指定的内存在加载时被提交;其余的页面每次只提供一个页面,直到达到这个预留大小


SizeOfHeapCommit

要为本地堆提交的字节数


LoaderFlags

该成员已过时


NumberOfRvaAndSizes

可选头的其余部分中的目录条目数。每个条目都描述了一个位置和大小


DataDirectory

指向数据目录中第一个IMAGE_DATA_DIRECTORY结构的指针

所需目录条目的索引号。该参数可以是以下值之一:

宏定义 含义
IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 特定于体系结构的数据,预留为0
IMAGE_DIRECTORY_ENTRY_BASERELOC 5 基地址重定位表
IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 绑定导入表
IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 COM描述符表
IMAGE_DIRECTORY_ENTRY_DEBUG 6 调试表
IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 延迟导入表
IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 异常表
IMAGE_DIRECTORY_ENTRY_EXPORT 0 导出表
IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 全局指针的相对虚拟地址
IMAGE_DIRECTORY_ENTRY_IAT 12 导入地址表
IMAGE_DIRECTORY_ENTRY_IMPORT 1 导入表
IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 加载配置表
IMAGE_DIRECTORY_ENTRY_RESOURCE 2 资源表
IMAGE_DIRECTORY_ENTRY_SECURITY 4 安全表
IMAGE_DIRECTORY_ENTRY_TLS 9 线程本地存储表

实战分析

获取数据

从先前分析的标准PE头的结尾开始看起,选中部分为扩展PE头,共占224字节

image-20210330141822154


按顺序依次将数据填入对应的成员得到:

成员
Magic 0x010B
MajorLinkerVersion 0x09
MinorLinkerVersion 0x00
SizeOfCode 0x00199200
SizeOfInitializedData 0x000BEC00
SizeOfUninitializedData 0x00000000
AddressOfEntryPoint 0x0016AF12
BaseOfCode 0x00001000
BaseOfData 0x0019B000
ImageBase 0x00400000
SectionAlignment 0x00001000
FileAlignment 0x00000200
MajorOperatingSystemVersion 0x0005
MinorOperatingSystemVersion 0x0000
MajorImageVersion 0x0000
MinorImageVersion 0x0000
MajorSubsystemVersion 0x0005
MinorSubsystemVersion 0x0000
Win32VersionValue 0x00000000
SizeOfImage 0x00298000
SizeOfHeaders 0x00000400
CheckSum 0x0025cd89
Subsystem 0x0002
DllCharacteristics 0x8140
SizeOfStackReserve 0x00100000
SizeOfStackCommit 0x00001000
SizeOfHeapReserve 0x00100000
SizeOfHeapCommit 0x00001000
LoaderFlags 0x00000000
NumberOfRvaAndSizes 0x00000010
DataDirectory[16] 留作之后的笔记

分析数据

鉴于篇幅和实用性考虑,只分析比较重要的成员


Magic

Magic的值为0x010B,根据前面的分析可知:该文件是一个可执行的映像(32位)


AddressOfEntryPoint

AddressOfEntryPoint的值为0x0016AF12,根据前面的分析可知:一个指向入口点函数的指针,相对于Image的基址是0x0016AF12

而Image的基址就是后面的 ImageBase = 0x00400000

于是该指针的的绝对地址为基址+偏移=0x00400000+0x0016AF12=0x0056AF12

于是得到了该程序的程序入口点为0x0056AF12,为了验证其准确性,使用OD打开程序

image-20210330144334813

可以看到OD自动中断在了程序入口点0x0056AF12


PS:如果OD暂停到了其它地方,则需要设置一下OD的中断点:

选项→调试设置(或使用快捷键Alt+0)

image-20210330144509629


事件→主模块入口点

image-20210330144628298


ImageBase

ImageBase的值为0x00400000,根据前面的分析可知:ImageBase正好是应用程序的默认值0x00400000

且ImageBase=0x00400000是64K字节的倍数


SectionAlignment、FileAlignment、SizeOfHeaders

有关内存对齐和文件对齐的相关内容 在PE文件笔记二 PE文件的两种状态已经学习过了,这里不再赘述


SizeOfImage

SizeOfImage的值为0x00298000,根据前面的分析可知:

SizeOfImage表示内存中整个PE文件的映射的尺寸,可比实际的值大,必须是SectionAlignment的整数倍

于是打开程序,并使用WinHex查看其在内存中的状态(在PE文件笔记二 PE文件的两种状态中已经演示过,这里不再赘述,直接看结果)

用WinHex附加上程序后,拉到最底部,查看文件的最大偏移量

image-20210330150941336


可以看到此时的最大偏移量为697FFF,用最大偏移量减去ImageBase 0x400000得到相对偏移为297FFF=SizeOfImage-1

验证了SizeOfImage可以比实际的值大

再验证SizeOfImage为内存对齐SectionAlignment的整数倍:

SizeOfImage/SectionAlignment=0x00298000/0x1000=0x298,可以整除,验证完毕


CheckSum

CheckSum从头部开始 两两字节不断相加,一直相加到最后,期间如果溢出,则直接舍去溢出部分,最后加完的结果再加上整个文件的长度就得到了CheckSum,操作系统就是通过这种方式来检验文件是否被修改

image-20210330152330856


但只对以下文件在加载时进行验证:所有驱动程序,在引导时加载的任何DLL,以及加载到关键系统进程中的任何DLL

此时程序本身并不属于上面提到的文件类型,于是此时的CheckSum并不生效

由于CheckSum的计算比较麻烦,于是这里就略去CheckSum计算的验证,简单说明了CheckSum的计算原理

下面来验证一下CheckSum的作用范围,此时程序的CheckSum应该是无效的

CheckSum的值为0x0025cd89,将它修改为0

1.找到CheckSum

image-20210330152551798


2.选中CheckSum,鼠标右键→编辑

image-20210330152634764


3.填充选块

image-20210330152830026


4.填充0

image-20210330152847852


5.修改完毕

image-20210330152923193


6.保存,然后打开程序

使用快捷键 Ctrl+S 保存,确定

image-20210330152952218


打开程序,依旧可以正常运行,验证完毕

image-20210330153037836


Subsystem

Subsystem的值为0x0002,根据前面的分析可知:运行该程序所需子系统为:Windows图形用户界面子系统


DllCharacteristics

DllCharacteristics的值为0x8140

通过PE权威指南

转化为二进制得到:1000000101000000

数据位为1的位数有:第6位,第8位,第15位

根据前面的分析可知:DLL可以在加载时重新定位、该映像与数据执行预防(DEP)兼容、该映像是终端服务器感知的


通过官方文档

DllCharacteristics的值为0x8140=0x8000+0x0100+0x0040

根据前面的分析可知:DLL可以在加载时重新定位、该映像与数据执行预防(DEP)兼容、该映像是终端服务器感知的


自写代码解析PE文件头

在先前代码的基础上,进一步改进

// PE.cpp : Defines the entry point for the console application.
//
#include <stdio.h>
#include <windows.h>
#include <winnt.h>
//在VC6这个比较旧的环境里,没有定义64位的这个宏,需要自己定义,在VS2019中无需自己定义
#define IMAGE_FILE_MACHINE_AMD64  0x8664
int main(int argc, char* argv[])
{
        //创建DOS对应的结构体指针
        _IMAGE_DOS_HEADER* dos;
        //读取文件,返回文件句柄
        HANDLE hFile = CreateFileA("C:\\Users\\lyl610abc\\Desktop\\dbgview64.exe", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
        //根据文件句柄创建映射
        HANDLE hMap = CreateFileMappingA(hFile, NULL, PAGE_READONLY, 0, 0, 0);
        //映射内容
        LPVOID pFile = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
        //类型转换,用结构体的方式来读取
        dos = (_IMAGE_DOS_HEADER*)pFile;
        //输出dos->e_magic,以十六进制输出
        printf("dos->e_magic:%X\n", dos->e_magic);

        //创建指向PE文件头标志的指针
        DWORD* peId;
        //让PE文件头标志指针指向其对应的地址=DOS首地址+偏移
        peId = (DWORD*)((UINT)dos + dos->e_lfanew);
        //输出PE文件头标志,其值应为4550,否则不是PE文件
        printf("peId:%X\n", *peId);

        //创建指向可选PE头的第一个成员magic的指针
        WORD* magic;
        //让magic指针指向其对应的地址=PE文件头标志地址+PE文件头标志大小+标准PE头大小
        magic = (WORD*)((UINT)peId + sizeof(DWORD) + sizeof(_IMAGE_FILE_HEADER));
        //输出magic,其值为0x10b代表32位程序,其值为0x20b代表64位程序
        printf("magic:%X\n", *magic);
        //根据magic判断为32位程序还是64位程序
        switch (*magic) {
        case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
        {
                printf("32位程序\n");
                //确定为32位程序后,就可以使用_IMAGE_NT_HEADERS来接收数据了
                //创建指向PE文件头的指针
                _IMAGE_NT_HEADERS* nt;
                //让PE文件头指针指向其对应的地址
                nt = (_IMAGE_NT_HEADERS*)peId;
                printf("Machine:%X\n", nt->FileHeader.Machine);
                printf("Magic:%X\n", nt->OptionalHeader.Magic);
                break;
        }

        case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
        {
                printf("64位程序\n");
                //确定为64位程序后,就可以使用_IMAGE_NT_HEADERS64来接收数据了
                //创建指向PE文件头的指针
                _IMAGE_NT_HEADERS64* nt;
                nt = (_IMAGE_NT_HEADERS64*)peId;
                printf("Machine:%X\n", nt->FileHeader.Machine);
                printf("Magic:%X\n", nt->OptionalHeader.Magic);
                break;
        }

        default:
        {
                printf("error!\n");
                break;
        }

        }
        return 0;
}

运行结果

32位程序

image-20210330165824365

64位程序

image-20210330165958055


代码说明

代码基于上一次的笔记PE文件笔记四 PE文件头之标准PE头改进了判断程序32位或64位的方法,并没有什么太大的变动,有关代码的分析在上一次笔记已经给出,这里也就不再赘述


总结

  • 扩展PE头根据程序是32位或64位而对应两种不同的结构
  • 根据扩展PE头的第一个成员Magic可以判断出程序是32位的还是64位的
  • 两种扩展PE头结构实际相差不多,掌握了32位的结构其实也相当于掌握了64位的结构
  • 扩展PE头英文虽然为optional header,译为可选头,但它其实必不可少且极其重要
  • 扩展PE头中的DataDirectory内容涉及较多,留作之后

附件

附上本笔记中分析的EverEdit文件:点我下载

免费评分

参与人数 11吾爱币 +8 热心值 +11 收起 理由
Alutemurat + 1 + 1 谢谢@Thanks!
atom_warhead + 1 用心讨论,共获提升!
悠然地2012 + 1 热心回复!
cavendishhhh + 1 + 1 谢谢@Thanks!
chycry5123 + 1 + 1 我很赞同!
vinafule + 1 我很赞同!
debug_cat + 2 + 1 来给大佬打call了。牛逼这么详细的笔记。
wanfon + 1 + 1 谢谢@Thanks!
国际豆哥 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
bailemenmlbj + 1 + 1 谢谢@Thanks!
正己 + 1 我很赞同!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| lyl610abc 发表于 2021-3-30 17:15
本帖最后由 lyl610abc 于 2021-3-30 17:16 编辑

关于扩展PE头的内容属实较多,为力求准确,我翻阅了微软官方的文档:官方文档地址和《Windows PE 权威指南》这本宝藏书
限于篇幅原因,已经尽可能地说明了扩展PE头的各个成员作用,可能会由于笔记偏文档化而劝退不少人
觉得枯燥的,可以先大致看一下32位扩展PE头和64位扩展PE头的差异,然后直接跳到后面的实战分析 结合 前面的文档学习
笔记可能写的不是很好,大家轻喷
PS:写笔记真的很花时间,觉得有用的可以支持一下
 楼主| lyl610abc 发表于 2021-3-30 19:05
8jUmJL 发表于 2021-3-30 18:50
pe?做系统的那个吗?

PE文件的全称是Portable Executable,意为可移植的可执行的文件,常见的EXE、DLL、OCX、SYS、COM都是PE文件,PE文件是微软Windows操作系统上的程序文件(可能是间接被执行,如DLL)

你说的PE是:
Windows Preinstallation Environment(Windows PE),Windows预安装环境,是带有有限服务的最小Win32子系统,基于以保护模式运行的Windows XP Professional及以上内核。
bester 发表于 2021-3-30 17:26
想当初肝PE结构学IAT/EAT hook 肝到头秃,回头一看这玩意看似没啥用,但是关键时候总得了解一下,等觉得有必要学习一下的时候,几百年难得用一次里面的知识
正己 发表于 2021-3-30 17:33
这就是大佬吗?爱了爱了
 楼主| lyl610abc 发表于 2021-3-30 17:36
bester 发表于 2021-3-30 17:26
想当初肝PE结构学IAT/EAT hook 肝到头秃,回头一看这玩意看似没啥用,但是关键时候总得了解一下,等觉得有 ...

很多东西潜移默化地发挥作用,会了以后看它就觉得习以为常,但要是不会或者不熟悉碰到的时候就会很难受
Kingmxp 发表于 2021-3-30 18:22
学习学习 支持了
顺⑦zi然 发表于 2021-3-30 18:44
看不懂,不怪我……
8jUmJL 发表于 2021-3-30 18:50
pe?做系统的那个吗?
地藏王菩萨 发表于 2021-3-30 18:58
感谢分享,虽然看不懂。
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-4-19 11:55

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表