#include "callbacks.h" #include "process.h" #include "thread.h" #include "token.h" #include "registry.h" #include "minifilter.h" PAGED_FILE(); #define MAX_PATH_LENGTH 100 PVOID ProcessRegistrationHandle = NULL; PVOID ThreadRegistrationHandle = NULL; LARGE_INTEGER Cookie; ULONG g_ServicePID = 0; // // Registering callbacks for log collection // _IRQL_requires_max_(PASSIVE_LEVEL) NTSTATUS RegisterCallbacks( _In_ PDRIVER_OBJECT DriverObject, _In_ PDEVICE_OBJECT DeviceObject ) { UNREFERENCED_PARAMETER(DeviceObject); UNREFERENCED_PARAMETER(DriverObject); PAGED_CODE(); NTSTATUS status = STATUS_SUCCESS; status = PsSetCreateProcessNotifyRoutineEx(CreateProcessNotifyRoutineEx, FALSE); if (!NT_SUCCESS(status)) { DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "Failed to load PsSetCreateProcessNotifyRoutineEx : 0x%X\n", status); return status; } DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "PsSetCreateProcessNotifyRoutineEx Loaded\n"); status = PsSetCreateProcessNotifyRoutine(TerminateProcessNotifyRoutine, FALSE); if (!NT_SUCCESS(status)) { DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "Failed to load PsSetCreateProcessNotifyRoutine : 0x%X\n", status); return status; } DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "PsSetCreateProcessNotifyRoutine Loaded\n"); status = PsSetCreateThreadNotifyRoutine(PsCreateThreadNotifyRoutine); if (!NT_SUCCESS(status)) { DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "Failed to load PsSetCreateThreadNotifyRoutine : 0x%X\n", status); return status; } DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "PsSetCreateThreadNotifyRoutine Loaded\n"); status = PsSetLoadImageNotifyRoutine(LoadImageRoutine); if (!NT_SUCCESS(status)) { DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "Failed to load PsSetLoadImageNotifyRoutine : 0x%X\n", status); return status; } DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "PsSetLoadImageNotifyRoutine Loaded\n"); UNICODE_STRING Altitude; RtlInitUnicodeString(&Altitude, L"385202"); // //Setting up callback for PsProcessType // OB_CALLBACK_REGISTRATION CallbackRegistration; OB_OPERATION_REGISTRATION OperationRegistration; OperationRegistration.ObjectType = PsProcessType; OperationRegistration.Operations = OB_OPERATION_HANDLE_DUPLICATE | OB_OPERATION_HANDLE_CREATE; OperationRegistration.PreOperation = NULL; OperationRegistration.PostOperation = PostProcessHandleCallback; // // Setting members // CallbackRegistration.Version = OB_FLT_REGISTRATION_VERSION; CallbackRegistration.OperationRegistrationCount = 1; CallbackRegistration.Altitude = Altitude; CallbackRegistration.RegistrationContext = NULL; CallbackRegistration.OperationRegistration = &OperationRegistration; status = ObRegisterCallbacks(&CallbackRegistration, &ProcessRegistrationHandle); if (!NT_SUCCESS(status)) { DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "Failed to load ObRegisterCallbacks : 0x%X\n", status); return status; } DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "ObRegisterCallbacks Loaded\n"); status = FltCallbackStart(DriverObject); if (!NT_SUCCESS(status)) { DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "Failed to load FltCallbackStart : 0x%X\n", status); return status; } DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "FltCallbackStart Loaded\n"); status = CmRegisterCallbackEx(RegistryCallback, &Altitude, DriverObject, NULL, &Cookie, NULL); if (!NT_SUCCESS(status)) { DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "Failed to load CmRegisterCallbackEx : 0x%X\n", status); return status; } DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "CmRegisterCallbackEx Loaded\n"); return status; } // [DONE] // LoadImage callback worker thread. This function will perform the appropriate processing and then terminate. // _IRQL_requires_max_(PASSIVE_LEVEL) VOID LoadImageWorkerThread( _In_ PVOID StartContext ) { PLOAD_IMAGE_CALLBACK_INFO callbackInfo = NULL; UNICODE_STRING imagePath{ 0 }; UNICODE_STRING sourceFullUserName{}; NTSTATUS status; ULONG systemModeImage; PAGED_CODE(); callbackInfo = (PLOAD_IMAGE_CALLBACK_INFO)StartContext; FILETIME filetime = callbackInfo->FileTime; HANDLE sourcePID = callbackInfo->SourceProcessId; ULONGLONG uSourcePID = HandleToULong(sourcePID); ULONGLONG sourceThreadID = HandleToULong(callbackInfo->SourceThread); systemModeImage = callbackInfo->SystemModeImage; PEPROCESS sourceProcess; if (systemModeImage == 1) { EventWriteDriverLoad(NULL, &filetime, callbackInfo->ModuleName.Buffer); goto Exit; } status = PsLookupProcessByProcessId(sourcePID, &sourceProcess); if (!NT_SUCCESS(status)) { goto Exit; } ULONGLONG sourceProcessStartKey = PsGetProcessStartKey(sourceProcess); imagePath.Length = 0; imagePath.MaximumLength = MAX_ALLOC; imagePath.Buffer = (PWSTR)ExAllocatePool2(POOL_FLAG_PAGED, MAX_ALLOC, CALBACK_TAG); status = GetProcessImageName(sourcePID, &imagePath); if (!NT_SUCCESS(status) || imagePath.Buffer == NULL) { goto Exit; } sourceFullUserName.Length = 0; sourceFullUserName.MaximumLength = MAX_ALLOC; sourceFullUserName.Buffer = (PWCH)ExAllocatePool2(POOL_FLAG_PAGED, sourceFullUserName.MaximumLength, CALBACK_TAG); if (sourceFullUserName.Buffer == NULL) { goto Exit; } DWORD SourceAuthenticationId = 0; status = GetProcessUserName(&sourceFullUserName, sourcePID, &SourceAuthenticationId); if (!NT_SUCCESS(status)) { goto Exit; } EventWriteImageLoaded(NULL, &filetime, imagePath.Buffer, uSourcePID, sourceThreadID, sourceProcessStartKey, callbackInfo->ModuleName.Buffer, sourceFullUserName.Buffer, SourceAuthenticationId); Exit: if (sourceFullUserName.Buffer != NULL) { ExFreePoolWithTag(sourceFullUserName.Buffer, CALBACK_TAG); } if (imagePath.Buffer != NULL) { ExFreePoolWithTag(imagePath.Buffer, CALBACK_TAG); } if (callbackInfo->ModuleName.Buffer != NULL) { ExFreePoolWithTag(callbackInfo->ModuleName.Buffer, CALBACK_TAG); } if (callbackInfo != NULL) { ExFreePoolWithTag(callbackInfo, SYSTEM_THREAD_TAG); } PsTerminateSystemThread(STATUS_SUCCESS); } // [DONE] // LoadImage callback. Routine will capture when an image is loaded into a process and will create a worker thread to perform the appropriate processing. // _IRQL_requires_max_(PASSIVE_LEVEL) VOID LoadImageRoutine( _In_ PUNICODE_STRING FullImageName, _In_ HANDLE ProcessId, _In_ PIMAGE_INFO ImageInfo ) { NTSTATUS status; FILETIME fileTime; HANDLE hLoadImageThread = NULL; UNICODE_STRING imagePath{ 0 }; KeQuerySystemTime(&fileTime); PAGED_CODE(); auto callbackInfo = (PLOAD_IMAGE_CALLBACK_INFO)ExAllocatePool2(POOL_FLAG_PAGED, sizeof(LOAD_IMAGE_CALLBACK_INFO), SYSTEM_THREAD_TAG); if (callbackInfo == NULL) { goto Exit; } imagePath.Length = FullImageName->Length + sizeof(UNICODE_NULL); imagePath.MaximumLength = FullImageName->Length + sizeof(UNICODE_NULL); imagePath.Buffer = (PWSTR)ExAllocatePool2(POOL_FLAG_PAGED, imagePath.MaximumLength, CALBACK_TAG); if (imagePath.Buffer == NULL) { goto Exit; } // // Copy the image path into the callback info structure. // RtlCopyUnicodeString(&imagePath, FullImageName); // //null terminate the string // imagePath.Buffer[imagePath.Length / sizeof(UNICODE_NULL)] = UNICODE_NULL; // //setting the callback info structure // callbackInfo->ModuleName = imagePath; callbackInfo->FileTime = fileTime; callbackInfo->SourceProcessId = ProcessId; callbackInfo->SourceThread = NtCurrentThread(); callbackInfo->SourceEThread = PsGetCurrentThread(); callbackInfo->SystemModeImage = ImageInfo->SystemModeImage; OBJECT_ATTRIBUTES objectAttributes; InitializeObjectAttributes(&objectAttributes, NULL, OBJ_KERNEL_HANDLE, NULL, NULL); status = PsCreateSystemThread(&hLoadImageThread, THREAD_ALL_ACCESS, &objectAttributes, NULL, NULL, (PKSTART_ROUTINE)LoadImageWorkerThread, callbackInfo); if (!NT_SUCCESS(status)) { DbgPrint("PsCreateSystemThread failed: %x\n", status); ExFreePoolWithTag(imagePath.Buffer, CALBACK_TAG); ExFreePoolWithTag(callbackInfo, SYSTEM_THREAD_TAG); goto Exit; } Exit: if(hLoadImageThread != NULL) { ZwClose(hLoadImageThread); } return; } // [DONE] // Registry callback to capture registry actions and create a worker thread to perform the appropriate processing. // _IRQL_requires_max_(PASSIVE_LEVEL) NTSTATUS RegistryCallback( _In_ PVOID CallbackContext, _In_ PVOID RegNotifyClass, _In_ PVOID RegObject ) { NTSTATUS status = STATUS_SUCCESS; PCWSTR keyPath = NULL; HANDLE registryThreadHandle = NULL; REG_NOTIFY_CLASS notifyClass; notifyClass = (REG_NOTIFY_CLASS)(ULONG_PTR)RegNotifyClass; PAGED_CODE(); UNREFERENCED_PARAMETER(CallbackContext); if (RegObject == NULL) { DbgPrint("Callback RegObject is NULL. \n"); status = STATUS_UNSUCCESSFUL; goto Exit; } switch (notifyClass) { case RegNtPostCreateKeyEx: { PREG_POST_OPERATION_INFORMATION postObject = (PREG_POST_OPERATION_INFORMATION)RegObject; if (postObject->Status == STATUS_SUCCESS) { PREG_CREATE_KEY_INFORMATION_V1 info = (PREG_CREATE_KEY_INFORMATION_V1)postObject->PreInformation; if (*info->Disposition == REG_CREATED_NEW_KEY) { if (info->CompleteName->Buffer != NULL) { PCUNICODE_STRING registryPath = NULL; status = CmCallbackGetKeyObjectIDEx(&Cookie, info->RootObject, NULL, ®istryPath, 0); if (!NT_SUCCESS(status)) { DbgPrint("CmCallbackGetKeyObjectIDEx failed. Status 0x%x", status); goto Exit; } UNICODE_STRING regPath; regPath.Length = registryPath->Length + info->RemainingName->Length + sizeof(L"\\") + sizeof(UNICODE_NULL); regPath.MaximumLength = registryPath->Length + info->RemainingName->Length + sizeof(L"\\") + sizeof(UNICODE_NULL); regPath.Buffer = (PWSTR)ExAllocatePool2(POOL_FLAG_PAGED, regPath.Length, REGISTRY_TAG); RtlCopyUnicodeString(®Path, registryPath); RtlAppendUnicodeToString(®Path, L"\\"); RtlAppendUnicodeStringToString(®Path, info->RemainingName); // //adding null terminator // regPath.Buffer[regPath.Length / sizeof(UNICODE_NULL)] = UNICODE_NULL; auto callbackInfo = (PREG_CREATE_KEY_CALLBACK_INFO)ExAllocatePool2(POOL_FLAG_PAGED, sizeof(REG_CREATE_KEY_CALLBACK_INFO), SYSTEM_THREAD_TAG); if (callbackInfo == NULL) { goto Exit; } callbackInfo->DesiredAccess = info->DesiredAccess; callbackInfo->ProcStartKey = PsGetProcessStartKey(PsGetCurrentProcess()); callbackInfo->SourceProcessId = PsGetCurrentProcessId(); callbackInfo->KeyPath = regPath; callbackInfo->SourceThread = PsGetCurrentThread(); callbackInfo->SourceThreadId = PsGetCurrentThreadId(); CmCallbackReleaseKeyObjectIDEx(registryPath); OBJECT_ATTRIBUTES objectAttributes; InitializeObjectAttributes(&objectAttributes, NULL, OBJ_KERNEL_HANDLE, NULL, NULL); status = PsCreateSystemThread(®istryThreadHandle, THREAD_ALL_ACCESS, &objectAttributes, NULL, NULL, (PKSTART_ROUTINE)CreateKey, callbackInfo); if (!NT_SUCCESS(status)) { DbgPrint("PsCreateSystemThread failed: %x\n", status); ExFreePoolWithTag(regPath.Buffer, REGISTRY_TAG); ExFreePoolWithTag(callbackInfo, SYSTEM_THREAD_TAG); goto Exit; } goto Exit; } } goto Exit; } goto Exit; break; } case RegNtPostSaveKey: { PREG_POST_OPERATION_INFORMATION postObject = (PREG_POST_OPERATION_INFORMATION)RegObject; if (postObject->Status == STATUS_SUCCESS) { SaveKey(CallbackContext, (PREG_SAVE_KEY_INFORMATION)postObject->PreInformation); } //goto Exit; break; } case RegNtPreDeleteKey: { PREG_DELETE_KEY_INFORMATION DeleteObject = (PREG_DELETE_KEY_INFORMATION)RegObject; if (DeleteObject->Object != NULL) { DeleteKey(CallbackContext, (PREG_DELETE_KEY_INFORMATION)RegObject); } //goto Exit; break; } case RegNtPostSetValueKey: { PREG_POST_OPERATION_INFORMATION postObject = (PREG_POST_OPERATION_INFORMATION)RegObject; if (postObject->Status != STATUS_SUCCESS) { goto Exit; } PREG_SET_VALUE_KEY_INFORMATION info = (PREG_SET_VALUE_KEY_INFORMATION)postObject->PreInformation; // // creating a copy of the value name, otherwise it will be cleared by the callback // if (info->ValueName == NULL || info->ValueName->Length == 0) { goto Exit; } status = GetRegistryKeyPath(info->Object, SYSTEM_THREAD_TAG, &keyPath); if (status != STATUS_SUCCESS || keyPath == NULL) { DbgPrint("[RegNtPostSetValueKey] - GetRegistryKeyPath failed. Status 0x%x", status); goto Exit; } // // Allocating memory for the callback info structure // auto callbackInfo = (PREG_SET_VALUE_CALLBACK_INFO)ExAllocatePool2(POOL_FLAG_PAGED, sizeof(REG_SET_VALUE_CALLBACK_INFO), SYSTEM_THREAD_TAG); if (callbackInfo == NULL) { goto Exit; } if (info->DataSize <= 0) { ExFreePoolWithTag((PVOID)keyPath, SYSTEM_THREAD_TAG); ExFreePoolWithTag(callbackInfo, SYSTEM_THREAD_TAG); goto Exit; } PVOID Data = ExAllocatePool2(POOL_FLAG_PAGED, info->DataSize, SYSTEM_THREAD_TAG); if (Data == NULL) { ExFreePoolWithTag((PVOID)keyPath, SYSTEM_THREAD_TAG); ExFreePoolWithTag(callbackInfo, SYSTEM_THREAD_TAG); goto Exit; } RtlCopyMemory(Data, info->Data, info->DataSize); UNICODE_STRING valueName; valueName.Length = info->ValueName->Length + sizeof(UNICODE_NULL); // Compensate for NULL terminator. valueName.MaximumLength = info->ValueName->Length + sizeof(UNICODE_NULL); valueName.Buffer = (PWSTR)ExAllocatePool2(POOL_FLAG_PAGED, valueName.Length, SYSTEM_THREAD_TAG); // Use valueName.Length here. if (valueName.Buffer == NULL || valueName.Length == 0) { ExFreePoolWithTag((PVOID)keyPath, SYSTEM_THREAD_TAG); ExFreePoolWithTag(callbackInfo, SYSTEM_THREAD_TAG); goto Exit; } // // Copy and NULL terminate the string. The Length member of the UNICODE_STRING doesn't compensate for the NULL terminator. // RtlCopyUnicodeString(&valueName, info->ValueName); valueName.Buffer[valueName.Length / sizeof(UNICODE_NULL)] = UNICODE_NULL; callbackInfo->Data = Data; callbackInfo->KeyPath = keyPath; callbackInfo->Type = info->Type; callbackInfo->ValueName = valueName; callbackInfo->SourceProcessId = PsGetCurrentProcessId(); callbackInfo->SourceThreadId = PsGetCurrentThreadId(); callbackInfo->SourceProcess = PsGetCurrentProcess(); callbackInfo->SourceThread = PsGetCurrentThread(); callbackInfo->DataSize = info->DataSize; OBJECT_ATTRIBUTES objectAttributes; InitializeObjectAttributes(&objectAttributes, NULL, OBJ_KERNEL_HANDLE, NULL, NULL); status = PsCreateSystemThread(®istryThreadHandle, GENERIC_ALL, &objectAttributes, NULL, NULL, (PKSTART_ROUTINE)SendSetValueRegistryInfo, callbackInfo); if (!NT_SUCCESS(status)) { DbgPrint("[RegNtPostSetValueKey] - PsCreateSystemThread failed. Status 0x%x", status); ExFreePoolWithTag((PVOID)keyPath, SYSTEM_THREAD_TAG); ExFreePoolWithTag(callbackInfo, SYSTEM_THREAD_TAG); ExFreePoolWithTag(valueName.Buffer, SYSTEM_THREAD_TAG); //goto Exit; } //goto Exit; break; } default: { break; } } Exit: if (registryThreadHandle != NULL) { ZwClose(registryThreadHandle); } return status; } // [DONE] // Thread Creation routine that captures remote thread creation // _IRQL_requires_max_(PASSIVE_LEVEL) void PsCreateThreadNotifyRoutine( _In_ HANDLE ProcessId, _In_ HANDLE ThreadId, _In_ BOOLEAN Create ) { NTSTATUS status; PEPROCESS sourceProcess; PEPROCESS targetProcess; UNICODE_STRING sourceImage = { 0 }; UNICODE_STRING sourceUserName = { 0 }; UNICODE_STRING targetImage = { 0 }; UNICODE_STRING targetUserName = { 0 }; UNICODE_STRING sIntegrityLevel = { 0 }; UNICODE_STRING tIntegrityLevel = { 0 }; DWORD sourceAuthenticationId, targetAuthenticationId = 0; HANDLE tToken = NULL; HANDLE sToken = NULL; PAGED_CODE(); //Checking for thread creation if (Create != TRUE) { goto Exit; } HANDLE CurrentPID = PsGetCurrentProcessId(); if (CurrentPID == ProcessId) { goto Exit; } if (CurrentPID == (HANDLE)0x4) { goto Exit; } if (ProcessId == (HANDLE)0x4) { goto Exit; } HANDLE sourceThreadId = PsGetCurrentThreadId(); status = PsLookupProcessByProcessId(ProcessId, &targetProcess); if (status != STATUS_SUCCESS) { DbgPrint("Failed to get target process, status: %d", status); return; } status = PsLookupProcessByProcessId(CurrentPID, &sourceProcess); if (status != STATUS_SUCCESS) { DbgPrint("Failed to get source process, status: %d", status); return; } ULONGLONG sourceProcStartKey = PsGetProcessStartKey(sourceProcess); ULONGLONG targetProcStartKey = PsGetProcessStartKey(targetProcess); //Source Process Information sourceImage.Length = 0; sourceImage.MaximumLength = MAX_ALLOC; sourceImage.Buffer = (PWSTR)ExAllocatePool2(POOL_FLAG_PAGED, MAX_ALLOC, CALBACK_TAG); status = GetProcessImageName(CurrentPID, &sourceImage); if (status != STATUS_SUCCESS) { DbgPrint("Error getting source image name: %d", status); sourceImage.Buffer = L"Unknown"; } sourceUserName.Length = 0; sourceUserName.MaximumLength = MAX_ALLOC; sourceUserName.Buffer = (PWSTR)ExAllocatePool2(POOL_FLAG_PAGED, MAX_ALLOC, CALBACK_TAG); status = GetProcessUserName(&sourceUserName, CurrentPID, &sourceAuthenticationId); if (status != STATUS_SUCCESS) { DbgPrint("Error getting source user name: %d", status); goto Exit; } if (sourceUserName.Length == 0) { goto Exit; } //Create unicode string that holds "SYSTEM" UNICODE_STRING SystemName; RtlInitUnicodeString(&SystemName, L"NT AUTHORITY\\SYSTEM"); if (RtlCompareUnicodeString(&sourceUserName, &SystemName, TRUE) == 0) { goto Exit; } status = GetProcessToken(CurrentPID, &sToken); if (status != STATUS_SUCCESS) { DbgPrint("Error getting source token: %d", status); sToken = NULL; } status = GetTokenIntegrityLevel(sToken, &sIntegrityLevel); if (status != STATUS_SUCCESS) { DbgPrint("Error getting source integrity level: %d", status); sIntegrityLevel.Buffer = L"Unknown"; } //Target Process Information targetImage.Length = 0; targetImage.MaximumLength = MAX_ALLOC; targetImage.Buffer = (PWSTR)ExAllocatePool2(POOL_FLAG_PAGED, MAX_ALLOC, CALBACK_TAG); status = GetProcessImageName(ProcessId, &targetImage); if (status != STATUS_SUCCESS) { goto Exit; } targetUserName.Length = 0; targetUserName.MaximumLength = MAX_ALLOC; targetUserName.Buffer = (PWSTR)ExAllocatePool2(POOL_FLAG_PAGED, MAX_ALLOC, CALBACK_TAG); status = GetProcessUserName(&targetUserName, ProcessId, &targetAuthenticationId); if (status != STATUS_SUCCESS) { DbgPrint("Error getting target user name: %d", status); goto Exit; } status = GetProcessToken(ProcessId, &tToken); if (status != STATUS_SUCCESS) { DbgPrint("Error getting target token: %d", status); tToken = NULL; } status = GetTokenIntegrityLevel(tToken, &tIntegrityLevel); if (status != STATUS_SUCCESS) { DbgPrint("Error getting target integrity level: %d", status); tIntegrityLevel.Buffer = L"Unknown"; } FILETIME filetime; KeQuerySystemTime(&filetime); EventWriteRemoteThreadCreation(NULL, &filetime, sourceImage.Buffer, reinterpret_cast(CurrentPID), reinterpret_cast(sourceThreadId), sourceProcStartKey, sourceUserName.Buffer, sourceAuthenticationId, sIntegrityLevel.Buffer, targetImage.Buffer, reinterpret_cast(ProcessId), targetProcStartKey, reinterpret_cast(ThreadId), targetUserName.Buffer, targetAuthenticationId, tIntegrityLevel.Buffer); Exit: if (sourceUserName.Buffer != NULL && sourceUserName.Length != 0) { ExFreePoolWithTag(sourceUserName.Buffer, CALBACK_TAG); } if (targetUserName.Buffer != NULL && targetUserName.Length != 0) { ExFreePoolWithTag(targetUserName.Buffer, CALBACK_TAG); } if (sourceImage.Buffer != NULL && sourceImage.Length != 0) { ExFreePoolWithTag(sourceImage.Buffer, CALBACK_TAG); } if (targetImage.Buffer != NULL && targetImage.Length != 0) { ExFreePoolWithTag(targetImage.Buffer, CALBACK_TAG); } if (sToken != NULL) { ZwClose(sToken); } if (tToken != NULL) { ZwClose(tToken); } } // [DONE] // Create Process Worker Thread // VOID CreateProcessWorkerThread( _In_ PVOID StartContext ) { PPROCESS_CREATE_CALLBACK_INFO callbackInfo = NULL; UNICODE_STRING processImagePath{}; UNICODE_STRING parentImagePath{}; UNICODE_STRING parentUserName{}; UNICODE_STRING creatorUserName{}; UNICODE_STRING creatorImagePath{}; UNICODE_STRING childUserName{}; DWORD parentAuthenticationId = 0; DWORD childAuthenticationId = 0; DWORD creatorAuthenticationId = 0; NTSTATUS status; PAGED_CODE(); callbackInfo = (PPROCESS_CREATE_CALLBACK_INFO)StartContext; HANDLE sourcePID = callbackInfo->ParentProcessId; HANDLE targetPID = callbackInfo->ProcessId; PEPROCESS targetProcess = callbackInfo->Process; FILETIME fileTime = callbackInfo->FileTime; CLIENT_ID creatorId = callbackInfo->CreatorId; ULONGLONG sourceThreadId = HandleToULong(creatorId.UniqueThread); ULONGLONG procStartKey = PsGetProcessStartKey(targetProcess); ULONGLONG procStartTime = PsGetProcessCreateTimeQuadPart(targetProcess); parentImagePath.Length = 0; parentImagePath.MaximumLength = MAX_ALLOC; parentImagePath.Buffer = (PWSTR)ExAllocatePool2(POOL_FLAG_PAGED, MAX_ALLOC, SYSTEM_THREAD_TAG); status = GetProcessImageName(sourcePID, &parentImagePath); if (status != STATUS_SUCCESS) { goto Exit; } parentUserName.Length = 0; parentUserName.MaximumLength = MAX_ALLOC; parentUserName.Buffer = (PWSTR)ExAllocatePool2(POOL_FLAG_PAGED, MAX_ALLOC, SYSTEM_THREAD_TAG); status = GetProcessUserName(&parentUserName, sourcePID, &parentAuthenticationId); if (status != STATUS_SUCCESS) { goto Exit; } processImagePath.Length = 0; processImagePath.MaximumLength = MAX_ALLOC; processImagePath.Buffer = (PWSTR)ExAllocatePool2(POOL_FLAG_PAGED, MAX_ALLOC, SYSTEM_THREAD_TAG); status = GetProcessImageName(targetPID, &processImagePath); if (status != STATUS_SUCCESS) { goto Exit; } childUserName.Length = 0; childUserName.MaximumLength = MAX_ALLOC; childUserName.Buffer = (PWSTR)ExAllocatePool2(POOL_FLAG_PAGED, MAX_ALLOC, SYSTEM_THREAD_TAG); status = GetProcessUserName(&childUserName, targetPID, &childAuthenticationId); if (status != STATUS_SUCCESS) { goto Exit; } ULONGLONG uParentPID = HandleToULong(sourcePID); ULONGLONG uTargetPID = HandleToULong(targetPID); ULONGLONG uCreatorPID = HandleToULong(creatorId.UniqueProcess); EventWriteProcessCreation(NULL, &fileTime, processImagePath.Buffer, callbackInfo->CommandLine.Buffer, uTargetPID, procStartKey, procStartTime, parentUserName.Buffer, childAuthenticationId, uParentPID, sourceThreadId, parentImagePath.Buffer, uCreatorPID, childUserName.Buffer, parentAuthenticationId); if (uParentPID != uCreatorPID) { creatorUserName.Length = 0; creatorUserName.MaximumLength = MAX_ALLOC; creatorUserName.Buffer = (PWSTR)ExAllocatePool2(POOL_FLAG_PAGED, MAX_ALLOC, SYSTEM_THREAD_TAG); status = GetProcessUserName(&creatorUserName, creatorId.UniqueProcess, &creatorAuthenticationId); if (status != STATUS_SUCCESS) { goto Exit; } creatorImagePath.Length = 0; creatorImagePath.MaximumLength = MAX_ALLOC; creatorImagePath.Buffer = (PWSTR)ExAllocatePool2(POOL_FLAG_PAGED, MAX_ALLOC, SYSTEM_THREAD_TAG); status = GetProcessImageName(creatorId.UniqueProcess, &creatorImagePath); if (status != STATUS_SUCCESS) { goto Exit; } EventWriteProcessReparenting(NULL, &fileTime, processImagePath.Buffer, callbackInfo->CommandLine.Buffer, uParentPID, sourceThreadId, uTargetPID, procStartKey, procStartTime, parentImagePath.Buffer, uCreatorPID, creatorImagePath.Buffer, creatorUserName.Buffer, parentUserName.Buffer, childUserName.Buffer, parentAuthenticationId, childAuthenticationId, creatorAuthenticationId); goto Exit; } Exit: if (processImagePath.Buffer != NULL) { ExFreePoolWithTag(processImagePath.Buffer, SYSTEM_THREAD_TAG); } if (parentImagePath.Buffer != NULL) { ExFreePoolWithTag(parentImagePath.Buffer, SYSTEM_THREAD_TAG); } if (parentUserName.Buffer != NULL) { ExFreePoolWithTag(parentUserName.Buffer, SYSTEM_THREAD_TAG); } if (childUserName.Buffer != NULL) { ExFreePoolWithTag(childUserName.Buffer, SYSTEM_THREAD_TAG); } if (creatorUserName.Buffer != NULL) { ExFreePoolWithTag(creatorUserName.Buffer, SYSTEM_THREAD_TAG); } if (creatorImagePath.Buffer != NULL) { ExFreePoolWithTag(creatorImagePath.Buffer, SYSTEM_THREAD_TAG); } if (callbackInfo->CommandLine.Buffer != NULL) { ExFreePoolWithTag(callbackInfo->CommandLine.Buffer, SYSTEM_THREAD_TAG); } if (callbackInfo != NULL) { ExFreePoolWithTag(callbackInfo, SYSTEM_THREAD_TAG); } PsTerminateSystemThread(STATUS_SUCCESS); } // // Callback routine to capture process creation events // _IRQL_requires_max_(PASSIVE_LEVEL) void CreateProcessNotifyRoutineEx( _In_ PEPROCESS Process, _In_ HANDLE ProcessId, _In_ PPS_CREATE_NOTIFY_INFO CreateInfo ) { NTSTATUS status; FILETIME fileTime; HANDLE hCreateProcessThread = NULL; UNICODE_STRING commandLine{ 0 }; PAGED_CODE(); if (CreateInfo == NULL) { goto Exit; } KeQuerySystemTime(&fileTime); auto callbackInfo = (PPROCESS_CREATE_CALLBACK_INFO)ExAllocatePool2(POOL_FLAG_PAGED, sizeof(PROCESS_CREATE_CALLBACK_INFO), SYSTEM_THREAD_TAG); if (callbackInfo == NULL) { return; } callbackInfo->ParentProcessId = CreateInfo->ParentProcessId; callbackInfo->FileTime = fileTime; callbackInfo->CreatorId = CreateInfo->CreatingThreadId; callbackInfo->ProcessId = ProcessId; callbackInfo->Process = Process; //callbackInfo->CommandLine = CreateInfo->CommandLine->Buffer; // //Checking to see if CommandLine is NULL and if it isn't, creating a buffer // if (CreateInfo->CommandLine != NULL) { // //create buffer // commandLine.Buffer = (PWSTR)ExAllocatePool2(POOL_FLAG_PAGED, CreateInfo->CommandLine->Length + sizeof(UNICODE_NULL), SYSTEM_THREAD_TAG); if (commandLine.Buffer == NULL) { ExFreePoolWithTag(commandLine.Buffer, SYSTEM_THREAD_TAG); ExFreePoolWithTag(callbackInfo, SYSTEM_THREAD_TAG); goto Exit; } // //Zero out the buffer // RtlZeroMemory(commandLine.Buffer, CreateInfo->CommandLine->Length + sizeof(UNICODE_NULL)); // //Copy the CommandLine into the buffer // RtlCopyMemory(commandLine.Buffer, CreateInfo->CommandLine->Buffer, CreateInfo->CommandLine->Length); // //Null terminate the buffer // commandLine.Buffer[CreateInfo->CommandLine->Length / sizeof(UNICODE_NULL)] = UNICODE_NULL; } else { commandLine.Buffer = L"NULL"; commandLine.Length = sizeof(L"NULL"); commandLine.MaximumLength = sizeof(L"NULL") + sizeof(UNICODE_NULL); } callbackInfo->CommandLine = commandLine; OBJECT_ATTRIBUTES objectAttributes; InitializeObjectAttributes(&objectAttributes, NULL, OBJ_KERNEL_HANDLE, NULL, NULL); status = PsCreateSystemThread(&hCreateProcessThread, THREAD_ALL_ACCESS, &objectAttributes, NULL, NULL, (PKSTART_ROUTINE)CreateProcessWorkerThread, callbackInfo); if (!NT_SUCCESS(status)) { DbgPrint("PsCreateSystemThread failed: %x\n", status); ExFreePoolWithTag(callbackInfo, SYSTEM_THREAD_TAG); return; } Exit: if (hCreateProcessThread != NULL) { ZwClose(hCreateProcessThread); } } // [DONE] // Post Handle (Open/Duplication) Worker Thread // VOID PostHandleWorkerThread(PVOID StartContext) { PHANDLE_CREATION_CALLBACK_INFO callbackInfo = NULL; UNICODE_STRING TargetImagePath{}, RequestorImagePath{}, SourceFullUserName{}; NTSTATUS status; PAGED_CODE(); callbackInfo = (PHANDLE_CREATION_CALLBACK_INFO)StartContext; if (callbackInfo == NULL) { goto Exit; } FILETIME filetime = callbackInfo->FileTime; HANDLE targetPID = callbackInfo->TargetProcessId; HANDLE sourcePID = callbackInfo->SourceProcessId; ULONGLONG UTargetPID = HandleToULong(targetPID); ULONGLONG USourcePID = HandleToULong(sourcePID); ULONGLONG sourceThreadId = HandleToULong(callbackInfo->SourceThreadId); ULONGLONG sourceProcessStartKey = callbackInfo->SourceProcessStartKey; ULONGLONG targetProcessStartKey = callbackInfo->TargetProcessStartKey; DWORD OperationType = callbackInfo->OperationType; ACCESS_MASK DesiredAccess = callbackInfo->DesiredAccess; RequestorImagePath.Length = 0; RequestorImagePath.MaximumLength = MAX_ALLOC; RequestorImagePath.Buffer = (PWSTR)ExAllocatePool2(POOL_FLAG_PAGED, MAX_ALLOC, SYSTEM_THREAD_TAG); status = GetProcessImageName(sourcePID, &RequestorImagePath); if (!NT_SUCCESS(status)) { goto Exit; } SourceFullUserName.Length = 0; SourceFullUserName.MaximumLength = MAX_ALLOC; SourceFullUserName.Buffer = (PWCH)ExAllocatePool2(POOL_FLAG_PAGED, SourceFullUserName.MaximumLength, SYSTEM_THREAD_TAG); if (SourceFullUserName.Buffer == NULL) { goto Exit; } DWORD SourceAuthenticationId = 0; status = GetProcessUserName(&SourceFullUserName, sourcePID, &SourceAuthenticationId); if (!NT_SUCCESS(status)) { goto Exit; } TargetImagePath.Length = 0; TargetImagePath.MaximumLength = MAX_ALLOC; TargetImagePath.Buffer = (PWSTR)ExAllocatePool2(POOL_FLAG_PAGED, MAX_ALLOC, SYSTEM_THREAD_TAG); status = GetProcessImageName(targetPID, &TargetImagePath); if (!NT_SUCCESS(status)) { goto Exit; } if (OperationType == 1) { EventWriteProcessAccess(NULL, &filetime, DesiredAccess, UTargetPID, targetProcessStartKey, TargetImagePath.Buffer, USourcePID, sourceThreadId, sourceProcessStartKey, RequestorImagePath.Buffer, SourceFullUserName.Buffer, SourceAuthenticationId); // // Grabbing impersonation data // HANDLE hToken = NULL; status = GetProcessToken(sourcePID, &hToken); if (!NT_SUCCESS(status)) { goto Exit; } DWORD SessionId = GetSessionIdFromToken(hToken); if (SessionId != 0) { status = ThreadImpersonationEvent(hToken, callbackInfo->SourceThread, L"OpenProcess", RequestorImagePath.Buffer, USourcePID, sourceProcessStartKey, UTargetPID, targetProcessStartKey); } if (hToken != NULL) { ZwClose(hToken); } // // End of querying for impersonation // goto Exit; } if (OperationType == 2) { EventWriteProcessAccessDuplicated(NULL, &filetime, DesiredAccess, UTargetPID, targetProcessStartKey, TargetImagePath.Buffer, USourcePID, sourceThreadId, sourceProcessStartKey, RequestorImagePath.Buffer, SourceFullUserName.Buffer, SourceAuthenticationId); goto Exit; } Exit: if (TargetImagePath.Buffer != NULL) { ExFreePoolWithTag(TargetImagePath.Buffer, SYSTEM_THREAD_TAG); } if (SourceFullUserName.Buffer != NULL) { ExFreePoolWithTag(SourceFullUserName.Buffer, SYSTEM_THREAD_TAG); } if (RequestorImagePath.Buffer != NULL) { ExFreePoolWithTag(RequestorImagePath.Buffer, SYSTEM_THREAD_TAG); } ExFreePoolWithTag(StartContext, SYSTEM_THREAD_TAG); PsTerminateSystemThread(STATUS_SUCCESS); } _IRQL_requires_max_(PASSIVE_LEVEL) void PostProcessHandleCallback( _In_ PVOID RegistrationContext, _In_ POB_POST_OPERATION_INFORMATION OperationInformation ) { UNREFERENCED_PARAMETER(RegistrationContext); HANDLE hPostHandleWorkerThread = NULL; NTSTATUS status; FILETIME filetime; PAGED_CODE(); PEPROCESS targetProcess = (PEPROCESS)OperationInformation->Object; HANDLE TargetProcessId = PsGetProcessId(targetProcess); HANDLE SourceProcessId = PsGetCurrentProcessId(); if ((HANDLE)g_ServicePID == SourceProcessId) { goto Exit; } ACCESS_MASK CreatedGrantedAccess = OperationInformation->Parameters->CreateHandleInformation.GrantedAccess; KeQuerySystemTime(&filetime); if (CreatedGrantedAccess == 0x0) { goto Exit; } if (SourceProcessId == TargetProcessId) { goto Exit; } if (SourceProcessId == (HANDLE)0x4 || TargetProcessId == (HANDLE)0x4) { goto Exit; } PHANDLE_CREATION_CALLBACK_INFO callbackInfo = NULL; callbackInfo = (PHANDLE_CREATION_CALLBACK_INFO)ExAllocatePool2(POOL_FLAG_PAGED, sizeof(HANDLE_CREATION_CALLBACK_INFO), SYSTEM_THREAD_TAG); if (callbackInfo == NULL) { DbgPrint("ExAllocatePool2 failed\n"); goto Exit; } callbackInfo->SourceProcessId = SourceProcessId; callbackInfo->SourceThreadId = PsGetCurrentThreadId(); callbackInfo->TargetProcessId = TargetProcessId; callbackInfo->SourceProcessStartKey = PsGetProcessStartKey(PsGetCurrentProcess()); callbackInfo->TargetProcessStartKey = PsGetProcessStartKey(targetProcess); callbackInfo->FileTime = filetime; callbackInfo->SourceThread = PsGetCurrentThread(); switch (OperationInformation->Operation) { case OB_OPERATION_HANDLE_CREATE: { callbackInfo->DesiredAccess = CreatedGrantedAccess; callbackInfo->OperationType = 1; break; } case OB_OPERATION_HANDLE_DUPLICATE: { if ((CreatedGrantedAccess & 0x40) != 0x40) { if(callbackInfo != NULL) { ExFreePoolWithTag(callbackInfo, SYSTEM_THREAD_TAG); } return; } ACCESS_MASK DuplicatedRights = OperationInformation->Parameters->DuplicateHandleInformation.GrantedAccess; callbackInfo->DesiredAccess = DuplicatedRights; callbackInfo->OperationType = 2; break; } } OBJECT_ATTRIBUTES objectAttributes; InitializeObjectAttributes(&objectAttributes, NULL, OBJ_KERNEL_HANDLE, NULL, NULL); status = PsCreateSystemThread(&hPostHandleWorkerThread, THREAD_ALL_ACCESS, &objectAttributes, NULL, NULL, (PKSTART_ROUTINE)PostHandleWorkerThread, callbackInfo); if (!NT_SUCCESS(status)) { DbgPrint("PsCreateSystemThread failed: %x\n", status); ExFreePoolWithTag(callbackInfo, SYSTEM_THREAD_TAG); goto Exit; } Exit: if(hPostHandleWorkerThread != NULL) { ZwClose(hPostHandleWorkerThread); } return; } // [DONE] // Callback to capture process termination events // _IRQL_requires_max_(PASSIVE_LEVEL) void TerminateProcessNotifyRoutine( _In_ HANDLE ParentProcessId, _In_ HANDLE ProcessId, _In_ BOOLEAN Create ) { NTSTATUS status; UNICODE_STRING targetImagePath{}; UNICODE_STRING sourceImagePath{}; HANDLE sourcePID = NULL; HANDLE targetPID = NULL; FILETIME fileTime; PAGED_CODE(); UNREFERENCED_PARAMETER(ParentProcessId); UNREFERENCED_PARAMETER(ProcessId); if (!Create) { KeQuerySystemTime(&fileTime); targetPID = ProcessId; sourcePID = ParentProcessId; ULONGLONG uTargetPID = HandleToULong(targetPID); ULONGLONG uSourcePID = HandleToULong(sourcePID); ULONGLONG sourceThreadId = HandleToULong(PsGetCurrentThreadId()); sourceImagePath.Length = 0; sourceImagePath.MaximumLength = MAX_ALLOC; sourceImagePath.Buffer = (PWCH)ExAllocatePool2(POOL_FLAG_PAGED, sourceImagePath.MaximumLength, CALBACK_TAG); status = GetProcessImageName(sourcePID, &sourceImagePath); if (status != STATUS_SUCCESS) { sourceImagePath.Buffer = NULL; } targetImagePath.Length = 0; targetImagePath.MaximumLength = MAX_ALLOC; targetImagePath.Buffer = (PWCH)ExAllocatePool2(POOL_FLAG_PAGED, targetImagePath.MaximumLength, CALBACK_TAG); status = GetProcessImageName(targetPID, &targetImagePath); if (status != STATUS_SUCCESS) { targetImagePath.Buffer = NULL; } EventWriteProcessTerminate(NULL, &fileTime, sourceImagePath.Buffer, uSourcePID, sourceThreadId, targetImagePath.Buffer, uTargetPID); goto Exit; } Exit: if (targetImagePath.Buffer != NULL) { ExFreePoolWithTag(targetImagePath.Buffer, CALBACK_TAG); } if (sourceImagePath.Buffer != NULL) { ExFreePoolWithTag(sourceImagePath.Buffer, CALBACK_TAG); } }