句柄表

为什么要有句柄

在3环可以通过句柄访问到内核对象,如果windows不提供句柄,而是直接提供内核对象的指针给用户,那么是容易出错的。
例如:CloseHandle使用了错误的指针,在0环访问到了错误的地址,就会发生蓝屏。
因此提供了句柄给3环使用,进入0环时,在句柄表通过句柄为索引找到对应的内核对象。
在这里插入图片描述

句柄表里面有哪些内核对象

所有在自己进程的线程创建或者打开的内核对象,进程本身是父进程创建的,所以进程的句柄在父进程的句柄表里。

使用OpenProcess后,句柄表的变化

连续多次打开相同对象后,返回给三环的句柄的值是不同的,句柄表里面也会添加相应的值。

通过句柄寻找内核对象

第一步 :WinDbg使用命令 !Process 0 0 遍历所有进程
第二步 :dt _EPROCESS +指定进程的地址

在这里插入图片描述
第三步 :_EPROCESS+c4里面的值 就是句柄表位置的指针
kd> dt _HANDLE_TABLE 0xe2775e80
nt!_HANDLE_TABLE
+0x000 TableCode : 0xe2de3000 //句柄表的指针
+0x004 QuotaProcess : 0x8a12f448 _EPROCESS
+0x008 UniqueProcessId : 0x00000d00 Void
+0x00c HandleTableLock : [4] _EX_PUSH_LOCK
+0x01c HandleTableList : _LIST_ENTRY [ 0x80565ba8 - 0xe11dbebc ]
+0x024 HandleContentionEvent : _EX_PUSH_LOCK
+0x028 DebugInfo : (null)
+0x02c ExtraInfoPages : 0n0
+0x030 FirstFree : 0x7a0
+0x034 LastFree : 0
+0x038 NextHandleNeedingPool : 0x800 在分配下一层句柄表之前可以容纳的最大句柄的值 这个值/4就是这一层句柄表最大的索引
+0x03c HandleCount : 0n28
+0x040 Flags : 0
+0x040 StrictFIFO : 0y0

第四步 : kd> dq e2de3000(句柄表位置的指针) 句柄表每一项是8字节,前四字节是 句柄 ,后四字节是内核对象地址

在这里插入图片描述

第五步:找句柄7a4对应的内核对象
7a4为索引,该值/4*8就是要找的位置 /4表示在句柄表的第几项,*8 因为每一项的大小是8字节

在这里插入图片描述
句柄表里面的值,低两位清0 最后一位固定为0. 第二位表示是否可继承(9->8),然后加上0x18就是该内核对象的位置。(+18是_OBJECT_HEADER的大小)
因为每一个内核对象开始都是_OBJECT_HEADER这个结构体。然后才是_EPROCESS的开始位置

kd> dt _OBJECT_HEADER
nt!_OBJECT_HEADER
+0x000 PointerCount : Int4B
+0x004 HandleCount : Int4B
+0x004 NextToFree : Ptr32 Void
+0x008 Type : Ptr32 _OBJECT_TYPE
+0x00c NameInfoOffset : UChar
+0x00d HandleInfoOffset : UChar
+0x00e QuotaInfoOffset : UChar
+0x00f Flags : UChar
+0x010 ObjectCreateInfo : Ptr32 _OBJECT_CREATE_INFORMATION
+0x010 QuotaBlockCharged : Ptr32 Void
+0x014 SecurityDescriptor : Ptr32 Void
+0x018 Body : _QUAD

kd> dt _EPROCESS 8a1413b8+18
nt!_EPROCESS
+0x000 Pcb : _KPROCESS
+0x06c ProcessLock : _EX_PUSH_LOCK
+0x070 CreateTime : _LARGE_INTEGER 0x1d51d70`0b380ae4
+0x078 ExitTime : _LARGE_INTEGER 0x0
+0x080 RundownProtect : _EX_RUNDOWN_REF
+0x084 UniqueProcessId : 0x00000980 Void
+0x088 ActiveProcessLinks : _LIST_ENTRY [ 0x8a0fe498 - 0x8a134c68 ]
+0x090 QuotaUsage : [3] 0xa00
+0x09c QuotaPeak : [3] 0xc10
+0x0a8 CommitCharge : 0x17d
+0x0ac PeakVirtualSize : 0x22ef000
+0x0b0 VirtualSize : 0x1f9d000
+0x0b4 SessionProcessLinks : _LIST_ENTRY [ 0x8a0fe4c4 - 0x8a134c94 ]
+0x0bc DebugPort : (null)
+0x0c0 ExceptionPort : 0xe15d3bb8 Void
+0x0c4 ObjectTable : 0xe10cd258 _HANDLE_TABLE
+0x0c8 Token : _EX_FAST_REF
+0x0cc WorkingSetLock : _FAST_MUTEX
+0x0ec WorkingSetPage : 0x46763
+0x0f0 AddressCreationLock : _FAST_MUTEX
+0x110 HyperSpaceLock : 0
+0x114 ForkInProgress : (null)
+0x118 HardwareTrigger : 0
+0x11c VadRoot : 0x8a139268 Void
+0x120 VadHint : 0x8a1391d8 Void
+0x124 CloneRoot : (null)
+0x128 NumberOfPrivatePages : 0xcb
+0x12c NumberOfLockedPages : 0
+0x130 Win32Process : 0xe123e188 Void
+0x134 Job : (null)
+0x138 SectionObject : 0xe12955f8 Void
+0x13c SectionBaseAddress : 0x01000000 Void
+0x140 QuotaBlock : 0x8a1d56d8 _EPROCESS_QUOTA_BLOCK
+0x144 WorkingSetWatch : (null)
+0x148 Win32WindowStation : 0x00000038 Void
+0x14c InheritedFromUniqueProcessId : 0x00000644 Void
+0x150 LdtInformation : (null)
+0x154 VadFreeHint : (null)
+0x158 VdmObjects : (null)
+0x15c DeviceMap : 0xe1af54d8 Void
+0x160 PhysicalVadList : _LIST_ENTRY [ 0x8a141530 - 0x8a141530 ]
+0x168 PageDirectoryPte : _HARDWARE_PTE
+0x168 Filler : 0
+0x170 Session : 0xba5da000 Void
+0x174 ImageFileName : [16] “calc.exe”
+0x184 JobLinks : _LIST_ENTRY [ 0x0 - 0x0

在这里插入图片描述
第三位 :第0位 为1 第1位:跟OpenProcess有关 第2位:为0
在这里插入图片描述

句柄表的一项是8字节,一页可以存512项句柄。
当句柄超过512时,句柄表的结构会发生变化,TableCode存的是句柄表地址,一页存了1024个句柄表地址。也就是最多可以存1024*512个句柄。

把TableCode的值拆开,最后两位决定了句柄表有多少级。

总结:

通过进程结构体_EPROCESS +C4可以指定进程的句柄表 _HANDLE_TABLE结构 该结构第一个成员为TableCode
如果TableCode的低两位为 00 说明句柄表结构只有1级,通过TableCode 里面的句柄表地址+句柄/4*8 (8字节的最低两位要清0)就可以找到对应的内核结构体_OBJECT_HEADER,+0x18即为真正的内核对象结构体 例如:_EPROCESS _ETHREAD.

句柄表有一级的情况下:HANDLE的 0-9位 用于找具体项
句柄表有两级的情况下:HANDLE的 10-19用于找第一级偏移 0-9位 用于找具体项
句柄表有三级的情况下:HANDLE的 20-29 用于找第一级的偏移 10-19用于找第二级偏移 0-9位 用于找具体项

如果TableCode的低两位为 01 说明句柄表结构只有2级 ,TableCode 低两位清0,里面存的是句柄表指针,再次读出里面的值才是句柄表的位置,然后也是句柄表地址+句柄*4/8跟上面一样
以此类推 10 11 的情况。

##练习:根据handle查找进程的内核对象

ULONG FindKernelObjectByHandle(ULONG Index)
{

	PULONG pThread=NULL;
	PULONG pProcess=NULL;
	PULONG ObjectTable=NULL;
	ULONG TableCode=0;
	//句柄表有几层
	ULONG Tier=0;
	PVOID KeObject=NULL;
	PULONG pFirstTableBase=NULL;
	GetCurrentThread(&pThread);
	pProcess=(PULONG)(*(pThread+0x44/4));
	ObjectTable=(PULONG)(*(pProcess+0xc4/4));
	TableCode=*ObjectTable;
	Tier=TableCode&3;
	switch (Tier)
	{
	case 0:
		//句柄表只有一级,PspCidTable低两位清0 然后加上索引*4/8   低4字节的低2位清0 就是内核对象的头_OBJECT_HEADER  +0x18就是真正的内核对象_ETHREAD 或者_EPROCESS
		KeObject=(PVOID)(TableCode+Index*2);
		break;
	case 1:
		//找在第一级表的位置   一个表最大的HANDLE 是 512*4=2048  索引、2048 就找出第一级表中的偏移
		pFirstTableBase=(PULONG)(TableCode&0xFFFFFFFC)+Index/512/4;
		//在表中的偏移等于     e1003000 e1fc0000
		//低10位 是在一级表中的偏移
		KeObject=(PVOID)((*pFirstTableBase)+(Index%2048*2));
		break;
	}
	//进程私有句柄表存的是_OBJECT_HEADER的指针  +0x18才是真正的内核结构体
	return ((*(PULONG)KeObject)&0xfffffffe)+0x18;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据提供的引用内容,没有明确说明“哈希的处理句含义”这个问题。但是可以解释一下哈希中的处理句的概念。 在哈希中,处理句通常是指一个指向哈希中某个元素的指针或引用。通过处理句,我们可以访问哈希中的元素,进行查找、插入、删除等操作。 在使用第三方头文件uthash.h实现哈希时,处理句通常是一个指向自定义结构体的指针。通过在结构体中定义UTHASH_HANDLE字段,uthash.h可以自动为结构体生成处理句。例如: ```c #include "uthash.h" struct my_struct { int id; char name[10]; UT_hash_handle hh; }; struct my_struct *users = NULL; ``` 在上面的例子中,我们定义了一个名为my_struct的结构体,并在其中定义了UTHASH_HANDLE字段hh。通过这个字段,我们可以使用uthash.h提供的宏来实现哈希的相关操作。例如,我们可以使用HASH_ADD_INT宏将一个整型id和一个my_struct结构体添加到哈希中: ```c struct my_struct *s = malloc(sizeof(struct my_struct)); s->id = 1; strcpy(s->name, "John"); HASH_ADD_INT(users, id, s); ``` 在上面的例子中,我们使用HASH_ADD_INT宏将s添加到users哈希中,并以s->id作为哈希的键值。通过这个操作,我们可以使用处理句users来访问哈希中的元素,例如: ```c struct my_struct *s; int id_to_find = 1; HASH_FIND_INT(users, &id_to_find, s); if (s != NULL) { printf("Found user %s\n", s->name); } ``` 在上面的例子中,我们使用HASH_FIND_INT宏在users哈希中查找id为1的元素,并将结果存储在s中。如果找到了元素,我们就可以使用s->name来访问元素的name字段。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值