基于全局句柄表发现隐藏进程
字数 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的地址
  • 全局句柄表存储了所有EPROCESSETHREAD
  • 与进程句柄表不同,全局句柄表项低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 实现效果验证

  1. 首先安装驱动并运行,可以正常遍历所有进程
  2. 使用PEB断链技术隐藏notepad.exe进程
  3. 验证任务管理器和cmd中都无法看到notepad.exe
  4. 再次运行遍历全局句柄表的驱动,仍然可以发现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");
}
基于全局句柄表发现隐藏进程的技术研究 0x00 前言 本文详细介绍了如何通过Windows内核中的全局句柄表 PspCidTable 来发现被隐藏的进程。传统的PEB断链技术虽然可以隐藏进程,但这种方法只是表面上的隐藏,通过全局句柄表仍然可以检测到这些"隐藏"的进程。 0x01 句柄表基础 什么是句柄 当一个进程创建或打开一个内核对象时,将获得一个句柄 通过这个句柄可以访问内核对象 句柄的作用 避免在应用层直接修改内核对象 提供安全层,防止应用层直接操作内核地址导致系统崩溃 句柄表结构 每个句柄表项占8字节 一个页4KB,可存储512个句柄表项 当句柄数量超过512时,句柄表会采用分级存储结构(最多三级) 实验代码:生成句柄 0x02 全局句柄表PspCidTable 关键特性 全局变量 PspCidTable 存储了全局句柄表 _HANDLE_TABLE 的地址 全局句柄表存储了所有 EPROCESS 和 ETHREAD 与进程句柄表不同,全局句柄表项低32位直接指向内核对象 结构同样可分为1、2、3级 定位PspCidTable的方法 通过分析 PsLookupProcessByProcessId 函数的反汇编代码,可以发现它引用了 PspCidTable : 0x03 遍历PspCidTable的实现 关键数据结构定义 遍历实现代码 0x04 实现效果验证 首先安装驱动并运行,可以正常遍历所有进程 使用PEB断链技术隐藏notepad.exe进程 验证任务管理器和cmd中都无法看到notepad.exe 再次运行遍历全局句柄表的驱动,仍然可以发现notepad.exe 结论 PEB断链只能实现表面上的进程隐藏 系统主要通过全局句柄表而非PEB链表来跟踪进程 要实现真正的进程隐藏需要从全局句柄表中移除条目,但这可能导致系统不稳定 全局句柄表遍历技术可用于检测高级隐藏技术 完整驱动代码