【旧文章搬运】Windows句柄表分配算法分析(实验部分)
原文发表于百度空间,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句柄表分配算法分析(实验部分)的更多相关文章
- 【旧文章搬运】Windows句柄表分配算法分析(一)
原文发表于百度空间,2009-03-30========================================================================== 阅读提示: ...
- 【旧文章搬运】Windows句柄表分配算法分析(三)
原文发表于百度空间,2009-03-30========================================================================== 三.当需要 ...
- 【旧文章搬运】Windows句柄表分配算法分析(二)
原文发表于百度空间,2009-03-30========================================================================== 四.句柄表 ...
- 【旧文章搬运】Windows句柄分配算法(一)
原文发表于百度空间,2009-04-04========================================================================== 分析了Wi ...
- 【旧文章搬运】Windows句柄分配算法(二)
原文发表于百度空间,2009-04-04========================================================================== 在创建句柄 ...
- 【旧文章搬运】Windows句柄表格式
原文发表于百度空间,2009-02-28========================================================================== 句柄是Wi ...
- 【旧文章搬运】Windows内核常见数据结构(进程相关)
原文发表于百度空间,2008-7-24========================================================================== 进程的相关结 ...
- 【旧文章搬运】更正一个枚举PspCidTable时的错误
原文发表于百度空间及看雪论坛,2009-02-27 看雪论坛地址:https://bbs.pediy.com/thread-82919.htm============================= ...
- 【旧文章搬运】深入分析Win7的对象引用跟踪机制
原文发表于百度空间及看雪论坛,2010-09-12 看雪论坛地址:https://bbs.pediy.com/thread-120296.htm============================ ...
随机推荐
- qq空间微博等更多社交平台分享
<!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title>& ...
- 【Gradle】配置中引用的jar包版本后面自动加冒号导致引入jar包失败的问题/gradle中引用jar包版本不一致的问题/gradle中引用jar失败的问题 解决方法
idea中 gradle中 引用jar包,版本后面默认加:的问题 gradle中引用jar包版本不一致的问题 gradle中引用jar失败的问题 如上题目所示,三个问题其实都是同一样的简单又恶心,因为 ...
- docker on UP Board
前言 原创文章,转载引用务必注明链接.水平有限,如有疏漏,欢迎指正. 本文使用Markdown写成,为获得更好的阅读体验和正常的图片.链接,请访问我的博客: http://www.cnblogs.co ...
- 怎么下载google商店的扩展程序?
首先,你有机会进入到了外网! 浏览器输入:chrome://extensions 复制唯一的ID进入:http://crx.2333.me/ ,然后下载即可!
- DW格式与布局
- 【翻译自mos文章】多租户中的service管理
来源于: Service Management For Multitenant (文档 ID 2009500.1) APPLIES TO: Oracle Database - Enterprise E ...
- postgres启动过程分析
今天来学习一下pg启动的过程. pg的启动命令./bin/postgres -D path/to/data. 1.主体监控进程 postmaster进程进入无限循环,等待客户端请求并为之提供请求的服务 ...
- Django-配置celery
首先需要安装的包 pip install cellerypip install django-cellery pip install django-cellery-results pip instal ...
- Redis相关知识
Redis 存储的五种 字符串类型:string 一个String类型的value最大可以存储512M String是最常用的一种数据类型,普通的key/value存储. 散列类型: hash 键值 ...
- poj 2264 Advanced Fruits(DP)
Advanced Fruits Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 1944 Accepted: 967 ...