25.实现过TP保护ACE保护NP保护BE保护EAC保护-内存读写检测(私有句柄表和全局句柄表的句柄提权)-Windows驱动

免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动!

本次游戏没法给

内容参考于:微尘网络安全

上一个内容:24.IDA逆向句柄表算法-Windows驱动

效果图:

不管什么检测,在句柄上的处理都是一样的,下方是提权代码,原理就是找到要提权进程的_EPROCESS结构,然后读取它里面的私有句柄表,然后从0开始遍历,一直到找不到(NULL),把它们的权限全部设置为0x1FFFFF,也就是最高权限就可以了,当前操作系统版本是Windows11的10.0.26200版本,如果其它版本结构体可能会有变化,需要去https://www.vergiliusproject.com/这里找,注意这里给的结构体没问题,但是union会有问题,怎样修改参考下方的结构体,一般变化不大,或者复制windbg里的结构信息,让ai生成,所以电脑设置好后一定不能更新,更新了可能就没法用了,或者也可以不用结构体,可以直接使用偏移的方式遍历私有句柄表

c++ 复制代码
// 【核心宏1】句柄值递增步长:Windows句柄表中句柄值按4递增(系统规定,避免句柄冲突)
#define HANDLE_VALUE_INC 4

// 【EXHANDLE结构体】- 系统底层句柄表示结构(8字节)
// 作用:封装句柄的核心信息(Tag标记位 + Index索引),是访问句柄表的"索引钥匙"
//0x8 bytes (sizeof)
typedef struct  _EXHANDLE
{
	union
	{
		struct
		{
			ULONG TagBits : 2;        //0x0  句柄标记位(2位):用于系统内部校验句柄有效性
			ULONG Index : 30;         //0x0  句柄索引(30位):句柄在句柄表中的位置索引(核心)
		};
		VOID* GenericHandleOverlay;   //0x0  通用句柄覆盖指针:兼容不同类型句柄的指针表示
		ULONGLONG Value;              //0x0  句柄的64位数值(整合TagBits+Index)
	};
} EXHANDLE, * PEXHANDLE;

// 【HANDLE_TABLE_ENTRY结构体】- 句柄表项(每个句柄对应一个该结构体,存储句柄核心属性)
// 作用:记录单个句柄的权限、引用计数、指向的内核对象(如进程/线程/文件)等信息
typedef struct _HANDLE_TABLE_ENTRY
{
	union
	{
		LONG_PTR VolatileLowValue;    // 易失性低32位值(多线程场景下的低值)
		LONG_PTR LowValue;            // 低32位值(核心存储区)
		PVOID InfoTable;              // 指向句柄信息表的指针
		LONG_PTR RefCountField;       // 句柄引用计数字段(记录多少个地方引用该句柄)
		struct
		{
			ULONG_PTR Unlocked : 1;       // 1位:句柄是否未锁定(0=锁定,1=未锁定)
			ULONG_PTR RefCnt : 16;       // 16位:句柄引用计数(最大值65535)
			ULONG_PTR Attributes : 3;    // 3位:句柄属性(如是否继承、是否保护)
			ULONG_PTR ObjectPointerBits : 44; // 44位:指向内核对象的指针(核心!需转换为真实指针)
		};
	};
	union
	{
		LONG_PTR HighValue;               // 高32位值
		struct _HANDLE_TABLE_ENTRY* NextFreeHandleEntry; // 指向下一个空闲句柄表项(内存管理用)
		EXHANDLE LeafHandleValue;         // 叶子节点句柄值(多层句柄表用)
		struct
		{
			ULONG32 GrantedAccessBits : 25; // 25位:句柄的实际授权权限(核心!对应之前的0x1FFFFF)
			ULONG32 NoRightsUpgrade : 1;    // 1位:是否禁止权限升级(0=允许,1=禁止)
			ULONG32 Spare1 : 6;             // 6位:预留位(无实际作用)
		};
		ULONG32 Spare2;                   // 备用32位值
	};
} HANDLE_TABLE_ENTRY, * PHANDLE_TABLE_ENTRY;

// 【HANDLE_TABLE_FREE_LIST结构体】- 句柄表空闲链表(0x40字节)
// 作用:管理句柄表中的空闲表项,提高句柄分配/释放效率
typedef struct _HANDLE_TABLE_FREE_LIST
{
	ULONG_PTR FreeListLock;              //0x0  空闲链表锁(防止多线程操作冲突)
	PHANDLE_TABLE_ENTRY FirstFreeHandleEntry; //0x8  第一个空闲句柄表项指针
	PHANDLE_TABLE_ENTRY LastFreeHandleEntry;  //0x10 最后一个空闲句柄表项指针
	LONG HandleCount;                    //0x18 空闲句柄数量
	ULONG HighWaterMark;                 //0x1c 句柄表使用的高水位标记(记录最大使用量)
}HANDLE_TABLE_FREE_LIST,* PHANDLE_TABLE_FREE_LIST;

// 【HANDLE_TABLE结构体】- 进程句柄表的根结构(0x80字节)
// 作用:每个进程有且仅有一个该结构体,是整个句柄表的"总控台"
//0x80 bytes (sizeof)
typedef struct _HANDLE_TABLE
{
	ULONG NextHandleNeedingPool;         //0x0  下一个需要分配内存池的句柄值(句柄表扩容用)
	LONG ExtraInfoPages;                 //0x4  额外信息页数量(句柄表扩展页)
	volatile ULONGLONG TableCode;        //0x8  句柄表代码(核心!存储句柄表层级+基地址)
	struct _EPROCESS* QuotaProcess;      //0x10 关联的配额进程(权限配额管控)
	struct _LIST_ENTRY HandleTableList;  //0x18 句柄表链表节点(系统串联所有进程句柄表)
	ULONG UniqueProcessId;               //0x28 进程PID(句柄表所属进程的ID)
	union
	{
		ULONG Flags;                     //0x2c 句柄表标记位
		struct
		{
			UCHAR StrictFIFO : 1;            //0x2c 严格FIFO分配句柄(0=否,1=是)
			UCHAR EnableHandleExceptions : 1;//0x2c 是否启用句柄异常(0=否,1=是)
			UCHAR Rundown : 1;               //0x2c 句柄表是否正在清理(0=否,1=是)
			UCHAR Duplicated : 1;            //0x2c 句柄表是否被复制(0=否,1=是)
			UCHAR RaiseUMExceptionOnInvalidHandleClose : 1; //0x2c 关闭无效句柄时是否抛用户层异常
		};
	};
	ULONG_PTR HandleContentionEvent;     //0x30 句柄竞争事件(多线程操作句柄时的同步事件)
	ULONG_PTR HandleTableLock;           //0x38 句柄表锁(保护句柄表读写)
	union
	{
		HANDLE_TABLE_FREE_LIST FreeLists[1]; //0x40 空闲链表(管理空闲句柄表项)
		struct
		{
			UCHAR ActualEntry[32];          //0x40 实际句柄表项(初始32个)
			struct _HANDLE_TRACE_DEBUG_INFO* DebugInfo; //0x60 调试信息指针
		};
	};
} HANDLE_TABLE, * PHANDLE_TABLE;

// 【核心宏2】Win11系统中EPROCESS结构体里句柄表的偏移量(0x300)
// 作用:通过EPROCESS+0x300能找到进程的句柄表根结构(不同系统版本偏移不同,Win11固定0x300)
#define HANDLE_TABLE_OFFSET_WIN11	0x300
// 【核心宏3】页大小(等于系统PAGE_SIZE,通常0x1000=4096字节)
#define TABLE_PAGE_SIZE	PAGE_SIZE
// 【核心宏4】低层级句柄表项数量 = 页大小 / 单个句柄表项大小(计算单层句柄表能存多少个句柄)
#define LOWLEVEL_COUNT (TABLE_PAGE_SIZE / sizeof(HANDLE_TABLE_ENTRY))
// 【核心宏5】中间层级句柄表项数量 = 页大小 / 句柄表项指针大小(多层句柄表用)
#define MIDLEVEL_COUNT (PAGE_SIZE / sizeof(PHANDLE_TABLE_ENTRY))

// 【ExpLookupHandleTableEntry函数】- 核心!根据句柄值查找对应的句柄表项
// 入参1:HandleTable - 进程句柄表根结构指针
// 入参2:tHandle - 要查找的EXHANDLE句柄
// 返回值:PHANDLE_TABLE_ENTRY - 找到的句柄表项(NULL=未找到)
// 作用:把"句柄值"转换成"句柄表项指针",是操作句柄权限的前提
PHANDLE_TABLE_ENTRY ExpLookupHandleTableEntry(
	IN PHANDLE_TABLE HandleTable,
	IN EXHANDLE tHandle
)
{
	ULONG_PTR i, j, k;                  // 临时变量:存储句柄表层级索引
	ULONG_PTR CapturedTable;            // 句柄表基地址(剥离层级后的地址)
	ULONG TableLevel;                   // 句柄表层级(0/1/2层,系统最多3层句柄表)
	PHANDLE_TABLE_ENTRY Entry = NULL;   // 要返回的句柄表项指针
	EXHANDLE Handle;                    // 处理后的句柄(清空TagBits)

	PUCHAR TableLevel1;                 // 1级句柄表基地址(最底层)
	PUCHAR TableLevel2;                 // 2级句柄表基地址(中间层)
	PUCHAR TableLevel3;                 // 3级句柄表基地址(最高层)

	ULONG_PTR MaxHandle;                // 进程句柄表的最大句柄值(超过则无效)

	PAGED_CODE();                       // 标记该函数可在分页内存中执行(内核规范)
	//__debugbreak();                  // 调试断点(可注释)

	// 步骤1:清理句柄的TagBits(只保留Index索引,避免标记位干扰查找)
	Handle = tHandle;
	Handle.TagBits = 0;

	// 步骤2:检查句柄值是否超出进程句柄表的最大范围(超出则无效)
	MaxHandle = *(volatile ULONG*)&HandleTable->NextHandleNeedingPool;
	if (Handle.Value >= MaxHandle)
	{
		return NULL;
	}

	// 步骤3:获取句柄表基地址+层级(TableCode=基地址+层级(低2位))
	CapturedTable = *(volatile ULONG_PTR*)&HandleTable->TableCode;
	TableLevel = (ULONG)(CapturedTable & 3); // 低2位是层级(0/1/2)
	CapturedTable = CapturedTable - TableLevel; // 剥离层级,得到纯基地址

	// 步骤4:根据句柄表层级查找对应的句柄表项(系统句柄表分0/1/2层,层数越多存储的句柄越多)
	switch (TableLevel)
	{
	// 层级0:单层句柄表(小进程,句柄少)
	case 0:
	{
		TableLevel1 = (PUCHAR)CapturedTable; // 1级表基地址

		// 计算句柄表项位置:基地址 + 句柄值 * (表项大小/递增步长)
		Entry = (PHANDLE_TABLE_ENTRY)&TableLevel1[Handle.Value *
			(sizeof(HANDLE_TABLE_ENTRY) / HANDLE_VALUE_INC)];

		break;
	}

	// 层级1:双层句柄表(中等进程,句柄较多)
	case 1:
	{
		TableLevel2 = (PUCHAR)CapturedTable; // 2级表基地址

		// 计算低层级索引i + 中间层索引j
		i = Handle.Value % (LOWLEVEL_COUNT * HANDLE_VALUE_INC);
		Handle.Value -= i;
		j = Handle.Value / ((LOWLEVEL_COUNT * HANDLE_VALUE_INC) / sizeof(PHANDLE_TABLE_ENTRY));

		// 先找2级表→再找1级表→最后找具体表项
		TableLevel1 = (PUCHAR) * (PHANDLE_TABLE_ENTRY*)&TableLevel2[j];
		Entry = (PHANDLE_TABLE_ENTRY)&TableLevel1[i * (sizeof(HANDLE_TABLE_ENTRY) / HANDLE_VALUE_INC)];

		break;
	}

	// 层级2:三层句柄表(大进程,句柄极多)
	case 2:
	{
		TableLevel3 = (PUCHAR)CapturedTable; // 3级表基地址

		// 计算低层级索引i + 中间层索引j + 高层级索引k
		i = Handle.Value % (LOWLEVEL_COUNT * HANDLE_VALUE_INC);
		Handle.Value -= i;
		k = Handle.Value / ((LOWLEVEL_COUNT * HANDLE_VALUE_INC) / sizeof(PHANDLE_TABLE_ENTRY));
		j = k % (MIDLEVEL_COUNT * sizeof(PHANDLE_TABLE_ENTRY));
		k -= j;
		k /= MIDLEVEL_COUNT;

		// 先找3级表→再找2级表→再找1级表→最后找具体表项
		TableLevel2 = (PUCHAR) * (PHANDLE_TABLE_ENTRY*)&TableLevel3[k];
		TableLevel1 = (PUCHAR) * (PHANDLE_TABLE_ENTRY*)&TableLevel2[j];
		Entry = (PHANDLE_TABLE_ENTRY)&TableLevel1[i * (sizeof(HANDLE_TABLE_ENTRY) / HANDLE_VALUE_INC)];

		break;
	}

	default: _assume(0); // 无效层级(系统不会出现,触发断言)
	}

	return Entry; // 返回找到的句柄表项(NULL=未找到)
}

// 【内核导出函数声明】ObGetObjectType - 获取内核对象的类型(如Process/Thread/File)
// 作用:判断句柄指向的对象是不是"进程"类型(核心!只修改进程句柄的权限)
NTKERNELAPI POBJECT_TYPE ObGetObjectType(PVOID Object);

// 【RestoreObjectAccess函数】- 核心!遍历目标进程的句柄表,修改所有Process类型句柄的权限为0x1FFFFF
// 入参:EProcess - 目标进程的EPROCESS指针
// 返回值:NTSTATUS - 执行状态(STATUS_SUCCESS=成功,其他=失败)
NTSTATUS RestoreObjectAccess(PEPROCESS EProcess/*,ULONG32 PassiveId */)
{
	NTSTATUS Status = STATUS_UNSUCCESSFUL; // 默认返回失败
	ULONG_PTR Handle = 0;                 // 遍历用的句柄值(从0开始递增)
	PHANDLE_TABLE_ENTRY Entry = NULL;     // 当前遍历到的句柄表项
	PVOID Object = NULL;                  // 句柄指向的内核对象(如进程对象)
	POBJECT_TYPE ObjectType = NULL;       // 内核对象类型(如Process)

	// 步骤1:遍历目标进程的所有句柄(按4递增,符合HANDLE_VALUE_INC)
	for (Handle = 0;; Handle += HANDLE_VALUE_INC)
	{
		// 步骤2:根据句柄值查找对应的句柄表项
		// 关键:(PUCHAR)EProcess + HANDLE_TABLE_OFFSET_WIN11 → 找到进程的句柄表根结构
		Entry = ExpLookupHandleTableEntry(*(PHANDLE_TABLE*)((PUCHAR)EProcess + HANDLE_TABLE_OFFSET_WIN11), *(PEXHANDLE)&Handle);
		if (Entry == NULL) // 句柄表项为空(遍历到末尾),退出循环
		{
			break;
		}

		// 步骤3:从句柄表项的ObjectPointerBits还原真实的内核对象指针
		// ObjectPointerBits是44位,需左移4位 + 高位补0xFFFF + 偏移0x30(内核对象地址转换规则)
		*(ULONG_PTR*)&Object = Entry->ObjectPointerBits;
		*(ULONG_PTR*)&Object <<= 4;          // 左移4位(补全指针位)
		if (Object == NULL)                  // 对象指针无效,跳过
		{
			continue;
		}
		*(ULONG_PTR*)&Object |= 0xFFFF000000000000; // 高位补0xFFFF(64位指针规则)
		*(ULONG_PTR*)&Object += 0x30;        // 偏移0x30(指向真实的内核对象)

		// 步骤4:获取对象类型(判断是不是Process类型)
		ObjectType = ObGetObjectType(Object);
		if (ObjectType == NULL)              // 对象类型无效,跳过
		{
			continue;
		}

		// 步骤5:判断对象类型是否是"Process"(进程)
		// 关键:ObjectType+0x18是对象类型名(Unicode字符串,如L"Process")
		if (wcscmp(*(PCWSTR*)((PUCHAR)ObjectType + 0x18), L"Process") == 0)
		{
			// 调试打印:确认找到Process类型句柄,开始修改权限
			DbgPrintEx(77, 0, "R0 GrantedAccessBits\n\n");
			// 核心操作:将句柄的实际授权权限设为0x1FFFFF(PROCESS_ALL_ACCESS,最大权限)
			// GrantedAccessBits是25位,刚好能容纳0x1FFFFF(21位),无溢出
			Entry->GrantedAccessBits = 0x1FFFFF;
		}
	}

	// 步骤6:设置返回状态为成功,并释放进程对象引用(内核规范)
	Status = STATUS_SUCCESS;
	ObDereferenceObject(EProcess); // 释放EPROCESS引用(避免内存泄漏)

	return Status;
}

// 【进程句柄前置回调函数】- 所有进程句柄创建/复制操作前触发
// 核心逻辑:
// 1. 给"aaaa.exe"的句柄请求设置最大权限(0x1FFFFF);
// 2. 检测目标PID(18184)的进程,强制遍历其句柄表修改Process句柄权限为0x1FFFFF;
OB_PREOP_CALLBACK_STATUS PobPreOperationCallback(
	PVOID RegistrationContext,  // 注册回调时的自定义参数(此处为NULL)
	POB_PRE_OPERATION_INFORMATION OperationInformation  // 句柄操作详情
)
{
	// 步骤1:获取发起句柄操作的进程(操作方)EPROCESS
	PEPROCESS pep = IoGetCurrentProcess();
	// 步骤2:提取操作方进程名(如"aaaa.exe")
	char* nName = PsGetProcessImageFileName(pep);

	// 步骤3:判断操作方是否是"aaaa.exe"(不区分大小写)
	if (_strnicmp(nName, "aaaa.exe", strlen("aaaa.exe")) == 0) {
		// 调试打印:确认匹配到aaaa.exe
		DbgPrintEx(DPFLTR_IHVDRIVER_ID, 0, "进入nName = %s\n", nName);
		
		// 给aaaa.exe的句柄请求设置最大权限(创建/复制句柄都生效)
		OperationInformation->Parameters->CreateHandleInformation.DesiredAccess = 0x1FFFFF;
		OperationInformation->Parameters->DuplicateHandleInformation.DesiredAccess = 0x1FFFFF;
	}

	// 步骤4:处理目标PID(18184)的进程,强制修改其句柄权限
	PEPROCESS npep = NULL;
	// 提取被操作进程的PID(OperationInformation->Object是被操作进程的EPROCESS)
	UINT32  目标进程PID = (UINT32)(UINT_PTR)PsGetProcessId((PEPROCESS)OperationInformation->Object);
	// 判断被操作进程是否是目标PID(18184)
	if (目标进程PID == 18184)
	{
		//DbgPrintEx(77, 0, "进入目标进程 成功\n");
		//DbgBreakPoint();
		//DbgPrintEx(DPFLTR_IHVDRIVER_ID, 0, " 进入目标进程PID=%s \n", nName);
		
		// 根据PID查找目标进程的EPROCESS(成功则返回STATUS_SUCCESS)
		if (PsLookupProcessByProcessId((HANDLE)目标进程PID, &npep) == STATUS_SUCCESS)
		{
			// 核心调用:遍历目标进程句柄表,修改所有Process句柄权限为0x1FFFFF,提权
			RestoreObjectAccess(npep);
		}
	}

	// 【致命坑点】:必须返回回调状态!补充后才不会蓝屏
	// return OB_PREOP_SUCCESS;
}

相关推荐
企鹅侠客2 小时前
第07章—实战应用篇:List命令详解与实战(下)
windows·redis·log4j·list
Huazzi.2 小时前
PowerShell 配置以及使用指南
windows·git·编辑器·shell·powershell·效率
じ☆冷颜〃3 小时前
二分查找的推广及其在排序与链表结构中的关联
网络·windows·经验分享·笔记·算法·链表
古城小栈3 小时前
AI直连Windows:Windows MCP开源,开启无视觉操控新时代
人工智能·windows
冷雨夜中漫步3 小时前
Kubernetes入门笔记 ——(4)Windows搭建k8s测试集群
windows·笔记·kubernetes
星空椰4 小时前
Windows 安装 Oracle 19c Instant Client
数据库·windows·oracle
蒜丶13 小时前
Windows 11 22H2 跳过联网激活
windows
摘星编程16 小时前
Elasticsearch(es)在Windows系统上的安装与部署(含Kibana)
windows·elasticsearch·kibana
YJlio17 小时前
PsPing 学习笔记(14.1):ICMP Ping 进阶——替代系统 ping 的正确姿势
windows·笔记·学习