zoukankan      html  css  js  c++  java
  • Windows系统调用中的系统服务表描述符(SSDT)

     Windows内核分析索引目录:https://www.cnblogs.com/onetrainee/p/11675224.html

    Windows系统调用中的系统服务表描述符(SSDT)

      在前面,我们将解过 系统服务表。可是,我们有个疑问,系统服务表存储在哪里呢?

      答案就是:系统服务表 存储在 系统服务描述符表中。(其又称为 SSDT Service Descriptor Table)

      

     一、使用PELord函数从ntoskrnl.exe文件中查看SSDT导出函数

      如图,可以看出KeServiceDescriptorTable导出函数。

      通过该函数可以查找SSDT表的位置。

      

    二、通过Windbg来内存中查看SSDT表

      使用Windbg,可以使用 kd> dd nt!KeServiceDescriptorTable 指令来查看SSDT表。

      但该指令存在缺点,可以看到第二张表为0,说明如果使用KeServiceDescriptorTable这个公开的导出函数,我们无法看到win32k.sys这张表结构

      kd> dd nt!KeServiceDescriptorTable
        83f759c0  83e89d9c 00000000 00000191 83e8a3e4
        83f759d0  00000000 00000000 00000000 00000000
        83f759e0  83ee86af 00000000 0327aa43 000000bb
        83f759f0  00000011 00000100 5385d2ba d717548f

      为了解决上面这个问题,我们只能使用另外一个指令,该指令对应的是一个未公开导出的函数。

      如下,可以看到其第二行,win32k.sys系统服务表已经可见。

      kd> dd KeServiceDescriptorTableShadow
        83f75a00  83e89d9c 00000000 00000191 83e8a3e4
        83f75a10  83b66000 00000000 00000339 83b6702c
        83f75a20  00000000 00000000 83f75a24 00000340
        83f75a30  00000340 855e8440 00000007 00000000

    三、验证ReadMemory真正的内核实现部分

      我们在这篇《Windows系统调用中API的三环部分(依据分析重写ReadProcessMemory函数)》中曾提到过直接使用快速调用来摒弃R3层层封装的API,其中给eax一个函数号,现在我们来实战刨析一下。

    mov eax, 0x115
    mov edx, 0X7FFE0300

      如下,系统描述符的数据结构,其依次分别为

      

      其依次分别为 ServiceTable 83e89d9c,Count 00000000,ServiceLimit  00000191,ServiceTable 83e8a3e4 

      使用Windbg来查看其115h序号的函数地址 115h*4 + 83e89d9c (ServiceTable)

      得到函数地址为 8406c82c

      kd> dd 115h*4 + 83e89d9c
        83e8a1f0  8406c82c 840feb46 83fb488c 83fb6128 

      再对此进行反汇编可得

      kd > u 8406c82c   
                    nt!NtReadVirtualMemory:
                    8406c82c 6a18            push    18h
                    8406c82e 68282ae683      push    offset nt!? ? ::FNODOBFM::`string'+0x3ea8 (83e62a28)
                    8406c833 e870e3e1ff      call    nt!_SEH_prolog4(83e8aba8)
                    8406c838 648b3d24010000  mov     edi, dword ptr fs : [124h]
                    8406c83f 8a873a010000    mov     al, byte ptr[edi + 13Ah]
                    8406c845 8845e4          mov     byte ptr[ebp - 1Ch], al
                    8406c848 8b7514          mov     esi, dword ptr[ebp + 14h]
                    8406c84b 84c0            test    al, al

      之后,我们查看该nt!NtReadVirtualMemory函数的参数个数

      kd > db 83e8a3e4 + 115
                    83e8a4f9  14 08 04 04 14 04 10 08 - 0c 04 14 18 08 08 08 0c
                    83e8a509  0c 08 10 14 08 08 0c 08 - 0c 0c 04 08 08 08 08 08  
                    83e8a519  08 0c 0c 24 00 08 08 08 - 0c 04 08 04 08 10 08 04  

      

    四、通过修改SSDT表增添系统服务函数

      我们在 Windows系统调用中API的三环部分(依据分析重写ReadProcessMemory函数) 调用的是 115h 号函数。

      现在,我们将该函数地址放到 191 号函数处(之前一共有191个函数,占据0-190位)。

      修改思路:

      1)将 nt!NtReadVirtualMemory 函数地址 8406c82c 放到 191号处(83e89d9 + 191h*4)

        kd> ed 83e89d9 + 191h*4 8406c82c 

      2)  增大 服务表最大个数。 (因为我们上一节分析其反汇编代码的时候,发现其会进行最大个数的判断)

        kd> ed 83f75a00+8 192

      3)  修改参数个数表中对应的191号参数个数。(我们之前查阅过其为 14,以字节为单位)

        kd> eb 83e8a3e4+191 14

      4)  之后,我们运行下列代码。其与《Windows系统调用中API的三环部分(依据分析重写ReadProcessMemory函数)》唯一的不同调用函数号为192,最终效果完全一样。

     1 #include "pch.h"
     2 #include <iostream>
     3 #include <algorithm>
     4 #include <Windows.h>
     5 void  ReadMemory(HANDLE hProcess, PVOID pAddr, PVOID pBuffer, DWORD dwSize, DWORD  *dwSizeRet)
     6 {
     7 
     8     _asm
     9     {
    10         lea     eax, [ebp + 0x14]
    11         push    eax
    12         push[ebp + 0x14]
    13         push[ebp + 0x10]
    14         push[ebp + 0xc]
    15         push[ebp + 8]
    16         sub esp, 4
    17         mov eax, 0x192  // 注意:修改的是这里
    18         mov edx, 0X7FFE0300   //sysenter不能直接调用,我间接call的
    19         CALL DWORD PTR[EDX]
    20         add esp, 24
    21 
    22     }
    23 }
    24 int main()
    25 {
    26     HANDLE hProcess = 0;
    27     int t = 123;
    28     DWORD pBuffer;
    29     //hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0,a);
    30     ReadMemory((HANDLE)-1, (PVOID)&t, &pBuffer, sizeof(int), 0);
    31     printf("%X
    ", pBuffer);
    32     ReadProcessMemory((HANDLE)-1, &t, &pBuffer, sizeof(int), 0);
    33     printf("%X
    ", pBuffer);
    34 
    35     getchar();
    36     return 0;
    37 }

     五、驱动代码实现

      其中涉及页保护问题,可以查看我的博客其他文章,有介绍。

     1 /*
     2     利用IDT_HOOK技术,将115h号函数地址挂靠在191h处。
     3 */
     4 
     5 #include <ntddk.h>
     6 #include <ntstatus.h>
     7 // 系统服务表的结构体
     8 typedef struct _KSYSTEM_SERVICE_TABLE
     9 {
    10     PULONG  ServiceTableBase;                               // SSDT (System Service Dispatch Table)的基地址
    11     PULONG  ServiceCounterTableBase;                        // 用于 checked builds, 包含 SSDT 中每个服务被调用的次数
    12     ULONG   NumberOfService;                                // 服务函数的个数, NumberOfService * 4 就是整个地址表的大小
    13     // ParamTableBase[0x115],一个字节一个数,因此要使用PUCHAR数据类型而不是PULONG
    14     PUCHAR   ParamTableBase                                 // SSPT(System Service Parameter Table)的基地址
    15 } KSYSTEM_SERVICE_TABLE, *PKSYSTEM_SERVICE_TABLE;
    16 // SSDT结构体
    17 typedef struct _KSERVICE_TABLE_DESCRIPTOR
    18 {
    19     KSYSTEM_SERVICE_TABLE   ntoskrnl;                       // ntoskrnl.exe 的服务函数
    20     KSYSTEM_SERVICE_TABLE   win32k;                         // win32k.sys 的服务函数(GDI32.dll/User32.dll 的内核支持)
    21     KSYSTEM_SERVICE_TABLE   notUsed1;
    22     KSYSTEM_SERVICE_TABLE   notUsed2;
    23 }KSERVICE_TABLE_DESCRIPTOR, *PKSERVICE_TABLE_DESCRIPTOR;
    24  // KeServiceDescriptorTable 这个变量名不能改,其为 ntoskrnl.exe 导出的变量名
    25 extern PKSYSTEM_SERVICE_TABLE KeServiceDescriptorTable; 
    26 //关闭内存保护裸函数
    27 void _declspec(naked)OffMemoryProtect()
    28 {
    29     __asm { //关闭内存保护
    30         push eax;
    31         mov eax, cr0;
    32         and eax, ~0x10000;
    33         mov cr0, eax;
    34         pop eax;
    35         ret;
    36     }
    37 }
    38 //开启内存保护裸函数
    39 void  _declspec(naked)OnMemoryProtect()
    40 {
    41     __asm { //恢复内存保护
    42         push eax;
    43         mov eax, cr0;
    44         or eax, 0x10000;
    45         mov cr0, eax;
    46         pop eax;
    47         ret;
    48     }
    49 }
    50 
    51 // 驱动卸载函数
    52 NTSTATUS DriverUnload(PDRIVER_OBJECT Driver) {
    53     return STATUS_SUCCESS;
    54 }
    55 
    56 // 驱动入口函数
    57 NTSTATUS DriverEntry(PDRIVER_OBJECT Driver, PUNICODE_STRING RegPath) {
    58     NTSTATUS status;
    59 
    60     Driver->DriverUnload = DriverUnload;
    61     DbgPrint("---> %x", KeServiceDescriptorTable);
    62 
    63     // 关闭内存保护
    64     OffMemoryProtect();
    65 
    66     // 修改服务表的最大个数
    67     KeServiceDescriptorTable->NumberOfService++;
    68     // 增加一个新地址
    69     KeServiceDescriptorTable->ServiceTableBase[0x191] = KeServiceDescriptorTable->ServiceTableBase[0x115];
    70     // 将参数地址也给同步更新
    71     KeServiceDescriptorTable->ParamTableBase[0x191] = KeServiceDescriptorTable->ParamTableBase[0x115];
    72 
    73     // 开启内存保护
    74     OnMemoryProtect();
    75     return STATUS_SUCCESS;
    76 }

      

  • 相关阅读:
    OC-字典
    作业
    block语法排序 遍历
    oc-NSArray
    oc之获取系统当前时间的方法
    修改mysql的默认字符集
    mysql查询结果添加序列号
    PHP Socket 编程过程详解
    一篇详细的 Mysql Explain 详解
    阿里云云主机挂载数据盘,格式化硬盘(新购云主机)(转)
  • 原文地址:https://www.cnblogs.com/onetrainee/p/11717309.html
Copyright © 2011-2022 走看看