WindowsAPI编程核心技术-注册表监控

前置芝士

CmRegisterCallback

注册注册表监控例程。

1
2
3
4
5
NTSTATUS CmRegisterCallback(
_In_ PEX_CALLBACK_FUNCTION Function, //例程
_In_opt_ PVOID Context, //例程参数
_Out_ PLARGE_INTEGER Cookie //回调例程标识
)

PEX_CALLBACK_FUNCTION

回调函数。

1
2
3
4
5
NTSTATUS RegistryCallback(
_In_ PVOID CallbackContext, //参数
_In_opt_ PVOID Argument1, //操作类型
_In_opt_ PVOID Argument2 //操作信息
)

实现方法

设置注册表回调函数时会拿到Cookie:

1
2
3
4
5
6
7
8
9
10
// 设置回调函数
NTSTATUS SetRegisterCallback() {
NTSTATUS status = CmRegisterCallback(RegisterMonCallback, NULL, &g_liRegCookie);
if (!NT_SUCCESS(status)) {
ShowError("CmRegisterCallback", status);
g_liRegCookie.QuadPart = 0;
return status;
};
return status;
};

删除注册表回调函数时直接用Cookie删就好啦:

1
2
3
4
5
6
// 删除回调函数
VOID RemoveRegisterCallback(VOID) {
if (0 < g_liRegCookie.QuadPart)
CmUnRegisterCallback(g_liRegCookie);
return;
};

当要拒绝注册表指定键操作时,回调函数直接除STATUS_SUCCESS以外的错误码,如STATUS_ACCESS_DENIED等。

回调函数的第一个参数判断操作类型,RegNtPreCreateKey将要创建注册表,RegNtPreOpenKey将要打开注册表,RegNtPreDeleteKey将要删除注册表,RegNtPreDeleteValueKey将要删除注册表键值,RegNtPreSetValueKey将要修改键值。

第二个参数获取操作类型对应结构体数据,从中获取注册表路径对象,用ObQueryNameString获取字符串路径。不同操作结构体不同。

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
// 回调函数
NTSTATUS RegisterMonCallback(_In_ PVOID CallbackContext, _In_opt_ PVOID Argument1, _In_opt_ PVOID Argument2) {
NTSTATUS status = STATUS_SUCCESS;
UNICODE_STRING ustrRegPath;
// 获取操作类型
LONG lOperateType = (REG_NOTIFY_CLASS)Argument1;
// 申请内存
ustrRegPath.Length = 0;
ustrRegPath.MaximumLength = 1024 * sizeof(WCHAR);
ustrRegPath.Buffer = ExAllocatePool(NonPagedPool, ustrRegPath.MaximumLength);
if (NULL == ustrRegPath.Buffer) {
ShowError("ExAllocatePool", 0);
return status;
};
RtlZeroMemory(ustrRegPath.Buffer, ustrRegPath.MaximumLength);
// 判断操作
switch (lOperateType) {
// 创建注册表之前
case RegNtPreCreateKey: {
// 获取注册表路径
GetRegisterObjectCompletePath(&ustrRegPath, ((PREG_CREATE_KEY_INFORMATION)Argument2)->RootObject);
// 显示
DbgPrint("[RegNtPreCreateKey][%wZ][%wZ]\n", &ustrRegPath, ((PREG_CREATE_KEY_INFORMATION)Argument2)->CompleteName);
break;
};
// 打开注册表之前
case RegNtPreOpenKey: {
// 获取注册表路径
GetRegisterObjectCompletePath(&ustrRegPath, ((PREG_CREATE_KEY_INFORMATION)Argument2)->RootObject);
// 显示
DbgPrint("[RegNtPreOpenKey][%wZ][%wZ]\n", &ustrRegPath, ((PREG_CREATE_KEY_INFORMATION)Argument2)->CompleteName);
break;
};
// 删除键之前
case RegNtPreDeleteKey: {
// 获取注册表路径
GetRegisterObjectCompletePath(&ustrRegPath, ((PREG_DELETE_KEY_INFORMATION)Argument2)->Object);
// 显示
DbgPrint("[RegNtPreDeleteKey][%wZ]\n", &ustrRegPath);
break;
};
// 删除键值之前
case RegNtPreDeleteValueKey: {
// 获取注册表路径
GetRegisterObjectCompletePath(&ustrRegPath, ((PREG_DELETE_VALUE_KEY_INFORMATION)Argument2)->Object);
// 显示
DbgPrint("[RegNtPreDeleteValueKey][%wZ][%wZ]\n", &ustrRegPath, ((PREG_DELETE_VALUE_KEY_INFORMATION)Argument2)->ValueName);
break;
};
// 修改键值之前
case RegNtPreSetValueKey: {
// 获取注册表路径
GetRegisterObjectCompletePath(&ustrRegPath, ((PREG_SET_VALUE_KEY_INFORMATION)Argument2)->Object);
// 显示
DbgPrint("[RegNtPreSetValueKey][%wZ][%wZ]\n", &ustrRegPath, ((PREG_SET_VALUE_KEY_INFORMATION)Argument2)->ValueName);
break;
};
default:
break;
};
// 判断是否是被保护的注册表
if (IsProtectReg(ustrRegPath))
// 拒绝操作
status = STATUS_ACCESS_DENIED;
// 释放内存
if (NULL != ustrRegPath.Buffer) {
ExFreePool(ustrRegPath.Buffer);
ustrRegPath.Buffer = NULL;
};
// 获取当前进程, 即操作注册表的进程
PEPROCESS pEProcess = PsGetCurrentProcess();
if (NULL != pEProcess) {
UCHAR* lpszProcessName = PsGetProcessImageFileName(pEProcess);
if (NULL != lpszProcessName)
DbgPrint("Current Process[%s]\n", lpszProcessName);
};
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

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#ifndef _NOTIFY_ROUTINE_H_
#define _NOTIFY_ROUTINE_H_


#include <ntddk.h>


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

NTSTATUS ObQueryNameString(
_In_ PVOID Object,
_Out_writes_bytes_opt_(Length) POBJECT_NAME_INFORMATION ObjectNameInfo,
_In_ ULONG Length,
_Out_ PULONG ReturnLength
);


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

// 删除回调函数
VOID RemoveRegisterCallback();

// 回调函数
NTSTATUS RegisterMonCallback(
_In_ PVOID CallbackContext,
// 操作类型(只是操作编号,不是指针)
_In_opt_ PVOID Argument1,
// 操作详细信息的结构体指针
_In_opt_ PVOID Argument2
);

// 获取注册表完整路径
BOOLEAN GetRegisterObjectCompletePath(PUNICODE_STRING pRegistryPath, PVOID pRegistryObject);

// 判断是否是保护注册表路径
BOOLEAN IsProtectReg(UNICODE_STRING ustrRegPath);



// 注册表回调Cookie
LARGE_INTEGER g_liRegCookie;


#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
#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;
}

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

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



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


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


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


// 设置回调函数
NTSTATUS SetRegisterCallback()
{
NTSTATUS status = CmRegisterCallback(RegisterMonCallback, NULL, &g_liRegCookie);
if (!NT_SUCCESS(status))
{
ShowError("CmRegisterCallback", status);
g_liRegCookie.QuadPart = 0;
return status;
}

return status;
}


// 删除回调函数
VOID RemoveRegisterCallback()
{
if (0 < g_liRegCookie.QuadPart)
{
CmUnRegisterCallback(g_liRegCookie);
}
}


// 回调函数
NTSTATUS RegisterMonCallback(
_In_ PVOID CallbackContext,
// 操作类型(只是操作编号,不是指针)
_In_opt_ PVOID Argument1,
// 操作详细信息的结构体指针
_In_opt_ PVOID Argument2
)
{
NTSTATUS status = STATUS_SUCCESS;
UNICODE_STRING ustrRegPath;

// 获取操作类型
LONG lOperateType = (REG_NOTIFY_CLASS)Argument1;
// 申请内存
ustrRegPath.Length = 0;
ustrRegPath.MaximumLength = 1024 * sizeof(WCHAR);
ustrRegPath.Buffer = ExAllocatePool(NonPagedPool, ustrRegPath.MaximumLength);
if (NULL == ustrRegPath.Buffer)
{
ShowError("ExAllocatePool", 0);
return status;
}
RtlZeroMemory(ustrRegPath.Buffer, ustrRegPath.MaximumLength);
// 判断操作
switch (lOperateType)
{
// 创建注册表之前
case RegNtPreCreateKey:
{
// 获取注册表路径
GetRegisterObjectCompletePath(&ustrRegPath, ((PREG_CREATE_KEY_INFORMATION)Argument2)->RootObject);

// 显示
DbgPrint("[RegNtPreCreateKey][%wZ][%wZ]\n", &ustrRegPath, ((PREG_CREATE_KEY_INFORMATION)Argument2)->CompleteName);
break;
}
// 打开注册表之前
case RegNtPreOpenKey:
{
// 获取注册表路径
GetRegisterObjectCompletePath(&ustrRegPath, ((PREG_CREATE_KEY_INFORMATION)Argument2)->RootObject);

// 显示
DbgPrint("[RegNtPreOpenKey][%wZ][%wZ]\n", &ustrRegPath, ((PREG_CREATE_KEY_INFORMATION)Argument2)->CompleteName);
break;
}
// 删除键之前
case RegNtPreDeleteKey:
{
// 获取注册表路径
GetRegisterObjectCompletePath(&ustrRegPath, ((PREG_DELETE_KEY_INFORMATION)Argument2)->Object);

// 显示
DbgPrint("[RegNtPreDeleteKey][%wZ]\n", &ustrRegPath);
break;
}
// 删除键值之前
case RegNtPreDeleteValueKey:
{
// 获取注册表路径
GetRegisterObjectCompletePath(&ustrRegPath, ((PREG_DELETE_VALUE_KEY_INFORMATION)Argument2)->Object);

// 显示
DbgPrint("[RegNtPreDeleteValueKey][%wZ][%wZ]\n", &ustrRegPath, ((PREG_DELETE_VALUE_KEY_INFORMATION)Argument2)->ValueName);
break;
}
// 修改键值之前
case RegNtPreSetValueKey:
{
// 获取注册表路径
GetRegisterObjectCompletePath(&ustrRegPath, ((PREG_SET_VALUE_KEY_INFORMATION)Argument2)->Object);

// 显示
DbgPrint("[RegNtPreSetValueKey][%wZ][%wZ]\n", &ustrRegPath, ((PREG_SET_VALUE_KEY_INFORMATION)Argument2)->ValueName);
break;
}
default:
break;
}
// 判断是否是被保护的注册表
if (IsProtectReg(ustrRegPath))
{
// 拒绝操作
status = STATUS_ACCESS_DENIED;
}
// 释放内存
if (NULL != ustrRegPath.Buffer)
{
ExFreePool(ustrRegPath.Buffer);
ustrRegPath.Buffer = NULL;
}

// 获取当前进程, 即操作注册表的进程
PEPROCESS pEProcess = PsGetCurrentProcess();
if (NULL != pEProcess)
{
UCHAR *lpszProcessName = PsGetProcessImageFileName(pEProcess);
if (NULL != lpszProcessName)
{
DbgPrint("Current Process[%s]\n", lpszProcessName);
}
}

return status;
}


// 获取注册表完整路径
BOOLEAN GetRegisterObjectCompletePath(PUNICODE_STRING pRegistryPath, PVOID pRegistryObject)
{
// 判断数据地址是否有效
if ((FALSE == MmIsAddressValid(pRegistryObject)) ||
(NULL == pRegistryObject))
{
return FALSE;
}
// 申请内存
ULONG ulSize = 512;
PVOID lpObjectNameInfo = ExAllocatePool(NonPagedPool, ulSize);
if (NULL == lpObjectNameInfo)
{
ShowError("ExAllocatePool", 0);
return FALSE;
}
// 获取注册表路径
ULONG ulRetLen = 0;
NTSTATUS status = ObQueryNameString(pRegistryObject, (POBJECT_NAME_INFORMATION)lpObjectNameInfo, ulSize, &ulRetLen);
if (!NT_SUCCESS(status))
{
ExFreePool(lpObjectNameInfo);
ShowError("ObQueryNameString", status);
return FALSE;
}
// 复制
RtlCopyUnicodeString(pRegistryPath, (PUNICODE_STRING)lpObjectNameInfo);
// 释放内存
ExFreePool(lpObjectNameInfo);
return TRUE;
}


// 判断是否是保护注册表路径
BOOLEAN IsProtectReg(UNICODE_STRING ustrRegPath)
{
if (NULL != wcsstr(ustrRegPath.Buffer, L"DemonGan"))
{
return TRUE;
}

return FALSE;
}

反注册表监控

基本原理

系统里所有通过CmRegisterCallback设置的注册表回调函数地址和Cookie都存储在以CallbackListHead为表头的双向链表中。这个CallbackListHead双向链表指向的数据结构是:

1
2
3
4
5
6
7
8
typedef struct _CM_NOTIFY_ENTRY {
LIST_ENTRY ListEntryHead; //下一个或上一个CM_NOTIFY_ENTRY结构信息
ULONG UnKnown1;
ULONG UnKnown2;
LARGE_INTEGER Cookie;
PVOID Context;
PVOID Function;
}CM_NOTIFY_ENTRY, * PCM_NOTIFY_ENTRY;

获取CallbackListHead表头地址方法:最简单从删除注册表回调函数CmUnRegisterCallback获取:

1
2
3
4
5
6
7
8
9
10
11
;Windows 10 x64
nt!CmUnRegisterCallback+0x44:
45 33 C0 xor r8d, r8d
48 8D 54 24 38 lea rdx, [rsp+38h]
48 8D 0D 39 F5 DB FF lea rcx,[nt!CallbackListHead]

;Windows 7 x64
nt!CmUnRegisterCallback+0xC6:
45 33 C0 xor r8d,r8d
48 8D 54 24 20 lea rdx, [rsp+20h]
48 8D 0D 6B 69 DC FF lea rcx, [nt!CallbackListHead]

扫描特征码即可,在x86下获取表头地址,在x64下获取表头偏移地址。

Windows 7 Windows 8.1 Windows 10
x86 BF BE B9
x64 488D54(488D0D出现次数太多) 488D0D 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
// 根据特征码获取 CallbackListHead 链表地址
PVOID SearchCallbackListHead(PUCHAR pSpecialData, ULONG ulSpecialDataSize, LONG lSpecialOffset) {
UNICODE_STRING ustrFuncName;
PVOID pAddress = NULL;
LONG lOffset = 0;
PVOID pCmUnRegisterCallback = NULL;
PVOID pCallbackListHead = NULL;
// 先获取 CmUnRegisterCallback 函数地址
RtlInitUnicodeString(&ustrFuncName, L"CmUnRegisterCallback");
pCmUnRegisterCallback = MmGetSystemRoutineAddress(&ustrFuncName);
if (NULL == pCmUnRegisterCallback) {
ShowError("MmGetSystemRoutineAddress", 0);
return pCallbackListHead;
};
// 然后, 查找 PspSetCreateProcessNotifyRoutine 函数地址
pAddress = SearchMemory(pCmUnRegisterCallback, (PVOID)((PUCHAR)pCmUnRegisterCallback + 0xFF), pSpecialData, ulSpecialDataSize);
if (NULL == pAddress) {
ShowError("SearchMemory", 0);
return pCallbackListHead;
};
// 获取地址
#ifdef _WIN64
// 64 位先获取偏移, 再计算地址
lOffset = *(PLONG)((PUCHAR)pAddress + lSpecialOffset);
pCallbackListHead = (PVOID)((PUCHAR)pAddress + lSpecialOffset + sizeof(LONG) + lOffset);
#else
// 32 位直接获取地址
pCallbackListHead = *(PVOID*)((PUCHAR)pAddress + lSpecialOffset);
#endif
return pCallbackListHead;
};

枚举系统注册表回调函数:

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
// 遍历回调
BOOLEAN EnumCallback(VOID) {
ULONG i = 0;
PVOID pCallbackListHeadAddress = NULL;
PCM_NOTIFY_ENTRY pNotifyEntry = NULL;
// 获取 CallbackListHead 链表地址
pCallbackListHeadAddress = GetCallbackListHead();
if (NULL == pCallbackListHeadAddress) {
DbgPrint("GetCallbackListHead Error!\n");
return FALSE;
};
DbgPrint("pCallbackListHeadAddress=0x%p\n", pCallbackListHeadAddress);
// 开始遍历双向链表
pNotifyEntry = (PCM_NOTIFY_ENTRY)pCallbackListHeadAddress;
do {
// 判断 pNotifyEntry 地址是否有效
if (FALSE == MmIsAddressValid(pNotifyEntry))
break;
// 判断 回调函数 地址是否有效
if (MmIsAddressValid(pNotifyEntry->Function))
// 显示
DbgPrint("CallbackFunction=0x%p, Cookie=0x%I64X\n", pNotifyEntry->Function, pNotifyEntry->Cookie.QuadPart);
// 获取下一链表
pNotifyEntry = (PCM_NOTIFY_ENTRY)pNotifyEntry->ListEntryHead.Flink;
} while (pCallbackListHeadAddress != (PVOID)pNotifyEntry);
return TRUE;
};

删除注册表回调函数:

1
2
3
4
5
6
7
// 移除回调
NTSTATUS RemoveCallback(LARGE_INTEGER Cookie) {
NTSTATUS status = CmUnRegisterCallback(Cookie);
if (!NT_SUCCESS(status))
ShowError("CmUnRegisterCallback", 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
25
26
27
28
29
30
31
32
33
34
35
#ifndef _ENUM_REMOVE_H_
#define _ENUM_REMOVE_H_


#include <ntddk.h>


typedef struct _CM_NOTIFY_ENTRY
{
LIST_ENTRY ListEntryHead;
ULONG UnKnown1;
ULONG UnKnown2;
LARGE_INTEGER Cookie;
PVOID Context;
PVOID Function;
}CM_NOTIFY_ENTRY, *PCM_NOTIFY_ENTRY;


// 遍历回调
BOOLEAN EnumCallback();

// 移除回调
NTSTATUS RemoveCallback(LARGE_INTEGER Cookie);

// 获取 CallbackListHead 链表地址
PVOID GetCallbackListHead();

// 根据特征码获取 CallbackListHead 链表地址
PVOID SearchCallbackListHead(PUCHAR pSpecialData, ULONG ulSpecialDataSize, LONG lSpecialOffset);

// 指定内存区域的特征码扫描
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;
}

// 遍历回调
EnumCallback();

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


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


// 遍历回调
BOOLEAN EnumCallback()
{
ULONG i = 0;
PVOID pCallbackListHeadAddress = NULL;
PCM_NOTIFY_ENTRY pNotifyEntry = NULL;

// 获取 CallbackListHead 链表地址
pCallbackListHeadAddress = GetCallbackListHead();
if (NULL == pCallbackListHeadAddress)
{
DbgPrint("GetCallbackListHead Error!\n");
return FALSE;
}
DbgPrint("pCallbackListHeadAddress=0x%p\n", pCallbackListHeadAddress);

// 开始遍历双向链表
pNotifyEntry = (PCM_NOTIFY_ENTRY)pCallbackListHeadAddress;
do
{
// 判断 pNotifyEntry 地址是否有效
if (FALSE == MmIsAddressValid(pNotifyEntry))
{
break;
}
// 判断 回调函数 地址是否有效
if (MmIsAddressValid(pNotifyEntry->Function))
{
// 显示
DbgPrint("CallbackFunction=0x%p, Cookie=0x%I64X\n", pNotifyEntry->Function, pNotifyEntry->Cookie.QuadPart);
}
// 获取下一链表
pNotifyEntry = (PCM_NOTIFY_ENTRY)pNotifyEntry->ListEntryHead.Flink;

} while (pCallbackListHeadAddress != (PVOID)pNotifyEntry);


return TRUE;
}


// 移除回调
NTSTATUS RemoveCallback(LARGE_INTEGER Cookie)
{
NTSTATUS status = CmUnRegisterCallback(Cookie);
if (!NT_SUCCESS(status))
{
ShowError("CmUnRegisterCallback", status);
}
return status;
}


// 获取 CallbackListHead 链表地址
PVOID GetCallbackListHead()
{
PVOID pCallbackListHeadAddress = NULL;
RTL_OSVERSIONINFOW osInfo = { 0 };
UCHAR pSpecialData[50] = { 0 };
ULONG ulSpecialDataSize = 0;
LONG lSpecialOffset = 0;

// 获取系统版本信息, 判断系统版本
RtlGetVersion(&osInfo);
if (6 == osInfo.dwMajorVersion)
{
if (1 == osInfo.dwMinorVersion)
{
// Win7
#ifdef _WIN64
// 64 位
// 488D54
pSpecialData[0] = 0x48;
pSpecialData[1] = 0x8D;
pSpecialData[2] = 0x54;
ulSpecialDataSize = 3;
lSpecialOffset = 5;
#else
// 32 位
// BF
pSpecialData[0] = 0xBF;
ulSpecialDataSize = 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
pSpecialData[0] = 0x48;
pSpecialData[1] = 0x8D;
pSpecialData[2] = 0x0D;
ulSpecialDataSize = 3;
#else
// 32 位
// BE
pSpecialData[0] = 0xBE;
ulSpecialDataSize = 1;
#endif
}
}
else if (10 == osInfo.dwMajorVersion)
{
// Win10
#ifdef _WIN64
// 64 位
// 488D0D
pSpecialData[0] = 0x48;
pSpecialData[1] = 0x8D;
pSpecialData[2] = 0x0D;
ulSpecialDataSize = 3;
#else
// 32 位
// B9
pSpecialData[0] = 0xB9;
ulSpecialDataSize = 1;
#endif
}

// 根据特征码获取地址
pCallbackListHeadAddress = SearchCallbackListHead(pSpecialData, ulSpecialDataSize, lSpecialOffset);
return pCallbackListHeadAddress;
}


// 根据特征码获取 CallbackListHead 链表地址
PVOID SearchCallbackListHead(PUCHAR pSpecialData, ULONG ulSpecialDataSize, LONG lSpecialOffset)
{
UNICODE_STRING ustrFuncName;
PVOID pAddress = NULL;
LONG lOffset = 0;
PVOID pCmUnRegisterCallback = NULL;
PVOID pCallbackListHead = NULL;

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

// 然后, 查找 PspSetCreateProcessNotifyRoutine 函数地址
pAddress = SearchMemory(pCmUnRegisterCallback,
(PVOID)((PUCHAR)pCmUnRegisterCallback + 0xFF),
pSpecialData, ulSpecialDataSize);
if (NULL == pAddress)
{
ShowError("SearchMemory", 0);
return pCallbackListHead;
}

// 获取地址
#ifdef _WIN64
// 64 位先获取偏移, 再计算地址
lOffset = *(PLONG)((PUCHAR)pAddress + lSpecialOffset);
pCallbackListHead = (PVOID)((PUCHAR)pAddress + lSpecialOffset + sizeof(LONG) + lOffset);
#else
// 32 位直接获取地址
pCallbackListHead = *(PVOID *)((PUCHAR)pAddress + lSpecialOffset);
#endif

return pCallbackListHead;
}


// 指定内存区域的特征码扫描
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;
}