Alter.Org.UA
 << Back Home UK uk   Donate Donate

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()
<< Back designed by Alter aka Alexander A. Telyatnikov powered by Apache+PHP under FBSD © 2002-2024