PspCidTable简单介绍

PspCidTable是一个全局句柄表,关于句柄表的信息可以参考我前面一篇文章。它基本符合进程句柄表的所有特质,除了它的成员是对象,而不是对象头。PspCidTable存放的是系统线程和进程对象,包括已经退出的,它的索引就是PID或TID,所以存活的进程和线程的PID和TID是不会重合的。

因为进程枚举会通过PspCidTable,所以可以通过PspCidTable隐藏进程。可以像分析普通进程句柄表一样分析PspCidTable。我的虚拟机是Win XP sp2。

先看一下它的地址:


lkd> x nt!PspCidTable
8055b360 nt!PspCidTable =  <no type information> 

在8055b360,句柄表结构是HANDLE_TABLE,dt _handle_table查看它的信息:


lkd> dt _handle_table poi(8055b360)
nt!_HANDLE_TABLE
   +0x000 TableCode        : 0xe2713001
   +0x004 QuotaProcess     : (null) 
   +0x008 UniqueProcessId  : (null) 
   +0x00c HandleTableLock  : [4] _EX_PUSH_LOCK
   +0x01c HandleTableList  : _LIST_ENTRY [ 0xe10017b4 - 0xe10017b4 ]
   +0x024 HandleContentionEvent : _EX_PUSH_LOCK
   +0x028 DebugInfo        : (null) 
   +0x02c ExtraInfoPages   : 0n0
   +0x030 FirstFree        : 0x2c0
   +0x034 LastFree         : 0xfdc
   +0x038 NextHandleNeedingPool : 0x1000
   +0x03c HandleCount      : 0n398
   +0x040 Flags            : 1
   +0x040 StrictFIFO       : 0y1

可以看到TableCode的最低两位是01,所以是两层句柄表:


lkd> dd 0xe2713000
e2713000  e1003000 e2717000 00000000 00000000

发现只用到了两个表项。以第一个表项举例:


lkd> dqs e1003000
e1003000  fffffffe`00000000
e1003008  00000000`825b9661
e1003010  00000000`825b93e9
e1003018  00000000`825b8021
e1003020  00000000`825b8d21
e1003028  00000000`825b8aa9
e1003030  00000000`825b8831
e1003038  00000000`825b85b9
e1003040  00000000`825b8341
e1003048  00000000`825b7021
e1003050  00000000`825b7da9
e1003058  00000000`825b7b31
e1003060  00000000`825b78b9
e1003068  00000000`825b7641
e1003070  00000000`825b73c9
e1003078  00000000`825b6021

随便挑两个看一下:


lkd> !object 8234f620
Object: 8234f620  Type: (825b9ca0) Process
    ObjectHeader: 8234f608 (old version)
    HandleCount: 2  PointerCount: 17
lkd> !object 8239eda8
Object: 8239eda8  Type: (825b9ad0) Thread
    ObjectHeader: 8239ed90 (old version)
    HandleCount: 1  PointerCount: 2

果然是进程和线程,可以用dt _eprocess和dt _ethread查看具体的信息。

之前看过一个程序,是通过枚举PspCidTable查找所有的EPROCESS结构体的,写的感觉有点儿罗嗦,不过还是实现了功能。


#include "ntddk.h"

typedef struct _OBJECT_HEADER {
    union {
        struct {
            LONG PointerCount;
            LONG HandleCount;
        };
        LIST_ENTRY Entry;
    };
    POBJECT_TYPE Type;
    UCHAR NameInfoOffset;
    UCHAR HandleInfoOffset;
    UCHAR QuotaInfoOffset;
    UCHAR Flags;

    union {
        PVOID QuotaBlockCharged;
    };

    PSECURITY_DESCRIPTOR SecurityDescriptor;

    QUAD Body;
} OBJECT_HEADER, *POBJECT_HEADER;

#define OBJECT_TO_OBJECT_HEADER(obj) \
CONTAINING_RECORD( (obj), OBJECT_HEADER, Body )

typedef struct _PROCESS_INFO     
{
	ULONG addr;                  
	int pid;            
	UCHAR name[16];     
	struct _PROCESS_INFO *next;  
}ProcessInfo; 
	
ProcessInfo *head, *p, *q;

ULONG GetPspCidTable()
{
  PUCHAR addr;
  PUCHAR p;
  UNICODE_STRING szNeed;
  ULONG cid;
  RtlInitUnicodeString(&szNeed,
  L"PsLookupProcessByProcessId");
  addr = (PUCHAR)MmGetSystemRoutineAddress(&szNeed);
  for (p=addr; p<addr+PAGE_SIZE; p++)
  {
    if ((*(PUSHORT)p==0x35ff)&&(*(p+6)==0xe8))
    {
      cid = *(PULONG)(p+2);
      return cid;
    }
  }
  return 0;
}

ULONG GetProcessType()
{
  ULONG p;
  ULONG type;
  p = (ULONG)PsGetCurrentProcess();
  p = (ULONG)OBJECT_TO_OBJECT_HEADER(p);
  type = *(PULONG)(p + 0x08);
  return type;
}

VOID Unload(PDRIVER_OBJECT driver)
{
  DbgPrint("Unload!\n");
}

VOID GetInformation(ULONG i)
{
  ProcessInfo *r;
  if (head == NULL)
  {
    if ((head=(ProcessInfo*) ExAllocatePool(NonPagedPool,     sizeof(ProcessInfo)))==NULL)
    {
      return;
    }
    head-> addr = 0x0;
  }
  if (head-> addr == 0)
  {
    head-> addr = i;
    p = head;
  } else
  {
    if ((r=(ProcessInfo*)ExAllocatePool(NonPagedPool,   sizeof(ProcessInfo)))==NULL)
    {
      return;
    }
    p-> next = r;
    r-> addr = i;
    r-> next = NULL;
    p = r;
  }
}

NTSTATUS DriverEntry(PDRIVER_OBJECT driver, 
	PUNICODE_STRING szReg)
{
  ULONG addr, table1, table2, type, processtype;
  ULONG hHandleTable, object;
  ULONG dwTableCode;
  ULONG NextFreeTableEntry;
  ULONG objectheader;
  ULONG flags;
  int i;
  driver-> DriverUnload = Unload;
  addr = GetPspCidTable();
	
  DbgPrint("PspCidTable address: 0x%08x",addr);
  hHandleTable = *(ULONG*)(addr);
  DbgPrint("HANDLE_TABLE : 0x%08x",hHandleTable);
  dwTableCode = *(ULONG*)(hHandleTable);
  DbgPrint("TableCode: 0x%08x\n", dwTableCode);
  if ((dwTableCode & 3)==1)
  {
    dwTableCode = dwTableCode & 0xfffffffc;
    table1 = *(ULONG*)dwTableCode;
    table2 = *(ULONG*)(dwTableCode+4);
  } else
  if ((dwTableCode & 3)==0)
  {
    table1 = dwTableCode;
    table2 = 0;
  }
  DbgPrint("table1: 0x%08x,     table2: 0x%08x\n",table1,table2);
	
  processtype = GetProcessType();
  DbgPrint("type: 0x%08x\n", processtype);
	
  for (i = 0; i <= 0x800; i++)
  {
    if (MmIsAddressValid((PULONG)(table1 + i*2)))
    {
      object = *(PULONG)(table1+2*i);
      if (MmIsAddressValid((PULONG)   (table1 + i*2 + 0x4)))
      {
        NextFreeTableEntry = *(PULONG)(table1 + i*2 + 0x4);
	if (NextFreeTableEntry == 0)
	{
	  object = ((object | 0x80000000)&0xfffffff8);
	  objectheader = (ULONG)     OBJECT_TO_OBJECT_HEADER(object);
	  if (MmIsAddressValid((PULONG)(objectheader+0x08)))
	  {
	    type = *(PULONG)(objectheader+0x08);
	    if (type == processtype)
	    {
	      flags = *(PULONG)((ULONG)object + 0x248);
	      if ((flags&0xC)!=0xC)
	        GetInformation(object);
	    }
	  }
	}
      }
    }
  }
  for (i = 0x801; i<0x4e1c; i++)
  {
    if (MmIsAddressValid((PULONG) (table2 + (i-0x800)*2)))
    {
      object = *(PULONG)(table2 + (i-0x800)*2);
    }
  }
  for (p = head; p; p=p-> next)
  {
    p-> pid = *(int*)(p-> addr + 0x84);
    strcpy(p-> name, (UCHAR*)(p-> addr + 0x174));
  }
  for (p = head; p; p=p-> next)
  {
    DbgPrint("PROCESS 0x%08x,%4d,%s\n",  p-> addr, p-> pid, p-> name);
  }
  p=head;
  q = p-> next;
  while (q!=NULL)
  {
    ExFreePool(p);
    p = q;
    q = p-> next;
  }
  ExFreePool(p);
  return STATUS_SUCCESS;
}

解释一下0xC代表什么,0xC是EPROCESS的Flags成员,WRK的ps.h可以看到


#define PS_PROCESS_FLAGS_PROCESS_EXITING 0x00000004UL // PspExitProcess entered
#define PS_PROCESS_FLAGS_PROCESS_DELETE 0x00000008UL // Delete process has been issued

所以意义不言而喻。