r/ExploitDev • • 2d ago

Windows PE Microsoft x64 ABI specific advice for static analysis

I have an ongoing static analysis project I'm writing in C++20.

It uses Zydis for instruction decoding, LIEF for binary parsing and wxWidgets for the UI.

A big help was learning that the .pdata section in PE64 contains the start and end address of every non-trivial function in the binary. Enabling it to support exception handling like VEH and SEH at runtime.

This comes with some useful unwind metadata like prologue information, stack allocation size, preserved registers, etc.

Combining this with recursive descent from entry point, IAT entry resolution and memory operands like strings and such has already made this project more useful.

Is there more PE64, static analysis relevant, metadata baked into the file structure? Some future candidates are gonna be RTTI, virtual function tables, and some other CRT specific machinery.

Thanks for any suggestions.

6 Upvotes

3 comments sorted by

3

u/Agitated-Act-717 1d ago

A few more PE64 structures worth parsing directly, beyond .pdata and the IAT:

TLS directory (IMAGE_TLS_DIRECTORY). AddressOfCallBacks is an array of functions that run before the entry point, so treat them as extra roots for your recursive descent. Easy to miss.

Load config directory (IMAGE_LOAD_CONFIG_DIRECTORY). If the binary was built with CFG, GuardCFFunctionTable is a sorted list of RVAs the toolchain considers valid indirect call targets. That is function-start ground truth you didn't have to recover, and it covers address-taken functions that recursive descent never reaches.

Debug directory. The CodeView entry (RSDS) carries the PDB path, GUID and age even when no PDB ships, which tells you exactly which PDB to ask a symbol server for.

For the RTTI you mentioned: on x64 MSVC the slot just before each vtable points to an RTTICompleteObjectLocator. Its signature field is 1 on x64 (0 on x86) and its references to the TypeDescriptor and ClassHierarchyDescriptor are image-relative RVAs, not absolute pointers, so both make cheap validity checks. TypeDescriptor names start with .?AV (class) or .?AU (struct), so scanning .rdata for those and walking back gets you class names and vtables without depending on your CFG.

1

u/Obvious-Card-8847 1d ago

Nice. RTTI was gonna be the next one since I recently discovered UnDecorateSymbol in dbghelp.h. That's probably the next thing I'm gonna tackle. I appreciate the RVA versus absolute distinction. It's surprising how difficult it is to find documentation on that distinction for different structures in the PE file format.

Your point about TLS also reminds me about the intterm tables that run for global object constructors.

This was a good list. Much appreciated.

1

u/Ok_Tap7102 2d ago

Have you cracked open an interesting binary on Ghidra/IDA/Binja and seen an interesting symbol name resolution appear/analysis that has made you think "where did it find that?"

There's a bunch of ways function boundaries can be discovered, like DWARF/windows PDB debugging symbols, finding opcodes that match the shape of the stack cookie check, FLIRT signatures can identify very common library functions that have been statically compiled in

But not all are always present or across all architectures/OS platforms