Replace Blackbird_controller_injection.c with injection.cpp. Add core/util/handles.h
with RAII handle wrappers (UniqueHandle, UniqueProcessHandle) used across the C++
modules to avoid manual CloseHandle call paths. Refs #11.
Replace Blackbird_controller_etw_monitor.c and Blackbird_controller_subscriptions.c
with etw_monitor.cpp and subscriptions.cpp. ETW event callback registration and
the subscription routing table are unchanged. Refs #11.
Replace Blackbird_controller_hollowing.c with hollowing.cpp. The cross-process
hollowing detection logic and intent correlation queries are unchanged. Refs #11.
Replace Blackbird_controller_heuristics.c and its companion .h with heuristics.cpp
and heuristics.h. The scoring and correlation logic is preserved. Update
Blackbird_controller_private.h to remove declarations that were folded into the
per-module headers during the C++ migration. Refs #11.
Replace Blackbird_controller_runtime.c, blackbird_controller_node_runtime.c, and
blackbird_controller_symbol_service.c with runtime.cpp, node_runtime.cpp, and
symbol_service.cpp respectively. Node-runtime thread management uses std::thread
and the symbol service uses std::string for path handling. Refs #11.
Replace Blackbird_controller_ipc.c with ipc.cpp. Critical section handles
are now wrapped through g_DriverLock.get() using the RAII lock wrapper
introduced in the private header. The IPC server dispatch loop and all
command handler stubs are preserved functionally. Refs #11.
Replace Blackbird_controller.c with controller.cpp. The C++ translation uses
RAII for the log file and service handles through a ControllerLogger class,
removing the manual critical-section teardown paths. Refs #11.
Two regressions landed in the same control path.
First, interface authentication was bound to the transient control client that
issued MarkInterfaceReady. Follow-on handles from the same interface process
were treated as unauthenticated and immediately hit ACCESS_DENIED, which broke
OpenControlDevice and later controller calls even when the interface was the
trusted client.
Second, usermode hook events were published into the ETW transport with the
origin address but with StackCount zeroed, so the controller/UI stack chain
collapsed before symbol enrichment could do any useful work.
Restructure the controller side to keep the auth chain and hook evidence intact:
- persist the authorized interface process/session after MarkInterfaceReady
- allow later control handles from that same trusted interface session to
resume authenticated access
- accept both published and repo-build interface paths when establishing trust
- carry captured hook stack frames into the mapped ETW event instead of
discarding them
- prime and symbol-enrich every captured frame up to the ETW stack limit
This restores the expected control-device handshake while also keeping caller
origin and stack evidence available to downstream analysis surfaces.