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