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