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

No comments:

Post a Comment