当前位置:编程学习 > 网站相关 >>

基于EPROCESS结构中双向链表的进程检测方法

文/图 武汉大学计算机学院信息安全专业 段俊锋
本文介绍了Windows下进程的EPROCESS结构,并针对该结构中的两个双向链表,给出了一种检测隐藏进程的方法。

EPROCESS结构中的双向链表
对于Windows下的每一个进程,系统都会给它分配一个excutive process (EPROCESS) block。该结构包含和指向一系列其他相关的数据结构,比如每个进程的所有线程信息。EPROCESS及其所有相关数据结构都位于系统空间,只有Process environment block(peb)是位于进程地址空间,因为它包含由用户模式代码所决定的信息。具体如图1所示。

图1

在Windbg下,我们可以通过dt命令获取EPROCESS的具体格式。

lkd> dt _EPROCESS
nt!_EPROCESS
   +0x000 Pcb              : _KPROCESS
   +0x06c ProcessLock      : _EX_PUSH_LOCK
   +0x070 CreateTime       : _LARGE_INTEGER
   +0x078 ExitTime         : _LARGE_INTEGER
   +0x080 RundownProtect   : _EX_RUNDOWN_REF
   +0x084 UniqueProcessId  : Ptr32 Void
   +0x088 ActiveProcessLinks : _LIST_ENTRY
   +0x090 QuotaUsage       : [3] Uint4B
   +0x09c QuotaPeak        : [3] Uint4B
   +0x0a8 CommitCharge     : Uint4B
   +0x0ac PeakVirtualSize  : Uint4B
   +0x0b0 VirtualSize      : Uint4B
   +0x0b4 SessionProcessLinks : _LIST_ENTRY
   +0x0bc DebugPort        : Ptr32 Void
   +0x0c0 ExceptionPort    : Ptr32 Void
   +0x0c4 ObjectTable      : Ptr32 _HANDLE_TABLE
   +0x0c8 Token            : _EX_FAST_REF
……

在这个结构中有我们关心的两个数据,一个是偏移0x88处的ActiveProcessLinks,另一个是偏移0xc4处的ObjectTable。
顾名思义,ActiveProcessLinks是活动进程链表,从图1中我们可以看出,它的作用就是把一个个的EPROCESS结构链接在一起。它是一个_LIST_ENTRY的结构类型:

lkd> dt _LIST_ENTRY
nt!_LIST_ENTRY
   +0x000 Flink    : Ptr32 _LIST_ENTRY
   +0x004 Blink    : Ptr32 _LIST_ENTRY

Flink是指向当前节点之后的指针,Blink是指向当前节点之前的指针,_LIST_ENTRY是一个双向链表。活动进程可以通过这个链表来遍历,如图2所示。

图2
与ActiveProcessLinks不同的是,ObjectTable并不是一个双向链表,它指向的是进程的句柄表。而系统内所有这些句柄表结构都是通过_LIST_ENTRY类型的双向链表链接起来的,类似于ActiveProcessLinks将进程链接起来的方式。ObjectTable是一个_HANDLE_TABLE结构,具体如下:

lkd> dt _HANDLE_TABLE
nt!_HANDLE_TABLE
   +0x000 TableCode        : Uint4B
   +0x004 QuotaProcess     : Ptr32 _EPROCESS
   +0x008 UniqueProcessId  : Ptr32 Void
   +0x00c HandleTableLock  : [4] _EX_PUSH_LOCK
   +0x01c HandleTableList  : _LIST_ENTRY
   ……

偏移0x1c处就是一个类型为_LIST_ENTRY的HandleTableList,即句柄表链。活动进程也可以通过这个链表遍历,如图3所示。

图3

需要注意注意的是,ObjectTable结构体内偏移0x008处的UniqueProcessId就是当前EPROCESS对应进程的ID号。同时,ObjectTable结构体内偏移0x004处有一个指针QuotaProcess,指向了当前进程的EPROCESS结构(Sytem进程比较特殊,对应的指针为空)。因而,我们不仅能够得到进程的ID号,而且可以获得进程的其他信息。

编写进程检测的驱动程序
上面我们已经弄清楚了EPROCESS结构中的双向链表,接下来看如何编写驱动程序。
对于偏移0x088处的ActiveProcessLinks,我们先通过函数PsGetCurrentProcess获得任意进程的EPROCESS的地址,该结构偏移0x084处存放进程ID的指针,读取该指针处的ID作为循环的终止判断条件。到EPROCESS的偏移0x088处ActiveProcessLinks中读取当前进程之后的进程的ActiveProcessLinks指针,通过该指针计算其EPROCESS地址。如此循环,即可以获得所有活动进程的信息。

plist_active_procs=(PLIST_ENTRY)(eproc+FLINKOFFSET);
eproc=(ULONG)plist_active_procs->Flink;
eproc=eproc-FLINKOFFSET;
current_PID=*((int *)(eproc+PIDOFFSET));
if(current_PID<0)
//读取到System Process Idle时,这个值是一个异常的负数,因而作置零处理
current_PID=0;
DbgPrint("process id %4d,address %8x ",current_PID,eproc);

对于偏移0x0c4 处的ObjectTable,依然是先通过函数PsGetCurrentProcess获得任意进程的EPROCESS地址,进而计算得到ObjectTable的地址。记录当前的ObjectTable地址作为循环终止判断条件。到ObjectTable中偏移0x008处获得进程ID,偏移0x004处QuotaProcess读取EPROCESS地址。经过循环,即可获得所有进程的信息。

do {
pid=*(PULONG)((ULONG)HandleTableList+0x08-0x1c);
if(pid!=4)
addr=*(PULONG)((ULONG)HandleTableList+0x04-0x1c);
else
addr=0x0;
DbgPrint("process id %4d,address %8x ",pid,addr);
if(pid!=4)
addr=*(PULONG)((ULONG)HandleTableList+0x08-0x1c);
HandleTableList=HandleTableList->Flink;
} while(start_list!=HandleTableList);

由于在Windows XP下System的ObjectTable偏移0x004处QuotaProcess为null,是一个异常值,不可读,因而我将其直接置零。另外,通过这个方法无法读取System Process Idle的地址,运行时显示为0。

小结
Windows系统中的EPROCESS结构是一个非常重要的结构,与系统进程、线程管理密切相关。本文为了便于说明,只给出了Windows XP SP2下的具体实现,在Windows 2000和Windows 2003下这个方法是可行的,但我们只需在驱动中判断系统版本,然后给出不同的系统偏移量即可。同时,在EPROCESS结构中还存在着其他重要的进程特征,通过分析该结构可以找到其他进程检测的方法
补充:综合编程 , 其他综合 ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,