Showing posts with label RootKit. Show all posts
Showing posts with label RootKit. Show all posts

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, ако искате да си заредите драйвърът )

Sunday, November 20, 2011

Enumerating Process list

И така първото нещо, с което смятам да се занимая е как точно да открием и обходим PsActiveProcessHead - една не-експортната променлива, която е header pointer към doubly-linked lisт, съставен от _EPROCESS структури. В някой следващ постинг може да обърна повече внимание на тази структура. Но, да се върна на въпросът какво смятам да направя:
1. Първото и най-важно е да успеем да вземем адреса на PsActiveProcessHead, което пък ще ни даде възможност да обходим листа с всички _EPROCESS структури (освен онези, които са били премахнати от някой rootkit, нещо, което в последно време не се забелязва много, но и затова мога да пиша доста). 

2. Да обходя листа и да принтна някаква информация, колкото да покажа, че е възможно това да се направи. И така, да започнем.  

Вземане на адресът на PsActiveProcessHead 

За да се сдобия с адреса, ще използвам нещо, което се знае като "KPCR trick", какво всъщност представлява това - в Windows има една структура, която е дефинирана per processor и съдържа интересна информация като например - GDT таблицата, IDT таблицата, TSS, информация за L2 кеша, PRCB структурата, която за разлика от PCR е недокументиран както и един поинтър (сори, ама мразя да казвам на поинтърите - указатели) към структура, използвана от windows debugger-а (windbg). Структурата е от тип KPCR, а пойнтър към нея съответно PKPCR. За по-детайлна информация просто погледнете в даденият хедър.

И така, след като имаме бегла пердстава с какво си имаме работа ще вземем стойността на KdVersionBlock (този пойнтър е бил въведен за пръв път в Windows Xp, преди това мисля, че е бил Reserved). Само че тук има една малка уловка, този пойнтър е дефиниран като PVOID, което значи, че няма как (всъщност лъжа!) да знаем към какво сочи, е, точно тук е магията. Малко ще излъжа и ще се наложи да ми повярвате, но този пойнтър всъщност сочи към структура от тип DBGKD_GET_VERSION64, дефиницията, на която може да бъде намерена в 'wdbgexts.h', който идва с WDK-то. Не смятам, да пействам тук дефиницията, тъй като може да бъде видяна от всеки. Но все пак, member-а (е, как да кажа  член?), който представлява интерес за нас е DebuggerDataList, за който е сложен следният коментар в дефиницията:
 //
 // Components may register a debug data block for use by
 // debugger extensions.  This is the address of the list head.
 //
 // There will always be an entry for the debugger.
 //

Което автоматично ни казва, че това е някакъв пойнтър към лист. Имайки в предвид името DebuggerDataList, частта от коментара "debugger data block" и като прочетем малко по надолу:


typedef struct _KDDEBUGGER_DATA64 { 
//ommited for brevity
} KDDEBUGGER_DATA64, *PKDDEBUGGER_DATA64;
 
То лесно може да заключим, че DebuggerDataList всъщност пойнтър към doubly linked-list, съставен от структури от тип _KDDEBUGGER_DATA64. След като знаем това и като погледнем дефиницията на тази структура би трябвало да забележим нещо интересно:

 //
 // Addresses of various kernel data structures and lists
 // that are of interest to the kernel debugger.
 //
 
 ULONG64   PsLoadedModuleList;
 ULONG64   PsActiveProcessHead;
 ULONG64   PspCidTable;
Намерихме каквото търсехме - пойнтъри към променливи, които по подразбиране не са експортнати по подразбиране. Засега ще обърна внимание само на PsActiveProcessHead. Както името подсказва, това е първият node от един doubly linked-list, който съдържа всички _EPROCESS структури на вървящите процеси в системата. От тук нататък, може да правите каквото си искате с него - да го модифицирате, така че да "скриете" процес - макар, че при един cross-view на системата, лесно може да бъдете намерени. Или пък да вземете информация, която ви е нужна.  И накрая един бонус - функция, която илюстрира току-що написаното.

void PrintProcessInformation() {
 char name[16]; //holds the process name
 char *currentProc; //pointer to current _EPROCESS, char * to facilitate pointer arithmetics
 char *currentName; 
 int currentPid;
 int startPid;
 int count = 0;
 PKPCR kpcrBlock;
 PDBGKD_GET_VERSION64 versionBlock;
 PKDDEBUGGER_DATA64 debugData;
 
 //get access to the global struct with all the variables
 kpcrBlock = __readfsdword(FIELD_OFFSET(KPCR, SelfPcr));
 versionBlock = kpcrBlock->KdVersionBlock;
 debugData = ((PLIST_ENTRY64)(versionBlock->DebuggerDataList))->Flink;
 
 //this won't be touched, ever
 name[15] = '\0';
 
 //print the first process
 currentProc = (char *)((PLIST_ENTRY)debugData->PsActiveProcessHead)->Flink - EPROCESS_LIST_OFFSET;
 startPid = currentPid = *(int *)(currentProc + EPROCESS_PID_OFFSET);
 
 strncpy(name, getName(currentProc), 15);
 DbgPrint("[Proc: %d] PID: %d Name: %s Pointer Address: 0x%p\n", count, currentPid, name, currentProc);
 

 currentProc = nextEproc(currentProc);
 currentPid = *(int *)(currentProc + EPROCESS_PID_OFFSET);
 
 while(startPid != currentPid ) {
 
  strncpy(name, getName(currentProc), 15);
  DbgPrint("[Proc: %d] PID: %d Name: %s Pointer Address: 0x%p\n", count, currentPid, name, currentProc);
  
  currentProc = nextEproc(currentProc);
  currentPid = *(int *)(currentProc + EPROCESS_PID_OFFSET);
  count++;
 
 }
}
Ето и offset-ите, които съм ползвал - те важат Win 7 x32:
/* OFFSETS ARE TAKEN FROM WINDBG */
#define EPROCESS_PID_OFFSET  0x0b4
#define EPROCESS_LIST_OFFSET 0x0b8
#define EPROCESS_NAME_OFFSET 0x16c