介绍
PspCidTable 是一个内核句柄表,存放进程和线程的内核对象(EPROCESS 和 ETHREAD),并通过 PID 和 TID 进行索引(所以进程ID和线程ID不可能相同),ID 号以 4 递增。
获取 PspCidTable 地址
win7:
PsLookupProcessByProcessId(被导出) -> PspCidTable
win10:
PsLookupProcessByProcessId(被导出) -> PspReferenceCidTableEntry -> PspCidTable
手动查询 PspTable
打开 WinDbg,输入: dp PspCidTable
得到 PspTable 地址 ——

输入 dt _handle_table 0xffffc300d9016a80

TableCode 是指向句柄表的指针,低二位(二进制)记录句柄表的等级:0(00)表示一级表,1(01)表示二级表,2(10)表示三级表。这里的 0xffffc300`dda6f001 就说名它是一个二级表。
一级表里存放的就是进程和线程对象(加密过的,需要一些计算来解密),二级表里存放的是指向某个一级表的指针,同理三级表存放的是指向二级表的指针。
x64 系统中,每张表的大小是 0x1000(4096),一级表中存放的是 _handle_table_entry 结构(大小 = 16),二级表和三级表存放的是指针(大小 = 8)。
我们对 0xffffc300dda6f001 抹去低二位,输入 dp 0xffffc300dda6f000

可以看到我这张二级表中有 50个一级表指针。查看第一张一级表:dp 0xffffc300d901a000

我们知道一级句柄表是根据 进程或线程ID来索引的,且以 ‘4’ 累加,所以第一行对应 id = 0,第二行对应 id = 4。 根据尝试,PID = 4 的进程是 System:

所以 0x9888c5afd440d84f 解密后就应该是 System 进程的 EPROCESS。
解密算法:
| 系统版本 | 计算方法 |
|---|---|
| win7 | value & 0xfffffffffffffff0 |
| win8 | (value >> 0x13) & 0xfffffffffffffff0 |
| win10 | (value >> 0x10) & 0xfffffffffffffff0 |
我的系统是 win10,按照上面的计算方式得到的结果是 0xFFFF9888C5AFD440
输入 dt _eprocess 0xFFFF9888C5AFD440 验证:

成功!
代码
// 获取 PspCidTable
BOOLEAN get_PspCidTable(ULONG64* tableAddr) {
// 获取 PsLookupProcessByProcessId 地址
UNICODE_STRING uc_funcName;
RtlInitUnicodeString(&uc_funcName, L"PsLookupProcessByProcessId");
ULONG64 ul_funcAddr = MmGetSystemRoutineAddress(&uc_funcName);
if (ul_funcAddr == NULL) {
//DbgPrint("[LYSM] MmGetSystemRoutineAddress error.\n");
return FALSE;
}
//DbgPrint("[LYSM] PsLookupProcessByProcessId:%p\n", ul_funcAddr);
// 前 40 字节有 call(PspReferenceCidTableEntry)
ULONG64 ul_entry = 0;
for (INT i = 0; i < 40; i++) {
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("[LYSM] i_callCode:%X\n", i_callCode);
ULONG64 ul_callJmp = ul_entry + i_callCode + 5;
//DbgPrint("[LYSM] ul_callJmp:%p\n", ul_callJmp);
// 来到 call(PspReferenceCidTableEntry) 内找 PspCidTable
for (INT i = 0; i < 20; i++) {
if (*(PUCHAR)(ul_callJmp + i) == 0x48 &&
*(PUCHAR)(ul_callJmp + i + 1) == 0x8b &&
*(PUCHAR)

本文深入探讨了PspCidTable的结构和工作原理,包括如何获取其地址,手动查询及解密过程,适用于Win7到Win10系统。通过代码示例展示了遍历进程和线程的方法。

500

被折叠的 条评论
为什么被折叠?



