I am currently writing a C program that finds injected API-Hooks in imported DLLs (e.g. Ntdll). One method I'm using is comparing the size of each function from the already loaded DLL to a freshly loaded copy of the same DLL from disk.
This works perfectly on my Windows11 (build 22631) notebook. However, as soon as I switch to Windows10 (tried several builds), I can no longer retrieve the EAT (Export Address Table) of the DLL from the hard disk.
The following code (simplified without error checking) loads the DLL into memory, then finds the DOS and PE header and calculates the ExportDirectory from it, from which I can then store all function names and addresses via a loop:
// Concatenate the DLL name and Path
LPCSTR currentDllName = "ntdll.dll";
sprintf(fullDllPath, "C:\\Windows\\System32\\%s", currentDllName);
hDllFile = CreateFile(fullDllPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
// Get the file size and read the file into memory
DWORD fileSizeDiskDll = GetFileSize(hDllFile, NULL);
// Allocate Memory for loading the DLL form the disk
bufferDiskDll = malloc(fileSizeDiskDll);
// Load the DLL from the disk
DWORD bytesRead;
ReadFile(hDllFile, bufferDiskDll, fileSizeDiskDll, &bytesRead, NULL)
CloseHandle(hDllFile); // Close handle to the file as the content was copied into memory
// Locate NT Header
dosHeader = (PIMAGE_DOS_HEADER)bufferDiskDll;
imageNTHeaders = (PIMAGE_NT_HEADERS)((DWORD_PTR)bufferDiskDll + dosHeader->e_lfanew);
// Locate export address table of the loaded DLL from the disk
exportDirectoryRVA =
imageNTHeaders>OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
imageExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((DWORD_PTR)bufferDiskDll + exportDirectoryRVA);
// Create FunctionInfo struct (used to store function name and start address of each function)
functionInfosDiskDll = malloc(imageExportDirectory->NumberOfNames * sizeof(FunctionInfo));
// Iterate through imageExportDirectory to find all exported functions
parseDllPE((void*)bufferDiskDll, functionInfosDiskDll, imageExportDirectory);
The problem is that the imageExportDirectory structure contains incorrect information that I can not use (export from within the Debugger):
imageExportDirectory = {PIMAGE_EXPORT_DIRECTORY} 0xc82180
Characteristics = {DWORD} 1425706
TimeDateStamp = {DWORD} 1425725
MajorVersion = {WORD} 49507
MinorVersion = {WORD} 21
Name = {DWORD} 1425781
Base = {DWORD} 1425800
NumberOfFunctions = {DWORD} 1425825
NumberOfNames = {DWORD} 1425842
AddressOfFunctions = {DWORD} 1425870
AddressOfNames = {DWORD} 1425906
AddressOfNameOrdinals = {DWORD} 1425938
E.g.: NumberOfFunctions should be about 2500. The value 1425825 is not possible at all. The other values are also messed up. Therefore the function parseDllPE fails, which is why I didn't even show the implementation of that function in the code above.
I have tried several things, all without any luck:
- Using CreateFileMapping & MapViewOfFile to read the DLL from disk
- Using different verisons of Windows10
- Using Windows10 VMs and physical devices
- Using different DLLs like Kernel32.dll and Kernelbase.dll --> still the same outcome (Windows11 works, Windows10 doesn't)
- Disabling Windows Defender and also any exploit protections like ASLR
I cannot simply explain why it works on Windows11 but not on Windows10...