Windows SSDT Hook(二)

article/2025/6/29 10:17:45

获取SSDT基址

在32位Windows系统中,SSDT(System Service Descriptor Table)作为系统服务调用的核心数据结构,其基址信息是公开且稳定的。由于ntoskrnl.exe内核模块默认导出KeServiceDescriptorTable符号,开发者只需使用extern声明即可直接访问:

extern PKSERVICE_TABLE_DESCRIPTOR KeServiceDescriptorTable;

然而,64位系统架构带来了重大变化。微软出于安全考虑,不再导出SSDT相关符号,并引入了以下保护机制:

  1. SSDT表基址变为动态变化,每次系统启动都会重新随机化
  2. 移除了直接的符号导出
  3. 增加了内核PatchGuard保护
    因此,想要获取64位SSDT基址就需要一些特殊技术手段来实现,常见方法包括:
  • 借助第三方符号(PDB)或调试器
    在这里插入图片描述

  • 通过msr寄存器读取

通过读取msr 0xC0000082可得到 KiSystemCall64的函数地址,从此函数地址向下可以定位到KiSystemServiceRepeat函数,从而间接获取 SSDT。
在这里插入图片描述
从上图中可以看出,4C 8D 15[lea r10] 指令后面的操作数即为SSDT地址的相对偏移,因此在通过特征值定位到此处时,可以通过以下公式计算出实际的SSDT地址。

SSDT地址 = Rip + offset = 0x140070FF9 + 0x237847

这种方法的实现在网络上可以轻松获取,这里就不详细展开了。值得注意的一点是,win10 1903版本之后,由于引入了内核隔离机制,通过msr获取到的地址不再是 KiSystemCall64,而是KiSystemCall64Shadow,这也导致了无法按照以上方法找到SSDT表。
在这里插入图片描述

  • 通过特征码扫描内核映像

由于无法再通过msr来实现查找,因此在此基础上又延伸出了类似的方案。即通过某些确定的系统函数地址,向上或向下寻找SSDT的特征值。通过IDA分析内核模块中Zw* 系统调用接口(这里并非ntdll中的用户态调用接口,而是提供给wdm驱动使用的内核态接口),可以看出内核模块中的系统调用实际上同样是通过SSDT表实现的。以ZwCreateFile 函数为例,内部调用KiServiceInternal ,然后通过jmp指令跳转到KiSystemCall64函数中。
在这里插入图片描述
在这里插入图片描述

PLONG HookHandler::GetSSDTBase() {
#ifdef _WIN64const PVOID zw_close = ZwClose;const ULONG offset = 0x400;static PSSDTEntry ssdt_address = nullptr;__try {if (ssdt_address) return ssdt_address->ServiceTableBase;PUCHAR service_internal_address = nullptr;PUCHAR zw_route_address = (PUCHAR)zw_close;// .text:000000014006A653 50               push    rax// .text:000000014006A654 B8 0C 00 00 00   mov     eax, 0Ch// .text:000000014006A659 E9 E2 67 00 00   jmp     KiServiceInternalconst UCHAR service_internal_code[] = {0x50, 0xB8, 0x00, 0x00, 0x00, 0x00, 0xE9, 0x00};for (ULONG i = 0; i < offset; i++) {if ((*(PULONGLONG)&zw_route_address[i] & *(PULONGLONG)&service_internal_code) ==*(PULONGLONG)&service_internal_code) {ULONG jmp_offset = *(PULONG)&zw_route_address[i + 7];service_internal_address = &zw_route_address[i + 6] + 5 + jmp_offset;break;}}if (!service_internal_address) return nullptr;// .text:0000000140070FF2 4C 8D 15 47 78 23 00   lea     r10, KeServiceDescriptorTable// .text:0000000140070FF9 4C 8D 1D 80 78 23 00   lea     r11, KeServiceDescriptorTableShadowconst UCHAR ssdt_table_code[] = {0x4C, 0x8D, 0x15, 0x00};for (ULONG i = 0; i < offset; i++) {if ((*(PULONG)&service_internal_address[i] & *(PULONG)&ssdt_table_code) ==*(PULONG)&ssdt_table_code) {ULONG jmp_offset = *(PULONG)&service_internal_address[i + 3];ssdt_address = (PSSDTEntry)(&service_internal_address[i] + 7 + jmp_offset);break;}}if (!ssdt_address) return nullptr;return ssdt_address->ServiceTableBase;} __except (EXCEPTION_EXECUTE_HANDLER) {return nullptr;}
#elsereturn KeServiceDescriptorTable.ServiceTableBase;
#endif
}

除了上述方法,在网上还见过一种通过在_strnicmp函数到KdDebuggerNotPresent之间搜索8b f8 c1 ef 07 83 e7 20 25 ff 0f 00 00的定位方式。

SSDT解析

前一篇中提到SSDT中的ServiceTableBase指向的一个ULONG数组,32位系统中的数组元素代表函数地址,而在64位系统中,由于ULONG类型无法存储完整的指针类型,因此实际存储的是函数地址相对于SSDT基址的"偏移"。需要注意的是这里的偏移并不仅仅是简单的偏移,而是需要固定公式计算出的偏移长度。

在这里插入图片描述

// 计算函数地址
PVOID func_addr = (PVOID)(((LONG)ssdt_base[info.func_index] >> 4) + (ULONGLONG)func_addr);
// 计算偏移
ssdt_base[info.func_index] = (LONG)((ULONGLONG)target_addr - (ULONGLONG)ssdt_base) << 4

跳转指令

由于ServiceTableBase中存储的实际上是相对于SSDT基址的"偏移",因此hook函数的地址必须在以SSDT基址为中心的4GB内存范围内。而通常情况下我们自己的驱动的加载地址必然在4GB范围外,因此我们不能简单的将Hook函数的地址写入SSDT表,而应该在范围内选择一块地址作为函数跳转的中转。

第一步,选择合适的跳转指令

这里我们选择长度最小的12字节跳转法,实现从中转地址跳转到Hook函数。

#pragma pack(push, 1)
struct HookShellCode {USHORT mov;ULONG_PTR addr;UCHAR push;UCHAR ret;
};
#pragma pack(pop)
HookShellCode shell_code;
shell_code.mov = 0xB848;
shell_code.addr = (ULONG_PTR)func_addr;
shell_code.push = 0x50;
shell_code.ret = 0xC3;

第二步,遍历所有的代码段,从中找出合适的中转地址

PULONG kernel_base = GetKernelBase();// 获取内核模块基址
if (!kernel_base) return nullptr;// Dos Header
PIMAGE_DOS_HEADER dos_header = (PIMAGE_DOS_HEADER)kernel_base;
if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) return nullptr;// NT Header
PIMAGE_NT_HEADERS nt_header = (PIMAGE_NT_HEADERS)((PUCHAR)dos_header + dos_header->e_lfanew);
if (nt_header->Signature != IMAGE_NT_SIGNATURE) return nullptr;// Section Header
IMAGE_SECTION_HEADER* section_header = IMAGE_FIRST_SECTION(nt_header);
ULONG rva = (ULONG)((PUCHAR)base_addr - (PUCHAR)kernel_base);
// 遍历代码段
for (USHORT i = 0; i < nt_header->FileHeader.NumberOfSections; i++) {// find the section which function existsif (section_header[i].VirtualAddress <= rva &&section_header[i].VirtualAddress + section_header[i].Misc.VirtualSize > rva) {PUCHAR section_addr = (PUCHAR)kernel_base + section_header[i].VirtualAddress;for (ULONG offset = 0, size = 0; offset < section_header[i].SizeOfRawData; offset++) {if (section_addr[i] == 0x90 || section_addr[i] == 0xCC)  // NOP or INT3size++;elsesize = 0;// 找到大小匹配的空闲内存if (size == HOOK_CODE_SIZE)return (PULONG)(section_addr + offset - HOOK_CODE_SIZE + 1);}}
}

PatchGuard

基于以上内容,就可以实现在64位系统上的SSDT Hook了。然而,当实际运行Hook驱动程序时,会发现正常运行一段时间后,系统就会触发BSOD(错误代码:0x109,内核检测到关键内核代码或数据损坏),而这就是PatchGuard的作用了。

到目前为止,通过PatchGuard的方法未作深入研究,仅通过网络得知可能存在以下两种方法:

  • 通过某种方式伪装骗过PG检测,例如EPT Hook等。
  • 直接与PG对抗

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

相关文章

郑钦文巴黎街头唱《日不落》 甜蜜16强庆祝

北京时间5月30日,2025年法网第六个比赛日,中国球员郑钦文以6比3、6比4战胜18岁加拿大新星姆博科,顺利挺进16强,追平了她在法网的最佳战绩。赛后,郑钦文更新多条动态,发微博称“甜蜜16强”,并发布视频展示自己在法国巴黎街头即兴演唱蔡依林的经典歌曲《日不落》。在接受采…

马克龙兜售“印太战略”能实现吗 经贸与防务双管齐下

法国总统马克龙于2025年5月25日抵达越南首都河内,开始为期三天的国事访问。这是自1973年两国建交以来,法国总统第五次访问越南,也是马克龙就任总统后首次访问越南。在访问期间,马克龙与越南国会主席陈青敏会晤。此次东南亚之行还包括对印度尼西亚和新加坡的访问,并出席在新…

100%国产化 揭秘中国“蓝盔”作战装备

2025年5月29日是第23个维持和平人员国际日,旨在纪念为联合国维和行动献身的维和人员。维和部队主要任务是维持地区和平,那么中国维和部队有哪些装备?中国蓝盔作战装备100%国产化var chan_v_w = 960,chan_v_h = 540,chan_v_p = https://mts-audio.huawangzhixun.com/image/20…

58-dify案例分享-用 Dify 工作流 搭建数学错题本,考试错题秒变提分神器-同类型题生成篇

1.前言 错题本是一种学习工具&#xff0c;用于记录和总结学生在学习过程中做错的题目&#xff0c;以便找出学习中的薄弱环节&#xff0c;提高学习效率和成绩。 一下是错题本定义、作用、建立方法、使用技巧等内容。 定义&#xff1a;错题本是指中小学学生在学习过程中&#x…

高压锅煮粽子致爆炸女子发声 警惕厨房“炸弹”

端午将至,很多人已经准备好糯米、粽叶和各种馅料,打算在家制作美味的粽子。然而,使用高压锅煮粽子时需格外小心,以免发生意外。去年,浙江温州一位61岁女子在家中用高压锅煮粽子时,因饭粒堵塞排气孔导致高压锅爆炸,造成她面部及全身多处受伤,右眼伤势尤为严重。这类事故…

小伙亚硝酸盐中毒 尿液呈蓝色 解毒剂亚甲蓝起效

前不久的一个深夜,浙江省立同德医院ICU收治了一名25岁的年轻小伙。他全身青紫,口唇发紫,送到医院时已陷入深昏迷状态。在之后的治疗过程中,他的尿液甚至变成了蓝色。医生检查后发现,小伙的血氧饱和度仅有64%,血压只有70/40mmHg,高铁血红蛋白浓度高达75%。心脏和大脑已处…

Edmonds-Karp详解-基于BFS的最短增广路径

Edmonds-Karp详解-基于BFS的最短增广路径与py/cpp/Java三语言实现 一、网络流问题与相关概念回顾1.1 网络流问题定义1.2 关键概念 二、Edmonds-Karp算法原理2.1 算法核心思想2.2 算法具体流程 三、Edmonds-Karp算法的代码实现3.1 Python实现3.2 C实现3.3 Java实现 四、Edmonds-…

软件设计综合知识

software-design 软考中级-软件设计师-综合知识&#xff1a;计算机系统基础、操作系统、计算机网络与信息安全、程序语言基础、数据库基础、数据结构与算法、软件工程基础知识、标准与知识产权等。 —— 2025 年 3 月 5 日 甲辰年二月初六 惊蛰 目录 software-design1、计算机基…

(一)微服务(垂直AP/分布式缓存/装饰器Pattern)

文章目录 项目地址一、创建第一个垂直API1.1 创建Common层1. ICommand接口2. IQuery接口 1.2 创建API1. 实体2. Handler3. endpoint 1.3 使用Marten作为ORM 二、Redis缓存2.1 使用缓存装饰器1. 创建装饰器2. 注册装饰器 2.2 创建docker-compose1. docker-compose2. docker-comp…

单依纯疑似回应演唱失误 沉浸演绎引共鸣

在歌手第三期的袭榜赛中,单依纯获得了第二名。一些网友对此表示惊讶,认为她连续两期夺冠后,这次居然输给了断眉,只拿到第二名。不过大多数网友认为直播中的失误是正常现象。单依纯在微博上回应了此事,她表示《爱情》这首歌很早就在她的备选歌单里,《Yesterday Once More》…

汽车被巨石砸中司机逃生 当地回应 地质灾害点已设警示牌

5月28日,贵州毕节市七星关区何官屯镇的一条通村公路上发生了一起突发落石事件。一块约300斤重的巨石砸中一辆过路汽车,导致车辆从路边高坎坠落。司机受轻伤,送医检查后当日返家,车损由保险公司处理。落石还击碎了附近民房的玻璃门,但没有造成人员受伤。当地多部门表示,事…

警方:进入兵马俑坑男子患精神疾病 警情通报发布

5月31日03时15分,平安临潼通过新浪微博发布了一则警情通报。责任编辑:zhangxiaohua

激光武器是否会重塑现代战争形态 以色列首次战场应用引发讨论

当地时间28日,以色列国防部宣布,在针对巴勒斯坦伊斯兰抵抗运动(哈马斯)的军事行动中,以军使用激光武器进行了数十次拦截。这是以色列首次承认在战场上使用激光武器,引发外界广泛关注。以色列国防部长卡茨认为这一技术将改变该地区的游戏规则。以色列公布了一段激光武器拦…

今夏首签!官方:利物浦签24岁荷兰边卫弗林蓬,付3500万解约金 五年长约锁定未来

利物浦宣布签下勒沃库森右边翼卫弗林蓬。据报道,红军支付了3500万欧元的解约金,双方签约五年。弗林蓬出生于荷兰,拥有加纳血统,7岁时随家人移居英国,并在9岁时加入曼城梯队。他在曼城各级梯队成长,2019年离开曼城,以38万欧元的转会费加盟凯尔特人。2021年冬窗,勒沃库森…

端午节北京云量较多 白天阴有阵雨最高气温28℃ 假期出行需带雨具

今天是端午节,北京云量较多,白天阴有阵雨,最高气温28℃。假期后两天,北京云量依然较多,可能会有雷雨,公众外出需带好雨具,小心慢行,注意交通安全。昨天,北京以晴天为主,最高气温达到29.2℃,午后出行体感微热。今晨,北京天空阴沉,海淀、门头沟等多地出现降雨。端午…

世俱杯夺冠赔率:皇马居首曼城拜仁三甲 2025赛事即将揭幕

2025年世俱杯将于6月15日拉开帷幕,目前皇马被认为是夺冠的最大热门。曼城以5.5的赔率紧随其后,拜仁则以7的赔率位列第三。利雅得新月和迈阿密国际的夺冠赔率均为67,并列第18位。以下是世俱杯夺冠赔率前十名的具体情况: - 皇马:4.5 - 曼城:5.5 - 拜仁:7 - 巴黎圣日耳曼:…

“车圈恒大”到底存不存在 中国车企财务更稳健

汽车产业中的高负债问题引发激烈讨论,有人将某些车企比作“车圈恒大”,但这种类比忽略了汽车与房地产的本质差异。车企的负债主要用于研发和生产投资,而非高杠杆囤地,因此资产流动性和现金流稳定性远超房企。比亚迪集团品牌及公关处总经理李云飞通过个人微博回应了这一观点…

玉渊谭天丨签证限制升级背后:美国的困局

当地时间5月28日,美国国务院发布公告称,将开始大力撤销中国留学生的签证。美国政府近年来多次在中国留学生签证的问题上使绊子,总结起来,大致分为四类:将大学和研究机构列入实体清单;签证管控;限制中美正常交流项目;对研究人员采取持续调查。这一次,主要聚焦在签证管控…

Transformer 通关秘籍11:Word2Vec 及工具的使用

将文字文本转换为词向量(word embedding&#xff09;的过程中&#xff0c;一个非常著名的算法模型应该就是 Word2Vec 了。 相信大家或多或少都听说过&#xff0c;本节就来简单介绍一下 Word2Vec 。 什么是 Word2Vec &#xff1f; Word2Vec 可以非常有效的创建词嵌入向量&…

数组做函数参数,嵌套调用与链式访问

文章目录 前言一、数组做函数参数二、嵌套调用和链式访问2.1 嵌套调用2.2 链式访问 前言 这一块内容是衔接上一节函数内容&#xff0c;从更层次分析函数之中的细节 一、数组做函数参数 在平时用函数解决问题的时候&#xff0c;难免会将数组作为参数传递给函数&#xff0c;在函…