Files
2026-05-06 09:06:42 +10:00

1267 lines
37 KiB
C

#include "control_private.h"
#include "..\..\hooks\monitor\ntapi_monitor.h"
static volatile LONG g_BkctlClientLockBusyCounter = 0;
BOOLEAN BkctlModeAllowed(_In_ WDFREQUEST Request)
{
return (WdfRequestGetRequestorMode(Request) == UserMode);
}
ULONG BkctlGetRequestorPid(VOID)
{
return (ULONG)(ULONG_PTR)PsGetCurrentProcessId();
}
PCSTR BkctlIoctlName(_In_ ULONG Ioctl)
{
switch (Ioctl)
{
case IOCTL_BK_SUBSCRIBE:
return "SUBSCRIBE";
case IOCTL_BK_UNSUBSCRIBE:
return "UNSUBSCRIBE";
case IOCTL_BK_GET_EVENT:
return "GET_EVENT";
case IOCTL_BK_GET_STATS:
return "GET_STATS";
case IOCTL_BK_SET_PIDS:
return "SET_PIDS";
case IOCTL_BK_QUERY_PROCESS_IMAGE:
return "QUERY_PROCESS_IMAGE";
case IOCTL_BK_SET_SHUTDOWN_MODE:
return "SET_SHUTDOWN_MODE";
case IOCTL_BK_GET_HEALTH:
return "GET_HEALTH";
case IOCTL_BK_GET_DIAGNOSTICS:
return "GET_DIAGNOSTICS";
case IOCTL_BK_ARM_PENDING_LAUNCH:
return "ARM_PENDING_LAUNCH";
case IOCTL_BK_CONTROL_EXECUTION:
return "CONTROL_EXECUTION";
case IOCTL_BK_SET_RUNTIME_CONFIG:
return "SET_RUNTIME_CONFIG";
case IOCTL_BK_GET_RUNTIME_CONFIG:
return "GET_RUNTIME_CONFIG";
case IOCTL_BK_READ_MEMORY:
return "READ_MEMORY";
case IOCTL_BK_REGISTER_INSTRUMENTATION_RANGE:
return "REGISTER_INSTRUMENTATION_RANGE";
case IOCTL_BK_REGISTER_HOOK_PATCH:
return "REGISTER_HOOK_PATCH";
case IOCTL_BK_SET_ENDPOINT_GUARD:
return "SET_ENDPOINT_GUARD";
case IOCTL_BK_SET_QPC_TIMING_CONFIG:
return "SET_QPC_TIMING_CONFIG";
case IOCTL_BK_GET_QPC_TIMING_STATE:
return "GET_QPC_TIMING_STATE";
default:
return "UNKNOWN_IOCTL";
}
}
BOOLEAN BkctlIsShutdown(VOID)
{
return (InterlockedCompareExchange(&g_ControlShutdown, 0, 0) != 0);
}
VOID BkctlSetTelemetryArmed(_In_ BOOLEAN Armed)
{
InterlockedExchange(&g_ControlTelemetryArmed, Armed ? 1 : 0);
if (KeGetCurrentIrql() == PASSIVE_LEVEL)
{
BkntkiMonitorSetArmedState(Armed);
}
}
static BOOLEAN BkctlTryAcquireClientLock(_Inout_ PBK_CLIENT Client, _In_z_ PCSTR Reason)
{
LONG busyCount;
if (Client == NULL)
{
return FALSE;
}
if (ExTryToAcquireFastMutex(&Client->Lock))
{
return TRUE;
}
busyCount = InterlockedIncrement(&g_BkctlClientLockBusyCounter);
if (busyCount == 1 || ((busyCount & 0xFF) == 0))
{
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_WARNING_LEVEL,
"BK: client lock busy reason=%s busyCount=%ld client=0x%p subscriptions=%lu queueDepth=%lu.\n",
Reason, busyCount, Client, Client->SubscriptionCount, Client->QueueDepth);
}
return FALSE;
}
static BOOLEAN BkctlTryAcquireGlobalQueueSlot(VOID)
{
LONG current;
for (;;)
{
current = InterlockedCompareExchange(&g_ControlTotalQueuedEvents, 0, 0);
if (current >= BK_MAX_TOTAL_QUEUED_EVENTS)
{
return FALSE;
}
if (InterlockedCompareExchange(&g_ControlTotalQueuedEvents, current + 1, current) == current)
{
return TRUE;
}
}
}
VOID BkctlReleaseGlobalQueueSlot(VOID)
{
LONG remaining;
remaining = InterlockedDecrement(&g_ControlTotalQueuedEvents);
NT_ASSERT(remaining >= 0);
if (remaining < 0)
{
InterlockedExchange(&g_ControlTotalQueuedEvents, 0);
}
}
BOOLEAN BkctlClientConsumeQueryBudgetLocked(_Inout_ PBK_CLIENT Client)
{
ULONGLONG now;
now = KeQueryInterruptTime();
if (Client->QueryWindowStart100ns == 0 || now < Client->QueryWindowStart100ns ||
(now - Client->QueryWindowStart100ns) >= BK_QUERY_IMAGE_WINDOW_100NS)
{
Client->QueryWindowStart100ns = now;
Client->QueryWindowCount = 1;
return TRUE;
}
if (Client->QueryWindowCount >= BK_QUERY_IMAGE_MAX_PER_WINDOW)
{
return FALSE;
}
Client->QueryWindowCount += 1;
return TRUE;
}
BOOLEAN BkctlTryAcquireQueryInflightSlot(VOID)
{
LONG current;
for (;;)
{
current = InterlockedCompareExchange(&g_QueryImageInflight, 0, 0);
if (current >= BK_QUERY_IMAGE_MAX_INFLIGHT)
{
return FALSE;
}
if (InterlockedCompareExchange(&g_QueryImageInflight, current + 1, current) == current)
{
return TRUE;
}
}
}
VOID BkctlReleaseQueryInflightSlot(VOID)
{
LONG remaining;
remaining = InterlockedDecrement(&g_QueryImageInflight);
NT_ASSERT(remaining >= 0);
if (remaining < 0)
{
InterlockedExchange(&g_QueryImageInflight, 0);
}
}
VOID BkctlClientFreeQueuedEvents(_Inout_ PBK_CLIENT Client)
{
while (!IsListEmpty(&Client->EventQueue))
{
PLIST_ENTRY entry = RemoveHeadList(&Client->EventQueue);
PBK_EVENT_NODE node = CONTAINING_RECORD(entry, BK_EVENT_NODE, Link);
ExFreePoolWithTag(node, BK_POOL_TAG);
BkctlReleaseGlobalQueueSlot();
}
Client->QueueDepth = 0;
}
static VOID BkctlClientFreeSubscriptions(_Inout_ PBK_CLIENT Client)
{
if (Client == NULL || Client->Subscriptions == NULL)
{
return;
}
ExFreePoolWithTag(Client->Subscriptions, BK_POOL_TAG);
Client->Subscriptions = NULL;
Client->SubscriptionCount = 0;
Client->SubscriptionCapacity = 0;
}
static BOOLEAN BkctlClientEnsureSubscriptionCapacityLocked(_Inout_ PBK_CLIENT Client, _In_ UINT32 RequiredCount)
{
PBK_SUBSCRIPTION newSubscriptions;
UINT32 newCapacity;
SIZE_T bytes;
if (Client == NULL || RequiredCount == 0 || RequiredCount > BK_MAX_CLIENT_SUBSCRIPTIONS)
{
return FALSE;
}
if (Client->SubscriptionCapacity >= RequiredCount && Client->Subscriptions != NULL)
{
return TRUE;
}
newCapacity = Client->SubscriptionCapacity;
if (newCapacity == 0)
{
newCapacity = BK_INITIAL_CLIENT_SUBSCRIPTIONS;
}
while (newCapacity < RequiredCount && newCapacity < BK_MAX_CLIENT_SUBSCRIPTIONS)
{
UINT32 grown = newCapacity * 2;
if (grown <= newCapacity)
{
newCapacity = BK_MAX_CLIENT_SUBSCRIPTIONS;
break;
}
newCapacity = (grown > BK_MAX_CLIENT_SUBSCRIPTIONS) ? BK_MAX_CLIENT_SUBSCRIPTIONS : grown;
}
if (newCapacity < RequiredCount)
{
return FALSE;
}
bytes = sizeof(BK_SUBSCRIPTION) * (SIZE_T)newCapacity;
newSubscriptions = (PBK_SUBSCRIPTION)BkpoolAllocateCompat(POOL_FLAG_NON_PAGED, bytes, BK_POOL_TAG);
if (newSubscriptions == NULL)
{
return FALSE;
}
RtlZeroMemory(newSubscriptions, bytes);
if (Client->Subscriptions != NULL && Client->SubscriptionCount != 0)
{
RtlCopyMemory(newSubscriptions, Client->Subscriptions,
sizeof(BK_SUBSCRIPTION) * (SIZE_T)Client->SubscriptionCount);
ExFreePoolWithTag(Client->Subscriptions, BK_POOL_TAG);
}
Client->Subscriptions = newSubscriptions;
Client->SubscriptionCapacity = newCapacity;
return TRUE;
}
VOID BkctlClientRelease(_Inout_ PBK_CLIENT Client)
{
if (InterlockedDecrement(&Client->RefCount) != 0)
{
return;
}
ExAcquireFastMutex(&Client->Lock);
BkctlClientFreeQueuedEvents(Client);
BkctlClientFreeSubscriptions(Client);
ExReleaseFastMutex(&Client->Lock);
ExFreePoolWithTag(Client, BK_POOL_TAG);
}
VOID BkctlClientReference(_Inout_ PBK_CLIENT Client)
{
(void)InterlockedIncrement(&Client->RefCount);
}
BOOLEAN BkctlIsValidStreamMask(_In_ UINT32 StreamMask)
{
return ((StreamMask & (BK_STREAM_HANDLE | BK_STREAM_MEMORY | BK_STREAM_THREAD | BK_STREAM_FILESYSTEM |
BK_STREAM_REGISTRY | BK_STREAM_TIMING | BK_STREAM_ENTERPRISE)) != 0);
}
VOID BkctlClientClearPendingLaunchLocked(_Inout_ PBK_CLIENT Client)
{
if (Client == NULL)
{
return;
}
Client->PendingLaunchArmed = FALSE;
Client->PendingLaunchStreamMask = 0;
Client->PendingAnalysisSubjectKind = BlackbirdAnalysisSubjectProcess;
Client->PendingLaunchPathNormDos[0] = L'\0';
Client->PendingLaunchPathNormNt[0] = L'\0';
Client->PendingLaunchPathTail[0] = L'\0';
Client->PendingSubjectPathNormDos[0] = L'\0';
Client->PendingSubjectPathNormNt[0] = L'\0';
Client->PendingSubjectPathTail[0] = L'\0';
}
VOID BkctlClientConfigurePendingLaunchLocked(_Inout_ PBK_CLIENT Client,
_In_opt_ const BK_ARM_PENDING_LAUNCH_REQUEST *Request)
{
BkctlClientClearPendingLaunchLocked(Client);
if (Client == NULL || Request == NULL)
{
return;
}
Client->PendingLaunchStreamMask = Request->StreamMask;
Client->PendingAnalysisSubjectKind = Request->AnalysisSubjectKind;
Client->PendingLaunchArmed = TRUE;
(void)RtlStringCchCopyW(Client->PendingLaunchPathNormDos, RTL_NUMBER_OF(Client->PendingLaunchPathNormDos),
Request->ImagePathNormDos);
(void)RtlStringCchCopyW(Client->PendingLaunchPathNormNt, RTL_NUMBER_OF(Client->PendingLaunchPathNormNt),
Request->ImagePathNormNt);
(void)RtlStringCchCopyW(Client->PendingLaunchPathTail, RTL_NUMBER_OF(Client->PendingLaunchPathTail),
Request->ImagePathTail);
(void)RtlStringCchCopyW(Client->PendingSubjectPathNormDos, RTL_NUMBER_OF(Client->PendingSubjectPathNormDos),
Request->AnalysisSubjectNormDos);
(void)RtlStringCchCopyW(Client->PendingSubjectPathNormNt, RTL_NUMBER_OF(Client->PendingSubjectPathNormNt),
Request->AnalysisSubjectNormNt);
(void)RtlStringCchCopyW(Client->PendingSubjectPathTail, RTL_NUMBER_OF(Client->PendingSubjectPathTail),
Request->AnalysisSubjectTail);
}
static SIZE_T BkctlKnownPathPrefixChars(_In_reads_opt_(InputChars) PCWSTR Input, _In_ SIZE_T InputChars)
{
if (Input == NULL || InputChars < 4)
{
return 0;
}
if ((Input[0] == L'\\' && Input[1] == L'\\' && Input[2] == L'?' && Input[3] == L'\\') ||
(Input[0] == L'\\' && Input[1] == L'?' && Input[2] == L'?' && Input[3] == L'\\'))
{
return 4;
}
return 0;
}
static VOID BkctlNormalizeWidePathForCompare(_In_opt_z_ PCWSTR Input, _Out_writes_z_(OutputChars) PWSTR Output,
_In_ SIZE_T OutputChars)
{
SIZE_T i;
SIZE_T inputChars = 0;
SIZE_T skippedChars;
SIZE_T written = 0;
if (Output == NULL || OutputChars == 0)
{
return;
}
Output[0] = L'\0';
if (Input == NULL)
{
return;
}
if (!NT_SUCCESS(RtlStringCchLengthW(Input, OutputChars * 2, &inputChars)))
{
return;
}
skippedChars = BkctlKnownPathPrefixChars(Input, inputChars);
Input += skippedChars;
inputChars -= skippedChars;
for (i = 0; i < inputChars && (written + 1) < OutputChars; ++i)
{
WCHAR ch = Input[i];
if (ch == L'/')
{
ch = L'\\';
}
Output[written++] = RtlDowncaseUnicodeChar(ch);
}
Output[written] = L'\0';
}
static VOID BkctlNormalizeUnicodePathForCompare(_In_opt_ PCUNICODE_STRING Input,
_Out_writes_z_(OutputChars) PWSTR Output, _In_ SIZE_T OutputChars)
{
SIZE_T i;
SIZE_T inputChars;
SIZE_T skippedChars;
SIZE_T written = 0;
PCWSTR inputBuffer;
if (Output == NULL || OutputChars == 0)
{
return;
}
Output[0] = L'\0';
__try
{
if (Input == NULL || Input->Buffer == NULL || Input->Length == 0)
{
return;
}
inputBuffer = Input->Buffer;
inputChars = Input->Length / sizeof(WCHAR);
skippedChars = BkctlKnownPathPrefixChars(inputBuffer, inputChars);
inputBuffer += skippedChars;
inputChars -= skippedChars;
for (i = 0; i < inputChars && (written + 1) < OutputChars; ++i)
{
WCHAR ch = inputBuffer[i];
if (ch == L'/')
{
ch = L'\\';
}
Output[written++] = RtlDowncaseUnicodeChar(ch);
}
Output[written] = L'\0';
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
Output[0] = L'\0';
}
}
static BOOLEAN BkctlPathHasTrailingSegmentInsensitive(_In_z_ PCWSTR Path, _In_z_ PCWSTR Tail)
{
SIZE_T pathLen;
SIZE_T tailLen;
PCWSTR end;
if (Path == NULL || Tail == NULL || Tail[0] == L'\0')
{
return FALSE;
}
pathLen = wcslen(Path);
tailLen = wcslen(Tail);
if (tailLen > pathLen)
{
return FALSE;
}
end = Path + (pathLen - tailLen);
if (_wcsicmp(end, Tail) != 0)
{
return FALSE;
}
if (end == Path)
{
return TRUE;
}
return (end[-1] == L'\\');
}
static BOOLEAN BkctlClientPathMatchesPendingLaunchLocked(_In_ const BK_CLIENT *Client, _In_z_ PCWSTR CandidateNorm)
{
if (Client == NULL || CandidateNorm == NULL || CandidateNorm[0] == L'\0' || !Client->PendingLaunchArmed)
{
return FALSE;
}
if (Client->PendingLaunchPathNormNt[0] != L'\0' && _wcsicmp(Client->PendingLaunchPathNormNt, CandidateNorm) == 0)
{
return TRUE;
}
if (Client->PendingLaunchPathNormDos[0] != L'\0' && _wcsicmp(Client->PendingLaunchPathNormDos, CandidateNorm) == 0)
{
return TRUE;
}
if (Client->PendingLaunchPathTail[0] != L'\0' &&
BkctlPathHasTrailingSegmentInsensitive(CandidateNorm, Client->PendingLaunchPathTail))
{
return TRUE;
}
return FALSE;
}
static BOOLEAN BkctlClientPathMatchesAnalysisSubjectLocked(_In_ const BK_CLIENT *Client, _In_z_ PCWSTR CandidateNorm)
{
if (Client == NULL || CandidateNorm == NULL || CandidateNorm[0] == L'\0' || Client->AnalysisSubjectProcessId == 0 ||
Client->AnalysisSubjectKind != BlackbirdAnalysisSubjectDll)
{
return FALSE;
}
if (Client->AnalysisSubjectPathNormNt[0] != L'\0' &&
_wcsicmp(Client->AnalysisSubjectPathNormNt, CandidateNorm) == 0)
{
return TRUE;
}
if (Client->AnalysisSubjectPathNormDos[0] != L'\0' &&
_wcsicmp(Client->AnalysisSubjectPathNormDos, CandidateNorm) == 0)
{
return TRUE;
}
if (Client->AnalysisSubjectPathTail[0] != L'\0' &&
BkctlPathHasTrailingSegmentInsensitive(CandidateNorm, Client->AnalysisSubjectPathTail))
{
return TRUE;
}
return FALSE;
}
static VOID BkctlClientBindAnalysisSubjectLocked(_Inout_ PBK_CLIENT Client, _In_ UINT32 ProcessId)
{
if (Client == NULL || ProcessId == 0)
{
return;
}
Client->AnalysisSubjectProcessId = 0;
Client->AnalysisSubjectKind = BlackbirdAnalysisSubjectProcess;
Client->AnalysisSubjectImageBase = 0;
Client->AnalysisSubjectImageSize = 0;
Client->AnalysisSubjectPathNormDos[0] = L'\0';
Client->AnalysisSubjectPathNormNt[0] = L'\0';
Client->AnalysisSubjectPathTail[0] = L'\0';
if (Client->PendingAnalysisSubjectKind != BlackbirdAnalysisSubjectDll ||
(Client->PendingSubjectPathNormDos[0] == L'\0' && Client->PendingSubjectPathNormNt[0] == L'\0' &&
Client->PendingSubjectPathTail[0] == L'\0'))
{
return;
}
Client->AnalysisSubjectProcessId = ProcessId;
Client->AnalysisSubjectKind = Client->PendingAnalysisSubjectKind;
(void)RtlStringCchCopyW(Client->AnalysisSubjectPathNormDos, RTL_NUMBER_OF(Client->AnalysisSubjectPathNormDos),
Client->PendingSubjectPathNormDos);
(void)RtlStringCchCopyW(Client->AnalysisSubjectPathNormNt, RTL_NUMBER_OF(Client->AnalysisSubjectPathNormNt),
Client->PendingSubjectPathNormNt);
(void)RtlStringCchCopyW(Client->AnalysisSubjectPathTail, RTL_NUMBER_OF(Client->AnalysisSubjectPathTail),
Client->PendingSubjectPathTail);
}
BOOLEAN BkctlClientAddOrUpdateSubscriptionLocked(_Inout_ PBK_CLIENT Client, _In_ UINT32 ProcessId,
_In_ UINT32 StreamMask)
{
UINT32 i;
if (Client == NULL || ProcessId == 0 || StreamMask == 0)
{
return FALSE;
}
if (Client->Subscriptions == NULL && !BkctlClientEnsureSubscriptionCapacityLocked(Client, 1))
{
return FALSE;
}
for (i = 0; i < Client->SubscriptionCount; ++i)
{
if (Client->Subscriptions[i].ProcessId == ProcessId)
{
Client->Subscriptions[i].StreamMask |= StreamMask;
return TRUE;
}
}
if (!BkctlClientEnsureSubscriptionCapacityLocked(Client, Client->SubscriptionCount + 1))
{
return FALSE;
}
Client->Subscriptions[Client->SubscriptionCount].ProcessId = ProcessId;
Client->Subscriptions[Client->SubscriptionCount].StreamMask = StreamMask;
Client->SubscriptionCount += 1;
return TRUE;
}
BOOLEAN BkctlClientRemoveSubscriptionLocked(_Inout_ PBK_CLIENT Client, _In_ UINT32 ProcessId)
{
UINT32 i;
if (Client == NULL || ProcessId == 0)
{
return FALSE;
}
if (Client->Subscriptions == NULL || Client->SubscriptionCount == 0)
{
return FALSE;
}
for (i = 0; i < Client->SubscriptionCount; ++i)
{
if (Client->Subscriptions[i].ProcessId == ProcessId)
{
UINT32 tail = Client->SubscriptionCount - 1;
if (i != tail)
{
Client->Subscriptions[i] = Client->Subscriptions[tail];
}
Client->SubscriptionCount -= 1;
return TRUE;
}
}
return FALSE;
}
UINT32 BkctlClientReplaceSubscriptionsLocked(_Inout_ PBK_CLIENT Client,
_In_reads_(ProcessCount) const UINT32 *ProcessIds,
_In_ UINT32 ProcessCount, _In_ UINT32 StreamMask)
{
UINT32 i;
if (Client == NULL || ProcessIds == NULL || ProcessCount == 0 || StreamMask == 0)
{
if (Client != NULL)
{
Client->SubscriptionCount = 0;
}
return 0;
}
Client->SubscriptionCount = 0;
if (!BkctlClientEnsureSubscriptionCapacityLocked(
Client, (ProcessCount > BK_MAX_CLIENT_SUBSCRIPTIONS) ? BK_MAX_CLIENT_SUBSCRIPTIONS : ProcessCount))
{
return 0;
}
for (i = 0; i < ProcessCount; ++i)
{
UINT32 pid = ProcessIds[i];
if (pid == 0)
{
continue;
}
(void)BkctlClientAddOrUpdateSubscriptionLocked(Client, pid, StreamMask);
}
return Client->SubscriptionCount;
}
static VOID BkctlFlushAllClientState(VOID)
{
PBK_CLIENT snapshot[BK_MAX_TOTAL_CLIENTS];
UINT32 snapshotCount = 0;
UINT32 i;
PLIST_ENTRY e;
ExAcquireFastMutex(&g_ClientListLock);
for (e = g_ClientList.Flink; e != &g_ClientList; e = e->Flink)
{
PBK_CLIENT c = CONTAINING_RECORD(e, BK_CLIENT, Link);
if (snapshotCount >= RTL_NUMBER_OF(snapshot))
{
break;
}
BkctlClientReference(c);
snapshot[snapshotCount++] = c;
}
ExReleaseFastMutex(&g_ClientListLock);
for (i = 0; i < snapshotCount; ++i)
{
PBK_CLIENT c = snapshot[i];
WDFQUEUE pendingQueue = NULL;
ExAcquireFastMutex(&c->Lock);
pendingQueue = c->PendingGetEventQueue;
c->PendingGetEventQueue = NULL;
c->SubscriptionCount = 0;
BkctlClientClearPendingLaunchLocked(c);
BkctlClientFreeQueuedEvents(c);
ExReleaseFastMutex(&c->Lock);
if (pendingQueue != NULL)
{
WdfIoQueuePurgeSynchronously(pendingQueue);
}
BkctlClientRelease(c);
}
}
VOID BkctlRefreshArmedState(VOID)
{
PBK_CLIENT snapshot[BK_MAX_TOTAL_CLIENTS];
UINT32 snapshotCount = 0;
UINT32 i;
PLIST_ENTRY entry;
BOOLEAN armed = FALSE;
ExAcquireFastMutex(&g_ClientListLock);
for (entry = g_ClientList.Flink; entry != &g_ClientList; entry = entry->Flink)
{
PBK_CLIENT client = CONTAINING_RECORD(entry, BK_CLIENT, Link);
if (snapshotCount >= RTL_NUMBER_OF(snapshot))
{
break;
}
BkctlClientReference(client);
snapshot[snapshotCount++] = client;
}
ExReleaseFastMutex(&g_ClientListLock);
for (i = 0; i < snapshotCount; ++i)
{
PBK_CLIENT client = snapshot[i];
if (!BkctlTryAcquireClientLock(client, "refresh-armed"))
{
armed = TRUE;
BkctlClientRelease(client);
break;
}
if (client->SubscriptionCount != 0 || client->PendingLaunchArmed)
{
armed = TRUE;
}
ExReleaseFastMutex(&client->Lock);
BkctlClientRelease(client);
if (armed)
{
break;
}
}
for (++i; i < snapshotCount; ++i)
{
BkctlClientRelease(snapshot[i]);
}
BkctlSetTelemetryArmed(armed);
}
VOID BkctlBeginShutdown(VOID)
{
if (InterlockedExchange(&g_ControlShutdown, 1) == 0)
{
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "BK: control plane entering shutdown mode.\n");
}
BkctlFlushAllClientState();
BkctlSetTelemetryArmed(FALSE);
}
static PBK_CLIENT BkctlClientCreate(VOID)
{
PBK_CLIENT client;
client = (PBK_CLIENT)BkpoolAllocateCompat(POOL_FLAG_NON_PAGED, sizeof(*client), BK_POOL_TAG);
if (client == NULL)
{
return NULL;
}
RtlZeroMemory(client, sizeof(*client));
InitializeListHead(&client->EventQueue);
client->PendingGetEventQueue = NULL;
ExInitializeFastMutex(&client->Lock);
client->RefCount = 1;
if (!BkctlClientEnsureSubscriptionCapacityLocked(client, BK_INITIAL_CLIENT_SUBSCRIPTIONS))
{
ExFreePoolWithTag(client, BK_POOL_TAG);
return NULL;
}
return client;
}
BOOLEAN BkctlClientMatchSubscriptionEither(_In_ PBK_CLIENT Client, _In_ UINT32 PrimaryProcessId,
_In_ UINT32 SecondaryProcessId, _In_ UINT32 StreamMask)
{
UINT32 i;
if (Client == NULL || Client->Subscriptions == NULL || Client->SubscriptionCount == 0)
{
return FALSE;
}
for (i = 0; i < Client->SubscriptionCount; ++i)
{
UINT32 subscribedPid;
UINT32 subscribedMask;
subscribedPid = Client->Subscriptions[i].ProcessId;
subscribedMask = Client->Subscriptions[i].StreamMask;
if ((subscribedMask & StreamMask) == 0)
{
continue;
}
if (subscribedPid == PrimaryProcessId || (SecondaryProcessId != 0 && subscribedPid == SecondaryProcessId))
{
return TRUE;
}
}
return FALSE;
}
BOOLEAN
BkctlBindPendingLaunchProcess(_In_ UINT32 ProcessId, _In_opt_ PCUNICODE_STRING ImagePath)
{
PBK_CLIENT snapshot[BK_MAX_TOTAL_CLIENTS];
WCHAR candidateNorm[BK_MAX_IMAGE_PATH_CHARS];
UINT32 snapshotCount = 0;
UINT32 i;
PLIST_ENTRY entry;
BOOLEAN matchedAny = FALSE;
if (ProcessId == 0 || ImagePath == NULL || ImagePath->Buffer == NULL || ImagePath->Length == 0)
{
return FALSE;
}
if (!BkctlIsArmedFast())
{
return FALSE;
}
BkctlNormalizeUnicodePathForCompare(ImagePath, candidateNorm, RTL_NUMBER_OF(candidateNorm));
if (candidateNorm[0] == L'\0')
{
return FALSE;
}
ExAcquireFastMutex(&g_ClientListLock);
for (entry = g_ClientList.Flink; entry != &g_ClientList; entry = entry->Flink)
{
PBK_CLIENT client = CONTAINING_RECORD(entry, BK_CLIENT, Link);
if (snapshotCount >= RTL_NUMBER_OF(snapshot))
{
break;
}
BkctlClientReference(client);
snapshot[snapshotCount++] = client;
}
ExReleaseFastMutex(&g_ClientListLock);
for (i = 0; i < snapshotCount; ++i)
{
PBK_CLIENT client = snapshot[i];
ExAcquireFastMutex(&client->Lock);
if (BkctlClientPathMatchesPendingLaunchLocked(client, candidateNorm))
{
UINT32 streamMask = client->PendingLaunchStreamMask;
BOOLEAN subscribed = BkctlClientAddOrUpdateSubscriptionLocked(client, ProcessId, streamMask);
BkctlClientBindAnalysisSubjectLocked(client, ProcessId);
BkctlClientClearPendingLaunchLocked(client);
if (subscribed)
{
matchedAny = TRUE;
BkctlSetTelemetryArmed(TRUE);
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,
"BK: pending launch bound targetPid=%lu streamMask=0x%08X image=%ws.\n", ProcessId,
streamMask, candidateNorm);
}
else
{
DbgPrintEx(
DPFLTR_IHVDRIVER_ID, DPFLTR_WARNING_LEVEL,
"BK: pending launch match could not bind targetPid=%lu image=%ws reason=subscription-capacity.\n",
ProcessId, candidateNorm);
}
}
ExReleaseFastMutex(&client->Lock);
BkctlClientRelease(client);
}
return matchedAny;
}
BOOLEAN
BkctlMarkAnalysisSubjectImageLoad(_In_ UINT32 ProcessId, _In_opt_ PCUNICODE_STRING ImagePath, _In_ UINT64 ImageBase,
_In_ UINT64 ImageSize)
{
PBK_CLIENT snapshot[BK_MAX_TOTAL_CLIENTS];
WCHAR candidateNorm[BK_MAX_IMAGE_PATH_CHARS];
UINT32 snapshotCount = 0;
UINT32 i;
PLIST_ENTRY entry;
BOOLEAN matchedAny = FALSE;
if (ProcessId == 0 || ImagePath == NULL || ImagePath->Buffer == NULL || ImagePath->Length == 0)
{
return FALSE;
}
if (!BkctlIsArmedFast())
{
return FALSE;
}
BkctlNormalizeUnicodePathForCompare(ImagePath, candidateNorm, RTL_NUMBER_OF(candidateNorm));
if (candidateNorm[0] == L'\0')
{
return FALSE;
}
ExAcquireFastMutex(&g_ClientListLock);
for (entry = g_ClientList.Flink; entry != &g_ClientList; entry = entry->Flink)
{
PBK_CLIENT client = CONTAINING_RECORD(entry, BK_CLIENT, Link);
if (snapshotCount >= RTL_NUMBER_OF(snapshot))
{
break;
}
BkctlClientReference(client);
snapshot[snapshotCount++] = client;
}
ExReleaseFastMutex(&g_ClientListLock);
for (i = 0; i < snapshotCount; ++i)
{
PBK_CLIENT client = snapshot[i];
ExAcquireFastMutex(&client->Lock);
if (client->AnalysisSubjectProcessId == ProcessId &&
BkctlClientPathMatchesAnalysisSubjectLocked(client, candidateNorm))
{
client->AnalysisSubjectImageBase = ImageBase;
client->AnalysisSubjectImageSize = ImageSize;
matchedAny = TRUE;
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,
"BK: analysis subject image loaded targetPid=%lu base=0x%llX size=0x%llX image=%ws.\n",
ProcessId, ImageBase, ImageSize, candidateNorm);
}
ExReleaseFastMutex(&client->Lock);
BkctlClientRelease(client);
}
return matchedAny;
}
static VOID BkctlClientEnqueueEvent(_Inout_ PBK_CLIENT Client, _In_ const BK_EVENT_RECORD *Source)
{
PBK_EVENT_NODE node;
LONG dropLogCounter;
if (Client->QueueDepth >= BK_MAX_CLIENT_QUEUE_DEPTH)
{
Client->DroppedEvents += 1;
dropLogCounter = InterlockedIncrement(&g_ControlQueueDropLogCounter);
if (dropLogCounter == 1 || ((dropLogCounter & 0xFF) == 0))
{
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_WARNING_LEVEL,
"BK: queue drop (client depth cap=%lu) totalDrops=%lu clientDrops=%u queueDepth=%u.\n",
BK_MAX_CLIENT_QUEUE_DEPTH, (ULONG)dropLogCounter, Client->DroppedEvents, Client->QueueDepth);
}
return;
}
if (!BkctlTryAcquireGlobalQueueSlot())
{
Client->DroppedEvents += 1;
dropLogCounter = InterlockedIncrement(&g_ControlQueueDropLogCounter);
if (dropLogCounter == 1 || ((dropLogCounter & 0xFF) == 0))
{
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_WARNING_LEVEL,
"BK: queue drop (global cap=%lu) totalDrops=%lu clientDrops=%u globalQueued=%ld.\n",
BK_MAX_TOTAL_QUEUED_EVENTS, (ULONG)dropLogCounter, Client->DroppedEvents,
InterlockedCompareExchange(&g_ControlTotalQueuedEvents, 0, 0));
}
return;
}
node = (PBK_EVENT_NODE)BkpoolAllocateCompat(POOL_FLAG_NON_PAGED, sizeof(*node), BK_POOL_TAG);
if (node == NULL)
{
BkctlReleaseGlobalQueueSlot();
Client->DroppedEvents += 1;
dropLogCounter = InterlockedIncrement(&g_ControlQueueDropLogCounter);
if (dropLogCounter == 1 || ((dropLogCounter & 0xFF) == 0))
{
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_WARNING_LEVEL,
"BK: queue drop (alloc failure) totalDrops=%lu clientDrops=%u queueDepth=%u.\n",
(ULONG)dropLogCounter, Client->DroppedEvents, Client->QueueDepth);
}
return;
}
RtlZeroMemory(node, sizeof(*node));
RtlCopyMemory(&node->Record, Source, sizeof(node->Record));
InsertTailList(&Client->EventQueue, &node->Link);
Client->QueueDepth += 1;
}
static BOOLEAN BkctlClientTryTakePendingGetEventRequestLocked(_In_ PBK_CLIENT Client, _Out_ WDFREQUEST *Request)
{
NTSTATUS status;
if (Request == NULL)
{
return FALSE;
}
*Request = NULL;
if (Client == NULL || Client->PendingGetEventQueue == NULL)
{
return FALSE;
}
status = WdfIoQueueRetrieveNextRequest(Client->PendingGetEventQueue, Request);
return NT_SUCCESS(status);
}
static NTSTATUS BkctlCompleteGetEventRequestWithRecord(_In_ WDFREQUEST Request, _In_ const BK_EVENT_RECORD *Record)
{
NTSTATUS status;
PBK_EVENT_RECORD out;
size_t outSize;
LONG deliverCounter;
ULONG requesterPid;
if (Request == NULL || Record == NULL)
{
return STATUS_INVALID_PARAMETER;
}
status = WdfRequestRetrieveOutputBuffer(Request, sizeof(*out), (PVOID *)&out, &outSize);
if (!NT_SUCCESS(status))
{
WdfRequestComplete(Request, status);
return status;
}
if (outSize < sizeof(*out))
{
WdfRequestComplete(Request, STATUS_BUFFER_TOO_SMALL);
return STATUS_BUFFER_TOO_SMALL;
}
RtlCopyMemory(out, Record, sizeof(*out));
WdfRequestCompleteWithInformation(Request, STATUS_SUCCESS, sizeof(*out));
deliverCounter = InterlockedIncrement(&g_IoctlGetEventDeliverCounter);
if (deliverCounter == 1 || ((deliverCounter & 0x1FF) == 0))
{
requesterPid = BkctlGetRequestorPid();
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,
"BK: get-event delivered(pended) requesterPid=%lu deliveredCount=%ld eventType=%lu seq=%lu.\n",
requesterPid, deliverCounter, Record->Header.Type, Record->Header.Sequence);
}
return STATUS_SUCCESS;
}
VOID BkctlPublishRecordToSubscribers(_In_ UINT32 PrimaryPid, _In_ UINT32 SecondaryPid, _In_ UINT32 StreamMask,
_In_ BK_EVENT_RECORD *Record)
{
ULONGLONG tempusStartQpc = BktmpEnter(BktmpSubsystemControl);
PBK_CLIENT snapshot[BK_MAX_TOTAL_CLIENTS];
UINT32 snapshotCount = 0;
UINT32 i;
PLIST_ENTRY e;
if (InterlockedCompareExchange(&g_ControlInitialized, 0, 0) == 0)
{
BktmpLeave(BktmpSubsystemControl, tempusStartQpc);
return;
}
if (BkctlIsShutdown())
{
BktmpLeave(BktmpSubsystemControl, tempusStartQpc);
return;
}
ExAcquireFastMutex(&g_ClientListLock);
for (e = g_ClientList.Flink; e != &g_ClientList; e = e->Flink)
{
PBK_CLIENT c = CONTAINING_RECORD(e, BK_CLIENT, Link);
if (snapshotCount >= RTL_NUMBER_OF(snapshot))
{
break;
}
BkctlClientReference(c);
snapshot[snapshotCount++] = c;
}
ExReleaseFastMutex(&g_ClientListLock);
for (i = 0; i < snapshotCount; ++i)
{
PBK_CLIENT c = snapshot[i];
WDFREQUEST pendingRequest = NULL;
BK_EVENT_RECORD stagedRecord;
BOOLEAN matched = FALSE;
NTSTATUS deliverStatus = STATUS_UNSUCCESSFUL;
RtlZeroMemory(&stagedRecord, sizeof(stagedRecord));
if (!BkctlTryAcquireClientLock(c, "publish-record"))
{
BkctlClientRelease(c);
continue;
}
if (BkctlClientMatchSubscriptionEither(c, PrimaryPid, SecondaryPid, StreamMask))
{
matched = TRUE;
RtlCopyMemory(&stagedRecord, Record, sizeof(stagedRecord));
stagedRecord.Header.Sequence = ++c->Sequence;
if (!BkctlClientTryTakePendingGetEventRequestLocked(c, &pendingRequest))
{
BkctlClientEnqueueEvent(c, &stagedRecord);
}
}
ExReleaseFastMutex(&c->Lock);
if (matched && pendingRequest != NULL)
{
deliverStatus = BkctlCompleteGetEventRequestWithRecord(pendingRequest, &stagedRecord);
if (!NT_SUCCESS(deliverStatus) && !BkctlIsShutdown())
{
ExAcquireFastMutex(&c->Lock);
BkctlClientEnqueueEvent(c, &stagedRecord);
ExReleaseFastMutex(&c->Lock);
}
}
BkctlClientRelease(c);
}
BktmpLeave(BktmpSubsystemControl, tempusStartQpc);
}
EVT_WDF_DEVICE_FILE_CREATE BkctlEvtFileCreate;
EVT_WDF_FILE_CLEANUP BkctlEvtFileCleanup;
EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL BkctlEvtIoDeviceControl;
_Use_decl_annotations_ VOID BkctlEvtFileCreate(WDFDEVICE Device, WDFREQUEST Request, WDFFILEOBJECT FileObject)
{
PBK_FILE_CONTEXT ctx;
PBK_CLIENT client;
WDF_IO_QUEUE_CONFIG queueConfig;
WDF_OBJECT_ATTRIBUTES queueAttrs;
WDFQUEUE pendingQueue = NULL;
NTSTATUS queueStatus = STATUS_SUCCESS;
ULONG requesterPid;
LONG clientCountSnapshot;
if (KeGetCurrentIrql() != PASSIVE_LEVEL)
{
WdfRequestComplete(Request, STATUS_INVALID_DEVICE_STATE);
return;
}
if (BkctlIsShutdown())
{
WdfRequestComplete(Request, STATUS_DELETE_PENDING);
return;
}
client = BkctlClientCreate();
if (client == NULL)
{
WdfRequestComplete(Request, STATUS_INSUFFICIENT_RESOURCES);
return;
}
ExAcquireFastMutex(&g_ClientListLock);
if (g_ClientCount >= BK_MAX_TOTAL_CLIENTS)
{
ExReleaseFastMutex(&g_ClientListLock);
BkctlClientRelease(client);
WdfRequestComplete(Request, STATUS_QUOTA_EXCEEDED);
return;
}
InsertTailList(&g_ClientList, &client->Link);
g_ClientCount += 1;
clientCountSnapshot = g_ClientCount;
ExReleaseFastMutex(&g_ClientListLock);
WDF_IO_QUEUE_CONFIG_INIT(&queueConfig, WdfIoQueueDispatchManual);
queueConfig.PowerManaged = WdfFalse;
WDF_OBJECT_ATTRIBUTES_INIT(&queueAttrs);
queueAttrs.ParentObject = FileObject;
queueStatus = WdfIoQueueCreate(Device, &queueConfig, &queueAttrs, &pendingQueue);
if (!NT_SUCCESS(queueStatus))
{
ExAcquireFastMutex(&g_ClientListLock);
RemoveEntryList(&client->Link);
InitializeListHead(&client->Link);
if (g_ClientCount > 0)
{
g_ClientCount -= 1;
}
ExReleaseFastMutex(&g_ClientListLock);
BkctlClientRelease(client);
WdfRequestComplete(Request, queueStatus);
return;
}
client->PendingGetEventQueue = pendingQueue;
ctx = BkctlGetFileContext(FileObject);
ctx->Client = client;
requesterPid = BkctlGetRequestorPid();
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "BK: client attached pid=%lu activeClients=%ld fileObj=0x%p.\n",
requesterPid, clientCountSnapshot, FileObject);
WdfRequestComplete(Request, STATUS_SUCCESS);
}
_Use_decl_annotations_ VOID BkctlEvtFileCleanup(WDFFILEOBJECT FileObject)
{
PBK_FILE_CONTEXT ctx = BkctlGetFileContext(FileObject);
PBK_CLIENT client = ctx->Client;
PLIST_ENTRY e;
LONG clientCountSnapshot = 0;
UINT32 subscriptionCountSnapshot = 0;
UINT32 queueDepthSnapshot = 0;
UINT32 droppedSnapshot = 0;
ULONG requesterPid;
if (client == NULL)
{
return;
}
if (KeGetCurrentIrql() != PASSIVE_LEVEL)
{
return;
}
ExAcquireFastMutex(&g_ClientListLock);
for (e = g_ClientList.Flink; e != &g_ClientList; e = e->Flink)
{
if (CONTAINING_RECORD(e, BK_CLIENT, Link) == client)
{
RemoveEntryList(e);
if (g_ClientCount > 0)
{
g_ClientCount -= 1;
}
clientCountSnapshot = g_ClientCount;
break;
}
}
ExReleaseFastMutex(&g_ClientListLock);
if (clientCountSnapshot == 0)
{
BkctlSetTelemetryArmed(FALSE);
}
else
{
BkctlRefreshArmedState();
}
ExAcquireFastMutex(&client->Lock);
{
WDFQUEUE pendingQueue = client->PendingGetEventQueue;
client->PendingGetEventQueue = NULL;
subscriptionCountSnapshot = client->SubscriptionCount;
queueDepthSnapshot = client->QueueDepth;
droppedSnapshot = client->DroppedEvents;
ExReleaseFastMutex(&client->Lock);
if (pendingQueue != NULL)
{
WdfIoQueuePurgeSynchronously(pendingQueue);
}
}
requesterPid = (ULONG)(ULONG_PTR)PsGetCurrentProcessId();
DbgPrintEx(
DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,
"BK: client detached pid=%lu activeClients=%ld subscriptions=%lu queueDepth=%lu dropped=%lu fileObj=0x%p.\n",
requesterPid, clientCountSnapshot, subscriptionCountSnapshot, queueDepthSnapshot, droppedSnapshot, FileObject);
BkctlClientRelease(client);
ctx->Client = NULL;
}