Sunday, January 22, 2012

Намиране на адресът на Ntoskrnl.exe в паметта.

Понякога за определени нужди е необходимо да се разбере адресът на ntoskrnl.exe. За целта могат да се направят няколко неща - като може би най-лесното е да се извика ZwQuerySystemInformation  с information class ModuleQueryInformation. Така ще имате копие на PsLoadedModuleLIst, който може да се обходи и да разберете адресът, на който е зареден кърнъла. Разбира се това не е единственият начин и определено е най-скучният. Днес ще покажа един друг метод, който е "стабилен" - тоест едва дали в бъдеще ще бъде премахнат и като цяло не разчита на volatile структури, които могат да бъдат обект на промяна при даден service pack или update. И така, за какво става на въпрос. 

PDRIVER_OBJECT
Всеки драйвър в entry point-а му бива подаден един pointer към структура от тип DRIVER_OBJECT. Тази структура е документирана в WDK-то и затова не смятам да я поствам тук, но щ обърна внимание на един интересен pointer : 
struct _DRIVER_OBJECT { 
//ommited for brevity
PVOID DriverSection;

 От декларацията му не става ясно към какво точно сочи, но всъщност ако човек си направи труда да се поразрови из сорсовете на WRK ще разбере, че всъщност това е pointer към структура от следният тип: 

typedef struct _KLDR_DATA_TABLE_ENTRY {
 LIST_ENTRY InLoadOrderLinks;
 PVOID ExceptionTable;
 ULONG ExceptionTableSize;
 // ULONG padding on IA64
 PVOID GpValue;
 PVOID NonPagedDebugInfo;
 PVOID DllBase;
 PVOID EntryPoint;
 ULONG SizeOfImage;
 UNICODE_STRING FullDllName;
 UNICODE_STRING BaseDllName;
 ULONG Flags;
 USHORT LoadCount;
 USHORT __Unused5;
 PVOID SectionPointer;
 ULONG CheckSum;
 // ULONG padding on IA64
 PVOID LoadedImports;
 PVOID PatchInformation;
} KLDR_DATA_TABLE_ENTRY, *PKLDR_DATA_TABLE_ENTRY;  
Това, което трябва да се отбележи е, че всички нови променливи, които ще бъдат добавени в декларация на тази структура, винаги се добавят в края, което означава, че съвсем спокойно можем да я ползваме. Полетата, които ни интересуват са LIST_ENTRY InLoadOrderLinks, както лесно може да се досетим - това е linked-list, които съдържа всички заредени драйвъри. BaseDllName  е името на даденият драйвър - за windows kernel-а, това винаги ще бъде ntoskrnl.exe, без значение коя версия на кърнъла сме заредили - ntoskrnl.exe или ntoskrnlpa. След като намерим entry-то, което ни трябва можем съвсем спокойно да вземем стойността на DllBase, която е адресът, на който е зареден кърнъла. От там нататък можем да парсваме този адрес като най-обикновен PE файл. Ето и малко код: 

PVOID GetNtosBaseAddr(PDRIVER_OBJECT DriverObject) {
 
 PKLDR_DATA_TABLE_ENTRY driverSection;
 PKLDR_DATA_TABLE_ENTRY ldrDataTableEntry;
 PVOID kernelBase = NULL;
 PLIST_ENTRY headEntry;
 PLIST_ENTRY currentEntry;
 UNICODE_STRING ntosString = {0};
 driverSection = (PKLDR_DATA_TABLE_ENTRY) DriverObject->DriverSection;
 RtlInitUnicodeString(&ntosString, L"ntoskrnl.exe");
 
 headEntry = driverSection->InLoadOrderLinks.Blink; //header points to prev entry
 
 currentEntry = headEntry->Flink;
 
 while(currentEntry != headEntry) {
 
  ldrDataTableEntry = CONTAINING_RECORD(currentEntry, KLDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
 
  if(RtlCompareUnicodeString(&ntosString, &ldrDataTableEntry->BaseDllName, TRUE) == 0) {
   DbgPrint("Found base address of NTOSKRNL: 0x%p\n", ldrDataTableEntry->DllBase );
 
   kernelBase = ldrDataTableEntry->DllBase;
 
   break;
  }
 
  currentEntry = currentEntry->Flink;
 }
 
 return kernelBase;
 
}

No comments:

Post a Comment