Windows SSDT Hook(一)

article/2025/7/5 9:45:26

前言

虽然在 Windows Vista 以后的 64 位操作系统中,PatchGuard(内核补丁保护机制)对 SSDT(System Service Dispatch Table,系统服务分派表)实施了强力保护,直接 Hook SSDT 的方式几乎不可行;但是在许多旧系统中,32 位的 SSDT Hook 仍然是合法可行的手段

此外,即便不能直接用于现代系统,SSDT Hook 的实现机制是理解系统调用流程、内核函数转发、内核保护绕过的基础知识。对于从事安全研究、驱动开发、逆向工程的人来说,它是不可绕过的一块内容。

Windows系统调用

Windows 系统调用是用户态程序访问操作系统核心功能(如打开文件、启动进程、分配内存等)的唯一合法通道。具体流程如下:

  1. 用户态程序调用 kernel32.dll / user32.dll 等 API;
  2. 这些 API 内部再调用 ntdll.dll 提供的 Nt*Zw* 系统调用接口;
  3. ntdll.dll 使用 syscall(64 位)或 int 0x2e(32 位)将调用转入内核态;
  4. 内核通过调用一系列的内部函数找到 SSDT 中对应的函数指针,执行实际内核函数。

SSDT 就是这张“索引表”,其每个项都是 nt!Nt* 函数的地址。

在这里插入图片描述

系统调用号

上一节中提到ntdll.dll 使用 syscallint 0x2e进入内核态时,其中一个关键参数就是**“系统调用号”(System Call Index)**,它用来在 SSDT 或 Shadow SSDT 中查找目标函数的入口地址。

由于系统调用号在不同版本的 Windows 中的值不一定相同。这意味着不推荐硬编码系统调用号(除非针对特定版本)。
在这里插入图片描述
ZwCreateFile的反汇编代码中可以看出,通过B8[mov eax] 55 00 00 00[0x55h]指令将系统调用号赋值给eax寄存器,因此可以通过读取系统调用接口的反汇编指令获取对应的调用号。

// 获取ntdll.dll导出表
PIMAGE_EXPORT_DIRECTORY export_table = (PIMAGE_EXPORT_DIRECTORY)((PUCHAR)dos_header + nt_header->OptionalHeader.DataDirectory[0].VirtualAddress);
PULONG func_name_addr = (PULONG)((PUCHAR)dos_header + export_table->AddressOfNames);
PUSHORT func_ordinal_addr = (PUSHORT)((PUCHAR)dos_header + export_table->AddressOfNameOrdinals);
PULONG func_addr = (PULONG)((PUCHAR)dos_header + export_table->AddressOfFunctions);for (ULONG i = 0; i < export_table->NumberOfNames; i++) {PCHAR export_func_name = (PCHAR)((PUCHAR)dos_header + func_name_addr[i]);// 遍历导出函数表 找到对应的函数if (0 == _strnicmp(func_name, export_func_name, strlen(func_name))) {// 获取导出函数地址PULONG entry_point = (PULONG)((PUCHAR)dos_header + func_addr[func_ordinal_addr[i]]);// 匹配特征值 获取索引
#ifdef _WIN64// move r10, rcx// move raxif (entry_point[0] == 0xB8D18B4C) {return entry_point[1];}
#else// move raxif (*(PUCHAR)entry_point == 0xB8) {return *(ULONG*)((PUCHAR)entry_point + 1);}
#endifbreak;}
}

符号表

在Windows操作系统中,存在两张符号表SSDT和Shadow SSDT。其中 SSDT位于内核模块 ntoskrnl.exe 中,映射的是 ntdll.dll 中以 NtZw 开头的系统调用函数(如 NtOpenProcessZwCreateFile);而Shadow SSDT则位于内核图形子系统模块 win32k.sys 中,专门用于处理的是 GUI 子系统调用(如 NtUserCreateWindowExNtGdiBitBlt)。

#pragma pack(1)
typedef struct _SERVICE_DESCIPTOR_TABLE {PLONG ServiceTableBase;         // SSDT base addressPVOID ServiceCounterTableBase;  //
#ifdef _WIN64ULONGLONG NumberOfService;      // Number of SSDT services
#elseULONG NumberOfService;          // Number of SSDT services
#endifPVOID ParamTableBase;           // Base address of the system service parameter table
} SSDTEntry, *PSSDTEntry;
#pragma pack()

不管是SSDT,还是Shadow SSDT,其数据结构都是完全一致的。其中所有的导出函数地址都按照系统调用号的顺序存储在ServiceTableBase指向的ULONG数组中,在32位系统中这里存储着函数的绝对地址,而在64位系统中,这里存储的则是相对于SSDT基址的“偏移”。

修改只读内存

在获取了SSDT基址和系统调用号之后,需要做的就是将自身的Hook函数替换SSDT表中原本的系统调用,而由于在Windows 内核中, SSDT 表被标记为只读(Read-Only),尝试直接修改将触发保护异常。因此,在实现 SSDT Hook 前,需要一定的手段规避写保护机制

  • [修改CR0](x64驱动 关闭&开启 写时保护_wpoffx64-CSDN博客)
// 关闭写保护
KIRQL WPOFFx64()
{// 提高中断请求优先级到 DISPATCH_LEVEL 级别KIRQL irql = KeRaiseIrqlToDpcLevel();// 获取 cr0 寄存器中的值(cr0 控制 cpu 的操作模式)UINT64 cr0 = __readcr0();// 修改写保护cr0 &= 0xfffffffffffeffff;__writecr0(cr0);// 禁用中断_disable();return irql;
}// 开启写保护
void WPONx64(KIRQL irql)
{// 获取 cr0 中的值UINT64 cr0 = __readcr0();// 恢复写保护cr0 |= 0x10000;__writecr0(cr0);// 恢复中断_enable();// 恢复 IRQL 到原始值KeLowerIrql(irql);
}KIRQL kIrql = WPOFFx64();
// 此处修改内存数据
WPONx64(kIrql);
  • MDL映射
bool UpdateNonPagedMemory(void* target, void* data, unsigned long length) {PMDL mdl = NULL;__try {if (target == nullptr || data == nullptr) return false;if (length == 0) return true;// 为指定地址申请MDLmdl = IoAllocateMdl(target, length, FALSE, FALSE, NULL);if (mdl == NULL) {return false;}// 判断IRQL级别,大于APC直接返回if (KeGetCurrentIrql() > APC_LEVEL) {return false;}// 更新MDL以描述基础物理页MmBuildMdlForNonPagedPool(mdl);                                                                             // 将MDL物理页映射到一个虚拟页面PVOID mapping_addr = MmMapLockedPagesSpecifyCache(mdl, KernelMode, MmNonCached, NULL, FALSE, NormalPagePriority);  //if (!mapping_addr) {return false;}// 更新内存RtlCopyMemory(mapping_addr, data, length);MmUnmapLockedPages((PVOID)mapping_addr, mdl);return true;} __finally {if (mdl) IoFreeMdl(mdl);}
}

http://www.hkcw.cn/article/lVKyZsrILu.shtml

相关文章

centos7.6阿里云镜像各个版本介绍

&#xff08;水一期&#xff09; Index of /centos-vault/centos/7.6.1810/isos/x86_64/ File NameFile SizeDateParent directory/--0_README.txt2.4 KB2018-12-01 21:21CentOS-7-x86_64-DVD-1810.iso4.3 GB2018-11-26 07:55CentOS-7-x86_64-DVD-1810.torrent86.0 KB2018-12-…

在大型 GIS 数据库中按属性高效溶解相邻多边形

您是否拥有一个大型 GIS 数据集&#xff0c;并希望高效地融合所有具有相同属性的相邻多边形&#xff1f;在本文中&#xff0c;我将分享如何使用 PostGIS 处理包含超过 75 万行数据的土地利用数据集来实现这一目标。 我将以维多利亚州土地利用数据集为例。该数据可从Data VIC免…

Spring Web高保真Axure动态交互元件库

在当今快速发展的Web设计与开发领域&#xff0c;设计师和开发者们一直在寻找高效、高质量的工具来加速原型设计过程。今天&#xff0c;我要向大家介绍一款专为Web设计与开发领域量身打造的Axure交互元件集合——Spring UI Web端高保真动态交互元件库。这款元件库不仅全面且易于…

Chrome插件学习笔记(二)

Chrome插件学习笔记&#xff08;二&#xff09; 参考文章&#xff1a; https://developer.chrome.com/docs/extensions/reference/api/sidePanel?hlzh-cnhttps://developer.chrome.com/docs/extensions/reference/api/webRequest?hlzh-cnhttps://developer.chrome.com/docs/e…

判断质数的基础方法

判断一个数是否为质数&#xff1a;基础方法(运算效率较慢) 另一种运用API来提高运算效率&#xff1a; 以下是添加了详细注释的代码版本&#xff0c;并优化了部分逻辑&#xff1a; package test;public class test5 {public static void main(String[] args) {//判断一个数是否…

列表单独展开收起同时关闭其余子项的问题优化

如图所示&#xff0c;当在列表中&#xff0c;需要分别单独点开子选项时&#xff0c;直接这样用一个index参数判断即可&#xff0c;非常简单方便&#xff0c;只需要满足点开当前index,然后想同index用null值自动关闭即可

java25

1.可变参数 2.集合工具类Collections 3.综合练习 集合嵌套&#xff1a; 4.不可变集合 JDK9以后才能用 这个静态方法名是of&#xff0c;返回值是List<E>,是泛型方法。 JDK10以后的简化版&#xff1a; 5.Stream流 爽一下&#xff1a; 简化后的: 注意&#xff1a;stream.ma…

中方:南南合作始终是对外合作优先方向

当地时间5月30日,联合国南南合作基金30周年纪念活动在纽约联合国总部举行。中国常驻联合国代表傅聪在活动致辞中表示,中方高度赞赏基金支持的务实合作成果。中方表示,始终坚定支持联合国发展支柱,始终坚定支持真正的多边主义,始终将南南合作作为对外合作的优先方向。中方指…

在线博客系统【测试报告】

&#x1f552; 一. 项目背景 由于纸质笔记容易丢失&#xff0c;携带不变&#xff0c;为了方便自己学习的过程中记录笔记&#xff0c;特开发了这个博客系统。这个系统后端采用 SpringBoot MyBatis SpringMVC &#xff1b;前端使用Html CSS JS&#xff1b;数据库使用的是Mysq…

近期手上的一个基于Function Grap(类AWS的Lambda)小项目的改造引发的思考

函数式Function是云计算里最近几年流行起来的新的架构和模式&#xff0c;因为它不依赖云主机&#xff0c;非常轻量&#xff0c;按需使用&#xff0c;甚至是免费使用&#xff0c;特别适合哪种数据同步&#xff0c;数据转发&#xff0c;本身不需要保存数据的业务场景&#xff0c;…

C++ - 模板(一) #泛型编程 #函数模板 #类模板

文章目录 前言 一、泛型编程 二、函数模板 1、函数模板的概念 2、函数模板的格式 3、函数模板的原理 4、函数模板的实例化 1、隐式实例化&#xff1a; 2、显式实例化&#xff1a; 5、模板参数的匹配原则 三、类模板 1、类模板的定义格式 2、类模板的实例化 总结 …

智能制造全场景数字化解决方案

制造企业数字化转型面临的挑战 数智化转型已成为中国制造业高质量发展的关键战略。面对全球制造业格局调整&#xff0c;如何快速构建覆盖全业务流程的可视化应用&#xff0c;通过数据驱动的方式为企业经营管理、预警监测、质量管控、决策支持提供全面支撑&#xff0c;是企业面…

Vue-收集表单信息

收集表单信息 Input label for 和 input id 关联, 点击账号标签 也能聚焦 input 代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><title>表单数据</title><!-- 引入Vue --><scrip…

篮球分组问题讨论

1 问题概述 问题&#xff1a;有5支球队在同一块场地上进行单循环赛,共要进行10场比赛。下表是一个赛程安排&#xff0c;有些队觉得不公平。研究以下问题 A B C D E 每两场比赛间相隔场次数 A X 1 9 3 6 1, 2, 2 B 1 X 2 5 8 0, 2, 2 C 9 2 X 7 10 4…

成都鼎讯--通信干扰设备功能全解析

在现代电子战与通信对抗领域&#xff0c;一款高性能的通信干扰设备是掌握电磁频谱主动权的关键。本文将深入解析一款先进的通信干扰设备&#xff0c;其凭借多频段覆盖、多通道并行、多样化调制方式及灵活供电等特性&#xff0c;成为部队、科研院所等机构在电磁对抗训练与研究中…

vscode中让文件夹一直保持展开不折叠

vscode中让文件夹一直保持展开不折叠 问题 很多小伙伴使用vscode发现空文件夹会折叠显示, 让人看起来非常难受, 如下图 解决办法 首先打开设置->setting, 搜索compact Folders, 去掉勾选即可, 如下图所示 效果如下 看起来非常爽 ! ! !

中国城市间地理距离矩阵(2024)

1825 中国城市间地理距离矩阵(2024) 数据简介 中国城市间地理距离矩阵数据集&#xff0c;通过审图号GS(2024)0650的中国城市地图在Albers投影坐标系中进行计算得出矩阵表格&#xff0c;单位为KM&#xff0c;方便大家研究使用。 中国城市地理距离矩阵数据通过计算城市中心距离…

Linux中的shell脚本

什么是shell脚本 shell脚本是文本的一种shell脚本是可以运行的文本shell脚本的内容是由逻辑和数据组成shell脚本是解释型语言 用file命令可以查看文件是否是一个脚本文件 file filename 脚本书写规范 注释 单行注释 使用#号来进行单行注释 多行注释 使用 : " 注释内容…

20250530-C#知识:抽象类、抽象方法、接口

C#知识&#xff1a;抽象类、抽象方法、接口 在开发过程中接口一般用得较多&#xff0c;程序框架往往定义一堆接口规范&#xff0c;然后程序员自己写逻辑来实现接口功能。掌握接口的知识还是很有必要的。 1、抽象类 用abstract关键字修饰的类不能用来实例化对象可以包含抽象方法…

韩国首尔一地铁车厢内遭纵火 乘客被紧急疏散

当地时间5月31日8时47分左右,韩国首尔地铁5号线一辆列车车厢内起火,乘客随后被紧急疏散。据初步调查,火灾原因为有人纵火,嫌疑人已被抓获。目前暂无人员伤亡报告。受火灾事件影响,该地铁线路部分区段一度暂停运行,首尔市交通部门10时13分通报,事故处理已经完毕,暂停运行…