lordmilko/PESpy

GitHub: lordmilko/PESpy

PESpy 是一个用 C#/PowerShell 编写的逆向工程库,用于解析、分析和可视化微软编译器生成的各类二进制文件和符号格式。

Stars: 3 | Forks: 0

# PESpy [![构建状态](https://ci.appveyor.com/api/projects/status/n3o1pfy22ki6saoj?svg=true)](https://ci.appveyor.com/project/lordmilko/pespy) [![NuGet](https://img.shields.io/nuget/v/PESpy.svg)](https://www.nuget.org/packages/PESpy/) [![捐赠](http://img.shields.io/liberapay/patrons/lordmilko.svg?logo=liberapay)](https://liberapay.com/lordmilko/donate) PESpy 是一个用于逆向工程、分析和可视化 Microsoft 编译器生成的文件格式的 C#/PowerShell 库。 给定一个文件,PESpy 旨在 * 理解*该文件中每一个字节*的含义 * 支持解析*所有已知实体*,无论它们多么冷门 * 尽量减少抽象,并在尽可能的地方映射原生类型名称 * 在保证易用性的同时保持高性能。内存分配必须尽可能低! * 支持*所有已知的符号格式*;COFF、OMF、CodeView、SYM、DBG、PDB 文件、DNDRB、NB00-NB10、RSDS - 只要存在符号,PESpy 就会读取并展示给你 * 真正实现*触手可及的信息*。整个文件层级通过属性暴露出来;只需打开一个文件,然后在 Locals 窗口中查看即可 * 支持从远程调试目标中读取预先不知道大小的 PE 文件 * 提供用于执行各种文件操作的工具,包括 * 检测文件类型 * 定位符号文件(不再需要 `symsrv.dll`!) * 解析 RPC 服务器 * 操作符号键 (Symbol Keys) * 解析 vftables * 还原符号名 * 读取并解压 Windows 安装介质中包含的文件 * 对 NativeAOT 高度友好 PESpy 能够与以下文件类型进行交互 | 名称 | 描述 |--------|--------------- | PE | Portable Executable 文件,最早出现于 Windows NT 3.1 | | PDB | “旧式” (JG 1.0)、MSF (JG 2.0, DS 7.0) 和 Portable PDB 文件 | | OBJ | 原则上我们主要关注 `*.obj` 文件,但严格来说任何使用 COFF 的文件(如 `*.exp`、`*.iobj` 等)都可以被打开 | | DOS | 带有 `IMAGE_DOS_HEADER` 和可能存在的尾部 CodeView 数据的简单 DOS 文件 | | NE | 16 位 New Executable 文件,常见于 16 位 Windows 以及在较小程度上见于 Windows 9x | | LE | 32 位 Linear Executable 文件;具体而言,即 VxD 驱动文件使用的格式 | | DBG | 基于 COFF 的文件,包含从主可执行文件中分离出来的调试元数据 | | LIB | 链接器使用的基于 COFF 的 Archive 库,其中可能潜藏有对象文件 | | OMF | DOS 时代旧编译器工具链生成的 `*.obj` 文件,它们使用 COFF 的前身——Object Module Format | | OMFLIB | DOS 时代旧编译器工具链生成和使用的 `*.lib` 文件,它们使用 OMF | | OMFDBG | 旧式 `*.dbg` 文件,其全部内容即为原始的 OMF 样式 CodeView 段 | | SYM | 由 `mapsym.exe` 或编译器解析 `*.map` 文件生成的 `*.sym` 文件 | ## 安装说明 ``` Install-Package PESpy ``` PESpy 在 [nuget.org](https://www.nuget.org/packages/PESpy/) 和 [PowerShell Gallery](https://www.powershellgallery.com/packages/PESpy/) 上均可用。PESpy 提供了针对 .NET 9.0 和 .NET Standard 的目标,并且兼容 SourceLink。为了从 PowerShell Gallery 安装 PESpy,你必须运行 PowerShell 5.1+。PESpy 兼容 Windows PowerShell 和 PowerShell Core。 ## 快速入门 PESpy 的主要卖点在于,只要有可能,它都会尽力向你展示文件中数据的真实形态。以下代码片段展示了 PESpy 核心功能的各个入口点。有关 PESpy 所有功能的极其详尽的文档,请参阅 [wiki](https://github.com/lordmilko/PESpy/wiki)。 ### 枚举所有导入 ``` /* Retrieving locals in native code involves traversing the IMAGE_IMPORT_DESCRIPTOR entities, resolving various RVAs * traversing a list of IMAGE_THUNK_DATA entities followed, checking various bit fields, resolving * even more RVAs, before finally retrieving the strings you're after. That is what the data looks like. PESpy provides * many mechanisms to simplify complex lookups, but it will never hide the underlying shape of the data to "make it easy" */ using var peFile = PEFile.FromFile("C:\\Windows\\system32\\kernel32.dll"); ImageImportDescriptor[]? importTable = peFile.ImportTable; if (importTable != null) { foreach (var imageImportDescriptor in importTable) { /* Any field that is an RVA to another entity is modelled as a field of type RVA. This type * provides access to the original RVA that was listed in the field, whether the RVA could actually * be resolved to a valid address, and the actual value that was read from that address */ RVA dllName = imageImportDescriptor.Name; if (!dllName.IsValid) continue; RVA originalFirstThunk = imageImportDescriptor.OriginalFirstThunk; if (!originalFirstThunk.IsValid) continue; /* A custom collection type prevents us from having to allocate a large array to access all * of the thunks in the section. Note that the trailing "null" IMAGE_THUNK_DATA is also included * as the last item in this list */ foreach (ImageThunkData entry in originalFirstThunk.Value) { //IMAGE_THUNK_DATA is defined as a union of four possible fields. PESpy tries to figure out //which logical type the thunk represents, and stores this in an added Kind field if (entry.Value == 0) continue; //This is the trailing "null" entry which marks the end of this import's thunks if (entry.Kind == ImageThunkData.DataKind.Name) { RVA thunkName = entry.Name; if (!thunkName.IsValid) Console.WriteLine($"{dllName}: Invalid Name (0x{thunkName.ListedOffset})"); else Console.WriteLine($"{dllName}: {thunkName}"); } } } } ``` ### 定位符号文件 PESpy 的 `Locator` 类提供了 mspdbcore 中 `LOCATOR` 类的托管实现,这也是 DIA 背后的技术支撑 * `Locator` 可以定位各种符号;PDB(无论是常规的、Portable、Embedded 还是 NGEN 的)、`*.dbg` 文件(可能反过来指向 `*.pdb` 文件),甚至是旧式的 `*.sym` 文件 * 它知道如何读取你的符号路径;如果未设置 `_NT_SYMBOL_PATH`,它会自动使用包含 `msdl.microsoft.com` 的符号路径 * 它可以从远程 HTTP 服务器下载符号,并将它们级联存储到你的符号路径中 * 为各种不同的场景提供了多个入口点,同时支持同步和异步模式 * 允许指定一个回调来接收进度通知 * 历经重重优化以实现尽可能低的内存分配 * 完全可移植,完全不依赖 `symsrv.dll` ``` var pdbPath = Locator.LocatePDB("C:\\Windows\\system32\\ntdll.dll"); ``` `Locator` 只是 PESpy 功能表面的冰山一角,但我很惊讶自己竟然如此频繁地使用它;它出乎意料地成为了 PESpy 对我来说最棒的功能之一! ### 枚举所有符号 ``` /* PEFile provides various members (SymStoreKeys, GetSymStoreKey()) that provide identifiers for files * that you can lookup on a symbol server. If you're writing unit tests for a diagnostic application that analyzes * a certain DLL, you can potentialy "bookmark" that DLL by hardcoding its SymStoreKey, and then have your test re-download * that file as needed so your test always produces the same result! */ var key = new SymStoreKey("coreclr.pdb/75099299D3D948A68B594FC4439DFA521/coreclr.pdb"); var pdbPath = Locator.LocatePDB(key); /* The PDBFile class provides access to every single piece of functionality you might see in an MSF based PDB File. * Every hash, every lookup, every struct since the introduction of MSF in Visual C++ 2.0 (1994) */ using var pdbFile = PDBFile.FromFile(pdbPath); /* The native representation of a symbol is a SYMTYPE*. SymType is a zero cost abstraction over a pointer, but unlike * a native SYMTYPE*, SymType uses insane debugger magic to show you all of the symbol's fields in the Locals window * without you having to write any code */ foreach (SymType symType in pdbFile.EnumerateSymbols()) { /* A SymType can be cast to a more specific symbol type (e.g. ProcSym32) based on the `SYM_ENUM_e` of its `rectyp`, * or you can use extension methods that replicate the behavior of the various getters seen on `IDiaSymbol` */ if (symType.TryGetFramePointerPresent(out var framePointerPresent)) { if (symType.rectyp == SYM_ENUM_E.S_GPROC32) { var pubSym32 = (PubSym32) symType; /* Modern PDBs contain UTF-8 null terminated strings. But older PDBs use length prefixed "ST" strings. * PESpy can use magic to figure out that the expected string format is, or you can just provide the PDBFile. * ProcSym32's "name" property provides easy access to the symbol's name, but for high performance access * you'll want to use the GetName method */ SymString name = pubSym32.GetName(pdbFile); } } } ``` ### 可视化文件 ``` /* In two lines of code, you can visualize the entire contents of a file: view all sections, the regions * within those sections, how code and data intertwine, and the xrefs between everything. Explore * the entire structure of a file right from within your debugger. Query offsets, RVAs and VAs to find * exactly what is located at that address. Strings are automatically detected, and an interface is provided * to facilitate tagging disassembled code */ using var peFile = PEFile.FromFile("C:\\Windows\\system32\\kernel32.dll"); /* Unless you say otherwise, GetView will automatically attempt to download symbols, * so the first time you call this you may need to wait while symbols are downloaded. * Secify a progress callback to receive notice of what is going on. See the wiki for * more information on interfacing with views */ var view = peFile.GetView(); ``` 有关使用 PESpy 的更多信息,请参阅 [wiki](https://github.com/lordmilko/PESpy)
标签:AI合规, PDB解析, PE文件, 二进制分析, 云安全运维, 云资产清单, 逆向工程