[原创] 驱动开发:Win10内核枚举SSDT表基址
2022-10-19 10:55:0 Author: bbs.pediy.com(查看原文) 阅读量:7 收藏

三年前面朝黄土背朝天的我,写了一篇如何在Windows 7系统下枚举内核SSDT表的文章《驱动开发:内核读取SSDT表基址》三年过去了我还是个单身狗,开个玩笑,微软的Windows 10系统已经覆盖了大多数个人PC终端,以前的方法也该进行迭代更新了,或许在网上你能够找到类似的文章,但我可以百分百肯定都不能用,今天LyShark将带大家一起分析Win10 x64最新系统SSDT表的枚举实现。

直接步入正题,首先SSDT表中文为系统服务描述符表,SSDT表的作用是把应用层与内核联系起来起到桥梁的作用,枚举SSDT表也是反内核工具最基本的功能,通常在64位系统中要想找到SSDT表,需要先找到KeServiceDescriptorTable这个函数,由于该函数没有被导出,所以只能动态的查找它的地址,庆幸的是我们可以通过查找msr(c0000082)这个特殊的寄存器来替代查找KeServiceDescriptorTable这一步,在新版系统中查找SSDT可以归纳为如下这几个步骤。

首先第一步通过rdmsr C0000082 MSR寄存器得到KiSystemCall64Shadow的函数地址,计算KiSystemCall64ShadowKiSystemServiceUser偏移量,如下图所示。

如上当我们找到了KiSystemServiceUser的地址以后,在KiSystemServiceUser向下搜索可找到KiSystemServiceRepeat里面就是我们要找的SSDT表基址。

得到ServiceTableBase的地址后,就能得到每个服务函数的地址。但这个表存放的并不是SSDT函数的完整地址,而是其相对于ServiceTableBase[Index]>>4的数据,每个数据占四个字节,所以计算指定Index函数完整地址的公式是;

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

// 署名权

// right to sign one's name on a piece of work

// PowerBy: LyShark

typedef struct _SYSTEM_SERVICE_TABLE

{

    PVOID     ServiceTableBase;

    PVOID     ServiceCounterTableBase;

    ULONGLONG   NumberOfServices;

    PVOID     ParamTableBase;

} SYSTEM_SERVICE_TABLE, *PSYSTEM_SERVICE_TABLE;

ULONGLONG ssdt_base_aadress;

PSYSTEM_SERVICE_TABLE KeServiceDescriptorTable;

typedef UINT64(__fastcall *SCFN)(UINT64, UINT64);

SCFN scfn;

// 解密算法

VOID DecodeSSDT()

{

    UCHAR strShellCode[36] = "\x48\x8B\xC1\x4C\x8D\x12\x8B\xF8\xC1\xEF\x07\x83\xE7\x20\x4E\x8B\x14\x17\x4D\x63\x1C\x82\x49\x8B\xC3\x49\xC1\xFB\x04\x4D\x03\xD3\x49\x8B\xC2\xC3";

    /*

    48:8BC1                  | mov rax,rcx                             |  rcx=index

    4C:8D12                  | lea r10,qword ptr ds:[rdx]              |  rdx=ssdt

    8BF8                     | mov edi,eax                             |

    C1EF 07                  | shr edi,7                               |

    83E7 20                  | and edi,20                              |

    4E:8B1417                | mov r10,qword ptr ds:[rdi+r10]          |

    4D:631C82                | movsxd r11,dword ptr ds:[r10+rax*4]     |

    49:8BC3                  | mov rax,r11                             |

    49:C1FB 04               | sar r11,4                               |

    4D:03D3                  | add r10,r11                             |

    49:8BC2                  | mov rax,r10                             |

    C3                       | ret                                     |

    */

    scfn = ExAllocatePool(NonPagedPool, 36);

    memcpy(scfn, strShellCode, 36);

}

// 获取 KeServiceDescriptorTable 首地址

ULONGLONG GetKeServiceDescriptorTable()

{

    // 设置起始位置

    PUCHAR StartSearchAddress = (PUCHAR)__readmsr(0xC0000082) - 0x1806FE;

    // 设置结束位置

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

            // 4c 8d 15 e5 9e 3b 00  lea r10,[nt!KeServiceDescriptorTable (fffff802`64da4880)]

            // 4c 8d 1d de 20 3a 00  lea r11,[nt!KeServiceDescriptorTableShadow (fffff802`64d8ca80)]

            if (OpCodeA == 0x4c && OpCodeB == 0x8d && OpCodeC == 0x15)

            {

                // 获取高位地址fffff802

                memcpy(&templong, ByteCode + 3, 4);

                // 与低位64da4880地址相加得到完整地址

                addr = (ULONGLONG)templong + (ULONGLONG)ByteCode + 7;

                return addr;

            }

        }

    }

    return  0;

}

// 得到函数相对偏移地址

ULONG GetOffsetAddress(ULONGLONG FuncAddr)

{

    ULONG dwtmp = 0;

    PULONG ServiceTableBase = NULL;

    if (KeServiceDescriptorTable == NULL)

    {

        KeServiceDescriptorTable = (PSYSTEM_SERVICE_TABLE)GetKeServiceDescriptorTable();

    }

    ServiceTableBase = (PULONG)KeServiceDescriptorTable->ServiceTableBase;

    dwtmp = (ULONG)(FuncAddr - (ULONGLONG)ServiceTableBase);

    return dwtmp << 4;

}

// 根据序号得到函数地址

ULONGLONG GetSSDTFunctionAddress(ULONGLONG NtApiIndex)

{

    ULONGLONG ret = 0;

    if (ssdt_base_aadress == 0)

    {

        // 得到ssdt基地址

        ssdt_base_aadress = GetKeServiceDescriptorTable();

    }

    if (scfn == NULL)

    {

        DecodeSSDT();

    }

    ret = scfn(NtApiIndex, ssdt_base_aadress);

    return ret;

}

VOID UnDriver(PDRIVER_OBJECT driver)

{

    DbgPrint(("驱动程序卸载成功! \n"));

}

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)

{

    DbgPrint("hello lyshark.com \n");

    ULONGLONG ssdt_address = GetKeServiceDescriptorTable();

    DbgPrint("SSDT基地址 = %p \n", ssdt_address);

    // 根据序号得到函数地址

    ULONGLONG address = GetSSDTFunctionAddress(51);

    DbgPrint("[LyShark] NtOpenFile地址 = %p \n", address);

    // 得到相对SSDT的偏移量

    DbgPrint("函数相对偏移地址 = %p \n", GetOffsetAddress(address));

    DriverObject->DriverUnload = UnDriver;

    return STATUS_SUCCESS;

}


文章来源: https://bbs.pediy.com/thread-274780.htm
如有侵权请联系:admin#unsafe.sh