PsSetCreateProcessNotifyRoutine函数用来注册一个进程创建的回调函数,当有新从进程被创建时,就把父进程的ID,和子进程(被创建的进程)ID传给回调函数,通过回调函数,可以监控新创建的进程
NTSTATUS PsSetCreateProcessNotifyRoutine(
_In_ PCREATE_PROCESS_NOTIFY_ROUTINE NotifyRoutine,
_In_ BOOLEAN Remove
);
想要实现的功能:新进程被系统创建时,打印出父进程和子进程的名字
在回调函数中可以直接获取两个进程的PID,但是要想通过PID得到进程名字,也有很多种方式。
但最简单的是还是通过PsLookupProcessByProcessId
函数来根据PID获取EPROCESS
结构体,然后可以直接通过结构体+偏移的方式读取到该进程的名字。
需要注意的是,在使用PsLookupProcessByProcessId
得到一个 EPROCESS
结构以后,系统会对该进程的引用计数累加1,需要在不使用该结构时,及时调用ObDereferenceObject
解除引用计数
也可以使用PsGetProcessImageFileName
函数来获取,其内部实现原理也是通过EPROCESS+偏移
的方式得到的进程名字
进程名字在EPROCESS
结构体偏移0x16C
的位置,可能不同的系统版本,偏移会有区别。
再来看PsGetProcessImageFileName
函数的反汇编,是不是也贼简单:
效果:
完整代码:
extern NTSYSAPI PUCHAR NTAPI PsGetProcessImageFileName(PEPROCESS Process);
// 监控进程创建回调函数
VOID CreateProcessNotify(IN HANDLE ParentId, IN HANDLE ChildId, IN BOOLEAN Create)
{
PEPROCESS ParentEprocess = NULL;
PEPROCESS ChildEprocess = NULL;
NTSTATUS status;
if (Create)
{
// 获取EPROCESS结构体
status = PsLookupProcessByProcessId(ParentId, &ParentEprocess);
if (!NT_SUCCESS(status))
{
KdPrint(("Get Parent Eprocess Failed\n"));
return;
}
status = PsLookupProcessByProcessId(ChildId, &ChildEprocess);
if (!NT_SUCCESS(status))
{
KdPrint(("Get Child Eprocess Failed\n"));
return;
}
// 通过EPROCESS获取进程名
KdPrint((
"ParentName:%s---> ChildName:%s\n",
PsGetProcessImageFileName(ParentEprocess),
PsGetProcessImageFileName(ChildEprocess)
));
ObDereferenceObject(ParentEprocess);
ObDereferenceObject(ChildEprocess);
}
}
VOID DriverUnload(PDRIVER_OBJECT driver)
{
// 卸载时,移除回掉
PsSetCreateProcessNotifyRoutine(CreateProcessNotify, TRUE);
DbgPrint("first: Our driver is unloading...\r\n");
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING reg_path)
{
DbgBreakPoint();
PsSetCreateProcessNotifyRoutine(CreateProcessNotify, FALSE);
pDriver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}