原文发表于百度空间,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. UICollectionView 讲解

    什么是UICollectionView UICollectionView是一种新的数据展示方式,简单来说可以把他理解成多列的UITableView(请一定注意这是 UICollectionView的最 ...

  2. flask使用debug模式时,存在错误时,会占用设备内存直至服务重启才释放;debug模式会开启一个守护进程(daemon process)

    函数调用顺序flask的app.py的run-->werkzeug的serving.py的run_simple-->调用werkzeug的debug的__init__.py里的类Debug ...

  3. js 验证 输入值 全是数字

    1.使用isNaN()函数 isNaN()的缺点就在于 null.空格以及空串会被按照0来处理 NaN: Not a Number /** *判断是否是数字 **/ function isRealNu ...

  4. 8. Smarty3:模版中的内置函数

    smarty3中对内置函数的修改比較大,加入了很多新的功能:变量声明.表达式,流程控制,函数.数组等.可是建议不要在模版中去使用过于复杂的逻辑,而是要尽量将一些程序设计逻辑写到PHP中,并在模版中採用 ...

  5. C++中结构和类的区别

    首先从从语言角度来看,c语言是一种结构化的语言,便于按照模块化的方式来组织程序,易于程序员的调试和维护,而对于c++来说,我么可以认为它是标准c的超集.实际上所有的c程序也是c++程序.但两者之间还是 ...

  6. CPU die

    http://en.wikipedia.org/wiki/CPU_Die Die (integrated circuit) From Wikipedia, the free encyclopedia ...

  7. Creo二次开发--内存清理函数

    我们在处理模型文件时,总会遇到内存环境的清除问题.一个干净的Creo工作环境.是保证工作能顺利完毕的保障. ProMdlEraseNotDisplayed()函数提供了清除未显示模型的功能. 当须要循 ...

  8. 文件管理中心iOS APP (国外市场:File Center) 技术支持

    文件管理中心iOS APP (国外市场:File Center) 技术支持网址:http://www.cnblogs.com/flychen/邮箱:592802944@qq.com

  9. Java中的文件上传和下载

    文件上传原理: 早期的文件上传机制: 在TCP/IP中.最早出现的文件上传机制是FTP.他是将文件由客户端发送到服务器的标准机制. jsp中的文件上传机制: 在jsp编程中不能使用FTP的方法来上传文 ...

  10. spring-jar包详解整理(大合集)

    转:https://blog.csdn.net/weisong530624687/article/details/50888094 spring.jar 是包含有完整发布模块的单个jar 包.但是不包 ...