首页 > 系统相关 >6.2 Windows驱动开发:内核枚举SSSDT表基址

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

时间:2023-11-26 19:55:06浏览次数:34  
标签:ULONGLONG SERVICE W32pServiceTable Windows fffff802 6.2 SSSDT ByteCode

在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 KiSystemServiceRepeat
                nt!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],80h
                fffff802`7c1d2ba9 7413            je      nt!KiSystemServiceRepeat+0x2a (fffff802`7c1d2bbe)
                fffff802`7c1d2bab f7437800002000  test    dword ptr [rbx+78h],200000h
                fffff802`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)
            {
                // 获取高位地址fffff802
                memcpy(&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 KiSystemServiceRepeat
            nt!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],80h
            fffff802`7c1d2ba9 7413            je      nt!KiSystemServiceRepeat+0x2a (fffff802`7c1d2bbe)
            fffff802`7c1d2bab f7437800002000  test    dword ptr [rbx+78h],200000h
            fffff802`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)
            {
                // 获取高位地址fffff802
                memcpy(&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);

    // 循环枚举SSSDT
    for (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)-0x10000000

        ULONGLONG 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进程内执行;

标签:ULONGLONG,SERVICE,W32pServiceTable,Windows,fffff802,6.2,SSSDT,ByteCode
From: https://www.cnblogs.com/LyShark/p/17857805.html

相关文章

  • Java 系统学习 | windows 环境安装 java
    学习语言,首先搭建环境。当前最新是Java21,本篇安装17版本。一、下载进入官网OracleDevelopers开发语言选择JavaDownloads选择JDK选择windows环境的JDK17下载exe文件即可想要其它老版本选择archive二、安装配置双击下载的exe......
  • SQL Server使用Windows身份验证模式安装后,重新设置SA密码
    首次安装SQLServer使用Windows身份验证模式安装后,要重新设置自带用户SA的密码(1)右键数据库--》属性(2)安全性-->勾选“SQLserver和Windows身份认证模式(S)”(3)数据库下“安全性”-->“登录名”-->“sa”-->"属性"(4)直接输入需要设置的密码(5)给sa用户设置权限,在“服务器角色”设置为public......
  • Windows App SDK? C++/WinRT? 狗都不学!
    空荡荡的官网开发文档,打开直接心凉一截!只写个HelloWorld教程就敢宣布自己为“跨时代”新产品?什么“C++桌面开发者的狂欢”?什么ProjectReunion?笑死!直接让所有C++WinAPI爱好者变成真正的......
  • 如何在windows系统下安装适用于linux的windows子系统
    最近部分工作需要在linux系统下操作,想着在自己本本上安装个虚拟机,废了九牛二虎之力安装好了VMware及Ubuntu系统,但是其在主机和虚拟机间共享数据不是很方便。于是又废了半天劲安装了VMwaretools,设置了共享盘、并启动后自动挂载。一切搞定后,无意中发现windows下可以安装linux子系统......
  • 【WPF】如何引用System.Windows.Forms;
    修改项目的csproj文件<PropertyGroup><OutputType>WinExe</OutputType><TargetFramework>net6.0-windows</TargetFramework><Nullable>enable</Nullable><UseWPF>true</UseWPF><UseWindowsFo......
  • 延长windows更新时间
       FlightSettingsMaxPauseDays    win+I快捷键打开windows设置-->Windows更新:     ......
  • Windows环境下修改my.ini导致MySQL启动失败
    问题:修改my.ini导致MySQL启动失败在Windows环境中,使用默认编辑器编辑my.ini配置文件可能导致MySQL启动失败,是因为默认编辑器使用的是UTF-8编码。解决方法:更改编码为ANSI使用文本编辑器打开my.ini,选择“另存为”并将编码更改为ANSI。重新启动MySQL服务,确保配置文件与MySQL默认......
  • 使用 NSIS 将 Python 程序制作成 Windows 环境下的安装程序(安装包)
    转载于:https://www.freesion.com/article/1583987771/#1_NSIS__6NSIS官网下载:https://nsis.sourceforge.io/Main_Page首先,需要使用PythonPyInstaller模块将Python程序制作成可直接运行的EXE程序。(可以不要-F参数)将我们所需的、打包好的windowdemo目录(文件夹)压索成.zip......
  • windows定时备份日志文件
    前言之前项目因为各种原因没有上CI/CD,也没有部署什么nginx管理工具,再加上nginx部署在windowsserver上,,面对日积月累不停膨胀的日志文件,需要一个定时脚本处理自动分割日志文件。本篇文章主要讲解如何在windows上创建定时任务,以及如何编写一个简单的bat脚本Windows备......
  • Day02 Windows快捷命令
    常见的Terminal命令:#盘符切换:直接输入盘符,不区分大小写 直接输入E:等 #查看当前目录下的所有文件:dir#切换目录:cdnewdirectory 只返回目标路径但不会跳转 cd\dnewdirectory 跨盘符切换的话要加\d参数后才会切换到目标路径, Linux用cd[路......