前段时间项目需要实现对 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. 利用SPI实现全自动化——LCD屏与RGB灯

    如果你开启了广告屏蔽,请将博客园加入白名单,帮助博客园渡过难关,谢谢! 前言 在21年做物理实验和23年客串电赛之后,我带着STM32重回电子DIY界.这次的项目是一个电池供电的补光灯,由于用途更偏向 ...

  2. [GXYCTF 2019]BabyUpload

    看到题目是一个文件上传 就先随便传的东西试试,看有什么过滤之类的 上传一个一句话木马,提示后缀名不能为ph 随便上传了带有一句话木马的图片,发现上传成功,但这个图片不能直接利用,要先上传一个.htac ...

  3. Opencv系列之一:简介与基本使用

    1 Opencv简介 Opencv是计算机视觉中经典的专用库,其支持多语言,跨平台,功能强大.Opencv-Python为Opencv提供了Python接口,使得使用者在Python中能够调用C/C+ ...

  4. 详述Java内存屏障,透彻理解volatile

    一般来说内存屏障分为两层:编译器屏障和CPU屏障,前者只在编译期生效,目的是防止编译器生成乱序的内存访问指令:后者通过插入或修改特定的CPU指令,在运行时防止内存访问指令乱序执行. 下面简单说一下这两 ...

  5. MySQL 高级(进阶) SQL 语句——其二

    MySQL 高级(进阶) SQL 语句 视图 ---- CREATE VIEW ----视图,可以被当作是虚拟表或存储查询. 视图跟表格的不同是,表格中有实际储存数据记录,而视图是建立在表格之上的一个 ...

  6. Vue源码学习(十五):diff算法(二)交叉比对(双指针)

    好家伙, 本节来解决我们上一章留下来的问题, 新旧节点同时有儿子的情况本章继续解决 1.要做什么? 本章将解决, 1.在相同tag下子元素的替换问题 2.使用双指针进行元素替换, 实现效果如下:   ...

  7. Petals

    ------------恢复内容开始------------ 打开发现一堆地址冒红 滑倒后面发现E8,根据经验应该是花指令考点 然后D-->nop-->C-->P得到正常结果 然后第 ...

  8. 为React Ant-Design Table增加字段设置

    最近做的几个项目经常遇到这样的需求,要在表格上增加一个自定义表格字段设置的功能.就是用户可以自己控制那些列需要展示. 在几个项目里都实现了一遍,每个项目的需求又都有点儿不一样,迭代了很多版,所以抽时间 ...

  9. (Good topic)卡牌分组(3.27leetcode每日打卡)

    给定一副牌,每张牌上都写着一个整数. 此时,你需要选定一个数字 X,使我们可以将整副牌按下述规则分成 1 组或更多组:  每组都有 X 张牌. 组内所有的牌上都写着相同的整数. 仅当你可选的 X &g ...

  10. Centos离线安装JDK+Tomcat+MySQL8.0+Nginx

    一.安装JDK 注:以下命令环境在Xshell中进行. 1.查询出系统自带的OpenJDK及版本 rpm -qa | grep jdk 2.如果显示已安装openjdk则对其进行卸载. #卸载 rpm ...