[原创] 驱动开发:内核枚举PspCidTable句柄表
2022-10-16 16:13:0 Author: bbs.pediy.com(查看原文) 阅读量:9 收藏

在上一篇文章《驱动开发:内核枚举DpcTimer定时器》中我们通过枚举特征码的方式找到了DPC定时器基址并输出了内核中存在的定时器列表,本章将学习如何通过特征码定位的方式寻找Windows 10系统下面的PspCidTable内核句柄表地址。

PspCidTable 就是这样的一种表(内核句柄表),表的内部存放的是进程EPROCESS线程ETHREAD的内核对象,并通过进程PID线程TID进行索引,ID号以4递增,内核句柄表不属于任何进程,也不连接在系统的句柄表上,通过它可以返回系统的任何对象。

首先第一步先要得到PspCidTable函数内存地址,输入dp PspCidTable即可得到,如果在程序中则是调用MmGetSystemRoutineAddress取到。

PspCidTable是一个HANDLE_TALBE结构,当新建一个进程时,对应的会在PspCidTable存在一个该进程和线程对应的HANDLE_TABLE_ENTRY项。在windows10中依然采用动态扩展的方法,当句柄数少的时候就采用下层表,多的时候才启用中层表或上层表。

总结起来一个句柄表有一个上层表,一个上层表最多可以有256个中层表的入口指针,每个中层表最多可以有256个下层表的入口指针,每个下层表最多可以有256个进程和线程对象的指针。PspCidTable表可以看成是HANDLE_TBALE_ENTRY项的多级索引。

如上图所示TableCode是指向句柄表的指针,低二位(二进制)记录句柄表的等级:0(00)表示一级表,1(01)表示二级表,2(10)表示三级表。这里的 0xffffdc88-7d09b001 就说名它是一个二级表。

x64 系统中,每张表的大小是 0x1000(4096),一级表中存放的是 _handle_table_entry 结构(大小 = 16),二级表和三级表存放的是指针(大小 = 8)

继续查看第一张一级表,输入dp 0xffffdc887962a000命令,我们知道一级句柄表是根据进程或线程ID来索引的,且以4累加,所以第一行对应id = 0,第二行对应id = 4。根据尝试,PID = 4的进程是System

所以此处的第二行0xb281de28-3300ffa7就是加密后的System进程的EPROCESS结构,对于Win10系统来说解密算法(value >> 0x10) & 0xfffffffffffffff0是这样的,我们通过代码计算出来。

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

// 获取 PspCidTable

// By: LyShark.com

BOOLEAN get_PspCidTable(ULONG64* tableAddr)

{

    // 获取 PsLookupProcessByProcessId 地址

    UNICODE_STRING uc_funcName;

    RtlInitUnicodeString(&uc_funcName, L"PsLookupProcessByProcessId");

    ULONG64 ul_funcAddr = MmGetSystemRoutineAddress(&uc_funcName);

    if (ul_funcAddr == NULL)

    {

        return FALSE;

    }

    DbgPrint("PsLookupProcessByProcessId addr = %p \n", ul_funcAddr);

    // 40 字节有 call(PspReferenceCidTableEntry)

    /*

    0: kd> uf PsLookupProcessByProcessId

        nt!PsLookupProcessByProcessId:

        fffff802`0841cfe0 48895c2418      mov     qword ptr [rsp+18h],rbx

        fffff802`0841cfe5 56              push    rsi

        fffff802`0841cfe6 4883ec20        sub     rsp,20h

        fffff802`0841cfea 48897c2438      mov     qword ptr [rsp+38h],rdi

        fffff802`0841cfef 488bf2          mov     rsi,rdx

        fffff802`0841cff2 65488b3c2588010000 mov   rdi,qword ptr gs:[188h]

        fffff802`0841cffb 66ff8fe6010000  dec     word ptr [rdi+1E6h]

        fffff802`0841d002 b203            mov     dl,3

        fffff802`0841d004 e887000000      call    nt!PspReferenceCidTableEntry (fffff802`0841d090)

        fffff802`0841d009 488bd8          mov     rbx,rax

        fffff802`0841d00c 4885c0          test    rax,rax

        fffff802`0841d00f 7435            je      nt!PsLookupProcessByProcessId+0x66 (fffff802`0841d046)  Branch

    */

    ULONG64 ul_entry = 0;

    for (INT i = 0; i < 100; i++)

    {

        // fffff802`0841d004 e8 87 00 00 00      call    nt!PspReferenceCidTableEntry (fffff802`0841d090)

        if (*(PUCHAR)(ul_funcAddr + i) == 0xe8)

        {

            ul_entry = ul_funcAddr + i;

            break;

        }

    }

    if (ul_entry != 0)

    {

        // 解析 call 地址

        INT i_callCode = *(INT*)(ul_entry + 1);

        DbgPrint("i_callCode = %p \n", i_callCode);

        ULONG64 ul_callJmp = ul_entry + i_callCode + 5;

        DbgPrint("ul_callJmp = %p \n", ul_callJmp);

        // 来到 call(PspReferenceCidTableEntry) 内找 PspCidTable

        /*

        0: kd> uf PspReferenceCidTableEntry

            nt!PspReferenceCidTableEntry+0x115:

            fffff802`0841d1a5 488b0d8473f5ff  mov     rcx,qword ptr [nt!PspCidTable (fffff802`08374530)]

            fffff802`0841d1ac b801000000      mov     eax,1

            fffff802`0841d1b1 f0480fc107      lock xadd qword ptr [rdi],rax

            fffff802`0841d1b6 4883c130        add     rcx,30h

            fffff802`0841d1ba f0830c2400      lock or dword ptr [rsp],0

            fffff802`0841d1bf 48833900        cmp     qword ptr [rcx],0

            fffff802`0841d1c3 0f843fffffff    je      nt!PspReferenceCidTableEntry+0x78 (fffff802`0841d108)  Branch

        */

        for (INT i = 0; i < 0x120; i++)

        {

            // fffff802`0841d1a5 48 8b 0d 84 73 f5 ff  mov     rcx,qword ptr [nt!PspCidTable (fffff802`08374530)]

            if (*(PUCHAR)(ul_callJmp + i) == 0x48 && *(PUCHAR)(ul_callJmp + i + 1) == 0x8b && *(PUCHAR)(ul_callJmp + i + 2) == 0x0d)

            {

                // 解析 mov 地址

                INT i_movCode = *(INT*)(ul_callJmp + i + 3);

                DbgPrint("i_movCode = %p \n", i_movCode);

                ULONG64 ul_movJmp = ul_callJmp + i + i_movCode + 7;

                DbgPrint("ul_movJmp = %p \n", ul_movJmp);

                // 得到 PspCidTable

                *tableAddr = ul_movJmp;

                return TRUE;

            }

        }

    }

    return FALSE;

}

VOID UnDriver(PDRIVER_OBJECT driver)

{

    DbgPrint(("Uninstall Driver Is OK \n"));

}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)

{

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

    ULONG64 tableAddr = 0;

    get_PspCidTable(&tableAddr);

    DbgPrint("PspCidTable Address = %p \n", tableAddr);

    Driver->DriverUnload = UnDriver;

    return STATUS_SUCCESS;

}

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

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

// 获取 PspCidTable

// By: LyShark.com

BOOLEAN get_PspCidTable(ULONG64* tableAddr)

{

    // 获取 PsLookupProcessByProcessId 地址

    UNICODE_STRING uc_funcName;

    RtlInitUnicodeString(&uc_funcName, L"PsLookupProcessByProcessId");

    ULONG64 ul_funcAddr = MmGetSystemRoutineAddress(&uc_funcName);

    if (ul_funcAddr == NULL)

    {

        return FALSE;

    }

    DbgPrint("PsLookupProcessByProcessId addr = %p \n", ul_funcAddr);

    // 40 字节有 call(PspReferenceCidTableEntry)

    /*

    0: kd> uf PsLookupProcessByProcessId

        nt!PsLookupProcessByProcessId:

        fffff802`0841cfe0 48895c2418      mov     qword ptr [rsp+18h],rbx

        fffff802`0841cfe5 56              push    rsi

        fffff802`0841cfe6 4883ec20        sub     rsp,20h

        fffff802`0841cfea 48897c2438      mov     qword ptr [rsp+38h],rdi

        fffff802`0841cfef 488bf2          mov     rsi,rdx

        fffff802`0841cff2 65488b3c2588010000 mov   rdi,qword ptr gs:[188h]

        fffff802`0841cffb 66ff8fe6010000  dec     word ptr [rdi+1E6h]

        fffff802`0841d002 b203            mov     dl,3

        fffff802`0841d004 e887000000      call    nt!PspReferenceCidTableEntry (fffff802`0841d090)

        fffff802`0841d009 488bd8          mov     rbx,rax

        fffff802`0841d00c 4885c0          test    rax,rax

        fffff802`0841d00f 7435            je      nt!PsLookupProcessByProcessId+0x66 (fffff802`0841d046)  Branch

    */

    ULONG64 ul_entry = 0;

    for (INT i = 0; i < 100; i++)

    {

        // fffff802`0841d004 e8 87 00 00 00      call    nt!PspReferenceCidTableEntry (fffff802`0841d090)

        if (*(PUCHAR)(ul_funcAddr + i) == 0xe8)

        {

            ul_entry = ul_funcAddr + i;

            break;

        }

    }

    if (ul_entry != 0)

    {

        // 解析 call 地址

        INT i_callCode = *(INT*)(ul_entry + 1);

        DbgPrint("i_callCode = %p \n", i_callCode);

        ULONG64 ul_callJmp = ul_entry + i_callCode + 5;

        DbgPrint("ul_callJmp = %p \n", ul_callJmp);

        // 来到 call(PspReferenceCidTableEntry) 内找 PspCidTable

        /*

        0: kd> uf PspReferenceCidTableEntry

            nt!PspReferenceCidTableEntry+0x115:

            fffff802`0841d1a5 488b0d8473f5ff  mov     rcx,qword ptr [nt!PspCidTable (fffff802`08374530)]

            fffff802`0841d1ac b801000000      mov     eax,1

            fffff802`0841d1b1 f0480fc107      lock xadd qword ptr [rdi],rax

            fffff802`0841d1b6 4883c130        add     rcx,30h

            fffff802`0841d1ba f0830c2400      lock or dword ptr [rsp],0

            fffff802`0841d1bf 48833900        cmp     qword ptr [rcx],0

            fffff802`0841d1c3 0f843fffffff    je      nt!PspReferenceCidTableEntry+0x78 (fffff802`0841d108)  Branch

        */

        for (INT i = 0; i < 0x120; i++)

        {

            // fffff802`0841d1a5 48 8b 0d 84 73 f5 ff  mov     rcx,qword ptr [nt!PspCidTable (fffff802`08374530)]

            if (*(PUCHAR)(ul_callJmp + i) == 0x48 && *(PUCHAR)(ul_callJmp + i + 1) == 0x8b && *(PUCHAR)(ul_callJmp + i + 2) == 0x0d)

            {

                // 解析 mov 地址

                INT i_movCode = *(INT*)(ul_callJmp + i + 3);

                DbgPrint("i_movCode = %p \n", i_movCode);

                ULONG64 ul_movJmp = ul_callJmp + i + i_movCode + 7;

                DbgPrint("ul_movJmp = %p \n", ul_movJmp);

                // 得到 PspCidTable

                *tableAddr = ul_movJmp;

                return TRUE;

            }

        }

    }

    return FALSE;

}

/* 解析一级表

// By: LyShark.com

BaseAddr:一级表的基地址

index1:第几个一级表

index2:第几个二级表

*/

VOID parse_table_1(ULONG64 BaseAddr, INT index1, INT index2)

{

    // 遍历一级表(每个表项大小 16 ),表大小 4k,所以遍历 4096/16 = 526

    PEPROCESS p_eprocess = NULL;

    PETHREAD p_ethread = NULL;

    INT i_id = 0;

    for (INT i = 0; i < 256; i++)

    {

        if (!MmIsAddressValid((PVOID64)(BaseAddr + i * 16)))

        {

            DbgPrint("非法地址= %p \n", BaseAddr + i * 16);

            continue;

        }

        ULONG64 ul_recode = *(PULONG64)(BaseAddr + i * 16);

        // 解密

        ULONG64 ul_decode = (LONG64)ul_recode >> 0x10;

        ul_decode &= 0xfffffffffffffff0;

        // 判断是进程还是线程

        i_id = i * 4 + 1024 * index1 + 512 * index2 * 1024;

        if (PsLookupProcessByProcessId(i_id, &p_eprocess) == STATUS_SUCCESS)

        {

            DbgPrint("进程PID: %d | ID: %d | 内存地址: %p | 对象: %p \n", i_id, i, BaseAddr + i * 0x10, ul_decode);

        }

        else if (PsLookupThreadByThreadId(i_id, &p_ethread) == STATUS_SUCCESS)

        {

            DbgPrint("线程TID: %d | ID: %d | 内存地址: %p | 对象: %p \n", i_id, i, BaseAddr + i * 0x10, ul_decode);

        }

    }

}

/* 解析二级表

// By: LyShark.com

BaseAddr:二级表基地址

index2:第几个二级表

*/

VOID parse_table_2(ULONG64 BaseAddr, INT index2)

{

    // 遍历二级表(每个表项大小 8),表大小 4k,所以遍历 4096/8 = 512

    ULONG64 ul_baseAddr_1 = 0;

    for (INT i = 0; i < 512; i++)

    {

        if (!MmIsAddressValid((PVOID64)(BaseAddr + i * 8)))

        {

            DbgPrint("非法二级表指针(1):%p \n", BaseAddr + i * 8);

            continue;

        }

        if (!MmIsAddressValid((PVOID64)*(PULONG64)(BaseAddr + i * 8)))

        {

            DbgPrint("非法二级表指针(2):%p \n", BaseAddr + i * 8);

            continue;

        }

        ul_baseAddr_1 = *(PULONG64)(BaseAddr + i * 8);

        parse_table_1(ul_baseAddr_1, i, index2);

    }

}

/* 解析三级表

// By: LyShark.com

BaseAddr:三级表基地址

*/

VOID parse_table_3(ULONG64 BaseAddr)

{

    // 遍历三级表(每个表项大小 8),表大小 4k,所以遍历 4096/8 = 512

    ULONG64 ul_baseAddr_2 = 0;

    for (INT i = 0; i < 512; i++)

    {

        if (!MmIsAddressValid((PVOID64)(BaseAddr + i * 8)))

        {

            continue;

        }

        if (!MmIsAddressValid((PVOID64)* (PULONG64)(BaseAddr + i * 8)))

        {

            continue;

        }

        ul_baseAddr_2 = *(PULONG64)(BaseAddr + i * 8);

        parse_table_2(ul_baseAddr_2, i);

    }

}

VOID UnDriver(PDRIVER_OBJECT driver)

{

    DbgPrint(("Uninstall Driver Is OK \n"));

}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)

{

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

    ULONG64 tableAddr = 0;

    get_PspCidTable(&tableAddr);

    DbgPrint("PspCidTable Address = %p \n", tableAddr);

    // 获取 _HANDLE_TABLE 的 TableCode

    ULONG64 ul_tableCode = *(PULONG64)(((ULONG64)*(PULONG64)tableAddr) + 8);

    DbgPrint("ul_tableCode = %p \n", ul_tableCode);

    // 取低 2位(二级制11 = 3

    INT i_low2 = ul_tableCode & 3;

    DbgPrint("i_low2 = %X \n", i_low2);

    // 一级表

    if (i_low2 == 0)

    {

        // TableCode 低 2位抹零(二级制11 = 3

        parse_table_1(ul_tableCode & (~3), 0, 0);

    }

    // 二级表

    else if (i_low2 == 1)

    {

        // TableCode 低 2位抹零(二级制11 = 3

        parse_table_2(ul_tableCode & (~3), 0);

    }

    // 三级表

    else if (i_low2 == 2)

    {

        // TableCode 低 2位抹零(二级制11 = 3

        parse_table_3(ul_tableCode & (~3));

    }

    else

    {

        DbgPrint("LyShark提示: 错误,非法! ");

        return FALSE;

    }

    Driver->DriverUnload = UnDriver;

    return STATUS_SUCCESS;

}

至此文章就结束了,这里多说一句,实际上ZwQuerySystemInformation枚举系统句柄时就是走的这条双链,枚举系统进程如果使用的是这个API函数,那么不出意外它也是在这些内核表中做的解析。

http://www.blogfshare.com/details-in-pspcidtbale.html
https://blog.csdn.net/whatday/article/details/17189093
https://www.cnblogs.com/kuangke/p/5761615.html
https://www.cnblogs.com/LyShark/p/16796158.html


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