原文发表于百度空间,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. MVC Html.AntiForgeryToken() 防止CSRF攻击 - CSDN博客

    原文:MVC Html.AntiForgeryToken() 防止CSRF攻击 - CSDN博客 (一)MVC Html.AntiForgeryToken() 防止CSRF攻击 MVC中的Html.A ...

  2. vim编码方式配置的学习和思考

    哎呀呀,今天9月30号,立即就要十一长假了,心里还有点小小浮躁.工作已经基本做完,想成为技术大牛怎么能够如此浮躁.为了应付浮躁的心灵,决定写一篇小博,平静一把. 今天一个配置文件须要有中文,而且同事是 ...

  3. HDU 1085 Holding Bin-Laden Captive!(母函数,或者找规律)

    Holding Bin-Laden Captive! Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Ja ...

  4. FastDFS的配置、部署与API使用解读(4)FastDFS配置详解之Client配置(转)

    一种方式是通过调用ClientGlobal类的初始化方法对配置文件进行加载,另一种是通过调用API逐一设置配置参数.后一种方式对于使用Zookeeper等加载属性的方式很方便. 1. 加载配置文件: ...

  5. Spring Boot与Micronaut性能比较

    文章转载出处:微信公众号——锅外的大佬 链接:https://mp.weixin.qq.com/s/MdBByJ0ju-rROKg7jsWygA 今天我们将比较两个在JVM上构建微服务的框架:Spri ...

  6. java的多态以及重载,重写,前期绑定,后期绑定

    多态的定义: 一个类实例的相同方法在不同情形有不同表现形式.多态机制使具有不同内部结构的对象可以共享相同的外部接口.这意味着,虽然针对不同对象的具体操作不同,但通过一个公共的类,它们(那些操作)可以通 ...

  7. 流媒体开发之开源项目live555---更改server端的帧率大小和码率大小

    -----------------------------qq:1327706646 010101010101010110010101010101010101010author:midu 010101 ...

  8. BZOJ 2244: [SDOI2011]拦截导弹 DP+CDQ分治

    2244: [SDOI2011]拦截导弹 Description 某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度.并且能够拦截 ...

  9. app上架的照片尺寸大小

  10. 在Android Studio中修改应用包名

    紧凑模式下(包名中的每个字段紧贴在一起,例如),右键单击包名,Refactor -> Rename,只能修改包名最外层的字段 分离模式下(点击设置,将Hide Empty Middle Pack ...