PE文件解析器的编写(二)——PE文件头的解析
之前在学习PE文件格式的时候,是通过自己查看各个结构,自己一步步计算各个成员在结构中的偏移,然后在计算出其在文件中的偏移,从而找到各个结构的值,但是在使用C语言编写这个工具的时候,就比这个方便的多,只要将对应的指针类型转化为各个结构类型,就可以使用指针中的箭头来直接寻址到结构中的各个成员。
这次主要说明的是PE文件头的解析,也就是之前看到的第一个界面中显示的内容,这个部分涉及到CPeFileInfo这个解析类的部分代码,以及CPeFileInfoDlg这个对话框类的代码。
选择目标文件
首先通过点击open按钮来弹出一个对话框,让用户选择需要解析的文件。
这个部分的代码如下:
void CPEInfoDlg::OnBnClickedBtnOpen()
{
// TODO: 在此添加控件通知处理程序代码
TCHAR szFilePath[MAX_PATH] = _T("");
OPENFILENAME ofn = {0};
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = m_hWnd;
ofn.hInstance = GetModuleHandle(NULL);
ofn.nMaxFile = MAX_PATH;
ofn.lpstrInitialDir = _T(".");
ofn.lpstrFile = szFilePath;
ofn.lpstrTitle = _T("Open ...[PEInfo] by liuhao");
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
ofn.lpstrFilter = _T("*.*\0*.*\0");
GetOpenFileName(&ofn);
m_PeFileInfo.strFilePath = szFilePath;
GetDlgItem(IDC_FILE_PATH)->SetWindowText(szFilePath);
m_PeFileInfo.UnLoadFile();
BOOL bLoadSuccess = m_PeFileInfo.LoadFile();
if (bLoadSuccess)
{
//成功打开
if (m_PeFileInfo.IsPeFile())
{
ShowFileHeaderInfo();
ShowOptionHeaderInfo();
GetDlgItem(IDC_BTN_CALC)->EnableWindow(TRUE);
GetDlgItem(IDC_BTN_DATA_DIR_INFO)->EnableWindow(TRUE);
GetDlgItem(IDC_BTN_SECTION_INFO)->EnableWindow(TRUE);
GetDlgItem(IDC_BTN_CHAR_INFO)->EnableWindow(TRUE);
GetDlgItem(IDC_RVA)->EnableWindow(TRUE);
}else
{
MessageBox(_T("打开的文件不是PE文件"));
InitCommandCtrl();
}
}
}
在这段代码中首先通过GetOpenFileName函数来弹出一个选择文件的对话框。这个函数需要传入一个OPENFILENAME 结构的指针变量,这个结构比较复杂,在这说下它比较重要的几个成员:
lStructSize //结构的大小
hwndOwner //这个对话框所属的父窗口句柄
hInstance //所在模块的句柄
lpstrFile //用来保存用户选择文件的路径的缓冲
nMaxFile //缓冲区的大小
lpstrTitle //这个对话框的标题
Flags//对话框的标识,具体标识请查看MSDN,一般我们用这样几个就足够了
一般只需要更改标题,内存缓冲区指针和它的大小,其余按照上面的代码默认就好
用户选择后,将用户选择的文件的全路径显示出来,并调用CPefileInfo类中的加载函数进行加载,如果之前加载过,那么先卸载它。然后再在对话框中显示它主要的信息,并且将所有按钮设置为可用状态,
加载与卸载PE文件结构
在这个里面主要有这样几个函数
m_PeFileInfo.UnLoadFile();
m_PeFileInfo.LoadFile();
m_PeFileInfo.IsPeFile();
ShowFileHeaderInfo();
ShowOptionHeaderInfo();
接下来转到CPeFileInfo这个类中
在这个类中定义了这样四个主要的成员
CString strFilePath; //对应PE文件所在路径
HANDLE hFile; //打开这个文件时的文件句柄
PVOID pImageBase; //文件在内存中的首地址
HANDLE hMapping; //文件映射的句柄
BOOL CPeFileInfo::LoadFile()
{
if(strFilePath.IsEmpty())
{
return FALSE;
}
hFile = CreateFile(strFilePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (INVALID_HANDLE_VALUE == hFile)
{
return FALSE;
}
hMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
if (NULL == hMapping)
{
CloseHandle(hFile);
hFile = NULL;
return FALSE;
}
pImageBase = MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0);
if (NULL == pImageBase)
{
CloseHandle(hMapping);
CloseHandle(hFile);
hMapping = NULL;
hFile = NULL;
return FALSE;
}
return TRUE;
}
void CPeFileInfo::UnLoadFile()
{
if(pImageBase != NULL)
{
UnmapViewOfFile(pImageBase);
}
if(NULL != hMapping)
{
CloseHandle(hMapping);
}
if (NULL != hFile)
{
CloseHandle(hFile);
}
pImageBase = NULL;
hFile = NULL;
hMapping = NULL;
}
BOOL CPeFileInfo::IsPeFile()
{
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNtHeader = NULL;
if (NULL == pImageBase)
{
return FALSE;
}
pDosHeader = (PIMAGE_DOS_HEADER)pImageBase;
if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
{
return FALSE;
}
pNtHeader = (PIMAGE_NT_HEADERS)((DWORD)(pDosHeader->e_lfanew) + (DWORD)pImageBase);
if (pNtHeader->Signature != IMAGE_NT_SIGNATURE)
{
return FALSE;
}
return TRUE;
}
在加载的时候,主要通过一个文件映射的方式,将pe文件的整个内容原模原样的拷贝到内存中,并保存这个文件句柄,文件映射句柄,文件所在内存的首地址等信息,在卸载的时候进行关闭句柄,清理资源的操作。
在程序中有一个判断该文件是否是PE文件的操作。在PE的DOS头结构中的e_magic结构保存的是’MZ’这个标志对应的16进制数是0x4d5a,另外在pe头中有一个Sinature成员保存了0x50450000这个值,它对应的字符是‘PE’只有满足这两个条件的文件才是一个正常的PE文件。否则就不是。
获取DOS头和PE头
在之前我们说过,PE文件开始的位置是一个DOS头结构——IMAGE_DOS_HEADER STRUCT,它的第一个成员作为DOS头的标识,一般保存的都是‘MZ’,而它里面的e_lfanew则保存真正的PE头所在的偏移
所在获取DOS头的时候简单的将前面的几个字节转化为这个结构即可,在寻址PE头的时候用e_lfanew成员加上文件的起始地址就可以得到PE头的地址。具体对应的代码如下:
pDosHeader = (PIMAGE_DOS_HEADER)pImageBase;
pNtHeader = (PIMAGE_NT_HEADERS)((DWORD)(pDosHeader->e_lfanew) + (DWORD)pImageBase);
显示FileHeader信息和ptionalHeader信息
在PE头的结构体定义如下:
IMAGE_NT_HEADERS STRUCT
{
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS ENDS
这个里面的第二个第三个成员就分别是FileHeader信息和ptionalHeader信息,剩下的就只是对这个结构的部分重要成员进行解析和显示了
void CPEInfoDlg::ShowFileHeaderInfo()
{
PIMAGE_FILE_HEADER pFileHeader = m_PeFileInfo.GetFileHeader();
if (NULL != pFileHeader)
{
//省略部分不重要的代码
//时间戳转为具体时间
tm p;
errno_t err1;
err1 = gmtime_s(&p,(time_t*)&pFileHeader->TimeDateStamp);
TCHAR s[100] = {0};
_tcsftime (s, sizeof(s) / sizeof(TCHAR), _T("%Y-%m-%d %H:%M:%S"), &p);
GetDlgItem(IDC_TIME_STAMP)->SetWindowText(s);
}
else
{
MessageBox(_T("显示数据错误"));
}
}
在这函数中我们直接通过指针寻址的方式来获取其中的信息,比如NumberOfSections(当前文件中的节表数量)、TimeDateStamp(时间戳信息)、PointerToSymbolTable(符号表所在地址相对于文件的偏移)、NumberOfSymbols(符号表的数目)、SizeOfOptionalHeader(OptionalHeader结构的大小)、Characteristics(文件的属性值)。
在显示属性值时,另外提供了一个转化函数将这个值转化为具体的标识。需要进行转化的请参考下面的代码
void CPeFileInfo::GetFileCharacteristics(CString &strCharacter)
{
DWORD dwMachine = 0;
PIMAGE_FILE_HEADER pFileHeader = GetFileHeader();
if (0 != (pFileHeader->Characteristics & IMAGE_FILE_RELOCS_STRIPPED))
{
strCharacter += _T("文件中不存在重定位信息\r\n");
}
if(0 != (pFileHeader->Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE))
{
strCharacter += _T("文件可执行\r\n");
}
if (0 != (pFileHeader->Characteristics & IMAGE_FILE_LARGE_ADDRESS_AWARE))
{
strCharacter += _T("可以处理大于2GB内容\r\n");
}
if(0 != (pFileHeader->Characteristics & IMAGE_FILE_32BIT_MACHINE))
{
strCharacter += _T("目标平台为32位机器\r\n");
}
if (0 != (pFileHeader->Characteristics & IMAGE_FILE_SYSTEM))
{
strCharacter += _T("该文件是系统文件\r\n");
}
if (0 != (pFileHeader->Characteristics & IMAGE_FILE_DLL))
{
strCharacter += _T("该文件是dll文件\r\n");
}
if(0 != (pFileHeader->Characteristics & IMAGE_FILE_UP_SYSTEM_ONLY))
{
strCharacter += _T("该程序只能运行在单核处理器上");
}
}
对于OptionalHeader结构的解析,目前也只是简单的对它其中的数据结构进行打印,它里面最重要的结构DataDirectory留着在后面的部分进行说明。
PE文件解析器的编写(二)——PE文件头的解析的更多相关文章
- PE解析器的编写(一)——总体说明
之前自己学习了PE文件的格式,后来自己写了个PE文件的解析器,这段时间工作上刚好要用到它,老板需要能查看某个exe中加载的dll的一个工具,我在使用之前自己写的这个东西的时候,发现很多东西都忘记了,所 ...
- PE解析器的编写(四)——数据目录表的解析
在PE结构中最重要的就是区块表和数据目录表,上节已经说明了如何解析区块表,下面就是数据目录表,在数据目录表中一般只关心导入表,导出表和资源这几个部分,但是资源实在是太复杂了,而且在一般的病毒木马中也不 ...
- PE文件头格式解析
前言: 昨天写了一题de1ctf的题,发现要脱壳,手脱之后发现要iat修复,我就发现自己在这块知识缺失了,win逆向,好像一直都是打ctf,然后用逆向方法论去肝的 其他方面倒是没有很深入学习,但实际上 ...
- Mybatis源码学习之parsing包(解析器)(二)
简述 大家都知道mybatis中,无论是配置文件mybatis-config.xml,还是SQL语句,都是写在XML文件中的,那么mybatis是如何解析这些XML文件呢?这就是本文将要学习的就是,m ...
- PE解析器的编写(三)——区块表的解析
PE文件中所有节的属性都被定义在节表中,节表由一系列的IMAGE_SECTION_HEADER结构排列而成,每个结构用来描述一个节,结构的排列顺序和它们描述的节在文件中的排列顺序是一致的. 具有相同属 ...
- PE文件学习系列一为什么是PE
合肥程序员群:49313181. 合肥实名程序员群:128131462 (不愿透露姓名和信息者勿加入)Q Q:408365330 E-Mail:egojit@qq.com PE概述: ...
- Scala词法文法解析器 (二)分析C++类的声明
最近一直在学习Scala语言,偶然发现其Parser模块功能强大,乃为BNF而设计.啥是BNF,读大学的时候在课本上见过,那时候只觉得这个东西太深奥.没想到所有的计算机语言都是基于BNF而定义的一套规 ...
- springMVC源码解析--ViewResolverComposite视图解析器集合(二)
上一篇博客springMVC源码分析--ViewResolver视图解析器(一)中我们介绍了一些springMVC提供的很多视图解析器ViewResolver,在开发的一套springMVC系统中是可 ...
- springMVC源码分析--HandlerMethodReturnValueHandlerComposite返回值解析器集合(二)
在上一篇博客springMVC源码分析--HandlerMethodReturnValueHandler返回值解析器(一)我们介绍了返回值解析器HandlerMethodReturnValueHand ...
随机推荐
- Win32界面 主函数分析
WinMain即(函数运行入口): p { margin-bottom: 0.25cm; line-height: 120% } int WINAPI WinMain (HINSTANCE hinst ...
- linux OSI七层模型、TCP/IP协议栈及每层结构大揭秘
学习Linux,就算是像小编我这样的小萌新,也知道OSI模型.什么?!你不知道!!! 好吧,这篇秘籍拿走,不谢~~~ 一.两个协议 (1)OSI 协议模型(7层)国际协议 PDU:协议数据单元对 ...
- 移动端下拉刷新上拉加载-mescroll.js插件
最近无意间看到有这么一个上拉刷新下拉加载的插件 -- mescroll.js,个人感觉挺好用的,官网地址是:http://www.mescroll.com 然后我就看了一下文档,简单的写了一个小dem ...
- Ignite与Spark集成时,ClassNotFoundException问题解决
参考文章:https://apacheignite-fs.readme.io/docs/installation-deployment Spark application deployment mod ...
- Bootstrap学习笔记(二)---常见工具和流程导航范例
使用bootstrap框架避免不了写CSS,当CSS文件较大时,会发现维护起来很麻烦,一些默认值,如行高.背景色.标注颜色.字号等信息往往反复出现,还有一些大体上一致,只有小部分不同的样式定义,这就需 ...
- 【bird-front】前端框架介绍
bird前端项目,基于react.antd.antd-admin,封装常用数据组件,细粒度权限解决方案. bird-front是基于react的后台管理系统前端项目,框架构建部分严重借鉴于antd管理 ...
- java显示目录文件列表和删除目录
*/ .hljs { display: block; overflow-x: auto; padding: 0.5em; color: #333; background: #f8f8f8; } .hl ...
- 【1】ArcGIS API for JavaScript 4.5/4.6 本地部署
惭愧,和我的学弟比起来,我所开始接触前端开发,ArcGIS API for JavaScript的时间和深度远远不及于他. 一年之尾,亦是一年之始,我也将正式开始我的博客生涯.本人在校学习并且做项目, ...
- ArcGIS API for JavaScript 4.2学习笔记[17] 官方第七章Searching(空间查询)概览与解释
空间分析和空间查询是WebGIS有别于其他Web平台的特点.到这一章,就开始步入空间分析的内容了. [Search widget] 介绍空间查询的核心小部件"Search". [S ...
- 程序员的自我救赎---10.1:APP版本控制系统
<前言> (一) Winner2.0 框架基础分析 (二)PLSQL报表系统 (三)SSO单点登录 (四) 短信中心与消息中心 (五)钱包系统 (六)GPU支付中心 (七)权限系统 (八) ...