前段时间项目需要实现对 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; }

获取 PE 文件的版本信息:

#include "stdafx.h"
#include <string>
#include <iostream>
#include "attribute.h" #pragma comment(lib, "version") using namespace std; bool QueryValue(const std::string& ValueName, const std::string& szModuleName, std::string& RetStr)
{
bool bSuccess = FALSE;
BYTE* m_lpVersionData = NULL;
DWORD m_dwLangCharset = 0;
CHAR *tmpstr = NULL; do
{
if (!ValueName.size() || !szModuleName.size())
break; DWORD dwHandle;
// 判断系统能否检索到指定文件的版本信息
DWORD dwDataSize = ::GetFileVersionInfoSizeA((LPCSTR)szModuleName.c_str(), &dwHandle);
if (dwDataSize == 0)
break; m_lpVersionData = new (std::nothrow) BYTE[dwDataSize];// 分配缓冲区
if (NULL == m_lpVersionData)
break; // 检索信息
if (!::GetFileVersionInfoA((LPCSTR)szModuleName.c_str(), dwHandle, dwDataSize, (void*)m_lpVersionData))
break; UINT nQuerySize;
DWORD* pTransTable;
// 设置语言
if (!::VerQueryValueA(m_lpVersionData, "\\VarFileInfo\\Translation", (void **)&pTransTable, &nQuerySize))
break; m_dwLangCharset = MAKELONG(HIWORD(pTransTable[0]), LOWORD(pTransTable[0]));
if (m_lpVersionData == NULL)
break; tmpstr = new (std::nothrow) CHAR[128];
if (NULL == tmpstr)
break;
sprintf_s(tmpstr, 128, "\\StringFileInfo\\%08lx\\%s", m_dwLangCharset, ValueName.c_str());
LPVOID lpData; // 调用此函数查询前需要先依次调用函数GetFileVersionInfoSize和GetFileVersionInfo
if (::VerQueryValueA((void *)m_lpVersionData, tmpstr, &lpData, &nQuerySize))
RetStr = (char*)lpData; bSuccess = TRUE;
} while (FALSE); // 销毁缓冲区
if (m_lpVersionData)
{
delete[] m_lpVersionData;
m_lpVersionData = NULL;
}
if (tmpstr)
{
delete[] tmpstr;
tmpstr = NULL;
} return bSuccess;
} bool BaseFlow::Attribute::GetInternalName(const std::string& szModuleName, std::string& RetStr)
{
// return QueryValue("InternalName", szModuleName, RetStr); //获取内部名称
// return QueryValue("CompanyName", szModuleName, RetStr); //获取公司名称
// return QueryValue("LegalCopyright", szModuleName, RetStr); //获取版权
// return QueryValue("OriginalFilename", szModuleName, RetStr); //获取原始文件名
// return QueryValue("ProductName", szModuleName, RetStr); //获取产品名称
return QueryValue("ProductVersion", szModuleName, RetStr); //获取产品版本
};

效果演示:

提取 PE文件 / 目标程序 的各种信息的更多相关文章

  1. 用python提取txt文件中的特定信息并写入Excel

    这个是用 excel里面的 去掉空格最后导出的一个list: 原本是有空格的 后面是抵消了中间的空格. 然后 这里侧重说一下什么是split()函数 语法:str.split(str="&q ...

  2. PE文件结构部分解析以及输入的定位

    原文链接地址:http://www.cnblogs.com/shadow-lei/p/3554670.html PE文件定义 PE 文件("Portable executable" ...

  3. 【学习】Windows PE文件学习(一:导出表)

    今天做了一个读取PE文件导出表的小程序,用来学习. 参考了<Windows PE权威指南>一书. 首先, PE文件的全称是Portable Executable,可移植的可执行的文件,常见 ...

  4. C语言读取PE文件信息(一)

    接下来的内容来源于对该博客文章http://www.pediy.com/kssd/pediy06/pediy7006.htm的解析. 一.打印Sections信息.下面的程序打印出Windows_Gr ...

  5. C/C++ 实现PE文件特征码识别

    PE文件就是我们常说的EXE可执行文件,针对文件特征的识别可以清晰的知道该程序是使用何种编程语言实现的,前提是要有特征库,PE特征识别有多种形式,第一种是静态识别,此方法就是只针对磁盘中文件的特征码字 ...

  6. 破解软件系列-PE文件深入浅出之Section Table节表

    我们已经学了许多关于 DOS header 和 PE header 的知识.接下来就该轮到 section table(节表)了.节表其实就是紧挨着 PE header 的一结构数组.该数组成员的数目 ...

  7. 20145314郑凯杰《网络对抗技术》PE文件病毒捆绑(插入捆绑)的实现

    20145314郑凯杰<网络对抗技术>PE文件病毒捆绑(插入捆绑)的实现 一.本节摘要 简介:每个应用程序内部都有一定的空间(因为文件对齐余留的00字段)可以被利用,这样就可以保证被插入的 ...

  8. 浅析MSIL中间语言——PE文件结构篇

    一.开篇 开篇我想讲一下于本文无关的话题,其实我很想美化一下自己博客园一直没时间弄,无意间找了博客园李宝亨的博客园里面有一篇分享自己主题的文章,我就将这个模板暂时用作我的blog主题,我要讲述一个关于 ...

  9. PE文件学习系列笔记四-C++实现PE文件的分析

    合肥程序员群:49313181.    合肥实名程序员群:128131462 (不愿透露姓名和信息者勿加入) Q  Q:408365330     E-Mail:egojit@qq.com 综述: 首 ...

  10. PE文件学习系列三-PE头详解

    合肥程序员群:49313181.    合肥实名程序员群:128131462 (不愿透露姓名和信息者勿加入) Q  Q:408365330     E-Mail:egojit@qq.com 最近比较忙 ...

随机推荐

  1. 《流畅的Python》 读书笔记 230926(第一章后半部分)

    1.2 如何使用特殊方法 特殊方法的存在是为了被 Python 解释器调用的,你自己并不需要调用它们 就是说通常你都应该用len(obj)而不是obj.__len()__,无论是系统预置的,还是你自己 ...

  2. 本计划在 .NET 8 中推出的 WASI 推迟到 .NET 9

    本计划在 .NET 8 中推出的 WASI  已推迟到 .NET 9,请参阅 Github 上的 WASI 跟踪问题. 在.NET 8 Preview 4 开始支持生成与 WASI 兼容的 .wasm ...

  3. Q-REG论文阅读

    Q-REG Jin, S., Barath, D., Pollefeys, M., & Armeni, I. (2023). Q-REG: End-to-End Trainable Point ...

  4. ELK日志企业案例:(5.3版本)

    1.shell三剑客同居.分析nginx日志: 1)在企业生产环境中,日志内容主要用来做什么? 日志内容主要用于运维人员.开发人员.DBA排错软件服务故障的,因为通过日志内容能够第一时间找到软件服务的 ...

  5. CSP2023 模拟赛总结合集

    9.9 ZZFLS 感觉 ucup 剩下的题完全不可做了啊!先对比赛时间来写总结对队友道歉(鞠躬.jpg 开题策略很失败.开场 30min 得的分数是一整场考试的分数. 开题,发现 T1 是水题,30 ...

  6. 打造美团外卖新体验,HarmonyOS SDK持续赋能开发者共赢鸿蒙生态

    从今年8月起,所有升级到HarmonyOS 4的手机用户在美团外卖下单后,可通过屏幕上的一个"小窗口",随时追踪到"出餐.取餐.送达"等订单状态.这个能让用户实 ...

  7. C#判断字符串的显示宽度

    C#判断字符串的显示宽度 起因: 公司有一个使用项目使用HTML转换为PDF,其中有一个表格,表格的最后一列中的单元格,其字符串超长后会被丢弃,而不是换行到下一行展示(HtmlToPdf渲染引擎导致的 ...

  8. 理解Go中的零值

    在 Go 语言中,零值(Zero Value)是指在声明变量但没有显式赋值的情况下,变量会被自动赋予一个默认值.这个默认值取决于变量的类型,不同类型的变量会有不同的零值.零值是 Go 语言中的一个重要 ...

  9. Reflect API:每个 JavaScript 开发人员都需要的瑞士军刀

    前言 您是否曾经希望拥有一个神奇的工具包,可以让您像超级英雄一样控制 JavaScript 对象?向ReflectAPI 打个招呼吧,它是 ES6 中引入的一个新的全局对象 ,它能够处理简单的代码操作 ...

  10. 手撕Vue-Router-添加全局$router属性

    前言 经过上一篇文章的介绍,完成了初始化路由相关信息的内容,接下来我们需要将路由信息挂载到Vue实例上,这样我们就可以在Vue实例中使用路由信息了. 简而言之就是给每一个Vue实例添加一个$route ...