Въпросът на деня - "Как да вземем списък на всички вървящи процеси, без да използваме 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