内核-⑤句柄表

1. 句柄表

1.1 句柄

  • 什么是句柄(内核对象)
    • 当一个进程创建或者打开一个内核对象时,将获得一个句柄,通过这个句柄可以访问内核对象,句柄其实是句柄表的下标。如:
HANDLE g_hMutex = ::CreateMutex(NULL,FALSE, "XYZ");
HANDLE g_hMutex = ::OpenMutex(MUTEX_ALL_ACCESS,FALSE, "XYZ"); 
HANDLE g_hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL); 
HANDLE g_hThread = ::CreateThread(NULL, 0, Proc, NULL, 0, NULL);

1.2 句柄表

1.2.1 句柄表作用

  • 为什么要有句柄
    • 句柄存在的目的是为了避免在应用层直接修改内核对象。
    •  HANDLE g_hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    • 如果g_hEvent存储的就是EVENT内核对象的地址,那么就意味着我们可以在应用层修改这个地址,一旦指向了无效的内核内存地址就会蓝屏。 
    • 3环调用OpenProcess时,只是在句柄表中添加了一个句柄,将句柄表中的句柄返回,若调用了100次OpenProcess打开同一个进程,会得到100个不同的句柄,但是句柄表中,只是多了100个句柄,并且这100个句柄的内容最终都是指向同一个进程的地址,进程对象还是只有一个。

1.2.2 句柄表存储的值

  • 句柄表中,实际上存储了8个字节的数据,真正的内核对象地址是:低四字节,且低四字节的bit[0],bit[1],bit[2]设置为0后的值

1.2.3 句柄表位置

  • 句柄表也是一个结构体,结构体里面的TableCode才真正指向了句柄表

1.3 通过句柄表找内核对象

1.3.1 句柄表

  • 句柄表中,每个句柄的值占8个字节,句柄除以4得到该句柄在句柄表中的下标

1.3.2 获取句柄表的值

  • 1.OpenProcess获取句柄                             假设得到值:684
    •     684/4 = 该句柄在句柄表中的下标。句柄表中,每一个句柄的数值占8字节
  • 2.获取当前进程的句柄表
    • kd> !process 0 0                                       假设得到进程结构体值:86097da0
    • kd> dt _EPROCESS 86097DA0            找到0xc4的_HANDLE_TABLE结构体,得到值为0xe1928c08
    • kd> dt _HANDLE_TABLE e1928c08   找到句柄表TableCode,得到值为0xe1cf6000
  • 3.通过句柄,找到句柄表对应该句柄的值
    • ①句柄除以四得到下标,6BC/4 = 1A1
    • ②句柄表+下标*8,得到:句柄在句柄表中存储的数值
      • kd> dq e1cf6000+1A1*8
      • 根据1.2.2句柄表存储的值,可以得出:该内核对象(该进程结构体)地址是86050d48(二进制后3位置0)

1.3.3 句柄对应的内核对象

  • 完整的内核对象
    • 所有完整的内核对象都是以一个OBJECT_HEADER结构体开头的,真正的内核对象是存储在Body中的,_EPROCESS、_ETHREAD等结构体就是存储在Body里的
  • 句柄表中的内核对象指向的是一个完整的内核对象
    • kd> dt _EPROCESS 86050d48+0x18

1.3.4 句柄表反调试

  • 当一个进程调试我自己的进程的时候,那么这个进程的句柄表中就一定会有我的进程地址,可以判断自己的进程是否被调试了,用作反调试手段

2. 全局句柄表

2.1 全局句柄表

  • 每一个进程都有一个句柄表,这个句柄表是私有的,里面存储了当前进程所打开的所有句柄,除此之外,系统还有一个全局句柄表:PsdCidTable 
    • 1)  所有的进程和线程无论无论是否打开,都在这个表中。
    • 2)  每个进程和线程都有一个唯一的编号:PID和CID    这两个值其实就是全局句柄表中的索引。
    • 进程和线程的查询,主要是以下三个函数,按照给定的PID或CID从全局变量PspCidTable查找相应的进线程对象:
      • PsLookupProcessThreadByCid()            
      • PsLookupProcessByProcessId()            
      • PsLookupThreadByThreadId()      
    • PspCidTable指向_HANDLE_HEADER结构体,里面的TableCode才是真正句柄表

2.2 全局句柄表结构

2.3 通过PID在PspCidTable找到内核对象

  • 1.打开计算器,通过任务管理器获取计算器的PID:0x414
    • 获取该进程的下标:414/4=105
  • 2.获取全局句柄表
    • kd>  dd PspCidCode                            得到值:e1000858
    • kd> dt _HANDLE_TABLE e1000858     得到TableCode值:10030000
      • 低2位为0,说明此时的全局句柄表是一级的
    • kd> dq 10030000+105*8
  • 3.获取句柄表里面的值
    • 86201781,二进制后3位置零,得到86201780
  • 查看对应的进程结构体
    • kd> dt _EPROCESS 86201780,全局句柄表指向的内核对象没有OBJECT_HEADER,不需要+0x18,+0x174,看进程名是不是计算器





posted @ 2021-01-01 10:37  三一米田  阅读(444)  评论(0)    收藏  举报