#include "../controller_private.h" #include "ns/ns_launcher.h" struct ClientConstruct { BK_CONTROLLER_CLIENT *ptr = nullptr; ClientConstruct() noexcept = default; ~ClientConstruct() noexcept { Free(); } ClientConstruct(const ClientConstruct &) = delete; ClientConstruct &operator=(const ClientConstruct &) = delete; void Free() noexcept { if (!ptr) { return; } if (ptr->IoctlQueueDataEvent) { CloseHandle(ptr->IoctlQueueDataEvent); } if (ptr->EtwQueueDataEvent) { CloseHandle(ptr->EtwQueueDataEvent); } if (ptr->DispatchIdleEvent) { CloseHandle(ptr->DispatchIdleEvent); } DeleteCriticalSection(&ptr->Lock); if (ptr->IoctlNodeSlab) { free(ptr->IoctlNodeSlab); } if (ptr->EtwNodeSlab) { free(ptr->EtwNodeSlab); } if (ptr->Pipe != NULL && ptr->Pipe != INVALID_HANDLE_VALUE) { CloseHandle(ptr->Pipe); ptr->Pipe = INVALID_HANDLE_VALUE; } free(ptr); ptr = nullptr; } BK_CONTROLLER_CLIENT *release() noexcept { auto *p = ptr; ptr = nullptr; return p; } }; struct ClientWorkItem { BK_CONTROLLER_CLIENT *Client; PTP_WORK Work; }; static VOID CALLBACK ControllerClientWorkCallback(_Inout_ PTP_CALLBACK_INSTANCE Instance, _Inout_opt_ PVOID Context, _Inout_ PTP_WORK Work) { ClientWorkItem *item = static_cast(Context); UNREFERENCED_PARAMETER(Work); CallbackMayRunLong(Instance); if (item != nullptr) { (void)ControllerClientThreadProc(item->Client); if (item->Work != nullptr) { CloseThreadpoolWork(item->Work); } free(item); } } static HANDLE g_ServerAcceptThreads[BK_CONTROLLER_SERVER_ACCEPT_THREADS] = {0}; static HANDLE g_HookAcceptThreads[BK_CONTROLLER_HOOK_ACCEPT_THREADS] = {0}; typedef struct _BK_SERVER_ENDPOINT_CONTEXT { PCWSTR PipeName; DWORD ClientRole; } BK_SERVER_ENDPOINT_CONTEXT, *PBK_SERVER_ENDPOINT_CONTEXT; static const BK_SERVER_ENDPOINT_CONTEXT g_ControlEndpointContext = {BKIPC_PIPE_NAME, BkctlrClientRoleControl}; static const BK_SERVER_ENDPOINT_CONTEXT g_HookEndpointContext = {BKIPC_HOOK_PIPE_NAME, BkctlrClientRoleHook}; static volatile LONG g_ControllerProtectionReady = 0; static volatile LONG g_ControllerProtectionArmed = 0; VOID ControllerTryMarkProtectedReady(_In_ HANDLE DriverHandle, _In_ BOOL LogFailure) { if (DriverHandle == NULL || DriverHandle == INVALID_HANDLE_VALUE) { return; } if (InterlockedCompareExchange(&g_ControllerProtectionReady, 0, 0) == 0 || InterlockedCompareExchange(&g_ControllerProtectionArmed, 0, 0) != 0) { return; } if (BkscMarkControllerReady(DriverHandle, GetCurrentProcessId())) { if (InterlockedExchange(&g_ControllerProtectionArmed, 1) == 0) { ControllerLog("[DRIVER] controller protection readiness acknowledged\n"); } } else if (LogFailure) { ControllerLog("[DRIVER][WARN] controller protection readiness failed (%lu)\n", GetLastError()); } } static BOOL ControllerDriverPumpReadErrorIsRecoverable(_In_ DWORD ErrorCode) { switch (ErrorCode) { case ERROR_OPERATION_ABORTED: case ERROR_NOT_READY: case ERROR_DEVICE_NOT_CONNECTED: case ERROR_BROKEN_PIPE: case ERROR_INVALID_HANDLE: case ERROR_GEN_FAILURE: case ERROR_SEM_TIMEOUT: return TRUE; default: return FALSE; } } static DWORD ControllerDriverPumpBackoffMs(_In_ DWORD Failures) { DWORD cappedFailures = (Failures < 20u) ? Failures : 20u; DWORD scaled = 50 + cappedFailures * 25; return (scaled < 750u) ? scaled : 750u; } static BOOL ControllerDropDriverHandleIfCurrent(_In_ HANDLE LocalHandle, _In_z_ PCSTR Reason, _In_ DWORD ErrorCode) { BOOL dropped = FALSE; EnterCriticalSection(g_DriverLock.get()); if (g_DriverHandle == LocalHandle) { CloseHandle(g_DriverHandle); g_DriverHandle = INVALID_HANDLE_VALUE; InterlockedExchange(&g_ControllerProtectionArmed, 0); dropped = TRUE; } LeaveCriticalSection(g_DriverLock.get()); if (dropped) { ControllerMarkDriverSubscriptionsDirty(); ControllerLog("[DRIVER][WARN] dropped driver handle reason=%s err=%lu; controller and NetSvc remain online\n", Reason, ErrorCode); } return dropped; } static DWORD WINAPI ControllerServerThreadProc(_In_ LPVOID Context) { SECURITY_ATTRIBUTES sa; PSECURITY_DESCRIPTOR sd = NULL; DWORD pipeCreateFailures = 0; const BK_SERVER_ENDPOINT_CONTEXT *endpoint = (const BK_SERVER_ENDPOINT_CONTEXT *)Context; PCWSTR pipeName = BKIPC_PIPE_NAME; DWORD clientRole = BkctlrClientRoleControl; if (endpoint != NULL) { if (endpoint->PipeName != NULL && endpoint->PipeName[0] != L'\0') { pipeName = endpoint->PipeName; } if (endpoint->ClientRole != BkctlrClientRoleUnknown) { clientRole = endpoint->ClientRole; } } UniqueLocalPtr sdOwner; if (!ControllerCreatePipeSecurity(clientRole, &sa, &sd)) { ControllerLog("[-] BlackbirdController: failed to create pipe security role=%lu (%lu)\n", clientRole, GetLastError()); return 1; } sdOwner.reset(static_cast(sd)); while (!ControllerShouldStop()) { HANDLE pipe; BOOL connected; DWORD mode = PIPE_READMODE_MESSAGE; ClientWorkItem *workItem; DWORD slotIndex; ClientConstruct guard; pipe = CreateNamedPipeW(pipeName, PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, sizeof(BKIPC_PACKET), sizeof(BKIPC_PACKET), 3000, &sa); if (pipe == INVALID_HANDLE_VALUE) { pipeCreateFailures += 1; if (pipeCreateFailures == 1 || (pipeCreateFailures % 50) == 0) { ControllerLog("[WARN] BlackbirdController: CreateNamedPipe failed (%lu), retries=%lu\n", GetLastError(), pipeCreateFailures); } Sleep(250); continue; } pipeCreateFailures = 0; connected = ConnectNamedPipe(pipe, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED); if (!connected) { ControllerLog("[WARN] BlackbirdController: ConnectNamedPipe failed (%lu)\n", GetLastError()); CloseHandle(pipe); continue; } (void)SetNamedPipeHandleState(pipe, &mode, NULL, NULL); guard.ptr = (BK_CONTROLLER_CLIENT *)calloc(1, sizeof(*guard.ptr)); if (guard.ptr == NULL) { CloseHandle(pipe); continue; } guard.ptr->Pipe = pipe; guard.ptr->Role = clientRole; guard.ptr->QueueHead = NULL; guard.ptr->QueueTail = NULL; guard.ptr->QueueDepth = 0; guard.ptr->DroppedEvents = 0; guard.ptr->SubscriptionCount = 0; guard.ptr->SlotIndex = BK_CONTROLLER_INVALID_SLOT; guard.ptr->DispatchRefCount = 0; guard.ptr->Detached = 0; InitializeCriticalSection(&guard.ptr->Lock); guard.ptr->IoctlQueueDataEvent = CreateEventW(NULL, TRUE, FALSE, NULL); guard.ptr->EtwQueueDataEvent = CreateEventW(NULL, TRUE, FALSE, NULL); guard.ptr->DispatchIdleEvent = CreateEventW(NULL, TRUE, TRUE, NULL); if (guard.ptr->IoctlQueueDataEvent == NULL || guard.ptr->EtwQueueDataEvent == NULL || guard.ptr->DispatchIdleEvent == NULL) { continue; } guard.ptr->IoctlNodeSlab = (PBK_CONTROLLER_EVENT_NODE)calloc(BK_CONTROLLER_MAX_CLIENT_QUEUE_DEPTH, sizeof(BK_CONTROLLER_EVENT_NODE)); if (guard.ptr->IoctlNodeSlab != NULL) { for (DWORD i = 0; i < BK_CONTROLLER_MAX_CLIENT_QUEUE_DEPTH - 1; i++) { guard.ptr->IoctlNodeSlab[i].Next = &guard.ptr->IoctlNodeSlab[i + 1]; } guard.ptr->IoctlNodeSlab[BK_CONTROLLER_MAX_CLIENT_QUEUE_DEPTH - 1].Next = NULL; guard.ptr->IoctlNodeFreeHead = &guard.ptr->IoctlNodeSlab[0]; } guard.ptr->EtwNodeSlab = (PBK_CONTROLLER_ETW_EVENT_NODE)calloc(BK_CONTROLLER_MAX_CLIENT_ETW_QUEUE_DEPTH, sizeof(BK_CONTROLLER_ETW_EVENT_NODE)); if (guard.ptr->EtwNodeSlab != NULL) { for (DWORD i = 0; i < BK_CONTROLLER_MAX_CLIENT_ETW_QUEUE_DEPTH - 1; i++) { guard.ptr->EtwNodeSlab[i].Next = &guard.ptr->EtwNodeSlab[i + 1]; } guard.ptr->EtwNodeSlab[BK_CONTROLLER_MAX_CLIENT_ETW_QUEUE_DEPTH - 1].Next = NULL; guard.ptr->EtwNodeFreeHead = &guard.ptr->EtwNodeSlab[0]; } if (!GetNamedPipeClientProcessId(pipe, &guard.ptr->ProcessId)) { guard.ptr->ProcessId = 0; } if (!ProcessIdToSessionId(guard.ptr->ProcessId, &guard.ptr->SessionId)) { guard.ptr->SessionId = 0; } ControllerLog("[IPC] client connected role=%lu pid=%lu session=%lu pipe=%ls\n", guard.ptr->Role, guard.ptr->ProcessId, guard.ptr->SessionId, pipeName); EnterCriticalSection(g_ClientListLock.get()); if (g_ClientCount >= BK_CONTROLLER_MAX_CLIENTS) { LeaveCriticalSection(g_ClientListLock.get()); continue; } slotIndex = ControllerAllocateClientSlotLocked(); if (slotIndex == BK_CONTROLLER_INVALID_SLOT) { LeaveCriticalSection(g_ClientListLock.get()); continue; } guard.ptr->SlotIndex = slotIndex; g_ClientSlots[slotIndex] = guard.ptr; guard.ptr->Next = g_ClientList; g_ClientList = guard.ptr; g_ClientCount += 1; ControllerLog("[IPC] active clients=%lu\n", g_ClientCount); LeaveCriticalSection(g_ClientListLock.get()); workItem = (ClientWorkItem *)calloc(1, sizeof(*workItem)); if (workItem == NULL) { ControllerLog("[WARN] BlackbirdController: failed to allocate client work item for pid=%lu\n", guard.ptr->ProcessId); ControllerDetachClient(guard.ptr); continue; } workItem->Client = guard.ptr; workItem->Work = CreateThreadpoolWork(ControllerClientWorkCallback, workItem, NULL); if (workItem->Work == NULL) { ControllerLog("[WARN] BlackbirdController: failed to queue client work for pid=%lu (%lu)\n", guard.ptr->ProcessId, GetLastError()); free(workItem); ControllerDetachClient(guard.ptr); continue; } (void)guard.release(); SubmitThreadpoolWork(workItem->Work); } return 0; } static DWORD WINAPI ControllerDriverPumpThreadProc(_In_ LPVOID Context) { UNREFERENCED_PARAMETER(Context); DWORD driverOpenFailures = 0; DWORD recoverableReadFailures = 0; while (!ControllerShouldStop()) { BK_EVENT_RECORD record; DWORD bytes = 0; BOOL ok; HANDLE localHandle; EnterCriticalSection(g_DriverLock.get()); if (g_DriverHandle == INVALID_HANDLE_VALUE) { g_DriverHandle = BkscOpenControlDevice(); localHandle = g_DriverHandle; LeaveCriticalSection(g_DriverLock.get()); if (localHandle != INVALID_HANDLE_VALUE) { BK_STATS_RESPONSE stats; DWORD statsBytes = 0; ControllerTryMarkProtectedReady(localHandle, TRUE); ControllerMarkDriverSubscriptionsDirty(); (void)ControllerApplyDriverSubscriptionsIfDirty(); ControllerRefreshNetSvcDriverHandle(localHandle, "driver-open"); driverOpenFailures = 0; recoverableReadFailures = 0; ZeroMemory(&stats, sizeof(stats)); if (BkscGetStats(localHandle, &stats, &statsBytes)) { ControllerLog("[DRIVER] connected and verified. subscriptions=%lu queueDepth=%lu dropped=%lu\n", stats.SubscriptionCount, stats.QueueDepth, stats.DroppedEvents); } else { ControllerLog("[DRIVER][WARN] connected, but stats query failed (%lu)\n", GetLastError()); } } else { driverOpenFailures += 1; if (driverOpenFailures == 1 || (driverOpenFailures % 40) == 0) { ControllerLog("[DRIVER][WARN] open failed (%lu), retries=%lu\n", GetLastError(), driverOpenFailures); } } Sleep(250); continue; } localHandle = g_DriverHandle; LeaveCriticalSection(g_DriverLock.get()); (void)ControllerApplyDriverSubscriptionsIfDirty(); ok = BkscGetEvent(localHandle, &record, &bytes); if (!ok) { DWORD err = GetLastError(); if (err == ERROR_NO_MORE_ITEMS) { recoverableReadFailures = 0; Sleep(20); continue; } if (ControllerShouldStop()) { break; } if (ControllerDriverPumpReadErrorIsRecoverable(err)) { recoverableReadFailures += 1; if (recoverableReadFailures == 1 || (recoverableReadFailures % 20u) == 0) { ControllerLog( "[DRIVER][WARN] event read failed (%lu), recoverableFailures=%lu; reopening driver path without stopping NetSvc\n", err, recoverableReadFailures); } (void)ControllerDropDriverHandleIfCurrent(localHandle, "event-read-recoverable", err); Sleep(ControllerDriverPumpBackoffMs(recoverableReadFailures)); continue; } ControllerLog( "[DRIVER][WARN] event read failed (%lu); unrecoverable driver path failure, stopping controller and NetSvc\n", err); (void)ControllerDropDriverHandleIfCurrent(localHandle, "event-read-unrecoverable", err); if (g_StopEvent != NULL) { (void)SetEvent(g_StopEvent); } break; } if (bytes >= sizeof(record)) { recoverableReadFailures = 0; ControllerDispatchDriverRecord(&record); } } return 0; } #ifndef BK_INTENT_PROCESS_MEMORY #define BK_INTENT_PROCESS_MEMORY 0x00000001u #endif #ifndef BK_INTENT_THREAD_CONTEXT #define BK_INTENT_THREAD_CONTEXT 0x00000002u #endif #ifndef BK_INTENT_DUP_HANDLE #define BK_INTENT_DUP_HANDLE 0x00000004u #endif static BOOL ControllerIsInterestingHandleAccessMask(_In_ ULONG DesiredAccess) { return ((DesiredAccess & PROCESS_VM_OPERATION) != 0) || ((DesiredAccess & PROCESS_VM_WRITE) != 0) || ((DesiredAccess & PROCESS_CREATE_THREAD) != 0) || ((DesiredAccess & PROCESS_ALL_ACCESS) == PROCESS_ALL_ACCESS) || ((DesiredAccess & THREAD_SET_CONTEXT) != 0) || ((DesiredAccess & THREAD_SUSPEND_RESUME) != 0) || ((DesiredAccess & THREAD_ALL_ACCESS) == THREAD_ALL_ACCESS); } #ifndef BK_SOCKET_OPCODE_SEND #define BK_SOCKET_OPCODE_SEND 10u #define BK_SOCKET_OPCODE_RECV 11u #define BK_SOCKET_OPCODE_CONNECT 12u #define BK_SOCKET_OPCODE_DISCONNECT 13u #define BK_SOCKET_OPCODE_ACCEPT 15u #define BK_SOCKET_OPCODE_RECONNECT 16u #define BK_SOCKET_OPCODE_SENDUDP 42u #define BK_SOCKET_OPCODE_RECVUDP 43u #define BK_SOCKET_OPCODE_FAILUDP 49u #endif static BOOL ControllerIsInterestingSocketOpcode(_In_ UCHAR Opcode) { switch (Opcode) { case BK_SOCKET_OPCODE_SEND: case BK_SOCKET_OPCODE_RECV: case BK_SOCKET_OPCODE_CONNECT: case BK_SOCKET_OPCODE_DISCONNECT: case BK_SOCKET_OPCODE_ACCEPT: case BK_SOCKET_OPCODE_RECONNECT: case BK_SOCKET_OPCODE_SENDUDP: case BK_SOCKET_OPCODE_RECVUDP: case BK_SOCKET_OPCODE_FAILUDP: return TRUE; default: return FALSE; } } static PCSTR ControllerSocketOperationFromOpcode(_In_ UCHAR Opcode) { switch (Opcode) { case BK_SOCKET_OPCODE_SEND: return "SEND"; case BK_SOCKET_OPCODE_RECV: return "RECV"; case BK_SOCKET_OPCODE_CONNECT: return "CONNECT"; case BK_SOCKET_OPCODE_DISCONNECT: return "DISCONNECT"; case BK_SOCKET_OPCODE_ACCEPT: return "ACCEPT"; case BK_SOCKET_OPCODE_RECONNECT: return "RECONNECT"; case BK_SOCKET_OPCODE_SENDUDP: return "SEND_UDP"; case BK_SOCKET_OPCODE_RECVUDP: return "RECV_UDP"; case BK_SOCKET_OPCODE_FAILUDP: return "FAIL_UDP"; default: return "SOCKET"; } } static BOOL ControllerShouldForwardEtwRecord(_In_ PEVENT_RECORD Record, _In_opt_z_ PCWSTR EventName, _In_ const BKIPC_ETW_EVENT *Event) { ULONG desiredAccess = 0; ULONG correlationFlags = 0; ULONG severity = 0; ULONGLONG processId = 0; ULONGLONG creatorPid = 0; ULONGLONG targetPid = 0; CHAR className[64]; CHAR detectionName[RTL_NUMBER_OF(Event->DetectionName)]; BOOL selfTarget = FALSE; BOOL handleNoiseDetection = FALSE; BOOL lowSignalDetection = FALSE; BOOL strongDetection = FALSE; if (Record == NULL || Event == NULL) { return FALSE; } if (EventName == NULL || EventName[0] == L'\0') { if (Event->Source == BlackbirdIpcEtwSourceKernelNetwork) { return ControllerIsInterestingSocketOpcode((UCHAR)Event->Opcode); } return FALSE; } if (wcscmp(EventName, L"DetectionTelemetry") == 0) { ZeroMemory(detectionName, sizeof(detectionName)); if (Event->DetectionName[0] != '\0') { (void)StringCchCopyA(detectionName, RTL_NUMBER_OF(detectionName), Event->DetectionName); } else { (void)ControllerEtwGetAnsiProperty(Record, L"detectionName", detectionName, RTL_NUMBER_OF(detectionName)); } (void)_strupr_s(detectionName, sizeof(detectionName)); severity = Event->Severity; targetPid = Event->TargetPid; processId = (Event->ProcessId != 0) ? Event->ProcessId : Event->EventProcessId; selfTarget = (processId != 0 && targetPid != 0 && processId == targetPid); correlationFlags = Event->CorrelationFlags; selfTarget = (processId != 0 && targetPid != 0 && processId == targetPid); if (correlationFlags == 0) { (void)ControllerEtwGetU32Property(Record, L"correlationFlags", &correlationFlags); } BOOL directSyscallHandleDetection = (strstr(detectionName, "DIRECT_SYSCALL") != NULL) || (strstr(detectionName, "DIRECT-SYSCALL") != NULL); handleNoiseDetection = directSyscallHandleDetection || (strstr(detectionName, "STACK_INTEGRITY_ANOMALY_ON_HANDLE_OP") != NULL) || (strstr(detectionName, "SUSPECT_HANDLE_OPERATION") != NULL) || (strstr(detectionName, "ANOMALY_ON_HANDLE_OP") != NULL); if (!handleNoiseDetection && ((strstr(detectionName, "USPECT_HANDLE_OPERATION") != NULL) || (strstr(detectionName, "NOMALY_ON_HANDLE_OP") != NULL))) { handleNoiseDetection = TRUE; } lowSignalDetection = (strstr(detectionName, "REMOTE_THREAD_WITH_RECENT_HANDLE_INTENT") != NULL) || (strstr(detectionName, "THREAD_ACTIVITY_WITH_THREAD_CONTEXT_INTENT") != NULL) || (strstr(detectionName, "REMOTE_THREAD_OUTSIDE_MAIN_IMAGE") != NULL); strongDetection = (strstr(detectionName, "PROCESS_HOLLOWING") != NULL) || (strstr(detectionName, "INJECTION") != NULL) || (strstr(detectionName, "THREAD_HIJACK") != NULL) || (strstr(detectionName, "REMOTE_APC") != NULL) || (strstr(detectionName, "NON_IMAGE_EXECUTABLE_REGION") != NULL) || (strstr(detectionName, "CREDENTIAL_ACCESS_LSASS_HANDLE") != NULL) || (strstr(detectionName, "REGISTRY_LSA_PACKAGE_WRITE") != NULL) || (strstr(detectionName, "AGGREGATE_THREAT_SIGNAL") != NULL) || (strstr(detectionName, "SHELLCODE_STAGE_PATTERN") != NULL) || (strstr(detectionName, "PROCESS_IMAGE_TAMPER") != NULL) || (strstr(detectionName, "PROCESS_IMAGE_GHOSTING") != NULL); if (handleNoiseDetection) { if (directSyscallHandleDetection) { return (severity >= 5); } if (selfTarget) { return FALSE; } if (severity < 7) { return FALSE; } if ((correlationFlags & (BK_INTENT_PROCESS_MEMORY | BK_INTENT_THREAD_CONTEXT | BK_INTENT_DUP_HANDLE)) == 0) { return FALSE; } return TRUE; } if (lowSignalDetection) { return FALSE; } if (strongDetection) { return (severity >= 5); } return (severity >= 6); } if (Event->Source == BlackbirdIpcEtwSourceThreatIntel) { return (Event->Task == 1 || Event->Task == 2 || Event->Task == 7); } if (Event->Source == BlackbirdIpcEtwSourceKernelNetwork) { return ControllerIsInterestingSocketOpcode((UCHAR)Event->Opcode); } if (wcscmp(EventName, L"HandleTelemetry") == 0) { ZeroMemory(className, sizeof(className)); if (Event->ClassName[0] != '\0') { (void)StringCchCopyA(className, RTL_NUMBER_OF(className), Event->ClassName); } else { (void)ControllerEtwGetAnsiProperty(Record, L"class", className, RTL_NUMBER_OF(className)); } desiredAccess = Event->DesiredAccess; if (desiredAccess == 0) { (void)ControllerEtwGetU32Property(Record, L"desiredAccess", &desiredAccess); } if (_stricmp(className, "DIRECT-SYSCALL-SUSPECT") == 0 || _stricmp(className, "UNKNOWN-ORIGIN") == 0) { return TRUE; } return ControllerIsInterestingHandleAccessMask(desiredAccess); } if (wcscmp(EventName, L"ApcTelemetry") == 0) { desiredAccess = Event->DesiredAccess; processId = Event->ProcessId; targetPid = Event->TargetPid; if (desiredAccess == 0) { (void)ControllerEtwGetU32Property(Record, L"desiredAccess", &desiredAccess); } if (processId == 0) { (void)ControllerEtwGetU64Property(Record, L"processId", &processId); } if (targetPid == 0) { (void)ControllerEtwGetU64Property(Record, L"targetPid", &targetPid); } if (targetPid == 0) { targetPid = Event->TargetPid; } if (processId == 0) { processId = Event->ProcessId; } if (processId != 0 && targetPid != 0 && processId == targetPid) { return FALSE; } return ((desiredAccess & (THREAD_SET_CONTEXT | THREAD_SUSPEND_RESUME)) != 0); } if (wcscmp(EventName, L"ThreadTelemetry") == 0) { correlationFlags = Event->CorrelationFlags; targetPid = Event->TargetPid; processId = Event->ProcessId; creatorPid = Event->CreatorProcessId; if (correlationFlags == 0) { (void)ControllerEtwGetU32Property(Record, L"correlationFlags", &correlationFlags); } if (targetPid == 0) { (void)ControllerEtwGetU64Property(Record, L"targetPid", &targetPid); } if (processId == 0) { (void)ControllerEtwGetU64Property(Record, L"processId", &processId); } if (creatorPid == 0) { (void)ControllerEtwGetU64Property(Record, L"creatorPid", &creatorPid); } if (creatorPid == 0) { creatorPid = Event->CreatorProcessId; } if (processId == 0) { processId = Event->ProcessId; } if (targetPid == 0) { targetPid = Event->TargetPid != 0 ? Event->TargetPid : processId; } if ((targetPid != 0 && creatorPid != 0 && targetPid == creatorPid) || (processId != 0 && creatorPid != 0 && processId == creatorPid)) { return ((correlationFlags & BK_INTENT_PROCESS_MEMORY) != 0); } if ((correlationFlags & (BK_INTENT_PROCESS_MEMORY | BK_INTENT_THREAD_CONTEXT | BK_INTENT_DUP_HANDLE)) != 0) { return TRUE; } if (processId != 0 && creatorPid != 0 && processId != creatorPid) { return TRUE; } return FALSE; } if (wcscmp(EventName, L"ProcessTelemetry") == 0) { if ((Event->Flags & BKIPC_ETW_FLAG_PROCESS_IS_CREATE) != 0u) { return TRUE; } return FALSE; } if (wcscmp(EventName, L"ImageTelemetry") == 0) { return ((Event->Flags & BKIPC_ETW_FLAG_IMAGE_SYSTEM_MODE) == 0u); } if (wcscmp(EventName, L"RegistryTelemetry") == 0) { return FALSE; } if (wcscmp(EventName, L"NtApiTelemetry") == 0) { return TRUE; } if (wcscmp(EventName, L"SystemInformationTelemetry") == 0) { return TRUE; } if (wcscmp(EventName, L"QpcTimingTelemetry") == 0) { return TRUE; } return FALSE; } static VOID WINAPI ControllerEtwCallback(_In_ PEVENT_RECORD Record, _In_opt_z_ PCWSTR EventName, _In_opt_ PVOID Context) { static const PCWSTR stackPropertyNames[BKIPC_MAX_ETW_STACK_FRAMES] = { L"stack0", L"stack1", L"stack2", L"stack3", L"stack4", L"stack5", L"stack6", L"stack7", L"stack8", L"stack9", L"stack10", L"stack11", L"stack12", L"stack13", L"stack14", L"stack15"}; BKIPC_ETW_EVENT event; ULONGLONG processId = 0; ULONGLONG threadId = 0; ULONGLONG callerPid = 0; ULONGLONG targetPid = 0; ULONGLONG callerTid = 0; ULONGLONG parentProcessId = 0; ULONGLONG creatorProcessId = 0; ULONGLONG creatorThreadId = 0; ULONGLONG processStartKey = 0; ULONGLONG arg0 = 0; ULONGLONG arg1 = 0; ULONGLONG arg2 = 0; ULONGLONG arg3 = 0; ULONGLONG arg4 = 0; ULONGLONG arg5 = 0; ULONGLONG arg6 = 0; ULONGLONG arg7 = 0; ULONGLONG rawCounter = 0; ULONGLONG virtualCounter = 0; ULONGLONG rawDelta = 0; ULONGLONG virtualDelta = 0; ULONGLONG correctionTicks = 0; ULONGLONG autoBiasTicks = 0; ULONGLONG originAddress = 0; ULONGLONG deepAllocationBase = 0; ULONGLONG deepRegionSize = 0; ULONGLONG startAddress = 0; ULONGLONG imageBase = 0; ULONGLONG imageSize = 0; ULONG severity = 0; ULONG correlationFlags = 0; ULONG correlationAccessMask = 0; ULONG correlationAgeMs = 0; ULONG desiredAccess = 0; ULONG originProtect = 0; ULONG frameCount = 0; ULONG deepRegionProtect = 0; ULONG deepRegionState = 0; ULONG deepRegionType = 0; ULONG deepSampleSize = 0; ULONG startRegionProtect = 0; ULONG startRegionState = 0; ULONG startRegionType = 0; ULONG sessionId = 0; ULONG notifyClass = 0; ULONG dataType = 0; ULONG dataSize = 0; ULONG systemInformationClass = 0; ULONG systemInformationLength = 0; ULONG returnLength = 0; ULONG execFlags = 0; ULONG sourceFlags = 0; LONG statusOpenProcess = 0; LONG statusBasicInfo = 0; LONG statusSectionName = 0; LONG startRegionStatus = 0; LONG createStatus = 0; LONG callStatus = 0; LONG queryStatus = 0; BOOL boolValue = FALSE; BOOL traitsComputed = FALSE; UCHAR signatureLevel = 0; UCHAR signatureType = 0; CHAR apiName[96]; DWORD i; UNREFERENCED_PARAMETER(Context); if (Record == NULL) { return; } if (IsEqualGUID(Record->EventHeader.ProviderId, BKSC_PROVIDER_GUID_TI)) { return; } ZeroMemory(&event, sizeof(event)); ZeroMemory(apiName, sizeof(apiName)); event.EventId = Record->EventHeader.EventDescriptor.Id; event.Opcode = Record->EventHeader.EventDescriptor.Opcode; event.Task = Record->EventHeader.EventDescriptor.Task; event.EventProcessId = Record->EventHeader.ProcessId; event.EventThreadId = Record->EventHeader.ThreadId; if (EventName != NULL && EventName[0] != L'\0') { (void)StringCchCopyW(event.EventName, RTL_NUMBER_OF(event.EventName), EventName); } if (IsEqualGUID(Record->EventHeader.ProviderId, BKSC_PROVIDER_GUID_BLACKBIRD)) { event.Source = BlackbirdIpcEtwSourceBlackbird; } else if (IsEqualGUID(Record->EventHeader.ProviderId, BKSC_PROVIDER_GUID_KERNEL_NETWORK)) { event.Source = BlackbirdIpcEtwSourceKernelNetwork; event.Family = BlackbirdIpcEtwFamilySocket; } else { return; } (void)ControllerEtwGetU32Property(Record, L"severity", &severity); event.Severity = severity; (void)ControllerEtwGetU32Property(Record, L"correlationFlags", &correlationFlags); (void)ControllerEtwGetU32Property(Record, L"correlationAccessMask", &correlationAccessMask); (void)ControllerEtwGetU32Property(Record, L"correlationAgeMs", &correlationAgeMs); event.CorrelationFlags = (UINT32)correlationFlags; event.CorrelationAccessMask = (UINT32)correlationAccessMask; event.CorrelationAgeMs = (UINT32)correlationAgeMs; (void)ControllerEtwGetU64Property(Record, L"processId", &processId); if (processId == 0 && event.Source == BlackbirdIpcEtwSourceKernelNetwork) { (void)ControllerEtwGetU64Property(Record, L"PID", &processId); if (processId == 0) { (void)ControllerEtwGetU64Property(Record, L"ProcessId", &processId); } } (void)ControllerEtwGetU64Property(Record, L"threadId", &threadId); (void)ControllerEtwGetU64Property(Record, L"callerPid", &callerPid); (void)ControllerEtwGetU64Property(Record, L"callerTid", &callerTid); (void)ControllerEtwGetU64Property(Record, L"targetPid", &targetPid); (void)ControllerEtwGetU64Property(Record, L"parentProcessId", &parentProcessId); (void)ControllerEtwGetU64Property(Record, L"creatorProcessId", &creatorProcessId); if (creatorProcessId == 0) { (void)ControllerEtwGetU64Property(Record, L"creatorPid", &creatorProcessId); } (void)ControllerEtwGetU64Property(Record, L"creatorThreadId", &creatorThreadId); (void)ControllerEtwGetU64Property(Record, L"processStartKey", &processStartKey); if (processId == 0 && callerPid != 0) { processId = callerPid; } if (processId == 0) { processId = Record->EventHeader.ProcessId; } event.ProcessId = processId; event.ThreadId = threadId; event.CallerPid = callerPid; event.TargetPid = targetPid; event.ParentProcessId = parentProcessId; event.CreatorProcessId = creatorProcessId; event.CreatorThreadId = creatorThreadId; event.ProcessStartKey = processStartKey; if (event.Source == BlackbirdIpcEtwSourceKernelNetwork) { int converted = 0; ULONG daddrNbo = 0; ULONG saddrNbo = 0; ULONG dport = 0; ULONG sport = 0; ULONG pktSize = 0; CHAR ipDst[48] = {'\0'}; CHAR ipSrc[48] = {'\0'}; event.Family = BlackbirdIpcEtwFamilySocket; (void)StringCchCopyA(event.Operation, RTL_NUMBER_OF(event.Operation), ControllerSocketOperationFromOpcode((UCHAR)event.Opcode)); if (event.EventName[0] == L'\0' && event.Operation[0] != '\0') { converted = MultiByteToWideChar(CP_ACP, 0, event.Operation, -1, event.EventName, (int)RTL_NUMBER_OF(event.EventName)); if (converted <= 0) { event.EventName[0] = L'\0'; } } if (event.Severity == 0) { event.Severity = ControllerIsInterestingSocketOpcode((UCHAR)event.Opcode) ? 2u : 1u; } (void)ControllerEtwGetU32Property(Record, L"daddr", &daddrNbo); (void)ControllerEtwGetU32Property(Record, L"saddr", &saddrNbo); (void)ControllerEtwGetU32Property(Record, L"dport", &dport); (void)ControllerEtwGetU32Property(Record, L"sport", &sport); (void)ControllerEtwGetU32Property(Record, L"size", &pktSize); if (daddrNbo != 0) { UINT8 *a = (UINT8 *)&daddrNbo; (void)StringCchPrintfA(ipDst, sizeof(ipDst), "%u.%u.%u.%u", (unsigned)a[0], (unsigned)a[1], (unsigned)a[2], (unsigned)a[3]); } if (saddrNbo != 0) { UINT8 *a = (UINT8 *)&saddrNbo; (void)StringCchPrintfA(ipSrc, sizeof(ipSrc), "%u.%u.%u.%u", (unsigned)a[0], (unsigned)a[1], (unsigned)a[2], (unsigned)a[3]); } if (ipDst[0] != '\0' && ipSrc[0] != '\0') { (void)StringCchPrintfW(event.Reason, RTL_NUMBER_OF(event.Reason), L"kernel.net op=%S src=%S:%lu dst=%S:%lu bytes=%lu", event.Operation, ipSrc, (unsigned long)sport, ipDst, (unsigned long)dport, (unsigned long)pktSize); } else if (ipDst[0] != '\0') { (void)StringCchPrintfW(event.Reason, RTL_NUMBER_OF(event.Reason), L"kernel.net op=%S dst=%S:%lu bytes=%lu", event.Operation, ipDst, (unsigned long)dport, (unsigned long)pktSize); } else { (void)StringCchPrintfW(event.Reason, RTL_NUMBER_OF(event.Reason), L"kernel.net op=%S bytes=%lu", event.Operation, (unsigned long)pktSize); } if (ipDst[0] != '\0') { (void)StringCchCopyA(event.DetectionName, RTL_NUMBER_OF(event.DetectionName), "KERNEL_NETWORK_CONNECT"); } else { (void)StringCchCopyA(event.DetectionName, RTL_NUMBER_OF(event.DetectionName), "KERNEL_NETWORK_IO"); } } else if (EventName != NULL && wcscmp(EventName, L"HandleTelemetry") == 0) { event.Family = BlackbirdIpcEtwFamilyHandle; (void)ControllerEtwGetAnsiProperty(Record, L"class", event.ClassName, RTL_NUMBER_OF(event.ClassName)); (void)ControllerEtwGetU32Property(Record, L"desiredAccess", &desiredAccess); (void)ControllerEtwGetU64Property(Record, L"originAddress", &originAddress); (void)ControllerEtwGetU32Property(Record, L"originProtect", &originProtect); (void)ControllerEtwGetWideProperty(Record, L"originPath", event.OriginPath, RTL_NUMBER_OF(event.OriginPath)); (void)ControllerEtwGetI32Property(Record, L"statusOpenProcess", &statusOpenProcess); (void)ControllerEtwGetI32Property(Record, L"statusBasicInfo", &statusBasicInfo); (void)ControllerEtwGetI32Property(Record, L"statusSectionName", &statusSectionName); (void)ControllerEtwGetU64Property(Record, L"deepAllocationBase", &deepAllocationBase); (void)ControllerEtwGetU64Property(Record, L"deepRegionSize", &deepRegionSize); (void)ControllerEtwGetU32Property(Record, L"deepRegionProtect", &deepRegionProtect); (void)ControllerEtwGetU32Property(Record, L"deepRegionState", &deepRegionState); (void)ControllerEtwGetU32Property(Record, L"deepRegionType", &deepRegionType); (void)ControllerEtwGetU32Property(Record, L"deepSampleSize", &deepSampleSize); (void)ControllerEtwCopyBinaryProperty(Record, L"deepSample", event.DeepSample, RTL_NUMBER_OF(event.DeepSample), &event.DeepSampleSize); if (event.DeepSampleSize == 0 && deepSampleSize != 0) { event.DeepSampleSize = (deepSampleSize > RTL_NUMBER_OF(event.DeepSample)) ? RTL_NUMBER_OF(event.DeepSample) : deepSampleSize; } (void)ControllerEtwGetU32Property(Record, L"frameCount", &frameCount); event.StackCount = (frameCount > BKIPC_MAX_ETW_STACK_FRAMES) ? BKIPC_MAX_ETW_STACK_FRAMES : frameCount; for (i = 0; i < RTL_NUMBER_OF(stackPropertyNames); ++i) { (void)ControllerEtwGetU64Property(Record, stackPropertyNames[i], &event.Stack[i]); } if (ControllerEtwGetBoolProperty(Record, L"execProtect", &boolValue) && boolValue) { event.Flags |= BKIPC_ETW_FLAG_HANDLE_EXEC_PROTECT; } if (ControllerEtwGetBoolProperty(Record, L"fromNtdll", &boolValue) && boolValue) { event.Flags |= BKIPC_ETW_FLAG_HANDLE_FROM_NTDLL; } if (ControllerEtwGetBoolProperty(Record, L"fromExe", &boolValue) && boolValue) { event.Flags |= BKIPC_ETW_FLAG_HANDLE_FROM_EXE; } { ULONG handleFlags = 0; if (ControllerEtwGetU32Property(Record, L"handleFlags", &handleFlags)) { event.Flags |= handleFlags & (BKIPC_ETW_FLAG_SYSCALL_EXPORT_MATCH | BKIPC_ETW_FLAG_SYSCALL_EXPORT_MISMATCH | BKIPC_ETW_FLAG_MODULE_CHAIN_SANE | BKIPC_ETW_FLAG_UNWIND_METADATA_VALID | BKIPC_ETW_FLAG_TEB_STACK_BOUNDS_VALID | BKIPC_ETW_FLAG_FRAMES_OUTSIDE_TEB_STACK); } } event.DesiredAccess = desiredAccess; event.OriginAddress = originAddress; event.OriginProtect = originProtect; event.StatusOpenProcess = statusOpenProcess; event.StatusBasicInfo = statusBasicInfo; event.StatusSectionName = statusSectionName; event.DeepAllocationBase = deepAllocationBase; event.DeepRegionSize = deepRegionSize; event.DeepRegionProtect = deepRegionProtect; event.DeepRegionState = deepRegionState; event.DeepRegionType = deepRegionType; } else if (EventName != NULL && wcscmp(EventName, L"ThreadTelemetry") == 0) { event.Family = BlackbirdIpcEtwFamilyThread; (void)ControllerEtwGetU64Property(Record, L"startAddress", &startAddress); (void)ControllerEtwGetU64Property(Record, L"imageBase", &imageBase); (void)ControllerEtwGetU64Property(Record, L"imageSize", &imageSize); (void)ControllerEtwGetU32Property(Record, L"startRegionProtect", &startRegionProtect); (void)ControllerEtwGetU32Property(Record, L"startRegionState", &startRegionState); (void)ControllerEtwGetU32Property(Record, L"startRegionType", &startRegionType); (void)ControllerEtwGetI32Property(Record, L"startRegionStatus", &startRegionStatus); (void)ControllerEtwGetU32Property(Record, L"workerFrameCount", &frameCount); event.StackCount = (frameCount > BKIPC_MAX_ETW_STACK_FRAMES) ? BKIPC_MAX_ETW_STACK_FRAMES : frameCount; for (i = 0; i < RTL_NUMBER_OF(stackPropertyNames); ++i) { (void)ControllerEtwGetU64Property(Record, stackPropertyNames[i], &event.Stack[i]); } if (ControllerEtwGetBoolProperty(Record, L"gotStart", &boolValue) && boolValue) { event.Flags |= BKIPC_ETW_FLAG_THREAD_GOT_START; } if (ControllerEtwGetBoolProperty(Record, L"gotRange", &boolValue) && boolValue) { event.Flags |= BKIPC_ETW_FLAG_THREAD_GOT_RANGE; } if (ControllerEtwGetBoolProperty(Record, L"isRemoteCreator", &boolValue) && boolValue) { event.Flags |= BKIPC_ETW_FLAG_THREAD_REMOTE_CREATOR; } if (ControllerEtwGetBoolProperty(Record, L"outsideMainImage", &boolValue) && boolValue) { event.Flags |= BKIPC_ETW_FLAG_THREAD_OUTSIDE_MAIN_IMAGE; } event.StartAddress = startAddress; event.ImageBase = imageBase; event.ImageSize = imageSize; event.StartRegionProtect = startRegionProtect; event.StartRegionState = startRegionState; event.StartRegionType = startRegionType; event.StartRegionStatus = startRegionStatus; } else if (EventName != NULL && wcscmp(EventName, L"ProcessTelemetry") == 0) { event.Family = BlackbirdIpcEtwFamilyProcess; (void)ControllerEtwGetU32Property(Record, L"sessionId", &sessionId); (void)ControllerEtwGetI32Property(Record, L"createStatus", &createStatus); (void)ControllerEtwGetWideProperty(Record, L"imagePath", event.ImagePath, RTL_NUMBER_OF(event.ImagePath)); (void)ControllerEtwGetWideProperty(Record, L"commandLine", event.CommandLine, RTL_NUMBER_OF(event.CommandLine)); if (ControllerEtwGetBoolProperty(Record, L"isCreate", &boolValue) && boolValue) { event.Flags |= BKIPC_ETW_FLAG_PROCESS_IS_CREATE; } event.SessionId = sessionId; event.CreateStatus = createStatus; } else if (EventName != NULL && wcscmp(EventName, L"ImageTelemetry") == 0) { event.Family = BlackbirdIpcEtwFamilyImage; (void)ControllerEtwGetU64Property(Record, L"imageBase", &imageBase); (void)ControllerEtwGetU64Property(Record, L"imageSize", &imageSize); (void)ControllerEtwGetWideProperty(Record, L"imagePath", event.ImagePath, RTL_NUMBER_OF(event.ImagePath)); if (ControllerEtwGetBoolProperty(Record, L"isSystemModeImage", &boolValue) && boolValue) { event.Flags |= BKIPC_ETW_FLAG_IMAGE_SYSTEM_MODE; } if (ControllerEtwGetBoolProperty(Record, L"isSignatureLevelKnown", &boolValue) && boolValue) { event.Flags |= BKIPC_ETW_FLAG_IMAGE_SIGNATURE_KNOWN; } (void)ControllerEtwGetU8Property(Record, L"signatureLevel", &signatureLevel); (void)ControllerEtwGetU8Property(Record, L"signatureType", &signatureType); event.ImageBase = imageBase; event.ImageSize = imageSize; event.SignatureLevel = signatureLevel; event.SignatureType = signatureType; } else if (EventName != NULL && wcscmp(EventName, L"RegistryTelemetry") == 0) { event.Family = BlackbirdIpcEtwFamilyRegistry; (void)ControllerEtwGetAnsiProperty(Record, L"operation", event.Operation, RTL_NUMBER_OF(event.Operation)); (void)ControllerEtwGetU32Property(Record, L"sessionId", &sessionId); (void)ControllerEtwGetU32Property(Record, L"notifyClass", ¬ifyClass); (void)ControllerEtwGetU32Property(Record, L"dataType", &dataType); (void)ControllerEtwGetU32Property(Record, L"dataSize", &dataSize); (void)ControllerEtwGetWideProperty(Record, L"keyPath", event.KeyPath, RTL_NUMBER_OF(event.KeyPath)); (void)ControllerEtwGetWideProperty(Record, L"valueName", event.ValueName, RTL_NUMBER_OF(event.ValueName)); if (ControllerEtwGetBoolProperty(Record, L"isHighValuePath", &boolValue) && boolValue) { event.Flags |= BKIPC_ETW_FLAG_REGISTRY_HIGH_VALUE; } event.SessionId = sessionId; event.NotifyClass = notifyClass; event.DataType = dataType; event.DataSize = dataSize; } else if (EventName != NULL && wcscmp(EventName, L"ApcTelemetry") == 0) { event.Family = BlackbirdIpcEtwFamilyApc; (void)ControllerEtwGetAnsiProperty(Record, L"class", event.ClassName, RTL_NUMBER_OF(event.ClassName)); (void)ControllerEtwGetU32Property(Record, L"desiredAccess", &desiredAccess); if (ControllerEtwGetBoolProperty(Record, L"isDuplicateOperation", &boolValue) && boolValue) { event.Flags |= BKIPC_ETW_FLAG_APC_DUPLICATE_OPERATION; } event.DesiredAccess = desiredAccess; } else if (EventName != NULL && wcscmp(EventName, L"DetectionTelemetry") == 0) { event.Family = BlackbirdIpcEtwFamilyDetection; (void)InterlockedIncrement(&g_EtwDetectionEvents); (void)ControllerEtwGetAnsiProperty(Record, L"detectionName", event.DetectionName, RTL_NUMBER_OF(event.DetectionName)); if (event.DetectionName[0] == '\0') { (void)StringCchCopyA(event.DetectionName, RTL_NUMBER_OF(event.DetectionName), "UNKNOWN"); } (void)ControllerEtwGetWideProperty(Record, L"reason", event.Reason, RTL_NUMBER_OF(event.Reason)); } else if (EventName != NULL && wcscmp(EventName, L"QpcTimingTelemetry") == 0) { event.Family = BlackbirdIpcEtwFamilyUserHook; event.Severity = 2u; if (callerPid != 0) { event.ProcessId = callerPid; event.CallerPid = callerPid; } if (callerTid != 0) { event.ThreadId = callerTid; } (void)StringCchCopyA(event.Operation, RTL_NUMBER_OF(event.Operation), "NtQueryPerformanceCounter"); (void)StringCchCopyW(event.EventName, RTL_NUMBER_OF(event.EventName), L"NtQueryPerformanceCounter"); (void)StringCchCopyA(event.DetectionName, RTL_NUMBER_OF(event.DetectionName), "ANTIVM_TIMING_QPC_COMPENSATION"); (void)ControllerEtwGetU64Property(Record, L"rawCounter", &rawCounter); (void)ControllerEtwGetU64Property(Record, L"virtualCounter", &virtualCounter); (void)ControllerEtwGetU64Property(Record, L"rawDelta", &rawDelta); (void)ControllerEtwGetU64Property(Record, L"virtualDelta", &virtualDelta); (void)ControllerEtwGetU64Property(Record, L"correctionTicks", &correctionTicks); (void)ControllerEtwGetU32Property(Record, L"sourceFlags", &sourceFlags); (void)ControllerEtwGetU64Property(Record, L"autoBiasTicks", &autoBiasTicks); (void)ControllerEtwGetI32Property(Record, L"queryStatus", &queryStatus); if (queryStatus != 0) { return; } event.HookArgCount = 7u; event.HookArgs[0] = rawCounter; event.HookArgs[1] = virtualCounter; event.HookArgs[2] = rawDelta; event.HookArgs[3] = virtualDelta; event.HookArgs[4] = correctionTicks; event.HookArgs[5] = sourceFlags; event.HookArgs[6] = autoBiasTicks; if ((sourceFlags & BK_QPC_TIMING_SOURCE_SUSPEND_PAUSE) != 0) { event.Severity = 3u; } (void)StringCchPrintfW( event.Reason, RTL_NUMBER_OF(event.Reason), L"anti-virtualization.qpc raw=0x%llX virtual=0x%llX rawDelta=0x%llX virtualDelta=0x%llX correction=%lld sourceFlags=0x%X autoBias=%lld status=0x%08X", (unsigned long long)rawCounter, (unsigned long long)virtualCounter, (unsigned long long)rawDelta, (unsigned long long)virtualDelta, (long long)correctionTicks, (unsigned int)sourceFlags, (long long)autoBiasTicks, (unsigned int)queryStatus); } else if (EventName != NULL && wcscmp(EventName, L"NtApiTelemetry") == 0) { event.Family = BlackbirdIpcEtwFamilyUserHook; (void)ControllerEtwGetAnsiProperty(Record, L"api", apiName, RTL_NUMBER_OF(apiName)); if (apiName[0] == '\0') { (void)StringCchCopyA(apiName, RTL_NUMBER_OF(apiName), "UNKNOWN_NTAPI"); } (void)StringCchCopyA(event.Operation, RTL_NUMBER_OF(event.Operation), apiName); (void)MultiByteToWideChar(CP_ACP, 0, apiName, -1, event.EventName, (int)RTL_NUMBER_OF(event.EventName)); (void)ControllerEtwGetU64Property(Record, L"arg0", &arg0); (void)ControllerEtwGetU64Property(Record, L"arg1", &arg1); (void)ControllerEtwGetU64Property(Record, L"arg2", &arg2); (void)ControllerEtwGetU64Property(Record, L"arg3", &arg3); (void)ControllerEtwGetU64Property(Record, L"arg4", &arg4); (void)ControllerEtwGetU64Property(Record, L"arg5", &arg5); (void)ControllerEtwGetU64Property(Record, L"arg6", &arg6); (void)ControllerEtwGetU64Property(Record, L"arg7", &arg7); (void)ControllerEtwGetU32Property(Record, L"execFlags", &execFlags); (void)ControllerEtwGetI32Property(Record, L"status", &callStatus); if (callStatus != 0) { return; } event.HookArgCount = BKIPC_MAX_HOOK_ARGS; event.HookArgs[0] = arg0; event.HookArgs[1] = arg1; event.HookArgs[2] = arg2; event.HookArgs[3] = arg3; event.HookArgs[4] = arg4; event.HookArgs[5] = arg5; event.HookArgs[6] = arg6; event.HookArgs[7] = arg7; if (callerPid != 0) { event.ProcessId = callerPid; event.CallerPid = callerPid; } if (callerTid != 0) { event.ThreadId = callerTid; } if ((execFlags & BK_NTAPI_EXEC_FLAG_CALLER_KERNEL) != 0) { event.Flags |= BKIPC_ETW_FLAG_HOOK_KERNEL_CALLER; } if ((execFlags & BK_NTAPI_EXEC_FLAG_CALLER_USER) != 0) { event.Flags |= BKIPC_ETW_FLAG_HOOK_USER_CALLER; } if ((execFlags & BK_NTAPI_EXEC_FLAG_TARGET_CURRENT_PROCESS) != 0) { event.Flags |= BKIPC_ETW_FLAG_HOOK_TARGET_CURRENT_PROCESS; } if ((execFlags & BK_NTAPI_EXEC_FLAG_SECTION_IMAGE) != 0) { event.Flags |= BKIPC_ETW_FLAG_HOOK_SECTION_IMAGE; } if ((lstrcmpiA(apiName, "NtAllocateVirtualMemory") == 0 || lstrcmpiA(apiName, "NtWriteVirtualMemory") == 0 || lstrcmpiA(apiName, "NtProtectVirtualMemory") == 0 || lstrcmpiA(apiName, "NtMapViewOfSection") == 0 || lstrcmpiA(apiName, "NtMapViewOfSectionEx") == 0) && arg6 != 0 && arg6 <= 0xFFFFFFFFull && (UINT32)arg6 != callerPid) { event.TargetPid = (UINT32)arg6; } else if ((lstrcmpiA(apiName, "NtUnmapViewOfSection") == 0 || lstrcmpiA(apiName, "NtUnmapViewOfSectionEx") == 0 || lstrcmpiA(apiName, "NtCreateThread") == 0 || lstrcmpiA(apiName, "NtCreateThreadEx") == 0) && arg2 != 0 && arg2 <= 0xFFFFFFFFull && (UINT32)arg2 != callerPid) { event.TargetPid = (UINT32)arg2; } else if (lstrcmpiA(apiName, "NtQueueApcThread") == 0 && arg6 != 0 && arg6 <= 0xFFFFFFFFull && (UINT32)arg6 != callerPid) { event.TargetPid = (UINT32)arg6; } else if ((lstrcmpiA(apiName, "NtQueueApcThreadEx") == 0 || lstrcmpiA(apiName, "NtQueueApcThreadEx2") == 0) && arg7 != 0 && arg7 <= 0xFFFFFFFFull && (UINT32)arg7 != callerPid) { event.TargetPid = (UINT32)arg7; } PCWSTR callerModeText = (event.Flags & BKIPC_ETW_FLAG_HOOK_KERNEL_CALLER) != 0 ? L"kernel" : L"user"; PCWSTR targetText = (event.Flags & BKIPC_ETW_FLAG_HOOK_TARGET_CURRENT_PROCESS) != 0 ? L"current" : L"external"; PCWSTR sectionText = (event.Flags & BKIPC_ETW_FLAG_HOOK_SECTION_IMAGE) != 0 ? L"image" : L"private"; if (lstrcmpiA(apiName, "NtAllocateVirtualMemory") == 0) { event.Severity = 2u; (void)StringCchCopyA(event.DetectionName, RTL_NUMBER_OF(event.DetectionName), "USERMODE_MEMORY_ACTIVITY"); (void)StringCchPrintfW( event.Reason, RTL_NUMBER_OF(event.Reason), L"kind=kernel_ntapi callerMode=%ws target=%ws memory.alloc base=0x%llX size=0x%llX allocType=0x%llX protect=0x%llX targetPid=%llu status=0x%08X", callerModeText, targetText, (unsigned long long)arg1, (unsigned long long)arg2, (unsigned long long)arg4, (unsigned long long)arg5, (unsigned long long)arg6, (unsigned int)callStatus); } else if (lstrcmpiA(apiName, "NtProtectVirtualMemory") == 0) { const BOOL sr71ProtectBlocked = (arg5 != 0 && callStatus == (NTSTATUS)0xC0000022L); event.Severity = sr71ProtectBlocked ? 8u : 3u; (void)StringCchCopyA(event.DetectionName, RTL_NUMBER_OF(event.DetectionName), sr71ProtectBlocked ? "SR71_HOOK_PROTECT_BLOCKED" : "USERMODE_MEMORY_ACTIVITY"); (void)StringCchPrintfW( event.Reason, RTL_NUMBER_OF(event.Reason), L"kind=kernel_ntapi callerMode=%ws target=%ws memory.protect base=0x%llX size=0x%llX newProtect=0x%llX oldProtect=0x%llX blockedSr71=%llu targetPid=%llu status=0x%08X", callerModeText, targetText, (unsigned long long)arg1, (unsigned long long)arg2, (unsigned long long)arg3, (unsigned long long)arg4, (unsigned long long)arg5, (unsigned long long)arg6, (unsigned int)callStatus); } else if (lstrcmpiA(apiName, "NtWriteVirtualMemory") == 0) { const BOOL sr71WriteBlocked = (arg5 != 0 && callStatus == (NTSTATUS)0xC0000022L); event.Severity = sr71WriteBlocked ? 8u : 3u; (void)StringCchCopyA(event.DetectionName, RTL_NUMBER_OF(event.DetectionName), sr71WriteBlocked ? "SR71_HOOK_WRITE_BLOCKED" : "USERMODE_MEMORY_ACTIVITY"); (void)StringCchPrintfW( event.Reason, RTL_NUMBER_OF(event.Reason), L"kind=kernel_ntapi callerMode=%ws target=%ws memory.write base=0x%llX size=0x%llX bytesWritten=0x%llX blockedSr71=%llu targetPid=%llu status=0x%08X", callerModeText, targetText, (unsigned long long)arg1, (unsigned long long)arg3, (unsigned long long)arg4, (unsigned long long)arg5, (unsigned long long)arg6, (unsigned int)callStatus); } else if (lstrcmpiA(apiName, "NtUnmapViewOfSection") == 0 || lstrcmpiA(apiName, "NtUnmapViewOfSectionEx") == 0) { const BOOL remoteUnmap = event.TargetPid != 0 && event.TargetPid != event.ProcessId; event.Severity = remoteUnmap ? 6u : 3u; (void)StringCchCopyA(event.DetectionName, RTL_NUMBER_OF(event.DetectionName), "USERMODE_SECTION_UNMAP_ACTIVITY"); (void)StringCchPrintfW( event.Reason, RTL_NUMBER_OF(event.Reason), L"kind=kernel_ntapi callerMode=%ws section.unmap api=%S processHandle=0x%llX base=0x%llX targetPid=%llu flags=0x%llX status=0x%08X", callerModeText, apiName, (unsigned long long)arg0, (unsigned long long)arg1, (unsigned long long)arg2, (unsigned long long)arg3, (unsigned int)callStatus); } else if (lstrcmpiA(apiName, "NtCreateThread") == 0 || lstrcmpiA(apiName, "NtCreateThreadEx") == 0) { const BOOL oldCreateThread = lstrcmpiA(apiName, "NtCreateThread") == 0; const BOOL remoteThread = event.TargetPid != 0 && event.TargetPid != event.ProcessId; const ULONGLONG startRoutine = oldCreateThread ? 0ull : arg3; const ULONGLONG argument = oldCreateThread ? 0ull : arg4; const ULONGLONG targetTid = oldCreateThread ? arg3 : arg6; const ULONGLONG createFlags = oldCreateThread ? 0ull : arg5; const BOOL createSuspended = oldCreateThread ? (arg4 != 0) : ((arg5 & 0x1ull) != 0); const BOOL hideFromDebugger = !oldCreateThread && ((arg5 & 0x4ull) != 0); event.Severity = remoteThread ? 6u : 2u; (void)StringCchCopyA(event.DetectionName, RTL_NUMBER_OF(event.DetectionName), "USERMODE_THREAD_CREATE_ACTIVITY"); (void)StringCchPrintfW( event.Reason, RTL_NUMBER_OF(event.Reason), L"kind=kernel_ntapi callerMode=%ws thread.create api=%S processHandle=0x%llX threadHandle=0x%llX targetPid=%llu targetTid=%llu startRoutine=0x%llX argument=0x%llX createFlags=0x%llX createSuspended=%u hideFromDebugger=%u status=0x%08X", callerModeText, apiName, (unsigned long long)arg0, (unsigned long long)arg1, (unsigned long long)arg2, (unsigned long long)targetTid, (unsigned long long)startRoutine, (unsigned long long)argument, (unsigned long long)createFlags, (unsigned int)createSuspended, (unsigned int)hideFromDebugger, (unsigned int)callStatus); } else if (lstrcmpiA(apiName, "NtQueueApcThread") == 0 || lstrcmpiA(apiName, "NtQueueApcThreadEx") == 0 || lstrcmpiA(apiName, "NtQueueApcThreadEx2") == 0) { const BOOL ex2 = lstrcmpiA(apiName, "NtQueueApcThreadEx2") == 0; const BOOL ex = lstrcmpiA(apiName, "NtQueueApcThreadEx") == 0; const ULONGLONG routine = ex2 ? arg3 : (ex ? arg2 : arg1); const ULONGLONG targetPidForReason = (ex || ex2) ? arg7 : arg6; event.Severity = (event.TargetPid != 0 && event.TargetPid != event.ProcessId) ? 6u : 2u; (void)StringCchCopyA(event.DetectionName, RTL_NUMBER_OF(event.DetectionName), "USERMODE_APC_QUEUE_ACTIVITY"); (void)StringCchPrintfW( event.Reason, RTL_NUMBER_OF(event.Reason), L"kind=kernel_ntapi callerMode=%ws thread.apc api=%S threadHandle=0x%llX routine=0x%llX targetPid=%llu status=0x%08X", callerModeText, apiName, (unsigned long long)arg0, (unsigned long long)routine, (unsigned long long)targetPidForReason, (unsigned int)callStatus); } else if (lstrcmpiA(apiName, "NtMapViewOfSection") == 0 || lstrcmpiA(apiName, "NtMapViewOfSectionEx") == 0) { const BOOL remoteMap = event.TargetPid != 0 && event.TargetPid != event.ProcessId; event.Severity = remoteMap ? 3u : 1u; (void)StringCchCopyA(event.DetectionName, RTL_NUMBER_OF(event.DetectionName), "USERMODE_SECTION_MAP_ACTIVITY"); (void)StringCchPrintfW( event.Reason, RTL_NUMBER_OF(event.Reason), L"kind=kernel_ntapi callerMode=%ws section.map api=%S sectionHandle=0x%llX processHandle=0x%llX base=0x%llX size=0x%llX protect=0x%llX targetPid=%llu status=0x%08X", callerModeText, apiName, (unsigned long long)arg0, (unsigned long long)arg1, (unsigned long long)arg2, (unsigned long long)arg3, (unsigned long long)arg4, (unsigned long long)arg6, (unsigned int)callStatus); } else if (lstrcmpiA(apiName, "NtQueryInformationProcess") == 0 || lstrcmpiA(apiName, "NtQuerySystemInformation") == 0) { event.Severity = 3u; (void)StringCchCopyA(event.DetectionName, RTL_NUMBER_OF(event.DetectionName), "USERMODE_PROCESS_RECON"); (void)StringCchPrintfW( event.Reason, RTL_NUMBER_OF(event.Reason), L"kind=kernel_ntapi callerMode=%ws target=%ws process.recon api=%S c0=0x%llX c1=0x%llX c2=0x%llX c3=0x%llX status=0x%08X", callerModeText, targetText, apiName, (unsigned long long)arg0, (unsigned long long)arg1, (unsigned long long)arg2, (unsigned long long)arg3, (unsigned int)callStatus); } else { event.Severity = 2u; (void)StringCchCopyA(event.DetectionName, RTL_NUMBER_OF(event.DetectionName), "USERMODE_HOOK_API_CALL"); (void)StringCchPrintfW( event.Reason, RTL_NUMBER_OF(event.Reason), L"kind=kernel_ntapi callerMode=%ws target=%ws section=%ws api=%S c0=0x%llX c1=0x%llX c2=0x%llX c3=0x%llX c4=0x%llX c5=0x%llX c6=0x%llX c7=0x%llX status=0x%08X", callerModeText, targetText, sectionText, apiName, (unsigned long long)arg0, (unsigned long long)arg1, (unsigned long long)arg2, (unsigned long long)arg3, (unsigned long long)arg4, (unsigned long long)arg5, (unsigned long long)arg6, (unsigned long long)arg7, (unsigned int)callStatus); } } else if (EventName != NULL && wcscmp(EventName, L"SystemInformationTelemetry") == 0) { event.Family = BlackbirdIpcEtwFamilyUserHook; event.Severity = 3u; if (callerPid != 0) { event.ProcessId = callerPid; event.CallerPid = callerPid; } if (callerTid != 0) { event.ThreadId = callerTid; } (void)StringCchCopyA(event.Operation, RTL_NUMBER_OF(event.Operation), "NtQuerySystemInformation"); (void)StringCchCopyW(event.EventName, RTL_NUMBER_OF(event.EventName), L"NtQuerySystemInformation"); (void)StringCchCopyA(event.DetectionName, RTL_NUMBER_OF(event.DetectionName), "USERMODE_PROCESS_RECON"); (void)ControllerEtwGetU32Property(Record, L"systemInformationClass", &systemInformationClass); (void)ControllerEtwGetU32Property(Record, L"systemInformationLength", &systemInformationLength); (void)ControllerEtwGetU32Property(Record, L"returnLength", &returnLength); (void)ControllerEtwGetI32Property(Record, L"queryStatus", &queryStatus); if (queryStatus != 0) { return; } event.HookArgCount = 6u; event.HookArgs[0] = systemInformationClass; event.HookArgs[1] = 0ull; event.HookArgs[2] = systemInformationLength; event.HookArgs[3] = 0ull; event.HookArgs[4] = returnLength; event.HookArgs[5] = (UINT32)queryStatus; (void)StringCchPrintfW( event.Reason, RTL_NUMBER_OF(event.Reason), L"process.recon api=NtQuerySystemInformation systemInformationClass=0x%X systemInformationLength=0x%X returnLength=0x%X status=0x%08X", (unsigned int)systemInformationClass, (unsigned int)systemInformationLength, (unsigned int)returnLength, (unsigned int)queryStatus); } else { if (event.Source == BlackbirdIpcEtwSourceThreatIntel) { event.Family = BlackbirdIpcEtwFamilyThreatIntel; } } ControllerProcessHollowingEtwRecord(Record, EventName, &event); if (EventName != NULL && wcscmp(EventName, L"ProcessTelemetry") == 0) { DWORD sourcePid32 = 0; DWORD childPid32 = 0; if (event.ProcessId != 0 && event.ProcessId <= 0xFFFFFFFFull) { childPid32 = (DWORD)event.ProcessId; if (event.CreatorProcessId != 0 && event.CreatorProcessId <= 0xFFFFFFFFull) { sourcePid32 = (DWORD)event.CreatorProcessId; } else if (event.ParentProcessId != 0 && event.ParentProcessId <= 0xFFFFFFFFull) { sourcePid32 = (DWORD)event.ParentProcessId; } } if (sourcePid32 != 0 && childPid32 != 0 && sourcePid32 != childPid32) { ControllerExpandMonitoringGraph(sourcePid32, childPid32, BK_CONTROLLER_DRIVER_STREAM_MASK); } } if (event.ProcessId != 0 && event.Severity >= 2u) { event.Reserved2 = ControllerComputeEtwDetectionTraits(event); traitsComputed = TRUE; UINT32 heurFlags = ControllerHeurFlagsFromDetectionTraits(event.Reserved2); ControllerHeuristicsObserveEvent((DWORD)event.ProcessId, event.Severity, heurFlags); } if (!ControllerShouldForwardEtwRecord(Record, EventName, &event)) { return; } if (!traitsComputed) { event.Reserved2 = ControllerComputeEtwDetectionTraits(event); } ControllerDispatchEtwEvent(&event); } static DWORD WINAPI ControllerEtwThreadProc(_In_ LPVOID Context) { DWORD status; UNREFERENCED_PARAMETER(Context); if (g_EtwSession == NULL) { return 1; } status = BkscRunEtwSession(g_EtwSession); ControllerLog("[ETW] run loop exited status=%lu tiEvents=%ld detectionEvents=%ld\n", status, InterlockedCompareExchange(&g_EtwTiEvents, 0, 0), InterlockedCompareExchange(&g_EtwDetectionEvents, 0, 0)); return status; } static BOOL ControllerIsThreatIntelRetryableError(_In_ DWORD ErrorCode) { if (ErrorCode == ERROR_SUCCESS) { return FALSE; } if (ErrorCode == ERROR_ACCESS_DENIED || ErrorCode == ERROR_PRIVILEGE_NOT_HELD || ErrorCode == ERROR_NOT_SUPPORTED) { return FALSE; } return TRUE; } static VOID WINAPI ControllerEtwWarmupCallback(_In_ PEVENT_RECORD Record, _In_opt_z_ PCWSTR EventName, _In_opt_ PVOID Context) { UNREFERENCED_PARAMETER(Record); UNREFERENCED_PARAMETER(EventName); UNREFERENCED_PARAMETER(Context); } static BOOL ControllerWarmupThreatIntelProvider(_Out_opt_ DWORD *ErrorCode) { WCHAR warmupSessionName[96]; BKSC_ETW_PROVIDER_CONFIG provider; BKSC_ETW_SESSION_CONFIG config; BKSC_ETW_SESSION *warmupSession = NULL; BOOL ok; DWORD err = ERROR_SUCCESS; ZeroMemory(&provider, sizeof(provider)); provider.ProviderId = BKSC_PROVIDER_GUID_TI; provider.Level = TRACE_LEVEL_INFORMATION; provider.MatchAnyKeyword = ~0ULL; provider.MatchAllKeyword = 0; ZeroMemory(&config, sizeof(config)); (void)StringCchPrintfW(warmupSessionName, RTL_NUMBER_OF(warmupSessionName), L"%ws-TiWarmup", BK_CONTROLLER_ETW_SESSION_NAMEW); config.SessionName = warmupSessionName; config.Providers = &provider; config.ProviderCount = 1; config.Callback = ControllerEtwWarmupCallback; config.CallbackContext = NULL; ok = BkscStartEtwSession(&config, &warmupSession); if (ok) { Sleep(120); BkscStopEtwSession(warmupSession); warmupSession = NULL; err = ERROR_SUCCESS; } else { err = GetLastError(); } if (ErrorCode != NULL) { *ErrorCode = err; } return ok; } static VOID ControllerTryRecoverThreatIntelSession(_In_ DWORD InitialTiError) { const DWORD maxAttempts = 3; DWORD attempt; DWORD tiErr = InitialTiError; if (g_EtwSession == NULL || g_ThreatIntelEnabled || !ControllerIsThreatIntelRetryableError(InitialTiError)) { return; } for (attempt = 1; attempt <= maxAttempts && !ControllerShouldStop(); ++attempt) { DWORD warmErr = ERROR_SUCCESS; BOOL warmOk; ControllerLog("[ETW][WARN] TI inactive after subscribe (err=%lu). warmup+restart attempt %lu/%lu\n", tiErr, attempt, maxAttempts); warmOk = ControllerWarmupThreatIntelProvider(&warmErr); if (!warmOk) { ControllerLog("[ETW][WARN] TI warmup failed attempt=%lu err=%lu\n", attempt, warmErr); } BkscStopEtwSession(g_EtwSession); g_EtwSession = NULL; g_ThreatIntelEnabled = FALSE; if (!BkscStartBlackbirdEtwSession(BK_CONTROLLER_ETW_SESSION_NAMEW, TRUE, ControllerEtwCallback, NULL, &g_EtwSession, &g_ThreatIntelEnabled)) { DWORD restartErr = GetLastError(); tiErr = BkscGetLastThreatIntelEnableError(); ControllerLog("[ETW][WARN] restart after TI warmup failed attempt=%lu startErr=%lu tiErr=%lu\n", attempt, restartErr, tiErr); if (attempt < maxAttempts) { Sleep(180 * attempt); } continue; } tiErr = BkscGetLastThreatIntelEnableError(); g_ThreatIntelEnableError = tiErr; if (g_ThreatIntelEnabled) { ControllerLog("[ETW] TI provider enabled after warmup attempt %lu (tiEnableErr=%lu)\n", attempt, tiErr); return; } ControllerLog("[ETW][WARN] TI still inactive after restart attempt=%lu tiEnableErr=%lu\n", attempt, tiErr); if (!ControllerIsThreatIntelRetryableError(tiErr)) { return; } if (attempt < maxAttempts) { Sleep(180 * attempt); } } UNREFERENCED_PARAMETER(tiErr); } static BOOL ControllerStartEtwSession(VOID) { DWORD tiErr = ERROR_SUCCESS; ControllerCleanupStaleEtwSessions(); (void)InterlockedExchange(&g_EtwTiEvents, 0); (void)InterlockedExchange(&g_EtwDetectionEvents, 0); g_ThreatIntelEnabled = FALSE; g_ThreatIntelEnableError = ERROR_SUCCESS; if (!BkscStartBlackbirdEtwSession(BK_CONTROLLER_ETW_SESSION_NAMEW, TRUE, ControllerEtwCallback, NULL, &g_EtwSession, &g_ThreatIntelEnabled)) { DWORD err = GetLastError(); g_ThreatIntelEnableError = err; ControllerLog("[WARN] BlackbirdController: failed to start ETW TI session name=%ws (%lu)\n", BK_CONTROLLER_ETW_SESSION_NAMEW, err); g_EtwSession = NULL; g_ThreatIntelEnabled = FALSE; return FALSE; } tiErr = BkscGetLastThreatIntelEnableError(); g_ThreatIntelEnableError = tiErr; if (!g_ThreatIntelEnabled) { ControllerTryRecoverThreatIntelSession(tiErr); tiErr = BkscGetLastThreatIntelEnableError(); g_ThreatIntelEnableError = tiErr; } if (g_EtwSession == NULL) { ControllerLog("[WARN] BlackbirdController: ETW session unavailable after TI recovery attempts\n"); return FALSE; } g_EtwThread = CreateThread(NULL, 0, ControllerEtwThreadProc, NULL, 0, NULL); if (g_EtwThread == NULL) { ControllerLog("[WARN] BlackbirdController: failed to start ETW thread (%lu)\n", GetLastError()); BkscStopEtwSession(g_EtwSession); g_EtwSession = NULL; g_ThreatIntelEnabled = FALSE; return FALSE; } ControllerLog("[*] BlackbirdController: ETW session started. threat-intel=%s tiEnableErr=%lu\n", g_ThreatIntelEnabled ? "enabled" : "disabled", tiErr); return TRUE; } static VOID ControllerStopEtwSession(VOID) { if (g_EtwSession != NULL) { BkscStopEtwSession(g_EtwSession); g_EtwSession = NULL; } if (g_EtwThread != NULL) { (void)WaitForSingleObject(g_EtwThread, 3000); CloseHandle(g_EtwThread); g_EtwThread = NULL; } ControllerStopEtwSessionByNameBestEffort(BK_CONTROLLER_ETW_SESSION_NAMEW, "service-stop"); ControllerLog("[ETW] session stopped tiEvents=%ld detectionEvents=%ld\n", InterlockedCompareExchange(&g_EtwTiEvents, 0, 0), InterlockedCompareExchange(&g_EtwDetectionEvents, 0, 0)); } static BOOL ControllerStartCore(VOID) { DWORD serverThreadsStarted = 0; DWORD serverThreadIndex; InterlockedExchange(&g_ControllerProtectionReady, 0); InterlockedExchange(&g_ControllerProtectionArmed, 0); g_ClientList = NULL; g_ClientCount = 0; g_PidIndexCount = 0; ZeroMemory(g_ClientSlots, sizeof(g_ClientSlots)); ZeroMemory(g_PidIndex, sizeof(g_PidIndex)); ControllerLog("[*] BlackbirdController: core start requested\n"); ControllerResetHollowingState(); ControllerHeuristicsInitialize(); g_StopEvent = CreateEventW(NULL, TRUE, FALSE, NULL); if (g_StopEvent == NULL) { ControllerLog("[-] BlackbirdController: failed to create stop event (%lu)\n", GetLastError()); return FALSE; } if (!ControllerStartHollowingWorkers()) { ControllerLog("[WARN] controller hollowing worker start failed (%lu)\n", GetLastError()); } if (!ControllerSymbolServiceStart()) { ControllerLog("[WARN] controller symbol service start failed (%lu)\n", GetLastError()); } BkscUseServiceProtocol(); g_DriverHandle = BkscOpenControlDevice(); if (g_DriverHandle == INVALID_HANDLE_VALUE) { ControllerLog("[WARN] BlackbirdController: initial driver open failed (%lu). retrying in background.\n", GetLastError()); } else { ControllerLog("[DRIVER] initial open succeeded\n"); } InterlockedExchange(&g_ControllerProtectionReady, 1); ControllerTryMarkProtectedReady(g_DriverHandle, TRUE); if (!ControllerStartDriverSubscriptionWorker()) { ControllerLog("[WARN] controller subscription apply worker start failed (%lu)\n", GetLastError()); return FALSE; } (void)ControllerStartEtwSession(); if (!ControllerStartNetService(g_DriverHandle)) { ControllerLog("[NETSVC][INFO] network service offline; controller core continuing without node networking\n"); } ControllerTryMarkProtectedReady(g_DriverHandle, TRUE); g_DriverPumpThread = CreateThread(NULL, 0, ControllerDriverPumpThreadProc, NULL, 0, NULL); if (g_DriverPumpThread == NULL) { ControllerLog("[-] BlackbirdController: failed to start driver pump thread (%lu)\n", GetLastError()); return FALSE; } ZeroMemory(g_ServerAcceptThreads, sizeof(g_ServerAcceptThreads)); ZeroMemory(g_HookAcceptThreads, sizeof(g_HookAcceptThreads)); for (serverThreadIndex = 0; serverThreadIndex < BK_CONTROLLER_SERVER_ACCEPT_THREADS; serverThreadIndex += 1) { HANDLE thread = CreateThread(NULL, 0, ControllerServerThreadProc, (LPVOID)&g_ControlEndpointContext, 0, NULL); if (thread == NULL) { ControllerLog("[WARN] BlackbirdController: failed to start control accept thread %lu (%lu)\n", serverThreadIndex, GetLastError()); break; } g_ServerAcceptThreads[serverThreadIndex] = thread; serverThreadsStarted += 1; } if (serverThreadsStarted == 0) { ControllerLog("[-] BlackbirdController: failed to start any control accept thread\n"); return FALSE; } g_ServerThread = g_ServerAcceptThreads[0]; if (serverThreadsStarted < BK_CONTROLLER_SERVER_ACCEPT_THREADS) { ControllerLog("[WARN] BlackbirdController: control accept thread pool reduced to %lu/%lu\n", serverThreadsStarted, (DWORD)BK_CONTROLLER_SERVER_ACCEPT_THREADS); } for (serverThreadIndex = 0; serverThreadIndex < BK_CONTROLLER_HOOK_ACCEPT_THREADS; serverThreadIndex += 1) { HANDLE thread = CreateThread(NULL, 0, ControllerServerThreadProc, (LPVOID)&g_HookEndpointContext, 0, NULL); if (thread == NULL) { ControllerLog("[WARN] BlackbirdController: failed to start hook accept thread %lu (%lu)\n", serverThreadIndex, GetLastError()); break; } g_HookAcceptThreads[serverThreadIndex] = thread; } ControllerLog("[*] BlackbirdController: IPC endpoints online control=%ls hook=%ls\n", BKIPC_PIPE_NAME, BKIPC_HOOK_PIPE_NAME); ControllerTryMarkProtectedReady(g_DriverHandle, TRUE); return TRUE; } static VOID ControllerWakeServerPipe(_In_z_ PCWSTR PipeName, _In_ DWORD Attempts) { DWORD index; if (PipeName == NULL || PipeName[0] == L'\0') { return; } for (index = 0; index < Attempts; index += 1) { HANDLE pipe = CreateFileW(PipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if (pipe != INVALID_HANDLE_VALUE) { CloseHandle(pipe); } } } static VOID ControllerDisarmRuntimeForTeardown(_In_z_ PCSTR Reason) { static const DWORD teardownMask = BK_RUNTIME_FLAG_ANTI_VIRTUALIZATION | BK_RUNTIME_FLAG_SELF_HIDE | BK_RUNTIME_FLAG_INTERFACE_PROTECTED_ACCESS | BK_RUNTIME_FLAG_CONTROLLER_PROTECTED_ACCESS | BK_RUNTIME_FLAG_NTAPI_HOOKS_DISARMED | BK_RUNTIME_FLAG_QPC_TIMING_DISABLED; static const DWORD teardownFlags = BK_RUNTIME_FLAG_NTAPI_HOOKS_DISARMED | BK_RUNTIME_FLAG_QPC_TIMING_DISABLED; EnterCriticalSection(g_DriverLock.get()); if (g_DriverHandle != INVALID_HANDLE_VALUE) { if (!BkscSetRuntimeConfig(g_DriverHandle, teardownFlags, teardownMask)) { ControllerLog("[DRIVER][WARN] teardown runtime disarm failed reason=%s err=%lu\n", Reason, GetLastError()); } else { ControllerLog("[DRIVER] teardown runtime disarmed reason=%s flags=0x%08lX mask=0x%08lX\n", Reason, teardownFlags, teardownMask); } } LeaveCriticalSection(g_DriverLock.get()); InterlockedExchange(&g_ControllerProtectionReady, 0); InterlockedExchange(&g_ControllerProtectionArmed, 0); } static VOID ControllerStopCore(VOID) { DWORD waitMs = 0; DWORD threadIndex = 0; ControllerLog("[*] BlackbirdController: stop requested\n"); if (g_StopEvent != NULL) { (void)SetEvent(g_StopEvent); } ControllerDisarmRuntimeForTeardown("stop-core-begin"); ControllerWakeServerPipe(BKIPC_PIPE_NAME, (DWORD)BK_CONTROLLER_SERVER_ACCEPT_THREADS); ControllerWakeServerPipe(BKIPC_HOOK_PIPE_NAME, (DWORD)BK_CONTROLLER_HOOK_ACCEPT_THREADS); ControllerStopDriverSubscriptionWorker(); for (threadIndex = 0; threadIndex < BK_CONTROLLER_SERVER_ACCEPT_THREADS; threadIndex += 1) { HANDLE thread = g_ServerAcceptThreads[threadIndex]; if (thread != NULL) { (void)WaitForSingleObject(thread, 3000); CloseHandle(thread); g_ServerAcceptThreads[threadIndex] = NULL; } } for (threadIndex = 0; threadIndex < BK_CONTROLLER_HOOK_ACCEPT_THREADS; threadIndex += 1) { HANDLE thread = g_HookAcceptThreads[threadIndex]; if (thread != NULL) { (void)WaitForSingleObject(thread, 3000); CloseHandle(thread); g_HookAcceptThreads[threadIndex] = NULL; } } g_ServerThread = NULL; if (g_DriverPumpThread != NULL) { (void)WaitForSingleObject(g_DriverPumpThread, 3000); CloseHandle(g_DriverPumpThread); g_DriverPumpThread = NULL; } EnterCriticalSection(g_ClientListLock.get()); { PBK_CONTROLLER_CLIENT client; for (client = g_ClientList; client != NULL; client = client->Next) { if (client->Pipe != INVALID_HANDLE_VALUE) { (void)CancelIoEx(client->Pipe, NULL); (void)DisconnectNamedPipe(client->Pipe); } } } LeaveCriticalSection(g_ClientListLock.get()); while (waitMs < 2000) { DWORD remaining; EnterCriticalSection(g_ClientListLock.get()); remaining = g_ClientCount; LeaveCriticalSection(g_ClientListLock.get()); if (remaining == 0) { break; } Sleep(50); waitMs += 50; } ControllerStopEtwSession(); ControllerStopNetService(); EnterCriticalSection(g_DriverLock.get()); if (g_DriverHandle != INVALID_HANDLE_VALUE) { CloseHandle(g_DriverHandle); g_DriverHandle = INVALID_HANDLE_VALUE; } LeaveCriticalSection(g_DriverLock.get()); ControllerStopHollowingWorkers(); if (g_StopEvent != NULL) { CloseHandle(g_StopEvent); g_StopEvent = NULL; } ControllerSymbolServiceStop(); ControllerResetHollowingState(); ControllerHeuristicsUninitialize(); ControllerLog("[*] BlackbirdController: core stopped\n"); } static DWORD WINAPI ControllerServiceControlHandlerEx(_In_ DWORD ControlCode, _In_ DWORD EventType, _In_ LPVOID EventData, _In_ LPVOID Context) { UNREFERENCED_PARAMETER(EventType); UNREFERENCED_PARAMETER(EventData); UNREFERENCED_PARAMETER(Context); if (ControlCode == SERVICE_CONTROL_STOP || ControlCode == SERVICE_CONTROL_SHUTDOWN) { ControllerLog("[*] BlackbirdController: service control received code=%lu\n", ControlCode); ControllerUpdateServiceStatus(SERVICE_STOP_PENDING, NO_ERROR, 3000); if (g_StopEvent != NULL) { (void)SetEvent(g_StopEvent); } return NO_ERROR; } return ERROR_CALL_NOT_IMPLEMENTED; } static VOID WINAPI ControllerServiceMain(_In_ DWORD Argc, _In_reads_(Argc) LPWSTR *Argv) { UNREFERENCED_PARAMETER(Argc); UNREFERENCED_PARAMETER(Argv); ZeroMemory(&g_ServiceStatus, sizeof(g_ServiceStatus)); g_ServiceStatusHandle = RegisterServiceCtrlHandlerExW(BK_CONTROLLER_SERVICE_NAMEW, ControllerServiceControlHandlerEx, NULL); if (g_ServiceStatusHandle == NULL) { return; } ControllerLog("[*] BlackbirdController: service main entered\n"); ControllerUpdateServiceStatus(SERVICE_START_PENDING, NO_ERROR, 6000); if (!ControllerStartCore()) { ControllerUpdateServiceStatus(SERVICE_STOPPED, ERROR_SERVICE_SPECIFIC_ERROR, 0); ControllerStopCore(); return; } ControllerUpdateServiceStatus(SERVICE_RUNNING, NO_ERROR, 0); ControllerLog("[*] BlackbirdController: service running\n"); (void)WaitForSingleObject(g_StopEvent, INFINITE); ControllerUpdateServiceStatus(SERVICE_STOP_PENDING, NO_ERROR, 3000); ControllerStopCore(); ControllerUpdateServiceStatus(SERVICE_STOPPED, NO_ERROR, 0); } int __cdecl wmain(_In_ int argc, _In_reads_(argc) wchar_t **argv) { SERVICE_TABLE_ENTRYW table[] = {{(LPWSTR)BK_CONTROLLER_SERVICE_NAMEW, ControllerServiceMain}, {NULL, NULL}}; BOOL runAsConsole = FALSE; BOOL forceServiceMode = FALSE; int i; ControllerLogInit(); ControllerApplyProcessMitigations(); ControllerLog("[*] BlackbirdController: wmain entered pid=%lu\n", GetCurrentProcessId()); for (i = 1; i < argc; ++i) { if (_wcsicmp(argv[i], L"--console") == 0) { runAsConsole = TRUE; continue; } if (_wcsicmp(argv[i], L"--service") == 0) { forceServiceMode = TRUE; } } #ifdef TEMPUS_DEBUG if (!forceServiceMode && !runAsConsole) { if (GetConsoleWindow() != NULL || AttachConsole(ATTACH_PARENT_PROCESS)) { runAsConsole = TRUE; } } #endif if (runAsConsole) { #ifdef TEMPUS_DEBUG if (GetConsoleWindow() == NULL && !AttachConsole(ATTACH_PARENT_PROCESS)) { (void)AllocConsole(); } #endif ControllerLog("[*] BlackbirdController: running in console mode.\n"); if (!ControllerStartCore()) { ControllerStopCore(); ControllerLogClose(); return 1; } ControllerLog("[*] BlackbirdController: running. Press Enter to stop.\n"); (void)getchar(); ControllerStopCore(); ControllerLogClose(); return 0; } if (!StartServiceCtrlDispatcherW(table)) { ControllerLog("[-] BlackbirdController: StartServiceCtrlDispatcherW failed (%lu)\n", GetLastError()); ControllerLogClose(); return 1; } ControllerLogClose(); return 0; }