驱动开发:内核枚举PspCidTable句柄表( 二 )


如上图所示TableCode是指向句柄表的指针,低二位(二进制)记录句柄表的等级:0(00)表示一级表,1(01)表示二级表,2(10)表示三级表 。这里的 0xffffdc88-7d09b001 就说名它是一个二级表 。

驱动开发:内核枚举PspCidTable句柄表

文章插图
一级表里存放的就是进程和线程对象(加密过的,需要一些计算来解密),二级表里存放的是指向某个一级表的指针,同理三级表存放的是指向二级表的指针 。
x64 系统中,每张表的大小是 0x1000(4096),一级表中存放的是 _handle_table_entry 结构(大小 = 16),二级表和三级表存放的是指针(大小 = 8) 。
我们对 0xffffdc88-7d09b001 抹去低二位,输入dp 0xffffdc887d09b000 输出的结果就是一张二级表,里面存储的就是一级表指针 。
驱动开发:内核枚举PspCidTable句柄表

文章插图
继续查看第一张一级表,输入dp 0xffffdc887962a000命令,我们知道一级句柄表是根据进程或线程ID来索引的,且以4累加,所以第一行对应id = 0,第二行对应id = 4 。根据尝试,PID = 4的进程是System 。
驱动开发:内核枚举PspCidTable句柄表

文章插图
所以此处的第二行0xb281de28-3300ffa7就是加密后的System进程的EPROCESS结构,对于Win10系统来说解密算法(value >> 0x10) & 0xfffffffffffffff0是这样的,我们通过代码计算出来 。
#include <Windows.h>#include <iostream>int _tmain(int argc, _TCHAR* argv[]){ std::cout << "hello lyshark.com" << std::endl; ULONG64 ul_recode = 0xb281de283300ffa7; ULONG64 ul_decode = (LONG64)ul_recode >> 0x10; ul_decode &= 0xfffffffffffffff0; std::cout << "解密后地址: " << std::hex << ul_decode << std::endl; getchar(); return 0;}运行程序得到如下输出,即可知道System系统进程解密后的EPROCESS结构地址是0xffffb281de283300
驱动开发:内核枚举PspCidTable句柄表

文章插图
回到WinDBG调试器,输入命令dt _EPROCESS 0xffffb281de283300解析以下这个结构,输出结果是System进程 。
驱动开发:内核枚举PspCidTable句柄表

文章插图
理论知识总结已经结束了,接下来就是如何实现枚举进程线程了,枚举流程如下:
  • 1.首先找到PspCidTable的地址 。
  • 2.然后找到HANDLE_TBALE的地址 。
  • 3.根据TableCode来判断层次结构 。
  • 4.遍历层次结构来获取对象地址 。
  • 5.判断对象类型是否为进程对象 。
  • 6.判断进程是否有效 。
这里先来实现获取PspCidTable函数的动态地址,代码如下 。
#include <ntifs.h>#include <windef.h>// 获取 PspCidTable// By: LyShark.comBOOLEAN get_PspCidTable(ULONG64* tableAddr){ // 获取 PsLookupProcessByProcessId 地址 UNICODE_STRING uc_funcName; RtlInitUnicodeString(&uc_funcName, L"PsLookupProcessByProcessId"); ULONG64 ul_funcAddr = MmGetSystemRoutineAddress(&uc_funcName); if (ul_funcAddr == NULL) {return FALSE; } DbgPrint("PsLookupProcessByProcessId addr = %p \n", ul_funcAddr); // 前 40 字节有 call(PspReferenceCidTableEntry) /* 0: kd> uf PsLookupProcessByProcessIdnt!PsLookupProcessByProcessId:fffff802`0841cfe0 48895c2418      mov     qword ptr [rsp+18h],rbxfffff802`0841cfe5 56              push    rsifffff802`0841cfe6 4883ec20        sub     rsp,20hfffff802`0841cfea 48897c2438      mov     qword ptr [rsp+38h],rdifffff802`0841cfef 488bf2          mov     rsi,rdxfffff802`0841cff2 65488b3c2588010000 mov   rdi,qword ptr gs:[188h]fffff802`0841cffb 66ff8fe6010000  dec     word ptr [rdi+1E6h]fffff802`0841d002 b203            mov     dl,3fffff802`0841d004 e887000000      call    nt!PspReferenceCidTableEntry (fffff802`0841d090)fffff802`0841d009 488bd8          mov     rbx,raxfffff802`0841d00c 4885c0          test    rax,raxfffff802`0841d00f 7435            je      nt!PsLookupProcessByProcessId+0x66 (fffff802`0841d046)  Branch */ ULONG64 ul_entry = 0; for (INT i = 0; i < 100; i++) {// fffff802`0841d004 e8 87 00 00 00      call    nt!PspReferenceCidTableEntry (fffff802`0841d090)if (*(PUCHAR)(ul_funcAddr + i) == 0xe8){ul_entry = ul_funcAddr + i;break;} } if (ul_entry != 0) {// 解析 call 地址INT i_callCode = *(INT*)(ul_entry + 1);DbgPrint("i_callCode = %p \n", i_callCode);ULONG64 ul_callJmp = ul_entry + i_callCode + 5;DbgPrint("ul_callJmp = %p \n", ul_callJmp);// 来到 call(PspReferenceCidTableEntry) 内找 PspCidTable/*0: kd> uf PspReferenceCidTableEntrynt!PspReferenceCidTableEntry+0x115:fffff802`0841d1a5 488b0d8473f5ff  mov     rcx,qword ptr [nt!PspCidTable (fffff802`08374530)]fffff802`0841d1ac b801000000      mov     eax,1fffff802`0841d1b1 f0480fc107      lock xadd qword ptr [rdi],raxfffff802`0841d1b6 4883c130        add     rcx,30hfffff802`0841d1ba f0830c2400      lock or dword ptr [rsp],0fffff802`0841d1bf 48833900        cmp     qword ptr [rcx],0fffff802`0841d1c3 0f843fffffff    je      nt!PspReferenceCidTableEntry+0x78 (fffff802`0841d108)  Branch*/for (INT i = 0; i < 0x120; i++){// fffff802`0841d1a5 48 8b 0d 84 73 f5 ff  mov     rcx,qword ptr [nt!PspCidTable (fffff802`08374530)]if (*(PUCHAR)(ul_callJmp + i) == 0x48 && *(PUCHAR)(ul_callJmp + i + 1) == 0x8b && *(PUCHAR)(ul_callJmp + i + 2) == 0x0d){// 解析 mov 地址INT i_movCode = *(INT*)(ul_callJmp + i + 3);DbgPrint("i_movCode = %p \n", i_movCode);ULONG64 ul_movJmp = ul_callJmp + i + i_movCode + 7;DbgPrint("ul_movJmp = %p \n", ul_movJmp);// 得到 PspCidTable*tableAddr = ul_movJmp;return TRUE;}} } return FALSE;}VOID UnDriver(PDRIVER_OBJECT driver){ DbgPrint(("Uninstall Driver Is OK \n"));}NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath){ DbgPrint(("hello lyshark \n")); ULONG64 tableAddr = 0; get_PspCidTable(&tableAddr); DbgPrint("PspCidTable Address = %p \n", tableAddr); Driver->DriverUnload = UnDriver; return STATUS_SUCCESS;}

经验总结扩展阅读