监控进程的启动与退出可以使用 PsSetCreateProcessNotifyRoutineEx
来创建回调,当新进程产生时,回调函数会被率先执行,然后执行我们自己的MyCreateProcessNotifyEx
函数,并在内部进行打印输出。
PsSetCreateProcessNotifyRoutineEx 是 Windows
内核提供的函数,用于注册一个回调函数,以便在新进程创建时进行通知。该函数位于 ntddk.h
头文件中,函数原型如下:
NTSTATUS PsSetCreateProcessNotifyRoutineEx(
PCREATE_PROCESS_NOTIFY_ROUTINE_EX NotifyRoutine,
BOOLEAN Remove
);
其中,PCREATE_PROCESS_NOTIFY_ROUTINE_EX
是回调函数类型,定义如下:
typedef VOID (*PCREATE_PROCESS_NOTIFY_ROUTINE_EX)(
PEPROCESS Process,
HANDLE ProcessId,
PPS_CREATE_NOTIFY_INFO_EX CreateInfo
);
在调用 PsSetCreateProcessNotifyRoutineEx
注册回调函数后,每当有新进程创建时,系统会调用我们自定义的回调函数 NotifyRoutine
,并传递相关参数,包括新创建的进程的 PEPROCESS
结构体指针、进程 ID (ProcessId)
和进程创建信息 (CreateInfo)。
在回调函数内部,我们可以进行自定义的操作,例如打印输出、记录日志、对进程进行监控等。注意,在回调函数中执行的操作应尽量保持简洁和高效,避免对系统性能和稳定性产生过大的影响。
#include <ntddk.h>
NTKERNELAPI PCHAR PsGetProcessImageFileName(PEPROCESS Process);
NTKERNELAPI NTSTATUS PsLookupProcessByProcessId(HANDLE ProcessId, PEPROCESS *Process);
PCHAR GetProcessNameByProcessId(HANDLE ProcessId)
{
NTSTATUS st = STATUS_UNSUCCESSFUL;
PEPROCESS ProcessObj = NULL;
PCHAR string = NULL;
st = PsLookupProcessByProcessId(ProcessId, &ProcessObj);
if (NT_SUCCESS(st))
{
string = PsGetProcessImageFileName(ProcessObj);
ObfDereferenceObject(ProcessObj);
}
return string;
}
VOID MyCreateProcessNotifyEx(PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo)
{
char ProcName[16] = { 0 };
if (CreateInfo != NULL)
{
strcpy(ProcName, PsGetProcessImageFileName(Process));
DbgPrint("父进程ID: %ld --->父进程名: %s --->进程名: %s---->进程路径:%wZ", CreateInfo->ParentProcessId,
GetProcessNameByProcessId(CreateInfo->ParentProcessId),
PsGetProcessImageFileName(Process),CreateInfo->ImageFileName);
}
else
{
strcpy(ProcName, PsGetProcessImageFileName(Process));
DbgPrint("进程[ %s ] 离开了,程序被关闭了",ProcName);
}
}
VOID UnDriver(PDRIVER_OBJECT driver)
{
PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)MyCreateProcessNotifyEx, TRUE);
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
NTSTATUS status;
status = PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)MyCreateProcessNotifyEx, FALSE);
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}
在上方代码基础上进行一定的改进,思路:通过PsGetProcessImageFileName
即将PID转换为进程名,然后通过_stricmp
对比,如果发现是calc.exe
进程则拒绝执行,禁止特定服务的运行,实现代码如下:
#include <ntddk.h>
NTKERNELAPI PCHAR PsGetProcessImageFileName(PEPROCESS Process);
NTKERNELAPI NTSTATUS PsLookupProcessByProcessId(HANDLE ProcessId, PEPROCESS *Process);
PCHAR GetProcessNameByProcessId(HANDLE ProcessId)
{
NTSTATUS st = STATUS_UNSUCCESSFUL;
PEPROCESS ProcessObj = NULL;
PCHAR string = NULL;
st = PsLookupProcessByProcessId(ProcessId, &ProcessObj);
if (NT_SUCCESS(st))
{
string = PsGetProcessImageFileName(ProcessObj);
ObfDereferenceObject(ProcessObj);
}
return string;
}
VOID MyCreateProcessNotifyEx(PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo)
{
char ProcName[16] = { 0 };
if (CreateInfo != NULL)
{
strcpy(ProcName, PsGetProcessImageFileName(Process));
if (!_stricmp(ProcName, "calc.exe"))
{
CreateInfo->CreationStatus = STATUS_UNSUCCESSFUL;
}
}
}
VOID UnDriver(PDRIVER_OBJECT driver)
{
PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)MyCreateProcessNotifyEx, TRUE);
DbgPrint(("驱动卸载成功"));
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
NTSTATUS status;
status = PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)MyCreateProcessNotifyEx, FALSE);
Driver->DriverUnload = UnDriver;
DbgPrint("驱动加载成功!");
return STATUS_SUCCESS;
}
将上方代码编译,当我们加载驱动程序以后,再次打开C:\Windows\System32\calc.exe
计算器进程则提示无法打开,我们的驱动已经成功的拦截了本次的请求。
而检测线程操作与检测进程差不多,检测线程需要调用PsSetCreateThreadNotifyRoutine
创建回调函数,然后就可以检测线程的创建了。
使用 PsSetCreateThreadNotifyRoutine
函数创建回调函数来实现。该函数位于 Windows
内核中,并用于注册一个回调函数,以便在新线程创建时进行通知。函数原型如下:
NTSTATUS PsSetCreateThreadNotifyRoutine(
PCREATE_THREAD_NOTIFY_ROUTINE NotifyRoutine
);
其中,PCREATE_THREAD_NOTIFY_ROUTINE
是回调函数类型,定义如下:
typedef VOID (*PCREATE_THREAD_NOTIFY_ROUTINE)(
HANDLE ProcessId,
HANDLE ThreadId,
BOOLEAN Create
);
调用 PsSetCreateThreadNotifyRoutine
注册回调函数后,每当有新线程创建时,系统会调用我们自定义的回调函数 NotifyRoutine
,并传递相关参数,包括创建线程的进程 ID (ProcessId)
、线程 ID (ThreadId)
和一个布尔值 (Create)
,表示线程是创建还是销毁。
具体代码如下:
#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);
DbgPrint(("驱动卸载成功"));
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
NTSTATUS status;
status = PsSetCreateThreadNotifyRoutine(MyCreateThreadNotify);
DbgPrint("PsSetCreateThreadNotifyRoutine: %x", status);
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}