|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
| ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||||||||||||
f_ptr may receive address of the call-stub, that belongs to your module:
but it is also possible, that the function you chosen, really begins with such instruction. For example starting from Windows 2000
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. 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. MiLocateExportNameJust for education: the following function (not exported) exists since Win 2000:
It has same functionality as KernelGetProcAddress(). Thanks to DeastSoft for reference. Functions and Appliance table
Sources
KernelGetModuleBaseNT 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()
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;
KernelGetModuleBase3NT 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()
SkipImportStubFor 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()
KernelGetModuleBaseByPtrFor 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_R2Seems, 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;
/************************/
SamplesSample 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");
2006.08.27 PS. Previous versions of this article you can find here:
See also: | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||||||||||||