zoukankan      html  css  js  c++  java
  • 句柄表(私有句柄表)

     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 
    ", 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!
    ");
    23     Test();
    24     system("pause");
    25     return 0;
    26 }
    View Code

    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,变为 816899c8,这指向 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"

  • 相关阅读:
    查找字符串中特定字符最后出现的位置
    C# List中的ForEach
    tensorflow中一个矩阵和一个向量相加
    Hibernate-ORM:16.Hibernate中的二级缓存Ehcache的配置
    Hibernate-ORM:15.Hibernate中的Criteria查询
    Hibernate-ORM:14.Hibernate中的命名查询
    Hibernate-ORM:12.Hibernate中的多对多关联关系
    Hibernate-ORM:10.Hibernate中的分页
    Hibernate-ORM:09.Hibernate中的getCurrentSession()
    Hibernate-ORM:08.Hibernate中的投影查询
  • 原文地址:https://www.cnblogs.com/onetrainee/p/11764117.html
Copyright © 2011-2022 走看看