Implementation of GetProcAddress and GetModuleHandle for Windows NT3.51/NT4/2000/XP/2003 kernel mode
I'm astonished, so many people ask questions like "How to get address of some exported function from ntdll.dll
dynamically, when we know only the name of this function ?".
People, please find the answer below. These are sources of kernel-mode implementation of
Win32 GetModuleHandle()
and GetProcAddress().
In general, this code should work not only for kernel-mode, but for user-mode too if
you use ntdll.dll.
I've found still one implementation of GetModuleHandle() (thanks to Egor Yuzik). It doesn't use
NtQuerySystemInformation which appeared in NT4. The implementation itself -
KernelGetModuleBase3(), is universal for
all Windows NT-family OS'es. The main idea is to use undocumented filed PVOID SectionHandle
from DRIVER_OBJECT structure. This is actually pointer to LDR_DATA_TABLE_ENTRY describing
corresponding driver. All such structures are linked in bidirectional list
(see LoadOrder member in
LDR_DATA_TABLE_ENTRY structure)
and cover all modules loaded in memory.
The root record of the list (also called PsLoadedModuleList) is located
just before LDR_DATA_TABLE_ENTRY structure corresponding to NTOSKRNL.EXE.
Main appliance of these functions is writing drivers with unified binary (.SYS) those
work under different NT OS versions. Together with PsGetVersion() they give you
a power to work wonders.
Note:
There are some interesting issues with finding ntoskrnl.exe and hal.dll modules.
When KernelGetModuleBase3() is used, these modules always have such names. But when we use
KernelGetModuleBase modules are named according to their real file names. For example if
ntkrnlpa.exe is loaded as kernel, KernelGetModuleBase("ntoskrnl.exe") shall return NULL,
while KernelGetModuleBase("ntkrnlpa.exe") - proper address. The same policy is applied to HAL.
KernelGetModuleBase
NT 4.0 and higher
PVOID
KernelGetModuleBase(
PCHAR pModuleName
)
{
PVOID pModuleBase = NULL;
PULONG pSystemInfoBuffer = NULL;
__try
{
NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES;
ULONG SystemInfoBufferSize = 0;
status = ZwQuerySystemInformation(SystemModuleInfo,
&SystemInfoBufferSize,
0,
&SystemInfoBufferSize);
if (!SystemInfoBufferSize)
return NULL;
pSystemInfoBuffer = (PULONG)ExAllocatePool(NonPagedPool, SystemInfoBufferSize*2);
if (!pSystemInfoBuffer)
return NULL;
memset(pSystemInfoBuffer, 0, SystemInfoBufferSize*2);
status = ZwQuerySystemInformation(SystemModuleInfo,
pSystemInfoBuffer,
SystemInfoBufferSize*2,
&SystemInfoBufferSize);
if (NT_SUCCESS(status))
{
PSYSTEM_MODULE_ENTRY pSysModuleEntry =((PSYSTEM_MODULE_INFORMATION)(pSystemInfoBuffer))->Module;
ULONG i;
for (i = 0; i <((PSYSTEM_MODULE_INFORMATION)(pSystemInfoBuffer))->Count; i++)
{
if (_stricmp(pSysModuleEntry[i].ModuleName + pSysModuleEntry[i].ModuleNameOffset, pModuleName) == 0)
{
pModuleBase = pSysModuleEntry[i].ModuleBaseAddress;
break;
}
}
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
pModuleBase = NULL;
}
if(pSystemInfoBuffer) {
ExFreePool(pSystemInfoBuffer);
}
return pModuleBase;
} // end KernelGetModuleBase()
KernelGetProcAddress
PVOID
KernelGetProcAddress(
PVOID ModuleBase,
PCHAR pFunctionName
)
{
PVOID pFunctionAddress = NULL;
__try
{
PIMAGE_DOS_HEADER dos =(PIMAGE_DOS_HEADER) ModuleBase;
PIMAGE_NT_HEADERS nt =(PIMAGE_NT_HEADERS)((ULONG) ModuleBase + dos->e_lfanew);
PIMAGE_DATA_DIRECTORY expdir =(PIMAGE_DATA_DIRECTORY) nt->OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_EXPORT;
ULONG size = expdir->Size;
ULONG addr = expdir->VirtualAddress;
PIMAGE_EXPORT_DIRECTORY exports =(PIMAGE_EXPORT_DIRECTORY)((ULONG) ModuleBase + addr);
PULONG functions =(PULONG)((ULONG) ModuleBase + exports->AddressOfFunctions);
PSHORT ordinals =(PSHORT)((ULONG) ModuleBase + exports->AddressOfNameOrdinals);
PULONG names =(PULONG)((ULONG) ModuleBase + exports->AddressOfNames);
ULONG max_name =exports->NumberOfNames;
ULONG max_func =exports->NumberOfFunctions;
ULONG i;
for (i = 0; i < exports->AddressOfNames; i++)
{
ULONG ord = ordinals[i];
if(i >= max_name || ord >= max_func) {
return NULL;
}
if (functions[ord] < addr || functions[ord] >= addr + size)
{
if (strcmp((PCHAR) ModuleBase + names[i], pFunctionName) == 0)
{
pFunctionAddress =(PVOID)((PCHAR) ModuleBase + functions[ord]);
break;
}
}
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
pFunctionAddress = NULL;
}
return pFunctionAddress;
} // end KernelGetProcAddress()
LDR_DATA_TABLE_ENTRY
typedef struct _LDR_DATA_TABLE_ENTRY {
LIST_ENTRY LoadOrder;
LIST_ENTRY MemoryOrder;
LIST_ENTRY InitializationOrder;
PVOID ModuleBaseAddress;
PVOID EntryPoint;
ULONG ModuleSize;
UNICODE_STRING FullModuleName;
UNICODE_STRING ModuleName;
ULONG Flags;
USHORT LoadCount;
USHORT TlsIndex;
union {
LIST_ENTRY Hash;
struct {
PVOID SectionPointer;
ULONG CheckSum;
};
};
ULONG TimeStamp;
} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
KernelGetModuleBase3
NT 3.51 and higher
PLIST_ENTRY g_LoadOrderListHead = NULL;
PVOID
KernelGetModuleBase3(
PDRIVER_OBJECT DriverObject,
PCHAR pModuleName
)
{
PVOID pModuleBase = NULL;
PLIST_ENTRY Next;
PLIST_ENTRY LoadOrderListHead;
UNICODE_STRING uStr;
PLDR_DATA_TABLE_ENTRY LdrDataTableEntry;
PLDR_DATA_TABLE_ENTRY LdrDataTableEntry0;
ULONG len;
BOOLEAN FreeUstr = FALSE;
uStr.Buffer = NULL;
__try
{
if(!g_LoadOrderListHead) {
LdrDataTableEntry0 = (PLDR_DATA_TABLE_ENTRY)DriverObject->DriverSection;
uStr.Length = sizeof(L"NTOSKRNL.EXE")-sizeof(WCHAR);
uStr.MaximumLength = sizeof(L"NTOSKRNL.EXE");
uStr.Buffer = L"NTOSKRNL.EXE";
Next = LdrDataTableEntry0->LoadOrder.Blink;
while ( TRUE ) {
LdrDataTableEntry = CONTAINING_RECORD( Next,
LDR_DATA_TABLE_ENTRY,
LoadOrder
);
Next = Next->Blink;
if(!LdrDataTableEntry->ModuleName.Buffer) {
return NULL;
}
if(RtlCompareUnicodeString(&LdrDataTableEntry->ModuleName, &uStr, TRUE) == 0)
{
LoadOrderListHead = Next;
break;
}
if(LdrDataTableEntry == LdrDataTableEntry0)
return NULL;
}
g_LoadOrderListHead = LoadOrderListHead;
} else {
LoadOrderListHead = g_LoadOrderListHead;
}
len = strlen(pModuleName);
if(!len)
return NULL;
len = (len+1)*sizeof(WCHAR);
uStr.MaximumLength = (USHORT)len;
uStr.Length = (USHORT)len - sizeof(WCHAR);
uStr.Buffer = (PWCHAR)ExAllocatePool(NonPagedPool, len);
FreeUstr = TRUE;
swprintf(uStr.Buffer, L"%S", pModuleName);
Next = LoadOrderListHead->Flink;
while ( Next != LoadOrderListHead ) {
LdrDataTableEntry = CONTAINING_RECORD( Next,
LDR_DATA_TABLE_ENTRY,
LoadOrder
);
if(RtlCompareUnicodeString(&LdrDataTableEntry->ModuleName, &uStr, TRUE) == 0)
{
pModuleBase = LdrDataTableEntry->ModuleBaseAddress;
break;
}
Next = Next->Flink;
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
pModuleBase = NULL;
}
if(FreeUstr && uStr.Buffer) {
ExFreePool(uStr.Buffer);
}
return pModuleBase;
} // end KernelGetModuleBase3()
|