• x64下读取SSDT表,并且获取SSDT表函数.


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

    一丶读取SSDT表 (KeServiceDescriptorTable)

    1.1 原理

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

    1.2 手动获取SSDT表

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

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


    观看其位置反汇编代码:

    .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表的方式

    KiServiceInternalKiSystemServiceStart

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

    例子:


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

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

    .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空间中.所以不能直接跳转使用.而为什么这样.
    就是重要点三所在的汇编所体现的.

    .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]
    

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

    offset = SSDT[index * 4] ;  
    offset = offset >> 4 ;  亦或者等价于 offset = offset / 16
    pfnAddr = ssdt.base + offset;
    

    在内存中如下:

    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];

    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

    
    
    #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

    #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;
    }
    
    作者:IBinary


    坚持两字,简单,轻便,但是真正的执行起来确实需要很长很长时间.当你把坚持两字当做你要走的路,那么你总会成功. 想学习,有问题请加群.群号:725864912(收费)群名称: 逆向学习小分队 群里有大量学习资源. 以及定期直播答疑.有一个良好的学习氛围. 涉及到外挂反外挂病毒 司法取证加解密 驱动过保护 VT 等技术,期待你的进入。

    详情请点击链接查看置顶博客 https://www.cnblogs.com/iBinary/p/7572603.html
  • 相关阅读:
    Anaconda安装之路——坑呀!
    初读《企业应用架构模式》——阅读笔记1
    《需求工程》阅读笔记3
    codeforces 432D. Prefixes and Suffixes(后缀数组)
    hdu 6096String(trie树)
    uva 1349 Optimal Bus Route Design(拆点,费用流)
    数据结构c语言
    六个排序算法
    c无聊编程
    文件写入与文件读取
  • 原文地址:https://www.cnblogs.com/iBinary/p/15757208.html
Copyright © 2020-2023  润新知