Friday, April 27, 2012

Структури и сканиране на памет.

Не съм писал от доста време - работа, занимания, чудесии. В една от предните статии стана въпрос за една структура, която съдържа адресите на някой доста често използвани структури - KdDebuggerBlog. Проблемът е,  че в 64 битовата 7-ца адресът на тази структура не е експортнат, съответно няма лесен (а дали?) начин да намерим адресът. Всъщност се оказва доста лесно да се намери адресът, използвайки малко common sense. 

Първото нещо, което може да се направи е да се disassemble-не някоя функция, за която знаем  че използва KdDebuggerBloc. Проблемите тук са основно 2:
  • Трябва да намерим функция, която използва въпросната променлива
  • Дори и да намерим такава, си е играчка да използваме някои от disassm библиотеките, за да обходим функция инструкция, по инструкция докато не стигнем до статично зададената инструкция, за която сме сигурни, че използва адресът на KdDebuggerBlock като аргумент. Например - 5-тата mov инструкция от функцията PsGetSystemProcess() (това е просто пример, таква функция в windows няма :) ).
Вторият вариант е подобен на първият, но доста по лесен за имплементиране (както ще стане ясно). За какво става на въпрос - този път пак ще сканираме паметта, но поне можем малко и ли много да съкратим search space-а и да използваме common knowledge, за да си осигурим адресът. Проблемът е да намерим needle in a haystack, като иглата е KdDebuggerBlock, а купата със сеното е kernel space-а. Разбира се абсурдно е да сканираме целият kernel space, затова ще сме умни и ще използваме следните фактори, за да намалим search space: 
  • KdDebuggerBlock  е променлива, която е част от кърнъла => ще се намира между kernel_base_address и kernel_base_address + size_of_kernel_image. 
  • Тъй като по дефиниция всички глобални променливи се записват в .data section-а на Windows автоматично ще ни интересува паметта между start_of_data_section и start_of_data_section + size_of_data_section. 
  • Тъй като знаем layout-а на структурата, виждаме че има променливи, които са глобално експортирани, тоест ако вземем някакъв блок памет, cast-нем го към типа на дадената структура и сравним дали на дадените позиции има стойностите, които имат тези глобални променливи (MmHighestUserAddres, MmSystemRangeStart и MmUserProbeAddress) то можем с голяма точно да определим дали даденият адрес представлява адресът на KdDebuggerBlock. 
     
    
    
И така ето го алгоритъмът:
  1. Намираме къде е зареден kernel image-а. 
  2. Parse-ваме PE image-a, за да намерим началото и краят на ".data" секцията. 
  3. Сканираме адресите в намереният в (2) диапазон и сравняваме дали от началото на всеки на точно определените offsets разполагаме с export-натите глобални променливи.
Ето и малко код:

void findKdDebuggerBlock() {
 
 DWORD bytesScanned;
 DWORD sectionSize = 0;
 PVOID kernelbase = GetNtosBaseAddr();
 PVOID sectionAddress = GetDataSectionAddress(kernelbase, &sectionSize);
 char *debugData = (char *)sectionAddress;
 
 DbgPrint("Starting search at address 0x%p\n", sectionAddress);
 for(bytesScanned = 0; bytesScanned < sectionSize; bytesScanned++) {
  if((PVOID)((PKDDEBUGGER_DATA64)debugData)->MmHighestUserAddress == &MmHighestUserAddress &&
   (PVOID)((PKDDEBUGGER_DATA64)debugData)->MmSystemRangeStart == &MmSystemRangeStart &&
   (PVOID)((PKDDEBUGGER_DATA64)debugData)->MmUserProbeAddress == &MmUserProbeAddress ) {
    DbgPrint("Found KDEVERSIOIN BLOCK AT 0x%p\n", debugData);
    break;
  }
 
  debugData++;
 }
 
 DbgPrint("Exhausted search space and found nothing\n");
}

Saturday, February 4, 2012

Enumerating Process list through HANDLE_TABLE

Въпросът на деня - "Как да вземем списък на всички вървящи процеси, без да използваме PsActiveProcessList". Отговорът е много прост, но нека малко встъпителни думи. "Вездесъщата" структура _EPROCESS съдържа следното поле (това се отнася за x64 Windows 7 под 32 бита офсетът е различен):

struct _EPROCESS {

//omitted for brevity

/*0x200*/     struct _HANDLE_TABLE* ObjectTable 

//omitted for brevity

}
 
Това всъщност е pointer към pointer към _HANDLE_TABLE: 
nt!_HANDLE_TABLE
   +0x000 TableCode        : Uint8B
   +0x008 QuotaProcess     : Ptr64 _EPROCESS
   +0x010 UniqueProcessId  : Ptr64 Void
   +0x018 HandleLock       : _EX_PUSH_LOCK
   +0x020 HandleTableList  : _LIST_ENTRY
   +0x030 HandleContentionEvent : _EX_PUSH_LOCK
   +0x038 DebugInfo        : Ptr64 _HANDLE_TRACE_DEBUG_INFO
   +0x040 ExtraInfoPages   : Int4B
   +0x044 Flags            : Uint4B
   +0x044 StrictFIFO       : Pos 0, 1 Bit
   +0x048 FirstFreeHandle  : Uint4B
   +0x050 LastFreeHandleEntry : Ptr64 _HANDLE_TABLE_ENTRY
   +0x058 HandleCount      : Uint4B
   +0x05c NextHandleNeedingPool : Uint4B
   +0x060 HandleCountHighWatermark : Uint4B

Идеята тук е проста - всеки процес в Windows отваря HANDLES към най-различни ресурси - файлове, други процеси, shared sections etc. Все някъде Windows трябва да пази тази информация и това се случва точно в тази структура, интересното обаче е, че всички handle таблици на процеси са вързани в един linked-list както се вижда от полето
HandleTableList
Тоест ние съвсем спокойно можем да обходим всички таблици на вървящи процеси на системата в даден момент. Други полета, които може да бъдат интересни са
QuotaProcess и UniqueProcessId

Първото съдържа pointer към _EPROCESS, на който принадлежи тази таблица, а второто съдържа PID на процесът. Следователно имаме абсолютно всичката необходима информация, която ни трябва да обходим процесите и в зависимост от даденият процес да ръчкаме по _EPROCESS обекта. Това би могло да послужи например, когато имаме процес, който е скрит от PsActiveProcessList и по-този начин да го открием. Всъщност това е един (от многото) похвати, които се използват от някои antirootkit програмки. Както винаги - ето и демо-код: 

VOID DskEnumProcessFromHandleTable() {
 
	PEPROCESS Process = NULL;
	PHANDLE_TABLE InitialHandleTable = NULL;
	PHANDLE_TABLE CurrentHandleTable = NULL;
	/*
	1. Get system process
	2. Get Handle table 
	2.1 Enumerate process of handle
	2.2 Get name of enumerated process 
	3. Travers handle table and repeat (2)
	*/
 
	/* we point to the system process */
	Process = PsInitialSystemProcess;
 
	InitialHandleTable = *(PHANDLE_TABLE *)((char *) Process + HANDLE_TABLE_OFFSET);
 
	Process = InitialHandleTable->QuotaProcess;
 
	/*QuotaProcess is alway null for system.exe, but just in case this changes in the future*/
	if(Process != NULL) {
		DbgPrint("Name of current process : %s\n", (char *) Process + PROCESS_NAME_OFFSET);
	} else {
		DbgPrint("system.exe\n");
 
	}
 
	CurrentHandleTable = CONTAINING_RECORD(InitialHandleTable->HandleTableList.Flink, HANDLE_TABLE, HandleTableList);
 
	while(InitialHandleTable != CurrentHandleTable) {
 
		Process = CurrentHandleTable->QuotaProcess;
		if(Process != NULL) {
			DbgPrint("Name of current process : %s\n", (char *) Process + PROCESS_NAME_OFFSET);
		} else {
			/*Idle's process name is always empty*/
			DbgPrint("We have the idle process\n");
 
		}
 
		CurrentHandleTable = CONTAINING_RECORD(CurrentHandleTable->HandleTableList.Flink, HANDLE_TABLE, HandleTableList);
	}
}

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;
 
}

Sunday, January 15, 2012

Обектно ориентиране в Windows (3)

И така след дългата ваканция е време да довърша мисълта си за hooking на object procedures. Както бях казал в краят на миналия постинг - сега ще дискутира _OBJECT_TYPE_INITIALIZER структурата. Ето какво представлява тя:


kd> dt _OBJECT_TYPE_INITIALIZER
ntdll!_OBJECT_TYPE_INITIALIZER
   +0x000 Length           : Uint2B
   +0x002 ObjectTypeFlags  : UChar
   +0x002 CaseInsensitive  : Pos 0, 1 Bit
   +0x002 UnnamedObjectsOnly : Pos 1, 1 Bit
   +0x002 UseDefaultObject : Pos 2, 1 Bit
   +0x002 SecurityRequired : Pos 3, 1 Bit
   +0x002 MaintainHandleCount : Pos 4, 1 Bit
   +0x002 MaintainTypeList : Pos 5, 1 Bit
   +0x002 SupportsObjectCallbacks : Pos 6, 1 Bit
   +0x004 ObjectTypeCode   : Uint4B
   +0x008 InvalidAttributes : Uint4B
   +0x00c GenericMapping   : _GENERIC_MAPPING
   +0x01c ValidAccessMask  : Uint4B
   +0x020 RetainAccess     : Uint4B
   +0x024 PoolType         : _POOL_TYPE
   +0x028 DefaultPagedPoolCharge : Uint4B
   +0x02c DefaultNonPagedPoolCharge : Uint4B
   +0x030 DumpProcedure    : Ptr32     void 
   +0x034 OpenProcedure    : Ptr32     long 
   +0x038 CloseProcedure   : Ptr32     void 
   +0x03c DeleteProcedure  : Ptr32     void 
   +0x040 ParseProcedure   : Ptr32     long 
   +0x044 SecurityProcedure : Ptr32     long 
   +0x048 QueryNameProcedure : Ptr32     long 
   +0x04c OkayToCloseProcedure : Ptr32     unsigned char 
 
Всеки обект от тип TYPE има такава структура, която представлява допълнителна информация за даденият тип. Например - от кой pool да се алокира памет по подразбиране (PoolType), дали трябва да има security descriptor  за даденият тип обект, дали е case (in)sensitive имената като цяло се подразбират. Но това, което представлява интерес са *Procedure полетата, които всъщност представляват функции, които трябва да бъдат извикани винаги когато трябва да се изпълни точно определена операция - отваряне(създаване), затваряне, парсване, изтриване etc. Ето например как изглежда тази структура за обект от тип FILE_OBJECT тоест файловете в Windows: 

kd> dt _OBJECT_TYPE_INITIALIZER 841e87a8+0x28
ntdll!_OBJECT_TYPE_INITIALIZER
   +0x000 Length           : 0x50
   +0x002 ObjectTypeFlags  : 0x11 ''
   +0x002 CaseInsensitive  : 0y1
   +0x002 UnnamedObjectsOnly : 0y0
   +0x002 UseDefaultObject : 0y0
   +0x002 SecurityRequired : 0y0
   +0x002 MaintainHandleCount : 0y1
   +0x002 MaintainTypeList : 0y0
   +0x002 SupportsObjectCallbacks : 0y0
   +0x004 ObjectTypeCode   : 1
   +0x008 InvalidAttributes : 0x130
   +0x00c GenericMapping   : _GENERIC_MAPPING
   +0x01c ValidAccessMask  : 0x1f01ff
   +0x020 RetainAccess     : 0
   +0x024 PoolType         : 0 ( NonPagedPool )
   +0x028 DefaultPagedPoolCharge : 0x400
   +0x02c DefaultNonPagedPoolCharge : 0xf8
   +0x030 DumpProcedure    : (null) 
   +0x034 OpenProcedure    : (null) 
   +0x038 CloseProcedure   : 0x82a9bbaf     void  nt!IopCloseFile+0
   +0x03c DeleteProcedure  : 0x82a81cbb     void  nt!IopDeleteFile+0
   +0x040 ParseProcedure   : 0x82ac9fe0     long  nt!IopParseFile+0
   +0x044 SecurityProcedure : 0x82aa041d     long  nt!IopGetSetSecurityObject+0
   +0x048 QueryNameProcedure : 0x82aade11     long  nt!IopQueryName+0
   +0x04c OkayToCloseProcedure : (null) 
 
Тоест при всяко затваряне на файлов обект ще се изпълнява IopCloseFile, при всяко изтриване на файл (респективно изтриването на обектът от Windows Object Manager) ще се изпълни функцията IopDeleteFile и така нататък. Следоватено ако искаме да проследяваме или дори да блокираме създаването на определен файл то можем да "инсталираме" собствена функция, която въз основа на това какво име носи обектът да решава дали да разреши или забрани операцията. А как всъщност става това? Всяка една от 7те процедури си има индивидуална сигнатура, за целите на статията ще се концентрирам само върху OpenProcedure, но по абсолютно аналогичен начин е възможно да се вземе сигнатурата за всяка една от останалите 6 функции. Ето какво трябва да представлява OB_OPEN_PROCEDURE:

typedef NTSTATUS (*OB_OPEN_METHOD) (OB_OPEN_REASON OpenReas, CHAR Unknown1, PEPROCESS Process, PVOID Object, ACCESS_MASK GrantedAccess, ULONG32 HandleCount);

И така - това което представлява интерес за нас е PVOID Object пойнтъра, който сочи към тялото на обектът, който се създава/отваря. И тъй като в случая ще hook-нем файл, то тялото ще е от тип _FILE_OBJECT. Дефиницията може да бъде намерена на сайта на майкрософт тук. Както се вижда може да използваме FileName от структурата, за да разберем кой файл точно се отваря/създава. Единственото, което остава е само да имплементираме нашата версия на OpenProcedure и да set-нем адреса и като стойност на OpenProcedure member-а. И за да не съм празнословен ето малко код: 

1. Ето една много проста имплементация на OB_OPEN_METHOD: 

NTSTATUS myOpenProcedure(OB_OPEN_REASON OpenReason, CHAR Unknown1, PEPROCESS Process, PVOID Object, ACCESS_MASK GrantedAccess, ULONG32 HandleCount) {
 
 DbgPrint("Our openProcedure routine has been invoked\n");
 
 if(Process != NULL) {
  DbgPrint("Process with name: %s is requesting file opening\n", getName(Process));
  DbgPrint("A handle for %wZ has been opened \n", &((PFILE_OBJECT)Object)->FileName);
 }
 
 return STATUS_SUCCESS;
}
 
2. Ето и методът, който прави hook-ването: 
 
void hookFileObject() {

 DbgPrint("Address of our routine is: 0x%p\n", myOpenProcedure);
 DbgPrint("Address of original routine is: 0x%p", (*IoFileObjectType)->TypeInfo.OpenProcedure);

 InterlockedExchangePointer(&(*IoFileObjectType)->TypeInfo.OpenProcedure, myOpenProcedure); 
 
 DbgPrint("!!THIS IS DONE!! The current address of routine is 0x%p\n", (*IoFileObjectType)->TypeInfo.OpenProcedure);
} 

Възможно най-тъпият 1 liner, който ни осигурява thread-safety (тази тема, особено в контекста на windows kernel) си заслужава отделна серия постинги :).
Ако случайно се чудите (или искате да правите по-fine-grined) филтриране въз основа на типа събитие, ето и декларацията на OB_OPEN_REASON:

typedef enum _OB_OPEN_REASON {  
 ObCreateHandle,  
 ObOpenHandle,  
 ObDuplicateHandle,  
 ObInheritHandle,  
 ObMaxOpenReason  
} OB_OPEN_REASON; 

Ето как макар и да имаме 1 функция, тя ще се извиква при всички по-горе посочени действия - създаване, отваряне, дупликиране на handle etc. А тази структура, до колкото съм запознат в момента не се следи от Patchguard, съответно това би трябвало да работи и на x64 (макар че все пак ще се наложи да излъжите patchguard, ако искате да си заредите драйвърът )