The load configuration directory, at data directory 10, began as a few settings for
the heap and critical sections. It has grown with nearly every Windows release, and now
holds what the loader needs for the security features: the stack cookie, the table of
safe exception handlers, and the tables of Control Flow Guard. Microsoft’s C run-time
library supplies it, as the variable _load_config_used.
The directory
IMAGE_LOAD_CONFIG_DIRECTORY32. Its first field, Size, says how much of it is there, so a file carries only the fields its tools knew of, and the loader treats the size as a version check. The fields below are from winnt.h; Microsoft’s PE page gives offsets only up to GuardLongJumpTargetCount, and those after it are worked out from winnt.h’s field sizes. Addresses are virtual addresses, the image base included.
| Offset | Size | Type | Field | Meaning |
|---|---|---|---|---|
| 0 | 4 | u32 | Size | The size of the structure as written. |
| 4 | 4 | u32 | TimeDateStamp | Usually 0. |
| 8 | 2 | u16 | MajorVersion | Usually 0. |
| 10 | 2 | u16 | MinorVersion | |
| 12 | 4 | u32 | GlobalFlagsClear | Global flags to clear for the process. |
| 16 | 4 | u32 | GlobalFlagsSet | Global flags to set. |
| 20 | 4 | u32 | CriticalSectionDefaultTimeout | The default timeout for critical sections. |
| 24 | 4 | u32 | DeCommitFreeBlockThreshold | Heap settings. |
| 28 | 4 | u32 | DeCommitTotalFreeThreshold | |
| 32 | 4 | u32 | LockPrefixTable | The lock prefix table. |
| 36 | 4 | u32 | MaximumAllocationSize | Heap settings. |
| 40 | 4 | u32 | VirtualMemoryThreshold | |
| 44 | 4 | u32 | ProcessHeapFlags | Flags for the process heap. |
| 48 | 4 | u32 | ProcessAffinityMask | The processors the process may run on. |
| 52 | 2 | u16 | CSDVersion | The service pack version. |
| 54 | 2 | u16 | DependentLoadFlags | Default flags for the DLLs this image loads. |
| 56 | 4 | u32 | EditList | Reserved. |
| 60 | 4 | u32 | SecurityCookie | The address of the /GS stack cookie, which the loader fills with a random value. |
| 64 | 4 | u32 | SEHandlerTable | The safe exception handler table. |
| 68 | 4 | u32 | SEHandlerCount | Its number of entries. |
| 72 | 4 | u32 | GuardCFCheckFunctionPointer | Where the Control Flow Guard check function’s address is stored. |
| 76 | 4 | u32 | GuardCFDispatchFunctionPointer | Where the dispatch function’s address is stored. |
| 80 | 4 | u32 | GuardCFFunctionTable | The CFG function table. |
| 84 | 4 | u32 | GuardCFFunctionCount | Its number of entries. |
| 88 | 4 | u32 | GuardFlags | See Guard flags. |
| 92 | 12 | – | CodeIntegrity | See Code integrity. |
| 104 | 4 | u32 | GuardAddressTakenIatEntryTable | The import address table slots whose address the code takes. |
| 108 | 4 | u32 | GuardAddressTakenIatEntryCount | |
| 112 | 4 | u32 | GuardLongJumpTargetTable | The valid targets of longjmp. |
| 116 | 4 | u32 | GuardLongJumpTargetCount | |
| 120 | 4 | u32 | DynamicValueRelocTable | Dynamic value relocations. |
| 124 | 4 | u32 | CHPEMetadataPointer | Metadata for compiled hybrid images. |
| 128 | 4 | u32 | GuardRFFailureRoutine | For Return Flow Guard. |
| 132 | 4 | u32 | GuardRFFailureRoutineFunctionPointer | |
| 136 | 4 | u32 | DynamicValueRelocTableOffset | Where the dynamic value relocations are, as an offset in a section. |
| 140 | 2 | u16 | DynamicValueRelocTableSection | |
| 142 | 2 | u16 | Reserved2 | 0. |
| 144 | 4 | u32 | GuardRFVerifyStackPointerFunctionPointer | For Return Flow Guard. |
| 148 | 4 | u32 | HotPatchTableOffset | Hot patching information. |
| 152 | 4 | u32 | Reserved3 | 0. |
| 156 | 4 | u32 | EnclaveConfigurationPointer | Configuration of an enclave image. |
| 160 | 4 | u32 | VolatileMetadataPointer | Volatile metadata. |
| 164 | 4 | u32 | GuardEHContinuationTable | The valid targets of exception handling continuations. |
| 168 | 4 | u32 | GuardEHContinuationCount | |
| 172 | 4 | u32 | GuardXFGCheckFunctionPointer | For eXtended Flow Guard (XFG). |
| 176 | 4 | u32 | GuardXFGDispatchFunctionPointer | |
| 180 | 4 | u32 | GuardXFGTableDispatchFunctionPointer | |
| 184 | 4 | u32 | CastGuardOsDeterminedFailureMode | For Cast Guard. |
| 188 | 4 | u32 | GuardMemcpyFunctionPointer | For the memcpy guard. |
ProcessHeapFlags and ProcessAffinityMask. In this 32-bit structure ProcessHeapFlags comes first, at 44, and ProcessAffinityMask second, at 48, as winnt.h defines it in both Microsoft’s SDK and Wine. Microsoft’s PE format page shows them the other way round, which is the order of the 64-bit structure. The explorer follows winnt.h.
00009590 5C 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000095A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000095B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000095C0 00 00 00 00 00 00 00 00 00 00 00 00 04 C0 00 10
000095D0 F0 A1 00 10 01 00 00 00 10 92 00 10 00 00 00 00
000095E0 00 00 00 00 00 00 00 00 00 01 00 00
0xA190 (file 0x9590): Size 92; the security cookie at 0x1000C004; one safe handler, in a table at 0x1000A1F0; the CFG check function pointer at 0x10009210; GuardFlags 0x100, CF_INSTRUMENTEDSize says 92 bytes, but data directory 10 says 64. x86 files from Visual C++ have 64 there for compatibility with Windows XP, whose loader checks that size against the one it knows. The explorer reads the structure at the length Size gives.
Safe exception handlers
32-bit x86 code handles exceptions with handler records on the stack, which a buffer
overflow can overwrite. An image linked with /SAFESEH lists every
legitimate handler, and Windows refuses to call a handler in that image that is not
in the list. SEHandlerTable points at the list: SEHandlerCount 4-byte RVAs, sorted.
In the example the one entry, at RVA 0xA1F0, is 0x8D5B. An
image with the NO_SEH flag has no handlers at
all.
Control Flow Guard tables
With Control Flow Guard, every indirect call first passes its target to a check function, which stops the program if the target is not the start of a known function. The image lists the valid targets:
- GuardCFFunctionTable – the functions that may be called indirectly;
- GuardAddressTakenIatEntryTable – the import slots whose address is taken;
- GuardLongJumpTargetTable – the places
longjmpmay return to; - GuardEHContinuationTable – the places exception handling may continue at.
Each table is a sorted array of 4-byte RVAs, each followed by the number of extra bytes that bits 28–31 of GuardFlags give. The explorer shows all four tables.
Guard flags
| Bit | Name | Meaning |
|---|---|---|
0x00000100 | CF_INSTRUMENTED | The module performs control flow checks. |
0x00000200 | CFW_INSTRUMENTED | The module performs control flow and write checks. |
0x00000400 | CF_FUNCTION_TABLE_PRESENT | It has a CFG function table. |
0x00000800 | SECURITY_COOKIE_UNUSED | It does not use the /GS cookie. |
0x00001000 | PROTECT_DELAYLOAD_IAT | The delay-load import table can be read-only. |
0x00002000 | DELAYLOAD_IAT_IN_ITS_OWN_SECTION | The delay-load import table is in a section of its own. |
0x00004000 | CF_EXPORT_SUPPRESSION_INFO_PRESENT | It has export suppression information. |
0x00008000 | CF_ENABLE_EXPORT_SUPPRESSION | Export suppression is enabled. |
0x00010000 | CF_LONGJUMP_TABLE_PRESENT | It has a long jump target table. |
0xF0000000 | CF_FUNCTION_TABLE_SIZE_MASK | Bits 28–31: the extra bytes after each RVA in the CFG tables. |
The explorer also names these, which are in the SDK’s winnt.h but not on
Microsoft’s PE page: 0x20000 RF_INSTRUMENTED, 0x40000
RF_ENABLE, 0x80000 RF_STRICT, 0x100000 RETPOLINE_PRESENT,
0x400000 EH_CONTINUATION_TABLE_PRESENT, 0x800000
XFG_ENABLED, 0x1000000 CASTGUARD_PRESENT and 0x2000000
MEMCPY_PRESENT.
Code integrity
IMAGE_LOAD_CONFIG_CODE_INTEGRITY, 12 bytes at offset 92:
| Offset | Size | Type | Field | Meaning |
|---|---|---|---|---|
| 0 | 2 | u16 | Flags | Flags that say whether the catalog fields are used. |
| 2 | 2 | u16 | Catalog | 0xFFFF means the catalog is not available. |
| 4 | 4 | u32 | CatalogOffset | Reserved for the catalog. |
| 8 | 4 | u32 | Reserved | 0. |
Lock prefix table
An x86-only table from the days of single-processor machines: a list of the
addresses of lock prefixes in the code, ending with 0, which Windows
could replace with nop on a machine with one processor. The explorer
shows the list, whose addresses are virtual addresses.