PsSetCreateProcessNotifyRoutine takes two parameters:
NTSTATUSPsSetCreateProcessNotifyRoutine( // pointer to a function to be called when a process is spawned or terminatedPCREATE_PROCESS_NOTIFY_ROUTINE NotifyRoutine, // specifies whether to subscribe or unsubscribe from this eventBOOLEAN Remove);
Below is a snippet that shows how the routine sCreateProcessNotifyRoutine (line 2) gets registered for new/terminated process notifications on line 24:
// handle incoming notifications about new/terminated processesvoidsCreateProcessNotifyRoutine(HANDLE ppid,HANDLE pid,BOOLEAN create){if (create) { PEPROCESS process =NULL; PUNICODE_STRING parentProcessName =NULL, processName =NULL;PsLookupProcessByProcessId(ppid,&process);SeLocateProcessImageName(process,&parentProcessName);PsLookupProcessByProcessId(pid,&process);SeLocateProcessImageName(process,&processName);DbgPrint("%d %wZ\n\t\t%d %wZ", ppid, parentProcessName, pid, processName); }else {DbgPrint("Process %d lost child %d", ppid, pid); }}// register sCreateProcessNotifyRoutine function to receive notifications about new/terminated processesPsSetCreateProcessNotifyRoutine(sCreateProcessNotifyRoutine, FALSE);
Below shows how the routine sCreateProcessNotifyRoutine gets executed when a new process hostname.exe (PID 2892) is spawned by powershell (PID 7176). Additionally, it shows that the process 7176 (hostname) terminated:
PsSetLoadImageNotifyRoutine
PsSetLoadImageNotifyRoutine only takes one parameter - a pointer to a function that will handle notifications about DLLs that processes running on the system loaded:
Below indicates that the routine sLoadImageNotifyRoutine is going to handle our notifications as registered with PsSetLoadImageNotifyRoutine on line 14:
// handle incoming notifications about module loadsvoidsLoadImageNotifyRoutine(PUNICODE_STRING imageName,HANDLE pid,PIMAGE_INFO imageInfo){UNREFERENCED_PARAMETER(imageInfo); PEPROCESS process =NULL; PUNICODE_STRING processName =NULL;PsLookupProcessByProcessId(pid,&process);SeLocateProcessImageName(process,&processName);DbgPrint("%wZ (%d) loaded %wZ", processName, pid, imageName);}// register sLoadImageNotifyRoutinefunction to receive notifications new DLLs being loaded to processesPsSetLoadImageNotifyRoutine(sLoadImageNotifyRoutine);
Testing the driver - once we open a notepad.exe, our driver gets notified about all the modules that notepad.exe loaded:
PsSetCreateThreadNotifyRoutine
PsSetCreateThreadNotifyRoutine only takes one parameter - a pointer to a function that will handle notifications about new or killed threads across all the system processes:
Below indicates that the routine sCreateThreadNotifyRoutine is going to handle our notifications as registered with PsSetCreateThreadNotifyRoutine on line 15:
// handle incoming notifications about new/terminated processesvoidsCreateThreadNotifyRoutine(HANDLE pid,HANDLE tid,BOOLEAN create){if (create) {DbgPrint("%d created thread %d", pid, tid); }else {DbgPrint("Thread %d of process %d exited", tid, pid); }}// register sCreateThreadNotifyRoutine to receive notifications about thread creation / terminationPsSetCreateThreadNotifyRoutine(sCreateThreadNotifyRoutine);
Testing the driver now, we can see we are indeed geting notified about new and terminated threads across processes on our system:
PsSetCreateProcessNotifyRoutineEx
PsSetCreateProcessNotifyRoutineEx takes two arguments:
NTSTATUSPsSetCreateProcessNotifyRoutineEx( // pointer to a function to be called when a process is spawned PCREATE_PROCESS_NOTIFY_ROUTINE_EX NotifyRoutine, // specifies whether to subscribe or unsubscribe from this eventBOOLEAN Remove);
Below is a snippet that shows how the routine sCreateProcessNotifyRoutineEx (line 3) gets registered for new process notifications on line 19. Processes with commandline containing notepad in them will be killed by setting the createInfo.reationStatus member to STATUS_ACCESS_DENIED (line 13):
// handle incoming notifications about new/terminated processes and kill// processes that have "notepad" in their commandline argumentsvoidsCreateProcessNotifyRoutineEx(PEPROCESS process,HANDLE pid,PPS_CREATE_NOTIFY_INFO createInfo){UNREFERENCED_PARAMETER(process);UNREFERENCED_PARAMETER(pid);if (createInfo !=NULL) {if (wcsstr(createInfo->CommandLine->Buffer,L"notepad") !=NULL) {DbgPrint("[!] Access to launch notepad.exe was denied!");createInfo->CreationStatus = STATUS_ACCESS_DENIED; } }}// subscribe sCreateProcessNotifyRoutineEx to new / terminated process notificationsPsSetCreateProcessNotifyRoutineEx(sCreateProcessNotifyRoutineEx, FALSE);
If PsSetCreateProcessNotifyRoutineEx is not working in your driver, you will need to add a /integritycheck switch in your linker configuration
Below shows how an attempt to spawn notepad.exe is blocked by our driver:
Code
Belos is the full working driver code that registers all the callback routines mentioned above:
#include<Ntifs.h>#include<ntddk.h>#include<wdm.h>DRIVER_DISPATCH HandleCustomIOCTL;#defineIOCTL_SPOTLESSCTL_CODE(FILE_DEVICE_UNKNOWN,0x2049, METHOD_BUFFERED, FILE_ANY_ACCESS)UNICODE_STRING DEVICE_NAME =RTL_CONSTANT_STRING(L"\\Device\\SpotlessDevice");UNICODE_STRING DEVICE_SYMBOLIC_NAME =RTL_CONSTANT_STRING(L"\\??\\SpotlessDeviceLink");voidsCreateProcessNotifyRoutine(HANDLE ppid,HANDLE pid,BOOLEAN create){if (create) { PEPROCESS process =NULL; PUNICODE_STRING parentProcessName =NULL, processName =NULL;PsLookupProcessByProcessId(ppid,&process);SeLocateProcessImageName(process,&parentProcessName);PsLookupProcessByProcessId(pid,&process);SeLocateProcessImageName(process,&processName);DbgPrint("%d %wZ\n\t\t%d %wZ", ppid, parentProcessName, pid, processName); }else {DbgPrint("Process %d lost child %d", ppid, pid); }}voidsCreateProcessNotifyRoutineEx(PEPROCESS process,HANDLE pid,PPS_CREATE_NOTIFY_INFO createInfo){UNREFERENCED_PARAMETER(process);UNREFERENCED_PARAMETER(pid);if (createInfo !=NULL) {if (wcsstr(createInfo->CommandLine->Buffer,L"notepad") !=NULL) {DbgPrint("[!] Access to launch notepad.exe was denied!");createInfo->CreationStatus = STATUS_ACCESS_DENIED; } }}voidsLoadImageNotifyRoutine(PUNICODE_STRING imageName,HANDLE pid,PIMAGE_INFO imageInfo){UNREFERENCED_PARAMETER(imageInfo); PEPROCESS process =NULL; PUNICODE_STRING processName =NULL;PsLookupProcessByProcessId(pid,&process);SeLocateProcessImageName(process,&processName);DbgPrint("%wZ (%d) loaded %wZ", processName, pid, imageName);}voidsCreateThreadNotifyRoutine(HANDLE pid,HANDLE tid,BOOLEAN create){if (create) {DbgPrint("%d created thread %d", pid, tid); }else {DbgPrint("Thread %d of process %d exited", tid, pid); }}voidDriverUnload(PDRIVER_OBJECT dob){DbgPrint("Driver unloaded, deleting symbolic links and devices");IoDeleteDevice(dob->DeviceObject);IoDeleteSymbolicLink(&DEVICE_SYMBOLIC_NAME);PsSetCreateProcessNotifyRoutine(sCreateProcessNotifyRoutine, TRUE);PsRemoveLoadImageNotifyRoutine(sLoadImageNotifyRoutine);PsRemoveCreateThreadNotifyRoutine(sCreateThreadNotifyRoutine);PsSetCreateProcessNotifyRoutineEx(sCreateProcessNotifyRoutineEx, TRUE);}NTSTATUSHandleCustomIOCTL(PDEVICE_OBJECT DeviceObject,PIRP Irp){UNREFERENCED_PARAMETER(DeviceObject); PIO_STACK_LOCATION stackLocation =NULL; CHAR *messageFromKernel ="ohai from them kernelz"; stackLocation =IoGetCurrentIrpStackLocation(Irp);if (stackLocation->Parameters.DeviceIoControl.IoControlCode == IOCTL_SPOTLESS) {DbgPrint("IOCTL_SPOTLESS (0x%x) issued",stackLocation->Parameters.DeviceIoControl.IoControlCode);DbgPrint("Input received from userland: %s", (char*)Irp->AssociatedIrp.SystemBuffer); }Irp->IoStatus.Information =strlen(messageFromKernel);Irp->IoStatus.Status = STATUS_SUCCESS;DbgPrint("Sending to userland: %s", messageFromKernel);RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, messageFromKernel,strlen(Irp->AssociatedIrp.SystemBuffer));IoCompleteRequest(Irp, IO_NO_INCREMENT);return STATUS_SUCCESS;}NTSTATUSMajorFunctions(PDEVICE_OBJECT DeviceObject,PIRP Irp){UNREFERENCED_PARAMETER(DeviceObject); PIO_STACK_LOCATION stackLocation =NULL; stackLocation =IoGetCurrentIrpStackLocation(Irp);switch (stackLocation->MajorFunction) {case IRP_MJ_CREATE:DbgPrint("Handle to symbolink link %wZ opened", DEVICE_SYMBOLIC_NAME);break;case IRP_MJ_CLOSE:DbgPrint("Handle to symbolink link %wZ closed", DEVICE_SYMBOLIC_NAME);break;default:break; }Irp->IoStatus.Information =0;Irp->IoStatus.Status = STATUS_SUCCESS;IoCompleteRequest(Irp, IO_NO_INCREMENT);return STATUS_SUCCESS;}NTSTATUSDriverEntry(PDRIVER_OBJECT DriverObject,PUNICODE_STRING RegistryPath) {UNREFERENCED_PARAMETER(DriverObject);UNREFERENCED_PARAMETER(RegistryPath); NTSTATUS status =0; // routine that will execute when our driver is unloaded/service is stoppedDriverObject->DriverUnload = DriverUnload; // routine for handling IO requests from userlandDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = HandleCustomIOCTL; // routines that will execute once a handle to our device's symbolik link is opened/closedDriverObject->MajorFunction[IRP_MJ_CREATE] = MajorFunctions;DriverObject->MajorFunction[IRP_MJ_CLOSE] = MajorFunctions;DbgPrint("Driver loaded"); // subscribe to notificationsPsSetCreateProcessNotifyRoutine(sCreateProcessNotifyRoutine, FALSE);PsSetLoadImageNotifyRoutine(sLoadImageNotifyRoutine);PsSetCreateThreadNotifyRoutine(sCreateThreadNotifyRoutine);PsSetCreateProcessNotifyRoutineEx(sCreateProcessNotifyRoutineEx, FALSE);DbgPrint("Listeners isntalled..");IoCreateDevice(DriverObject,0,&DEVICE_NAME, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE,&DriverObject->DeviceObject);if (!NT_SUCCESS(status)) {DbgPrint("Could not create device %wZ", DEVICE_NAME); }else {DbgPrint("Device %wZ created", DEVICE_NAME); } status =IoCreateSymbolicLink(&DEVICE_SYMBOLIC_NAME,&DEVICE_NAME);if (NT_SUCCESS(status)) {DbgPrint("Symbolic link %wZ created", DEVICE_SYMBOLIC_NAME); }else {DbgPrint("Error creating symbolic link %wZ", DEVICE_SYMBOLIC_NAME); }return STATUS_SUCCESS;}