PspCidTable 是一个内核句柄表,存放进程和线程的内核对象(EPROCESS 和 ETHREAD),并通过 PID 和 TID 进行索引(所以进程ID和线程ID不可能相同),ID 号以 4 递增。
win7:
PsLookupProcessByProcessId(被导出) -> PspCidTable
win10:
PsLookupProcessByProcessId(被导出) -> PspReferenceCidTableEntry -> PspCidTable
打开 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)(ul_callJmp + i + 2) == 0x05) {
// 解析 mov 地址
INT i_movCode = *(INT*)(ul_callJmp+i + 3);
//DbgPrint("[LYSM] i_movCode:%X\n", i_movCode);
ULONG64 ul_movJmp = ul_callJmp+i + i_movCode + 7;
//DbgPrint("[LYSM] ul_movJmp:%p\n", ul_movJmp);
// 得到 PspCidTable
*tableAddr = ul_movJmp;
return TRUE;
}
}
}
// 前 40字节没有 call
else {
// 直接在 PsLookupProcessByProcessId 找 PspCidTable
for (INT i = 0; i < 70; i++) {
if(*(PUCHAR)(ul_funcAddr + i) == 0x49 &&
*(PUCHAR)(ul_funcAddr + i + 1) == 0x8b &&
*(PUCHAR)(ul_funcAddr + i + 2) == 0xdc &&
*(PUCHAR)(ul_funcAddr + i + 3) == 0x48 &&
*(PUCHAR)(ul_funcAddr + i + 4) == 0x8b &&
*(PUCHAR)(ul_funcAddr + i + 5) == 0xd1 &&
*(PUCHAR)(ul_funcAddr + i + 6) == 0x48 &&
*(PUCHAR)(ul_funcAddr + i + 7) == 0x8b){
// 解析 mov 地址
INT i_movCode = *(INT*)(ul_funcAddr+i+6 + 3);
//DbgPrint("[LYSM] i_movCode:%X\n", i_movCode);
ULONG64 ul_movJmp = ul_funcAddr+i+6 + i_movCode + 7;
//DbgPrint("[LYSM] ul_movJmp:%p\n", ul_movJmp);
// 得到 PspCidTable
*tableAddr = ul_movJmp;
return TRUE;
}
}
}
return FALSE;
}
/* 解析一级表
BaseAddr:一级表的基地址
index1:第几个一级表
index2:第几个二级表
*/
VOID parse_table_1(ULONG64 BaseAddr,INT index1,INT index2) {
//DbgPrint("[LYSM] BaseAddr 1:%p\n", BaseAddr);
// 获取系统版本
RTL_OSVERSIONINFOEXW OSVersion = { 0 };
OSVersion.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
RtlGetVersion((PRTL_OSVERSIONINFOW)&OSVersion);
// 遍历一级表(每个表项大小 16 ),表大小 4k,所以遍历 4096/16 = 526 次
PEPROCESS p_eprocess = NULL;
PETHREAD p_ethread = NULL;
INT i_id = 0;
for (INT i = 0; i < 256; i++) {
if (!MmIsAddressValid((PVOID64)(BaseAddr + i * 16))) {
//DbgPrint("[LYSM] 非法地址:%p\n", BaseAddr + i * 16);
continue;
}
// win10
if (OSVersion.dwMajorVersion == 10 && OSVersion.dwMinorVersion == 0) {
ULONG64 ul_recode = *(PULONG64)(BaseAddr + i * 16);
// 解密
ULONG64 ul_decode = (LONG64)ul_recode >> 0x10;
ul_decode &= 0xfffffffffffffff0;
// 判断是进程还是线程
i_id = i*4 + 1024*index1 + 512*index2*1024;
if (PsLookupProcessByProcessId(i_id , &p_eprocess) == STATUS_SUCCESS) {
DbgPrint("[LYSM] PID:%d , i:%d , addr:%p , object:%p\n", i_id , i, BaseAddr + i*0x10, ul_decode);
}
else if (PsLookupThreadByThreadId(i_id , &p_ethread) == STATUS_SUCCESS) {
DbgPrint("[LYSM] TID:%d , i:%d , addr:%p , object:%p\n", i_id , i, BaseAddr + i*0x10, ul_decode);
}
}
// win7
if (OSVersion.dwMajorVersion == 6 && OSVersion.dwMinorVersion == 1) {
ULONG64 ul_recode = *(PULONG64)(BaseAddr + i * 16);
// 解密
ULONG64 ul_decode = ul_recode & 0xfffffffffffffff0;
// 判断是进程还是线程
i_id = i*4 + 1024*index1 + 512*index2*1024;
if (PsLookupProcessByProcessId(i_id , &p_eprocess) == STATUS_SUCCESS) {
DbgPrint("[LYSM] PID:%d , i:%d , addr:%p , object:%p\n", i_id , i, BaseAddr + i*0x10, ul_decode);
}
else if (PsLookupThreadByThreadId(i_id , &p_ethread) == STATUS_SUCCESS) {
DbgPrint("[LYSM] TID:%d , i:%d , addr:%p , object:%p\n", i_id , i, BaseAddr + i*0x10, ul_decode);
}
else { continue; }
}
}
}
/* 解析二级表
BaseAddr:二级表基地址
index2:第几个二级表
*/
VOID parse_table_2(ULONG64 BaseAddr, INT index2) {
//DbgPrint("[LYSM] BaseAddr 2:%p\n", BaseAddr);
// 遍历二级表(每个表项大小 8),表大小 4k,所以遍历 4096/8 = 512 次
ULONG64 ul_baseAddr_1 = 0;
for (INT i = 0; i < 512; i++) {
if (!MmIsAddressValid((PVOID64)(BaseAddr + i * 8))) {
//DbgPrint("[LYSM] 非法二级表指针(1):%p\n", BaseAddr + i * 8);
continue;
}
if (!MmIsAddressValid((PVOID64)*(PULONG64)(BaseAddr + i * 8))) {
//DbgPrint("[LYSM] 非法二级表指针(2):%p\n", BaseAddr + i * 8);
continue;
}
ul_baseAddr_1 = *(PULONG64)(BaseAddr + i * 8);
parse_table_1(ul_baseAddr_1, i, index2);
}
}
/* 解析三级表
BaseAddr:三级表基地址
*/
VOID parse_table_3(ULONG64 BaseAddr) {
//DbgPrint("[LYSM] BaseAddr 3:%p\n", BaseAddr);
// 遍历三级表(每个表项大小 8),表大小 4k,所以遍历 4096/8 = 512 次
ULONG64 ul_baseAddr_2 = 0;
for (INT i = 0; i < 512; i++) {
if (!MmIsAddressValid((PVOID64)(BaseAddr + i * 8))) { continue; }
if (!MmIsAddressValid((PVOID64) * (PULONG64)(BaseAddr + i * 8))) { continue; }
ul_baseAddr_2 = *(PULONG64)(BaseAddr + i * 8);
parse_table_2(ul_baseAddr_2, i);
}
}
/* 遍历进程和线程
cidTableAddr:PspCidTable 地址
*/
BOOLEAN enum_PspCidTable(ULONG64 cidTableAddr) {
// 获取 _HANDLE_TABLE 的 TableCode
ULONG64 ul_tableCode = *(PULONG64)(((ULONG64)*(PULONG64)cidTableAddr) + 8);
//DbgPrint("[LYSM] ul_tableCode:%p\n", ul_tableCode);
// 取低 2位(二级制11 = 3)
INT i_low2 = ul_tableCode & 3;
//DbgPrint("[LYSM] i_low2:%X\n", i_low2);
// 一级表
if (i_low2 == 0) {
// TableCode 低 2位抹零(二级制11 = 3)
parse_table_1(ul_tableCode & (~3),0,0);
}
// 二级表
else if (i_low2 == 1) {
// TableCode 低 2位抹零(二级制11 = 3)
parse_table_2(ul_tableCode & (~3),0);
}
// 三级表
else if (i_low2 == 2) {
// TableCode 低 2位抹零(二级制11 = 3)
parse_table_3(ul_tableCode & (~3));
}
else {
DbgPrint("[LYSM] i_low2 非法!\n");
return FALSE;
}
return TRUE;
}
调用:
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObj, PUNICODE_STRING pRegistryString)
{
// 初始化省略...
// ...
// ...
// 测试
ULONG64 tableAddr = 0;
if (get_PspCidTable(&tableAddr) == FALSE) {
DbgPrint("[LYSM] get_PspCidTable error.\n");
}
else {
enum_PspCidTable(tableAddr);
}
return STATUS_SUCCESS;
}