句柄表(私有句柄表)

 Windows内核分析索引目录:https://www.cnblogs.com/onetrainee/p/11675224.html

句柄表(私有句柄表)

  我们在R3环编程中,会接触到句柄HANDLE的概念。

  比如OPENPROCESS,打开进程获取其进程句柄,这些被称为“内核句柄”。

  注意,与GUI图形界面不同,那些 画刷句柄 被称为“用户句柄”,不在我们讨论范围之列。

一、 句柄表的基本概念

  句柄表分为私有私有句柄表和全局句柄表,我们这一节只讨论私有句柄表。

  每一个进程都有自己的私有句柄表,在一个进程中使用OpenProcess打开另一个进程时,则会将被打开进程在内核对象的 _EPROCESS 结构体完整的映射到打开进程的私有句柄表中。

  注意:是映射,不是创建,被打开进程的_EPROCESS在创建时就已经存储到全局句柄表中了,其他进程打开时直接从这里映射一份即可。

二、句柄表有关的结构

1. 获取_HANDLE_TABLE结构体

  私有句柄表存储在_EPROCESS+0x0c4 这个位置,其指针指向一个 _HANDLE_TABLE结构体
    

2. _HANDLE_TABLE 结构体

  我们使用 dt _HANDLE_TABLE 0xe26cc488,该结构的第一个成员 TableCode就是句柄表存储的位置。

   kd> dt  0xe26cc488 _HANDLE_TABLE
    ntdll!_HANDLE_TABLE
       +0x000 TableCode        : 0xe10af000
       +0x004 QuotaProcess     : 0x81eedb40 _EPROCESS
       +0x008 UniqueProcessId  : 0x000001e8 Void
3. 句柄表的结构

  句柄表分为多级结构,一般为一级结构,当句柄值太大时则会展开多级结构,以后三位为准。

  一个句柄值占8个字节,一页4KB,故一页能存储 512 个句柄值。

  如果采用多级结构(以两级为例),第一级就会存储第二级的地址。存储地址则能存储 1024 个地址,这样算下来采用两级结构可以存储 512*1024 个值。

  我们观察是否采用多级结构,是看 TableCode的最后一位,比如 为 1,则采用二级结构,依次类推...

  

4. 句柄值结构

  在句柄表中一个句柄值占8字节,故我们采用dq来查看。

  kd> dq 0xe10af000+1d1*8
    e10afe88  0000003a`816c800b 0000003a`816c800b
    e10afe98  0000003a`816c800b 0000003a`816c800b

  其前4个字节是关于属性,后面4字节才是真正存储句柄的值。

  注意,后3位是关于属性,要真正找到正确的地址,要变为0,比如 816c800b ,b = 1011 , 正确的应该为 1000,即 816c8008。

5. 如何使用R3环的句柄值查找R0中的句柄值

  我们在三环,hPro = OpenProcess(),将 t = hPro / 4,所得到的结果就是在句柄表中的索引号t,其占8位,故需要 t*8 得到地址。

6. 句柄的结构体

  从结构体中来看,句柄可以看作 句柄头+句柄体

  句柄头是 _OHJECT_HEADER;句柄体就是其句柄的本体,比如线程的句柄体为 _ETHREAD,进程的句柄体为 _EPROCESS

  句柄体在句柄头下方 +0x18 的位置。

  kd> dt _OBJECT_HEADER
    nt!_OBJECT_HEADER
       +0x000 PointerCount     : Int4B
       ····
       +0x014 SecurityDescriptor : Ptr32 Void
       +0x018 Body             : _QUAD
  因此,如果我们打算查看 _EPROCESS,就需要偏移 +0x18个字节。

  e2808380  81b1e5b3 0000003a 81b1e5b3 0000003a
  kd> dt _EPROCESS 81b1e5b0+18
    ntdll!_EPROCESS   
      +0x174 ImageFileName    : [16]  "calc.exe"

7. 句柄值的属性

  前面我们介绍前句柄值占8位,前四个字节代表属性,下面我们就来介绍一下。

  

  举一个例子,我们使用下面代码来修改最后一个的句柄的属性

    SetHandleInformation(hPro,HANDLE_FLAG_PROTECT_FROM_CLOSE,HANDLE_FLAG_PROTECT_FROM_CLOSE);

  然后通过windbg查看,可以明显看到与其他存在的不同:

    e10fdc80  816899cb 0200003a 816899cb 0000003a

三、通过实验来验证句柄表有关信息:

1)实验代码 handle_test.exe

 1 // handle_test.cpp : Defines the entry point for the console application.
 2 //
 3
 4 #include "stdafx.h"
 5 #include <Windows.h>
 6 #include <stdlib.h>
 7 #include <stdio.h>
 8 void Test(){
 9     DWORD PID;
10     HANDLE hPro = NULL;
11     HWND hWnd = ::FindWindow(NULL,"计算器");
12     ::GetWindowThreadProcessId(hWnd,&PID);
13     for(int i = 0; i<100;i++){
14         hPro = ::OpenProcess(PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ,TRUE,PID);
15         printf("handle value: %x \n", hPro);
16     }
17     // 修改属性
18     SetHandleInformation(hPro,HANDLE_FLAG_PROTECT_FROM_CLOSE,HANDLE_FLAG_PROTECT_FROM_CLOSE);
19 }
20 int main(int argc, char* argv[])
21 {
22     printf("Hello World!\n");
23     Test();
24     system("pause");
25     return 0;
26 }

2)实验原理:

  1. 先将计算器打开。

  2. handle_test.exe 会先查找计算器的窗口获取PID,然后打开该进程。

  3. 这样就会在 handle_test.exe 进程内部创建关于计算器的句柄的值,我们来研究该值。

  

3)实验流程:

  1)获取 句柄值 640,按照  【二、5】  所描述的,地址在TableCode中的为 640/4*8 = C80.

  2)通过 windbg 的!process 0 0来查找 handle_test.exe 的 _EPROCESS地址。

  Failed to get VadRoot
    PROCESS 81c45020  SessionId: 0  Cid: 07cc    Peb: 7ffd3000  ParentCid: 00a8
        DirBase: 1af46000  ObjectTable: e154d450  HandleCount: 123.
        Image: handle_test.exe
  3)先从 0x174 中验证其是否是该进程

     +0x174 ImageFileName    : [16]  "handle_test.exe"

    之后从  0xc4位置找到句柄表地址 

     +0x0c4 ObjectTable      : 0xe154d450 _HANDLE_TABLE

  4)之后,我们从 句柄表地址来查看 TableCode 0xe10fd000,加上之前算的 C80 偏移得到句柄值。

  kd> dt 0xe154d450 _HANDLE_TABLE
    ntdll!_HANDLE_TABLE
       +0x000 TableCode        : 0xe10fd000
       +0x004 QuotaProcess     : 0x81c45020 _EPROCESS
       +0x008 UniqueProcessId  : 0x000007cc Void
       +0x00c HandleTableLock  : [4] _EX_PUSH_LOCK
       +0x01c HandleTableList  : _LIST_ENTRY [ 0xe2728354 - 0xe24faafc ]
       +0x024 HandleContentionEvent : _EX_PUSH_LOCK
       +0x028 DebugInfo        : (null)
  5)按照【二、4】所介绍的,取得的值 816899cb,变为 816899c1,这指向 OBJECT_HEAD,偏移+0x18字节才真正指向 BODY(_EPROCESS)

    kd> dq 0xe10fd000+c80
      e10fdc80  0200003a`816899cb

  6)验证我们的 _EPROCESS,验证通过,实验成功。

    kd> dt _EPROCESS 816899c8 + 18
    ntdll!_EPROCESS

      +0x174 ImageFileName    : [16]  "calc.exe"

原文地址:https://www.cnblogs.com/onetrainee/p/11764117.html

时间: 2024-11-19 16:56:44

句柄表(私有句柄表)的相关文章

mybatis3动态创建表,判断表是否存在,删除表

1.mybatis3动态创建表,判断表是否存在,删除表 mapper配置文件: <span style="font-size:18px;"><?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/

hive join 优化 --小表join大表

1.小.大表 join 在小表和大表进行join时,将小表放在前边,效率会高,hive会将小表进行缓存. 2.mapjoin 使用mapjoin将小表放入内存,在map端和大表逐一匹配,从而省去reduce. 例子: select /*+MAPJOIN(b)*/ a.a1,a.a2,b.b2 from tablea a JOIN tableb b ON a.a1=b.b1 在0.7版本后,也可以用配置来自动优化 set hive.auto.convert.join=true;

hive表与外部表的区别

相信很多用户都用过关系型数据库,我们可以在关系型数据库里面创建表(create table),这里要讨论的表和关系型数据库中的表在概念上很类似.我们可以用下面的语句在Hive里面创建一个表: hive> create table wyp(id int, > name string, > age int, > tele string) > ROW FORMAT DELIMITED > FIELDS TERMINATED BY '\t' > STORED AS TEX

触发器中的inserted表和deleted表

触发器语句中使用了两种特殊的表:deleted 表和 inserted 表.Microsoft? SQL Server 2000 自动创建和管理这些表.可以使用这两个临时的驻留内存的表测试某些数据修改的效果及设置触发器操作的条件:然而,不能直接对表中的数据进行更改. inserted和deleted表主要用于触发器中: ·扩展表间引用完整性 ·在以视图为基础的基表中插入或更新数据 ·检查错误并基于错误采取行动 找到数据修改前后表状态的差异,并基于此差异采取行动. Deleted表用于存储DELE

父表、子表 主外键关系

ORACLE官方文档介绍: Concurrency Control, Indexes, and Foreign Keys You almost always index foreign keys. The only exception is when the matching unique or primary key is never updated or deleted.(你总是需要对 外键添加索引! 唯一的例外就是:匹配的主键列 或是 唯一列 从不进行更新操作或者 删除操作) Oracle

MySQL复制表结构,表数据。

1.复制表结构及数据到新表CREATE TABLE 新表 SELECT * FROM 旧表 这种方法会将oldtable中所有的内容都拷贝过来,当然我们可以用delete from newtable;来删除. 不过这种方法的一个最不好的地方就是新表中没有了旧表的primary key.Extra(auto_increment)等属性.需要自己用"alter"添加,而且容易搞错. 2.只复制表结构到新表 CREATE TABLE 新表 SELECT * FROM 旧表 WHERE 1=2

小甲鱼PE详解之输入表(导入表)详解(PE详解07)

捷径并不是把弯路改直了,而是帮你把岔道堵上! 走得弯路跟成长的速度是成正比的!不要害怕走上弯路,弯路会让你懂得更多,最终还是会在终点交汇! 岔路会将你引入万劫不复的深渊,并越走越深…… 在开始讲解输入表(导入表)概念之前,请允许小甲鱼童鞋用简短的几句话来总结之前我们学过的内容,并做进一步的思想综合提升,注意咯! 首先,我们知道PE 文件中的数据被载入内存后根据不同页面属性被划分成很多区块(节),并有区块表(节表)的数据来描述这些区块.这里我们需要注意的问题是:一个区块中的数据仅仅只是由于属性相同

数据结构Java实现02----线性表与顺序表

[正文] 本节内容: 线性结构 线性表抽象数据类型 顺序表 顺序表应用 一.线性结构: 如果一个数据元素序列满足: (1)除第一个和最后一个数据元素外,每个数据元素只有一个前驱数据元素和一个后继数据元素: (2)第一个数据元素没有前驱数据元素: (3)最后一个数据元素没有后继数据元素. 则称这样的数据结构为线性结构. 二.线性表抽象数据类型: 1.线性表抽象数据类型的概念: 线性表抽象数据类型主要包括两个方面:既数据集合和该数据集合上的操作集合. 数据集合: 可以表示为a0,a1,a2,...a

求LR(0)文法的规范族集和ACTION表、GOTO表的构造算法

原理 数据结构 1 // GO 2 private static Map<Map<Integer,String>,Integer> GO 3 = new HashMap<Map<Integer,String>,Integer>(); 4 5 // 规范族集 C 6 private static Map<Integer,Map<String,List<String>>> C 7 = new HashMap<Intege