x64驱动 遍历 PspCidTable 枚举隐进程和线程

介绍

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。
解密算法:

系统版本计算方法
win7value & 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;
}
  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值