原文发表于百度空间,2009-03-31
==========================================================================

理论结合实践,这是我一贯的学习方法~~
实验目的:以实验的方式观察PspCidTable的变化,从中了解Windows句柄表的分配过程.
实验器材:Windbg,RunIt(一个可控的不断创建线程的程序),DebugView
知识回顾:

如图所示,句柄表的结构根据TableLevel来确定:
TableLevel为0时,CapturedTable是一级表(蓝箭头)
TableLevel为1时,CapturedTable是二级表,CapturedTable[i]是一级表(绿箭头)
TableLevel为2时,CapturedTable是三级表,CapturedTable[i]是二级表,CapturedTable[i][j]是一级表(红箭头)
三级表的内容是二级表指针,二级表的内容是一级表指针,一级表中放的才是对象及访问属性(HANDLE_TABLE_ETNRY结构)

准备工作:获取PspCidTable的基本信息

lkd> dd PspCidTable l1
8055a360 e1001810 //获取PspCidTable的地址
lkd> dt _HANDLE_TABLE e1001810
nt!_HANDLE_TABLE
+0x000 TableCode : 0xe1003000 //表基址为0xe1003000,一级表
+0x004 QuotaProcess : (null)
+0x008 UniqueProcessId : (null)
+0x00c HandleTableLock : [] _EX_PUSH_LOCK
+0x01c HandleTableList : _LIST_ENTRY [ 0xe100182c - 0xe100182c ]
+0x024 HandleContentionEvent : _EX_PUSH_LOCK
+0x028 DebugInfo : (null)
+0x02c ExtraInfoPages :
+0x030 FirstFree : 0x308
+0x034 LastFree : 0x34c
+0x038 NextHandleNeedingPool : 0x800 //当前的句柄上限
+0x03c HandleCount :
+0x040 Flags :
+0x040 StrictFIFO : 0y1

此时可以观察到PspCidTable=e1001810,当前TableCode为0xe1003000,低两位表明是一级表,表地址为0xe1003000

lkd> dd 0xe1003000
e1003000 fffffffe 821bb661
e1003010 821bb3e9 821ba021
e1003020 821bad21 821baaa9
e1003030 821ba831 821ba5b9
e1003040 821ba341 821b9021
e1003050 821b9da9 821b9b31
e1003060 821b98b9 821b9641
e1003070 821b93c9 821b8021

这时可以看到一级表存放的进线程对象了

实验一:观察句柄表的升级
由于二级表升级为三级表需要极大的句柄容量,因此我们通常只能观察到句柄表由一级表升为二级表的过程
运行RunIt.exe,按回车不断创建线程,直至新线程的ThreadId大于当前句柄表的上限0x800.

此时再观察PspCidTable:

lkd> dt _HANDLE_TABLE e1001810
nt!_HANDLE_TABLE
+0x000 TableCode : 0xe11a4001 //这时已经为二级表了
+0x004 QuotaProcess : (null)
+0x008 UniqueProcessId : (null)
+0x00c HandleTableLock : [] _EX_PUSH_LOCK
+0x01c HandleTableList : _LIST_ENTRY [ 0xe100182c - 0xe100182c ]
+0x024 HandleContentionEvent : _EX_PUSH_LOCK
+0x028 DebugInfo : (null)
+0x02c ExtraInfoPages :
+0x030 FirstFree : 0x860
+0x034 LastFree : 0x38c
+0x038 NextHandleNeedingPool : 0x1000 //句柄上限达到了0x800*2=0x1000
+0x03c HandleCount :
+0x040 Flags :
+0x040 StrictFIFO : 0y1

这时的TableCode低两位表时现在是二级表,掩去低两位就是二级表的地址0xe11a4000了

lkd> dd 0xe11a4000 //观察二级表的内容
e11a4000 e1003000 e11b5000
e11a4010
e11a4020
e11a4030
e11a4040
e11a4050
e11a4060
e11a4070

可以看到,原来的一级表e1003000已经成为了二级表中的第一个元素.同时新分配了一个一级表为e11b5000.这样,句柄表的升级就完成了

实验二:观察新分配的句柄表是如何填充的
前面已经分析过,新分配的句柄表被填充成一个有序的FreeHandle序列.
观察新分配的这个二级表:

lkd> dd e11b5000
e11b5000 fffffffe 81f008b9
e11b5010 81f00641 81f003c9
e11b5020 81f5d021 81f5dda9
e11b5030 81f5db31 81f5d8b9
e11b5040 81f5d641 81f5d3c9
e11b5050 81eff021 81effda9
e11b5060 81effb31 81eff8b9
e11b5070 81eff641 81eff3c9 //RunIt创建的最后一个线程的ETHREAD在e11b5078处
lkd> dd
e11b5080 //这里的一部分句柄也被使用过了,因为可能别的进程也创建了线程
e11b5090
e11b50a0 0000038c 81f5cda9
e11b50b0 0000084c
e11b50c0
e11b50d0 0000086c
e11b50e0
e11b50f0 0000087c
lkd>
e11b5100
e11b5110 0000088c
e11b5120
e11b5130 0000089c 000008a0
e11b5140 000008a4 000008a8
e11b5150 000008ac 000008b0
e11b5160 000008b4 000008b8
e11b5170 000008bc 000008c0

由图可知,最后一个ThreadId=0x83c,那么它在第二个表中的偏移是e11b5000+(0x83c-0x800)*2=e11b5078

从e11b5080到e11b50c0这部分的内容表明该范围内的部分句柄已经被使用过且又释放了(如果想避免该问题,你可以使用livekd的方式进行本实验,这样中断到调试器时就不会有其它动作来干扰我们的观察),但是尚未影响到e11b50c0之后的部分.
来观察这里:

e11b50c0
e11b50d0 0000086c
e11b50e0
e11b50f0 0000087c

e11b50c0作为二级表中的第二个一级表,它所对应的句柄为:

(e11b50c0-e11b5000)/2+0x800*(2-1)=0x860 //如果了解了句柄表的基本结构,这个计算很容易理解
而它的NextFreeHadleTableEntry则指向它紧挨着的下一个HANDLE_TABLE_ENTRY的所对应的句柄0x864
而且很容易看出0x864,0x868,0x86c...构成了一个等差数列.
这个结果可以与前面对ExpAllocateLowLevelTable函数的分析对比,两者是完全一致的.
附Runit程序的源码:

// RunIt.cpp : Defines the entry point for the console application.
//
#include <windows.h>
#include <stdio.h> DWORD WINAPI ThreadProc(LPVOID lpParameter );
HANDLE hEvent = NULL;
int main(int argc, char* argv[])
{ int i=;
DWORD tid=,oldtid=;
printf("MyPID=%d 0x%x\n",GetCurrentProcessId(),GetCurrentProcessId());
printf("每按一次回车键将产生一个新线程.\n");
hEvent=CreateEvent(NULL,FALSE,TRUE,"TEST");
for (i=;i<;i++)
{
getchar();
CreateThread(NULL,,ThreadProc,NULL,NULL,&tid);
printf("Runing %d...\tTID=%4d\t0x%x",i,tid,tid);
if (tid==oldtid)//已经达到当前进程所能创建线程数量的上限
{
break;
}
oldtid=tid;
//Sleep(20); }
while ()
{
Sleep();//停在这里
}
return ;
}
DWORD WINAPI ThreadProc(LPVOID lpParameter )
{
WaitForSingleObject(hEvent,INFINITE);
return ;
}

【旧文章搬运】Windows句柄表分配算法分析(实验部分)的更多相关文章

  1. 【旧文章搬运】Windows句柄表分配算法分析(一)

    原文发表于百度空间,2009-03-30========================================================================== 阅读提示: ...

  2. 【旧文章搬运】Windows句柄表分配算法分析(三)

    原文发表于百度空间,2009-03-30========================================================================== 三.当需要 ...

  3. 【旧文章搬运】Windows句柄表分配算法分析(二)

    原文发表于百度空间,2009-03-30========================================================================== 四.句柄表 ...

  4. 【旧文章搬运】Windows句柄分配算法(一)

    原文发表于百度空间,2009-04-04========================================================================== 分析了Wi ...

  5. 【旧文章搬运】Windows句柄分配算法(二)

    原文发表于百度空间,2009-04-04========================================================================== 在创建句柄 ...

  6. 【旧文章搬运】Windows句柄表格式

    原文发表于百度空间,2009-02-28========================================================================== 句柄是Wi ...

  7. 【旧文章搬运】Windows内核常见数据结构(进程相关)

    原文发表于百度空间,2008-7-24========================================================================== 进程的相关结 ...

  8. 【旧文章搬运】更正一个枚举PspCidTable时的错误

    原文发表于百度空间及看雪论坛,2009-02-27 看雪论坛地址:https://bbs.pediy.com/thread-82919.htm============================= ...

  9. 【旧文章搬运】深入分析Win7的对象引用跟踪机制

    原文发表于百度空间及看雪论坛,2010-09-12 看雪论坛地址:https://bbs.pediy.com/thread-120296.htm============================ ...

随机推荐

  1. Windows下部署多个Tomcat

    编辑bin/startup.bat SET JAVA_HOME=...(JDK所在路径) SET CATALINA_HOME=...(Tomcat解压的路径) 编辑server.xml文件 <! ...

  2. 【Todo】git的fast forward & git命令学习 & no-ff

    git的fast-forward在之前的文章有介绍过,但是介绍的不细: http://www.cnblogs.com/charlesblc/p/5953066.html fast-forward方式就 ...

  3. Codeforces Round #266 (Div. 2) C. Number of Ways

    You've got array a[1], a[2], ..., a[n], consisting of n integers. Count the number of ways to split ...

  4. Answer&#39;s Question about pointer

    When you create a new pointer, this will be in heap until you delete it.  So what you said is sort o ...

  5. hdoj-1856-More is better【并查集】

    More is better Time Limit: 5000/1000 MS (Java/Others) Memory Limit: 327680/102400 K (Java/Others) To ...

  6. BUPT复试专题—众数(2014)

    题目描述 有一个长度为N的非降数列,求数列中出现最多的数,若答案不唯一输出最小的数 输入 第一行T表示测试数据的组数(T<100) 对于每组测试数据: 第一行是一个正整数N表示数列长度 第二行有 ...

  7. GCD编程(封装GCD)

    //GCDGroup 类 @interface GCDGroup : NSObject @property (strong, nonatomic, readonly) dispatch_group_t ...

  8. 【Mongodb教程 第十九课 】PHP与MONGODB的条件查询

    与普通的关系型数据库类似,在对数据的删.改.查的时候,会用到查询条件,如mysql中的 where… 而MongoDB中,经过php来做的所有的操作指令都是用array来包裹的: MongoColle ...

  9. 【转载】读懂IL代码就这么简单 (一)

    一前言 感谢 @冰麟轻武 指出文章的错误之处,现已更正 对于IL代码没了解之前总感觉很神奇,初一看完全不知所云,只听高手们说,了解IL代码你能更加清楚的知道你的代码是如何运行相互调用的,此言一出不明觉 ...

  10. 第六十题(在O(1)时间内删除链表结点)

    题目:给定链表的头指针和一个结点指针,在O(1)时间删除该结点. 思路:将待删除节点下一个节点的数据赋给当前节点.删除下一个节点就可以.