Soul Orbit

PE文件解析库 – LibPE v0.1 Preview

原创文章,转载请标明出处:Soul Orbit
本文链接地址:PE文件解析库 – LibPE v0.1 Preview

最近突然想学习一下PE文件的结构,所以就写了一个PE文件的解析库,纯属好玩。于是经过了1个月的业余时间的折腾,LibPE v0.1 Preview终于诞生了。

项目名称:LibPE
项目地址:https://github.com/r1f/libpe
License:The BSD 2-Clause License

欢迎大家去围观,如果有人参与那就更加的Happy鸟~Licese是BSD的,所以基本上大家可以拿来随便用了。

1. 功能

现在这个库到底能做什么呢?说出来真的是太简单了,所以才把这个版本定位v0.1 Preview,供围观之用。

  • 解析磁盘上的PE文件,暂时不支持解析映射好的PE。
  • 支持解析32位(PE32)和64位(PE32+)的PE文件。
  • RVA,VA,FOA转换
  • 解析PE基本头部信息,段表,导出表,导入表,资源表,重定向表和导入函数地址表
  • 支持获取PE文件的原始数据结构,并且对结构中的字段提供想对应的getter,如IPEOptionalHeader::GetFieldDllCharacteristics
    (注意:这里拿到的是原始数据,在文件中和在映射好后的映像中可能会不一样)

好了,围观完了之后,我们现在再大概描述一下LibPE想做什么吧~

2. 目标和设计原则

LibPE想成为一个容易使用,32位64位兼容,性能不错并且可以跨越平台的PE解析库。虽然里面有一部分内容还没有完成,比如跨平台的迁移,IMAGE相关的结构还真的是多到不行啊,迁移起来还真是需要一些时间。

2.1. 容易使用

  • 尽量避免让使用者通过RVA和FOA去操作PE结构,而是直接提供获取PE元素的函数。
  • 获取到的每个PE结构都具备有获取RVA,FOA和大小的函数。
  • 能编译成lib和dll,方便使用
  • 使用引用技术来管理对象的生命周期,接口中只使用基本的数据类型,避免跨dll的问题。
  • 能够解析不同状态的PE文件结构,比如在磁盘上的,已经被映射进来之后的。当然这个现在还没有完成。。。。嗯。。。

2.2. 兼容32位和64位的PE格式

  • 这个功能简直就是必备的
  • 统一32位和64位的接口!!

    这个特性个人觉得很有用,网上大部分的PE库,为了支持64位都是使用模版来完成的,这样我们想实现一个批量的PE文件处理的话就会出现很多问题,而LibPE就是想解决这些问题:

    • 冗余的代码:除非使用模版,要不然处理的逻辑得32位和64位都写一遍,但是使用模版,又会让代码看起来更加晦涩,这些都是LibPE想避免的问题
    • 冗余的IO:由于处理的时候需要先判断文件是32位还是64位,所以就会出现需要一次IO去判断文件类型,再一次IO去解析文件,这样会导致处理大量文件时性能下降,因为这个时候磁盘IO是整个系统的瓶颈。

2.3. 不错但不是极致的性能

LibPE并不追求极致的性能,相对极致的性能,LibPE更加注重是否对程序员友好,整个库是否好用。但是性能也是很重要的!

为了达到不错的性能,我们主要做了两个方便的工作:

  • 即用即解析:如果不需要看导入表,那我解析个毛啊!
  • 最小化磁盘IO:在现在手机CPU都可以处理登月数据的年代,CPU其实已经非常的强。而大量的文件处理时,磁盘IO才是真正的瓶颈,减少磁盘IO才是真正对整个系统最有帮助的优化。

2.4. 跨平台

跨平台啊,跨平台,相当蛋疼的体力活也相当重要。客户端的代码一般都不会出现大量处理PE文件的情况,而后台一般都是linux,所以支持跨平台很重要。

2.5. 最小外部依赖

为什么不用boost或者其他跨平台的基础库?作出这个蛋疼决定的出发点只有一条:让大家能少checkout一点代码,改libpe时能少学一点东西。

3. 使用

好了,扯了这么多七里八里的东西,看看到底要怎么用这玩意吧~

  1. 编译LibPE,把生成的LibPE.lib(lib方式编译),LibPEDll.lib,LibPE.dll(Dll方式编译)拷贝到你自己的工程目录中去。
  2. 开始开心的写代码吧,先来个导入表玩玩:
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
#include <stdio.h>
#include <tchar.h>
#define LIBPE_DLL
#include "LibPE.h"
using namespace LibPE;
#pragma comment(lib, "LibPEDll.lib")
int _tmain(int argc, _TCHAR* argv[])
{
LibPEPtr<IPEFile> pFile;
ParsePEFromDiskFile(L"C:\\Windows\\system32\\kernel32.dll", &pFile);
// Import Table
LibPEPtr<IPEImportTable> pImportTable;
pFile->GetImportTable(&pImportTable);
printf("Import Table:\n");
UINT32 nImportModuleCount = pImportTable->GetModuleCount();
for(UINT32 nImportModuleIndex = 0; nImportModuleIndex < nImportModuleCount; ++nImportModuleIndex) {
LibPEPtr<IPEImportModule> pImportModule;
pImportTable->GetModuleByIndex(nImportModuleIndex, &pImportModule);
printf("Import Module: %s (Bound: %s)\n", pImportModule->GetName(), pImportModule->IsBound() ? "true" : "false");
for(UINT32 nImportFunctionIndex = 0; nImportFunctionIndex < pImportModule->GetFunctionCount(); ++nImportFunctionIndex) {
LibPEPtr<IPEImportFunction> pImportFunction;
pImportModule->GetFunctionByIndex(nImportFunctionIndex, &pImportFunction);
printf("Import Function: %s\n", pImportFunction->GetName());
}
printf("\n");
}
return 0;
}

更多的使用方法,大家可以看看工程中的LibPETest,其实这也不是一个Test工程,就是用来大概试一下新的写的东西。

当然,现在LibPE还有很多问题,还有很多需要做的内容没有做。日后再慢慢完善吧~

坚持原创技术分享,您的支持将鼓励我继续创作!