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

Implementation of GetProcAddress and GetModuleHandle
for Windows NT3.51/NT4/2000/XP/2003/Vista/7/8 kernel mode,
both 32 and 64 bit platforms

Описание методов

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

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

KernelGetModuleBase()

Классический и наиболее законный подход - использовать Nt/ZwQuerySystemInformation(SystemModuleInfo, ...) для получения списка модулей и их параметров. Находим нужный модуль по имени, разбираем секцию экспорта и все - золотой глючик у нас в кармане. Единственное ограничение - наличие в ntdll.dll работоспособной ф-ции NtQuerySystemInformation(). В NT3 ее нет вообще, а в ReactOS она до сих пор не работает. Нехххорошо.

Также, выяснилось, что под Windows 8 таким образом невозможно получить ntdll.dll. Этот модуль исключен из списка. Там нужен другой метод KernelGetModuleBase8(), см. RSDN. Спасибо Rick Papo за информацию. По идее, должны работать KernelGetModuleBase3() и KernelGetModuleBaseByPtr().

Зато работает на 64-битных платформах. Нужно только слегка поправить (thanks to Dmitri Arkhangelski, author of ultradefrag) объявление SYSTEM_MODULE_INFORMATION.

KernelGetModuleBase3()

Есть другой (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'овых за именем идет разнообразный мусор.

Note: В процессе тестирования обнаружился еще ряд интересных и важных подробностей связаных с работой нового кода.

Подробность 1

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

Подробность 2

KernelGetModuleBase3() показалась окончательным и идеальным решением. Но в версиях младше Win 2000 поле PVOID SectionHandle в DRIVER_OBJECT всегда NULL для BOOT-драйверов (те, у который в registry Start=0 и которые грузатся ntldr'ом). А в этом случае KernelGetModuleBase3() работать не сможет (см. выше почему). Т.е. из BOOT-драйвера этой функцией воспользоваться нельзя. Также оказалось, что эту особенность использует ф-ция NtUnloadDriver() для проверки возможности выгрузки драйвера. Считается, что BOOT-драйвера принципиально невыгружаемые, а индикатором BOOT'овости служит как раз SectionHandle == NULL.

Подробность 3

Исследование исходников ReactOS показало, что там SectionHandle показывает на нечто совершенно свое. Поэтому для данной ОС такой подход не катит.

KernelGetModuleBaseByPtr()

Пришлось придумать еще один метод - KernelGetModuleBaseByPtr(). Идея следующая: компилируем свой драйвер так, чтобы импортировалась хоть одна функция (или переменная) из интересующего нас модуля (пусть для определенности это будет ntoskrnl.exe). Причем, это должна быть функция, которая там была с самых первых Windows NT. Например DbgPrint(). Берем ее адрес и начиная от него ищем осмысленный MZ/PE заголовок по убывающим адресам. Нашу жизнь существенно упрощает тот факт, что заголовок всегда находится по адресу, выровненому на границу страницы (PAGE_SIZE=4096 для семейства x86). Адрес первого найденого заголовка и будет искомым ModuleHandle'ом. Его можно использовать при вызове KernelGetProcAddress(). Этот метод будет работать во всех NT- и 9x-совместимых операционках, в user- и kernel-mode в 32- и 64-битном режимах процессора.

Note: Не забудьте, что когда вы пишете код получения адреса импортируемой функции в виде

  PVOID f_ptr=DbgPrint;

то f_ptr'у может быть присвоен адрес stub'а, принадлежащего вашему модулю:

  yourmodule!DbgPrint:
  jmp  [yourmodule!__imp__DbgPrint]

а может быть и так, что избраная вами функция просто начинается с такой инструкции. Например, начиная с Windows 2000

  ntoskrnl!IofCallDriver:
  jmp  [ptr_IopCallDriver]

Плюс еще некоторые антивирусы, firewall'ы и другие более или менее полезные драйвера могут вставлять в системные функции первой иструкцией JMP на себя. А еще некоторые могут перехватывать загрузку модулей и менять адреса импортов прямо в вашем модуле. Так вот. Это я к тому, что абсолютного решения нет и при использовании описаных методов требуется некоторая внимательность. А еще к тому, что если вы сами пишете такую хитрую систему, подумайте еще и о совместимости с другими хитрецами ;).

Для надежности я бы порекомендовал проверить, не совпадает ли полученый ModuleHandle с нашим собственным, и если совпал - значит нам подсунули stub из нашего собственного модуля и из него нужно извлечь настоящий адрес точки входа в экспортируемую функцию. Например с помощью SkipImportStub(). Когда мы уже думаем, что у нас в руках правильный ModuleHandle, еще неплохо бы убедиться, что из этого модуля экспортируется та функция, с адреса которой мы начали. Т.е. если вдруг окажется, что отталкиваясь от адреса DbgPrint мы придем к модулю, который не экспортирует такой функции, значит нас обманули.

Note 2: Exkurs предложил более правильный подход - импортировать NtBuildNumber из ntoskrnl.exe. Во-первых, эта эта константа присутствует во всех версиях WinNT, а во-вторых - не нужно проверять, является ли полученый адрес call stub'ом.

О недостатках: Помимо того, что это совсем уж хакерский метод, его недостатком смело можно назвать невозможность найти произвольный модуль по одному лишь имени.

KernelGetModuleBase8

Есть рабочий код на C++, спасибо Rick Papo. Основан на псевдокоде с RSDN. У меня самого 8-ки под рукой нет, чтобы проверить и сделать классическую C-версию (без ООП) :) и обновить библиотеку CrossNT.lib
  1. Открыть "NTDLL.DLL" с помошью ZwCreateFile()
  2. Создать на нем секцию с помошью ZwCreateSection()
  3. Запросить данные секции с помощью ZwQuerySection(), указав класс SectionImageInformation. Поле TransferAddress будет содержать искомый module base.
Thanks to Rick Papo for working code release.
typedef enum _SECTION_INFORMATION_CLASS {
      SectionBasicInformation,
      SectionImageInformation
} SECTION_INFORMATION_CLASS, *PSECTION_INFORMATION_CLASS ;

typedef struct _SECTION_IMAGE_INFORMATION {
      PVOID EntryPoint ;
      ULONG StackZeroBits ;
      ULONG StackReserved ;
      ULONG StackCommit ;
      ULONG ImageSubsystem ;
      WORD SubSystemVersionLow ;
      WORD SubSystemVersionHigh ;
      ULONG Unknown1 ;
      ULONG ImageCharacteristics ;
      ULONG ImageMachineType ;
      ULONG Unknown2 [3] ;
} SECTION_IMAGE_INFORMATION, *PSECTION_IMAGE_INFORMATION ;


extern "C" NTSYSAPI NTSTATUS NTAPI ZwQuerySection (
      IN HANDLE SectionHandle,
      IN SECTION_INFORMATION_CLASS InformationClass,
      OUT PVOID InformationBuffer,
      IN ULONG InformationBufferSize,
      OUT PULONG ReturnLength OPTIONAL
);

class _KernelFile {
private:
      bool fOpened ;
      HANDLE Handle ;
public:

      _KernelFile ( const wchar_t *pszFilename ) : fOpened(false), Handle(INVALID_HANDLE_VALUE) {
            UNICODE_STRING usLibraryName ;
            RtlInitUnicodeString ( &usLibraryName, pszFilename ) ;
            OBJECT_ATTRIBUTES ObjectAttributes ; InitializeObjectAttributes (
                  &ObjectAttributes, &usLibraryName, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL ) ;

            IO_STATUS_BLOCK IoStatusBlock ;
            NTSTATUS Status = ZwOpenFile (
                  &Handle,
                  GENERIC_READ,
                  &ObjectAttributes,
                  &IoStatusBlock,
                  FILE_SHARE_READ|FILE_SHARE_WRITE,
                  FILE_NON_DIRECTORY_FILE
            ) ;

            if ( Status == STATUS_SUCCESS )
                  fOpened = true ;

      } /* endmethod */

      ~_KernelFile ( ) {

            if ( fOpened ) {
                  ZwClose ( Handle ) ;
                  fOpened = false ;
                  Handle = INVALID_HANDLE_VALUE ;
            } /* endif */

      } /* endmethod */

      bool isValid ( ) { return ( fOpened ) ; }
      HANDLE QueryHandle ( ) { return ( Handle ) ; }
} ;

class _KernelSection {
private:
      bool fCreated ;
      HANDLE Handle ;
public:
      _KernelSection ( HANDLE FileHandle, const wchar_t *pszSectionName ) : fCreated(false), Handle(INVALID_HANDLE_VALUE) {
            UNICODE_STRING usSectionName ;
            RtlInitUnicodeString ( &usSectionName, pszSectionName ) ;
            OBJECT_ATTRIBUTES ObjectAttributes ; InitializeObjectAttributes (
                  &ObjectAttributes, &usSectionName, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL ) ;
            LARGE_INTEGER Size ;
            Size.HighPart = 0 ;
            Size.LowPart = 0x1000 ;

            NTSTATUS Status = ZwCreateSection ( &Handle, SECTION_ALL_ACCESS, &ObjectAttributes, &Size, PAGE_READONLY, 0x1000000, FileHandle ) ;
            if ( Status == STATUS_SUCCESS )
                  fCreated = true ;
      } /* endmethod */

      ~_KernelSection ( ) {
            if ( fCreated ) {
                  ZwClose ( Handle ) ;
                  fCreated = false ;
                  Handle = INVALID_HANDLE_VALUE ;
            } /* endif */
      } /* endmethod */

      bool isValid ( ) { return ( fCreated ) ; }
      HANDLE QueryHandle ( ) { return ( Handle ) ; }
} ;

PVOID FindNTDLL ( ) {
      PVOID pDLL = NULL ;
      _KernelFile File ( L"\\DosDevices\\c:\\windows\\system32\\ntdll.dll" ) ;

      if ( File.isValid ( ) ) {
            _KernelSection Section ( File.QueryHandle(), L"\\BaseNamedObjects\\NtDllSharedSection" ) ;
            if ( Section.isValid ( ) ) {
                  _SECTION_IMAGE_INFORMATION Buffer ;
                  NTSTATUS Status = ZwQuerySection ( Section.QueryHandle(), SectionImageInformation, &Buffer, sizeof(Buffer), NULL ) ;
                  if ( Status == STATUS_SUCCESS ) {
                        pDLL = Buffer.EntryPoint ;
                  } /* endif */
            } /* endif */
      } /* endif */

      return ( pDLL ) ;
}

ModuleBaseByPeb()

Для полноты картины стоит упомянуть еще один метод - KernelGetModuleBaseByPeb(). Он похож на KernelGetModuleBase3(). Отличие состоит в том, что LDR_DATA_TABLE_ENTRY берется из PEB - Process Environment Block. А т.к. PEB присущ только Win32 процессам, в kernel-mode этим методом можно воспользоваться только в соответствующем контексте (т.е. в контексте Win32 или Native приложения). В system-thread работать не будет.

Note: Вскрытие показало, что начиная с Windows 2003 R2 изменилась структура KTHREAD_HDR, из которой мы PEB получаем. См. сюда.

MiLocateExportName

В порядке общего образование: начиная с Win 2000 существует такая неэкспортируемая функция:

PVOID
MiLocateExportName (
    IN PVOID DllBase,
    IN PCHAR FunctionName
    );

Делает то же, что и KernelGetProcAddress().

Спасибо DeastSoft за информацию.

Функции и таблица применимости

Function OS Kernel mode 32bit User mode
(Win32/native)
NTReactOS9x 32-bit64-bit
KernelGetProcAddress() + + + + + +
SkipImportStub() + + + + - +
KernelGetModuleBase() 4.0 - 7.0 - - + + +
KernelGetModuleBase3() 3.51+ - - w2k+
3.51,4.0 except
for boot drivers
- -
KernelGetModuleBase8() 8.0 - - 8.0 - -
KernelGetModuleBaseByPtr() 3.51+ + + + - +
KernelGetModuleBaseByPeb() 3.51(?)
4.0+
? - in context of user-mode
thread only
(e.g. IoControl handler)
- +

Исходники


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()

SYSTEM_MODULE_INFORMATION

// Class 11
typedef struct _SYSTEM_MODULE_INFORMATION_ENTRY
{
    ULONG  Unknown1;
    ULONG  Unknown2;
#ifdef _WIN64
        ULONG Unknown3;
        ULONG Unknown4;
#endif
    PVOID  Base;
    ULONG  Size;
    ULONG  Flags;
    USHORT  Index;
    USHORT  NameLength;
    USHORT  LoadCount;
    USHORT  PathLength;
    CHAR  ImageName[256];
} SYSTEM_MODULE_INFORMATION_ENTRY, *PSYSTEM_MODULE_INFORMATION_ENTRY;

typedef struct _SYSTEM_MODULE_INFORMATION
{
    ULONG Count;
    SYSTEM_MODULE_INFORMATION_ENTRY Module[1];
} SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION;

RtlImageDirectoryEntryToData

PVOID
RtlImageDirectoryEntryToData(
    IN PVOID Base,
    IN BOOLEAN MappedAsImage,
    IN USHORT DirectoryEntry,
    OUT PULONG Size
    );

KernelGetProcAddress

PVOID
KernelGetProcAddress(
    PVOID ModuleBase,
    PCHAR pFunctionName
    )
{
    PVOID pFunctionAddress = NULL;
    
    __try
    {
#ifndef WIN9X_SUPPORT
        ULONG                 size = 0;
        PIMAGE_EXPORT_DIRECTORY exports =(PIMAGE_EXPORT_DIRECTORY)
            RtlImageDirectoryEntryToData(ModuleBase, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &size);

        ULONG                 addr = (PUCHAR)((ULONG)exports-(ULONG)ModuleBase);
#else
        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                 addr = expdir->VirtualAddress;
        PIMAGE_EXPORT_DIRECTORY exports =(PIMAGE_EXPORT_DIRECTORY)((ULONG) ModuleBase + addr);
#endif
        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 < max_name; 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()

SkipImportStub

Для всего 32-битного семейства Windows на x86 (в т.ч. ReactOS, Win9x)
PVOID
SkipImportStub(
    PVOID p
    )
{
    if(((PUCHAR)p)[0] == 0xff && ((PUCHAR)p)[1] == 0x25) {
        p = (PVOID)(*(PULONG)(*(PULONG)((ULONG)p+2)));
    }
    return p;
} // end SkipImportStub()

KernelGetModuleBaseByPtr

Для всего 32-битного семейства Windows на x86 (в т.ч. ReactOS, Win9x)
PVOID
KernelGetModuleBaseByPtr(
    IN PVOID  ptrInSection,
    IN PCHAR  ptrExportedName
    )
{
    PUCHAR p;
    PIMAGE_DOS_HEADER dos;
    PIMAGE_NT_HEADERS nt;

    p = (PUCHAR)((ULONG)ptrInSection & ~(PAGE_SIZE-1));

    for(;p;p -= PAGE_SIZE) {
        __try
        {
            dos = (PIMAGE_DOS_HEADER)p;
            if(dos->e_magic != 0x5a4d) // MZ
                continue;

            nt = (PIMAGE_NT_HEADERS)((ULONG)dos + dos->e_lfanew);
            if((ULONG)nt >= (ULONG)ptrInSection)
                continue;
            if((ULONG)nt <= (ULONG)dos)
                continue;

            if(nt->Signature != 0x00004550) // PE
                continue;
                
            if(!ptrExportedName) {
                break;
            } else {
                if(ptrInSection == KernelGetProcAddress(p, ptrExportedName)) {
                    break;
                }
            }
            p = NULL;
            break;
        }
        __except(EXCEPTION_EXECUTE_HANDLER)
        {
        }
    }
    return p;
} // end KernelGetModuleBaseByPtr()

PEB_LDR_DATA

typedef struct _PEB_LDR_DATA {
    ULONG          Length;
    BOOLEAN        Initialized;
    HANDLE         SsHandle;
    LIST_ENTRY     LoadOrder;
    LIST_ENTRY     MemoryOrder;
    LIST_ENTRY     InitializationOrder;
} PEB_LDR_DATA, *PPEB_LDR_DATA;

PEB

typedef struct _PEB {
    BOOLEAN InheritedAddressSpace;   
    BOOLEAN ReadImageFileExecOptions;
    BOOLEAN BeingDebugged;           
    BOOLEAN SpareBool;               
    HANDLE Mutant;                   

    PVOID ImageBaseAddress;
    PPEB_LDR_DATA Ldr;
    struct _RTL_USER_PROCESS_PARAMETERS *ProcessParameters;
    PVOID SubSystemData;
    PVOID ProcessHeap;
    PVOID FastPebLock;
    PVOID FastPebLockRoutine;
    PVOID FastPebUnlockRoutine;
    ULONG EnvironmentUpdateCount;
    PVOID KernelCallbackTable;
    HANDLE EventLogSection;
    PVOID EventLog;
    PPEB_FREE_BLOCK FreeList;
    ULONG TlsExpansionCounter;
    PVOID TlsBitmap;
    ULONG TlsBitmapBits[2];         // relates to TLS_MINIMUM_AVAILABLE
    PVOID ReadOnlySharedMemoryBase;
    PVOID ReadOnlySharedMemoryHeap;
    PVOID *ReadOnlyStaticServerData;
    PVOID AnsiCodePageData;
    PVOID OemCodePageData;
    PVOID UnicodeCaseTableData;

    // Useful information for LdrpInitialize
    ULONG NumberOfProcessors;
    ULONG NtGlobalFlag;

    // Passed up from MmCreatePeb from Session Manager registry key

    LARGE_INTEGER CriticalSectionTimeout;
    ULONG HeapSegmentReserve;
    ULONG HeapSegmentCommit;
    ULONG HeapDeCommitTotalFreeThreshold;
    ULONG HeapDeCommitFreeBlockThreshold;

    // Where heap manager keeps track of all heaps created for a process
    // Fields initialized by MmCreatePeb.  ProcessHeaps is initialized
    // to point to the first free byte after the PEB and MaximumNumberOfHeaps
    // is computed from the page size used to hold the PEB, less the fixed
    // size of this data structure.

    ULONG NumberOfHeaps;
    ULONG MaximumNumberOfHeaps;
    PVOID *ProcessHeaps;

    //
    //
    PVOID GdiSharedHandleTable;
    PVOID ProcessStarterHelper;
    PVOID GdiDCAttributeList;
    PVOID LoaderLock;

    // Following fields filled in by MmCreatePeb from system values and/or
    // image header.

    ULONG OSMajorVersion;
    ULONG OSMinorVersion;
    ULONG OSBuildNumber;
    ULONG OSPlatformId;
    ULONG ImageSubsystem;
    ULONG ImageSubsystemMajorVersion;
    ULONG ImageSubsystemMinorVersion;
    ULONG ImageProcessAffinityMask;
    ULONG GdiHandleBuffer[GDI_HANDLE_BUFFER_SIZE];
} PEB, *PPEB;

TEB

typedef struct _TEB {
    NT_TIB NtTib;
    PVOID  EnvironmentPointer;
    CLIENT_ID ClientId;
    PVOID ActiveRpcHandle;
    PVOID ThreadLocalStoragePointer;
    PPEB ProcessEnvironmentBlock;
    ULONG LastErrorValue;
    ULONG CountOfOwnedCriticalSections;
    PVOID CsrClientThread;
    PVOID Win32ThreadInfo;          // PtiCurrent
    ULONG Win32ClientInfo[WIN32_CLIENT_INFO_LENGTH];    // User32 Client Info
    PVOID WOW32Reserved;           // used by WOW
    LCID CurrentLocale;
    ULONG FpSoftwareStatusRegister;
    PVOID SystemReserved1[54];      // Used by FP emulator
    PVOID Spare1;                   // unused
    NTSTATUS ExceptionCode;         // for RaiseUserException
    UCHAR SpareBytes1[40];
    PVOID SystemReserved2[10];                      // Used by user/console for temp obja
    GDI_TEB_BATCH GdiTebBatch;      // Gdi batching
    ULONG gdiRgn;
    ULONG gdiPen;
    ULONG gdiBrush;
    CLIENT_ID RealClientId;
    HANDLE GdiCachedProcessHandle;
    ULONG GdiClientPID;
    ULONG GdiClientTID;
    PVOID GdiThreadLocalInfo;
    PVOID UserReserved[5];          // unused
    PVOID glDispatchTable[280];     // OpenGL
    ULONG glReserved1[26];          // OpenGL
    PVOID glReserved2;              // OpenGL
    PVOID glSectionInfo;            // OpenGL
    PVOID glSection;                // OpenGL
    PVOID glTable;                  // OpenGL
    PVOID glCurrentRC;              // OpenGL
    PVOID glContext;                // OpenGL
    ULONG LastStatusValue;
    UNICODE_STRING StaticUnicodeString;
    WCHAR StaticUnicodeBuffer[STATIC_UNICODE_BUFFER_LENGTH];
    PVOID DeallocationStack;
    PVOID TlsSlots[TLS_MINIMUM_AVAILABLE];
    LIST_ENTRY TlsLinks;
    PVOID Vdm;
    PVOID ReservedForNtRpc;
    PVOID DbgSsReserved[2];
    ULONG HardErrorsAreDisabled;
    PVOID Instrumentation[16];
    PVOID WinSockData;              // WinSock
    ULONG GdiBatchCount;
    ULONG Spare2;
    ULONG Spare3;
    ULONG Spare4;
    PVOID ReservedForOle;
    ULONG WaitingOnLoaderLock;
} TEB;

KTHREAD_HDR, KTHREAD_HDR_2003_R2

По всей видимости для всех ОС младше 2003-R2.


typedef struct _KTHREAD_HDR {

    // The dispatcher header and mutant listhead are faifly infrequently
    // referenced, but pad the thread to a 32-byte boundary (assumption
    // that pool allocation is in units of 32-bytes).

    DISPATCHER_HEADER Header;
    LIST_ENTRY MutantListHead;

    // The following fields are referenced during trap, interrupts, or
    // context switches.
    //
    // N.B. The Teb address and TlsArray are loaded as a quadword quantity
    //      on MIPS and therefore must to on a quadword boundary.

    PVOID InitialStack;
    PVOID StackLimit;
    PVOID Teb ;
    PVOID TlsArray;
    PVOID KernelStack;
    BOOLEAN DebugActive;
    UCHAR State;
    BOOLEAN Alerted[MaximumMode];
    UCHAR Iopl;
    UCHAR NpxState;
    BOOLEAN Saturation;
    SCHAR Priority;

    /*
      .........
    */
} KTHREAD_HDR, *PKTHREAD_HDR;

typedef struct _KTHREAD_HDR_2003_R2 {
    DISPATCHER_HEADER Header;
    LIST_ENTRY MutantListHead;
    PVOID InitialStack;
    PVOID StackLimit;

    /*
      Several new items here
    */

    PVOID Teb ; // offset 0x74

    /*
      .........
    */
} KTHREAD_HDR_2003_R2, *PKTHREAD_HDR_2003_R2;

KernelGetModuleBaseByPeb


__declspec (naked)
PVOID
__stdcall
GetCurrentKThread(void) 
{
  _asm {
    mov eax,fs:[124h]
    ret
  }
}

/************************/
    
    PKTHREAD_HDR KThread;
    PPEB Peb = NULL;
    PPEB_LDR_DATA Ldr = NULL;
    PTEB Teb;
    PLIST_ENTRY InitialEntry;
    PLIST_ENTRY LoadOrderListHead;
    PLDR_DATA_TABLE_ENTRY LdrDataTableEntry;

    KThread = (PKTHREAD_HDR)GetCurrentKThread();
    if(!KThread)
        return NULL;

    if(WinVer_Id() >= WIN_VER_2003_R2) {
        Teb = (PTEB)(( ((PKTHREAD_HDR_2003_R2)KThread) ->Teb);
    } else {
        Teb = (PTEB)(KThread->Teb);
    }
    if(!Teb)
        return NULL;

    Peb = Teb->ProcessEnvironmentBlock;
    if(!Peb)
        return NULL;

    Ldr = Peb->Ldr;
    if(!Ldr)
        return NULL;

    InitialEntry = Ldr->InitializationOrder.Flink;
    LdrDataTableEntry = CONTAINING_RECORD( InitialEntry,
                                           LDR_DATA_TABLE_ENTRY,
                                           InitializationOrder
                                         );

    LoadOrderListHead = LdrDataTableEntry->LoadOrder.Blink;

/************************/

Примеры

Пример 1

    PVOID NtKrnlModuleHandle;
    PVOID ptrNtBuildNumber;
    PVOID ptrIofCallDriver;

    // get address of variable inside "ntoskrnl.exe"
    ptrNtBuildNumber = &NtBuildNumber;

    // get base address of "ntoskrnl.exe" image
    NtKrnlModuleHandle = KernelGetModuleBaseByPtr(ptrNtBuildNumber, "NtBuildNumber");

    // locate exported function "IofCallDriver" inside "ntoskrnl.exe"
    ptrIofCallDriver = KernelGetProcAddress(NtKrnlModuleHandle, "IofCallDriver");

Пример 2

    PVOID NtKrnlModuleHandle;
    PVOID ptrDbgPrint;
    PVOID ptrIofCallDriver;

    // get address of function inside "ntoskrnl.exe"
    ptrDbgPrint = SkipImportStub(DbgPrint);

    // get base address of "ntoskrnl.exe" image
    NtKrnlModuleHandle = KernelGetModuleBaseByPtr(ptrDbgPrint, "DbgPrint");

    // locate exported function "IofCallDriver" inside "ntoskrnl.exe"
    ptrIofCallDriver = KernelGetProcAddress(NtKrnlModuleHandle, "IofCallDriver");

Пример 3

    PVOID OurDriverModuleHandle;

    // get base address of our driver image
    OurDriverModuleHandle = KernelGetModuleBaseByPtr(DriverEntry, "DriverEntry");

Пример 4

    PVOID NtKrnlModuleHandle;
    PVOID ptrIofCallDriver;

    // get base address of our driver image
    NtKrnlModuleHandle = KernelGetModuleBase3(DriverObject, "ntoskrnl.exe");

    // locate exported function "IofCallDriver" inside "ntoskrnl.exe"
    ptrIofCallDriver = KernelGetProcAddress(NtKrnlModuleHandle, "IofCallDriver");

2012.12.06

PS. Старые версии этой статьи тоже сохранились:

См. также

<< Back Автор: Alter (Александр А. Телятников) Сервер: Apache+PHP под FBSD © 2002-2024