Alter.Org.UA
 << Back Home UK uk   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

Methods description

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 (except KernelGetModuleBase3()) should work not only for kernel-mode, but for user-mode too if you use ntdll.dll.

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. Lets go.

KernelGetModuleBase()

Standard and the most legal solution is to use Nt/ZwQuerySystemInformation(SystemModuleInfo, ...). We obtain list of loaded modules, walk through it looking for required name and parse its export section. Thats all - golden key is in our pocket. The only limitation is presence of adequate NtQuerySystemInformation() implementation in ntdll.dll. NT3 has not it at all. ReactOS has dummy do-nothing implementation. Not too good.

Also appeared, that this method doesn't wokr under Windows 8 for ntdll.dll. It is just not listed. See description on RSDN (in Russian). Thanks to Rick Papo for information. Theoretically should work KernelGetModuleBase3() and KernelGetModuleBaseByPtr(). KernelGetModuleBase8() must work.

But it has greate advantage: it works under 64-bit platforms. We should just update (thanks to Dmitri Arkhangelski, author of ultradefrag) SYSTEM_MODULE_INFORMATION structure definition.

KernelGetModuleBase3()

There is another 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.

Note: Some interesting issues were found during further testing of new code.

Issue 1

When looking for ntoskrnl.exe and hal.dll modules with KernelGetModuleBase3(), these modules always have such names (e.g. kernel is always named ntoskrnl.exe and HAL - always hal.dll). But when we use KernelGetModuleBase(), modules are named according to their real file names. For example if ntkrnlpa.exe or ntkrnlmp.exe is loaded as kernel, KernelGetModuleBase("ntoskrnl.exe") shall return NULL, while KernelGetModuleBase("ntkrnlpa.exe") - proper address. But KernelGetModuleBase("ntoskrnl.exe") works always. The same policy is applied to HAL. Rather useful feature, I should say.

Issue 2

KernelGetModuleBase3() seemed to be an ultimate solution. But in Windows prior to Win 2000 for BOOT-drivers (those are loaded by ntldr and have Start=0 in registry) PVOID SectionHandle member of DRIVER_OBJECT is always NULL. In this case KernelGetModuleBase3() doesn't work (see above why). So, you cannot use this function from your BOOT-driver. I've also found that NtUnloadDriver() checks this value to determine if the driver can be unloaded. It is assumed, that BOOT-drivers are not unloadable and the sign of BOOT-drivers is SectionHandle == NULL.

Issue 3

ReactOS source review shown, that they use SectionHandle in a different way. Thus, such approach is inapplicable there.

KernelGetModuleBaseByPtr()

I had to invent still one approach - KernelGetModuleBaseByPtr(). The idea: we build our driver so that it imports at least one function (or variable) from the module we are interested in. Let it be ntoskrnl.exe. The function is chosen to be present in all versions on Windows NT, e.g. DbgPrint(). We take its address and start looking for valid MZ/PE header at descending addresses. Very helpful fact: header is always aligned on page boundary (PAGE_SIZE=4096 for x86 family). Address of the first valid header would ModuleHandle of required module. You can use it in KernelGetProcAddress() calls. This technique shall work under all NT- and 9x-compatible OSes, in both kernel- and user-modes in 32- and 64-bit addressing modes.

Note: Do not forget, that when you write code for getting address of imported function like this:

  PVOID f_ptr=DbgPrint;

f_ptr may receive address of the call-stub, that belongs to your module:

  yourmodule!DbgPrint:
  jmp  [yourmodule!__imp__DbgPrint]

but it is also possible, that the function you chosen, really begins with such instruction. For example starting from Windows 2000

  ntoskrnl!IofCallDriver:
  jmp  [ptr_IopCallDriver]

Plus, some antivuruses, firewalls and other useful (and not) drivers can insert at the very beginning of system functions JMP to themselves. And some other guys hook module load procedure and patch import addresses directly inside your module. I tell this to show, that there is no universal solution and you must be careful when using described mothods. And I also tell this for those people, who write such clever and tricky drivers: please, think about compatibility with other cunning ones ;).

For better reliability I would recommend to check if derived ModuleHandle is not equal to our own. If it is equal, we got stub address inside our module instead of real exported function entry point. In this case we shall extract real address from the stub. For example with SkipImportStub(). When we alredy think, that ModuleHandle is in our hands, it would be nice to check, that this module actually exports the function we have started from. If we start from address of DbgPrint and get to module which doesn't export such function, somethig goes wrong.

Note 2: Exkurs proposed better way - to import NtBuildNumber from ntoskrnl.exe. This constant is available in all versions of WinNT and we need not to check if address of NtBuildNumber is a call stub.

Limitations: along with the fact that this is a hack, significant weakness of this method is impossibility of finding in memory arbitrary module by its name.

KernelGetModuleBase8

Now we have C++ working code, thanks to Rick Papo. It is based on pseudocode from RSDN. I've no Win 8, so C-style code and CrossNT.lib update would be available later.
  1. Open "NTDLL.DLL" with ZwCreateFile()
  2. Create section for it with ZwCreateSection()
  3. Call ZwQuerySection() with class SectionImageInformation. TransferAddress in returned structure will contain 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()

To draw full picture, I'll mention still one way - KernelGetModuleBaseByPeb(). It is similar to KernelGetModuleBase3(). The difference is that, LDR_DATA_TABLE_ENTRY is taken from PEB - Process Environment Block. Since PEB belongs to Win32 processes, we can use this method in appropriate execution context only (e.g. in context of Win32 or Native aplication). It shall not work in system threads.

Note: Since Windows 2003 R2 KTHREAD_HDR structure is changed This is the structure where we take PEB pointer. Look here for new definition.

MiLocateExportName

Just for education: the following function (not exported) exists since Win 2000:

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

It has same functionality as KernelGetProcAddress().

Thanks to DeastSoft for reference.

Functions and Appliance table

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

Sources


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

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

SkipImportStub

For entire 32-bit Windows family on x86 (e.g. 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

For entire 32-bit Windows family on x86 (e.g. 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

Seems, the following is valid for OSes prior to 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;

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

Samples

Sample 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");

Sample 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");

Sample 3

    PVOID OurDriverModuleHandle;

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

Sample 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. Previous versions of this article you can find here:

See also:

<< Back designed by Alter aka Alexander A. Telyatnikov powered by Apache+PHP under FBSD © 2002-2024