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 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_DIRECTORY64. 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. Most fields that hold an address or a count are 64 bits, so the offsets differ from the 32-bit structure’s from offset 24 on. 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 | 8 | u64 | DeCommitFreeBlockThreshold | Heap settings. |
| 32 | 8 | u64 | DeCommitTotalFreeThreshold | |
| 40 | 8 | u64 | LockPrefixTable | The lock prefix table; x86 only, 0 here. |
| 48 | 8 | u64 | MaximumAllocationSize | Heap settings. |
| 56 | 8 | u64 | VirtualMemoryThreshold | |
| 64 | 8 | u64 | ProcessAffinityMask | The processors the process may run on. |
| 72 | 4 | u32 | ProcessHeapFlags | Flags for the process heap. |
| 76 | 2 | u16 | CSDVersion | The service pack version. |
| 78 | 2 | u16 | DependentLoadFlags | Default flags for the DLLs this image loads. |
| 80 | 8 | u64 | EditList | Reserved. |
| 88 | 8 | u64 | SecurityCookie | The address of the /GS stack cookie, which the loader fills with a random value. |
| 96 | 8 | u64 | SEHandlerTable | The safe exception handler table; x86 only, 0 here. |
| 104 | 8 | u64 | SEHandlerCount | |
| 112 | 8 | u64 | GuardCFCheckFunctionPointer | Where the Control Flow Guard check function’s address is stored. |
| 120 | 8 | u64 | GuardCFDispatchFunctionPointer | Where the dispatch function’s address is stored. |
| 128 | 8 | u64 | GuardCFFunctionTable | The CFG function table. |
| 136 | 8 | u64 | GuardCFFunctionCount | Its number of entries. |
| 144 | 4 | u32 | GuardFlags | See Guard flags. |
| 148 | 12 | – | CodeIntegrity | See Code integrity. |
| 160 | 8 | u64 | GuardAddressTakenIatEntryTable | The import address table slots whose address the code takes. |
| 168 | 8 | u64 | GuardAddressTakenIatEntryCount | |
| 176 | 8 | u64 | GuardLongJumpTargetTable | The valid targets of longjmp. |
| 184 | 8 | u64 | GuardLongJumpTargetCount | |
| 192 | 8 | u64 | DynamicValueRelocTable | Dynamic value relocations. |
| 200 | 8 | u64 | CHPEMetadataPointer | Metadata for hybrid images, such as ARM64EC. |
| 208 | 8 | u64 | GuardRFFailureRoutine | For Return Flow Guard. |
| 216 | 8 | u64 | GuardRFFailureRoutineFunctionPointer | |
| 224 | 4 | u32 | DynamicValueRelocTableOffset | Where the dynamic value relocations are, as an offset in a section. |
| 228 | 2 | u16 | DynamicValueRelocTableSection | |
| 230 | 2 | u16 | Reserved2 | 0. |
| 232 | 8 | u64 | GuardRFVerifyStackPointerFunctionPointer | For Return Flow Guard. |
| 240 | 4 | u32 | HotPatchTableOffset | Hot patching information. |
| 244 | 4 | u32 | Reserved3 | 0. |
| 248 | 8 | u64 | EnclaveConfigurationPointer | Configuration of an enclave image. |
| 256 | 8 | u64 | VolatileMetadataPointer | Volatile metadata. |
| 264 | 8 | u64 | GuardEHContinuationTable | The valid targets of exception handling continuations. |
| 272 | 8 | u64 | GuardEHContinuationCount | |
| 280 | 8 | u64 | GuardXFGCheckFunctionPointer | For eXtended Flow Guard (XFG). |
| 288 | 8 | u64 | GuardXFGDispatchFunctionPointer | |
| 296 | 8 | u64 | GuardXFGTableDispatchFunctionPointer | |
| 304 | 8 | u64 | CastGuardOsDeterminedFailureMode | For Cast Guard. |
| 312 | 8 | u64 | GuardMemcpyFunctionPointer | For the memcpy guard. |
ProcessAffinityMask and ProcessHeapFlags. In this 64-bit structure ProcessAffinityMask, 8 bytes, comes first, at 64, and ProcessHeapFlags, 4 bytes, second, at 72. That is the order of winnt.h, in Microsoft’s SDK and in Wine, and of Microsoft’s PE format page. (The 32-bit structure has the two the other way round.)
0000A630 94 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0000A640 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0000A650 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0000A660 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0000A670 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0000A680 00 00 00 00 00 00 00 00 00 E0 00 80 01 00 00 00
0000A690 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0000A6A0 30 A4 00 80 01 00 00 00 38 A4 00 80 01 00 00 00
0000A6B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0000A6C0 00 01 00 00
0xB430 (file 0xA630): Size 148, the size Visual C++ 2015 wrote; zeros up to the security cookie at 0x18000E000; no SafeSEH table; the CFG check and dispatch function pointers at 0x18000A430 and 0x18000A438; no CFG function table; GuardFlags 0x100, CF_INSTRUMENTEDData directory 10 gives the same 148 bytes. Each of the three addresses has a base relocation of its own. The explorer reads the structure at the length Size gives.
Safe exception handlers
SEHandlerTable and SEHandlerCount belong to 32-bit x86, whose exception handlers are records on the stack that an image must vouch for. 64-bit code finds its handlers through the exception table instead, so in a PE32+ file both fields are 0. The explorer still shows a table if one is there: SEHandlerCount 4-byte RVAs, sorted.
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, even in a 64-bit image, each followed by the number of extra bytes that bits 28–31 of GuardFlags give. The explorer shows all four tables. In the example, GuardFlags says CF_INSTRUMENTED, but there is no function table and the DLL characteristics lack GUARD_CF, so Control Flow Guard is not enabled for that image.
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 148:
| 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. |
The example’s Size of 148 ends just before this field, so it has none.
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, which Windows could replace with
nop on a machine with one processor. 64-bit images leave LockPrefixTable
0. The explorer shows the list if one is there.