Files
2026-05-06 09:11:24 +10:00

190 lines
7.8 KiB
C#

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Windows;
namespace BlackbirdInterface
{
public partial class MainWindow
{
private void OpenEtwInspector()
{
var snapshot = EtwPaneHost.SnapshotItems();
if (snapshot.Count == 0)
{
return;
}
TelemetryInspectorWindow.ShowForRows(this, "ETW Inspector",
"Grouped ETW uplink with per-occurrence drill-down", snapshot,
EtwPaneHost.GetSelectedGroupClone(), ResolveHandleEvidenceClone);
}
private void OpenHeuristicsInspector()
{
var snapshot = HeuristicsPaneHost.SnapshotItems();
if (snapshot.Count == 0)
{
return;
}
TelemetryInspectorWindow.ShowForRows(
this, "Detection Chain", "Grouped detections with embedded evidence and correlation context", snapshot,
HeuristicsPaneHost.GetSelectedGroupClone(), ResolveHandleEvidenceClone);
}
private void OpenProcessRelationsInspector()
{
var snapshot = ProcessRelationsPaneHost.SnapshotItems();
if (snapshot.Count == 0)
{
return;
}
TelemetryInspectorWindow.ShowForRows(
this, "Process Relations", "Cross-process relation browser with actor-target drill-down", snapshot,
ProcessRelationsPaneHost.GetSelectedGroupClone(), ResolveHandleEvidenceClone);
}
private void OpenFilesystemInspector()
{
var snapshot = FilesystemPaneHost.SnapshotItems();
if (snapshot.Count == 0)
{
return;
}
TelemetryInspectorWindow.ShowForRows(
this, "Filesystem", "Grouped filesystem activity with path and operation filters", snapshot,
FilesystemPaneHost.GetSelectedGroupClone(), ResolveHandleEvidenceClone);
}
private void OpenRegistryInspector()
{
var snapshot = RegistryPaneHost.SnapshotItems();
if (snapshot.Count == 0)
{
return;
}
TelemetryInspectorWindow.ShowForRows(
this, "Registry", "Grouped registry operations with key path and operation filters", snapshot,
RegistryPaneHost.GetSelectedGroupClone(), ResolveHandleEvidenceClone);
}
private void SelectApiViewRowByApiName(string apiName)
{
if (ApiViewDataGrid == null || string.IsNullOrWhiteSpace(apiName))
{
return;
}
ApiCallGraphMainRowView? match =
_apiViewRows.Where(x => string.Equals(x.ApiName, apiName, StringComparison.OrdinalIgnoreCase))
.OrderByDescending(x => x.Hits)
.ThenByDescending(x => x.LastSeen, StringComparer.Ordinal)
.FirstOrDefault();
if (match == null)
{
return;
}
ApiViewDataGrid.SelectedItem = match;
ApiViewDataGrid.ScrollIntoView(match);
UpdateApiViewSelection(match);
}
private void OpenApiInspector(string apiName)
{
if (string.IsNullOrWhiteSpace(apiName))
{
return;
}
List<GroupedEventRow> snapshot = BuildApiInspectorSnapshot(apiName);
if (snapshot.Count == 0)
{
return;
}
GroupedEventRow? selectedGroup = null;
if (ApiViewDataGrid?.SelectedItem is ApiCallGraphMainRowView selectedRow &&
string.Equals(selectedRow.ApiName, apiName, StringComparison.OrdinalIgnoreCase))
{
selectedGroup = snapshot.FirstOrDefault(
x => string.Equals(x.GroupKey, selectedRow.GraphKey, StringComparison.Ordinal));
}
string subtitle = $"{apiName} grouped by caller, target, thread, and sensor";
TelemetryInspectorWindow.ShowForRows(this, $"{apiName} Inspector", subtitle, snapshot, selectedGroup,
ResolveHandleEvidenceClone);
}
private List<GroupedEventRow> BuildApiInspectorSnapshot(string apiName)
{
var rows = new List<GroupedEventRow>();
foreach ((string graphKey, ApiCallGraphRowSnapshot row) in _apiGraphRowsByKey
.Where(x => string.Equals(x.Value.ApiName, apiName, StringComparison.OrdinalIgnoreCase))
.OrderByDescending(x => x.Value.Hits)
.ThenByDescending(x => x.Value.LastSeenUtc))
{
uint targetPid = row.TargetPid != 0 ? row.TargetPid : row.SourcePid;
string sourceName = GetApiGraphProcessName(row.SourcePid);
string targetName = GetApiGraphProcessName(targetPid);
string sensor = _apiGraphSensorByKey.TryGetValue(graphKey, out string? sensorLabel) ? sensorLabel : row.SensorOrigin;
string decodedAction = _apiGraphActionByKey.TryGetValue(graphKey, out string? actionText) ? actionText : row.ApiName;
string detailText = _apiGraphDecodedByKey.TryGetValue(graphKey, out string? detail) ? detail : string.Empty;
var grouped = new GroupedEventRow {
GroupKey = graphKey, LastSeenUtc = row.LastSeenUtc,
Event = row.ApiName, Severity = string.IsNullOrWhiteSpace(sensor) ? "API" : sensor,
Detection = decodedAction, Hits = Math.Max(1, row.Hits)
};
grouped.Details.Add(new GroupedEventDetailRow {
TimestampUtc = row.LastSeenUtc, Event = row.ApiName,
Severity = string.IsNullOrWhiteSpace(sensor) ? "API" : sensor, Detection = decodedAction,
Source = string.IsNullOrWhiteSpace(sensor) ? "API Graph" : sensor,
Actor = string.IsNullOrWhiteSpace(sourceName)
? $"pid:{row.SourcePid.ToString(CultureInfo.InvariantCulture)}"
: sourceName,
Target = string.IsNullOrWhiteSpace(targetName)
? (targetPid == 0 ? string.Empty
: $"pid:{targetPid.ToString(CultureInfo.InvariantCulture)}")
: targetName,
ActorPid = row.SourcePid, TargetPid = targetPid,
ActorToolTip = row.SourcePid == 0 ? string.Empty
: $"PID {row.SourcePid.ToString(CultureInfo.InvariantCulture)}",
TargetToolTip =
targetPid == 0 ? string.Empty : $"PID {targetPid.ToString(CultureInfo.InvariantCulture)}",
ArgumentSummary =
row.ThreadId == 0
? string.Empty
: $"thread={row.ThreadId.ToString(CultureInfo.InvariantCulture)} hits={Math.Max(1, row.Hits).ToString(CultureInfo.InvariantCulture)}",
Details = detailText
});
rows.Add(grouped);
}
return rows;
}
private IoctlParsedEvent? ResolveHandleEvidenceClone(uint actorPid, uint targetPid)
{
if (actorPid == 0 || targetPid == 0)
{
return null;
}
if (TryGetHandleEvidence(actorPid, targetPid, out IoctlParsedEvent evidence))
{
return evidence.Clone();
}
return null;
}
}
}