(65)如何根据句柄从二级、三级结构句柄表中找到内核对象

2023-05-16

一、回顾

上一篇博客介绍了如何遍历一级句柄表。一级句柄表非常简单,就是一个4KB页,最多存储512个句柄表项。如果句柄数量在 512 - 1024*512 之间,句柄表就是二级结构;如果句柄数量大于 1024 * 512,就是三级结构。

这篇博客作为上一篇的补充,介绍如何从二级、三级结构句柄表中找到内核对象。

二、ExpLookupHandleTableEntry 函数

实际上这个需求就是 ExpLookupHandleTableEntry 函数做的事情:

PHANDLE_TABLE_ENTRY
ExpLookupHandleTableEntry (
    IN PHANDLE_TABLE HandleTable,
    IN EXHANDLE Handle
    )

参数1是句柄表的地址,即 TableCode,注意,这里 TableCode 的低位不能清零,函数里要判断句柄表结构的。

参数2是句柄值,PID 的值就是一个句柄值,调用 OpenProcess 打开一个进程得到的也是句柄值,前者用来索引全局句柄表,后者用来索引进程的句柄表。

下面给出函数源码,我给关键的代码添加了注释,我们要重点研究二级和三级结构的处理方式:

PHANDLE_TABLE_ENTRY
ExpLookupHandleTableEntry (
    IN PHANDLE_TABLE HandleTable,
    IN EXHANDLE Handle
    )

/*++

Routine Description:

    This routine looks up and returns the table entry for the
    specified handle value.

Arguments:

    HandleTable - Supplies the handle table being queried

    Handle - Supplies the handle value being queried

Return Value:

    Returns a pointer to the corresponding table entry for the input
        handle.  Or NULL if the handle value is invalid (i.e., too large
        for the tables current allocation.

--*/

{
    ULONG_PTR i,j,k;
    ULONG_PTR CapturedTable;
    ULONG TableLevel;
    PHANDLE_TABLE_ENTRY Entry;

    typedef HANDLE_TABLE_ENTRY *L1P;
    typedef volatile L1P *L2P;
    typedef volatile L2P *L3P;

    L1P TableLevel1;
    L2P TableLevel2;
    L3P TableLevel3;

    ULONG_PTR RemainingIndex;
    ULONG_PTR MaxHandle;
    ULONG_PTR Index;

    PAGED_CODE();


    //
    // Extract the handle index
    //
    Handle.TagBits = 0; // 低2位清零
    Index = Handle.Index; // 取31-2位作为句柄表下标

    MaxHandle = *(volatile ULONG *) &HandleTable->NextHandleNeedingPool;

    //
    // See if this can be a valid handle given the table levels.
    //
    if (Handle.Value >= MaxHandle) {
        return NULL;        
    }

    //
    // Now fetch the table address and level bits. We must preserve the
    // ordering here.
    //
    CapturedTable = *(volatile ULONG_PTR *) &HandleTable->TableCode;

    //
    //  we need to capture the current table. This routine is lock free
    //  so another thread may change the table at HandleTable->TableCode
    //

    TableLevel = (ULONG)(CapturedTable & LEVEL_CODE_MASK); // LEVEL_CODE_MASK == 3
    CapturedTable = CapturedTable & ~LEVEL_CODE_MASK;

    //
    //  The lookup code depends on number of levels we have
    //

    switch (TableLevel) {
        
        case 0:
            
            //
            //  We have a simple index into the array, for a single level
            //  handle table
            //


            TableLevel1 = (L1P) CapturedTable;

            Entry = &(TableLevel1[Index]);

            break;
        
        case 1:
            
            //
            //  we have a 2 level handle table. We need to get the upper index
            //  and lower index into the array
            //

            TableLevel2 = (L2P) CapturedTable;
                
			// LOWLEVEL_COUNT == 512
            i = Index / LOWLEVEL_COUNT; // 计算属于第几张句柄表
            j = Index % LOWLEVEL_COUNT; // 计算在句柄表内的偏移

            Entry = &(TableLevel2[i][j]);

            break;
        
        case 2:
            
            //
            //  We have here a three level handle table.
            //

            TableLevel3 = (L3P) CapturedTable;

            //
            //  Calculate the 3 indexes we need
            //
                
			// MIDLEVEL_THRESHOLD == 1024 * 512
            i = Index / (MIDLEVEL_THRESHOLD);
            RemainingIndex = Index - i * MIDLEVEL_THRESHOLD;
            j = RemainingIndex / LOWLEVEL_COUNT;
            k = RemainingIndex % LOWLEVEL_COUNT;
            Entry = &(TableLevel3[i][j][k]);

            break;

        default :
            _assume (0);
    }

    return Entry;
}

三、手动从二级句柄表里找内核对象

首先给出测试程序,打开计算器进程 1000 次,这意味着该进程的句柄表是二级结构的,TableCode 低2位是1,TableCode 所在的页可以存储1024个4字节地址,每个地址都可以指向一个4KB的句柄表页。因为我们打开了1000次,所以应该只有前两个地址是有效的。

int _tmain(int argc, _TCHAR* argv[])
{
	DWORD PID;
	HANDLE hPro = NULL;
	HWND hwnd = FindWindowA(NULL, "计算器");
	GetWindowThreadProcessId(hwnd, &PID);

	for (int i = 0; i < 1000; i++)
	{
		//hPro = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, TRUE, PID);
		hPro = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, TRUE, PID);
		printf("句柄:%x\n", hPro);
	}
	SetHandleInformation(hPro, HANDLE_FLAG_PROTECT_FROM_CLOSE, HANDLE_FLAG_PROTECT_FROM_CLOSE);
	getchar();
	return 0;
}

在这里插入图片描述

随便拿一个 fcc,首先要算出它属于第几个句柄表, 0xfcc / 0x200 = 7,因为地址是4字节,所以要除以4,意味着要到 TableCode[1] 找;

0xfcc % 0x200 = 0x1cc,所以就是 TableCode[1][0x1cc]。下面来windbg找找看:

现在找到 TableCode:

kd> dt 0xe15bb300 _HANDLE_TABLE
ntdll!_HANDLE_TABLE
   +0x000 TableCode        : 0xe2280001
   +0x004 QuotaProcess     : 0x81c01c10 _EPROCESS
   +0x008 UniqueProcessId  : 0x000003dc Void
   +0x00c HandleTableLock  : [4] _EX_PUSH_LOCK
   +0x01c HandleTableList  : _LIST_ENTRY [ 0xe22b5b74 - 0xe23359d4 ]
   +0x024 HandleContentionEvent : _EX_PUSH_LOCK
   +0x028 DebugInfo        : (null) 
   +0x02c ExtraInfoPages   : 0n0
   +0x030 FirstFree        : 0xfdc
   +0x034 LastFree         : 0
   +0x038 NextHandleNeedingPool : 0x1000
   +0x03c HandleCount      : 0n1013
   +0x040 Flags            : 0
   +0x040 StrictFIFO       : 0y0

dd 一下 0xe2280001:

kd> dd 0xe2280000
e2280000  e2147000 e2281000 00000000 00000000
e2280010  00000000 00000000 00000000 00000000
e2280020  00000000 00000000 00000000 00000000
e2280030  00000000 00000000 00000000 00000000
e2280040  00000000 00000000 00000000 00000000
e2280050  00000000 00000000 00000000 00000000
e2280060  00000000 00000000 00000000 00000000
e2280070  00000000 00000000 00000000 00000000

TableCode[1] 就是 e2281000 ,dq看一下:

kd> dq e2281000
e2281000  fffffffe`00000000 0000003a`81c41543
e2281010  0000003a`81c41543 0000003a`81c41543
e2281020  0000003a`81c41543 0000003a`81c41543
e2281030  0000003a`81c41543 0000003a`81c41543
e2281040  0000003a`81c41543 0000003a`81c41543
e2281050  0000003a`81c41543 0000003a`81c41543
e2281060  0000003a`81c41543 0000003a`81c41543
e2281070  0000003a`81c41543 0000003a`81c41543

TableCode[1][0x1cc] :

kd> dq e2281000+1cc*8
e2281e60  0000003a`81c41543 0000003a`81c41543
e2281e70  0000003a`81c41543 0000003a`81c41543
e2281e80  0000003a`81c41543 0000003a`81c41543
e2281e90  0000003a`81c41543 0000003a`81c41543
e2281ea0  0000003a`81c41543 0000003a`81c41543
e2281eb0  0000003a`81c41543 0000003a`81c41543
e2281ec0  0000003a`81c41543 0000003a`81c41543
e2281ed0  0000003a`81c41543 0000003a`81c41543

找到对象头了,下面看看 EPROCESS,注意低3位清零,还要加上 0x18:

kd> dt _EPROCESS 81c41540+18
ntdll!_EPROCESS
   +0x000 Pcb              : _KPROCESS
...
   +0x170 Session          : 0xf8bc8000 Void
   +0x174 ImageFileName    : [16]  "calc.exe"
   +0x184 JobLinks         : _LIST_ENTRY [ 0x0 - 0x0 ]
...

相信看到这,你已经知道怎么通过句柄找内核对象了,如果还是不懂,请留言说出你的问题。

三级结构我就不找了,因为原理差不多,可以照抄 ExpLookupHandleTableEntry 函数。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

(65)如何根据句柄从二级、三级结构句柄表中找到内核对象 的相关文章

  • ROS CMakeLists.txt中catkin_package和INCLUDE_DIRS的区别

    CMakeLists txt中 catkin package INCLUDE DIRS include 这里代表的是catkin的构建选项 xff0c INCLUDE DIRS表示将使用INCLUDE DIRS后面的内部目录include
  • 利用ROS框架搭建云平台提供机器人服务

    我们要怎么做呢 我们在云平台我们识别物体之后输出的是全局的二维码坐标 x y z 我们接下来要做两件事情 一种是使用云端的服务 xff08 在ROS中的表现形式是云平台提供的action xff09 第二种是请求云端的数据 xff08 可以
  • 虚拟现实技术vr可以用来干什么?虚拟现实技术vr有什么特征

    科技行业的不断蓬勃发展 xff0c 每天会出现一些新的科技产品 xff0c 例如现在很火的虚拟现实技术vr xff0c 虚拟现实技术用的领域很多 xff0c 就拿游戏行业来说 xff0c 玩家可以通过vr眼镜 vr手柄等体验vr游戏 xff
  • vr直播是如何实现的?vr直播都有哪些优势

    科技改变了我们的生活方式 xff0c 提起科技相信大家对这个直播行业恐怕都不陌生 xff0c 最近直播行业也玩出来新的花样 xff0c 引进了vr技术 xff0c 摇身一变 xff0c 变成了vr直播 xff0c 很多朋友不太理解vr直播是
  • Python归并排序

    归并排序 数据科学家每天都在处理算法 然而 xff0c 数据科学学科作为一个整体已经发展成为一个不涉及复杂算法实现的角色 尽管如此 xff0c 从业者仍然可以从建立对算法的理解和知识库中受益 在本文中 xff0c 对排序算法归并排序进行了介
  • 平衡车PID调节总结

    https blog csdn net a568713197 article details 82845959
  • FreeRTOS详解三

  • Invalid bound statement (not found)出现的原因和解决方法

    解决错误的步骤 出现了什么错误可能导致的原因解决办法 出现了什么错误 错误截图 xff1a BindingException 数据绑定异常 not found 找不到 org apache ibatis binding BindingExc
  • TI Processor SDK 如何生成例程

    TI现在新的SDK都叫Process SDK了 例程要自己生成 这样好多人都说自己找不到例程在哪里 其实就是生成这一步搞不定 我以AM5728为例子说 先打开到pdk的目录 编辑箭头所示文件 安装在默认路径Cpan的可以忽略这一步 否则要改
  • PCIE BAR空间理解

    PCIE应用程序编程 xff0c 首先就要理清PCIE BAR空间到底说的是什么 在PCIE配置空间里 xff0c 0x10开始后面有6个32位的BAR寄存器 xff0c BAR寄存器中存储的数据是表示PCIE设备在PCIE地址空间中的基地
  • 老男孩读PCIe之五:TLP结构

    来源 xff1a http www ssdfans com p 61 3683 无论Request TLP xff0c 还是作为回应的Completion TLP xff0c 它们模样都差不多 xff1a 图5 1 TLP主要由三部分组成
  • vxWorks6.9及workBench3.3常见配置

    1 双斜杠注释 在workBench集成开发环境当中 xff0c 默认的注释方式为 xxxxxx 如果想要使用 注释的方法必须修改workBench 的编译选项 xff0c 为编译选项添加c99支持或者gnu89 在编译选项中添加 std
  • 可能是最全的FreeRTOS源码分析及应用开发系列

    可能是最全的FreeRTOS源码分析及应用开发系列 FreeRTOS 是一个可裁剪的小型且免费的 RTOS 系统 xff0c 尺寸非常小 xff0c 可运行于微控制器上 其特点包括 xff1a 内核支持抢占式 xff0c 合作式和时间片调度
  • FreeRTOS系列|FreeRTOS简介

    1 RTOS简介 RTOS全称为 Real Time Operation System xff0c 即实时操作系统 RTOS强调的是实时性 xff0c 又分为硬实时和软实时 硬实时要求在规定的时间内必须完成操作 xff0c 不允许超时 xf
  • FreeRTOS系列|任务创建和删除

    1 任务创建和删除API函数 xTaskCreate 函数 xff1a 动态创建一个新的任务 xff0c 每个任务都需要RAM来保存任务状态 任务控制块 43 任务栈 xff0c 此接口采用动态分配内存资源 BaseType t span
  • FreeRTOS系列|多任务调度

    1 多任务启动流程 多任务启动流程如下表所示 启动后以下各函数由上至下依次执行含义osKernelStart 启动内核vTaskStartScheduler 启动任务调度器xPortStartScheduler 启动调度器prvStartF
  • PTP 报文格式

    HeaderBodySuffix34 字节Variable lengthOptional 所有的 PTP 帧都包含一个公共报头 xff0c 它决定了协议版本和消息类型 xff0c 还定义了消息的剩余内容 所有多字节字段以大端顺序发送 xff
  • makefile:make -C M=参数的使用

    Makefile为 xff0c PWD span class token operator 61 span span class token punctuation span shell pwd span class token punct
  • BW笔记(2011-10-24更新至No.237)

    1 同一个变量名的UID可能有多个 xff0c 记得注意 2 在查找时要注意技术名称还是名称 xff0c 因为查询时会在两个中进行 xff0c 模糊查询时要细心 xff0c FV与V都可以查到 3 复制的时候注意长度 xff0c 过长的会不
  • rpmsg 内核开发 用户层接口

    地址 xff1a https blog csdn net thisway diy article details 129195479 韦东山 Tina Linux E907开发指南 AMP 环境搭建 7 1 rpmsg 内核开发 7 2 r

随机推荐

  • __raw_writel, writel_relaxed 和 writel的区别

    因为对别的平台不了解 xff0c 下面仅谈它们在ARM上的区别 raw writel xff1a 因为有volatile关键字 xff0c 所以编译器不会打乱多个 raw writel的执行顺序 对于ARM而言 xff0c 当多个写以代码的
  • WFE和WFI的区别

    1 概念 xff1a WFI Wait for interrupt 和WFE Wait for event 是两个让ARM核进入low power standby模式的指令 xff0c 由ARM architecture定义 xff0c 由
  • Ubuntu16.04安装中文输入法

    转载地址 xff1a http blog csdn net suxiang198 article details 52040283 Ubuntu16 04安装完后 xff0c 和12 04以及14 04都不一样 xff0c 并没有中文输入功
  • QT linux安装

    转载地址 xff1a http www cnblogs com tangkaixuan p 6504102 html 文章来自https lug ustc edu cn sites qtguide 1 4 Qt在Linux下安装 Qt在Li
  • Linux CAN编程详解

    转载地址 xff1a http velep com archives 1181 html Linux CAN编程详解 是一篇百度文库上的文档 xff0c 主要描述了以下内容 xff1a can总线介绍及其帧类型 xff1b Linux 系统
  • buildroot学习(十)——at91sam9g45软件平台更新

    转载地址 xff1a https blog csdn net srf1986 article details 52474697 xff08 xff11 xff13 xff16 xff09 spice protocol In computin
  • killall 、kill 、pkill 命令详解

    转载地址 xff1a https www cnblogs com rsky p 4886043 html killall 命令 Linux系统中的killall命令用于杀死指定名字的进程 xff08 kill processes by na
  • PCIe扫盲——PCIe简介

    转载地址 xff1a http blog chinaaet com justlxy p 5100053066 PCI Express是继ISA和PCI总线之后的第三代I O总线 xff0c 即3GIO 由Intel在2001年的IDF上提出
  • Adaptive Autosar通讯层:ARA::COM中的Instance Identifiers

    一般概念 实例标识符 在收发两端都是要用的 是很核心的概念 proxy端用来搜索服务 xff0c skeleton端用来创建服务实例 站在API的角度来看 xff0c 这样的识别符是和特定的技术绑定的 所以 xff0c 标识符的结构和内容都
  • BW:数据源抽取机制(这篇是以前的笔记,写得很差,有不少错的地方,留着给自己看)

    题记 xff1a 忽然想到这么个问题 xff0c 后勤数据源和非后勤数据初始化有何区别 xff0c 然后进行周边的拓展 xff0c 所以就形成了下文 大部分知识源于 TBW350 和 SAP SDN 对数据源抽取机制的深入探讨 一 什么数据
  • 【ARA com API】ara::core::Optional

    文章目录 ara core Optional 是什么标准中的代码示例 ara core Optional 是什么 实际上就是std optional 但是当前的AP标准没有支持到那么新版本的C 43 43 标准 xff08 我没有具体研究是
  • ROS学习总结(1)--入门、学习路线

    最近由于项目需要 xff0c 我被分配到机器人驱动模块 xff0c 由此开始研究学习ROS xff0c 在此记录学习ROS的方法 过程 经历与应用 本节记录ROS学习路线 ROS xff08 robot operation system x
  • 使用uart数据起飞

    使用uart得到的位置信息进行起飞 在得到了位置信息的前提下 xff0c 我们开始进行模拟起飞 xff0c 即使用usb供电 xff0c 人工控制其高度 xff0c 在上位机查看油门大小 xff0c 电机的pwm输出 commander c
  • AirSim(五)---理解篇: Airsim世界坐标系、NED坐标系、机体坐标系以及控制相关API接口函数

    目录 1 坐标系 coordinate system 1 AirSim API的坐标系 xff1a NED 坐标系 with SI unit 2 Unreal Engine的坐标系 xff08 3 xff09 AirSim全局坐标系 61
  • 深度学习中常用的优化算法(SGD, Nesterov,Adagrad,RMSProp,Adam)总结

    深度学习中常用的优化算法 SGD Nesterov Adagrad RMSProp Adam 总结 1 引言 在深度学习中我们定义了损失函数以后 xff0c 会采取各种各样的方法来降低损失函数的数值 xff0c 从而使模型参数不断的逼近于真
  • 双系统安装ubuntu 22.04 LTS(一步到位)

    作为一个拥有两次都是一次成功安装好双系统的经验的人 xff0c 我觉得我可以借这个文章仔细讲述一下 xff0c 让大家都可以双系统安装都是一次成功 为什么有着两次安装经验呢 xff0c 第一次安装完成后由于电脑的内存不太够了 xff0c 然
  • UART、RS232、RS485 串行通信详解

    一 UART通信 UART是Universal Asynchronous Receiver Transmitter的缩写 xff0c 意即通用异步串行通信接口 xff0c 是最常用的通信技术之一 xff0c 广泛用于设备与电脑之间 设备与设
  • I2C总线基础知识及操作详解

    I2C总线是一种简单的双向两线式同步串行总线 xff0c 最初由Philips公司开发 xff0c 后又经过几次发展和完善 xff0c 目前已被业界厂商广泛采用 xff0c 成为最常用的板级通信总线之一 xff0c 大量应用于处理器与外围设
  • 对AI的理解及应用的思考

    1 概述 1 1 常用术语 1 2 AI学习方式及地位 序号 学习方法 地位 1 强化学习 Reinforcement Learning 犹如蛋糕上的一颗樱桃 2 监督学习 Supervised Learning 犹如蛋糕外的一层糖霜 3
  • (65)如何根据句柄从二级、三级结构句柄表中找到内核对象

    一 回顾 上一篇博客介绍了如何遍历一级句柄表 一级句柄表非常简单 xff0c 就是一个4KB页 xff0c 最多存储512个句柄表项 如果句柄数量在 512 1024 512 之间 xff0c 句柄表就是二级结构 xff1b 如果句柄数量大