LINK.EXE
version 14.1*LoadLibrary()
through the
/DEPENDENTLOADFLAG
§ linker option:
/DEPENDENTLOADFLAG[:load_flags]Note: the text cited above shows the original and wrong documentation, present until January 24, 2020.
[…]
On supported operating systems, this option has the effect of changing calls toLoadLibrary("dependent.dll")
to the equivalent ofLoadLibraryEx("dependent.dll", 0, load_flags)
.
[…]
This flag can be used to make DLL planting attacks more difficult.
[…]
An option of/DEPENDENTLOADFLAG:0x800
is even more restrictive, limiting search to the %windows%\system32 directory.
§ This option is supported by
LINK.EXE
since version 14.10, shipped with Visual Studio 2017
and newer versions; to use the feature with prior versions of
LINK.EXE
,
provide an initialised (constant)
IMAGE_LOAD_CONFIG_DIRECTORY
structure with public name
_load_config_used
and its DependentLoadFlags
(former
Reserved1
) member set to the desired value.
The MSDN article Dynamic-Link Library Search Order documents the standard search path.
The values for
/DEPENDENTLOADFLAG
are documented with the
LoadLibraryEx()
function:
[…]Especially notice the highlighted statement common to all flag descriptions there: directories in the standard search path are not searched.
- LOAD_LIBRARY_SEARCH_APPLICATION_DIR
- 0x00000200
- If this value is used, the application's installation directory is searched for the DLL and its dependencies. Directories in the standard search path are not searched. […]
[…]
- LOAD_LIBRARY_SEARCH_DEFAULT_DIRS
- 0x00001000
- This value is a combination of LOAD_LIBRARY_SEARCH_APPLICATION_DIR, LOAD_LIBRARY_SEARCH_SYSTEM32, and LOAD_LIBRARY_SEARCH_USER_DIRS. Directories in the standard search path are not searched. […]
- LOAD_LIBRARY_SEARCH_SYSTEM32
- 0x00000800
- If this value is used, %windows%\system32 is searched for the DLL and its dependencies. Directories in the standard search path are not searched. […]
[…]
- LOAD_LIBRARY_SEARCH_USER_DIRS
- 0x00000400
- If this value is used, directories added using the AddDllDirectory or the SetDllDirectory function are searched for the DLL and its dependencies. If more than one directory has been added, the order in which the directories are searched is unspecified. Directories in the standard search path are not searched. […]
If more than one LOAD_LIBRARY_SEARCH flag is specified, the directories are searched in the following order:
- The directory that contains the DLL (LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR). This directory is searched only for dependencies of the DLL to be loaded.
- The application directory (LOAD_LIBRARY_SEARCH_APPLICATION_DIR).
- Paths explicitly added to the application search path with the AddDllDirectory function (LOAD_LIBRARY_SEARCH_USER_DIRS) or the SetDllDirectory function. If more than one path has been added, the order in which the paths are searched is unspecified.
- The System32 directory (LOAD_LIBRARY_SEARCH_SYSTEM32).
/DEPENDENTLOADFLAG:…
is
ignored completely and no restriction is applied to
the search path for the Win32 function
LoadLibrary()
!
Create the text file
SNAFU.C
with the following content in an arbitrary, preferable empty
directory:
// Copyright © 2016-2021, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#ifdef DLL
#pragma comment(linker, "/DEFAULTLIB:KERNEL32.LIB")
#pragma comment(linker, "/DLL")
#pragma comment(linker, "/ENTRY:_DllMainCRTStartup")
#pragma comment(linker, "/EXPORT:_SNAFU,DATA")
#pragma comment(linker, "/SUBSYSTEM:CONSOLE")
__declspec(dllexport)
const CHAR SNAFU[] = "Situation normal, all\b\b\bbut \"/DEPENDENTLOADFLAG\" fucked up!\n";
const LPCSTR szReason[4] = {"DLL_PROCESS_DETACH\n",
"DLL_PROCESS_ATTACH\n",
"DLL_THREAD_ATTACH\n",
"DLL_THREAD_DETACH\n"};
BOOL WINAPI _DllMainCRTStartup(HANDLE hModule, DWORD dwReason, LPVOID lpReserved)
{
#ifdef VERBOSE
DWORD dwConsole;
WriteConsoleA(GetStdHandle(STD_ERROR_HANDLE), szReason[dwReason], lstrlenA(szReason[dwReason]), &dwConsole, NULL);
#else
OutputDebugStringA(szReason[dwReason]);
#endif
return FALSE;
}
#else
#pragma comment(linker, "/DEFAULTLIB:KERNEL32.LIB")
#pragma comment(linker, "/ENTRY:mainCRTStartup")
#pragma comment(linker, "/SUBSYSTEM:CONSOLE")
#ifdef LOADTIME
#pragma comment(linker, "/DEFAULTLIB:SNAFU.LIB")
__declspec(dllimport)
extern const CHAR SNAFU[];
__declspec(noreturn)
VOID WINAPI mainCRTStartup(VOID)
{
#ifdef VERBOSE
DWORD dwConsole;
WriteConsoleA(GetStdHandle(STD_ERROR_HANDLE), SNAFU, lstrlenA(SNAFU), &dwConsole, NULL);
#else
OutputDebugStringA(SNAFU);
#endif
ExitProcess(0);
}
#else
__declspec(noreturn)
VOID WINAPI mainCRTStartup(VOID)
{
DWORD dwError = ERROR_SUCCESS
HMODULE hModule = LoadLibraryA("SNAFU.DLL");
if (hModule == NULL)
dwError = GetLastError();
else
if (!FreeLibrary(hModule))
dwError = GetLastError();
ExitProcess(dwError);
}
#endif // LOADTIME
#endif // DLL
#ifdef LCU
#ifndef _WIN64
const struct _LOAD_CONFIG_USED_32
{
DWORD Size;
DWORD TimeDateStamp;
WORD MajorVersion;
WORD MinorVersion;
DWORD GlobalFlagsClear;
DWORD GlobalFlagsSet;
DWORD CriticalSectionDefaultTimeout;
DWORD DeCommitFreeBlockThreshold;
DWORD DeCommitTotalFreeThreshold;
DWORD LockPrefixTable;
DWORD MaximumAllocationSize;
DWORD VirtualMemoryThreshold;
DWORD ProcessHeapFlags;
DWORD ProcessAffinityMask;
WORD CSDVersion;
WORD DependentLoadFlags;
DWORD EditList;
DWORD SecurityCookie;
DWORD SEHandlerTable;
DWORD SEHandlerCount;
#ifdef SPOILER
DWORD GuardCFCheckFunctionPointer;
DWORD GuardCFDispatchFunctionPointer;
DWORD GuardCFFunctionTable;
DWORD GuardCFFunctionCount;
DWORD GuardFlags;
#endif
} _load_config_used = {sizeof(_load_config_used)};
#else
const struct _LOAD_CONFIG_USED_64
{
DWORD Size;
DWORD TimeDateStamp;
WORD MajorVersion;
WORD MinorVersion;
DWORD GlobalFlagsClear;
DWORD GlobalFlagsSet;
DWORD CriticalSectionDefaultTimeout;
DWORD64 DeCommitFreeBlockThreshold;
DWORD64 DeCommitTotalFreeThreshold;
DWORD64 LockPrefixTable;
DWORD64 MaximumAllocationSize;
DWORD64 VirtualMemoryThreshold;
DWORD64 ProcessAffinityMask;
DWORD ProcessHeapFlags;
WORD CSDVersion;
WORD DependentLoadFlags;
DWORD64 EditList;
DWORD64 SecurityCookie;
DWORD64 SEHandlerTable;
DWORD64 SEHandlerCount;
#ifdef SPOILER
DWORD64 GuardCFCheckFunctionPointer;
DWORD64 GuardCFDispatchFunctionPointer;
DWORD64 GuardCFFunctionTable;
DWORD64 GuardCFFunctionCount;
DWORD GuardFlags;
#endif
} _load_config_used = {sizeof(_load_config_used)};
#endif // _WIN64
#endif // LCU
Note: the
entry-point function
_DllMainCRTStartup()
intentionally returns
FALSE
to force an error on
DLL
load!
Build the DLL
SNAFU.DLL
and the application
SNAFU.EXE
from the source file
SNAFU.C
created in step 1.:
SET CL=/GF /Gy /W4 /wd4100 /Zl SET LINK=/DEPENDENTLOADFLAG:0x800 /DYNAMICBASE /NXCOMPAT /OPT:REF /OSVERSION:10.01 /RELEASE CL.EXE /DDLL /LD SNAFU.C CL.EXE SNAFU.CFor details and reference see the MSDN articles Compiler Options and Linker Options.
Note: if necessary, see the
MSDN article
Use the Microsoft C++ toolset from the command line
for an introduction.
Note: both portable executables build without the MSVCRT libraries.
Note: the command lines can be copied and pasted as block into the Command Processor window!
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 19.16.27027.1 for x86 Copyright (C) Microsoft Corporation. All rights reserved. SNAFU.C Microsoft (R) Incremental Linker Version 14.16.27027.1 Copyright (C) Microsoft Corporation. All rights reserved. /DEPENDENTLOADFLAG:0x800 /DYNAMICBASE /NXCOMPAT /OPT:REF /OSVERSION:10.01 /RELEASE /out:SNAFU.dll /dll /implib:SNAFU.lib SNAFU.obj Creating library SNAFU.lib and object SNAFU.exp Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 19.16.27027.1 for x86 Copyright (C) Microsoft Corporation. All rights reserved. SNAFU.C Microsoft (R) Incremental Linker Version 14.16.27027.1 Copyright (C) Microsoft Corporation. All rights reserved. /DEPENDENTLOADFLAG:0x800 /DYNAMICBASE /NXCOMPAT /OPT:REF /OSVERSION:10.01 /RELEASE /out:SNAFU.exe SNAFU.obj
Check whether at least the application
SNAFU.EXE
built in step 2. has
/DEPENDENTLOADFLAG
set:
LINK.EXE /DUMP /HEADERS /LOADCONFIG SNAFU.EXE
Microsoft (R) COFF/PE Dumper Version 14.16.27027.1 Copyright (C) Microsoft Corporation. All rights reserved. Dump of file SNAFU.EXE PE signature found File Type: EXECUTABLE IMAGE FILE HEADER VALUES 14C machine (x86) 3 number of sections 5E224C34 time date stamp Sat Jan 18 01:07:16 2020 0 file pointer to symbol table 0 number of symbols E0 size of optional header 102 characteristics Executable 32 bit word machine OPTIONAL HEADER VALUES 10B magic # (PE32) 14.16 linker version 200 size of code 400 size of initialized data 0 size of uninitialized data 1000 entry point (00401000) 1000 base of code 2000 base of data 400000 image base (00400000 to 00403FFF) 1000 section alignment 200 file alignment 10.01 operating system version 1.00 image version 6.00 subsystem version 0 Win32 version 4000 size of image 400 size of headers F6A9 checksum 3 subsystem (Windows CUI) 8540 DLL characteristics Dynamic base NX compatible No structured exception handler Terminal Server Aware 100000 size of stack reserve 1000 size of stack commit 100000 size of heap reserve 1000 size of heap commit 0 loader flags 10 number of directories 0 [ 0] RVA [size] of Export Directory 20F0 [ 28] RVA [size] of Import Directory 0 [ 0] RVA [size] of Resource Directory 0 [ 0] RVA [size] of Exception Directory 0 [ 0] RVA [size] of Certificates Directory 3000 [ 18] RVA [size] of Base Relocation Directory 2030 [ 1C] RVA [size] of Debug Directory 0 [ 0] RVA [size] of Architecture Directory 0 [ 0] RVA [size] of Global Pointer Directory 0 [ 0] RVA [size] of Thread Storage Directory 0 [ 0] RVA [size] of Load Configuration Directory 0 [ 0] RVA [size] of Bound Import Directory 2000 [ 20] RVA [size] of Import Address Table Directory 0 [ 0] RVA [size] of Delay Import Directory 0 [ 0] RVA [size] of COM Descriptor Directory 0 [ 0] RVA [size] of Reserved Directory …
LINK.EXE
fails to generate and include the
IMAGE_LOAD_CONFIG_DIRECTORY
structure which holds the
/DEPENDENTLOADFLAG
in its DependentLoadFlags
member!
The load configuration structure (IMAGE_LOAD_CONFIG_DIRECTORY) was formerly used in very limited cases in the Windows NT operating system itself to describe various features too difficult or too large to describe in the file header or optional header of the image. Current versions of the Microsoft linker and Windows XP and later versions of Windows use a new version of this structure for 32-bit x86-based systems that include reserved SEH technology.
[…]
The Microsoft linker automatically provides a default load configuration structure to include the reserved SEH data. If the user code already provides a load configuration structure, it must include the new reserved SEH fields. Otherwise, the linker cannot include the reserved SEH data and the image is not marked as containing reserved SEH.
LINK.EXE
also fails to report this omission with an appropriate error
message!
Build the application
SNAFU.EXE
again from the source file
SNAFU.C
created in step 1., now with the preprocessor macro
LCU
(and in consequence a load configuration structure
named _load_config_used
) defined, and verify that
/DEPENDENTLOADFLAG
is properly set:
CL.EXE /DLCU SNAFU.C LINK.EXE /DUMP /HEADERS /LOADCONFIG SNAFU.EXE
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 19.16.27027.1 for x86 Copyright (C) Microsoft Corporation. All rights reserved. SNAFU.C Microsoft (R) Incremental Linker Version 14.16.27027.1 Copyright (C) Microsoft Corporation. All rights reserved. /DEPENDENTLOADFLAG:0x800 /DYNAMICBASE /NXCOMPAT /OPT:REF /OSVERSION:10.01 /RELEASE /out:SNAFU.exe SNAFU.obj Microsoft (R) Incremental Linker Version 14.16.27027.1 Copyright (C) Microsoft Corporation. All rights reserved. Dump of file SNAFU.EXE PE signature found File Type: EXECUTABLE IMAGE FILE HEADER VALUES 14C machine (x86) 3 number of sections 5E224C9D time date stamp Sat Jan 18 01:09:01 2020 0 file pointer to symbol table 0 number of symbols E0 size of optional header 102 characteristics Executable 32 bit word machine OPTIONAL HEADER VALUES 10B magic # (PE32) 14.16 linker version 200 size of code 400 size of initialized data 0 size of uninitialized data 1000 entry point (00401000) 1000 base of code 2000 base of data 400000 image base (00400000 to 00403FFF) 1000 section alignment 200 file alignment 10.01 operating system version 1.00 image version 6.00 subsystem version 0 Win32 version 4000 size of image 400 size of headers 62D1 checksum 3 subsystem (Windows CUI) 8540 DLL characteristics Dynamic base NX compatible No structured exception handler Terminal Server Aware 100000 size of stack reserve 1000 size of stack commit 100000 size of heap reserve 1000 size of heap commit 0 loader flags 10 number of directories 0 [ 0] RVA [size] of Export Directory 2190 [ 28] RVA [size] of Import Directory 0 [ 0] RVA [size] of Resource Directory 0 [ 0] RVA [size] of Exception Directory 0 [ 0] RVA [size] of Certificates Directory 3000 [ 18] RVA [size] of Base Relocation Directory 20D0 [ 1C] RVA [size] of Debug Directory 0 [ 0] RVA [size] of Architecture Directory 0 [ 0] RVA [size] of Global Pointer Directory 0 [ 0] RVA [size] of Thread Storage Directory 2020 [ 40] RVA [size] of Load Configuration Directory 0 [ 0] RVA [size] of Bound Import Directory 2000 [ 20] RVA [size] of Import Address Table Directory 0 [ 0] RVA [size] of Delay Import Directory 0 [ 0] RVA [size] of COM Descriptor Directory 0 [ 0] RVA [size] of Reserved Directory … Section contains the following load config: 00000048 size 0 time date stamp 0.00 Version 0 GlobalFlags Clear 0 GlobalFlags Set 0 Critical Section Default Timeout 0 Decommit Free Block Threshold 0 Decommit Total Free Threshold 00000000 Lock Prefix Table 0 Maximum Allocation Size 0 Virtual Memory Threshold 0 Process Heap Flags 0 Process Affinity Mask 0 CSD Version 0800 Dependent Load Flag 00000000 Edit list 00000000 Security Cookie 00000000 Safe Exception Handler Table 0 Safe Exception Handler Count …Notice the wrong size (0x40 instead of 0x48) for the
IMAGE_LOAD_CONFIG_DIRECTORY
structure in the 11th entry of the
IMAGE_DATA_DIRECTORY
array in the
IMAGE_OPTIONAL_HEADER
structure!
Execute the application
SNAFU.EXE
built in step 4. to demonstrate the bug in
Windows’ module loader:
.\SNAFU.EXE ECHO %ERRORLEVEL% NET.EXE HELPMSG %ERRORLEVEL%
1114 A dynamic link library (DLL) initialization routine failed.Oops!
ERROR_MOD_NOT_FOUND
the application
SNAFU.EXE
returns the Win32 error code 1114 alias
ERROR_DLL_INIT_FAILED
,
indicating that the Win32 function
LoadLibrary()
loaded the DLL
SNAFU.DLL
from the application directory, i.e. the search path was not limited to the
system directory
%SystemRoot%\System32\
, and
/DEPENDENTLOADFLAG:0x800
does not work
as documented!
makefile
SNAFU.MAK
performs all necessary steps shown above (and below).
/DEPENDENTLOADFLAG
DLL spoofing, DLL preloading, directory poisoning, binary planting, DLL hijacking and DLL side-loading.
The posts MS09-014: Addressing the Safari Carpet Bomb vulnerability, More information about the DLL Preloading remote attack vector, An update on the DLL-preloading remote attack vector and Triaging a DLL planting vulnerability on Microsoft’s Security Research and Defense Blog give additional information on this ubiquituous vulnerabilty.
They replied with the following statements:
The team has finished their investigation and determined the way they will address this report is via a documentation update of https://docs.microsoft.com/en-us/cpp/build/reference/dependentloadflag?view=vs-2019.Notice that there’s no statement regarding the bugs inIt wasn't supposed to say that LoadLibrary will act as LoadLibraryEx, specifically this statement:
On supported operating systems, this option has the effect of changing calls to LoadLibrary("dependent.dll") to the equivalent of LoadLibraryEx("dependent.dll", 0, load_flags). Calls to LoadLibraryEx are unaffected. This option doesn't apply recursively to DLLs loaded by your app.
LINK.EXE
!
According to the documentation changed in response to my report on
January 24, 2020,
/DEPENDENTLOADFLAG
is now supposed to affect (static)
load-time linking
instead of (dynamic)
runtime linking:
Sets the default load flags used when the operating system resolves the statically linked imports of a module.Note: the documentation still lacks (necessary) information similar to that given for the/DEPENDENTLOADFLAG[:load_flags]
load_flags
An optional integer value that specifies the load flags to apply when resolving statically linked import dependencies of the module. The default value is 0. For a list of supported flag values, see theLOAD_LIBRARY_SEARCH_*
entries in LoadLibraryEx.
[…]
[…] if you specify the link option/DEPENDENTLOADFLAG:0x800
(the value of the flagLOAD_LIBRARY_SEARCH_SYSTEM32
), then the module search path is limited to the %windows%\system32 directory.
/SAFESEH
linker option:
If you link with /NODEFAULTLIB and you want a table of safe exception handlers, you need to supply a load config struct (such as can be found in loadcfg.c CRT source file) that contains all the entries defined for Visual C++. For example: […]Note: this also applies when the /Zl compiler option is used!
Build the application
SNAFU.COM
from the source file
SNAFU.C
created in step 1., now with the preprocessor macros
LCU
and LOADTIME
defined:
CL.EXE /DLCU /DLOADTIME /FeSNAFU.COM SNAFU.CFor details and reference see the MSDN articles Compiler Options and Linker Options.
Note: if necessary, see the
MSDN article
Use the Microsoft C++ toolset from the command line
for an introduction.
Note: the portable executable
SNAFU.COM
builds without the
MSVCRT
libraries.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 19.16.27027.1 for x86 Copyright (C) Microsoft Corporation. All rights reserved. SNAFU.C Microsoft (R) Incremental Linker Version 14.16.27027.1 Copyright (C) Microsoft Corporation. All rights reserved. /DEPENDENTLOADFLAG:0x800 /DYNAMICBASE /NXCOMPAT /OPT:REF /OSVERSION:10.01 /RELEASE /out:SNAFU.COM SNAFU.obj
Execute the application
SNAFU.COM
built in step 6. to demonstrate the bug in
Windows’ module loader:
.\SNAFU.COM ECHO %ERRORLEVEL% CERTUTIL.EXE /ERROR %ERRORLEVEL%
-1073741502 0xc0000142 (NT: 0xc0000142 STATUS_DLL_INIT_FAILED) -- 3221225794 (-1073741502) Error message text: {DLL Initialization Failed} Initialization of the dynamic link library %hs failed. The process is terminating abnormally. CertUtil: -error command completed successfully.Oops!
STATUS_DLL_INIT_FAILED
,
indicating that SNAFU.DLL
is loaded from the
application directory, i.e. the search path was not limited to the
system directory
%SystemRoot%\System32\
, and
/DEPENDENTLOADFLAG:0x800
does not work
as documented!
Note: the expected error message and exit code is
but 0xC0000135 alias
STATUS_DLL_NOT_FOUND
.
Build the application SNAFU.COM
from the source file
SNAFU.C
created in step 1. again, now with the preprocessor macros
LCU
, LOADTIME
and SPOILER
defined:
CL.EXE /DLCU /DLOADTIME /DSPOILER /FeSNAFU.COM SNAFU.CFor details and reference see the MSDN articles Compiler Options and Linker Options.
Note: if necessary, see the
MSDN article
Use the Microsoft C++ toolset from the command line
for an introduction.
Note: the portable executable
SNAFU.COM
builds without the
MSVCRT
libraries.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 19.16.27027.1 for x86 Copyright (C) Microsoft Corporation. All rights reserved. SNAFU.C Microsoft (R) Incremental Linker Version 14.16.27027.1 Copyright (C) Microsoft Corporation. All rights reserved. /DEPENDENTLOADFLAG:0x800 /DYNAMICBASE /NXCOMPAT /OPT:REF /OSVERSION:10.01 /RELEASE /out:SNAFU.COM SNAFU.obj
Execute the application SNAFU.COM
built in
step 8. to demonstrate the bug fix:
.\SNAFU.COM ECHO %ERRORLEVEL% CERTUTIL.EXE /ERROR %ERRORLEVEL%
-1073741515 0xc0000135 (NT: 0xc0000135 STATUS_DLL_NOT_FOUND) -- 3221225781 (-1073741515) Error message text: {Unable To Locate Component} This application has failed to start because %hs was not found. Reinstalling the application might fix this problem. CertUtil: -error command completed successfully.-1073741515 is the decimal representation of 0xC0000135 alias
STATUS_DLL_NOT_FOUND
.
Conclusion: in order to honor the
/DEPENDENTLOADFLAG
linker option, Windows’ module loader expects an
IMAGE_LOAD_CONFIG_DIRECTORY
which includes the GuardFlags
member at least!
Note: the members
GuardCFCheckFunctionPointer
to GuardFlags
were added with Windows 8.1 to the
IMAGE_LOAD_CONFIG_DIRECTORY
structure; the member DependentLoadFlags
alias
Reserved1
is but present there since
Windows NT 3.1!
Use the X.509 certificate to send S/MIME encrypted mail.
Note: email in weird format and without a proper sender name is likely to be discarded!
I dislike
HTML (and even
weirder formats too) in email, I prefer to receive plain text.
I also expect to see your full (real) name as sender, not your
nickname.
I abhor top posts and expect inline quotes in replies.
as iswithout any warranty, neither express nor implied.
cookiesin the web browser.
The web service is operated and provided by
Telekom Deutschland GmbH The web service provider stores a session cookie
in the web
browser and records every visit of this web site with the following
data in an access log on their server(s):