我是靠谱客的博主 幸福哈密瓜,这篇文章主要介绍C++PE文件格式解析类(轻松制作自己的PE文件解析器),现在分享给大家,希望可以做个参考。

PE是Portable Executable File Format(可移植的运行体)简写,它是眼下Windows平台上的主流可运行文件格式。

PE文件里包括的内容非常多,详细我就不在这解释了,有兴趣的能够參看之后列出的參考资料及其它相关内容。

近期我也在学习PE文件格式,參考了很多资料。用C++封装了一个高效方便的PE文件格式解析的类。

该类对想学PE文件结构的朋友可算一份可贵的资料。代码均非常易懂,考虑较全面,具有一定的通用性。

同一时候该类也能够让想创建自己的PE文件解析软件的朋能够轻松在此基础上实现。

最后。错误在所难免。假设大家发现有错误,欢迎大家指正。



下面是该类中接口函数的定义代码(完整代码附于之后下载链接中):


复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
<pre name="code" class="cpp">class CPeFile { public: CPeFile(); ~CPeFile(); // 下面函数无特殊说明不论什么时候均可用 public: //将PE文件附加到对象,成功返回IMAGE_DOS_SIGNATURE、IMAGE_OS2_SIGNATURE、IMAGE_OS2_SIGNATURE_LE、IMAGE_NT_SIGNATURE之中的一个;失败返回0UL(未知类型),1UL(文件操作失败),2(其它错误),【仅在没有PE文件附加到类对象时可用】 DWORD Attach(LPCTSTR lpszFilePath); //若已有PE文件附加到类对象则释放关联 void Detach(); //获取Attach信息,成功返回IMAGE_DOS_SIGNATURE、IMAGE_OS2_SIGNATURE、IMAGE_OS2_SIGNATURE_LE、IMAGE_NT_SIGNATURE之中的一个;未成功返回0UL DWORD GetAttachInfo() const; // 下面函数无特殊说明Attach成功后均可用 public: //获取文件句柄(注:不应在外部运行CloseHandle等有副作用的操作) HANDLE GetFileHandle() const; //获取内存映射文件头部地址 DWORD_PTR GetMappedFileStart() const; //获取内存映射文件头部指定偏移位置(不应大于文件大小。否则返回的指针可能引起错误) DWORD_PTR GetMappedFileOffset(DWORD dwFoa) const; //获取DOS头 const IMAGE_DOS_HEADER* GetDosHeader() const; //获取DOS的入口地址 DWORD GetDosEntryPoint() const; // 下面函数无特殊说明仅用于IMAGE_NT_SIGNATURE public: //获取PE文件头(假设是64位程序,返回的实际是const IMAGE_NT_HEADER64*)【类型为IMAGE_OS2_SIGNATURE、IMAGE_OS2_SIGNATURE_LE时仍可用,但操作需小心】 const IMAGE_NT_HEADERS32* GetNtHeader() const; //返回文件是否为64位(PE32+) BOOL Is64Bit() const; //获取PE文件头中的载入基地址ImageBase(64位程序返回的是ULONGLONG类型。32位程序可将返回值转换成DWORD类型) ULONGLONG GetImageBase() const; //获取PE文件头中的DataDirectory const IMAGE_DATA_DIRECTORY* GetDataDirectory() const; //获取各DataDirectory入口的RVA DWORD GetDataDirectoryEntryRva(DWORD dwIndex) const; //获取节表(节表的数量可由lpSectionNum传出) const IMAGE_SECTION_HEADER* GetSectionHeader(LPWORD lpSectionNum = NULL) const; //将RVA转换为FOA(可由lpFoa传出FOA、可由lpSection传出节序号。不在节中节序号为-1) BOOL RvaToFoa(DWORD dwRva, LPDWORD lpFoa = NULL, LPWORD lpSection = NULL) const; //将FOA转换为RVA(可由lpRva传出RVA、可由lpSection传出节序号。不在节中节序号为-1) BOOL FoaToRva(DWORD dwFoa, LPDWORD lpRva = NULL, LPWORD lpSection = NULL) const; //将VA转换为RVA(VA应大于ImageBase,函数中不检查) DWORD VaToRva(DWORD dwVa) const; DWORD VaToRva(ULONGLONG ullVa) const; //将RVA转换为VA(64位程序返回的是ULONGLONG类型,32位程序可将返回值转换成DWORD类型) ULONGLONG RvaToVa(DWORD dwRva) const; // 下面函数无特殊说明仅用于IMAGE_NT_SIGNATURE public: //读取PE中导出表 BOOL ReadExport(); //读取PE中导入表 BOOL ReadImport(); //读取PE中资源表 BOOL ReadResource(); //读取PE中异常表 BOOL ReadException(); //读取PE中属性证书表 BOOL ReadSecurity(); //读取PE中基址重定位表 BOOL ReadBaseRelocation(); //读取PE中调试数据 BOOL ReadDebug(); //读取PE中线程局部存储表 BOOL ReadTLS(); //读取PE中载入配置表 BOOL ReadLoadConfig(); //读取PE中绑定导入表 BOOL ReadBoundImport(); //读取PE中延迟载入导入表 BOOL ReadDelayImport(); //清理PE中导出表 void ClearExport(); //清理PE中导入表 void ClearImport(); //清理PE中资源表 void ClearResource(); //清理PE中异常表 void ClearException(); //清理PE中属性证书表 void ClearSecurity(); //清理PE中基址重定位表 void ClearBaseRelocation(); //清理PE中调试数据 void ClearDebug(); //清理PE中线程局部存储表 void ClearTLS(); //清理PE中载入配置表 void ClearLoadConfig(); //清理PE中绑定导入表 void ClearBoundImport(); //清理PE中延迟载入导入表 void ClearDelayImport(); //清理全部 void ClearAll(); //返回是否读取了PE中导出表 BOOL IsReadExport() const; //返回是否读取了PE中导入表 BOOL IsReadImport() const; //返回是否读取了PE中资源表 BOOL IsReadResource() const; //返回是否读取了PE中异常表 BOOL IsReadException() const; //返回是否读取了PE中属性证书表 BOOL IsReadSecurity() const; //返回是否读取了PE中基址重定位表 BOOL IsReadBaseRelocation() const; //返回是否读取了PE中调试数据 BOOL IsReadDebug() const; //返回是否读取了PE中线程局部存储表 BOOL IsReadTLS() const; //返回是否读取了PE中载入配置表 BOOL IsReadLoadConfig() const; //返回是否读取了PE中绑定导入表 BOOL IsReadBoundImport() const; //返回是否读取了PE中延迟载入导入表 BOOL IsReadDelayImport() const; // 下面函数无特殊说明仅用于IMAGE_NT_SIGNATURE且ReadExport成功 public: //获取导出表 const IMAGE_EXPORT_DIRECTORY* GetExportDirectory() const; //获取导出表中各导出函数地址数组(数量可由lpFuncNum传出) const DWORD* GetExportFunction(LPDWORD lpFuncNum = NULL) const; //获取导出表中被定义了名称的各导出函数名称地址数组(数量可由lpNameNum传出) const DWORD* GetExportName(LPDWORD lpNameNum = NULL) const; //获取导出表中被定义了名称的各导出函数的索引(数量可由lpNameNum传出) const WORD* GetExportNameOrdinal(LPDWORD lpNameNum = NULL) const; //解析导出函数地址数组中dwIndex项,返回值小于NumberOfNames为按名称导出(数值为序号),返回值等于NumberOfNames则为按序号导出 DWORD ParseExportFunction(DWORD dwIndex) const; // 下面函数无特殊说明仅用于IMAGE_NT_SIGNATURE且ReadImport成功 public: //获取各导入表(导入表数量可由lpImportDescriptorNum传出) const IMAGE_IMPORT_DESCRIPTOR* GetImportDescriptor(LPDWORD lpImportDescriptorNum = NULL) const; //获取第iImpoert个导入表中的IMAGE_THUNK_DATA32结构(64位程序实际是IMAGE_THUNK_DATA64)(数量可由lpCount传出) const IMAGE_THUNK_DATA32* GetImportThunkData(DWORD iImport, LPDWORD lpCount = NULL) const; //解析某个IMAGE_THUNK_DATA32结构(64位程序实际是IMAGE_THUNK_DATA64),返回结果:1表示按序号导入(lpParam可传出序号);2表示按名称导入(lpParam可传出相应IMAGE_IMPORT_BY_NAME的FOA);0失败【仅仅须要IMAGE_NT_SIGNATURE就可以用】 int ParseThunkData(const IMAGE_THUNK_DATA32* lpThunk, LPDWORD lpParam = NULL) const; // 下面函数无特殊说明仅用于IMAGE_NT_SIGNATURE且ReadResource成功 public: //获取第一层资源的ID,返回1表示第一层是文件夹,返回2表示第一层是数据。返回0表示无资源 int GetFirstResourceId(PIDTYPE lpFirstID) const; //获取下一层资源的ID,返回1表示下一层是文件夹,返回2表示下一层是数据。返回0表示无下一层 int GetNextResourceId(IDTYPE Id, DWORD iRes, PIDTYPE NextID) const; //解析Id相应的文件夹层,lpEntryNum可传出数组数量。lpLevel可传出第几级文件夹,lpResourceEntry传出本层相应的IMAGE_RESOURCE_DIRECTORY_ENTRY数组 const IMAGE_RESOURCE_DIRECTORY* ParseResourceDirectory(IDTYPE Id, LPDWORD lpEntryNum = NULL, LPDWORD lpLevel = NULL, IMAGE_RESOURCE_DIRECTORY_ENTRY** lpResourceEntry = NULL) const; //解析dwId相应的数据层 const IMAGE_RESOURCE_DATA_ENTRY* ParseResourceData(IDTYPE Id) const; //解析某个IMAGE_RESOURCE_DIRECTORY_ENTRY结构中Name成员,返回结果:1(dwParam为ID);2(dwParam为相应IMAGE_RESOURCE_DIR_STRING_U的FOA)【仅仅须要IMAGE_NT_SIGNATURE就可以用】 int ParseResourceDirectoryEntry(const IMAGE_RESOURCE_DIRECTORY_ENTRY* lpEntry, LPDWORD dwParam) const; // 下面函数无特殊说明仅用于IMAGE_NT_SIGNATURE且ReadException成功 public: //获取异常表(数量可由lpRuntimeFunctionNum传出) const IMAGE_RUNTIME_FUNCTION_ENTRY* GetRuntimeFunction(LPDWORD lpRuntimeFunctionNum = NULL) const; // 下面函数无特殊说明仅用于IMAGE_NT_SIGNATURE且ReadSecurity成功 public: //获取属性证书表(数量可由lpCertificateNum传出) const WIN_CERTIFICATE* const* GetCertificate(LPDWORD lpCertificateNum = NULL) const; // 下面函数无特殊说明仅用于IMAGE_NT_SIGNATURE且ReadBaseRelocation成功 public: //获取各基址重定位表(数量可由lpBaseRelocationNum传出) const IMAGE_BASE_RELOCATION* const* GetBaseRelocation(LPDWORD lpBaseRelocationNum = NULL) const; //获得某个基址重定位表中的重定位块(数量可由lpCount传出,包含对齐用的) const WORD* GetBaseRelocationBlock(const IMAGE_BASE_RELOCATION* lpBaseRelocation, LPDWORD lpCount = NULL) const; //解析某个基址重定位表后的某一项,返回的是高4位的值,低12位的值可由lpParam传出【不论什么时候均后可用】 static WORD ParseBaseRelocationBlock(WORD wBaseRelocationBlock, LPWORD lpParam = NULL); // 下面函数无特殊说明仅用于IMAGE_NT_SIGNATURE且ReadDebug成功 public: //获取调试数据(数量可由lpDebugDirectoryNum传出) const IMAGE_DEBUG_DIRECTORY* GetDebugDirectory(LPDWORD lpDebugDirectoryNum = NULL) const; //获取第dwIndex项调试信息起始地址,未获取到返回NULL LPCVOID GetDebugInfoStart(DWORD dwIndex); // 下面函数无特殊说明仅用于IMAGE_NT_SIGNATURE且ReadTLS成功 public: //获取线程局部存储表(假设是64位程序,返回的实际是const IMAGE_TLS_DIRECTORY64*) const IMAGE_TLS_DIRECTORY32* GetTLSDirectory() const; //获取线程局部存储表回调函数数组的指针(假设是64位程序,返回的实际是const ULONGLONG*)(数量可由lpCallbackNum传出) const DWORD* GetTLSCallback(LPDWORD lpCallbackNum = NULL) const; // 下面函数无特殊说明仅用于IMAGE_NT_SIGNATURE且ReadLoadConfig成功 public: //获取载入配置表(假设是64位程序,返回的实际是const IMAGE_LOAD_CONFIG_DIRECTORY64*) const IMAGE_LOAD_CONFIG_DIRECTORY32* GetLoadConfigDirectory() const; // 下面函数无特殊说明仅用于IMAGE_NT_SIGNATURE且ReadBoundImport成功 public: //获取绑定导入表(数量可由lpBoundImportNum传出) const IMAGE_BOUND_IMPORT_DESCRIPTOR* const* GetBoundImportDescriptor(LPDWORD lpBoundImportNum = NULL) const; //获取第iBoundImpoert个绑定导入表(数量可由lpRefNum传出) const IMAGE_BOUND_FORWARDER_REF* GetBoundImportForwarderRef(DWORD iBoundImport, LPDWORD lpRefNum = NULL) const; // 下面函数无特殊说明仅用于IMAGE_NT_SIGNATURE且ReadDelayImport成功 public: //获取延迟载入导入表(数量可由lpDelayImportNum传出) const IMAGE_DELAYLOAD_DESCRIPTOR* GetDelayImportDescriptor(LPDWORD lpDelayImportNum = NULL) const; /*--------------------------------------------------------------------------------------------------------------------*/ //其它私有成员略 };


复制代码
1



凝视部分已经具体说了各功能。主要操作例如以下:

1、调用Attach成员函数使类对象附加到一个PE文件

2、通过ReadXXX 读取须要的文件夹(包含导入表、导出表、资源表、基址重定位....)

3、调用相关处理函数获取对应信息(依据读取内容的不同而不同,详细參看凝视)

4、将获得的数据做你想要的操作(如显示出来等)

5、ClearXXX释放资源(可选,对象析构时会自己主动调用)

6、Detach释放对该文件的关联(可选。对象析构时会自己主动调用)


眼下基本数据文件夹中的大部分都能够获取。.Net部分(IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR)还未实现,之后会继续完好

之后的链接中附有一个Demo演示怎样使用该类,如仍有不清楚的朋友能够回复寻问我



最后发一个自己写的获取PE文件信息的软件,能够Dump出PE文件的各信息,该软件是用该PE解析类实现的,详细代码较多,初看并不易懂,所以在这不提供了国。

该软件我会附载之后的下载链接里。能够方便获取PE文件里各信息,因为精力有限,写的是控制台下的。有兴趣的朋友能够自己实现一个GUI版本号的~

软件中,仅仅需在控制台下输入PE文件路径。或者将文件拖拽进窗体(插入.lnk快捷方式也行)。程序会将须要的信息输出来。

下面附上软件的一部分截图:











哈。啰嗦了这么多,接下来就留给各位去体验吧。事实上PE文件没想像那么难~


相关资源下载地址:

点击打开链接

(内含PE文件解析类源代码文件、简单DEMO、以及自已实现的控制台PE文件查看器)



參考书籍:

《Windows PE权威指南》戚利

《加密与解密(第三版)》段钢

《软件加密技术内幕》看雪学院

《Windows环境下32位汇编语言程序设计(典葳版)》罗云彬

《加密与解密》吴强

《逆向project核心原理》李承远


參考软件:

Stud_PE

LordPE

StudyPE+

eXeScope-ha

CFF Explorer

Resource Hacker

PEID

PEview

ExeinfoPe

OllyDbg

IDA Pro


參考源代码:

PEDump

libpe-master

The Portable Executable File Format from Top to Bottom


PE文件格式官方文档:Microsoft PE and COFF Specification


在线PEDump工具:PEdump - dump your PE!


同一时候參考了CSDN、看雪学院、吾爱破解上等资料,在此一并感谢!



最后

以上就是幸福哈密瓜最近收集整理的关于C++PE文件格式解析类(轻松制作自己的PE文件解析器)的全部内容,更多相关C++PE文件格式解析类(轻松制作自己内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(75)

评论列表共有 0 条评论

立即
投稿
返回
顶部