当前位置: 首页 > news >正文

6.2 Windows驱动开发:内核枚举SSSDT表基址

在Windows内核中,SSSDT(System Service Shadow Descriptor Table)是SSDT(System Service Descriptor Table)的一种变种,其主要用途是提供Windows系统对系统服务调用的阴影拷贝。SSSDT表存储了系统调用的函数地址,类似于SSDT表,但在某些情况下,Windows系统会使用SSSDT表来对系统服务进行引导和调用。

SSSDT表的存在是为了加强系统的安全性和稳定性。通过使用SSSDT表,操作系统可以在运行时检查系统服务的合法性,并确保其不被非法修改。这有助于防止恶意软件或恶意行为修改系统服务地址,提高系统的整体安全性。

在笔者上一篇文章《枚举完整SSDT地址表》实现了针对SSDT表的枚举功能,本章继续实现对SSSDT表的枚举,ShadowSSDT中文名影子系统服务描述表,SSSDT其主要的作用是管理系统中的图形化界面,其Win32子系统的内核实现是Win32k.sys驱动,属于GUI线程的一部分,其自身没有导出表,枚举SSSDT表其与SSDT原理基本一致。

如下是闭源ARK工具的枚举效果:

首先需要找到SSSDT表的位置,通过《Win10内核枚举SSDT表基址》文章中的分析可知,SSSDT就在SSDT的下面,只需要枚举4c8d1dde1e3a00特征即可,如果你找不到上一篇具体分析流程了,那么多半你是看到了转载文章。

先实现第一个功能,得到SSSDT表的基地址以及SSDT函数个数,完整代码如下所示。

#include <ntifs.h>
#pragma intrinsic(__readmsr)typedef struct _SYSTEM_SERVICE_TABLE
{PVOID          ServiceTableBase;PVOID          ServiceCounterTableBase;ULONGLONG      NumberOfServices;PVOID          ParamTableBase;
} SYSTEM_SERVICE_TABLE, *PSYSTEM_SERVICE_TABLE;PSYSTEM_SERVICE_TABLE KeServiceDescriptorTableShadow = 0;
ULONG64 ul64W32pServiceTable = 0;// 获取 KeServiceDescriptorTableShadow 首地址
ULONGLONG GetKeServiceDescriptorTableShadow()
{// 设置起始位置PUCHAR StartSearchAddress = (PUCHAR)__readmsr(0xC0000082) - 0x1808FE;// 设置结束位置PUCHAR EndSearchAddress = StartSearchAddress + 0x8192;// DbgPrint("扫描起始地址: %p --> 扫描结束地址: %p \n", StartSearchAddress, EndSearchAddress);PUCHAR ByteCode = NULL;UCHAR OpCodeA = 0, OpCodeB = 0, OpCodeC = 0;ULONGLONG addr = 0;ULONG templong = 0;for (ByteCode = StartSearchAddress; ByteCode < EndSearchAddress; ByteCode++){// 使用MmIsAddressValid()函数检查地址是否有页面错误if (MmIsAddressValid(ByteCode) && MmIsAddressValid(ByteCode + 1) && MmIsAddressValid(ByteCode + 2)){OpCodeA = *ByteCode;OpCodeB = *(ByteCode + 1);OpCodeC = *(ByteCode + 2);// 对比特征值 寻找 nt!KeServiceDescriptorTable 函数地址/*lyshark kd> u KiSystemServiceRepeatnt!KiSystemServiceRepeat:fffff802`7c1d2b94 4c8d15e59c3b00  lea     r10,[nt!KeServiceDescriptorTable (fffff802`7c58c880)]fffff802`7c1d2b9b 4c8d1dde1e3a00  lea     r11,[nt!KeServiceDescriptorTableShadow (fffff802`7c574a80)]fffff802`7c1d2ba2 f7437880000000  test    dword ptr [rbx+78h],80hfffff802`7c1d2ba9 7413            je      nt!KiSystemServiceRepeat+0x2a (fffff802`7c1d2bbe)fffff802`7c1d2bab f7437800002000  test    dword ptr [rbx+78h],200000hfffff802`7c1d2bb2 7407            je      nt!KiSystemServiceRepeat+0x27 (fffff802`7c1d2bbb)fffff802`7c1d2bb4 4c8d1d051f3a00  lea     r11,[nt!KeServiceDescriptorTableFilter (fffff802`7c574ac0)]fffff802`7c1d2bbb 4d8bd3          mov     r10,r11*/if (OpCodeA == 0x4c && OpCodeB == 0x8d && OpCodeC == 0x1d){// 获取高位地址fffff802memcpy(&templong, ByteCode + 3, 4);// 与低位64da4880地址相加得到完整地址addr = (ULONGLONG)templong + (ULONGLONG)ByteCode + 7;return addr;}}}return  0;
}// 得到SSSDT个数
ULONGLONG GetSSSDTCount()
{PSYSTEM_SERVICE_TABLE pWin32k;ULONGLONG W32pServiceTable;pWin32k = (PSYSTEM_SERVICE_TABLE)((ULONG64)KeServiceDescriptorTableShadow + sizeof(SYSTEM_SERVICE_TABLE));W32pServiceTable = (ULONGLONG)(pWin32k->ServiceTableBase);// DbgPrint("Count => %d \n", pWin32k->NumberOfServices);return pWin32k->NumberOfServices;
}VOID UnDriver(PDRIVER_OBJECT driver)
{DbgPrint(("驱动程序卸载成功! \n"));
}NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{DbgPrint("hello lyshark \n");KeServiceDescriptorTableShadow = (PSYSTEM_SERVICE_TABLE)GetKeServiceDescriptorTableShadow();DbgPrint("[LyShark] SSSDT基地址 = 0x%p \n", KeServiceDescriptorTableShadow);ULONGLONG count = GetSSSDTCount();DbgPrint("[LyShark] SSSDT个数 = %d \n", count);DriverObject->DriverUnload = UnDriver;return STATUS_SUCCESS;
}

这段代码运行后即可得到SSSDT表基地址,以及该表中函数个数。

在此基础之上增加枚举计算过程即可,完整源代码如下所示。

SSSDT 函数起始index是0x1000,但W32pServiceTable是从基址开始记录的,这个误差则需要(index-0x1000)来得到,至于+4则是下一个元素与上一个元素的偏移。

计算公式:

  • W32pServiceTable + 4 * (index-0x1000)
#include <ntifs.h>
#pragma intrinsic(__readmsr)typedef struct _SYSTEM_SERVICE_TABLE
{PVOID          ServiceTableBase;PVOID          ServiceCounterTableBase;ULONGLONG      NumberOfServices;PVOID          ParamTableBase;
} SYSTEM_SERVICE_TABLE, *PSYSTEM_SERVICE_TABLE;PSYSTEM_SERVICE_TABLE KeServiceDescriptorTableShadow = 0;
ULONG64 ul64W32pServiceTable = 0;// 获取 KeServiceDescriptorTableShadow 首地址
ULONGLONG GetKeServiceDescriptorTableShadow()
{// 设置起始位置PUCHAR StartSearchAddress = (PUCHAR)__readmsr(0xC0000082) - 0x1808FE;// 设置结束位置PUCHAR EndSearchAddress = StartSearchAddress + 0x8192;// DbgPrint("扫描起始地址: %p --> 扫描结束地址: %p \n", StartSearchAddress, EndSearchAddress);PUCHAR ByteCode = NULL;UCHAR OpCodeA = 0, OpCodeB = 0, OpCodeC = 0;ULONGLONG addr = 0;ULONG templong = 0;for (ByteCode = StartSearchAddress; ByteCode < EndSearchAddress; ByteCode++){// 使用MmIsAddressValid()函数检查地址是否有页面错误if (MmIsAddressValid(ByteCode) && MmIsAddressValid(ByteCode + 1) && MmIsAddressValid(ByteCode + 2)){OpCodeA = *ByteCode;OpCodeB = *(ByteCode + 1);OpCodeC = *(ByteCode + 2);// 对比特征值 寻找 nt!KeServiceDescriptorTable 函数地址/*lyshark kd> u KiSystemServiceRepeatnt!KiSystemServiceRepeat:fffff802`7c1d2b94 4c8d15e59c3b00  lea     r10,[nt!KeServiceDescriptorTable (fffff802`7c58c880)]fffff802`7c1d2b9b 4c8d1dde1e3a00  lea     r11,[nt!KeServiceDescriptorTableShadow (fffff802`7c574a80)]fffff802`7c1d2ba2 f7437880000000  test    dword ptr [rbx+78h],80hfffff802`7c1d2ba9 7413            je      nt!KiSystemServiceRepeat+0x2a (fffff802`7c1d2bbe)fffff802`7c1d2bab f7437800002000  test    dword ptr [rbx+78h],200000hfffff802`7c1d2bb2 7407            je      nt!KiSystemServiceRepeat+0x27 (fffff802`7c1d2bbb)fffff802`7c1d2bb4 4c8d1d051f3a00  lea     r11,[nt!KeServiceDescriptorTableFilter (fffff802`7c574ac0)]fffff802`7c1d2bbb 4d8bd3          mov     r10,r11*/if (OpCodeA == 0x4c && OpCodeB == 0x8d && OpCodeC == 0x1d){// 获取高位地址fffff802memcpy(&templong, ByteCode + 3, 4);// 与低位64da4880地址相加得到完整地址addr = (ULONGLONG)templong + (ULONGLONG)ByteCode + 7;return addr;}}}return  0;
}// 得到SSSDT个数
ULONGLONG GetSSSDTCount()
{PSYSTEM_SERVICE_TABLE pWin32k;ULONGLONG W32pServiceTable;pWin32k = (PSYSTEM_SERVICE_TABLE)((ULONG64)KeServiceDescriptorTableShadow + sizeof(SYSTEM_SERVICE_TABLE));W32pServiceTable = (ULONGLONG)(pWin32k->ServiceTableBase);// DbgPrint("Count => %d \n", pWin32k->NumberOfServices);return pWin32k->NumberOfServices;
}VOID UnDriver(PDRIVER_OBJECT driver)
{DbgPrint(("驱动程序卸载成功! \n"));
}NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{DbgPrint("hello lyshark \n");KeServiceDescriptorTableShadow = (PSYSTEM_SERVICE_TABLE)GetKeServiceDescriptorTableShadow();DbgPrint("[LyShark] SSSDT基地址 = 0x%p \n", KeServiceDescriptorTableShadow);ULONGLONG count = GetSSSDTCount();DbgPrint("[LyShark] SSSDT个数 = %d \n", count);// 循环枚举SSSDTfor (size_t Index = 0; Index < count; Index++){PSYSTEM_SERVICE_TABLE pWin32k;ULONGLONG W32pServiceTable;pWin32k = (PSYSTEM_SERVICE_TABLE)((ULONG64)KeServiceDescriptorTableShadow + sizeof(SYSTEM_SERVICE_TABLE));W32pServiceTable = (ULONGLONG)(pWin32k->ServiceTableBase);// 获取SSSDT地址//ln win32k!W32pServiceTable+((poi(win32k!W32pServiceTable+4*(1-1000))&0x00000000`ffffffff)>>4)-10000000//u win32k!W32pServiceTable+((poi(win32k!W32pServiceTable+4*(Index-0x1000))&0x00000000`ffffffff)>>4)-0x10000000//u poi(win32k!W32pServiceTable+4*(1-0x1000))//u poi(win32k!W32pServiceTable+4*(1-0x1000))&0x00000000`ffffffff//u (poi(win32k!W32pServiceTable+4*(1-0x1000))&0x00000000`ffffffff)>>4//u win32k!W32pServiceTable+((poi(win32k!W32pServiceTable+4*(1-0x1000))&0x00000000`ffffffff)>>4)-0x10000000ULONGLONG qword_temp = 0;LONG dw = 0;// SSSDT 下标从1000开始,而W32pServiceTable是从0开始// + 4 则是每次向下4字节就是下一个地址qword_temp = W32pServiceTable + 4 * (Index - 0x1000);dw = *(PLONG)qword_temp;// dw = qword_temp & 0x00000000ffffffff;dw = dw >> 4;qword_temp = W32pServiceTable + (LONG64)dw;DbgPrint("[LyShark] ID: %d | SSSDT: 0x%p \n", Index, qword_temp);}DriverObject->DriverUnload = UnDriver;return STATUS_SUCCESS;
}

枚举效果如下图所示所示,注意这一步必须要在GUI线程中执行,否则会异常,建议将枚举过程写成DLL文件,注入到explorer.exe进程内执行;

http://www.lryc.cn/news/245921.html

相关文章:

  • 实时LCM的ImgPilot搭建部署
  • 开源与闭源:大模型未来的发展之争
  • linux系统初始化本地git,创建ssh-key
  • JDBC 操作 SQL Server 时如何传入列表参数
  • [算法总结] - 蓄水池采样算法
  • 【Dockerfile】将自己的项目构建成镜像部署运行
  • flink和机器学习模型的常用组合方式
  • 自动驾驶学习笔记(十二)——定位技术
  • 【MySQL系列】PolarDB入门使用
  • 第二节HarmonyOS DevEco Studio创建项目以及界面认识
  • 网页设计--第5次课后作业
  • Spring Cache框架,实现了基于注解的缓存功能。
  • CSS-鼠标属性篇
  • Fiddler弱网测试究竟该怎么做?
  • 蓝桥杯-平方和(599)
  • 从零构建属于自己的GPT系列1:预处理模块(逐行代码解读)、文本tokenizer化
  • STM32内存介绍
  • Qt::Window 、Qt::Tool是 Qt 框架中的一个窗口标志(Window Flag),用于指定窗口的类型和行为
  • 东胜物流软件 SQL注入漏洞复现
  • 第1章 爬虫基础
  • Python教程---序列--序列修改元素
  • Linux 中的 ls 命令使用教程
  • Kubernetes基础入门:Kubernetes的有关概述
  • C# 无法将“int[]“类型隐式转换为“int?[]“,无法将“string[]“类型隐式转换为“string?[]“
  • 趣链科技,HyperChain
  • 吴恩达《机器学习》9-7-9-8:综合起来、自主驾驶
  • HTTP/HTTPS
  • C语言中#ifndef的头文件保护用法和宏定义用法
  • 141.【Git版本控制-本地仓库-远程仓库-IDEA开发工具全解版】
  • OpenCV快速入门:移动物体检测和目标跟踪