深入理解句柄表

涉及到句柄表的有以下这些概念:

  • HANDLE_TABLE
  • HANDLE_TABLE结构体中的TableCode变量

实际上啊,TableCode是指向句柄表项第一个句柄表项的指针(NULL句柄表项),TableCode就是HANDLE_TABLE_ENTRY的指针。

但是,当有两级以上表时,这个时候就不是了,先来搞定最简单的。

HANDLE_TABLE_ENTRY:句柄表项

对象头_OBJECT_HANDLE

EXHANDLE:这个就是提供给用户使用的句柄值

HANDLE_TABLE_ENTRY是一个8个字节的结构体。它包括:

  • 指向对象头的指针
  • 32位的访问掩码

因为对象头_OBJECT_HANDLE的大小是0x18h,是8的倍数,因为对象头总是按照8个字节对齐。所以对象头的地址低3位肯定是0。所以HANDLE_TABLE_ENTRY的对象头的指针的低3位为0,低三位被用作一些访问标志。

句柄值:通过查看句柄值可以发现,句柄值总是4的倍数。例如:

0x0004

0x0008

0x000C

0x0010

所以,句柄值低2位总是0,所以低2位可以被用作标志位。

句柄值的结构类型是EXHANDLE,在EXHANDLE中低2位为标志位。

0x0000这一个句柄值被用来做位NULL无效句柄。提供给用户使用:

if(Handle == NULL/ *0x0000*/ )

return;

typedef struct _EXHANDLE

{

// 注意啦,这里是个联合。

// 实际上该结构体就占4个字节

union

{

struct

{

ULONG TagBits:2;

ULONG Index:30;

}

HANDLE GenericHandleOverlay; //呵呵,这是用来提供给用户使用的句柄。

#define HANLE_VALUE_INC 4

ULONG_PTR Value; //这个表示什么意思呢?

}

}EXHANDLE,*PEXHANDLE;

*******************************************************************************

如何知道HANDLE_TABLE和HANDLE_TABLE_ENTRY的关系,可以参考ExpAllocateHandleTable。

ExpAllocateHandleTable用来为每个进程分配句柄表,并初始化句柄表。第一次只分配0级的句柄(保存真正句柄项)。

*******************************************************************************

我不知道该怎么写才顺,我就按照我的分析流程写吧。

!Process 查看当前进程

PROCESS 87ca7cf8   SessionId: 0   Cid: 0cb0     Peb: 7ffd8000   ParentCid: 03c4

     DirBase: 3f13d000   ObjectTable: e2d11b78   HandleCount:   76.

     Image: windbg.exe

其中EPROCESS的地址在87ca7cf8,ObjectTable的地址在e2d11b78

ObjectTable是HANDLE_TABLE结构变量,它保存在EPROCESS中。

dt _EPROCESS 87ca7cf8   来查看EPROCESS结构体的值。

.....

    +0x0c4 ObjectTable       : 0xe2d11b78_HANDLE_TABLE

......

ObjectTable在EPROCESS偏移0x0c4处。

ObjectTable保存着关于句柄表的信息。

我们使用下面的命令来查看HANDLE_TABLE内容:

dt _HANDLE_TABLE 0xe2d11b78     

    +0x000 TableCode         : 0xe4702000

   ......

其中偏移0为TableCode,它实际上是句柄项(_HANDLE_TABLE_ENTRY)的指针。

句柄项(_HANDLE_TABLE_ENTRY)是用来保存真正的句柄信息的结构体。

(注意,当TableCode低2位为0时,TableCode才指向_HANDLE_TABLE_ENTRY,至于如何给TableCode分配值,还是需要分析一下源码。)

_HANDLE_TABLE_ENTRY是两个32位的结构体:一个指向对象头的指针;一个是32位的标志。

注意,这个32位的对象头的指针并不是全部有效。因为对象头为0x18个字节,所以windows能保证对象头的分配地址总是8的倍数。所以这32位的对象头的指针低3位肯定都为0,windows将这低3位用作其他用途。因此,当我们使用对象头指针时,一定要记得,低3位值不是我们需要的,应该置0 。即:value & 0xFFFFFFF8。

我们使用dt _HANDLE_TABLE_ENTRY 0xe4702000 来查看其值。

lkd> dt _HANDLE_TABLE_ENTRY 0xe4702000

nt!_HANDLE_TABLE_ENTRY

    +0x000 Object            : (null)

    +0x000 ObAttributes      : 0

    +0x000 InfoTable         : (null)

    +0x000 Value             : 0

    +0x004 GrantedAccess     : 0xfffffffe

    +0x004 GrantedAccessIndex : 0xfffe

    +0x006 CreatorBackTraceIndex : 0xffff

    +0x004 NextFreeTableEntry : -2

但是得到的值好像是无效的。

这就对了,因为句柄表的第一个值不被使用,做为一个无效值,用来提供给程序员做错误处理使用。

下面该怎么办呢?句柄表,句柄表肯定是一个连续的数组,连续的保存一些句柄表项。

我们使用dd 0x4702000来查看这块地址的一些值

lkd> dd 0xe4702000

e4702000   00000000 fffffffe e1008719 000f0003

e4702010   e1858019 00000003 87d68f13 00100020

......

看,值出来了,因为一个句柄表项_HANDLE_TABLE_ENTRY占8个字节。所以,前两个值看似一个无效的值。但是红色标示的却象个有用的句柄项。

注意了,_HANDLE_TABLE_ENTRY是2个32位组成的64位结构体。前面说过了,低32位是对象头的指针。但是要得到对象头的指针,我们必须将值&0xFFFFFFF8,将低3位置0。

e1008719 & 0xFFFFFFF8 = e1008718

我们还必须加上0x18才能得到真正的内核对象。因为内核对象在对象头的后面,对象头的大小是0x18

dt _object_header

lkd> dt _object_header

nt!_OBJECT_HEADER

    +0x000 PointerCount      : Int4B

...

    +0x018 Body              : _QUAD

用刚才的 e1008718 + 0x018 = e1008730 ,然后我们使用!Object e1008730查看。

lkd> !object e1008730

Object: e1008730    Type: (89bddad0) KeyedEvent

     ObjectHeader: e1008718 (old version)

     HandleCount: 48   PointerCount: 49

     Directory Object: e1000270   Name: CritSecOutOfMemoryEvent

哈哈,挺像的 。使用!Handle列出当前句柄来测试一把。

lkd> !handle

processor number 0, process 87ca7cf8

PROCESS 87ca7cf8   SessionId: 0   Cid: 0cb0     Peb: 7ffd8000   ParentCid: 03c4

     DirBase: 3f13d000   ObjectTable: e2d11b78   HandleCount:   99.

     Image: windbg.exe

Handle table at e4702000 with 99 Entries in use

0004: Object: e1008730   GrantedAccess: 000f0003 Entry: e4702008

Object: e1008730   Type: (89bddad0) KeyedEvent

     ObjectHeader: e1008718 (old version)

         HandleCount: 48   PointerCount: 49

         Directory Object: e1000270   Name: CritSecOutOfMemoryEvent

0008: Object: e1858030   GrantedAccess: 00000003 Entry: e4702010

Object: e1858030   Type: (89c153b0) Directory

......

对比一下,成功,我们已经找到了句柄值为0004的内核对象。

OK,冒出了句柄值,那么句柄值是如何被关联起来呢?想下0004,它对应_HANDLE_TABLE_ENTRY表项的低几个啊?

句柄值为0x0000代表是NULL,刚好_HANDLE_TABLE_ENTRY的第0个表项为无效值

句柄值为0x0004有效,刚好指的是_HANDLE_TABLE_ENTRY的第1个表项。

那句柄值为0x0008了?

原来句柄值总是4的倍数。值/4就代表句柄表项数组_HANDLE_TABLE_ENTRY的索引啊。

这时,句柄值的低两位永远是0啦,为啥呢?是4的倍数,第2为不就为0?自己算算。

0x00,0x04,0x08,0x10,0x14等等的二进制

既然第2位永远为0,那么微软就利用了这两位做一个标志位,用来指示当前句柄值所代表的内核对象到那个表项数组中找到?

什么意思呢?

句柄表实际上是分级的,分3级,我们可以像理解分页一样。

分页分为:页目录、页表、物理页。

每个页目录保存1024个页表,每个页表保存着1024个物理页,每个页为4k。

句柄表可以这样分:

4K的目录,保存1K个表指针(指针为一项,占4个字节吗,总共是1024个项)。

每个4K的表,保存着1K个_HANDLE_TABLE_ENTRY数组指针。

每个4K的_HANDLE_TABLE_ENTRY数组保存着512个_HANDLE_TABLE_ENTRY,咋是512个?因为每一个_HANDLE_TABLE_ENTRY是8个字节。

每个_HANDLE_TABLE_ENTRY指向真正的内核对象前的对象头。(第1个表项除外,代表空。)

哈哈,句柄表就这么简单,就是太多繁琐的东西。记不住。

记下笔记备忘之。

这两天试着写写访问句柄表信息的小驱动。写完后配合代码和WRK再整理。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
【课程简介】 本课程适合所有对MATLAB感兴趣的同学,通过本课程,你将学习到如何使用MATLAB,让你深入了解MATLAB的强大。 【完整课程列】 MATLAB程序设计教程 MATLAB与高等数学 MATLAB与科学计算(共166页).ppt MATLAB程序设计教程 MATLAB与高等数学 第01章 MATLAB操作基础(共33页).ppt MATLAB程序设计教程 MATLAB与高等数学 第02章 MATLAB矩阵及其运算(共78页).ppt MATLAB程序设计教程 MATLAB与高等数学 第03章 MATLAB程序设计(共48页).ppt MATLAB程序设计教程 MATLAB与高等数学 第04章 MATLAB文件操作(共12页).ppt MATLAB程序设计教程 MATLAB与高等数学 第05章 MATLAB绘图(共63页).ppt MATLAB程序设计教程 MATLAB与高等数学 第06章 MATLAB数据分析与多项式计算(共36页).ppt MATLAB程序设计教程 MATLAB与高等数学 第07章 MATLAB解方程与函数极值(共28页).ppt MATLAB程序设计教程 MATLAB与高等数学 第08章 MATLAB数值积分与微分(共14页).ppt MATLAB程序设计教程 MATLAB与高等数学 第09章 MATLAB符号计算(共23页).ppt MATLAB程序设计教程 MATLAB与高等数学 第10章 MATLAB图形句柄(共13页).ppt MATLAB程序设计教程 MATLAB与高等数学 第11章 MATLAB图形用户界面设计(共18页).ppt MATLAB程序设计教程 MATLAB与高等数学 第12章 Simulink动态仿真集成环境(共33页).ppt MATLAB程序设计教程 MATLAB与高等数学 第13章 在Word环境下使用MATLAB(共18页).ppt
智能指针是C++中的一种特殊类型的指针,它能够自动管理内存的释放,避免了手动释放内存的繁琐和容易出错的问题。C++11引入了几种智能指针,包括带引用计数的智能指针和不带引用计数的智能指针,如auto_ptr, scoped_ptr, unique_ptr, shared_ptr, weak_ptr等。\[1\] 带引用计数的智能指针(shared_ptr)使用引用计数来跟踪指针的引用次数,当引用计数为0时,自动释放内存。这种智能指针适用于多个指针共享同一块内存的情况,可以避免内存泄漏和悬挂指针的问题。 不带引用计数的智能指针(unique_ptr)则采用了独占所有权的方式,确保每个指针只能指向一个对象。当指针超出作用域或被显式释放时,内存会被自动释放。这种智能指针适用于需要独占资源的情况,如动态分配的内存、文件句柄等。 自己实现智能指针可以通过重载指针操作符和使用引用计数等技术来实现。但需要注意的是,自己实现智能指针需要考虑线程安全性和异常安全性等问题,确保指针的正确释放和资源的正确管理。 然而,需要注意的是,智能指针并不能解决所有的问题。例如,当存在循环引用时,带引用计数的智能指针(shared_ptr)可能导致内存泄漏。此外,使用智能指针时也需要注意避免裸指针和智能指针混用的情况,以免出现悬挂指针或重复释放内存的问题。\[2\]\[3\] 总之,深入掌握C++智能指针需要了解各种智能指针的原理、用法和适用场景,并注意使用智能指针的注意事项,以确保代码的正确性和安全性。 #### 引用[.reference_title] - *1* *2* [【C++】深入掌握智能指针](https://blog.csdn.net/weixin_45853856/article/details/121184992)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [722-深入掌握C++智能指针](https://blog.csdn.net/LINZEYU666/article/details/120982178)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值