首页
社区
课程
招聘
[原创]更正一个枚举PspCidTable时的错误
发表于: 2009-2-27 14:44 4875

[原创]更正一个枚举PspCidTable时的错误

2009-2-27 14:44
4875
PspCidTable现在已经很科普了,关于其具体格式及如何枚举,网上相关文章一大堆,最多的两篇是gz1x和sudami写的。我这里只谈一个问题,就是枚举PspCidTable时pid的上限问题。很多人提到以一个Magic Number为上限,其值为0x4e1c,比如sudami的《PspCidTable杂谈》。虽然大多数时候这个上限已经大到足够用了,但事实上这个上限值是不可靠的,附张图,这个极BT的pid把我雷到了...

看到这张图后,我自己也写个了程序来验证了一下,更证实了0x4e1c这个值的不可靠。
程序如下:
// RunIt.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <windows.h>
#include <stdio.h>

DWORD WINAPI ThreadProc(LPVOID lpParameter );
HANDLE hEvent=NULL;
int main(int argc, char* argv[])
{
	
	int i=0;
	DWORD tid=0;
	hEvent=CreateEvent(NULL,FALSE,TRUE,"TEST");
	for (i=0;i<2000;i++)
	{
		
		CreateThread(NULL,0,ThreadProc,NULL,NULL,&tid);
		printf("Runing %d...TID=%d\n",i,tid);
		Sleep(20);
	}
	while (1)
	{
		Sleep(100);
	}
	return 0;
}

DWORD WINAPI ThreadProc(LPVOID lpParameter )
{
	WaitForSingleObject(hEvent,INFINITE);
	return 0;
}


虽然ProcessId和ThreadId同样在PspCidTable占位,但是创建线程的开销明显要小于创建进程,因此这里创建2000个线程,而且这个线程里等待一个无信号的事件而进入挂起状态,运行开销也要小得多,不卡系统。(当然也可以直接在创建时就挂起)运行这个程序,可以直接从返回的ThreadId明显感觉到PspCidTable的膨胀。在我把这个程序运行了四次之后,就可以看到pid=25508=0x63A4(此时其线程的tid更大),再运行Windbg可看到其pid=33736=0x83C8,很BT~

很明显了,0x4e1c这个不知道哪儿来的数(貌似是BlackLight提出的)并不可靠,正确的方法呢?
正确方法就是以PspCidTable的NextHandleNeedingPool为上限。
来看一下当前的PspCidTable:
lkd> dd pspcidtable L1
805695e0  e1001a00
lkd> dt _HANDLE_TABLE e1001a00
nt!_HANDLE_TABLE
   +0x000 TableCode        : 0xe12e4001
   +0x004 QuotaProcess     : (null)
   +0x008 UniqueProcessId  : (null)
   +0x00c HandleTableLock  : [4] _EX_PUSH_LOCK
   +0x01c HandleTableList  : _LIST_ENTRY [ 0xe1001a1c - 0xe1001a1c ]
   +0x024 HandleContentionEvent : _EX_PUSH_LOCK
   +0x028 DebugInfo        : (null)
   +0x02c ExtraInfoPages   : 0
   +0x030 FirstFree        : 0x86d0
   +0x034 LastFree         : 0x86bc
   +0x038 NextHandleNeedingPool : 0x8800
   +0x03c HandleCount      : 8380
   +0x040 Flags            : 1
   +0x040 StrictFIFO       : 0y1
可以看到NextHandleNeedingPool值为0x8800=34816,这对当前情况来说是个很合理的值。其实由Windows分配句柄表的原理可知,0x8800才是当前句柄表(PspCidTable)的句柄上限,仅当当前Pool已满,不能放下更多对象时,才会再次分配一个一级表,而NextHandleNeedingPool也将增大到0x9000,步长为一个一级表的句柄大小0x800.

在枚举PspCidTable时就不再用0x4e1c的另一个理由是(当然,前面那张图已经很有说服力了,哈哈):系统自己在枚举句柄表时使用的ExEnumHandleTable函数仍然是调用了ExpLookupHandleTableEntry来实现的。而ExpLookupHandleTableEntry的开头是这样写的:
//////////略去无关代码//////////
MaxHandle = *(volatile ULONG *) &HandleTable->NextHandleNeedingPool;

    //
    // See if this can be a valid handle given the table levels.
    //
    if (Handle.Value >= MaxHandle) {
        return NULL;        
    }
//////////略去无关代码//////////

看到了吗?系统也是以NextHandleNeedingPool的值作为当前句柄表中的句柄上限,即使你用0x4e1c来暴力枚举,在超出NextHandleNeedingPool之后也得不到哪怕一个有效的进线程对象。不用多说什么了,以后我们要忘掉0x4e1c,使用NextHandleNeedingPool作为枚举时正确的句柄上限值。

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

上传的附件:
收藏
免费 0
支持
分享
最新回复 (4)
雪    币: 222
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
学习~
两个理由足以辩驳0x4e1c
2009-2-27 16:19
0
雪    币: 7309
活跃值: (3788)
能力值: (RANK:1130 )
在线值:
发帖
回帖
粉丝
3
学                 习
2009-2-27 20:13
0
雪    币: 196
活跃值: (135)
能力值: ( LV10,RANK:170 )
在线值:
发帖
回帖
粉丝
4
来学习一些高深的东西.
2009-2-27 20:24
0
雪    币: 382
活跃值: (352)
能力值: ( LV2,RANK:140 )
在线值:
发帖
回帖
粉丝
5
以前看explookuphandletableentry代码的时候看到过,当时没注意…唉学习还是得细心呀…
2009-3-2 01:19
0
游客
登录 | 注册 方可回帖
返回
//