WindowsAPI编程核心技术-模块加载监控

前置芝士

PsSetLoadImageNotifyRoutine

设置模块加载回调函数,完成模块加载时通知回调函数。

1
2
3
NTSTATUS PsSetLoadImageNotifyRoutine(
_In_ PLOAD_IMAGE_NOTIFY_ROUTINE NotifyRoutine //指向回调函数
)

可通过PsRemoveLoadImageNotifyRoutine删除回调。

PLOAD_IMAGE_NOTIFY_ROUTINE

回调函数。

1
2
3
4
5
VOID SetLoadImageNotifyRoutine(
_In_opt_ PUNICODE_STRING FullImageName, //标识可执行映像文件
_In_ HANDLE ProcessId, //加载模块所属进程ID 驱动程序为0
_In_ PIMAGE_INFO ImageInfo
)

IMAGE_INFO

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
typedef struct _IMAGE_INFO {
union {
ULONG Properties;
struct {
ULONG ImageAddressingMode : 8; // Code addressing mode 始终IMAGE_ADDRESSING_MODE_32BIT
ULONG SystemModeImage : 1; // System mode image 新加载的内核模式组件 对映射到用户空间的映像设置为零
ULONG ImageMappedToAllPids : 1; // Image mapped into all processes 始终0
ULONG ExtendedInfoPresent : 1; // IMAGE_INFO_EX available IMAGE_INFO是IMAGE_INFO_EX一部分
ULONG MachineTypeMismatch : 1; // Architecture type mismatch 始终0
ULONG ImageSignatureLevel : 4; // Signature level 代码完整性标记-映像签名级别
ULONG ImageSignatureType : 3; // Signature type 代码完整性标记-映像签名类型
ULONG ImagePartialMap : 1; // Nonzero if entire image is not mapped 非0映像视图不是映射整个映像的部分视图 0视图映射整个图像
ULONG Reserved : 12; //始终0
};
};
PVOID ImageBase; //映像虚拟基地址
ULONG ImageSelector; //始终0
SIZE_T ImageSize; //映像虚拟大小 单位字节
ULONG ImageSectionNumber; //始终0
} IMAGE_INFO, *PIMAGE_INFO;

驱动模块的卸载

回调函数收到模块加载的信息时,模块已经加载完成,不能直接控制模块的加载操,但可以通过其他方法卸载已加载的模块。

实现思路就是在驱动模块入口点DriverEntry中直接返回NTSTATUS错误码如STATUS_ACCESS_DENIED(0xC0000022),这样已加载的驱动程序会在执行时出错,导致驱动程序启动失败。加载回调函数第三个参数ImageInfo提供了模块在内存中的加载地址,根据PE头获取NT头IMAGE_NT_HEADERS中IMAGE_OPTIONAL_HEADER的入口点偏移AddressOfEntryPoint,将DriverEntry前几字节数据修改为以下,x86和x64通用:

1
2
B8 22 00 00 C0 mov eax, 0xC0000022
C3 ret

具体实现方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 拒绝加载驱动
NTSTATUS DenyLoadDriver(PVOID pImageBase) {
NTSTATUS status = STATUS_SUCCESS;
PMDL pMdl = NULL;
PVOID pVoid = NULL;
ULONG ulShellcodeLength = 16;
UCHAR pShellcode[16] = { 0xB8, 0x22, 0x00, 0x00, 0xC0, 0xC3, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 };
PIMAGE_DOS_HEADER pDosHeader = pImageBase;
PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((PUCHAR)pDosHeader + pDosHeader->e_lfanew);
PVOID pDriverEntry = (PVOID)((PUCHAR)pDosHeader + pNtHeaders->OptionalHeader.AddressOfEntryPoint);
pMdl = MmCreateMdl(NULL, pDriverEntry, ulShellcodeLength); //MDL方式修改内存 安全保险:)
MmBuildMdlForNonPagedPool(pMdl);
pVoid = MmMapLockedPages(pMdl, KernelMode);
RtlCopyMemory(pVoid, pShellcode, ulShellcodeLength);
MmUnmapLockedPages(pVoid, pMdl);
IoFreeMdl(pMdl);
return status;
};

DLL模块的卸载

DLL的返回值不能确定DLL是否加载成功,上述方法无效。可用MmUnmmapViewOfSection来卸载进程中已加载的模块。

当加载进程模块时系统有一个内部锁,为了避免死锁,在进程模块加载回调函数时不能映射、分配、查询、释放等操作。要想卸载DLL模块,必须等进程中所有模块加载完毕后卸载。可创建多线程延时等待,在进程模块加载完毕后用MmUnmapViewOfSection释放。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 调用 MmUnmapViewOfSection 函数来卸载已经加载的 DLL 模块
NTSTATUS DenyLoadDll(HANDLE ProcessId, PVOID pImageBase) {
NTSTATUS status = STATUS_SUCCESS;
PEPROCESS pEProcess = NULL;
status = PsLookupProcessByProcessId(ProcessId, &pEProcess);
if (!NT_SUCCESS(status)) {
DbgPrint("PsLookupProcessByProcessId Error[0x%X]\n", status);
return status;
};
// 卸载模块
status = MmUnmapViewOfSection(pEProcess, pImageBase);
if (!NT_SUCCESS(status)) {
DbgPrint("MmUnmapViewOfSection Error[0x%X]\n", status);
return status;
};
return status;
};

源代码

Driver.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#ifndef _DRIVER_H_
#define _DRIVER_H_


#include <ntddk.h>


#define DEV_NAME L"\\Device\\DEV_NAME"
#define SYM_NAME L"\\DosDevices\\SYM_NAME"
#define IOCTL_TEST CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)


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


#endif

LoadImageNotify.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
#ifndef _LOAD_IMAGE_NOTIFY_H_
#define _LOAD_IMAGE_NOTIFY_H_


#include <ntifs.h>
#include <ntimage.h>


typedef struct _MY_DATA
{
HANDLE ProcessId;
PVOID pImageBase;
}MY_DATA, *PMY_DATA;


NTSTATUS MmUnmapViewOfSection(PEPROCESS Process, PVOID BaseAddress);

NTSTATUS SetNotifyRoutine();
NTSTATUS RemoveNotifyRoutine();
VOID LoadImageNotifyRoutine(PUNICODE_STRING FullImageName, HANDLE ProcessId, PIMAGE_INFO ImageInfo);

NTSTATUS U2C(PUNICODE_STRING pustrSrc, PCHAR pszDest, ULONG ulDestLength);
VOID ThreadProc(_In_ PVOID StartContext);

// 拒绝加载驱动
NTSTATUS DenyLoadDriver(PVOID pImageBase);
// 拒绝加载DLL模块
NTSTATUS DenyLoadDll(HANDLE ProcessId, PVOID pImageBase);

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


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

pDriverObject->DriverUnload = DriverUnload;
pDriverObject->MajorFunction[IRP_MJ_CREATE] = DriverDefaultHandle;
pDriverObject->MajorFunction[IRP_MJ_CLOSE] = DriverDefaultHandle;
pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DriverControlHandle;
status = CreateDevice(pDriverObject);

status = SetNotifyRoutine();

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


VOID DriverUnload(PDRIVER_OBJECT pDriverObject)
{
DbgPrint("Enter DriverUnload\n");

RemoveNotifyRoutine();

UNICODE_STRING ustrSymName;
RtlInitUnicodeString(&ustrSymName, SYM_NAME);
IoDeleteSymbolicLink(&ustrSymName);
if (pDriverObject->DeviceObject)
{
IoDeleteDevice(pDriverObject->DeviceObject);
}

DbgPrint("Leave DriverUnload\n");
}


NTSTATUS DriverDefaultHandle(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
DbgPrint("Enter DriverDefaultHandle\n");
NTSTATUS status = STATUS_SUCCESS;

pIrp->IoStatus.Status = status;
pIrp->IoStatus.Information = 0;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);

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


NTSTATUS DriverControlHandle(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
DbgPrint("Enter DriverControlHandle\n");
NTSTATUS status = STATUS_SUCCESS;
PIO_STACK_LOCATION pIoStackLocation = IoGetCurrentIrpStackLocation(pIrp);
ULONG ulInputLength = pIoStackLocation->Parameters.DeviceIoControl.InputBufferLength;
ULONG ulOutputLength = pIoStackLocation->Parameters.DeviceIoControl.OutputBufferLength;
ULONG ulControlCode = pIoStackLocation->Parameters.DeviceIoControl.IoControlCode;
PVOID pBuf = pIrp->AssociatedIrp.SystemBuffer;
ULONG ulInfo = 0;

switch(ulControlCode)
{
case IOCTL_TEST:
{
break;
}
default:
break;
}

pIrp->IoStatus.Status = status;
pIrp->IoStatus.Information = 0;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);

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


NTSTATUS CreateDevice(PDRIVER_OBJECT pDriverObject)
{
DbgPrint("Enter CreateDevice\n");
NTSTATUS status = STATUS_SUCCESS;
UNICODE_STRING ustrDevName, ustrSymName;
PDEVICE_OBJECT pDevObj = NULL;
RtlInitUnicodeString(&ustrDevName, DEV_NAME);
RtlInitUnicodeString(&ustrSymName, SYM_NAME);

status = IoCreateDevice(pDriverObject, 0, &ustrDevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDevObj);
if (!NT_SUCCESS(status))
{
DbgPrint("IoCreateDevice Error![0x%X]\n", status);
return status;
}

status = IoCreateSymbolicLink(&ustrSymName, &ustrDevName);
if (!NT_SUCCESS(status))
{
DbgPrint("IoCreateSymbolicLink Error![0x%X]\n", status);
return status;
}

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

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


NTSTATUS SetNotifyRoutine()
{
NTSTATUS status = STATUS_SUCCESS;
status = PsSetLoadImageNotifyRoutine(LoadImageNotifyRoutine);
return status;
}


NTSTATUS RemoveNotifyRoutine()
{
NTSTATUS status = STATUS_SUCCESS;
status = PsRemoveLoadImageNotifyRoutine(LoadImageNotifyRoutine);
return status;
}


VOID LoadImageNotifyRoutine(PUNICODE_STRING FullImageName, HANDLE ProcessId, PIMAGE_INFO ImageInfo)
{
DbgPrint("[%d][%wZ][%d][0x%p]\n", ProcessId, FullImageName, ImageInfo->ImageSize, ImageInfo->ImageBase);

HANDLE hThread = NULL;
CHAR szTemp[1024] = { 0 };
U2C(FullImageName, szTemp, 1024);
if (NULL != strstr(szTemp, "winmm.dll"))
{
// EXE或者DLL
if (0 != ProcessId)
{
// 创建多线程, 延时1秒钟后再卸载模块
PMY_DATA pMyData = ExAllocatePool(NonPagedPool, sizeof(MY_DATA));
pMyData->ProcessId = ProcessId;
pMyData->pImageBase = ImageInfo->ImageBase;
PsCreateSystemThread(&hThread, 0, NULL, NtCurrentProcess(), NULL, ThreadProc, pMyData);
DbgPrint("Deny Load DLL\n");
}
// 驱动
else
{
DenyLoadDriver(ImageInfo->ImageBase);
DbgPrint("Deny Load Driver\n");
}
}
}


// 拒绝加载驱动
NTSTATUS DenyLoadDriver(PVOID pImageBase)
{
NTSTATUS status = STATUS_SUCCESS;
PMDL pMdl = NULL;
PVOID pVoid = NULL;
ULONG ulShellcodeLength = 16;
UCHAR pShellcode[16] = {0xB8, 0x22, 0x00, 0x00, 0xC0, 0xC3, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90};
PIMAGE_DOS_HEADER pDosHeader = pImageBase;
PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((PUCHAR)pDosHeader + pDosHeader->e_lfanew);
PVOID pDriverEntry = (PVOID)((PUCHAR)pDosHeader + pNtHeaders->OptionalHeader.AddressOfEntryPoint);

pMdl = MmCreateMdl(NULL, pDriverEntry, ulShellcodeLength);
MmBuildMdlForNonPagedPool(pMdl);
pVoid = MmMapLockedPages(pMdl, KernelMode);
RtlCopyMemory(pVoid, pShellcode, ulShellcodeLength);
MmUnmapLockedPages(pVoid, pMdl);
IoFreeMdl(pMdl);

return status;
}


// 调用 MmUnmapViewOfSection 函数来卸载已经加载的 DLL 模块
NTSTATUS DenyLoadDll(HANDLE ProcessId, PVOID pImageBase)
{
NTSTATUS status = STATUS_SUCCESS;
PEPROCESS pEProcess = NULL;

status = PsLookupProcessByProcessId(ProcessId, &pEProcess);
if (!NT_SUCCESS(status))
{
DbgPrint("PsLookupProcessByProcessId Error[0x%X]\n", status);
return status;
}

// 卸载模块
status = MmUnmapViewOfSection(pEProcess, pImageBase);
if (!NT_SUCCESS(status))
{
DbgPrint("MmUnmapViewOfSection Error[0x%X]\n", status);
return status;
}

return status;
}


VOID ThreadProc(_In_ PVOID StartContext)
{
PMY_DATA pMyData = (PMY_DATA)StartContext;
LARGE_INTEGER liTime = { 0 };
// 延时 1 秒
liTime.QuadPart = -10 * 1000 * 1000; // 100纳秒为单位时间, 1秒==1000毫秒==1000*1000微秒==1000*1000*1000纳秒, 负值表示相对时间
KeDelayExecutionThread(KernelMode, FALSE, &liTime);
// 卸载
DenyLoadDll(pMyData->ProcessId, pMyData->pImageBase);

ExFreePool(pMyData);
}


NTSTATUS U2C(PUNICODE_STRING pustrSrc, PCHAR pszDest, ULONG ulDestLength)
{
NTSTATUS status = STATUS_SUCCESS;
ANSI_STRING strTemp;

RtlZeroMemory(pszDest, ulDestLength);
RtlUnicodeStringToAnsiString(&strTemp, pustrSrc, TRUE);
if (ulDestLength > strTemp.Length)
{
RtlCopyMemory(pszDest, strTemp.Buffer, strTemp.Length);
}
RtlFreeAnsiString(&strTemp);

return status;
}

反模块加载监控

基本原理

与反进程/线程创建监控原理极为相似,这里概念就不重复了。

回调函数存储在PspLoadImageNotifyRoutine数组中,逆向PsSetLoadImageNotifyRoutine函数:

1
2
3
4
;Windows 10 x64
nt!PsSetLoadImageNotifyRoutine+0x36:
48 8D 0D 6F 0A DF FF lea rcx, [nt!PspLoadImageNotifyRoutine]
45 33 C0 xor r8d, r8d

通过该函数可直接获得数组地址或地址偏移。于是通过扫描内存特征码获取该函数。

Windows 7 Windows 8.1 Windows 10
x86 BE BB BF
x64 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
// 根据特征码获取 PspLoadImageNotifyRoutine 数组地址
PVOID SearchPspLoadImageNotifyRoutine(PUCHAR pSpecialData, ULONG ulSpecialDataSize) {
UNICODE_STRING ustrFuncName;
PVOID pAddress = NULL;
LONG lOffset = 0;
PVOID pPsSetLoadImageNotifyRoutine = NULL;
PVOID pPspLoadImageNotifyRoutine = NULL;
// 先获取 PsSetLoadImageNotifyRoutine 函数地址
RtlInitUnicodeString(&ustrFuncName, L"PsSetLoadImageNotifyRoutine");
pPsSetLoadImageNotifyRoutine = MmGetSystemRoutineAddress(&ustrFuncName);
if (NULL == pPsSetLoadImageNotifyRoutine) {
ShowError("MmGetSystemRoutineAddress", 0);
return pPspLoadImageNotifyRoutine;
};
// 然后, 查找 PspSetCreateProcessNotifyRoutine 函数地址
pAddress = SearchMemory(pPsSetLoadImageNotifyRoutine, (PVOID)((PUCHAR)pPsSetLoadImageNotifyRoutine + 0xFF), pSpecialData, ulSpecialDataSize);
if (NULL == pAddress) {
ShowError("SearchMemory", 0);
return pPspLoadImageNotifyRoutine;
};
// 获取地址
#ifdef _WIN64
// 64 位先获取偏移, 再计算地址
lOffset = *(PLONG)pAddress;
pPspLoadImageNotifyRoutine = (PVOID)((PUCHAR)pAddress + sizeof(LONG) + lOffset);
#else
// 32 位直接获取地址
pPspLoadImageNotifyRoutine = *(PVOID*)pAddress;
#endif
return pPspLoadImageNotifyRoutine;
};

然后是一模一样的解密:

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 pPspLoadImageNotifyRoutineAddress = NULL;
PVOID pNotifyRoutineAddress = NULL;
// 获取 PspLoadImageNotifyRoutine 数组地址
pPspLoadImageNotifyRoutineAddress = GetPspLoadImageNotifyRoutine();
if (NULL == pPspLoadImageNotifyRoutineAddress) {
DbgPrint("GetPspLoadImageNotifyRoutine Error!\n");
return FALSE;
};
DbgPrint("pPspLoadImageNotifyRoutineAddress=0x%p\n", pPspLoadImageNotifyRoutineAddress);
// 获取回调地址并解密
#ifdef _WIN64
for (i = 0; i < 64; i++) {
pNotifyRoutineAddress = *(PVOID*)((PUCHAR)pPspLoadImageNotifyRoutineAddress + 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)pPspLoadImageNotifyRoutineAddress + 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 = PsRemoveLoadImageNotifyRoutine((PLOAD_IMAGE_NOTIFY_ROUTINE)pNotifyRoutineAddress);
if (!NT_SUCCESS(status))
ShowError("PsRemoveLoadImageNotifyRoutine", status);
return status;
};

另一种想法

系统中还有PspNotifyEnableMask全局变量,是个32位整型掩码,根据掩码低位字节设置的位可确定将调用哪些类型回调。位0、位1、位3分别决定是否触发PsSetCreateProcessNotifyRoutine回调、PsSetCreateThreadNotifyRoutine回调、PsSetLoadImageNotifyRoutine回调。若置0,对应回调失效。该全局变量不受Patch Guard保护,可被修改。但该全局变量没有导出,也不能在导出函数中出现,获取该变量地址较难。

源代码

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);

// 获取 PspLoadImageNotifyRoutine 数组地址
PVOID GetPspLoadImageNotifyRoutine();

// 根据特征码获取 PspLoadImageNotifyRoutine 数组地址
PVOID SearchPspLoadImageNotifyRoutine(PUCHAR pSpecialData, ULONG ulSpecialDataSize);

// 指定内存区域的特征码扫描
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
#include "EnumRemove.h"


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


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

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

// 获取回调地址并解密
#ifdef _WIN64
for (i = 0; i < 64; i++)
{
pNotifyRoutineAddress = *(PVOID *)((PUCHAR)pPspLoadImageNotifyRoutineAddress + 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)pPspLoadImageNotifyRoutineAddress + 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 = PsRemoveLoadImageNotifyRoutine((PLOAD_IMAGE_NOTIFY_ROUTINE)pNotifyRoutineAddress);
if (!NT_SUCCESS(status))
{
ShowError("PsRemoveLoadImageNotifyRoutine", status);
}
return status;
}


// 获取 PspLoadImageNotifyRoutine 数组地址
PVOID GetPspLoadImageNotifyRoutine()
{
PVOID pPspLoadImageNotifyRoutineAddress = NULL;
RTL_OSVERSIONINFOW osInfo = { 0 };
UCHAR pSpecialData[50] = { 0 };
ULONG ulSpecialDataSize = 0;

// 获取系统版本信息, 判断系统版本
RtlGetVersion(&osInfo);
if (6 == osInfo.dwMajorVersion)
{
if (1 == osInfo.dwMinorVersion)
{
// Win7
#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 (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 位
// BB
pSpecialData[0] = 0xBB;
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 位
// BF
pSpecialData[0] = 0xBF;
ulSpecialDataSize = 1;
#endif
}

// 根据特征码获取地址
pPspLoadImageNotifyRoutineAddress = SearchPspLoadImageNotifyRoutine(pSpecialData, ulSpecialDataSize);
return pPspLoadImageNotifyRoutineAddress;
}


// 根据特征码获取 PspLoadImageNotifyRoutine 数组地址
PVOID SearchPspLoadImageNotifyRoutine(PUCHAR pSpecialData, ULONG ulSpecialDataSize)
{
UNICODE_STRING ustrFuncName;
PVOID pAddress = NULL;
LONG lOffset = 0;
PVOID pPsSetLoadImageNotifyRoutine = NULL;
PVOID pPspLoadImageNotifyRoutine = NULL;

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

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

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

return pPspLoadImageNotifyRoutine;
}


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