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

进线程枚举

进程枚举:

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
#include <ntifs.h>
NTKERNELAPI UCHAR* PsGetProcessImageFileName(IN PEPROCESS Process); //未公开的进行导出即可
NTKERNELAPI HANDLE PsGetProcessInheritedFromUniqueProcessId(IN PEPROCESS Process);//未公开进行导出
// 根据进程ID返回进程EPROCESS结构体,失败返回NULL
PEPROCESS LookupProcess(HANDLE Pid){
PEPROCESS eprocess = NULL;
NTSTATUS Status = STATUS_UNSUCCESSFUL;
Status = PsLookupProcessByProcessId(Pid, &eprocess);
if (NT_SUCCESS(Status))
return eprocess;
return NULL;
}
VOID EnumProcess(){
PEPROCESS eproc = NULL;
for (int temp = 0; temp < 100000; temp += 4){
eproc = LookupProcess((HANDLE)temp);
if (eproc != NULL){
DbgPrint("进程名: %s --> 进程PID = %d --> 父进程PPID = %d\r\n",PsGetProcessImageFileName(eproc),PsGetProcessId(eproc), PsGetProcessInheritedFromUniqueProcessId(eproc));
ObDereferenceObject(eproc);
}
}
}
VOID UnDriver(PDRIVER_OBJECT driver){
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath){
EnumProcess();
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
#include <ntddk.h>
#include <windef.h>
// 声明API
NTKERNELAPI UCHAR* PsGetProcessImageFileName(IN PEPROCESS Process);
NTKERNELAPI NTSTATUS PsLookupProcessByProcessId(HANDLE Id, PEPROCESS *Process);
NTKERNELAPI NTSTATUS PsLookupThreadByThreadId(HANDLE Id, PETHREAD *Thread);
NTKERNELAPI PEPROCESS IoThreadToProcess(PETHREAD Thread);
//根据进程ID返回进程EPROCESS,失败返回NULL
PEPROCESS LookupProcess(HANDLE Pid){
PEPROCESS eprocess = NULL;
if (NT_SUCCESS(PsLookupProcessByProcessId(Pid, &eprocess)))
return eprocess;
else
return NULL;
}
//根据线程ID返回线程ETHREAD,失败返回NULL
PETHREAD LookupThread(HANDLE Tid){
PETHREAD ethread;
if (NT_SUCCESS(PsLookupThreadByThreadId(Tid, &ethread)))
return ethread;
else
return NULL;
}
//枚举指定进程中的线程
VOID EnumThread(PEPROCESS Process){
ULONG i = 0, c = 0;
PETHREAD ethrd = NULL;
PEPROCESS eproc = NULL;
for (i = 4; i<262144; i = i + 4){ // 一般来说没有超过100000的PID和TID
ethrd = LookupThread((HANDLE)i);
if (ethrd != NULL){
//获得线程所属进程
eproc = IoThreadToProcess(ethrd);
if (eproc == Process)
//打印出ETHREAD和TID
DbgPrint("线程: ETHREAD=%p TID=%ld\n",ethrd,(ULONG)PsGetThreadId(ethrd));
ObDereferenceObject(ethrd);
}
}
}
// 通过枚举的方式定位到指定的进程,这里传递一个进程名称
VOID MyEnumThread(char *ProcessName){
ULONG i = 0;
PEPROCESS eproc = NULL;
for (i = 4; i<100000000; i = i + 4){
eproc = LookupProcess((HANDLE)i);
if (eproc != NULL){
ObDereferenceObject(eproc);
if (strstr(PsGetProcessImageFileName(eproc), ProcessName) != NULL)
EnumThread(eproc);  // 相等则说明是我们想要的进程,直接枚举其中的线程
}
}
}
VOID DriverUnload(IN PDRIVER_OBJECT DriverObject){}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath){
MyEnumThread("calc.exe");
DriverObject->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}

枚举进程模块,DLL模块记录在PEB的LDR链表中,LDR是个双向链表。

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
#include <ntddk.h>
#include <windef.h>
//声明结构体
typedef struct _KAPC_STATE{
LIST_ENTRY ApcListHead[2];
PKPROCESS Process;
UCHAR KernelApcInProgress;
UCHAR KernelApcPending;
UCHAR UserApcPending;
} KAPC_STATE, *PKAPC_STATE;
typedef struct _LDR_DATA_TABLE_ENTRY{
LIST_ENTRY64 InLoadOrderLinks;
LIST_ENTRY64 InMemoryOrderLinks;
LIST_ENTRY64 InInitializationOrderLinks;
PVOID DllBase;
PVOID EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
ULONG Flags;
USHORT LoadCount;
USHORT TlsIndex;
PVOID SectionPointer;
ULONG CheckSum;
PVOID LoadedImports;
PVOID EntryPointActivationContext;
PVOID PatchInformation;
LIST_ENTRY64 ForwarderLinks;
LIST_ENTRY64 ServiceTagLinks;
LIST_ENTRY64 StaticLinks;
PVOID ContextInformation;
ULONG64 OriginalBase;
LARGE_INTEGER LoadTime;
} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
ULONG64 LdrInPebOffset = 0x018; //peb.ldr
ULONG64 ModListInPebOffset = 0x010; //peb.ldr.InLoadOrderModuleList
//声明API
NTKERNELAPI UCHAR* PsGetProcessImageFileName(IN PEPROCESS Process);
NTKERNELAPI PPEB PsGetProcessPeb(PEPROCESS Process);
NTKERNELAPI HANDLE PsGetProcessInheritedFromUniqueProcessId(IN PEPROCESS Process);
//根据进程ID返回进程EPROCESS,失败返回NULL
PEPROCESS LookupProcess(HANDLE Pid){
PEPROCESS eprocess = NULL;
if (NT_SUCCESS(PsLookupProcessByProcessId(Pid, &eprocess)))
return eprocess;
else
return NULL;
}
//枚举指定进程的模块
VOID EnumModule(PEPROCESS Process){
SIZE_T Peb = 0;
SIZE_T Ldr = 0;
PLIST_ENTRY ModListHead = 0;
PLIST_ENTRY Module = 0;
ANSI_STRING AnsiString;
KAPC_STATE ks;
//EPROCESS地址无效则退出
if (!MmIsAddressValid(Process))
return;
//获取PEB地址
Peb = (SIZE_T)PsGetProcessPeb(Process);
//PEB地址无效则退出
if (!Peb)
return;
//依附进程
KeStackAttachProcess(Process, &ks);
__try{
//获得LDR地址
Ldr = Peb + (SIZE_T)LdrInPebOffset;
//测试是否可读,不可读则抛出异常退出
ProbeForRead((CONST PVOID)Ldr, 8, 8);
//获得链表头
ModListHead = (PLIST_ENTRY)(*(PULONG64)Ldr + ModListInPebOffset);
//再次测试可读性
ProbeForRead((CONST PVOID)ModListHead, 8, 8);
//获得第一个模块的信息
Module = ModListHead->Flink;
while (ModListHead != Module){
//打印信息:基址、大小、DLL路径
DbgPrint("模块基址=%p 大小=%ld 路径=%wZ\n",(PVOID)(((PLDR_DATA_TABLE_ENTRY)Module)->DllBase),(ULONG)(((PLDR_DATA_TABLE_ENTRY)Module)->SizeOfImage),&(((PLDR_DATA_TABLE_ENTRY)Module)->FullDllName));
Module = Module->Flink;
//测试下一个模块信息的可读性
ProbeForRead((CONST PVOID)Module, 80, 8);
}
}
__except (EXCEPTION_EXECUTE_HANDLER){;}
//取消依附进程
KeUnstackDetachProcess(&ks);
}
// 通过枚举的方式定位到指定的进程,这里传递一个进程名称
VOID MyEnumModule(char *ProcessName){
ULONG i = 0;
PEPROCESS eproc = NULL;
for (i = 4; i<100000000; i = i + 4){
eproc = LookupProcess((HANDLE)i);
if (eproc != NULL){
ObDereferenceObject(eproc);
if (strstr(PsGetProcessImageFileName(eproc), ProcessName) != NULL)
EnumModule(eproc);  // 相等则说明是我们想要的进程,直接枚举其中的线程
}
}
}
VOID DriverUnload(IN PDRIVER_OBJECT DriverObject){}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath){
MyEnumModule("calc.exe");
DriverObject->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}

枚举SYS文件:

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
#include <ntddk.h>
#include <wdm.h>
typedef struct _LDR_DATA_TABLE_ENTRY {
LIST_ENTRY InLoadOrderLinks;
LIST_ENTRY InMemoryOrderLinks;
LIST_ENTRY InInitializationOrderLinks;
PVOID DllBase;
PVOID EntryPoint;
ULONG SizeOfImages;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
ULONG Flags;
USHORT LoadCount;
USHORT TlsIndex;
union {
LIST_ENTRY HashLinks;
struct {
PVOID SectionPointer;
ULONG CheckSum;
};
};
union {
struct {
ULONG TimeDateStamp;
};
struct {
PVOID LoadedImports;
};
};
}LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
VOID DriverUnload(IN PDRIVER_OBJECT DriverObject){}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath){
ULONG count = 0;
NTSTATUS Status;
DriverObject->DriverUnload = DriverUnload;
PLDR_DATA_TABLE_ENTRY pLdr = NULL;
PLIST_ENTRY pListEntry = NULL;
PLDR_DATA_TABLE_ENTRY pModule = NULL;
PLIST_ENTRY pCurrentListEntry = NULL;
pLdr = (PLDR_DATA_TABLE_ENTRY)DriverObject->DriverSection;
pListEntry = pLdr->InLoadOrderLinks.Flink;
pCurrentListEntry = pListEntry->Flink;
while (pCurrentListEntry != pListEntry){
pModule = CONTAINING_RECORD(pCurrentListEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
if (pModule->BaseDllName.Buffer != 0)
DbgPrint("基址:%p ---> 偏移:%p ---> 结束地址:%p---> 模块名:%wZ \r\n",pModule->DllBase, pModule->SizeOfImages - (LONGLONG)pModule->DllBase,(LONGLONG)pModule->DllBase + pModule->SizeOfImages,pModule->BaseDllName);
pCurrentListEntry = pCurrentListEntry->Flink;
}
DriverObject->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}

进线程对象监控

ObRegisterCallbacks

原型如下:

1
2
3
4
5
6
7
8
NTSTATUS ObRegisterCallbacks(
[in]  POB_CALLBACK_REGISTRATION CallbackRegistration,
[out] PVOID           *RegistrationHandle
);
OB_PREOP_CALLBACK_STATUS MyLySharkComObjectCallBack( //回调函数原型
PVOID RegistrationContext, //用于标注上下文
POB_PRE_OPERATION_INFORMATION OperationInformation //用于标注进线程创建的信息结构体
);

ObRegisterCallbacks基础用法如下:

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
#include <ntddk.h>
#include <ntstrsafe.h>
PVOID Globle_Object_Handle;
OB_PREOP_CALLBACK_STATUS MyObjectCallBack(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION OperationInformation){ //回调函数
return STATUS_SUCCESS;
}
VOID UnDriver(PDRIVER_OBJECT driver){ //回调卸载
ObUnRegisterCallbacks(Globle_Object_Handle);
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath){
OB_OPERATION_REGISTRATION Base;              // 回调函数结构体(你所填的结构都在这里)
OB_CALLBACK_REGISTRATION CallbackReg;
CallbackReg.RegistrationContext = NULL;          // 注册上下文(你回调函数返回参数)
CallbackReg.Version = OB_FLT_REGISTRATION_VERSION;    // 注册回调版本
CallbackReg.OperationRegistration = &Base;
CallbackReg.OperationRegistrationCount = 1;        // 操作计数(下钩数量)
RtlUnicodeStringInit(&CallbackReg.Altitude, L"600000");  // 长度
Base.ObjectType = PsProcessType;              // 进程操作类型.此处为进程操作
Base.Operations = OB_OPERATION_HANDLE_CREATE;       // 操作句柄创建
Base.PreOperation = MyObjectCallBack;           // 你自己的回调函数
Base.PostOperation = NULL;
if (ObRegisterCallbacks(&CallbackReg, &Globle_Object_Handle)) // 注册回调
DbgPrint("回调注册成功...");
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
#include <ntddk.h>
#include <wdm.h>
#include <ntstrsafe.h>
#define PROCESS_TERMINATE 1
PVOID Globle_Object_Handle;
NTKERNELAPI UCHAR * PsGetProcessImageFileName(__in PEPROCESS Process);
char* GetProcessImageNameByProcessID(ULONG ulProcessID){
NTSTATUS Status;
PEPROCESS EProcess = NULL;
Status = PsLookupProcessByProcessId((HANDLE)ulProcessID, &EProcess);
if (!NT_SUCCESS(Status))
return FALSE;
ObDereferenceObject(EProcess);
return (char*)PsGetProcessImageFileName(EProcess);
}
OB_PREOP_CALLBACK_STATUS MyObjectCallBack(PVOID RegistrationContext,POB_PRE_OPERATION_INFORMATION Operation){
char ProcName[256] = { 0 };
HANDLE pid = PsGetProcessId((PEPROCESS)Operation->Object);      // 取出当前调用函数的PID
strcpy(ProcName, GetProcessImageNameByProcessID((ULONG)pid));     // 通过PID取出进程名,然后直接拷贝内存
//DbgPrint("当前进程的名字是:%s", ProcName);
if (strstr(ProcName, "win32calc.exe"))
if (Operation->Operation == OB_OPERATION_HANDLE_CREATE)
if ((Operation->Parameters->CreateHandleInformation.OriginalDesiredAccess & PROCESS_TERMINATE) ==PROCESS_TERMINATE){
DbgPrint("你想结束进程?");
// 如果是计算器,则去掉它的结束权限,在Win10上无效
Operation->Parameters->CreateHandleInformation.DesiredAccess = ~THREAD_TERMINATE;
return STATUS_UNSUCCESSFUL;
}
return STATUS_SUCCESS;
}
VOID UnDriver(PDRIVER_OBJECT driver){
ObUnRegisterCallbacks(Globle_Object_Handle);
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath){
NTSTATUS obst = 0;
OB_CALLBACK_REGISTRATION obReg;
OB_OPERATION_REGISTRATION opReg;
memset(&obReg, 0, sizeof(obReg));
obReg.Version = ObGetFilterVersion();
obReg.OperationRegistrationCount = 1;
obReg.RegistrationContext = NULL;
RtlInitUnicodeString(&obReg.Altitude, L"321125");
obReg.OperationRegistration = &opReg;
memset(&opReg, 0, sizeof(opReg));
opReg.ObjectType = PsProcessType;
opReg.Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
opReg.PreOperation = (POB_PRE_OPERATION_CALLBACK)&MyObjectCallBack;
obst = ObRegisterCallbacks(&obReg, &Globle_Object_Handle);
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
// 线程回调
OB_PREOP_CALLBACK_STATUS MyThreadObjectCallBack(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION pOperationInformation) {
PEPROCESS ep;
PETHREAD et;
HANDLE pid;
// 线程过滤
if (pOperationInformation->ObjectType != *PsThreadType)
return OB_PREOP_SUCCESS;
et = (PETHREAD)pOperationInformation->Object;
ep = IoThreadToProcess(et);
pid = PsGetProcessId(ep);
// DbgPrint("线程PID= %ld | TID= %ld \n", pid, PsGetThreadId(et));
UNREFERENCED_PARAMETER(RegistrationContext);
if (CheckProcess(ep)) {
if (pOperationInformation->Operation == OB_OPERATION_HANDLE_CREATE) {
pOperationInformation->Parameters->CreateHandleInformation.DesiredAccess = 0;
if ((pOperationInformation->Parameters->CreateHandleInformation.OriginalDesiredAccess & THREAD_TERMINATE2) == THREAD_TERMINATE2) {
DbgPrint("拦截aaaaaa.exe进程内 %d 线程创建 \n", PsGetThreadId(et));
pOperationInformation->Parameters->CreateHandleInformation.DesiredAccess &= ~THREAD_TERMINATE2;
}
}
if (pOperationInformation->Operation == OB_OPERATION_HANDLE_DUPLICATE) {
pOperationInformation->Parameters->DuplicateHandleInformation.DesiredAccess = 0;
if ((pOperationInformation->Parameters->DuplicateHandleInformation.OriginalDesiredAccess & THREAD_TERMINATE2) == THREAD_TERMINATE2)
pOperationInformation->Parameters->DuplicateHandleInformation.DesiredAccess &= ~THREAD_TERMINATE2;
}
}
return OB_PREOP_SUCCESS;
}

进程创建监控

因为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
#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
#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
#include "Driver.h"
#include "NotifyRoutine.h"
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegPath){
NTSTATUS status = STATUS_SUCCESS;
pDriverObject->DriverUnload = DriverUnload;
for (ULONG i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
pDriverObject->MajorFunction[i] = DriverDefaultHandle;
// 编程方式绕过签名检查
BypassCheckSign(pDriverObject);
// 设置回调函数
SetProcessNotifyRoutine();
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
#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");
}
}

线程创建监控

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <ntddk.h>
NTKERNELAPI PCHAR PsGetProcessImageFileName(PEPROCESS Process);
NTKERNELAPI NTSTATUS PsLookupProcessByProcessId(HANDLE ProcessId, PEPROCESS *Process);
VOID MyCreateThreadNotify(HANDLE ProcessId, HANDLE ThreadId, BOOLEAN Create){
PEPROCESS eprocess = NULL;
PsLookupProcessByProcessId(ProcessId, &eprocess);         // 通过此函数拿到程序的EPROCESS结构
if (Create)
DbgPrint("线程TID: %1d --> 所属进程名: %s --> 进程PID: %1d \n", ThreadId,PsGetProcessImageFileName(eprocess), PsGetProcessId(eprocess));
else
DbgPrint("%s 线程已退出...", ThreadId);
}
VOID UnDriver(PDRIVER_OBJECT driver){
PsRemoveCreateThreadNotifyRoutine(MyCreateThreadNotify);
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath){
NTSTATUS status;
status = PsSetCreateThreadNotifyRoutine(MyCreateThreadNotify);
DbgPrint("PsSetCreateThreadNotifyRoutine: %x", status);
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}

反进程创建监控

基本原理

系统设置的进程创建回调函数会存储在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
#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
#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
#include "EnumRemove.h"
#include "Driver.h"
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegPath){
NTSTATUS status = STATUS_SUCCESS;
pDriverObject->DriverUnload = DriverUnload;
for (ULONG i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
pDriverObject->MajorFunction[i] = DriverDefaultHandle;
// 遍历回调
EnumNotifyRoutine();
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
#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
#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
#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
#include "EnumRemove.h"
#include "Driver.h"
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegPath){
NTSTATUS status = STATUS_SUCCESS;
pDriverObject->DriverUnload = DriverUnload;
for (ULONG i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
pDriverObject->MajorFunction[i] = DriverDefaultHandle;
// 遍历回调
EnumNotifyRoutine();
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
#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;
}

进程模块加载监控

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
#include <ntddk.h>
#include <ntimage.h>
PVOID GetDriverEntryByImageBase(PVOID ImageBase){
PIMAGE_DOS_HEADER pDOSHeader;
PIMAGE_NT_HEADERS64 pNTHeader;
PVOID pEntryPoint;
pDOSHeader = (PIMAGE_DOS_HEADER)ImageBase;
pNTHeader = (PIMAGE_NT_HEADERS64)((ULONG64)ImageBase + pDOSHeader->e_lfanew);
pEntryPoint = (PVOID)((ULONG64)ImageBase + pNTHeader->OptionalHeader.AddressOfEntryPoint);
return pEntryPoint;
}
VOID MyLoadImageNotifyRoutine(PUNICODE_STRING FullImageName,HANDLE ProcessId,PIMAGE_INFO ImageInfo){
PVOID pDrvEntry;
if (FullImageName != NULL && MmIsAddressValid(FullImageName)) // MmIsAddress验证地址可用性
if (ProcessId == 0){
pDrvEntry = GetDriverEntryByImageBase(ImageInfo->ImageBase);
DbgPrint("模块名称:%wZ --> 装载基址:%p --> 镜像长度: %d", FullImageName, pDrvEntry,ImageInfo->ImageSize);
}
}
VOID UnDriver(PDRIVER_OBJECT driver){
PsRemoveLoadImageNotifyRoutine((PLOAD_IMAGE_NOTIFY_ROUTINE)MyLoadImageNotifyRoutine);
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath){
PsSetLoadImageNotifyRoutine((PLOAD_IMAGE_NOTIFY_ROUTINE)MyLoadImageNotifyRoutine);
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}

改一下可查找指定模块:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
VOID UnicodeToChar(PUNICODE_STRING dst, char *src){
ANSI_STRING string;
RtlUnicodeStringToAnsiString(&string, dst, TRUE);
strcpy(src, string.Buffer);
RtlFreeAnsiString(&string);
}
VOID MyLoadImageNotifyRoutine(PUNICODE_STRING FullImageName,HANDLE ModuleStyle,PIMAGE_INFO ImageInfo){
PVOID pDrvEntry;
char szFullImageName[256] = { 0 };
if (FullImageName != NULL && MmIsAddressValid(FullImageName)) // MmIsAddress验证地址可用性
if (ModuleStyle == 0){  // ModuleStyle为零表示加载sys非零表示加载DLL
pDrvEntry = GetDriverEntryByImageBase(ImageInfo->ImageBase);
UnicodeToChar(FullImageName, szFullImageName);
if (strstr(_strlwr(szFullImageName), "hook.sys"))
DbgPrint("准备拦截SYS内核模块:%s", _strlwr(szFullImageName));
}
}

为了屏蔽某SYS模块,可在驱动头部写入ret返回指令,以屏蔽加载特定驱动文件。

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
#include <ntddk.h>
#include <intrin.h>
#include <ntimage.h>
PVOID GetDriverEntryByImageBase(PVOID ImageBase){
PIMAGE_DOS_HEADER pDOSHeader;
PIMAGE_NT_HEADERS64 pNTHeader;
PVOID pEntryPoint;
pDOSHeader = (PIMAGE_DOS_HEADER)ImageBase;
pNTHeader = (PIMAGE_NT_HEADERS64)((ULONG64)ImageBase + pDOSHeader->e_lfanew);
pEntryPoint = (PVOID)((ULONG64)ImageBase + pNTHeader->OptionalHeader.AddressOfEntryPoint);
return pEntryPoint;
}
VOID UnicodeToChar(PUNICODE_STRING dst, char *src){
ANSI_STRING string;
RtlUnicodeStringToAnsiString(&string, dst, TRUE);
strcpy(src, string.Buffer);
RtlFreeAnsiString(&string);
}
// 使用开关写保护需要在 C/C++ 优化中启用内部函数
KIRQL WPOFFx64() {    // 关闭写保护
KIRQL irql = KeRaiseIrqlToDpcLevel();
UINT64 cr0 = __readcr0();
cr0 &= 0xfffffffffffeffff;
_disable();
__writecr0(cr0);
return irql;
}
void WPONx64(KIRQL irql){ // 开启写保护
UINT64 cr0 = __readcr0();
cr0 |= 0x10000;
_enable();
__writecr0(cr0);
KeLowerIrql(irql);
}
BOOLEAN DenyLoadDriver(PVOID DriverEntry){
UCHAR fuck[] = "\xB8\x22\x00\x00\xC0\xC3";
KIRQL kirql;
/* 在模块开头写入以下汇编指令
Mov eax,c0000022h
ret
*/
if (DriverEntry == NULL)
return FALSE;
kirql = WPOFFx64();
memcpy(DriverEntry, fuck,sizeof(fuck) / sizeof(fuck[0]));
WPONx64(kirql);
return TRUE;
}
VOID MyLoadImageNotifyRoutine(PUNICODE_STRING FullImageName, HANDLE ModuleStyle, PIMAGE_INFO ImageInfo){
PVOID pDrvEntry;
char szFullImageName[256] = { 0 };
if (FullImageName != NULL && MmIsAddressValid(FullImageName)) // MmIsAddress验证地址可用性
if (ModuleStyle == 0) { // ModuleStyle为零表示加载sys非零表示加载DLL
pDrvEntry = GetDriverEntryByImageBase(ImageInfo->ImageBase);
UnicodeToChar(FullImageName, szFullImageName);
if (strstr(_strlwr(szFullImageName), "hook.sys")){
DbgPrint("拦截SYS内核模块:%s", szFullImageName);
DenyLoadDriver(pDrvEntry);
}
}
}
VOID UnDriver(PDRIVER_OBJECT driver){
PsRemoveLoadImageNotifyRoutine((PLOAD_IMAGE_NOTIFY_ROUTINE)MyLoadImageNotifyRoutine);
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath){
PsSetLoadImageNotifyRoutine((PLOAD_IMAGE_NOTIFY_ROUTINE)MyLoadImageNotifyRoutine);
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}

另一种写法,拦截DLL:

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
char *UnicodeToLongString(PUNICODE_STRING uString){
ANSI_STRING asStr;
char *Buffer = NULL;;
RtlUnicodeStringToAnsiString(&asStr, uString, TRUE);
Buffer = ExAllocatePoolWithTag(NonPagedPool, uString->MaximumLength *
sizeof(wchar_t), 0);
if (Buffer == NULL)
return NULL;
RtlCopyMemory(Buffer, asStr.Buffer, asStr.Length);
return Buffer;
}
VOID MyLoadImageNotifyRoutine(PUNICODE_STRING FullImageName, HANDLE ModuleStyle, PIMAGE_INFO ImageInfo){
PVOID pDrvEntry;
char *PareString = NULL;
if (MmIsAddressValid(FullImageName))
if (ModuleStyle != 0){  // 非零则监控DLL加载
PareString = UnicodeToLongString(FullImageName);
if (PareString != NULL)
if (strstr(PareString, "hook.dll")){
pDrvEntry = GetDriverEntryByImageBase(ImageInfo->ImageBase);
if (pDrvEntry != NULL)
DenyLoadDriver(pDrvEntry);
}
}
}