前段时间项目需要实现对 Windows PE 文件版本信息的提取,如文件说明、文件版本、产品名称、版权、原始文件名等信息。获取这些信息在 Windows 下当然有一系列的 API 函数供调用,简单方便。
我们先看一下PE文件结构,PE文件由DOS首部,PE文件头,块表,块和调试信息组成,有关PE文件的数据结构信息在winnt.h中定义。
文章不过多赘述,直接上代码简单明了。
实现代码:
#include "stdafx.h"
#include <Windows.h>extern void DirectoryString(DWORD dwIndex);int _tmain(int argc, _TCHAR* argv[])
{//获取文件句柄HANDLE hFile = CreateFile(_T("D:\\Wmplayer.exe"),GENERIC_READ,0,NULL,OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);//获取文件大小DWORD dwFileSize = GetFileSize(hFile, NULL);CHAR *pFileBuf = new CHAR[dwFileSize];//将文件读取到内存DWORD ReadSize = 0;ReadFile(hFile, pFileBuf, dwFileSize, &ReadSize, NULL);//判断是否为PE文件PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuf;if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE){printf("非 PE 文件\n");system("pause");return 0;}PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)(pFileBuf + pDosHeader->e_lfanew);if (pNtHeader->Signature != IMAGE_NT_SIGNATURE){printf("非 PE 文件\n");system("pause");return 0;}//获取基本PE头信息//获取信息所用到的两个结构体指针 (这两个结构体都属于NT头)PIMAGE_FILE_HEADER pFileHeader = &(pNtHeader->FileHeader);PIMAGE_OPTIONAL_HEADER pOptionalHeader = &(pNtHeader->OptionalHeader);//输出PE头信息printf("================== 基 本 P E 头 信 息 ==================\n\n");printf("入 口 点:\t%08X\t", pOptionalHeader->AddressOfEntryPoint);printf("子 系 统:\t%04X\n", pOptionalHeader->Subsystem);printf("镜像基址:\t%08X\t", pOptionalHeader->ImageBase);printf("区段数目:\t%04X\n", pFileHeader->NumberOfSections);printf("镜像大小:\t%08X\t", pOptionalHeader->SizeOfImage);printf("日期时间标志:\t%08X\n", pFileHeader->TimeDateStamp);printf("代码基址:\t%08X\t", pOptionalHeader->BaseOfCode);printf("部首大小:\t%08X\n", pOptionalHeader->SizeOfHeaders);printf("数据基址:\t%08X\t", pOptionalHeader->BaseOfData);printf("特 征 值:\t%04X\n", pFileHeader->Characteristics);printf("块 对 齐:\t%08X\t", pOptionalHeader->SectionAlignment);printf("校 验 和:\t%08X\n", pOptionalHeader->CheckSum);printf("文件块对齐:\t%08X\t", pOptionalHeader->FileAlignment);printf("可选头部大小:\t%04X\n", pFileHeader->SizeOfOptionalHeader);printf("标 志 字:\t%04X\t\t", pOptionalHeader->Magic);printf("RVA数及大小:\t%08X\n\n", pOptionalHeader->NumberOfRvaAndSizes);printf("======================= 目 录 表 =======================\n");//获取目录表头指针PIMAGE_DATA_DIRECTORY pDataDirectory = pOptionalHeader->DataDirectory;printf("\t\t RAV\t\t 大小\n");for (DWORD i = 0; i < IMAGE_NUMBEROF_DIRECTORY_ENTRIES; i++){DirectoryString(i);printf("%08X\t%08X\n",pDataDirectory[i].VirtualAddress, pDataDirectory[i].Size);}printf("======================= 区 段 表 =======================\n");//获取区段表头指针PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(pNtHeader);printf("名称 VOffset VSize ROffset RSize 标志\n");//获取区段个数DWORD dwSectionNum = pFileHeader->NumberOfSections;//根据区段个数遍历区段信息for (DWORD i = 0; i < dwSectionNum; i++, pSectionHeader++){for (DWORD j = 0; j < IMAGE_SIZEOF_SHORT_NAME; j++){printf("%c", pSectionHeader->Name[j]);}printf(" %08X %08X %08X %08X %08X\n",pSectionHeader->VirtualAddress,pSectionHeader->Misc.VirtualSize,pSectionHeader->PointerToRawData,pSectionHeader->SizeOfRawData,pSectionHeader->Characteristics);}printf("\n");system("start https://www.chwm.vip/?PEinfo");system("pause");return 0;
}void DirectoryString(DWORD dwIndex)
{switch (dwIndex){case 0:printf("输出表:\t\t");break;case 1:printf("输入表:\t\t");break;case 2:printf("资源:\t\t");break;case 3:printf("异常:\t\t");break;case 4:printf("安全:\t\t");break;case 5:printf("重定位:\t\t");break;case 6:printf("调试:\t\t");break;case 7:printf("版权:\t\t");break;case 8:printf("全局指针:\t");break;case 9:printf("TLS表:\t\t");break;case 10:printf("载入配置:\t");break;case 11:printf("输入范围:\t");break;case 12:printf("IAT:\t\t");break;case 13:printf("延迟输入:\t");break;case 14:printf("COM:\t\t");break;case 15:printf("保留:\t\t");break;}
}
获取某指定区段的信息实现代码:
HANDLE hFile = CreateFile(_T("C:\\Windows\\SysNative\\ntoskrnl.exe"), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);if (hFile == INVALID_HANDLE_VALUE) {cout << "CreateFile failed:" << GetLastError() << endl;return false;}DWORD dwFileSize = GetFileSize(hFile, NULL);CHAR* pFileBuf = new CHAR[dwFileSize];DWORD ReadSize = 0;if (!ReadFile(hFile, pFileBuf, dwFileSize, &ReadSize, NULL)) {cout << "ReadFile failed:" << GetLastError() << endl;return false;}PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuf;PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)(pFileBuf + pDosHeader->e_lfanew);PIMAGE_FILE_HEADER pFileHeader = &(pNtHeader->FileHeader);PIMAGE_OPTIONAL_HEADER pOptionalHeader = &(pNtHeader->OptionalHeader);PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(pNtHeader);DWORD dwSectionNum = pFileHeader->NumberOfSections;int page_vaddr = 0, page_roff = 0;for (DWORD i = 0; i < dwSectionNum; i++, pSectionHeader++) {string s_name;for (DWORD j = 0; j < IMAGE_SIZEOF_SHORT_NAME; j++) {if (pSectionHeader->Name[j]) {s_name += pSectionHeader->Name[j];}}if (s_name == "PAGE") {page_vaddr = pSectionHeader->VirtualAddress;page_roff = pSectionHeader->PointerToRawData;break;}}if (page_vaddr == 0 || page_roff == 0) { cout << "没有找到 PAGE 区段" << endl; return false; }
效果演示: