ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

4.PE文件之扩展PE头(IMAGE_OPTIONAL_HEADER)

2021-10-26 18:01:49  阅读:319  来源: 互联网

标签:IMAGE PE pOpo HEADER printf OPTIONAL 0x%


可选/扩展PE头IMAGE_OPTIONAL_HEADER,它有着比标准PE头(IMAGE_FILE_HEADER)更多的内容.

IMAGE_OPTIONAL_HEADER

结构及成员含义如下:

//大小: 32bit(0xE0) 64bit(0xF0)

#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES    16

typedef struct _IMAGE_OPTIONAL_HEADER {

    WORD    Magic;                          //文件类型: 10bh为32位PE文件 / 20bh为64位PE文件
    BYTE    MajorLinkerVersion;             //链接器(主)版本号 对执行没有任何影响
    BYTE    MinorLinkerVersion;             //链接器(次)版本号 对执行没有任何影响
    DWORD   SizeOfCode;                     //包含代码的节的总大小.文件对齐后的大小.编译器填的没用
    DWORD   SizeOfInitializedData;          //包含已初始化数据的节的总大小.文件对齐后的大小.编译器填的没用.
    DWORD   SizeOfUninitializedData;        //包含未初始化数据的节的总大小.文件对齐后的大小.编译器填的没用.(未初始化数据,在文件中不占用空间;但在被加载到内存后,PE加载程序会为这些数据分配适当大小的虚拟地址空间).
    DWORD   AddressOfEntryPoint;            //程序入口(RVA)
    DWORD   BaseOfCode;                     //代码的节的基址(RVA).编译器填的没用(代码节起始的RVA,表示映像被加载进内存时代码节的开头相对于ImageBase的偏移地址,节的名称通常为".text")
    DWORD   BaseOfData;                     //数据的节的基址(RVA).编译器填的没用(数据节起始的RVA,表示映像被加载进内存时数据节的开头相对于ImageBase的偏移地址,节的名称通常为".data")
    DWORD   ImageBase;                      //内存镜像基址
    DWORD   SectionAlignment;               //内存对齐大小
    DWORD   FileAlignment;                  //文件对齐大小
    WORD    MajorOperatingSystemVersion;    //标识操作系统主版本号 
    WORD    MinorOperatingSystemVersion;    //标识操作系统次版本号 
    WORD    MajorImageVersion;              //PE文件自身的主版本号
    WORD    MinorImageVersion;              //PE文件自身的次版本号
    WORD    MajorSubsystemVersion;          //运行所需子系统主版本号
    WORD    MinorSubsystemVersion;          //运行所需子系统次版本号
    DWORD   Win32VersionValue;              //子系统版本的值.必须为0,否则程序运行失败.
    DWORD   SizeOfImage;                    //内存中整个PE文件的映射尺寸.可比实际的值大.必须是SectionAlignment的整数倍
    DWORD   SizeOfHeaders;                  //所有头+节表按照文件对齐后的大小.
    DWORD   CheckSum;                       //校验和.大多数PE文件该值为0.在内核模式的驱动程序和系统DLL中,该值则是必须存在且是正确的.在IMAGEHLP.DLL中函数CheckSumMappedFile就是用来计算文件头校验和的,对于整个PE文件也有一个校验函数MapFileAndCheckSum.
    WORD    Subsystem;                      //文件子系统  驱动程序(1) 图形界面(2) 控制台/DLL(3)
    WORD    DllCharacteristics;             //文件特性.不是针对DLL文件的
    DWORD   SizeOfStackReserve;             //初始化时保留的栈大小.该字段默认值为0x100000(1MB),如果调用API函数CreateThread时,堆栈参数大小传入NULL,则创建出来的栈大小将是1MB.
    DWORD   SizeOfStackCommit;              //初始化时实际提交的栈大小.保证初始线程的栈实际占用内存空间的大小,它是被系统提交的.这些提交的栈不存在与交换文件里,而是在内存中.
    DWORD   SizeOfHeapReserve;              //初始化时保留的堆大小.用来保留给初始进程堆使用的虚拟内存,这个堆的句柄可以通过调用函数GetProcessHeap获得.每一个进程至少会有一个默认的进程堆,该堆在进程启动时被创建,而且在进程的生命期中不会被删除.默认值为1MB.
    DWORD   SizeOfHeapCommit;               //初始化时实践提交的堆大小.在进程初始化时设定的堆所占用的内存空间,默认值为PAGE_SIZE. 
    DWORD   LoaderFlags;                    //调试相关
    DWORD   NumberOfRvaAndSizes;            //目录项数目,默认为10h.
    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];//结构数组 数组元素个数由IMAGE_NUMBEROF_DIRECTORY_ENTRIES定义
} IMAGE_OPTIONAL_HEADER32, * PIMAGE_OPTIONAL_HEADER32;

通过WinHex定位IMAGE_OPTIONAL_HEADER:

Magic: 魔术字,表示了PE文件类型,如下表所示:

常量符号常量值
IMAGE_NT_OPTIONAL_HDR32_MAGIC0x10bPE32
IMAGE_NT_OPTIONAL_HDR64_MAGIC0x20bPE64
IMAGE_ROM_OPTIONAL_HDR_MAGIC0x107ROM

SizeOfCode: 表示了所有代码节的总和(以字节计算),该大小是基于文件对齐后的大小.(判断某个节是否包含代码的方法是根据节的属性中是否含有IMAGE_SCN_CNT_CODE标志).

AddressOfEntryPoint: 表示了程序入口地址,该值是一个RVA,加上Imagebase为程序在内存中的入口地址(OEP).如果在一个可执行文件中附加了一段自己的代码,并且想让这段代码首先被执行,需要修改这里的值指向自己的代码位置.对于普通程序来说它就是启动地址;对于设备驱动程序来说它是初始化函数的地址.入口点对于DLL来说是可选的,如果不存在入口点,这个字段必须设置为0.

ImageBase: 指出PE文件的优先载入内存中的起始地址,如果这个地址已经被占用,操作系统会重新分配(DLL会出现这种情况),这时就需要重定位表提供的数据来修复,EXE首先加载不会出现这种情况,大部分EXE imagebase默认为0x400000. 此值可随便修改,不能超出虚拟地址空间以及必须是64KB的整数倍,并修复重定位数据即可正常运行.

使用OD / DBG打开进程观察入口点

入口点为ImageBase(0x00400000) + AddressOfEntryPoint(0x00015757)

SectionAlignment: 内存中节的对齐粒度,该字段指定了节被装入内存后的对齐单位.Win32的页面大小是4KB,所以Win32PE文件中节的内存对齐粒度一般都为4KB大小,十六进制表示为1000h.SectionAlignment必须大于或等于FileAlignment.当它小于系统页面大小时,必须保证SectionAlignmentFileAlignment相等.

FileAlignment: 文件中节的对齐粒度,Win32PE文件中节的文件对齐粒度一般都为200h,Windows会选择使用512字节的大小(一个物理扇区的大小).

SizeOfImage: 表示内存中整个PE文件的映射尺寸,必须是SectionAlignment的整数倍.(该值可以比实际的值大,但不能比它小).

SizeOfHeaders: 表示了PE文件中所有头+节表按照文件对齐后的大小:

代码计算如下:

int main()
{
  //读取文件二进制数据
  DWORD dwFileSize = 0;
  PCHAR pFileBuffer = FileToMem(FILE_PATH_IN, &dwFileSize);

  PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pFileBuffer;
  PIMAGE_NT_HEADERS pNth = (PIMAGE_NT_HEADERS)(pFileBuffer + pDos->e_lfanew);
  PIMAGE_FILE_HEADER pFil = (PIMAGE_FILE_HEADER)((PUCHAR)pNth + 4);


  DWORD SizeOfHeader =  pDos->e_lfanew/*包括了IMAGE_DOS_HEADERS + IMAGE_DOS_STUB*/ + \
              4/*IMAGE_NT_HEADER -> Signature*/ + \
              IMAGE_SIZEOF_FILE_HEADER/*IMAGE_FILE_HEADER结构大小*/ + \
              pFil->SizeOfOptionalHeader/*IMAGE_OPTIONAL_HEADER结构大小*/ + \
              pFil->NumberOfSections * sizeof(IMAGE_SECTION_HEADER)/*所有IMAGE_SECTION_HEADER结构大小*/;
  //之后按照文件对齐即可算出SizeOfHeader的大小               
  return 0;
}

Subsystem:子系统标识,定义如下:

常量符号常量值常量含义
IMAGE_SUBSYSTEM_UNKNOWN0未知子系统
IMAGE_SUBSYSTEM_NATIVE1设备驱动程序和Native Windows进程
IMAGE_SUBSYSTEM_WINDOWS_GUI2Windows图形用户界面
IMAGE_SUBSYSTEM_WINDOWS_CUI3Windows控制台
IMAGE_SUBSYSTEM_OS2_CUI5OS/2 控制台
IMAGE_SUBSYSTEM_POSIX_CUI7Posix 控制台
IMAGE_SUBSYSTEM_NATIVE_WINDOWS8Windows9x 驱动
IMAGE_SUBSYSTEM_WINDOWS_CE_GUI9Windows CE图形界面
IMAGE_SUBSYSTEM_EFI_APPLICATION10可扩展固件接口EFI 应用程序
IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER11带引导服务的EFI驱动程序
IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER12带运行时服务的EFI驱动程序
IMAGE_SUBSYSTEM_EFI_ROM13EFI ROM映像
IMAGE_SUBSYSTEM_XBOX14X- Box
IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION16
IMAGE_SUBSYSTEM_XBOX_CODE_CATALOG17

Subsystem = 0x0002(Windows图形用户界面).

DllCharacteristics: 文件属性,每位含义如下:

数据位常量符号常量值为1时含义
0IMAGE_LIBRARY_PROCESS_INIT0x0001保留必须为0
1IMAGE_LIBRARY_PROCESS_TERM0x0002保留必须为0
2IMAGE_LIBRARY_THREAD_INIT0x0004保留必须为0
3IMAGE_LIBRARY_THREAD_TERM0x0008保留必须为0
5IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA0x0020image can handle a high entropy 64-bit virtual address space.
6IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE0x0040DLL可以在加载时被重定位.
7IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY0x0080强制代码实施完整性验证
8IMAGE_DLLCHARACTERISTICS_NX_COMPAT0x0100该映像兼容DEP
9IMAGE_DLLCHARACTERISTICS_NO_ISOLATION0x0200可以隔离,但并不隔离次映像
10IMAGE_DLLCHARACTERISTICS_NO_SEH0x0400映像不使用SEH
11IMAGE_DLLCHARACTERISTICS_NO_BIND0x0800不绑定映像
12IMAGE_DLLCHARACTERISTICS_APPCONTAINER0x1000保留必须为0
13IMAGE_DLLCHARACTERISTICS_WDM_DRIVER0x2000该映像为一个WDM Driver
14IMAGE_DLLCHARACTERISTICS_GUARD_CF0x4000保留必须为0
15IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE0x8000可用于终端服务器

DataDirectory: 结构表示了PE文件中各个目录项信息,是一个数组,结构定义如下:

#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES    16

IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]

typedef struct _IMAGE_DATA_DIRECTORY {
    DWORD   VirtualAddress; //目录项对应RVA(VirtualAddress + Imagebase 为目录项在内存中的起始地址) PS(属性证书数据中该字段的值表示的是FOA)
    DWORD   Size;           //目录项对于大小
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

常量定义常量值含义
IMAGE_DIRECTORY_ENTRY_EXPORT0导出表
IMAGE_DIRECTORY_ENTRY_IMPORT1导入表
IMAGE_DIRECTORY_ENTRY_RESOURCE2资源
IMAGE_DIRECTORY_ENTRY_EXCEPTION3异常
IMAGE_DIRECTORY_ENTRY_SECURITY4安全证书
IMAGE_DIRECTORY_ENTRY_BASERELOC5重定位表
IMAGE_DIRECTORY_ENTRY_DEBUG6调试信息
IMAGE_DIRECTORY_ENTRY_ARCHITECTURE7版权所有
IMAGE_DIRECTORY_ENTRY_GLOBALPTR8全局指针
IMAGE_DIRECTORY_ENTRY_TLS9TLS 表
IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG10加载配置
IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT11绑定导入
IMAGE_DIRECTORY_ENTRY_IAT12IAT 表
IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT13延迟导入
IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR14COM

代码定位IMAGE_OPTIONAL_HEADER如下:

读取文件代码:

PVOID FileToMem(IN PCHAR szFilePath, OUT LPDWORD dwFileSize)
{
	//打开文件
	FILE* pFile = fopen(szFilePath, "rb");
	if (!pFile)
	{
		printf("FileToMem fopen Fail \r\n");
		return NULL;
	}
 
	//获取文件长度
	fseek(pFile, 0, SEEK_END);			//SEEK_END文件结尾
	DWORD Size = ftell(pFile);
	fseek(pFile, 0, SEEK_SET);			//SEEK_SET文件开头
 
	//申请存储文件数据缓冲区
	PCHAR pFileBuffer = (PCHAR)malloc(Size);
	if (!pFileBuffer)
	{
		printf("FileToMem malloc Fail \r\n");
		fclose(pFile);
		return NULL;
	}
 
	//读取文件数据
	fread(pFileBuffer, Size, 1, pFile);
 
	//判断是否为可执行文件
	if (*(PSHORT)pFileBuffer != IMAGE_DOS_SIGNATURE)
	{
		printf("Error: MZ \r\n");
		fclose(pFile);
		free(pFileBuffer);
		return NULL;
	}
 
	if (*(PDWORD)(pFileBuffer + *(PDWORD)(pFileBuffer + 0x3C)) != IMAGE_NT_SIGNATURE)
	{
		printf("Error: PE \r\n");
		fclose(pFile);
		free(pFileBuffer);
		return NULL;
	}
 
	if (dwFileSize)
	{
		*dwFileSize = Size;
	}
 
	fclose(pFile);
 
	return pFileBuffer;
}

读取扩展PE头数据代码

VOID PrintOpoHeader()
{
	//读取文件二进制数据
	DWORD dwFileSize = 0;
	PCHAR pFileBuffer = FileToMem(FILE_PATH_IN, &dwFileSize);
	if (!pFileBuffer)
	{
		return;
	}

	PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pFileBuffer;
	PIMAGE_NT_HEADERS pNth = (PIMAGE_NT_HEADERS)(pFileBuffer + pDos->e_lfanew);
	PIMAGE_FILE_HEADER pFil = (PIMAGE_FILE_HEADER)((PUCHAR)pNth + 4);
	PIMAGE_OPTIONAL_HEADER pOpo = (PIMAGE_OPTIONAL_HEADER)((PUCHAR)pFil + IMAGE_SIZEOF_FILE_HEADER);

	printf("IMAGE_OPTIONAL_HEADER->Magic  [0x%04x] \r\n", pOpo->Magic);
	printf("IMAGE_OPTIONAL_HEADER->MajorLinkerVersion  [0x%02x] \r\n", pOpo->MajorLinkerVersion);
	printf("IMAGE_OPTIONAL_HEADER->MinorLinkerVersion  [0x%02x] \r\n", pOpo->MinorLinkerVersion);
	printf("IMAGE_OPTIONAL_HEADER->SizeOfCode  [0x%08x] \r\n", pOpo->SizeOfCode);
	printf("IMAGE_OPTIONAL_HEADER->SizeOfInitializedData  [0x%08x] \r\n", pOpo->SizeOfInitializedData);
	printf("IMAGE_OPTIONAL_HEADER->SizeOfUninitializedData  [0x%08x] \r\n", pOpo->SizeOfUninitializedData);
	printf("IMAGE_OPTIONAL_HEADER->AddressOfEntryPoint  [0x%08x] \r\n", pOpo->AddressOfEntryPoint);
	printf("IMAGE_OPTIONAL_HEADER->BaseOfCode  [0x%08x] \r\n", pOpo->BaseOfCode);
	printf("IMAGE_OPTIONAL_HEADER->BaseOfData  [0x%08x] \r\n", pOpo->BaseOfData);
	printf("IMAGE_OPTIONAL_HEADER->ImageBase  [0x%08x] \r\n", pOpo->ImageBase);
	printf("IMAGE_OPTIONAL_HEADER->SectionAlignment  [0x%08x] \r\n", pOpo->SectionAlignment);
	printf("IMAGE_OPTIONAL_HEADER->FileAlignment  [0x%08x] \r\n", pOpo->FileAlignment);
	printf("IMAGE_OPTIONAL_HEADER->MajorOperatingSystemVersion  [0x%04x] \r\n", pOpo->MajorOperatingSystemVersion);
	printf("IMAGE_OPTIONAL_HEADER->MinorOperatingSystemVersion  [0x%04x] \r\n", pOpo->MinorOperatingSystemVersion);
	printf("IMAGE_OPTIONAL_HEADER->MajorImageVersion  [0x%04x] \r\n", pOpo->MajorImageVersion);
	printf("IMAGE_OPTIONAL_HEADER->MinorImageVersion  [0x%04x] \r\n", pOpo->MinorImageVersion);
	printf("IMAGE_OPTIONAL_HEADER->MajorSubsystemVersion  [0x%04x] \r\n", pOpo->MajorSubsystemVersion);
	printf("IMAGE_OPTIONAL_HEADER->MinorSubsystemVersion  [0x%04x] \r\n", pOpo->MinorSubsystemVersion);
	printf("IMAGE_OPTIONAL_HEADER->Win32VersionValue  [0x%08x] \r\n", pOpo->Win32VersionValue);
	printf("IMAGE_OPTIONAL_HEADER->SizeOfImage  [0x%08x] \r\n", pOpo->SizeOfImage);
	printf("IMAGE_OPTIONAL_HEADER->SizeOfHeaders  [0x%08x] \r\n", pOpo->SizeOfHeaders);
	printf("IMAGE_OPTIONAL_HEADER->CheckSum  [0x%08x] \r\n", pOpo->CheckSum);
	printf("IMAGE_OPTIONAL_HEADER->Subsystem  [0x%04x] \r\n", pOpo->Subsystem);
	printf("IMAGE_OPTIONAL_HEADER->DllCharacteristics  [0x%04x] \r\n", pOpo->DllCharacteristics);
	printf("IMAGE_OPTIONAL_HEADER->SizeOfStackReserve  [0x%08x] \r\n", pOpo->SizeOfStackReserve);
	printf("IMAGE_OPTIONAL_HEADER->SizeOfStackCommit  [0x%08x] \r\n", pOpo->SizeOfStackCommit);
	printf("IMAGE_OPTIONAL_HEADER->SizeOfHeapReserve  [0x%08x] \r\n", pOpo->SizeOfHeapReserve);
	printf("IMAGE_OPTIONAL_HEADER->SizeOfHeapCommit  [0x%08x] \r\n", pOpo->SizeOfHeapCommit);
	printf("IMAGE_OPTIONAL_HEADER->LoaderFlags  [0x%08x] \r\n", pOpo->LoaderFlags);
	printf("IMAGE_OPTIONAL_HEADER->NumberOfRvaAndSizes  [0x%08x] \r\n", pOpo->NumberOfRvaAndSizes);

	PIMAGE_DATA_DIRECTORY pDir = pOpo->DataDirectory;
	for (size_t i = 0; i < IMAGE_NUMBEROF_DIRECTORY_ENTRIES; i++)
	{
		printf("IMAGE_DATA_DIRECTORY[%02d]->VirtualAddress  [0x%08x] ->Size [0x%08x] \r\n", i, pDir[i].VirtualAddress, pDir[i].Size);
	}
}

标签:IMAGE,PE,pOpo,HEADER,printf,OPTIONAL,0x%
来源: https://blog.csdn.net/m0_46125480/article/details/120975889

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有