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