基于全局句柄表发现隐藏进程
字数 808 2025-08-07 08:22:29
基于全局句柄表发现隐藏进程的技术研究
0x00 前言
本文详细介绍了如何通过Windows内核中的全局句柄表PspCidTable来发现被隐藏的进程。传统的PEB断链技术虽然可以隐藏进程,但这种方法只是表面上的隐藏,通过全局句柄表仍然可以检测到这些"隐藏"的进程。
0x01 句柄表基础
什么是句柄
- 当一个进程创建或打开一个内核对象时,将获得一个句柄
- 通过这个句柄可以访问内核对象
句柄的作用
- 避免在应用层直接修改内核对象
- 提供安全层,防止应用层直接操作内核地址导致系统崩溃
句柄表结构
- 每个句柄表项占8字节
- 一个页4KB,可存储512个句柄表项
- 当句柄数量超过512时,句柄表会采用分级存储结构(最多三级)
实验代码:生成句柄
#include "stdafx.h"
#include <windows.h>
#include <tchar.h>
int _tmain(int argc, _TCHAR* argv[])
{
DWORD PID;
HANDLE hPro = NULL;
HWND hwnd = FindWindowA(NULL, "计算器");
GetWindowThreadProcessId(hwnd, &PID);
for (int i = 0; i < 100; i++)
{
hPro = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION |
PROCESS_VM_WRITE | PROCESS_VM_READ, TRUE, PID);
printf("句柄:%x\n", hPro);
}
SetHandleInformation(hPro, HANDLE_FLAG_PROTECT_FROM_CLOSE,
HANDLE_FLAG_PROTECT_FROM_CLOSE);
getchar();
return 0;
}
0x02 全局句柄表PspCidTable
关键特性
- 全局变量
PspCidTable存储了全局句柄表_HANDLE_TABLE的地址 - 全局句柄表存储了所有
EPROCESS和ETHREAD - 与进程句柄表不同,全局句柄表项低32位直接指向内核对象
- 结构同样可分为1、2、3级
定位PspCidTable的方法
通过分析PsLookupProcessByProcessId函数的反汇编代码,可以发现它引用了PspCidTable:
PspCidTable = **(PULONG*)((ULONG)PsLookupProcessByProcessId + 26);
DbgPrint("PspCidTable = %x\n", PspCidTable);
0x03 遍历PspCidTable的实现
关键数据结构定义
typedef struct _HANDLE_TABLE_ENTRY {
union {
PVOID Object;
ULONG ObAttributes;
PHANDLE_TABLE_ENTRY_INFO InfoTable;
ULONG_PTR Value;
};
union {
union {
ACCESS_MASK GrantedAccess;
struct {
USHORT GrantedAccessIndex;
USHORT CreatorBackTraceIndex;
};
};
LONG NextFreeTableEntry;
};
} HANDLE_TABLE_ENTRY, *PHANDLE_TABLE_ENTRY;
typedef struct _OBJECT_HEADER {
LONG PointerCount;
union {
LONG HandleCount;
PVOID NextToFree;
};
POBJECT_TYPE Type;
UCHAR NameInfoOffset;
UCHAR HandleInfoOffset;
UCHAR QuotaInfoOffset;
UCHAR Flags;
union {
POBJECT_CREATE_INFORMATION ObjectCreateInfo;
PVOID ObjectCreateInfo;
PVOID QuotaBlockCharged;
};
PSECURITY_DESCRIPTOR SecurityDescriptor;
QUAD Body;
} OBJECT_HEADER, *POBJECT_HEADER;
遍历实现代码
ULONG PspCidTable;
ULONG TableCode, TableLevel;
L1P TableLevel1;
L2P TableLevel2;
L3P TableLevel3;
// 获取PspCidTable和表信息
PspCidTable = **(PULONG*)((ULONG)PsLookupProcessByProcessId + 26);
TableCode = *(PULONG)PspCidTable;
TableLevel = TableCode & 0x03; // 句柄表等级
TableCode = TableCode & ~0x03; // 清除等级标志位
RtlInitUnicodeString(&ProcessString, L"Process");
RtlInitUnicodeString(&ThreadString, L"Thread");
switch (TableLevel)
{
case 0: // 一级句柄表
{
TableLevel1 = (L1P)TableCode;
for (i = 0; i < 512; i++)
{
if (MmIsAddressValid(TableLevel1[i].Object))
{
HandleAddr = ((ULONG)(TableLevel1[i].Object) & ~0x03);
pObjectHeader = (POBJECT_HEADER)(HandleAddr - 0x18);
if (RtlCompareUnicodeString(&pObjectHeader->Type->Name,
&ProcessString, TRUE) == 0)
{
pEprocess = (PEPROCESS)HandleAddr;
ImageFileName = (PCHAR)pEprocess + 0x174;
DbgPrint("进程名:%s\n", ImageFileName);
}
// 处理线程情况...
}
}
break;
}
case 1: // 二级句柄表
{
// 类似处理,增加一层循环
break;
}
case 2: // 三级句柄表
{
// 类似处理,增加两层循环
break;
}
}
0x04 实现效果验证
- 首先安装驱动并运行,可以正常遍历所有进程
- 使用PEB断链技术隐藏notepad.exe进程
- 验证任务管理器和cmd中都无法看到notepad.exe
- 再次运行遍历全局句柄表的驱动,仍然可以发现notepad.exe
结论
- PEB断链只能实现表面上的进程隐藏
- 系统主要通过全局句柄表而非PEB链表来跟踪进程
- 要实现真正的进程隐藏需要从全局句柄表中移除条目,但这可能导致系统不稳定
- 全局句柄表遍历技术可用于检测高级隐藏技术
完整驱动代码
#include <ntifs.h>
// 数据结构定义...
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING reg_path)
{
// 实现代码...
}
VOID DriverUnload(PDRIVER_OBJECT pDriver)
{
DbgPrint("DriverUnload successfully!\n");
}