SSDT Hook技术详解与应用

SSDT Hook技术详解与应用

一、SSDT简介

1、什么是SSDT

​ SSDT 的全称是 System Services Descriptor Table,系统服务描述符表。这个表就是一个把 Ring3 的 Win32 API 和 Ring0 的内核 API 联系起来。SSDT 并不仅仅只包含一个庞大的地址索引表,它还包含着一些其它有用的信息,诸如地址索引的基地址、服务函数个数等。通过修改此表的函数地址可以对常用 Windows 函数及 API 进行 Hook,从而实现对一些关心的系统动作进行过滤、监控的目的。一些 HIPS、防毒软件、系统监控、注册表监控软件往往会采用此接口来实现自己的监控模块。

​ 在 NT 4.0 以上的 Windows 操作系统中,默认就存在两个系统服务描述表,这两个调度表对应了两类不同的系统服务,这两个调度表为:KeServiceDescriptorTable 和 KeServiceDescriptorTableShadow,其中 KeServiceDescriptorTable 主要是处理来自 Ring3 层得 Kernel32.dll 中的系统调用,而 KeServiceDescriptorTableShadow 则主要处理来自 User32.dll 和 GDI32.dll 中的系统调用,并且 KeServiceDescriptorTable 在 ntoskrnl.exe(Windows 操作系统内核文件,包括内核和执行体层)是导出的,而 KeServiceDescriptorTableShadow 则是没有被 Windows 操作系统所导出,而关于 SSDT 的全部内容则都是通过 KeServiceDescriptorTable 来完成的。

2、SSDT结构

​ 下图是IDA分析的ntoskrnl.exe中导出的KeServiceDescriptorTable 结构。

img

​ 下面是KeServiceDescriptorTable 的具体含义。

~~~c++
typedef struct _KSYSTEM_SERVICE_TABLE{
PULONG ServiceTableBase; // SSDT (System Service Dispatch Table)的基地址
PULONG ServiceCounterTableBase; // 用于 checked builds, 包含 SSDT 中每个服务被调用的次数
ULONG NumberOfService; // 服务函数的个数, NumberOfService * 4 就是整个地址表的大小
ULONG ParamTableBase; // SSPT(System Service Parameter Table)的基地址
} KSYSTEM_SERVICE_TABLE, *PKSYSTEM_SERVICE_TABLE;

​ 有了上面的介绍后,我们可以简单的将 KeServiceDescriptor 看做是一个数组了(其实质也就是个数组),**在应用层 ntdll.dll 中的 API 在这个系统服务描述表(SSDT)中都存在一个与之相对应的服务,当我们的应用程序调用 ntdll.dll 中的 API 时,最终会调用内核中与之相对应的系统服务,由于有了 SSDT,所以我们只需要告诉内核需要调用的服务所在 SSDT 中的索引就 OK 了,然后内核根据这个索引值就可以在 SSDT 中找到相对应的服务了,然后再由内核调用服务完成应用程序 API 的调用请求即可。基本结构可以参考下图:

ssdt1

3、应用层调用 Win32 API 的完整执行流程

​ 有了上面的 SSDT 基础后,我们再来看一下在应用层调用 Win32 API(这里主要指的是 ntdll.dll 中的 API)的完整流程,这里我们主要是分析 ntdll.dll 中的 NtQuerySystemInformation 这个 API 的调用流程。

(PS:Windows 任务管理器即是通过这个 API 来获取到系统的进程等等信息的)。

ring03

再给出这些个 API 的基本的调用流程

ring03-2

​ 实质上,在 Windows 操作系统中,Ntdll.dll 中的ZwQuerySystemInformation 和 NtQuerySystemInformation 是同一函数,可以通过下面的截图看出,这两个函数的入口地址指向同一区域,他们的函数入口地址都是一样的 。

​ 众所周知 Ntdll.dll 中的 API 都只不过是一个简单的包装函数而已,当 Kernel32.dll 中的 API 通过 Ntdll.dll 时,会完成参数的检查,再调用一个中断(int 2Eh 或者 SysEnter 指令),从而实现从 Ring3 进入 Ring0 层,并且将所要调用的服务号(也就是在 SSDT 数组中的索引值)存放到寄存器 EAX 中,并且将参数地址放到指定的寄存器(EDX)中,再将参数复制到内核地址空间中,再根据存放在 EAX 中的索引值来在 SSDT 数组中调用指定的服务 。

下面来看 ntoskrnl.exe 中的 ZwQuerySystemInformation:

ZwQuerySystemInformation

​ 在上面的这幅截图中,可以看到在 Ring0 下的 ZwQuerySystemInformation 将 0ADh 放入了寄存器 eax 中,然后调用了系统服务分发函数 KiSystemService,而这个 KiSystemService 函数则是根据 eax 寄存器中的索引值,然后再 SSDT 数组中找到索引值为 eax 寄存器中存放的值得那个 SSDT 项,最后就是根据这个 SSDT 项中所存放的系统服务的地址来调用这个系统服务。比如在这里就是调用 KeServiceDescriptorTable[0ADh] 处所保存的地址所对应的系统服务 。也就是调用 Ring0 下的 NtQuerySystemInformation 了 。至此,在应用层中调用 NtQuerySystemInformation 的全部流程也就结束了 。

二、SSDT Hook原理

1、SSDT Hook原理简介

​ 有了上面的这部分基础后,就可以来看 SSDT HOOK 的原理了,其实 SSDT Hook 的原理是很简单的,从上面的分析中,我们可以知道在 SSDT 这个数组中,保存了系统服务的地址,比如对于 Ring0 下的 NtQuerySystemInformation 这个系统服务的地址,就保存在 KeServiceDescriptorTable[0ADh] 中,既然是 Hook 的话,我们就可以将这个 KeServiceDescriptorTable[0ADh] 下保存的服务地址替换掉,将我们自己的 Hook 处理函数的地址来替换掉原来的地址,这样当每次调用 KeServiceDescriptorTable[0ADh]时就会调用我们自己的这个 Hook 处理函数了。

下面截图是SSDT Hook之前:

ssdthook1

​ 下面的截图则是 SSDT Hook 之后,可以看到将 SSDT 中的服务地址修改为 MyHookNtQuerySystemInformation 了,这样的话,每次系统调用 NtQuerySystemInformation 这个系统服务时,实质上调用的就是MyHookNtQuerySystemInformation 了,而我们为了保证系统的稳定性(至少不让其崩溃),一般会在 MyHookNtQuerySystemInformation 中调用系统中原来的服务,也就是 NtQuerySystemInformation。

ssdthook2

下面来看一下备份、修改、还原SSDT的具体代码:

//=====================================================================================//
//Name:KSYSTEM_SERVICE_TABLE  |  KSERVICE_TABLE_DESCRIPTOR 定义                        //
//                                                                                     //
//Descripion: 定义SSDT表结构与获取函数索引值和地址的基本方法                              //
//                                                                                     //
//=====================================================================================//
typedef struct _KSYSTEM_SERVICE_TABLE
{
    PULONG  ServiceTableBase;        // SSDT (System Service Dispatch Table)的基地址
    PULONG  ServiceCounterTableBase; // 包含 SSDT 中每个服务被调用的次数
    ULONG   NumberOfService;     // 服务函数的个数, NumberOfService * 4 就是整个地址表的大小
    ULONG   ParamTableBase;          // SSPT(System Service Parameter Table)的基地址
} KSYSTEM_SERVICE_TABLE, *PKSYSTEM_SERVICE_TABLE;

typedef struct _KSERVI
### 如何获取 DeepSeek 免费 Token 对于希望获取 DeepSeek 免费 Token 的用户来说,存在多个途径来实现这一目标。 当前有特定时间段内的优惠活动可供利用。例如,在注册 DeepSeek 账户时,新用户可以获得价值10元人民币的免费 Token,这大约等于一千万元的 Token 数量[^1]。此外,针对接入 DeepSeek V3 版本的服务,也有过提供五百万元 Token 的限时优惠直至指定日期结束的通知[^2]。而更进一步地,某些情况下服务商为了表达对客户的感激之情以及促进未来的合作关系,会在一定期限内给予更高额度如五亿 Tokens 的免费使用权[^4]。 需要注意的是这些优惠政策可能会随时间变化,并且具体条款可能有所调整。因此建议访问官方渠道确认最新的促销信息并按照指引完成相应操作以获得免费资源。 #### 获取步骤概述 虽然这里不使用诸如“首先”这样的引导词,但以下是概括性的描述: - 访问官方网站或应用平台创建账户; - 阅读并同意服务协议及相关政策说明; - 完成身份验证流程(如果必要); - 查看可用的奖励计划详情页了解最新福利措施; - 根据页面提示领取相应的免费 Token 或参其他形式的激励项目; ```python # 示例代码用于展示如何通过API请求获取Token(假设场景),实际操作需参照官方文档指导。 import requests def get_free_token(api_url, user_info): response = requests.post(api_url, json=user_info) if response.status_code == 200: token_data = response.json() print(f"成功获取到 {token_data['amount']} tokens.") else: print("未能成功获取Token.") user_details = {"email": "example@example.com", "password": "securePassword"} get_free_token("https://api.deepseek.example/token/free", user_details) ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值