SSDT HOOK

环境win10 x64,代码已上传github,没有绕patch guard

X64 SSDT HOOK

代码是部分借鉴了Tesla.Angela的教材

先把代码奉上

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
#include <ntddk.h>
#include <intrin.h>

NTKERNELAPI UCHAR* PsGetProcessImageFileName(PEPROCESS EProcess);
NTKERNELAPI UCHAR* PsLookupProcessByProcessId(IN ULONG ulProcId, OUT PEPROCESS* pEProcess);
typedef unsigned long DWORD;
//定义ssdt结构
typedef struct _SERVICES_DESCRIPTOR_TABLE {
PVOID ServiceTableBase; // The Base of SSDT
PVOID ServiceCounterTableBase;
ULONGLONG ServiceCount; // The Count of SSDT Function
PVOID ParamTableBase;
}SERVICES_DESCRIPTOR_TABLE, * PSERVICES_DESCRIPTOR_TABLE;

typedef NTSTATUS(NTAPI* NTOPENPROCESS)(
_Out_ PHANDLE ProcessHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_opt_ PCLIENT_ID ClientId
);

NTOPENPROCESS originNtOpenProcess = NULL;//用来保存openprocess地址
ULONG originAddress[1000];
//UCHAR origincode[20];
VOID wpOff();//关闭
VOID wpOn();//写保护函数,主要是修改cr0寄存器,但是有一定风险比如核切换。。。所以测试时虚拟机分配单核
UINT64 getKeServiceDescirptorTable();//获取ssdt
UINT64 getSsdtFunctionAddress(UINT32 index);//获取函数地址
VOID initinlinehook(UINT32 index);//inline hook 跳板函数
VOID ssdtHook(UINT32 index);
UINT64 getFunctionCount();//获取ssdt中函数偏移个数
VOID ssdtUnhook(UINT32 index);
NTSTATUS NTAPI myNtOpenProcess(
_Out_ PHANDLE ProcessHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_opt_ PCLIENT_ID ClientId);


VOID Unload(IN PDRIVER_OBJECT pDriverObject)
{
ssdtUnhook(0x26);
DbgPrint("drive unload");
return STATUS_SUCCESS;
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING RegistryPath)
{
int nCount = getFunctionCount();
PSERVICES_DESCRIPTOR_TABLE p = (PSERVICES_DESCRIPTOR_TABLE)getKeServiceDescirptorTable();
PULONG ssdt = (PULONG)p->ServiceTableBase;
wpOff();
for (int i = 0; i < nCount; i++)
{
originAddress[i] = ssdt[i];
}
wpOn();
DbgPrint("start_hook");
ssdtHook(0x69);
DbgPrint("done!!!");
pDriverObject->DriverUnload = Unload;
return STATUS_SUCCESS;

}

VOID wpOff()
{

#ifdef _WIN64
__writecr0(__readcr0() & (~(0x10000)));

#else
__asm
{
push eax
mov eax, CR0
and eax, not 0x10000
mov CR0, eax
pop eax
}
#endif

}

VOID wpOn()
{

#ifdef _WIN64
__writecr0(__readcr0() | 0x10000);

#else
__asm
{
push eax
mov eax, CR0
or eax, 0x10000
mov CR0, eax
pop eax
}
#endif

}

NTSTATUS NTAPI myNtOpenProcess(
_Out_ PHANDLE ProcessHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_opt_ PCLIENT_ID ClientId)
{
PEPROCESS process = 0;
if (STATUS_SUCCESS == PsLookupProcessByProcessId(ClientId->UniqueProcess, &process))
{
if (strcmp(PsGetProcessImageFileName(process), "notepad.exe") == 0)
{
return STATUS_PNP_INVALID_ID;
}
}
return originNtOpenProcess(ProcessHandle, DesiredAccess, ObjectAttributes, ClientId);
}
UINT64 getKeServiceDescirptorTable()
{
UINT64 KeServiceDescirptorTable = 0;
PUCHAR addrStartSearch = (PUCHAR)__readmsr((ULONG)(0xC0000082)); //KiSystemCall64Shadow or KiSystemCall64
PUCHAR addrEndSearch = 0;
if (*(addrStartSearch + 0x9) == 0x00)
{
addrEndSearch = addrStartSearch + 0x500;
}
else if (*(addrStartSearch + 0x9) == 0x70)//这边可能不同版本有区别比如2004就是0x90,我虚拟机是0x70
{
PUCHAR pKiSystemCall64Shadow = addrStartSearch;
PUCHAR EndSearchAddress = pKiSystemCall64Shadow + 0x500;
PUCHAR i = NULL;
INT Temp = 0;
for (i = pKiSystemCall64Shadow; i < EndSearchAddress; i++)
{
if (MmIsAddressValid(i) && MmIsAddressValid(i + 5))
{
if (*i == 0xe9 && *(i + 5) == 0xc3)
{
memcpy(&Temp, i + 1, 4);
addrStartSearch = Temp + (i + 5);
addrEndSearch = addrStartSearch + 0x500;
}
}
}
}
ULONG tmpAddress = 0;
int j = 0;
for (PUCHAR i = addrStartSearch; i < addrEndSearch; i++, j++)
{
if (MmIsAddressValid(i) && MmIsAddressValid(i + 1) && MmIsAddressValid(i + 2))
{
if (addrStartSearch[j] == 0x4c &&
addrStartSearch[j + 1] == 0x8d &&
addrStartSearch[j + 2] == 0x15)
{
RtlCopyMemory(&tmpAddress, i + 3, 4);
KeServiceDescirptorTable = tmpAddress + (INT64)i + 7;
}
}
}
return KeServiceDescirptorTable;
}
UINT64 getSsdtFunctionAddress(UINT32 index)
{
INT64 address = 0;
PSERVICES_DESCRIPTOR_TABLE pServiceDescriptorTable = (PSERVICES_DESCRIPTOR_TABLE)getKeServiceDescirptorTable();
PULONG ssdt = (PULONG)pServiceDescriptorTable->ServiceTableBase;
ULONG dwOffset = ssdt[index];
dwOffset >>= 4;
address = (UINT64)ssdt + dwOffset;
//DbgPrint("0x%llX\n", address);
return address;
}
VOID initinlinehook(UINT32 index)
{
UCHAR jmpCode[13] = "\x48\xB8\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xE0";
UINT64 proxyFunction;
UINT64 AAA = getSsdtFunctionAddress(index);
proxyFunction = (UINT64)myNtOpenProcess;
RtlCopyMemory(jmpCode + 2, &proxyFunction, 8);
wpOff();
memset(AAA, 0x90, 15);
RtlCopyMemory(AAA, jmpCode, 12);
wpOn();
return;
}


VOID ssdtHook(UINT32 index)
{
PSERVICES_DESCRIPTOR_TABLE pKeServiceDescriptorTable = (PSERVICES_DESCRIPTOR_TABLE)getKeServiceDescirptorTable();
PULONG pSsdt = (PULONG)pKeServiceDescriptorTable->ServiceTableBase;
originNtOpenProcess = (NTOPENPROCESS)getSsdtFunctionAddress(0x26);
initinlinehook(index);
wpOff();
pSsdt[0x26] = pSsdt[index]; // SSDT HOOK
wpOn();
return;
}
UINT64 getFunctionCount()
{
PSERVICES_DESCRIPTOR_TABLE pKeServiceDescriptorTable;
pKeServiceDescriptorTable = (PSERVICES_DESCRIPTOR_TABLE)getKeServiceDescirptorTable();
return (UINT64)(pKeServiceDescriptorTable->ServiceCount);

}
VOID ssdtUnhook(UINT32 index)
{
int nCount = getFunctionCount();
PSERVICES_DESCRIPTOR_TABLE p = (PSERVICES_DESCRIPTOR_TABLE)getKeServiceDescirptorTable();
PULONG ssdt = (PULONG)p->ServiceTableBase;
wpOff();
for (int i = 0; i < nCount; i++)
{
ssdt[i] = originAddress[i];
}
wpOn();
}

因为在x64中ssdt和ssdtshadow都没有导出,所以得通过特征码搜寻找到地址

image-20200715162628766

这边取4c 8d 15就能获取系统服务表地址

这边有个坑就是__readmsr((ULONG)(0xC0000082)); //KiSystemCall64Shadow or KiSystemCall64

教材上是KiSystemCall64。然后往后0x500字节左右可以找到系统服务表地址,但是我再win10上测试发现readmsr地址是KiSystemCall64Shadow,这就需要再次匹配特侦码,具体看ntoskrnl.exe,一开始还看错了。。。我看的我物理机的ntoskrnl.exe,而虚拟机的和物理机有点差异。。。具体看代码了

获取到系统服务表地址后就需要得到需要ssdt hook的函数地址,这边hook的是ntopenprocess,系统调用号是0x26,关于调用号得看ntdll.dll,因为ntopenprocess后来被hook后直接调用操作系统会根据系统服务表去call ntopenprocess,然后系统服务表处已经被hook所以虚拟机会卡死,出现死循环。。。

保存好地址后初始化shellcode,因为x64得改变系统服务表中存放的不是地址而是偏移,这也导致了无法直接跳转到我们写的函数,必须寻找一个跳板函数来进行inline hook,像教材中采用的是蓝屏函数KeBugCheckEx,但是我的win10中这个函数已经不在ssdt表中,就换了个ZwAddBootEntry,问了别的大佬听说也能用NtCreatePort

inline hook完事后只需要把inline hook函数对应调用号处得偏移给我们ssdt hook处即可

1
2
3
wpOff();
pSsdt[0x26] = pSsdt[index]; // SSDT HOOK
wpOn();

并没有绕pg,但是钩子挂个一段时间不会蓝屏,运气好几小时,运气差点半小时就被检测了,所以unhook流程也没有把inline hook处给还原,就是单纯恢复系统服务表,可以看到在DriverEntry处首先做的就是备份,用来在unhook时还原。

主要hook功能是检测是否打开记事本,如果是则阻止,这边可以稍作修改比如设置一波自启动啊然后检测是否打开什么类似炉石传说,然后趁队友离开电脑时偷偷给他挂上什么的。。。


SSDT HOOK
http://www.psbazx.com/2020/08/11/SSDT-HOOK/
Beitragsautor
皮三宝
Veröffentlicht am
August 10, 2020
Urheberrechtshinweis