-
-
[原创]PTE hook 可hook内核函数不pg
-
发表于:
2025-7-17 21:02
1642
-
[原创]PTE hook 可hook内核函数不pg
网上找了很多代码,但都不能直接用。然后缝缝补补就用了。原本是想通过ptebase计算pte地址的,这个方法可以通用很多版本,但还是通过CR3寄存器逐级计算页表项地址,因为我没弄明白ptebase ,反正一直计算结果错误,有人能完善就好了。这个代码适用于win10 10240版本。
代码原理很简单,通过回调函数检查是否有人调用要hook的函数(这里是NtCreateFile),然后拦截调用进程并检查是否是系统进程如果是就隔离并hook全局内核函数,但假如是一般进程就隔离这个进程的页表然后hook。后面调用这个被hook函数都会跳转到hook函数执行一系列操作(可以修改内核函数参数或者返回值),最后执行正常内核函数。
整体执行流程
驱动加载(DriverEntry):初始化驱动,获取目标函数地址(如NtCreateFile),安装钩子。
钩子安装(pte_hook):隔离目标函数所在的内存页,修改其内容以实现跳转。
拦截执行:当目标函数被调用时,跳转到自定义处理函数(hook_nt_create_file)。
驱动卸载:恢复原始函数代码,释放资源。
函数作用详解
1. 辅助函数
read_cr3(): 读取CR3寄存器,获取当前进程的页目录物理地址。
invalidate_page(void* addr): 使用__invlpg指令刷新TLB缓存,确保内存更改生效。
physical_to_virtual(QWORD pa): 将物理地址转换为虚拟地址(通过MmGetVirtualForPhysical)。
virtual_to_physical(void* va): 将虚拟地址转换为物理地址(通过MmGetPhysicalAddress)。
get_pte_base(): 获取PTE基地址(通过遍历PML4表找到CR3对应的页表基址)。
get_pages_table(PTE_TABLE* Table): 根据线性地址(虚拟地址)获取其对应的各级页表项(PML4E, PDPTE, PDE, PTE)。
split_large_pages(PDE* in_pde, PDE* out_pde): 将大页(2MB)分割为4KB小页,便于单独修改目标页。
isolate_page_table(CR3 cr3_reg, void* replaceAlignAddr, PDE* splitPDE): 核心函数,为目标地址创建独立的页表结构,使其指向新的物理页(实现写时复制)。
isolate_pages(HANDLE pid, void* iso_address): 为指定进程的地址隔离页表(包括大页分割、禁用写保护、调用isolate_page_table等)。
mdl_write_memory(void* address, void* buffer, size_t size): 通过MDL(内存描述符列表)安全写入内核内存。
get_instruction_length(void* address): 获取指令长度,用于复制原始函数的前几条指令。
2. 钩子核心函数
3. 钩子示例函数
4. 驱动入口和卸载函数
DriverEntry():
获取NtCreateFile的地址。
调用pte_hook安装钩子(目标进程为系统进程,PID=4)。
设置驱动卸载函数。
驱动卸载函数(DriverUnload):
遍历所有钩子记录,恢复原始函数代码(使用保存的原始字节)。
释放跳板内存。
代码执行流程详细步骤
步骤1: 驱动加载
DriverEntry被调用。
通过MmGetSystemRoutineAddress获取NtCreateFile地址。
调用pte_hook,传入目标函数地址(NtCreateFile)和自定义钩子函数(hook_nt_create_file)。
pte_hook中:
保存原始函数的指针(g_OriginNtCreateFile)用于后续调用。
步骤2: 拦截函数调用
当系统调用NtCreateFile时,执行流程跳转到hook_nt_create_file。
在自定义钩子函数中:
步骤3: 驱动卸载
调用驱动卸载函数。
遍历所有钩子记录(m_info数组):
附加到对应进程。
使用MDL写入恢复原始函数代码。
刷新TLB(invalidate_page)。
释放跳板内存(m_trampLine)。
关键修复点总结
大页分割处理:在isolate_page_table中,正确处理大页分割后的页表。
指令边界复制:在pte_hook中,按指令长度复制原始函数指令,避免截断指令导致崩溃。
跳回指令生成:在跳板末尾添加正确的跳回指令(跳回原始函数后半部分)。
安全刷新TLB:在isolate_page_table中,通过临时切换CR3刷新整个TLB。
§§相关问题§§:
在Windows内核中,如何防止PTE Hook被PatchGuard检测?
在多处理器(SMP)系统中,如何确保所有CPU核心的TLB都被刷新?
除了MDL写入,还有哪些安全的内核内存修改方法?
如何扩展此代码以支持非x64架构(如ARM64)?
现在只支持2mb大页分割,1gb不支持
传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!