When trying to load C:\Windows\System32\user32.dll via LoadLibraryExW, it fails with the last error of ERROR_INVALID_IMAGE_HASH.
Here is how it is loaded:
HMODULE User32Lib = LoadLibraryExW(L"C:\\Windows\\System32\\user32.dll", NULL, LOAD_LIBRARY_REQUIRE_SIGNED_TARGET);
I looked at the DLL itself, and it was signed (for the version on my machine) on 8 April 2020 so it should still be valid.
Am I doing something incorrectly?
Apparently LOAD_LIBRARY_REQUIRE_SIGNED_TARGET
requires the PE image to be linked with IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY
(0x0080) in its DLL characteristics. This is a flag that forces the memory manager in the kernel to check for a digital signature when loading the image. Refer to the linker option /INTEGRITYCHECK.
Most of the system DLLs do not have this characteristic. "user32.dll" doesn't have it, but "bcrypt.dll" does:
PS C:\> $user32_hdr = get-peheader C:\Windows\System32\user32.dll
PS C:\> $bcrypt_hdr = get-peheader C:\Windows\System32\bcrypt.dll
PS C:\> '{0:x}' -f $user32_hdr.DllCharacteristics
4160
PS C:\> '{0:x}' -f $bcrypt_hdr.DllCharacteristics
41E0
I don't know much in particular about the subject of code signing and the implementation details in the loader and memory manager. I just used a debugger to discover that the load was failing with STATUS_INVALID_IMAGE_HASH
in LdrpCompleteMapModule
, after it checked for 0x80 in the DLL characteristics. From there I searched for discussions on this value and the /integritycheck
option in relation to LOAD_LIBRARY_REQUIRE_SIGNED_TARGET
. I found a few unofficial references that claimed the latter requires the former. So I wrote a script to dump the DLL characteristics of system DLLs in order to find one that has the IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY
flag. Having found "bcrypt.dll" and checked that it wasn't already loaded, I confirmed that loading it with LOAD_LIBRARY_REQUIRE_SIGNED_TARGET
does work.