WindowsAPI编程核心技术-进线程监控

进程创建监控

因为x64下没法HOOK,微软搞了一套接口。

PsSetCreateProcessNotifyRoutineEx

设置进程回调监控创建与退出,控制是否允许进程创建。

1
2
3
4
NTSTATUS PsSetCreateProcessNotifyRoutineEx(
_In_ PCREATE_PROCESS_NOTIFY_ROUTINE_EX NotifyRoutine, //调用例程
_In_ BOOLEAN Remove //TRUE从回调例程列表中删除指定例程并等待所有正运行的回调例程运行完成 FALSE反之添加
)

PS_CREATE_NOTIFY_INFO

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
typedef struct _PS_CREATE_NOTIFY_INFO {
_In_ SIZE_T Size; //该结构大小 单位字节
union {
_In_ ULONG Flags; //废弃
struct {
_In_ ULONG FileOpenNameAvailable : 1; //ImageFileName域是否包含打开进程可执行文件的确切文件名
_In_ ULONG IsSubsystemProcess : 1; //进程子系统类型是WIN32以外子系统
_In_ ULONG Reserved : 30; //保留 供系统使用
};
};
_In_ HANDLE ParentProcessId; //新进程的父进程ID
_In_ CLIENT_ID CreatingThreadId; //创建的新进程、线程ID
_Inout_ struct _FILE_OBJECT *FileObject; //进程的可执行文件对象
_In_ PCUNICODE_STRING ImageFileName; //可执行文件名 当FileOpenNameAvailable域为TRUE则指定打开可执行文件确切文件名 反之FALSE则系统可能只提供部分名称 当IsSubsystemProcess为TRUE则此处为NULL
_In_opt_ PCUNICODE_STRING CommandLine; //执行该过程的命令 NULL为命令不可用 当IsSubsystemProcess为TRUE这个可能为NULL
_Inout_ NTSTATUS CreationStatus; //创建操作返回值
} PS_CREATE_NOTIFY_INFO, *PPS_CREATE_NOTIFY_INFO;

破解内核函数强制完整性签名限制

例如PsSetCreateProcessNotifyRoutineExObRegisterCallbacks等函数要求程序强制完整性校验,否则调用失败。

首先要在编译时启用驱动签名强制签名属性。在/integritycheck链接器标志设置IMAGE_OPTIONAL_HEADER中DllCharacteristics字段为IMAGE_DLLCHARACTERISTICS_INTEGRITY属性。

内核通过MmVerifyCallbackFunction验证限制函数调用是否合法,但它只是验证了DriverObejct->DriverSection->Flags是否包含0x20,所以破解方法就异或0x20就行了。其中LDR_DATA_TABLE_ENTRY在x86和x64下有差异:

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
// 编程方式绕过签名检查
BOOLEAN BypassCheckSign(PDRIVER_OBJECT pDriverObject) {
#ifdef _WIN64
typedef struct _KLDR_DATA_TABLE_ENTRY {
LIST_ENTRY listEntry;
ULONG64 __Undefined1;
ULONG64 __Undefined2;
ULONG64 __Undefined3;
ULONG64 NonPagedDebugInfo;
ULONG64 DllBase;
ULONG64 EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING path;
UNICODE_STRING name;
ULONG Flags;
USHORT LoadCount;
USHORT __Undefined5;
ULONG64 __Undefined6;
ULONG CheckSum;
ULONG __padding1;
ULONG TimeDateStamp;
ULONG __padding2;
} KLDR_DATA_TABLE_ENTRY, * PKLDR_DATA_TABLE_ENTRY;
#else
typedef struct _KLDR_DATA_TABLE_ENTRY {
LIST_ENTRY listEntry;
ULONG unknown1;
ULONG unknown2;
ULONG unknown3;
ULONG unknown4;
ULONG unknown5;
ULONG unknown6;
ULONG unknown7;
UNICODE_STRING path;
UNICODE_STRING name;
ULONG Flags;
} KLDR_DATA_TABLE_ENTRY, * PKLDR_DATA_TABLE_ENTRY;
#endif
PKLDR_DATA_TABLE_ENTRY pLdrData = (PKLDR_DATA_TABLE_ENTRY)pDriverObject->DriverSection;
pLdrData->Flags = pLdrData->Flags | 0x20;
return TRUE;
};

例子

设置进程监控回调函数:

1
2
3
4
5
6
7
// 设置回调函数
NTSTATUS SetProcessNotifyRoutine(VOID) {
NTSTATUS status = PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)ProcessNotifyExRoutine, FALSE);
if (!NT_SUCCESS(status))
ShowError("PsSetCreateProcessNotifyRoutineEx", status);
return status;
};

回调函数原型:

1
2
3
4
5
VOID ProcessNotifyExRoutine (
_In_ HANDLE ParentId, //父进程ID
_In_ HANDLE ProcessId, //进程ID
_Inout_opt_ PPS_CREATE_NOTIFY_INFO CreateInfo //NULL进程结束 非NULL进程创建
)

进程创建回调函数的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 回调函数
VOID ProcessNotifyExRoutine(PEPROCESS pEProcess, HANDLE hProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo) {
// CreateInfo 为 NULL 时,表示进程退出;不为 NULL 时,表示进程创建
if (NULL == CreateInfo)
return;
// 获取进程名称
PCHAR pszImageFileName = PsGetProcessImageFileName(pEProcess);
// 显示创建进程信息
DbgPrint("[%s][%d][%wZ]\n", pszImageFileName, hProcessId, CreateInfo->ImageFileName);
// 禁止指定进程(520.exe)创建
if (0 == _stricmp(pszImageFileName, "520.exe")) {
// 禁止创建
CreateInfo->CreationStatus = STATUS_UNSUCCESSFUL;
DbgPrint("[禁止创建]\n");
};
return;
};

源代码

Driver.h:

1
2
3
4
5
6
7
8
9
10
11
12
#ifndef _DRIVER_H_
#define _DRIVER_H_


#include <ntddk.h>


VOID DriverUnload(PDRIVER_OBJECT pDriverObject);
NTSTATUS DriverDefaultHandle(PDEVICE_OBJECT pDevObj, PIRP pIrp);


#endif

NotifyRoutine.h:

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
#ifndef _NOTIFY_ROUTINE_H_
#define _NOTIFY_ROUTINE_H_


#include <ntddk.h>


// 未导出函数声明
PCHAR PsGetProcessImageFileName(PEPROCESS pEProcess);


// 编程方式绕过签名检查
BOOLEAN BypassCheckSign(PDRIVER_OBJECT pDriverObject);

// 设置回调函数
NTSTATUS SetProcessNotifyRoutine();

// 删除回调函数
NTSTATUS RemoveProcessNotifyRoutine();

// 回调函数
VOID ProcessNotifyExRoutine(PEPROCESS pEProcess, HANDLE hProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo);


#endif

Driver.c:

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
#include "Driver.h"
#include "NotifyRoutine.h"


NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegPath)
{
DbgPrint("Enter DriverEntry\n");

NTSTATUS status = STATUS_SUCCESS;
pDriverObject->DriverUnload = DriverUnload;
for (ULONG i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
{
pDriverObject->MajorFunction[i] = DriverDefaultHandle;
}

// 编程方式绕过签名检查
BypassCheckSign(pDriverObject);

// 设置回调函数
SetProcessNotifyRoutine();

DbgPrint("Leave DriverEntry\n");
return status;
}



VOID DriverUnload(PDRIVER_OBJECT pDriverObject)
{
// 删除回调函数
RemoveProcessNotifyRoutine();
}


NTSTATUS DriverDefaultHandle(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
NTSTATUS status = STATUS_SUCCESS;
pIrp->IoStatus.Status = status;
pIrp->IoStatus.Information = 0;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);

return status;
}

NotifyRoutine.c:

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
#include "NotifyRoutine.h"


VOID ShowError(PCHAR pszText, NTSTATUS ntStatus)
{
DbgPrint("%s Error[0x%X]\n", pszText, ntStatus);
}


// 编程方式绕过签名检查
BOOLEAN BypassCheckSign(PDRIVER_OBJECT pDriverObject)
{
#ifdef _WIN64
typedef struct _KLDR_DATA_TABLE_ENTRY
{
LIST_ENTRY listEntry;
ULONG64 __Undefined1;
ULONG64 __Undefined2;
ULONG64 __Undefined3;
ULONG64 NonPagedDebugInfo;
ULONG64 DllBase;
ULONG64 EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING path;
UNICODE_STRING name;
ULONG Flags;
USHORT LoadCount;
USHORT __Undefined5;
ULONG64 __Undefined6;
ULONG CheckSum;
ULONG __padding1;
ULONG TimeDateStamp;
ULONG __padding2;
} KLDR_DATA_TABLE_ENTRY, *PKLDR_DATA_TABLE_ENTRY;
#else
typedef struct _KLDR_DATA_TABLE_ENTRY
{
LIST_ENTRY listEntry;
ULONG unknown1;
ULONG unknown2;
ULONG unknown3;
ULONG unknown4;
ULONG unknown5;
ULONG unknown6;
ULONG unknown7;
UNICODE_STRING path;
UNICODE_STRING name;
ULONG Flags;
} KLDR_DATA_TABLE_ENTRY, *PKLDR_DATA_TABLE_ENTRY;
#endif

PKLDR_DATA_TABLE_ENTRY pLdrData = (PKLDR_DATA_TABLE_ENTRY)pDriverObject->DriverSection;
pLdrData->Flags = pLdrData->Flags | 0x20;

return TRUE;
}


// 设置回调函数
NTSTATUS SetProcessNotifyRoutine()
{
NTSTATUS status = PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)ProcessNotifyExRoutine, FALSE);
if (!NT_SUCCESS(status))
{
ShowError("PsSetCreateProcessNotifyRoutineEx", status);
}
return status;
}


// 删除回调函数
NTSTATUS RemoveProcessNotifyRoutine()
{
NTSTATUS status = PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)ProcessNotifyExRoutine, TRUE);
if (!NT_SUCCESS(status))
{
ShowError("PsSetCreateProcessNotifyRoutineEx", status);
}
return status;
}


// 回调函数
VOID ProcessNotifyExRoutine(PEPROCESS pEProcess, HANDLE hProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo)
{
// CreateInfo 为 NULL 时,表示进程退出;不为 NULL 时,表示进程创建
if (NULL == CreateInfo)
{
return;
}
// 获取进程名称
PCHAR pszImageFileName = PsGetProcessImageFileName(pEProcess);
// 显示创建进程信息
DbgPrint("[%s][%d][%wZ]\n", pszImageFileName, hProcessId, CreateInfo->ImageFileName);
// 禁止指定进程(520.exe)创建
if (0 == _stricmp(pszImageFileName, "520.exe"))
{
// 禁止创建
CreateInfo->CreationStatus = STATUS_UNSUCCESSFUL;

DbgPrint("[禁止创建]\n");
}
}

反进程创建监控

基本原理

系统设置的进程创建回调函数会存储在PspCreateProcessNotifyRoutine的PVOID类型数组里,这里面存储着系统里所有用PsSetCreateProcessNotifyRoutine函数设置的进程创建回调函数的加密地址。这个数组没有导出不能直接获取,需要逆向PspSetCreateProcessNotifyRoutine函数:

1
2
3
nt!PspSetCreateProcessNotifyRoutine+0x4C:
33 FF xor edi,edi
4C 8D 3D FB 0B DF FF lea r15, [nt!PspCreateProcessNotifyRoutine]

定位PspCreateProcessNotifyRoutine数组地址的方法是特征码,每个系统都不一样:

Windows 7 Windows 8.1 Windows 10
x86 C7450C B8 BB
x64 4C8D35 4C8D3D 4C8D3D

PspSetCreateProcessNotifyRoutine也不是导出函数,需要逆向PsSetCreateProcessNotifyRoutine函数:

1
2
3
nt!PsSetCreateProcessNotifyRoutine:
45 33 C0 xor r8d, r8d
E9 E8 FD FF FF jmp nt!PspSetCreateprocessNotifyRoutine

定位PspSetCreateProcessNotifyRoutine函数地址的方法也是特征码,每个系统也不一样:

Windows 7 Windows 8.1 Windows 10
x86 E8 E8 E8
x64 E9 E9 E9

于是获取PspCreateProcessNotifyRoutine数组实现代码:

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
// 根据特征码获取 PspCreateProcessNotifyRoutine 数组地址
PVOID SearchPspCreateProcessNotifyRoutine(PUCHAR pFirstSpecialData, ULONG ulFirstSpecialDataSize, PUCHAR pSecondSpecialData, ULONG ulSecondSpecialDataSize) {
UNICODE_STRING ustrFuncName;
PVOID pAddress = NULL;
LONG lOffset = 0;
PVOID pPsSetCteateProcessNotifyRoutine = NULL;
PVOID pPspSetCreateProcessNotifyRoutineAddress = NULL;
PVOID pPspCreateProcessNotifyRoutineAddress = NULL;
// 先获取 PsSetCreateProcessNotifyRoutine 函数地址
RtlInitUnicodeString(&ustrFuncName, L"PsSetCreateProcessNotifyRoutine");
pPsSetCteateProcessNotifyRoutine = MmGetSystemRoutineAddress(&ustrFuncName);
if (NULL == pPsSetCteateProcessNotifyRoutine) {
ShowError("MmGetSystemRoutineAddress", 0);
return pPspCreateProcessNotifyRoutineAddress;
};
// 然后, 查找 PspSetCreateProcessNotifyRoutine 函数地址
pAddress = SearchMemory(pPsSetCteateProcessNotifyRoutine, (PVOID)((PUCHAR)pPsSetCteateProcessNotifyRoutine + 0xFF), pFirstSpecialData, ulFirstSpecialDataSize);
if (NULL == pAddress) {
ShowError("SearchMemory1", 0);
return pPspCreateProcessNotifyRoutineAddress;
};
// 获取偏移数据, 并计算地址
lOffset = *(PLONG)pAddress;
pPspSetCreateProcessNotifyRoutineAddress = (PVOID)((PUCHAR)pAddress + sizeof(LONG) + lOffset);
// 最后, 查找 PspCreateProcessNotifyRoutine 地址
pAddress = SearchMemory(pPspSetCreateProcessNotifyRoutineAddress, (PVOID)((PUCHAR)pPspSetCreateProcessNotifyRoutineAddress + 0xFF), pSecondSpecialData, ulSecondSpecialDataSize);
if (NULL == pAddress) {
ShowError("SearchMemory2", 0);
return pPspCreateProcessNotifyRoutineAddress;
};
// 获取地址
#ifdef _WIN64
// 64 位先获取偏移, 再计算地址
lOffset = *(PLONG)pAddress;
pPspCreateProcessNotifyRoutineAddress = (PVOID)((PUCHAR)pAddress + sizeof(LONG) + lOffset);
#else
// 32 位直接获取地址
pPspCreateProcessNotifyRoutineAddress = *(PVOID*)pAddress;
#endif
return pPspCreateProcessNotifyRoutineAddress;
};

数组PspCreateProcessNotifyRoutine是加密的,还需要解密。x86和x64加密方法不同。x86下来说,该数组是个4字节无符号类型数组,数组大小最大为8,每个值与运算0xFFFFFFF8再加4即为解密;x64下来说,该数组是个8字节无符号类型数组,数组大小最大为64,每个值与运算0xFFFFFFFFFFFFFFF8即为解密:

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
// 遍历回调
BOOLEAN EnumNotifyRoutine(VOID) {
ULONG i = 0;
PVOID pPspCreateProcessNotifyRoutineAddress = NULL;
PVOID pNotifyRoutineAddress = NULL;
// 获取 PspCreateProcessNotifyRoutine 数组地址
pPspCreateProcessNotifyRoutineAddress = GetPspCreateProcessNotifyRoutine();
if (NULL == pPspCreateProcessNotifyRoutineAddress) {
DbgPrint("GetPspCreateProcessNotifyRoutine Error!\n");
return FALSE;
};
DbgPrint("pPspCreateProcessNotifyRoutineAddress=0x%p\n", pPspCreateProcessNotifyRoutineAddress);
// 获取回调地址并解密
#ifdef _WIN64
for (i = 0; i < 64; i++) {
pNotifyRoutineAddress = *(PVOID*)((PUCHAR)pPspCreateProcessNotifyRoutineAddress + sizeof(PVOID) * i);
pNotifyRoutineAddress = (PVOID)((ULONG64)pNotifyRoutineAddress & 0xfffffffffffffff8);
if (MmIsAddressValid(pNotifyRoutineAddress)) {
pNotifyRoutineAddress = *(PVOID*)pNotifyRoutineAddress;
DbgPrint("[%d]ullNotifyRoutine=0x%p\n", i, pNotifyRoutineAddress);
};
};
#else
for (i = 0; i < 8; i++) {
pNotifyRoutineAddress = *(PVOID*)((PUCHAR)pPspCreateProcessNotifyRoutineAddress + sizeof(PVOID) * i);
pNotifyRoutineAddress = (PVOID)((ULONG)pNotifyRoutineAddress & 0xfffffff8);
if (MmIsAddressValid(pNotifyRoutineAddress)) {
pNotifyRoutineAddress = *(PVOID*)((PUCHAR)pNotifyRoutineAddress + 4);
DbgPrint("[%d]ullNotifyRoutine=0x%p\n", i, pNotifyRoutineAddress);
};
};
#endif
return TRUE;
};

为了删除进程创建回调函数,有3种方法:

  • 函数PsSetCreateProcessNotifyRoutine也能删除回调函数,传入回调函数地址和删除标志TRUE即可。
  • 修改PspCreateProcessNotifyRoutine数组中数据,指向自定义空回调函数地址。
  • 修改回调函数内存数据前几字节,直接写入RET。

这里就用第一种最方便了:

1
2
3
4
5
6
7
// 移除回调
NTSTATUS RemoveNotifyRoutine(PVOID pNotifyRoutineAddress) {
NTSTATUS status = PsSetCreateProcessNotifyRoutine((PCREATE_PROCESS_NOTIFY_ROUTINE)pNotifyRoutineAddress, TRUE);
if (!NT_SUCCESS(status))
ShowError("PsSetCreateProcessNotifyRoutine", status);
return status;
};

源代码

Driver.h:

1
2
3
4
5
6
7
8
9
10
11
12
#ifndef _DRIVER_H_
#define _DRIVER_H_


#include <ntddk.h>


VOID DriverUnload(PDRIVER_OBJECT pDriverObject);
NTSTATUS DriverDefaultHandle(PDEVICE_OBJECT pDevObj, PIRP pIrp);


#endif

EnumRemove.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#ifndef _ENUM_REMOVE_H_
#define _ENUM_REMOVE_H_


#include <ntddk.h>


// 遍历回调
BOOLEAN EnumNotifyRoutine();

// 移除回调
NTSTATUS RemoveNotifyRoutine(PVOID pNotifyRoutineAddress);

// 获取 PspCreateProcessNotifyRoutine 数组地址
PVOID GetPspCreateProcessNotifyRoutine();

// 根据特征码获取 PspCreateProcessNotifyRoutine 数组地址
PVOID SearchPspCreateProcessNotifyRoutine(PUCHAR pFirstSpecialData, ULONG ulFirstSpecialDataSize, PUCHAR pSecondSpecialData, ULONG ulSecondSpecialDataSize);

// 指定内存区域的特征码扫描
PVOID SearchMemory(PVOID pStartAddress, PVOID pEndAddress, PUCHAR pMemoryData, ULONG ulMemoryDataSize);


#endif

Driver.c:

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
#include "EnumRemove.h"
#include "Driver.h"


NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegPath)
{
DbgPrint("Enter DriverEntry\n");

NTSTATUS status = STATUS_SUCCESS;
pDriverObject->DriverUnload = DriverUnload;
for (ULONG i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
{
pDriverObject->MajorFunction[i] = DriverDefaultHandle;
}

// 遍历回调
EnumNotifyRoutine();

DbgPrint("Leave DriverEntry\n");
return status;
}



VOID DriverUnload(PDRIVER_OBJECT pDriverObject)
{
}


NTSTATUS DriverDefaultHandle(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
NTSTATUS status = STATUS_SUCCESS;
pIrp->IoStatus.Status = status;
pIrp->IoStatus.Information = 0;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);

return status;
}

EnumRemove.c:

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
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
#include "EnumRemove.h"


VOID ShowError(PCHAR lpszText, NTSTATUS ntStatus)
{
DbgPrint("%s Error[0x%X]\n", lpszText, ntStatus);
}


// 遍历回调
BOOLEAN EnumNotifyRoutine()
{
ULONG i = 0;
PVOID pPspCreateProcessNotifyRoutineAddress = NULL;
PVOID pNotifyRoutineAddress = NULL;

// 获取 PspCreateProcessNotifyRoutine 数组地址
pPspCreateProcessNotifyRoutineAddress = GetPspCreateProcessNotifyRoutine();
if (NULL == pPspCreateProcessNotifyRoutineAddress)
{
DbgPrint("GetPspCreateProcessNotifyRoutine Error!\n");
return FALSE;
}
DbgPrint("pPspCreateProcessNotifyRoutineAddress=0x%p\n", pPspCreateProcessNotifyRoutineAddress);

// 获取回调地址并解密
#ifdef _WIN64
for (i = 0; i < 64; i++)
{
pNotifyRoutineAddress = *(PVOID *)((PUCHAR)pPspCreateProcessNotifyRoutineAddress + sizeof(PVOID) * i);
pNotifyRoutineAddress = (PVOID)((ULONG64)pNotifyRoutineAddress & 0xfffffffffffffff8);
if (MmIsAddressValid(pNotifyRoutineAddress))
{
pNotifyRoutineAddress = *(PVOID *)pNotifyRoutineAddress;
DbgPrint("[%d]ullNotifyRoutine=0x%p\n", i, pNotifyRoutineAddress);
}
}
#else
for (i = 0; i < 8; i++)
{
pNotifyRoutineAddress = *(PVOID *)((PUCHAR)pPspCreateProcessNotifyRoutineAddress + sizeof(PVOID) * i);
pNotifyRoutineAddress = (PVOID)((ULONG)pNotifyRoutineAddress & 0xfffffff8);
if (MmIsAddressValid(pNotifyRoutineAddress))
{
pNotifyRoutineAddress = *(PVOID *)((PUCHAR)pNotifyRoutineAddress + 4);
DbgPrint("[%d]ullNotifyRoutine=0x%p\n", i, pNotifyRoutineAddress);
}
}
#endif

return TRUE;
}


// 移除回调
NTSTATUS RemoveNotifyRoutine(PVOID pNotifyRoutineAddress)
{
NTSTATUS status = PsSetCreateProcessNotifyRoutine((PCREATE_PROCESS_NOTIFY_ROUTINE)pNotifyRoutineAddress, TRUE);
if (!NT_SUCCESS(status))
{
ShowError("PsSetCreateProcessNotifyRoutine", status);
}
return status;
}


// 获取 PspCreateProcessNotifyRoutine 数组地址
PVOID GetPspCreateProcessNotifyRoutine()
{
PVOID pPspCreateProcessNotifyRoutineAddress = NULL;
RTL_OSVERSIONINFOW osInfo = { 0 };
UCHAR pFirstSpecialData[50] = { 0 };
ULONG ulFirstSpecialDataSize = 0;
UCHAR pSecondSpecialData[50] = { 0 };
ULONG ulSecondSpecialDataSize = 0;

// 获取系统版本信息, 判断系统版本
RtlGetVersion(&osInfo);
if (6 == osInfo.dwMajorVersion)
{
if (1 == osInfo.dwMinorVersion)
{
// Win7
#ifdef _WIN64
// 64 位
// E9
pFirstSpecialData[0] = 0xE9;
ulFirstSpecialDataSize = 1;
// 4C8D35
pSecondSpecialData[0] = 0x4C;
pSecondSpecialData[1] = 0x8D;
pSecondSpecialData[2] = 0x35;
ulSecondSpecialDataSize = 3;
#else
// 32 位
// E8
pFirstSpecialData[0] = 0xE8;
ulFirstSpecialDataSize = 1;
// C7450C
pSecondSpecialData[0] = 0xC7;
pSecondSpecialData[1] = 0x45;
pSecondSpecialData[2] = 0x0C;
ulSecondSpecialDataSize = 3;
#endif
}
else if (2 == osInfo.dwMinorVersion)
{
// Win8
#ifdef _WIN64
// 64 位

#else
// 32 位

#endif
}
else if (3 == osInfo.dwMinorVersion)
{
// Win8.1
#ifdef _WIN64
// 64 位
// E9
pFirstSpecialData[0] = 0xE9;
ulFirstSpecialDataSize = 1;
// 4C8D3D
pSecondSpecialData[0] = 0x4C;
pSecondSpecialData[1] = 0x8D;
pSecondSpecialData[2] = 0x3D;
ulSecondSpecialDataSize = 3;
#else
// 32 位
// E8
pFirstSpecialData[0] = 0xE8;
ulFirstSpecialDataSize = 1;
// B8
pSecondSpecialData[0] = 0xB8;
ulSecondSpecialDataSize = 1;
#endif
}
}
else if (10 == osInfo.dwMajorVersion)
{
// Win10
#ifdef _WIN64
// 64 位
// E9
pFirstSpecialData[0] = 0xE9;
ulFirstSpecialDataSize = 1;
// 4C8D3D
pSecondSpecialData[0] = 0x4C;
pSecondSpecialData[1] = 0x8D;
pSecondSpecialData[2] = 0x3D;
ulSecondSpecialDataSize = 3;
#else
// 32 位
// E8
pFirstSpecialData[0] = 0xE8;
ulFirstSpecialDataSize = 1;
// BB
pSecondSpecialData[0] = 0xBB;
ulSecondSpecialDataSize = 1;
#endif
}

// 根据特征码获取地址
pPspCreateProcessNotifyRoutineAddress = SearchPspCreateProcessNotifyRoutine(pFirstSpecialData, ulFirstSpecialDataSize, pSecondSpecialData, ulSecondSpecialDataSize);
return pPspCreateProcessNotifyRoutineAddress;
}


// 根据特征码获取 PspCreateProcessNotifyRoutine 数组地址
PVOID SearchPspCreateProcessNotifyRoutine(PUCHAR pFirstSpecialData, ULONG ulFirstSpecialDataSize, PUCHAR pSecondSpecialData, ULONG ulSecondSpecialDataSize)
{
UNICODE_STRING ustrFuncName;
PVOID pAddress = NULL;
LONG lOffset = 0;
PVOID pPsSetCteateProcessNotifyRoutine = NULL;
PVOID pPspSetCreateProcessNotifyRoutineAddress = NULL;
PVOID pPspCreateProcessNotifyRoutineAddress = NULL;

// 先获取 PsSetCreateProcessNotifyRoutine 函数地址
RtlInitUnicodeString(&ustrFuncName, L"PsSetCreateProcessNotifyRoutine");
pPsSetCteateProcessNotifyRoutine = MmGetSystemRoutineAddress(&ustrFuncName);
if (NULL == pPsSetCteateProcessNotifyRoutine)
{
ShowError("MmGetSystemRoutineAddress", 0);
return pPspCreateProcessNotifyRoutineAddress;
}

// 然后, 查找 PspSetCreateProcessNotifyRoutine 函数地址
pAddress = SearchMemory(pPsSetCteateProcessNotifyRoutine,
(PVOID)((PUCHAR)pPsSetCteateProcessNotifyRoutine + 0xFF),
pFirstSpecialData, ulFirstSpecialDataSize);
if (NULL == pAddress)
{
ShowError("SearchMemory1", 0);
return pPspCreateProcessNotifyRoutineAddress;
}
// 获取偏移数据, 并计算地址
lOffset = *(PLONG)pAddress;
pPspSetCreateProcessNotifyRoutineAddress = (PVOID)((PUCHAR)pAddress + sizeof(LONG) + lOffset);

// 最后, 查找 PspCreateProcessNotifyRoutine 地址
pAddress = SearchMemory(pPspSetCreateProcessNotifyRoutineAddress,
(PVOID)((PUCHAR)pPspSetCreateProcessNotifyRoutineAddress + 0xFF),
pSecondSpecialData, ulSecondSpecialDataSize);
if (NULL == pAddress)
{
ShowError("SearchMemory2", 0);
return pPspCreateProcessNotifyRoutineAddress;
}
// 获取地址
#ifdef _WIN64
// 64 位先获取偏移, 再计算地址
lOffset = *(PLONG)pAddress;
pPspCreateProcessNotifyRoutineAddress = (PVOID)((PUCHAR)pAddress + sizeof(LONG) + lOffset);
#else
// 32 位直接获取地址
pPspCreateProcessNotifyRoutineAddress = *(PVOID *)pAddress;
#endif

return pPspCreateProcessNotifyRoutineAddress;
}


// 指定内存区域的特征码扫描
PVOID SearchMemory(PVOID pStartAddress, PVOID pEndAddress, PUCHAR pMemoryData, ULONG ulMemoryDataSize)
{
PVOID pAddress = NULL;
PUCHAR i = NULL;
ULONG m = 0;

// 扫描内存
for (i = (PUCHAR)pStartAddress; i < (PUCHAR)pEndAddress; i++)
{
// 判断特征码
for (m = 0; m < ulMemoryDataSize; m++)
{
if (*(PUCHAR)(i + m) != pMemoryData[m])
{
break;
}
}
// 判断是否找到符合特征码的地址
if (m >= ulMemoryDataSize)
{
// 找到特征码位置, 获取紧接着特征码的下一地址
pAddress = (PVOID)(i + ulMemoryDataSize);
break;
}
}

return pAddress;
}

反线程创建监控

基本原理

系统线程创建回调函数设置用PsSetCreateThreadNotifyRoutine函数,这个上面没讲但跟进程操作差不多。

线程创建回调函数地址数组叫PspCreateThreadNotifyRoutine,该数组地址通过PsSetCreateThreadNotifyRoutine获取:

1
2
3
4
5
6
7
8
9
10
11
;Windows 8.1 x86下:
nt!PsSetCreateThreadNotifyRoutine+0x16:
53 push ebx
57 push edi
BB C8 64 01 81 mov ebx, offset nt!PspCreateThreadNofityRoutine
33 FF xor edir, edi

;Windows 8.1 x64下:
nt!PsSetCreateThreadNotifyRoutine+0x1F:
48 8D 0D E6 FE DB FF lea rcx, [nt!PspCreateThreadNotifyRoutine]
45 33 C0 xor r8d, r8d

在Windows 7、Windows 8、Windows 8.1等中,函数PsSetCreateThreadNotifyRoutine是导出的,x86下可直接定位该数组地址,x64下可直接获取该数组偏移并计算出数组地址。

但Windows 10 x64下函数PsSetCreateThreadNotifyRoutine先调用了函数PspSetCreateThreadNotifyRoutine

1
2
3
4
5
6
7
nt!PsSetCreateThreadNotifyRoutine:
33 D2 xor edx, edx
E9 05 01 00 00 jmp nt!PspSetCreateThreadNotifyRoutine

nt!PspSetCreateThreadNotifyRoutine+0x2D:
48 8D 0D 74 0B DF FF lea rcx, [nt!PspCreateThreadNotifyRoutine]
45 33 C0 xor r8d, r8d

但是PspSetCreateThreadNotifyRoutine函数没导出,所以这个函数得特征码扫描,然后数组地址获取方法同Windows 10前操作系统方法。

数组特征码:

Windows 7 Windows 8.1 Windows 10
x86 BE BB E8/BF
x64 488D0D 488D0D E9/488D0D

根据特征码获取数组具体实现代码:

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
// 根据特征码获取 PspCreateThreadNotifyRoutine 数组地址
PVOID SearchPspCreateThreadNotifyRoutine(PUCHAR pFirstSpecialData, ULONG ulFirstSpecialDataSize, PUCHAR pSecondSpecialData, ULONG ulSecondSpecialDataSize) {
UNICODE_STRING ustrFuncName;
PVOID pAddress = NULL;
LONG lOffset = 0;
PVOID pPsSetCreateThreadNotifyRoutine = NULL;
PVOID pPspSetCreateThreadNotifyRoutineAddress = NULL;
PVOID pPspCreateThreadNotifyRoutineAddress = NULL;
// 先获取 PsSetCreateThreadNotifyRoutine 函数地址
RtlInitUnicodeString(&ustrFuncName, L"PsSetCreateThreadNotifyRoutine");
pPsSetCreateThreadNotifyRoutine = MmGetSystemRoutineAddress(&ustrFuncName);
if (NULL == pPsSetCreateThreadNotifyRoutine) {
ShowError("MmGetSystemRoutineAddress", 0);
return pPspCreateThreadNotifyRoutineAddress;
};
// 然后, 对于非 Win10 系统, 则根据第一个特征码获取 PspCreateThreadNotifyRoutine 地址; 对于 Win10 系统, 则需先根据第一个特征码获取获取 PspSetCreateThreadNotifyRoutine 地址, 再根据第二个特征码获取 PspCreateThreadNotifyRoutine 地址.
pAddress = SearchMemory(pPsSetCreateThreadNotifyRoutine, (PVOID)((PUCHAR)pPsSetCreateThreadNotifyRoutine + 0xFF), pFirstSpecialData, ulFirstSpecialDataSize);
if (NULL == pAddress) {
ShowError("SearchMemory1", 0);
return pPspCreateThreadNotifyRoutineAddress;
};
// 无第二个特征码, 则非 Win10 系统
if (0 == ulSecondSpecialDataSize) {
// 获取 PspCreateThreadNotifyRoutine 地址
#ifdef _WIN64
// 64 位 获取偏移数据, 并计算地址
lOffset = *(PLONG)pAddress;
pPspCreateThreadNotifyRoutineAddress = (PVOID)((PUCHAR)pAddress + sizeof(LONG) + lOffset);
#else
// 32 位
pPspCreateThreadNotifyRoutineAddress = *(PVOID*)pAddress;
#endif
// 直接返回
return pPspCreateThreadNotifyRoutineAddress;
};
// 存在第二个特征码, 即 Win10 系统 获取偏移数据, 并计算地址
lOffset = *(PLONG)pAddress;
pPspSetCreateThreadNotifyRoutineAddress = (PVOID)((PUCHAR)pAddress + sizeof(LONG) + lOffset);
// 最后, 查找 PspCreateThreadNotifyRoutine 地址
pAddress = SearchMemory(pPspSetCreateThreadNotifyRoutineAddress, (PVOID)((PUCHAR)pPspSetCreateThreadNotifyRoutineAddress + 0xFF), pSecondSpecialData, ulSecondSpecialDataSize);
if (NULL == pAddress) {
ShowError("SearchMemory2", 0);
return pPspCreateThreadNotifyRoutineAddress;
};
// 获取 PspCreateThreadNotifyRoutine 地址
#ifdef _WIN64
// 64 位先获取偏移, 再计算地址
lOffset = *(PLONG)pAddress;
pPspCreateThreadNotifyRoutineAddress = (PVOID)((PUCHAR)pAddress + sizeof(LONG) + lOffset);
#else
// 32 位直接获取地址
pPspCreateThreadNotifyRoutineAddress = *(PVOID*)pAddress;
#endif
return pPspCreateThreadNotifyRoutineAddress;
};

别忘了数组还需要解密,解密方法同进程:

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
// 遍历回调
BOOLEAN EnumNotifyRoutine(VOID) {
ULONG i = 0;
PVOID pPspCreateThreadNotifyRoutineAddress = NULL;
PVOID pNotifyRoutineAddress = NULL;
// 获取 PspCreateThreadNotifyRoutine 数组地址
pPspCreateThreadNotifyRoutineAddress = GetPspCreateThreadNotifyRoutine();
if (NULL == pPspCreateThreadNotifyRoutineAddress) {
DbgPrint("GetPspCreateThreadNotifyRoutine Error!\n");
return FALSE;
};
DbgPrint("pPspCreateThreadNotifyRoutineAddress=0x%p\n", pPspCreateThreadNotifyRoutineAddress);
// 获取回调地址并解密
#ifdef _WIN64
for (i = 0; i < 64; i++) {
pNotifyRoutineAddress = *(PVOID*)((PUCHAR)pPspCreateThreadNotifyRoutineAddress + sizeof(PVOID) * i);
pNotifyRoutineAddress = (PVOID)((ULONG64)pNotifyRoutineAddress & 0xfffffffffffffff8);
if (MmIsAddressValid(pNotifyRoutineAddress)) {
pNotifyRoutineAddress = *(PVOID*)pNotifyRoutineAddress;
DbgPrint("[%d]ullNotifyRoutine=0x%p\n", i, pNotifyRoutineAddress);
};
};
#else
for (i = 0; i < 8; i++) {
pNotifyRoutineAddress = *(PVOID*)((PUCHAR)pPspCreateThreadNotifyRoutineAddress + sizeof(PVOID) * i);
pNotifyRoutineAddress = (PVOID)((ULONG)pNotifyRoutineAddress & 0xfffffff8);
if (MmIsAddressValid(pNotifyRoutineAddress)) {
pNotifyRoutineAddress = *(PVOID*)((PUCHAR)pNotifyRoutineAddress + 4);
DbgPrint("[%d]ullNotifyRoutine=0x%p\n", i, pNotifyRoutineAddress);
};
};
#endif
return TRUE;
};

删除线程创建回调函数的方法依然同进程:

1
2
3
4
5
6
7
// 移除回调
NTSTATUS RemoveNotifyRoutine(PVOID pNotifyRoutineAddress) {
NTSTATUS status = PsRemoveCreateThreadNotifyRoutine((PCREATE_THREAD_NOTIFY_ROUTINE)pNotifyRoutineAddress);
if (!NT_SUCCESS(status))
ShowError("PsRemoveCreateThreadNotifyRoutine", status);
return status;
};

源代码

Driver.h:

1
2
3
4
5
6
7
8
9
10
11
12
#ifndef _DRIVER_H_
#define _DRIVER_H_


#include <ntddk.h>


VOID DriverUnload(PDRIVER_OBJECT pDriverObject);
NTSTATUS DriverDefaultHandle(PDEVICE_OBJECT pDevObj, PIRP pIrp);


#endif

EnumRemove.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#ifndef _ENUM_REMOVE_H_
#define _ENUM_REMOVE_H_


#include <ntddk.h>


// 遍历回调
BOOLEAN EnumNotifyRoutine();

// 移除回调
NTSTATUS RemoveNotifyRoutine(PVOID pNotifyRoutineAddress);

// 获取 PspCreateThreadNotifyRoutine 数组地址
PVOID GetPspCreateThreadNotifyRoutine();

// 根据特征码获取 PspCreateThreadNotifyRoutine 数组地址
PVOID SearchPspCreateThreadNotifyRoutine(PUCHAR pFirstSpecialData, ULONG ulFirstSpecialDataSize, PUCHAR pSecondSpecialData, ULONG ulSecondSpecialDataSize);

// 指定内存区域的特征码扫描
PVOID SearchMemory(PVOID pStartAddress, PVOID pEndAddress, PUCHAR pMemoryData, ULONG ulMemoryDataSize);


#endif

Driver.c:

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
#include "EnumRemove.h"
#include "Driver.h"


NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegPath)
{
DbgPrint("Enter DriverEntry\n");

NTSTATUS status = STATUS_SUCCESS;
pDriverObject->DriverUnload = DriverUnload;
for (ULONG i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
{
pDriverObject->MajorFunction[i] = DriverDefaultHandle;
}

// 遍历回调
EnumNotifyRoutine();

DbgPrint("Leave DriverEntry\n");
return status;
}



VOID DriverUnload(PDRIVER_OBJECT pDriverObject)
{
}


NTSTATUS DriverDefaultHandle(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
NTSTATUS status = STATUS_SUCCESS;
pIrp->IoStatus.Status = status;
pIrp->IoStatus.Information = 0;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);

return status;
}

EnumRemove.c:

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
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
#include "EnumRemove.h"


VOID ShowError(PCHAR lpszText, NTSTATUS ntStatus)
{
DbgPrint("%s Error[0x%X]\n", lpszText, ntStatus);
}


// 遍历回调
BOOLEAN EnumNotifyRoutine()
{
ULONG i = 0;
PVOID pPspCreateThreadNotifyRoutineAddress = NULL;
PVOID pNotifyRoutineAddress = NULL;

// 获取 PspCreateThreadNotifyRoutine 数组地址
pPspCreateThreadNotifyRoutineAddress = GetPspCreateThreadNotifyRoutine();
if (NULL == pPspCreateThreadNotifyRoutineAddress)
{
DbgPrint("GetPspCreateThreadNotifyRoutine Error!\n");
return FALSE;
}
DbgPrint("pPspCreateThreadNotifyRoutineAddress=0x%p\n", pPspCreateThreadNotifyRoutineAddress);

// 获取回调地址并解密
#ifdef _WIN64
for (i = 0; i < 64; i++)
{
pNotifyRoutineAddress = *(PVOID *)((PUCHAR)pPspCreateThreadNotifyRoutineAddress + sizeof(PVOID) * i);
pNotifyRoutineAddress = (PVOID)((ULONG64)pNotifyRoutineAddress & 0xfffffffffffffff8);
if (MmIsAddressValid(pNotifyRoutineAddress))
{
pNotifyRoutineAddress = *(PVOID *)pNotifyRoutineAddress;
DbgPrint("[%d]ullNotifyRoutine=0x%p\n", i, pNotifyRoutineAddress);
}
}
#else
for (i = 0; i < 8; i++)
{
pNotifyRoutineAddress = *(PVOID *)((PUCHAR)pPspCreateThreadNotifyRoutineAddress + sizeof(PVOID) * i);
pNotifyRoutineAddress = (PVOID)((ULONG)pNotifyRoutineAddress & 0xfffffff8);
if (MmIsAddressValid(pNotifyRoutineAddress))
{
pNotifyRoutineAddress = *(PVOID *)((PUCHAR)pNotifyRoutineAddress + 4);
DbgPrint("[%d]ullNotifyRoutine=0x%p\n", i, pNotifyRoutineAddress);
}
}
#endif

return TRUE;
}


// 移除回调
NTSTATUS RemoveNotifyRoutine(PVOID pNotifyRoutineAddress)
{
NTSTATUS status = PsRemoveCreateThreadNotifyRoutine((PCREATE_THREAD_NOTIFY_ROUTINE)pNotifyRoutineAddress);
if (!NT_SUCCESS(status))
{
ShowError("PsRemoveCreateThreadNotifyRoutine", status);
}
return status;
}


// 获取 PspCreateThreadNotifyRoutine 数组地址
PVOID GetPspCreateThreadNotifyRoutine()
{
PVOID pPspCreateThreadNotifyRoutineAddress = NULL;
RTL_OSVERSIONINFOW osInfo = { 0 };
UCHAR pFirstSpecialData[50] = { 0 };
ULONG ulFirstSpecialDataSize = 0;
UCHAR pSecondSpecialData[50] = { 0 };
ULONG ulSecondSpecialDataSize = 0;

// 获取系统版本信息, 判断系统版本
RtlGetVersion(&osInfo);
if (6 == osInfo.dwMajorVersion)
{
if (1 == osInfo.dwMinorVersion)
{
// Win7
#ifdef _WIN64
// 64 位
// 488D0D
pFirstSpecialData[0] = 0x48;
pFirstSpecialData[1] = 0x8D;
pFirstSpecialData[2] = 0x0D;
ulFirstSpecialDataSize = 3;
#else
// 32 位
// BE
pFirstSpecialData[0] = 0xBE;
ulFirstSpecialDataSize = 1;
#endif
}
else if (2 == osInfo.dwMinorVersion)
{
// Win8
#ifdef _WIN64
// 64 位

#else
// 32 位

#endif
}
else if (3 == osInfo.dwMinorVersion)
{
// Win8.1
#ifdef _WIN64
// 64 位
// 488D0D
pFirstSpecialData[0] = 0x48;
pFirstSpecialData[1] = 0x8D;
pFirstSpecialData[2] = 0x0D;
ulFirstSpecialDataSize = 3;
#else
// 32 位
// BB
pFirstSpecialData[0] = 0xBB;
ulFirstSpecialDataSize = 1;
#endif
}
}
else if (10 == osInfo.dwMajorVersion)
{
// Win10
#ifdef _WIN64
// 64 位
// E9
pFirstSpecialData[0] = 0xE9;
ulFirstSpecialDataSize = 1;
// 488D0D
pSecondSpecialData[0] = 0x48;
pSecondSpecialData[1] = 0x8D;
pSecondSpecialData[2] = 0x0D;
ulSecondSpecialDataSize = 3;
#else
// 32 位
// E8
pFirstSpecialData[0] = 0xE8;
ulFirstSpecialDataSize = 1;
// BF
pSecondSpecialData[0] = 0xBF;
ulSecondSpecialDataSize = 1;
#endif
}

// 根据特征码获取地址
pPspCreateThreadNotifyRoutineAddress = SearchPspCreateThreadNotifyRoutine(pFirstSpecialData, ulFirstSpecialDataSize, pSecondSpecialData, ulSecondSpecialDataSize);
return pPspCreateThreadNotifyRoutineAddress;
}


// 根据特征码获取 PspCreateThreadNotifyRoutine 数组地址
PVOID SearchPspCreateThreadNotifyRoutine(PUCHAR pFirstSpecialData, ULONG ulFirstSpecialDataSize, PUCHAR pSecondSpecialData, ULONG ulSecondSpecialDataSize)
{
UNICODE_STRING ustrFuncName;
PVOID pAddress = NULL;
LONG lOffset = 0;
PVOID pPsSetCreateThreadNotifyRoutine = NULL;
PVOID pPspSetCreateThreadNotifyRoutineAddress = NULL;
PVOID pPspCreateThreadNotifyRoutineAddress = NULL;

// 先获取 PsSetCreateThreadNotifyRoutine 函数地址
RtlInitUnicodeString(&ustrFuncName, L"PsSetCreateThreadNotifyRoutine");
pPsSetCreateThreadNotifyRoutine = MmGetSystemRoutineAddress(&ustrFuncName);
if (NULL == pPsSetCreateThreadNotifyRoutine)
{
ShowError("MmGetSystemRoutineAddress", 0);
return pPspCreateThreadNotifyRoutineAddress;
}

// 然后, 对于非 Win10 系统, 则根据第一个特征码获取 PspCreateThreadNotifyRoutine 地址;
// 对于 Win10 系统, 则需先根据第一个特征码获取获取 PspSetCreateThreadNotifyRoutine 地址,
// 再根据第二个特征码获取 PspCreateThreadNotifyRoutine 地址.
pAddress = SearchMemory(pPsSetCreateThreadNotifyRoutine,
(PVOID)((PUCHAR)pPsSetCreateThreadNotifyRoutine + 0xFF),
pFirstSpecialData, ulFirstSpecialDataSize);
if (NULL == pAddress)
{
ShowError("SearchMemory1", 0);
return pPspCreateThreadNotifyRoutineAddress;
}

// 无第二个特征码, 则非 Win10 系统
if (0 == ulSecondSpecialDataSize)
{
// 获取 PspCreateThreadNotifyRoutine 地址
#ifdef _WIN64
// 64 位
// 获取偏移数据, 并计算地址
lOffset = *(PLONG)pAddress;
pPspCreateThreadNotifyRoutineAddress = (PVOID)((PUCHAR)pAddress + sizeof(LONG) + lOffset);
#else
// 32 位
pPspCreateThreadNotifyRoutineAddress = *(PVOID *)pAddress;
#endif

// 直接返回
return pPspCreateThreadNotifyRoutineAddress;
}

// 存在第二个特征码, 即 Win10 系统
// 获取偏移数据, 并计算地址
lOffset = *(PLONG)pAddress;
pPspSetCreateThreadNotifyRoutineAddress = (PVOID)((PUCHAR)pAddress + sizeof(LONG) + lOffset);
// 最后, 查找 PspCreateThreadNotifyRoutine 地址
pAddress = SearchMemory(pPspSetCreateThreadNotifyRoutineAddress,
(PVOID)((PUCHAR)pPspSetCreateThreadNotifyRoutineAddress + 0xFF),
pSecondSpecialData, ulSecondSpecialDataSize);
if (NULL == pAddress)
{
ShowError("SearchMemory2", 0);
return pPspCreateThreadNotifyRoutineAddress;
}
// 获取 PspCreateThreadNotifyRoutine 地址
#ifdef _WIN64
// 64 位先获取偏移, 再计算地址
lOffset = *(PLONG)pAddress;
pPspCreateThreadNotifyRoutineAddress = (PVOID)((PUCHAR)pAddress + sizeof(LONG) + lOffset);
#else
// 32 位直接获取地址
pPspCreateThreadNotifyRoutineAddress = *(PVOID *)pAddress;
#endif

return pPspCreateThreadNotifyRoutineAddress;
}


// 指定内存区域的特征码扫描
PVOID SearchMemory(PVOID pStartAddress, PVOID pEndAddress, PUCHAR pMemoryData, ULONG ulMemoryDataSize)
{
PVOID pAddress = NULL;
PUCHAR i = NULL;
ULONG m = 0;

// 扫描内存
for (i = (PUCHAR)pStartAddress; i < (PUCHAR)pEndAddress; i++)
{
// 判断特征码
for (m = 0; m < ulMemoryDataSize; m++)
{
if (*(PUCHAR)(i + m) != pMemoryData[m])
{
break;
}
}
// 判断是否找到符合特征码的地址
if (m >= ulMemoryDataSize)
{
// 找到特征码位置, 获取紧接着特征码的下一地址
pAddress = (PVOID)(i + ulMemoryDataSize);
break;
}
}

return pAddress;
}