一丶读取SSDT表 (KeServiceDescriptorTable)

2022-05-10 13:28:17 浏览数 (1)

目录

  • 64位下读取SSDT表并且获取SSDT函数
    • 一丶读取SSDT表 (KeServiceDescriptorTable)
      • 1.1 原理
      • 1.2 手动获取SSDT表
        • 1.2.1 重点1 了解引用流程以及其它方式寻找SSDT表的方式
        • 1.2.2 重要点2 获取SSDT表以及Shadow表位置
        • 1.2.3 重要点3 SSDT表的加密获取以及使用
    • 二丶两种方式实现获取SSDT表
      • 2.1 常规方式获取SSDT表.
      • 2.2 通过API寻找方式来找寻SSDT

64位下读取SSDT表并且获取SSDT函数

一丶读取SSDT表 (KeServiceDescriptorTable)

1.1 原理

在64位系统下我们可以通过读取msr 寄存器来获取内核函数入口. msr在开启内核隔离模式下获取的是 KiSystemCall64Shadow 而在未开启内核模式下则是获取的 KiSystemCall64

1.2 手动获取SSDT表

windbg链接双机调试. 输入命令 rdmsr 0xC0000082 即可看到内核函数入口. 反汇编此函数的地址往下找即可看到获取SSDT表位置代码.

这里我以IDA举例子 如果你能反汇编内核文件,并且为其下载好符号.则在函数列表中直接搜索 KiSystemCall64 即可. 如下:

观看其位置反汇编代码:

代码语言:javascript复制
.text:00000001401D2980                               KiSystemServiceStart:                   ; DATA XREF: KiServiceInternal 5A↑o 重要点1
.text:00000001401D2980                                                                      
.text:00000001401D2980 48 89 A3 90 00 00 00                          mov     [rbx 90h], rsp
.text:00000001401D2987 8B F8                                         mov     edi, eax
.text:00000001401D2989 C1 EF 07                                      shr     edi, 7
.text:00000001401D298C 83 E7 20                                      and     edi, 20h
.text:00000001401D298F 25 FF 0F 00 00                                and     eax, 0FFFh
.text:00000001401D2994
.text:00000001401D2994                               KiSystemServiceRepeat:                  
.text:00000001401D2994 4C 8D 15 E5 9E 3B 00                          lea     r10, KeServiceDescriptorTable_0          重要点2
.text:00000001401D299B 4C 8D 1D DE 20 3A 00                          lea     r11, KeServiceDescriptorTableShadow
.text:00000001401D29A2 F7 43 78 80 00 00 00                          test    dword ptr [rbx 78h], 80h
.text:00000001401D29A9 74 13                                         jz      short loc_1401D29BE
.text:00000001401D29AB F7 43 78 00 00 20 00                          test    dword ptr [rbx 78h], 200000h
.text:00000001401D29B2 74 07                                         jz      short loc_1401D29BB
.text:00000001401D29B4 4C 8D 1D 05 21 3A 00                          lea     r11, KeServiceDescriptorTableFilter
.text:00000001401D29BB
.text:00000001401D29BB                               loc_1401D29BB:                          
.text:00000001401D29BB 4D 8B D3                                      mov     r10, r11
.text:00000001401D29BE
.text:00000001401D29BE                               loc_1401D29BE:                         
.text:00000001401D29BE 41 3B 44 3A 10                                cmp     eax, [r10 rdi 10h]
.text:00000001401D29C3 0F 83 2C 05 00 00                             jnb     loc_1401D2EF5
.text:00000001401D29C9 4D 8B 14 3A                                   mov     r10, [r10 rdi]
.text:00000001401D29CD 4D 63 1C 82                                   movsxd  r11, dword ptr [r10 rax*4]
.text:00000001401D29D1 49 8B C3                                      mov     rax, r11
.text:00000001401D29D4 49 C1 FB 04                                   sar     r11, 4                               重要点3
.text:00000001401D29D8 4D 03 D3                                      add     r10, r11
.text:00000001401D29DB 83 FF 20                                      cmp     edi, 20h ; ' '
.text:00000001401D29DE 75 50                                         jnz     short loc_1401D2A30
.text:00000001401D29E0 4C 8B 9B F0 00 00 00                          mov     r11, [rbx 0F0h]

上述汇编描述了三个重要点

1.2.1 重点1 了解引用流程以及其它方式寻找SSDT表的方式

KiServiceInternal 与 KiSystemServiceStart

这里要了解下SSDT表起始获取是 KiSystemServiceStart 而 KiServiceInternal 则会引用 KiSystemServiceStart 那么为什么讲一下这里. 因为在内核中我们可以通过任意一个内核函数来找到 KiServiceInternal 然后通过 KiServiceInternal 来找到 KiSystemServiceStart 然后通过 KiSystemServiceStart 来定位SSDT表或者SSDTShadow表

例子:

1.2.2 重要点2 获取SSDT表以及Shadow表位置

重要点2位置的两行代码则是获取SSDT表与Shadow表. 表示为如下:

代码语言:javascript复制
.text:00000001401D2994                               KiSystemServiceRepeat: 
.text:00000001401D2994 4C 8D 15 E5 9E 3B 00                          lea     r10, KeServiceDescriptorTable_0
.text:00000001401D299B 4C 8D 1D DE 20 3A 00                          lea     r11, KeServiceDescriptorTableShadow

特征码分别为 0x4c 0x8d 0x15 ---> Get SSDT 0x4c 0x8d 0x1d ---> Get SSDTShadow

1.2.3 重要点3 SSDT表的加密获取以及使用

这里是重点在32位下的SSDT表你可以任意HOOK 而到了64位下你则不能 "HOOK" 了 因为你的函数定义不在同一个4GB空间中.所以不能直接跳转使用.而为什么这样. 就是重要点三所在的汇编所体现的.

代码语言:javascript复制
.text:00000001401D29BE 41 3B 44 3A 10                                cmp     eax, [r10 rdi 10h]
.text:00000001401D29C3 0F 83 2C 05 00 00                             jnb     loc_1401D2EF5
.text:00000001401D29C9 4D 8B 14 3A                                   mov     r10, [r10 rdi]
.text:00000001401D29CD 4D 63 1C 82                                   movsxd  r11, dword ptr [r10 rax*4] offset = SSDT[sizeof(int) * index]
.text:00000001401D29D1 49 8B C3                                      mov     rax, r11
.text:00000001401D29D4 49 C1 FB 04                                   sar     r11, 4          offset = offset >>  4
.text:00000001401D29D8 4D 03 D3                                      add     r10, r11        pfn = ssdt.base   offset   = 实际的函数地址
.text:00000001401D29DB 83 FF 20                                      cmp     edi, 20h ; ' '
.text:00000001401D29DE 75 50                                         jnz     short loc_1401D2A30
.text:00000001401D29E0 4C 8B 9B F0 00 00 00                          mov     r11, [rbx 0F0h]

这里有一个右移的操作.观看汇编反汇编为高级代码则如下:

代码语言:javascript复制
offset = SSDT[index * 4] ;  
offset = offset >> 4 ;  亦或者等价于 offset = offset / 16
pfnAddr = ssdt.base   offset;

在内存中如下:

代码语言:javascript复制
1: kd> dq 0xfffff8067758c880   查看SSDT表
fffff806`7758c880  fffff806`77424cc0 00000000`00000000
fffff806`7758c890  00000000`000001d0 fffff806`77425404
fffff806`7758c8a0  00000000`00000000 00000000`00000000
fffff806`7758c8b0  00000000`00000000 00000000`00000000
fffff806`7758c8c0  fffff806`771cc8c0 fffff806`771ccc00
fffff806`7758c8d0  fffff806`771d1580 fffff806`771d18c0
fffff806`7758c8e0  fffff806`771d1c00 fffff806`771d2640
fffff806`7758c8f0  fffff806`771d2180 00000000`00000000
1: kd> dd fffff806`77424cc0   查看SSDT表数组中的内容
fffff806`77424cc0  fced6104 fcf76a00 02b81c02 04749800
fffff806`77424cd0  01ce2700 fd9fe900 01c03705 01c38c06
fffff806`77424ce0  0220d205 0288b301 028aaa00 01a96400
fffff806`77424cf0  01e26500 01c27900 028a4600 01cc7c00
fffff806`77424d00  0221e201 01bf7001 0295a500 01fde702
fffff806`77424d10  01a86600 01e0a200 01d09201 01ce8102
fffff806`77424d20  022b9002 01f4a401 01fbc601 02871e05
fffff806`77424d30  0228ee00 028bcf03 02362000 0461a300

我们可以手动计算出地址. 根据上面反汇编的代码得出

offset = *(PLONG)SSDT uid * 4 = fced6104 注意offset不是ULONG类型. 而是LONG类型. 偏移记录的是整数 而不是无符号整数.否则你计算出的基地址就会加10000000的数据.导致计算出错 offset = offset >> 4; pfnAddr = (PULONGLONG)(offset ssdt.base) ==> offset 0xfffff80677424cc0 最终计算出的地址为: pfnAddr = 0xfffff806771122d0 查看pcHunter

核心代码:

提供了两种方式.指针或者数组寻址 都是可以可以的.数组那块我是转换为了ULONG来操作的 因为: char * ary; offset = ary sizeof(type) * index 就是数组寻址. 可以优化为: PULONG ssdt; offset = ssdt[index];

代码语言:javascript复制
PVOID Cssdt::GetProcById(ULONG uId)
{
    PKSERVICE_TABLE_DESCRIPTOR pSsdt = NULL;
    PULONGLONG pFunctionAddr = NULL;
    PUCHAR ssdtbase = NULL;
    LONG offset = 0;
    pSsdt = this->GetSsdtBaseByKernelFunction((PVOID)ZwCreateFile, 0x500);
    if (pSsdt == NULL)
    {
        return NULL;
    }

 
    //calc function
    //pointer get addr
    // ssdtbase = (PUCHAR)pSsdt->Base;
    // offset = (LONG) * (PULONG)(ssdtbase   uId * 4);
    // offset = (LONG)offset >> 4;
    // pFunctionAddr = (PULONGLONG)(offset   ssdtbase);

    //array get addr
    ssdtbase = (PUCHAR)pSsdt->Base;
    offset = (LONG)((PULONG)ssdtbase)[uId];
    offset = (LONG)offset >> 4;
    pFunctionAddr = (PULONGLONG)(offset   ssdtbase);
    return pFunctionAddr;
}

二丶两种方式实现获取SSDT表

2.1 常规方式获取SSDT表.

暂时待写

2.2 通过API寻找方式来找寻SSDT

.h

代码语言:javascript复制
#pragma once

#ifdef __cplusplus
extern "C"
{
#endif

#include <ntifs.h>
#include <ntddk.h>
#include <Ntstrsafe.h>
#include "ntimage.h"
#ifdef __cplusplus
}
#endif

#ifdef _AMD64_
typedef struct _KSERVICE_TABLE_DESCRIPTOR
{
    PULONG_PTR Base;
    PULONG_PTR Count;
    PULONG_PTR Limit;
    PULONG_PTR Number;
} KSERVICE_TABLE_DESCRIPTOR, *PKSERVICE_TABLE_DESCRIPTOR;
#else
#endif

class Cssdt
{
private:
    /* data */
public:
    Cssdt(/* args */);
    ~Cssdt();

public:
    PVOID GetProcById(ULONG uId);
    PKSERVICE_TABLE_DESCRIPTOR GetSsdtBase();
    PKSERVICE_TABLE_DESCRIPTOR GetSsdtBaseByKernelFunction(PVOID pfnKernelFunction, ULONG findSize);
};

.cpp

代码语言:javascript复制
#include "ssdt.h"

Cssdt::Cssdt(/* args */)
{
}

Cssdt::~Cssdt()
{
}
PVOID Cssdt::GetProcById(ULONG uId)
{
    PKSERVICE_TABLE_DESCRIPTOR pSsdt = NULL;
    PULONGLONG pFunctionAddr = NULL;
    PUCHAR ssdtbase = NULL;
    LONG offset = 0;
    pSsdt = this->GetSsdtBaseByKernelFunction((PVOID)ZwCreateFile, 0x500);
    if (pSsdt == NULL)
    {
        return NULL;
    }

    //calc function
    //pointer get addr
    // ssdtbase = (PUCHAR)pSsdt->Base;
    // offset = (LONG) * (PULONG)(ssdtbase   uId * 4);
    // offset = (LONG)offset >> 4;
    // pFunctionAddr = (PULONGLONG)(offset   ssdtbase);

    //array get addr
    ssdtbase = (PUCHAR)pSsdt->Base;
    offset = (LONG)((PULONG)ssdtbase)[uId];
    offset = (LONG)offset >> 4;
    pFunctionAddr = (PULONGLONG)(offset   ssdtbase);
    return pFunctionAddr;
}
PKSERVICE_TABLE_DESCRIPTOR Cssdt::GetSsdtBaseByKernelFunction(PVOID pfnKernelFunction, ULONG findSize)
{
    BOOLEAN bIsFind = FALSE;
    PVOID pFindAddress = NULL;

    ULONG uSearchStartIndex = 0;
    PUCHAR pSearchAddress = (PUCHAR)pfnKernelFunction;
    PUCHAR pfnKiServiceInternal = NULL;
    PKSERVICE_TABLE_DESCRIPTOR pSsdtInfo = NULL;
    if (pfnKernelFunction == NULL)
    {
        return NULL;
    }
    if (!MmIsAddressValid(pfnKernelFunction))
    {
        return NULL;
    }
    //查找函数中的 .text:00000001401BD9E9 E9 D2 4B 01 00   jmp     KiServiceInternal
    for (uSearchStartIndex = 0; uSearchStartIndex < findSize; uSearchStartIndex  )
    {
        if (MmIsAddressValid(&pSearchAddress[uSearchStartIndex]))
        {
            if (pSearchAddress[uSearchStartIndex] == 0xE9)
            {
                //取出它记录的偏移地址. 公式: DstProc = offset   len(opcode)   CurrendRip

                if (MmIsAddressValid((PULONG)&pSearchAddress[uSearchStartIndex   1]))
                {
                    ULONG offset = *(PULONG)&pSearchAddress[uSearchStartIndex   1];
                    PUCHAR pCurRip = &pSearchAddress[uSearchStartIndex];
                    pfnKiServiceInternal = pCurRip   offset   5;
                    break;
                }
            }
        }
    }

    if (pfnKiServiceInternal == NULL)
    {
        return NULL;
    }

    for (uSearchStartIndex = 0; uSearchStartIndex < findSize; uSearchStartIndex  )
    {
        if (MmIsAddressValid((PULONGLONG)&pfnKiServiceInternal[uSearchStartIndex]))
        {
            if (
                pfnKiServiceInternal[uSearchStartIndex] == 0x4C && pfnKiServiceInternal[uSearchStartIndex   1] == 0x8D && pfnKiServiceInternal[uSearchStartIndex   2] == 0x15)
            {

                ULONG offset = *(PULONG)&pfnKiServiceInternal[uSearchStartIndex   3];
                PUCHAR pCurRip = &pfnKiServiceInternal[uSearchStartIndex];
                pSsdtInfo = (PKSERVICE_TABLE_DESCRIPTOR)(pCurRip   offset   7);
                break;
            }
        }
    }
    //Shadow 同上
    return pSsdtInfo;
}

PKSERVICE_TABLE_DESCRIPTOR Cssdt::GetSsdtBase()
{

    return NULL;
}

0 人点赞