Geoff Chappell - Software Analyst
From its origin in Windows NT 3.1 as the definitive record of a loaded module, the LDR_DATA_TABLE_ENTRY structure has a dword of Flags. These might be just an internal detail of the boot loader’s, the kernel’s and NTDLL’s management of loaded modules—something useful to know when debugging but nothing to use safely in programming—except that the Flags are copied whole to the RTL_PROCESS_MODULE_INFORMATION structure. Thus are the Flags exposed even to user-mode software from the beginning of Windows.
In the many years since this beginning, interpreting the Flags has got complicated. The LDR_DATA_TABLE_ENTRY originally served all three of the boot loader, kernel and NTDLL. Each has its own loader of executable modules—at the very least, each loads the next—but although the three loaders are essentially independent, their early versions all use the same LDR_DATA_TABLE_ENTRY as their structure for managing any one loaded module. Within this structure’s Flags, some bits are meaningful to all the loaders, most only to NTDLL, and a few only to the kernel.
Microsoft’s names for these bits as long ago as Windows NT 3.51 are known from the output of the !dlls command as implemented (then) by the KDEXTX86.DLL debugger extension from the contemporaneous Device Driver Kit (DDK). That a command for listing user-mode DLLs shows flags that look to have no meaning to the user-mode loader suggests most obviously two possibilities: I should look harder in my inspection of the binaries; or the writer of the debugger extension, possibly far removed from any programmers of the loaders, simply had !dlls show all the defined flags. Suppose the latter and then disregard bits whose known use in version 3.51 has no corresponding use in version 3.10, and the following is surely a reasonable guess for Microsoft’s original definitions of the Flags bits:
#define LDRP_STATIC_LINK 0x00000002 // ntdll #define LDRP_IMAGE_DLL 0x00000004 // ntdll #define LDRP_LOAD_IN_PROGRESS 0x00001000 // ntdll #define LDRP_UNLOAD_IN_PROGRESS 0x00002000 // ntdll #define LDRP_ENTRY_PROCESSED 0x00004000 // ntldr, ntoskrnl and ntdll #define LDRP_ENTRY_INSERTED 0x00008000 #define LDRP_CURRENT_LOAD 0x00010000 #define LDRP_FAILED_BUILTIN_LOAD 0x00020000 // ntoskrnl
When Windows XP specialised the LDR_DATA_TABLE_ENTRY into a new KLDR_DATA_TABLE_ENTRY for the kernel-mode loader, keeping the LDR_DATA_TABLE_ENTRY for the user-mode loader, the many members that were carried from the old to the new included the Flags. For a few versions more, the two structures look to have shared the one list of Flags bits.
Starting with version 6.2, what had just been a ULONG for Flags is elaborated formally as bit fields:
Mask | Definition | Versions |
---|---|---|
0x00000001 |
ULONG PackagedBinary : 1; |
6.2 and higher |
0x00000002 |
ULONG MarkedForRemoval : 1; |
6.2 and higher |
0x00000004 |
ULONG ImageDll : 1; |
6.2 and higher |
0x00000008 |
ULONG LoadNotificationsSent : 1; |
6.2 and higher |
0x00000010 |
ULONG TelemetryEntryProcessed : 1; |
6.2 and higher |
0x00000020 |
ULONG ProcessStaticImport : 1; |
6.2 and higher |
0x00000040 |
ULONG InLegacyLists : 1; |
6.2 and higher |
0x00000080 |
ULONG InIndexes : 1; |
6.2 and higher |
0x00000100 |
ULONG ShimDll : 1; |
6.2 and higher |
0x00000200 |
ULONG InExceptionTable : 1; |
6.2 and higher |
ULONG ReservedFlags1 : 2; |
6.2 and higher | |
0x00001000 |
ULONG LoadInProgress : 1; |
6.2 and higher |
0x00002000 |
ULONG ReservedFlags2 : 1; |
6.2 to 6.3 |
ULONG LoadConfigProcessed : 1; |
10.0 and higher | |
0x00004000 |
ULONG EntryProcessed : 1; |
6.2 and higher |
0x00008000 |
ULONG ProtectDelayLoad : 1; |
10.0 and higher |
ULONG ReservedFlags3 : 3; |
6.2 to 6.3 | |
ULONG ReservedFlags3 : 2; |
10.0 and higher | |
0x00040000 |
ULONG DontCallForThreads : 1; |
6.2 and higher |
0x00080000 |
ULONG ProcessAttachCalled : 1; |
6.2 and higher |
0x00100000 |
ULONG ProcessAttachFailed : 1; |
6.2 and higher |
0x00200000 |
ULONG CorDeferredValidate : 1; |
6.2 and higher |
0x00400000 |
ULONG CorImage : 1; |
6.2 and higher |
0x00800000 |
ULONG DontRelocate : 1; |
6.2 and higher |
0x01000000 |
ULONG CorILOnly : 1; |
6.2 and higher |
0x02000000 |
ULONG ChpeImage : 1; |
1803 and higher |
ULONG ReservedFlags5 : 3; |
6.2 to 1709 | |
ULONG ReservedFlags5 : 2; |
1803 and higher | |
0x10000000 |
ULONG Redirected : 1; |
6.2 and higher |
ULONG ReservedFlags6 : 2; |
6.2 and higher | |
0x80000000 |
ULONG CompatDatabaseProcessed : 1; |
6.2 and higher |
Though some programmer at Microsoft evidently went to some trouble to organise the Flags into bit fields for Windows 8, and may even have intended to define all the bits that are meaningful to the user-mode loader, the result is not complete and current. Certainly, the two bits that are named ReservedFlags1 both have new use even for Windows 8. The 0x00000400 bit is set in the entry for the Application Verifier provider. The 0x00000800 bit records that the shim engine (apphelp.dll) has been sent its initial notification (SE_DllLoaded) about this entry.
If earlier versions defined the Flags bits symbolically, it will have been through macros. These would not pass even into private symbols, but Microsoft’s names for some of the bits are known with good confidence from the output of the !dlls command as implemented in debugger extensions KDEXTX86.DLL at first and then EXTS.DLL in Windows XP and higher:
RESEARCH AND WRITING IN PROGRESS