PspCidTale的理解和思考


(一)介绍

   PspCidTable是一个句柄表,但是这个句柄表不同于普通的句柄表,它不是私有的存放的是系统中所有进程和线程的对象信息的句柄表,其索引是PID和CID。其格式和普通的句柄表完全一样,但也有不同之处:

  1. PspCidTable中存的是对象体(EProcess、EThread),而每个进程中私有的句柄表存放的是对象头(Objece_Header)
  2. PspCidTable是一个独立的句柄表,而进程中私有的句柄表是通过双向链表链接起来的。

   ZwQuerySystemInformation枚举系统句柄表时,就是通过每个进程的双向链表来枚举的。

下面时Reactos源码

    PspCidTable结构

      

0: kd> dd PspCidTable
84960bc4  8e4010a8 00000000 80000020 00000101
84960bd4  800002cc 80000024 00000000 00000113
84960be4  00000000 00000000 849121f0 00000000
84960bf4  00000000 00000000 00000000 00000008
84960c04  00000000 84960c08 84960c08 00000000
84960c14  00000000 00000000 00000000 00000000
84960c24  00000000 807cdc38 807d1c38 00000000
84960c34  00000000 00000000 00000000 00000001



0: kd>  dt _HANDLE_TABLE
nt!_HANDLE_TABLE
   +0x000 TableCode        : Uint4B
   +0x004 QuotaProcess     : Ptr32 _EPROCESS
   +0x008 UniqueProcessId  : Ptr32 Void
   +0x00c HandleLock       : _EX_PUSH_LOCK
   +0x010 HandleTableList  : _LIST_ENTRY
   +0x018 HandleContentionEvent : _EX_PUSH_LOCK
   +0x01c DebugInfo        : Ptr32 _HANDLE_TRACE_DEBUG_INFO
   +0x020 ExtraInfoPages   : Int4B
   +0x024 Flags            : Uint4B
   +0x024 StrictFIFO       : Pos 0, 1 Bit
   +0x028 FirstFreeHandle  : Uint4B
   +0x02c LastFreeHandleEntry : Ptr32 _HANDLE_TABLE_ENTRY
   +0x030 HandleCount      : Uint4B
   +0x034 NextHandleNeedingPool : Uint4B
   +0x038 HandleCountHighWatermark : Uint4B



0: kd>  dt _HANDLE_TABLE 84960bc4
nt!_HANDLE_TABLE
   +0x000 TableCode        : 0x8e4010a8
   +0x004 QuotaProcess     : (null) 
   +0x008 UniqueProcessId  : 0x80000020 Void
   +0x00c HandleLock       : _EX_PUSH_LOCK
   +0x010 HandleTableList  : _LIST_ENTRY [ 0x800002cc - 0x80000024 ]
   +0x018 HandleContentionEvent : _EX_PUSH_LOCK
   +0x01c DebugInfo        : 0x00000113 _HANDLE_TRACE_DEBUG_INFO
   +0x020 ExtraInfoPages   : 0n0
   +0x024 Flags            : 0
   +0x024 StrictFIFO       : 0y0
   +0x028 FirstFreeHandle  : 0x849121f0
   +0x02c LastFreeHandleEntry : (null) 
   +0x030 HandleCount      : 0
   +0x034 NextHandleNeedingPool : 0
   +0x038 HandleCountHighWatermark : 0

PspCidTable结构也是HANDLE_TABLE类型,其中最重要的就是第一字段TableCode,它是一个指针,它的高30位指向一个地址,存放句柄表结构,是一个4K大小的一个页面。低2位是代表当前句柄表的层数,00代表一层结构,01代表二层结构,10代表三层结构。

  • 最低层句柄表存放的项数有 4096/8 = 512 个,其中第一项做审计用,所以最多有 511个有效项。
  • 中间层句柄表存放的页表指针数有 4096/4 = 1024 个。
  • WindowsXp 及以后限定了每个进程句柄表存储的句柄表项不得超过:2^24=16777216,所以最高层最多有 2^24/(1024*512) = 32 项。
  • 二级表最大可以存放 511*1024 = 523264 个对象引用,没有特殊情况一般来说已经够了,所以我们一般只能观察到一层,两层句柄表。

(二)枚举PspCidTable句柄表信息

如何去获取PspCidTable呢?可以通过从几个函数中搜索特征码来找到地址。

PsLookupProcessThreadByCid()
PsLookupProcessByProcessId()
PsLookupThreadByThreadId()

从PsLookupProcessByProcessId中获取  ,偏移为 0x1e+2,得到PspCidTable的值。

获取得到PspCidTable以后就可以开始枚举了。

 

NTSTATUS MaGetPspCidTable(PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer, ULONG OutputBufferLength, ULONG * RertunLength)
{
	NTSTATUS Status = STATUS_UNSUCCESSFUL;
	PPSPCIDTABLE_INFO_CONTEXT PspCidTableContext = (PPSPCIDTABLE_INFO_CONTEXT)OutputBuffer;
	ULONG Count = (OutputBufferLength - sizeof(PSPCIDTABLE_INFO_CONTEXT)) / sizeof(PSPCIDTABLEINFO);

	//检查参数
	if (!InputBuffer ||
		!OutputBuffer ||
		InputBufferLength != sizeof(OPERATION_TYPE) ||
		OutputBufferLength < sizeof(PSPCIDTABLE_INFO_CONTEXT))
	{
		return STATUS_INVALID_PARAMETER;
	}
	ULONG PspCidTableAddress = GetPspCidTableAddress();

	/* PspCidTableAddress 0x84960bc4
	2: kd> dd 0x84960bc4
84960bc4  8e4010a8 00000000 80000020 00000101
84960bd4  800002cc 80000024 00000000 00000113
84960be4  00000000 00000000 849121f0 00000000
84960bf4  00000000 00000000 00000000 00000008
84960c04  00000000 84960c08 84960c08 00000000
84960c14  00000000 00000000 00000000 00000000
84960c24  00000000 807cdc38 807d1c38 00000000
84960c34  00000000 00000000 00000000 00000001

	*/

	if (PspCidTableAddress == 0)
	{
		return Status;
	}

	ULONG HandleTable = *(PULONG)PspCidTableAddress;
	ULONG TableCode = *(PULONG)HandleTable;
	ULONG Flag = TableCode & 3;   //获取TableCode的最后两位 来确定层数
     
	/*
	HandleTable 0x8e4010a8
	2: kd>  dd 8e4010a8
8e4010a8  95d2a001 00000000 00000000 00000000
8e4010b8  8e4010b8 8e4010b8 00000000 00000000
8e4010c8  00000000 00000001 00000914 95d2b830
8e4010d8  000002c6 00001000 000002fa 0020006f
8e4010e8  00010209 6e54624f 06030201 6944624f
8e4010f8  00000000 8784e570 06b87a1c 00200000
8e401108  06060203 6d4e624f 006f0049 006f0043
8e401118  0070006d 0065006c 00690074 006e006f


TableCode   0x95d2a001

Flag = TableCode & 3      = 1
	*/





	PspCidTableContext->Flag = Flag;

	TableCode &= 0xFFFFFFFC;      //如果系统采用了两层或者三层的时候,
	                              //TableCode就不是句柄表的地址了,把这个值后两位置为0之后,
								  //则是一个指向多层表的指针。
	// TableCode  0x95d2a000
	switch (Flag)
	{
	case 0:    //1层结构
		Operation1(TableCode, PspCidTableContext,Count);
		break;
	case 1:    //2层结构
		Operation2(TableCode, PspCidTableContext,Count);
		break;  
	case 2:    //3层结构
		Operation3(TableCode, PspCidTableContext,Count);
		break; 

	default:
		break;
	}
	if (Count >= PspCidTableContext->NumberOfInfo)
	{
		Status = STATUS_SUCCESS;
	}
	else
	{
		Status = STATUS_BUFFER_TOO_SMALL;
	}
	return Status;

}

ULONG Operation1(ULONG Address, PPSPCIDTABLE_INFO_CONTEXT PspCidTableContext, ULONG Count)
{
	LONG Object = 0;
	ULONG i = PspCidTableContext->NumberOfInfo;
	ULONG ItemCount = 511;
	ULONG ObjectID = 0;
	ULONG ObjectType = 0;
	ULONG HandleTableAddress = Address + 8;
	/*
	HandleTableAddress 0x8e404000 
	3: kd> dd 0x8e404000
8e404000  00000000 fffffffe 878dd799 00000000
8e404010  878dd4c1 00000000 8793c431 00000000
8e404020  8793c921 00000000 87938d49 00000000
8e404030  87938931 00000000 8792cd49 00000000
8e404040  8792ca71 00000000 87928d49 00000000
8e404050  87928a71 00000000 87914d49 00000000
8e404060  87914a71 00000000 87904d49 00000000
8e404070  87904a71 00000000 878ecd49 00000000

至于0,句柄值为0×0000代表是NULL 
刚好_HANDLE_TABLE_ENTRY的第0个表项为无效值
句柄值为0×0004有效

其实这个对象就是pid为4的system.exe进程。 EProcess 是 878dd798


记事本的PID为3708(十进制),应当位于第3708/4=927 个表项,
我们每一个1级索引表能容纳512个表项,毋庸置疑,
PID3708 应该在第2个二级索引指向的1级索引的第927-512=415=0x19F个表项(每个表项8Byte)
	*/
	do
	{
		if (Count > i)
		{
			Object = *(PULONG)HandleTableAddress;
			Object = Object & 0xFFFFFFF8;  //Object最后三位置0,Object即为EPROCESS的地址
			if (Object)
			{
				PspCidTableContext->PspCidTableInfo[i].Object = Object;
				ObjectType = MaGetObjectType(Object);
				if (ObjectType == *PsProcessType)
				{
					PspCidTableContext->PspCidTableInfo[i].ObjectType = 0;
					ObjectID = *(PULONG)(Object + GetGlobalVariable(E_UNIQUE_PROCESS_IDENTIFY_1));
				}

				if (ObjectType == *PsThreadType)
				{
					PspCidTableContext->PspCidTableInfo[i].ObjectType = 1;
					CLIENT_ID ID = { 0 };
					ID = *(PCLIENT_ID)(Object + GetGlobalVariable(E_CID_OFFSET));
					//   +0x22c Cid              : _CLIENT_ID
					ObjectID = ID.UniqueThread;
				}

				PspCidTableContext->PspCidTableInfo[i].ObjectID = ObjectID;
				i++;
			}
			
		}

		
		PspCidTableContext->NumberOfInfo++;
		HandleTableAddress += 8;
	} while ( --ItemCount>0);
	return 0;
}

ULONG Operation2(ULONG Address, PPSPCIDTABLE_INFO_CONTEXT PspCidTableContext, ULONG Count)
{
	do {
		Operation1(*(PULONG)Address, PspCidTableContext, Count);
		Address += 4;
	} while ((*(PULONG)Address) != 0);

	return 0;
}

ULONG Operation3(ULONG Address, PPSPCIDTABLE_INFO_CONTEXT PspCidTableContext, ULONG Count)
{
	do {
		Operation2(*(PULONG)Address, PspCidTableContext,Count);
		Address += 4;
	} while ((*(PULONG)Address) != 0);
	return 0;
}

ULONG GetPspCidTableAddress()
{
	ULONG PspCidTableAddress = 0;

	//搜索PsLookUpProcessByProcessId
	UNICODE_STRING v1;
	RtlInitUnicodeString(&v1, L"PsLookupProcessByProcessId");

	PVOID lpPsLookupProcessByProcessId = MmGetSystemRoutineAddress(&v1);
	if (lpPsLookupProcessByProcessId == NULL)
	{
		return PspCidTableAddress;
	}
	//Win7 x86的偏移位置
	const ULONG PSPCIDTABLE_OFFSEET = 0x1E + 0x02;
	PspCidTableAddress = *(ULONG*)((ULONG)lpPsLookupProcessByProcessId + PSPCIDTABLE_OFFSEET);
	/*
	2: kd> uf PsLookupProcessByProcessId
    nt!PsLookupProcessByProcessId:
    84a7a950 8bff            mov     edi,edi
	.......
	84a7a96e 8b3dc40b9684    mov     edi,dword ptr [nt!PspCidTable (84960bc4)]
    84a7a974 e83757feff      call    nt!ExMapHandleToPointer (84a600b0)


	PspCidTableAddress = 0x84960bc4
	*/


	return PspCidTableAddress;
}

 

(三)结果

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值