Alter.Org.UA
 << Back Home EN en   Donate Donate

Implementation of GetProcAddress and GetModuleHandle
for Windows NT3.51/NT4/2000/XP/2003 kernel mode

Так надоело уже отвечать на вопросы вида "Как из драйвера динамически получить адрес экспортируемой ф-ции из ntdll.dll по имени этой самой ф-ции ?". Вот, граждане, держите исходники аналогов Win32'шных GetModuleHandle() и GetProcAddress() для режима ядра. В принципе, этот код должен работать не только kernel-mode, но и в user-mode при условии использования ntdll.dll.

И вот еще недавно накопал (thanks to Egor Yuzik) способ реализации GetModuleHandle(), не использующий ф-цию NtQuerySystemInformation, которая появилась только в NT4. Сама же реализация - KernelGetModuleBase3(), универсальна для всей линейки Windows NT. Идея заключается в том, что поле PVOID SectionHandle в DRIVER_OBJECT является ничем иным как указателем на структуру LDR_DATA_TABLE_ENTRY, соответствующую данному драйверу. Эти структуры связаны двунаправленым списком (см. элемент LoadOrder в LDR_DATA_TABLE_ENTRY) и описывают все загруженые модули. Корневой элемент списка (также известный под именем PsLoadedModuleList) находится сразу перед элементом LDR_DATA_TABLE_ENTRY соответствующим NTOSKRNL.EXE. Кстати, заметил интересную особенность: буфер UNICODE_STRING'ов из LdrDataTableEntry->ModuleName содержит NULL-терминатор для всех модулей кроме BOOT'овых. А у BOOT'овых за именем идет разнообразный мусор.

Основное применение этих ф-ций - написание драйверов, работающих под разными версиями Windows, и имеющих общий .SYS. В сочетании с ф-цией PsGetVersion() можно творить чудеса.

Note: Обнаружились интересные подробности, связаные с поиском модулей ntoskrnl.exe и hal.dll. А именно - при использовании KernelGetModuleBase3() эти модули называются именно так. А при KernelGetModuleBase - своими настоящими именами, т.е. если грузится другое ядро (к примеру ntkrnlpa.exe), KernelGetModuleBase("ntoskrnl.exe") вернет NULL, а KernelGetModuleBase("ntkrnlpa.exe") - правильный адрес. И точно так же с HAL'ом.



KernelGetModuleBase

NT 4.0 и старше
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 и старше
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 Автор: Alter (Александр А. Телятников) Сервер: Apache+PHP под FBSD © 2002-2019