一组可重用的 PE 文件格式扫描函数






4.21/5 (15投票s)
用于从 PE 文件检索信息的有用函数
引言
本文伴随一组命令行示例应用程序,这些应用程序封装了我的一些通用代码。这些通用代码可用于从 PE 格式文件(PE 格式代表可移植可执行格式)中提取各种信息。这四个示例分别命名为 **bitness**、**pefileuses**、**dotnetsearch** 和 **pdbget**。
**bitness** 接受文件名作为命令行参数,并将告诉你传入的参数文件是 32 位还是 64 位 PE 文件。它封装了以下通用代码函数:
BOOL IsFile64BitPEFileW(LPCWSTR szFile, PBOOL pbIs64Bits);
BOOL IsFile64BitPEFileA(LPCSTR szFile, PBOOL pbIs64Bits);
参数应该很直观。如果函数成功,它将返回一个非零值。如果失败,返回值将是 FALSE
,并且可以通过 GetLastError
获取扩展错误信息。成功时,out
参数 pbIs64Bits
将包含一个非零值,如果作为参数 szFile
传入的 PE 文件是 64 位的。
**pefileuses** 用于确定给定的 PE 文件是否链接到某个 DLL 或使用给定 DLL 中的函数。它需要 3 个命令行参数,可选地还有第四个参数。第一个参数是一个介于零和 2 之间的数字。此数字决定是扫描导入表还是延迟加载函数的表,或者两者都扫描。“0”表示扫描两个表。“1”表示仅扫描导入表,“2”表示仅扫描延迟加载表的。“第二个参数是要扫描的 PE 文件。第三个参数表示应扫描其名称的 DLL。最后,第四个参数是一个可选的函数名称。应用程序将在 stdout
上打印出指定的二进制文件是否链接到给定的 DLL,甚至是否使用可选的函数名。此工具封装了以下通用代码函数:
BOOL __stdcall PeFileUsesImportA(LPCSTR szPeFile, LPCSTR szDllName,
LPCSTR szFunction,
PBOOL pbUse, DWORD dwFlags);
BOOL __stdcall PeFileUsesImportW(LPCWSTR szPeFile, LPCWSTR szDllName,
LPCWSTR szFunction, PBOOL pbUse,
DWORD dwFlags);
要传递给此函数的标志是传递给 pefiluses.exe 的第一个参数,并定义如下:
#define PUI_USE_IMPORT_ONLY 0x1
#define PUI_USE_DELAYLOAD_ONLY 0x2
如上所述,将 0L 作为 dwFlags
参数传递会扫描两个表。其他参数应该很直观。如果函数成功,它将返回一个非零值。如果失败,返回值将是 FALSE
,并且可以通过 GetLastError
获取扩展错误信息。
**dotnetsearch** 是一个工具,用于扫描整个目录树并评估找到的每个 DLL 和 EXE 文件,以确定它是否为 .NET 二进制文件。我编写此工具是为了查看 Windows Vista 的每个新版本,并找出整个 Vista 硬盘上有多少文件使用了 .NET 框架。一个使用 .NET 框架的二进制文件很容易识别,因为它链接到 mscoree.dll。dotnetsearch 工具封装了以下通用代码函数:
BOOL __stdcall BinaryUsesDotNetA(LPCSTR szFileName, PBOOL pbUse);
BOOL __stdcall BinaryUsesDotNetW(LPCWSTR szFileName, PBOOL pbUse);
同样,参数应该很直观。如果函数成功,它将返回一个非零值。如果失败,返回值将是 FALSE
,并且可以通过 GetLastError;
获取扩展错误信息。
**pdbget** 检查给定的 PE 文件以查找其调试信息的所在位置。如今,如果您使用 Microsoft 编译器,CodeView 调试信息位于 PDB 文件中。为了让调试器找到给定 PE 文件的 PDB 文件,其文件路径将作为 ANSI 字符串嵌入 PE 文件中。pdbget 示例应用程序将要检查的 PE 文件的路径作为命令行参数,并将关联的 PDB 文件的位置打印到 stdout。PDB 2.0(VC 6)和 PDB 7.0(Visual Studio .NET 及更高版本)这两种格式的调试信息都将被正确检测,此工具封装了以下通用代码函数:
BOOL __stdcall GetPDBFileA(LPCSTR szPeFile, LPSTR szPdbFile, PDWORD pdwSize);
BOOL __stdcall GetPDBFileW(LPCWSTR szPeFile, LPWSTR szPdbFile, PDWORD pdwSize);
这些函数的第一参数是要检查的 PE 文件的路径。第二个参数是可选参数,指向内存缓冲区,函数会将 PDB 文件的路径以空终止字符串的形式写入其中。第三个参数是以字符为单位的内存缓冲区的大小,它充当输入/输出参数。内存缓冲区必须由调用者分配,但可以将 szPdbFile
参数指定为 NULL
,在这种情况下,只有所需大小会写入 pdwSize
。这样,您可以先将第二个参数指定为 NULL
调用这些函数,然后使用通过第三个参数检索到的大小分配所需的内存。使用此分配的内存,您可以再次调用这些函数来检索 PDB 文件的路径。
如果函数成功,它将返回一个非零值。如果失败,返回值将是 FALSE
,并且可以通过 GetLastError
获取扩展错误信息。
Using the Code
该代码在 Visual Studio 2005 中,对于 x86 和 x64 目标,在 UNICODE 和 ANSI 构建中都能干净地编译。您还可以使用 Visual Studio 2008 并将项目和解决方案文件转换为其较新格式。
致谢
我在此通用代码中使用的函数大多是从 Matt Pietrek 和一个随 MSDN 杂志文章附带的示例中“借鉴”来的。我所做的只是将功能更好地分离成可重用的块,删除了原始代码中全局使用的变量,并对事物进行了更好的重新组织,以便在 x86 和 x64 构建以及最新的头文件中正确使用。如果在使用此代码时,您发现任何方面效果很好,那完全归功于 Matt Pietrek,如果代码出现故障或损坏,那将是我的错误。关于 PE 文件中 CodeView 2.0 调试信息的信息来自 debuginfo.com 的优秀人士的文章。
历史
- 1st 版本:2006/07/30:初始版本
- 2nd 版本:2010/04/13:移除了 VC6 支持,添加了
pdbget
- 3rd 版本:2010/05/05:
GetPDBFile
函数现在也可以从 x64 二进制文件中确定 PDB 位置,PeFileUsesImportW
不再将 PE 文件参数转换为 MBCS