warlocks-stave/src/lib.rs

1577 lines
52 KiB
Rust

// ============================================================================
// WARLOCK'S STAVE: PRODUCTION CORE IMPLEMENTATION (v7.0)
// ============================================================================
//
// This is the monolithic core library for Warlock's Stave.
// Submodules are split into separate files and declared below.
//
// Architecture: stateless FFI/JNI boundary -> Arc<Mutex<HexHeatmapEngine>>
// Design: Unix Philosophy — engine is permanent, UI is disposable.
use std::collections::{HashMap, HashSet};
use std::path::Path;
use std::os::raw::c_char;
use std::ffi::{CString, CStr};
use std::net::TcpListener;
use std::io::Read;
use std::sync::{Arc, Mutex};
// Submodule declarations — each file is an independent module.
pub mod ebpf;
pub mod windows_etw;
pub mod polymorphic_ipc;
pub mod firmware;
pub mod stave_ui;
use iced_x86::{Decoder, DecoderOptions, Instruction, Mnemonic};
use jni::JNIEnv;
use jni::objects::{JClass, JObject};
use jni::sys::{jlong, jint, jstring};
use async_trait::async_trait;
// Re-export types needed by other modules and the UI layer.
pub use ebpf::{KernelTraceEvent, LinuxEbpfEngine};
pub use windows_etw::WindowsEtwEngine;
pub use polymorphic_ipc::*;
pub use firmware::HardwareFirmwareAnalyzer;
// ---------------------------------------------------------------------------
// Global Constants
// ---------------------------------------------------------------------------
/// Square root of 3, pre-computed to f64 precision. Used by the hexagonal
/// pixel-raycaster to avoid calling .sqrt() on every frame tick.
pub const SQRT_3: f64 = 1.7320508075688772;
/// Static FFI error string allocated in the data segment. Returned on
/// error paths to prevent host memory leaks when C++ or Java callers
/// forget to invoke the cleanup handler on error branches.
const STATIC_FFI_ERROR: &str =
"{\"status\":\"ERROR\",\"message\":\"Invalid null parameters passed to core\"}\0";
// ============================================================================
// VECTOR HEX TIMELINE & PIXEL-RAYCASTER
// ============================================================================
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct HexCoordinate {
pub q: i32,
pub r: i32,
}
pub struct FractionalHex {
pub q: f64,
pub r: f64,
pub s: f64,
}
/// Semantic markers attached to hex cells to classify execution activity.
/// Used for timeline filtering via Alt+L/B/D/N.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum TimelineSemanticMarker {
InsideLoop,
BreakpointHit,
ModuleLoaded,
BulkDiskRead,
BulkNetworkStream,
}
/// Comprehensive cell state for a single hexagonal grid cell.
pub struct HexCellState {
pub coordinate: HexCoordinate,
pub associated_ticks: Vec<u64>,
pub total_disk_bytes: u64,
pub total_mem_bytes: u64,
pub total_net_bytes: u64,
pub has_breakpoint: bool,
pub markers: HashSet<TimelineSemanticMarker>,
}
/// Central analysis engine. Thread-safe via Arc<Mutex<>> wrapping.
/// Owns the heatmap grid, dictionary scanner, and signature database.
pub struct HexHeatmapEngine {
pub cell_matrix: HashMap<HexCoordinate, HexCellState>,
pub max_intensity_found: u64,
pub hunting_dictionary: Vec<DictionaryRule>,
pub signature_db: SignatureDatabaseEngine,
pub active_filter_mask: u8,
}
impl HexHeatmapEngine {
pub fn new() -> Self {
let engine = Self {
cell_matrix: HashMap::new(),
max_intensity_found: 0,
hunting_dictionary: DictionaryRule::get_default_hunting_library(),
signature_db: {
let mut db = SignatureDatabaseEngine::new();
db.load_default_heuristics();
db
},
active_filter_mask: 0x00,
};
engine
}
/// Converts absolute mouse screen pixels to axial hex coordinates.
/// Uses f64 for perfect floating-point coordinate stability.
pub fn pixel_to_axial(
&self,
px: f32,
py: f32,
radius: f32,
origin_x: f32,
origin_y: f32,
) -> HexCoordinate {
let lx = (px - origin_x) as f64;
let ly = (py - origin_y) as f64;
let r_val = radius as f64;
let frac_q = (2.0 / 3.0 * lx) / r_val;
let frac_r = (-1.0 / 3.0 * lx + SQRT_3 / 3.0 * ly) / r_val;
let frac_s = -frac_q - frac_r;
self.round_to_nearest_hex(FractionalHex {
q: frac_q,
r: frac_r,
s: frac_s,
})
}
fn round_to_nearest_hex(&self, frac: FractionalHex) -> HexCoordinate {
let mut q = frac.q.round() as i32;
let mut r = frac.r.round() as i32;
let s = frac.s.round() as i32;
let q_diff = (q as f64 - frac.q).abs();
let r_diff = (r as f64 - frac.r).abs();
let s_diff = (s as f64 - frac.s).abs();
if q_diff > r_diff && q_diff > s_diff {
q = -r - s;
} else if r_diff > s_diff {
r = -q - s;
}
HexCoordinate { q, r }
}
/// Determines if an execution cell should paint based on dashboard
/// filtering toggles (Alt+L / Alt+B / Alt+D / Alt+N).
pub fn is_cell_visible(&self, cell: &HexCellState) -> bool {
if (self.active_filter_mask & 0b0000_0001) != 0
&& !cell.markers.contains(&TimelineSemanticMarker::InsideLoop)
{
return false;
}
if (self.active_filter_mask & 0b0000_0010) != 0
&& !cell.markers.contains(&TimelineSemanticMarker::BreakpointHit)
{
return false;
}
if (self.active_filter_mask & 0b0000_0100) != 0
&& !cell.markers.contains(&TimelineSemanticMarker::BulkDiskRead)
{
return false;
}
if (self.active_filter_mask & 0b0000_1000) != 0
&& !cell.markers.contains(&TimelineSemanticMarker::BulkNetworkStream)
{
return false;
}
true
}
}
// ============================================================================
// PREDICTIVE BRANCH EVALUATOR ENGINE
// ============================================================================
/// Standalone, zero-allocation processing pipeline driven by iced-x86.
/// Decodes the current opcode segment and checks processor conditional
/// registers to predict instruction outcomes before hardware commits.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BranchOutcome {
ConditionMet { target_address: u64 },
ConditionNotMet,
Unconditional { target_address: u64 },
NonControlFlow,
}
pub struct BranchEvaluator;
impl BranchEvaluator {
/// Evaluates execution flow for the instruction at `rip` with
/// the given `eflags`. Enforces strict 15-byte decoder bounds
/// to prevent reading unmapped memory pages.
pub fn evaluate_execution_flow(
bytes: &[u8],
rip: u64,
eflags: u32,
) -> BranchOutcome {
let max_safe_length = std::cmp::min(bytes.len(), 15);
if max_safe_length == 0 {
return BranchOutcome::NonControlFlow;
}
let safe_decoding_slice = &bytes[0..max_safe_length];
let mut decoder =
Decoder::with_ip(64, safe_decoding_slice, rip, DecoderOptions::NONE);
let mut instruction = Instruction::default();
decoder.decode_out(&mut instruction);
if instruction.is_invalid() {
return BranchOutcome::NonControlFlow;
}
let zf = (eflags & (1 << 6)) != 0;
let sf = (eflags & (1 << 7)) != 0;
let of = (eflags & (1 << 11)) != 0;
match instruction.mnemonic() {
Mnemonic::Jmp | Mnemonic::Call => BranchOutcome::Unconditional {
target_address: instruction.near_branch_target(),
},
Mnemonic::Je => {
if zf {
BranchOutcome::ConditionMet {
target_address: instruction.near_branch_target(),
}
} else {
BranchOutcome::ConditionNotMet
}
}
Mnemonic::Jne => {
if !zf {
BranchOutcome::ConditionMet {
target_address: instruction.near_branch_target(),
}
} else {
BranchOutcome::ConditionNotMet
}
}
Mnemonic::Jg => {
if !zf && (sf == of) {
BranchOutcome::ConditionMet {
target_address: instruction.near_branch_target(),
}
} else {
BranchOutcome::ConditionNotMet
}
}
Mnemonic::Jl => {
if sf != of {
BranchOutcome::ConditionMet {
target_address: instruction.near_branch_target(),
}
} else {
BranchOutcome::ConditionNotMet
}
}
_ => BranchOutcome::NonControlFlow,
}
}
}
// ============================================================================
// DICTIONARY SCANNER & SIGNATURE DATABASE
// ============================================================================
/// A hunting rule for real-time binary pattern matching.
/// NOTE: Does not derive Copy — contains heap-allocated Strings.
pub struct DictionaryRule {
pub label: String,
pub pattern: Vec<u8>,
pub description: String,
}
#[derive(Debug, Clone)]
pub struct DictionaryMatch {
pub label: String,
pub start_offset: usize,
pub length: usize,
}
impl DictionaryRule {
/// Returns the default threat-hunting library containing common
/// indicators: outbound URLs, anti-debugging signatures, encoding
/// alphabets, and embedded executable headers.
pub fn get_default_hunting_library() -> Vec<Self> {
vec![
DictionaryRule {
label: "NET_URI_HTTP".to_string(),
pattern: b"http://".to_vec(),
description: "Outbound HTTP Connection String".to_string(),
},
DictionaryRule {
label: "NET_URI_HTTPS".to_string(),
pattern: b"https://".to_vec(),
description: "Encrypted HTTPS Connection String".to_string(),
},
DictionaryRule {
label: "ANTI_DBG_PRESENT".to_string(),
pattern: b"IsDebuggerPresent".to_vec(),
description: "Anti-Debug Presence Check API".to_string(),
},
DictionaryRule {
label: "ANTI_DBG_PTRACE".to_string(),
pattern: b"ptrace".to_vec(),
description: "Process Trace Request (Linux)".to_string(),
},
DictionaryRule {
label: "CRYPTO_BASE64".to_string(),
pattern: b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".to_vec(),
description: "Base64 Encoding Alphabet Matrix".to_string(),
},
DictionaryRule {
label: "PE_MAGIC_HEADER".to_string(),
pattern: b"MZ".to_vec(),
description: "Embedded Portable Executable Header".to_string(),
},
]
}
}
/// Linear scanner that searches target memory buffers against
/// the active dictionary rule set.
pub struct MemoryScanner;
impl MemoryScanner {
pub fn scan_buffer(
buffer: &[u8],
dictionary: &[DictionaryRule],
) -> Vec<DictionaryMatch> {
let mut matches = Vec::new();
for rule in dictionary {
let p_len = rule.pattern.len();
if p_len == 0 || p_len > buffer.len() {
continue;
}
for i in 0..=(buffer.len() - p_len) {
if &buffer[i..(i + p_len)] == rule.pattern.as_slice() {
matches.push(DictionaryMatch {
label: rule.label.clone(),
start_offset: i,
length: p_len,
});
}
}
}
matches
}
}
/// External signature rule for loading from YARA-compatible rule files,
/// ClamAV-compatible signature databases, or custom byte-sequence definitions.
#[derive(Debug, Clone)]
pub struct SignatureRule {
pub rule_id: String,
pub engine_source: String,
pub pattern: Vec<u8>,
pub description: String,
pub severity: u8, // 0=info, 1=low, 2=medium, 3=high, 4=critical
}
/// Pluggable signature database engine supporting multiple backends.
/// Performs multi-pass buffer evaluation against all registered rules.
pub struct SignatureDatabaseEngine {
pub active_rules_count: u32,
rules: Vec<SignatureRule>,
}
impl SignatureDatabaseEngine {
pub fn new() -> Self {
Self {
active_rules_count: 0,
rules: Vec::new(),
}
}
pub fn register_rule(&mut self, rule: SignatureRule) {
self.active_rules_count += 1;
self.rules.push(rule);
}
/// Loads default heuristics covering common packing and
/// anti-analysis indicators.
pub fn load_default_heuristics(&mut self) {
let defaults = vec![
SignatureRule {
rule_id: "SIG_UPX_PACKED".to_string(),
engine_source: "custom".to_string(),
pattern: b"UPX0".to_vec(),
description: "UPX-compressed binary section marker detected".to_string(),
severity: 1,
},
SignatureRule {
rule_id: "SIG_PETITE_PACKED".to_string(),
engine_source: "custom".to_string(),
pattern: b"petite".to_vec(),
description: "Petite packer section header detected".to_string(),
severity: 1,
},
SignatureRule {
rule_id: "SIG_VM_PROTECT".to_string(),
engine_source: "custom".to_string(),
pattern: b".vmp0".to_vec(),
description: "VMProtect virtualization section marker".to_string(),
severity: 3,
},
SignatureRule {
rule_id: "SIG_IMPORT_DLL_INJECT".to_string(),
engine_source: "custom".to_string(),
pattern: b"LoadLibraryA".to_vec(),
description: "Dynamic library injection API import".to_string(),
severity: 2,
},
];
for rule in defaults {
self.register_rule(rule);
}
}
/// Evaluates a buffer against all registered signature rules.
/// Primary hook point for integrating external scanner backends
/// (YARA via yara-rust, ClamAV via libclamav FFI).
pub fn evaluate_buffer(&self, buffer: &[u8]) -> Vec<String> {
let mut hits = Vec::new();
for rule in &self.rules {
let p_len = rule.pattern.len();
if p_len == 0 || p_len > buffer.len() {
continue;
}
for i in 0..=(buffer.len() - p_len) {
if &buffer[i..(i + p_len)] == rule.pattern.as_slice() {
hits.push(format!(
"[{}] {} (severity: {})",
rule.engine_source, rule.rule_id, rule.severity
));
break;
}
}
}
hits
}
}
// ============================================================================
// MIRROR ILLUSION STRUCTURAL DIFF ENGINE
// ============================================================================
/// Status of a single 16-byte grid line in the dual-pane diff viewport.
#[derive(Debug, Clone, PartialEq)]
pub enum DiffStatus {
Identical,
Modified,
Inserted,
Deleted,
}
/// A single row in the diff grid. Represents 16 bytes of aligned
/// comparison data with per-byte status flags and section context.
#[derive(Debug, Clone)]
pub struct DiffGridLine {
pub file_a_offset: u64,
pub file_a_bytes: Vec<u8>,
pub file_b_offset: u64,
pub file_b_bytes: Vec<u8>,
pub per_byte_status: Vec<DiffStatus>,
pub section_name: String,
}
/// Describes a binary section boundary (ELF/PE segment or section header).
/// Used to align diffs by logical structure rather than raw file offsets.
#[derive(Debug, Clone)]
pub struct BinarySectionBoundary {
pub name: String,
pub virtual_address: u64,
pub file_offset: u64,
pub size: u64,
}
/// Quick structural diff comparing two instruction streams by
/// (address, length) pairs for the hex heatmap.
pub fn calculate_structural_diff(
baseline: &[(u64, u8)],
current: &[(u64, u8)],
) -> Vec<DiffEntry> {
let base_set: HashSet<(u64, u8)> = baseline.iter().cloned().collect();
let curr_set: HashSet<(u64, u8)> = current.iter().cloned().collect();
let mut diffs = Vec::new();
for &(addr, len) in baseline.iter() {
if curr_set.contains(&(addr, len)) {
continue;
}
let is_deleted = !current.iter().any(|(a, _)| *a == addr);
diffs.push(DiffEntry {
address: addr,
length: len,
status: if is_deleted {
DiffStatus::Deleted
} else {
DiffStatus::Modified
},
});
}
for &(addr, len) in current.iter() {
if !base_set.contains(&(addr, len)) {
if !baseline.iter().any(|(a, _)| *a == addr) {
diffs.push(DiffEntry {
address: addr,
length: len,
status: DiffStatus::Inserted,
});
}
}
}
diffs.sort_by_key(|d| d.address);
diffs
}
/// Lightweight diff entry for heatmap integration.
#[derive(Debug, Clone)]
pub struct DiffEntry {
pub address: u64,
pub length: u8,
pub status: DiffStatus,
}
/// Full section-aware binary diff that aligns two files by their
/// ELF/PE section boundaries rather than raw byte offsets.
pub fn calculate_section_aware_diff(
data_a: &[u8],
data_b: &[u8],
sections_a: &[BinarySectionBoundary],
sections_b: &[BinarySectionBoundary],
) -> Vec<DiffGridLine> {
let mut grid_lines = Vec::new();
for sec_a in sections_a {
let sec_b = match sections_b.iter().find(|s| s.name == sec_a.name) {
Some(s) => s,
None => continue,
};
let compare_length = sec_a.size.min(sec_b.size) as usize;
let start_a = sec_a.file_offset as usize;
let start_b = sec_b.file_offset as usize;
let safe_len = compare_length
.min(data_a.len().saturating_sub(start_a))
.min(data_b.len().saturating_sub(start_b));
let mut offset = 0;
while offset + 16 <= safe_len {
let chunk_a = &data_a[start_a + offset..start_a + offset + 16];
let chunk_b = &data_b[start_b + offset..start_b + offset + 16];
let mut per_byte = Vec::with_capacity(16);
let mut has_any_diff = false;
for i in 0..16 {
if chunk_a[i] != chunk_b[i] {
per_byte.push(DiffStatus::Modified);
has_any_diff = true;
} else {
per_byte.push(DiffStatus::Identical);
}
}
if has_any_diff {
grid_lines.push(DiffGridLine {
file_a_offset: sec_a.file_offset + offset as u64,
file_a_bytes: chunk_a.to_vec(),
file_b_offset: sec_b.file_offset + offset as u64,
file_b_bytes: chunk_b.to_vec(),
per_byte_status: per_byte,
section_name: sec_a.name.clone(),
});
}
offset += 16;
}
}
grid_lines
}
// ============================================================================
// TRI-TIER SCOPE ENGINE
// ============================================================================
/// Three concurrent analytical layers that classify the context of each
/// execution frame: Global (cross-references), Local (function block),
/// and Micro (individual instruction side-effects).
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ScopeTier {
/// Global: maps cross-references, export footprints, and entropy strips.
Global,
/// Local: isolates analysis to the active function block.
/// NOTE: Contains a String, so this enum cannot be Copy.
Local { function_name: String, start_rip: u64 },
/// Micro: monitors individual instruction side-effects.
Micro,
}
/// Predicted implicit register and memory modifications for an instruction.
#[derive(Debug, Clone)]
pub struct MicroSideEffect {
pub implicit_write_registers: Vec<String>,
pub implicit_memory_writes: Vec<u64>,
pub flags_affected: bool,
}
/// The scope engine that determines the current analytical context
/// based on the instruction pointer and control flow history.
pub struct ScopeEngine {
pub current_tier: ScopeTier,
pub function_boundaries: HashMap<String, (u64, u64)>,
pub global_xref_count: usize,
pub region_entropy: f64,
}
impl ScopeEngine {
pub fn new() -> Self {
Self {
current_tier: ScopeTier::Global,
function_boundaries: HashMap::new(),
global_xref_count: 0,
region_entropy: 0.0,
}
}
/// Determines the active scope tier based on the current RIP.
pub fn update_scope_for_rip(&mut self, rip: u64) {
for (name, (start, end)) in &self.function_boundaries {
if rip >= *start && rip < *end {
self.current_tier = ScopeTier::Local {
function_name: name.clone(),
start_rip: *start,
};
return;
}
}
self.current_tier = ScopeTier::Global;
}
/// Registers a function boundary for local scope tracking.
pub fn register_function(&mut self, name: String, start: u64, end: u64) {
self.function_boundaries.insert(name, (start, end));
}
/// Analyzes the decoded instruction to predict which registers
/// and memory locations will be implicitly modified.
pub fn predict_side_effects(
&self,
bytes: &[u8],
rip: u64,
) -> MicroSideEffect {
let max_safe = std::cmp::min(bytes.len(), 15);
if max_safe == 0 {
return MicroSideEffect {
implicit_write_registers: Vec::new(),
implicit_memory_writes: Vec::new(),
flags_affected: false,
};
}
let mut decoder =
Decoder::with_ip(64, &bytes[0..max_safe], rip, DecoderOptions::NONE);
let mut instruction = Instruction::default();
decoder.decode_out(&mut instruction);
if instruction.is_invalid() {
return MicroSideEffect {
implicit_write_registers: Vec::new(),
implicit_memory_writes: Vec::new(),
flags_affected: false,
};
}
let mut written_regs = Vec::new();
let mut mem_writes = Vec::new();
let mut flags = false;
let mut info_factory = iced_x86::InstructionInfoFactory::new();
let info = info_factory.info(&instruction);
for i in 0..instruction.op_count() {
let access = info.op_access(i);
match instruction.op_kind(i) {
iced_x86::OpKind::Register => {
let reg = instruction.op_register(i);
if reg != iced_x86::Register::None {
let name = format!("{:?}", reg);
if instruction.mnemonic() != Mnemonic::Push
&& instruction.mnemonic() != Mnemonic::Pop
{
match access {
iced_x86::OpAccess::Write
| iced_x86::OpAccess::ReadWrite => {
written_regs.push(name);
}
_ => {}
}
}
}
}
iced_x86::OpKind::Memory => {
match access {
iced_x86::OpAccess::Write
| iced_x86::OpAccess::ReadWrite => {
let disp = instruction.memory_displacement64();
if disp != 0 {
mem_writes.push(disp as u64);
}
}
_ => {}
}
}
_ => {}
}
}
use iced_x86::FlowControl;
match instruction.flow_control() {
FlowControl::Next | FlowControl::ConditionalBranch => {
flags = true;
}
_ => {}
}
MicroSideEffect {
implicit_write_registers: written_regs,
implicit_memory_writes: mem_writes,
flags_affected: flags,
}
}
}
// ============================================================================
// MULTIVERSE SECURITY INFRASTRUCTURE PROVIDERS
// ============================================================================
/// Orchestrates independent local scanners and cloud intelligence
/// infrastructure using decoupled background pipelines.
#[derive(Debug, Clone)]
pub struct GlobalSpreadMetrics {
pub detection_count: u32,
pub total_scanners: u32,
pub velocity_flag: String,
}
#[derive(Debug, Clone)]
pub enum ScanEngineStatus {
Clean,
Infected { threat_name: String },
GlobalAwarenessUpdate(GlobalSpreadMetrics),
Pending,
Error(String),
}
/// Trait for external scanner backends. Implementations can wrap
/// YARA-compatible engines, ClamAV-compatible engines, or cloud
/// hash-lookup services.
#[async_trait]
pub trait ExternalScannerProvider: Send + Sync {
fn name(&self) -> &'static str;
async fn scan_file_path(&self, path: &std::path::Path) -> ScanEngineStatus;
async fn scan_hash(&self, sha256: &str) -> ScanEngineStatus;
}
// ============================================================================
// SELF-ASSEMBLING IPC TRANSMISSION FRAMEWORK
// ============================================================================
pub enum IpcTransport {
UnixSocket(String),
TcpNetwork(u16),
}
/// Wire-format payload for the Wine/Windows named-pipe translation bridge.
/// Packed to match the C++ struct layout on the other side of the FFI.
#[repr(C, packed)]
#[derive(Clone, Copy)]
pub struct WineIpcPayload {
pub rip: u64,
pub eflags: u32,
pub buffer_len: u32,
pub bytes: [u8; 15],
}
/// Dynamically assembles the most efficient IPC transport for the
/// current host environment at startup.
pub struct HybridIpcOrchestrator;
impl HybridIpcOrchestrator {
pub fn is_containerized() -> bool {
Path::new("/.dockerenv").exists()
|| Path::new("/var/run/secrets/kubernetes.io").exists()
|| std::env::var("STAVE_CONTAINER_MODE").is_ok()
}
pub fn is_waydroid_environment() -> bool {
Path::new("/dev/waydroid-binder").exists()
|| std::fs::read_to_string("/proc/self/cgroup")
.map(|c| c.contains("waydroid"))
.unwrap_or(false)
}
pub fn auto_assemble_transport() -> IpcTransport {
if Self::is_containerized() {
IpcTransport::TcpNetwork(21860)
} else {
IpcTransport::UnixSocket("/tmp/stave_bridge_ipc.sock".to_string())
}
}
/// Launches the IPC listener on a background thread. Uses Arc<Mutex<>>
/// for thread-safe access to the core engine from multiple IPC clients.
pub fn launch(
core_context: Arc<Mutex<HexHeatmapEngine>>,
) -> std::io::Result<()> {
let transport = Self::auto_assemble_transport();
std::thread::spawn(move || {
let mut struct_buffer =
vec![0u8; std::mem::size_of::<WineIpcPayload>()];
match transport {
IpcTransport::UnixSocket(path) => {
if Path::new(&path).exists() {
let _ = std::fs::remove_file(&path);
}
let listener =
std::os::unix::net::UnixListener::bind(path).unwrap();
for mut stream in listener.incoming().flatten() {
while stream.read_exact(&mut struct_buffer).is_ok() {
Self::process_buffer(&struct_buffer, &core_context);
}
}
}
IpcTransport::TcpNetwork(port) => {
let listener =
TcpListener::bind(format!("0.0.0.0:{}", port)).unwrap();
for stream in listener.incoming().flatten() {
let _ = stream.set_nodelay(true);
let mut s = stream;
while s.read_exact(&mut struct_buffer).is_ok() {
Self::process_buffer(&struct_buffer, &core_context);
}
}
}
}
});
Ok(())
}
fn process_buffer(
raw_buffer: &[u8],
context: &Arc<Mutex<HexHeatmapEngine>>,
) {
let payload =
unsafe { &*(raw_buffer.as_ptr() as *const WineIpcPayload) };
let mut core = context.lock().unwrap();
let coord = HexCoordinate {
q: (payload.rip & 0xFF) as i32,
r: ((payload.rip >> 8) & 0xFF) as i32,
};
{
let entry = core.cell_matrix.entry(coord).or_insert(HexCellState {
coordinate: coord,
associated_ticks: Vec::new(),
total_disk_bytes: 0,
total_mem_bytes: 0,
total_net_bytes: 0,
has_breakpoint: false,
markers: HashSet::new(),
});
entry.associated_ticks.push(payload.rip);
let hit_count = entry.associated_ticks.len() as u64;
if hit_count > core.max_intensity_found {
core.max_intensity_found = hit_count;
}
}
// Borrow on `entry` is now dropped; safe to access other fields.
let usable_len = std::cmp::min(payload.buffer_len as usize, 15);
let instr_bytes = &payload.bytes[0..usable_len];
let _branch = BranchEvaluator::evaluate_execution_flow(
instr_bytes, payload.rip, payload.eflags,
);
let _dict_hits =
MemoryScanner::scan_buffer(instr_bytes, &core.hunting_dictionary);
let _sig_hits = core.signature_db.evaluate_buffer(instr_bytes);
}
}
// ============================================================================
// C-COMPATIBLE FFI BOUNDARY LAYER
// ============================================================================
/// Opaque context handle exposed to C/C++ host environments.
/// Internally wraps the thread-safe Arc<Mutex<>> core engine.
pub struct StaveCoreContext;
/// Initializes the Warlock's Stave engine and returns an opaque handle.
///
/// # Safety
/// The returned pointer must be passed to `destroy_warlock_stave()` to
/// free resources. Do not double-free.
///
/// # Example (C)
/// ```c
/// void* ctx = initialize_warlock_stave();
/// const char* result = stave_ingest_frame(ctx, buf, len, rip, eflags);
/// free_stave_string(result);
/// destroy_warlock_stave(ctx);
/// ```
#[no_mangle]
pub unsafe extern "C" fn initialize_warlock_stave() -> *mut StaveCoreContext {
let core = Arc::new(Mutex::new(HexHeatmapEngine::new()));
let _ = HybridIpcOrchestrator::launch(core.clone());
Box::into_raw(Box::new(core)) as *mut StaveCoreContext
}
/// Ingests a single execution frame from the host debugger.
/// Returns a JSON-Lines string describing branch evaluation and threat hits.
///
/// # Safety
/// - `context` must be a valid pointer from `initialize_warlock_stave()`.
/// - `raw_buffer_ptr` must be a valid pointer to at least `buffer_length` bytes.
/// - The returned pointer must be freed with `free_stave_string()`.
#[no_mangle]
pub unsafe extern "C" fn stave_ingest_frame(
context: *mut StaveCoreContext,
raw_buffer_ptr: *const u8,
buffer_length: usize,
current_rip: u64,
eflags: u32,
) -> *mut c_char {
if context.is_null() || raw_buffer_ptr.is_null() || buffer_length == 0 {
return STATIC_FFI_ERROR.as_ptr() as *mut c_char;
}
let core_arc = &*(context as *mut Arc<Mutex<HexHeatmapEngine>>);
let mut core = core_arc.lock().unwrap();
let memory_slice = std::slice::from_raw_parts(raw_buffer_ptr, buffer_length);
let hex_coord = HexCoordinate {
q: (current_rip & 0xFF) as i32,
r: ((current_rip >> 8) & 0xFF) as i32,
};
{
let entry = core.cell_matrix.entry(hex_coord).or_insert(HexCellState {
coordinate: hex_coord,
associated_ticks: Vec::new(),
total_disk_bytes: 0,
total_mem_bytes: 0,
total_net_bytes: 0,
has_breakpoint: false,
markers: HashSet::new(),
});
entry.associated_ticks.push(current_rip);
let hits = entry.associated_ticks.len() as u64;
if hits > core.max_intensity_found {
core.max_intensity_found = hits;
}
}
let branch_outcome =
BranchEvaluator::evaluate_execution_flow(memory_slice, current_rip, eflags);
let local_dict_matches =
MemoryScanner::scan_buffer(memory_slice, &core.hunting_dictionary);
let signature_hits = core.signature_db.evaluate_buffer(memory_slice);
let dict_labels: HashSet<&str> = local_dict_matches
.iter()
.map(|m| m.label.as_str())
.collect();
let has_network_io = dict_labels.contains("NET_URI_HTTP")
|| dict_labels.contains("NET_URI_HTTPS");
let has_disk_io = dict_labels.contains("PE_MAGIC_HEADER");
let payload = serde_json::json!({
"status": "OK",
"tick": core.max_intensity_found,
"event": "STATE_SYNCHRONIZATION",
"execution_pointer": format!("{:#018X}", current_rip),
"hex_coords": {
"q": hex_coord.q,
"r": hex_coord.r
},
"branch_evaluation": format!("{:?}", branch_outcome),
"threat_signatures": {
"dictionary_hits": local_dict_matches.len(),
"dictionary_labels": local_dict_matches.iter().map(|m| &m.label).collect::<Vec<_>>(),
"signature_engine_hits": signature_hits
},
"io_activity": {
"disk_bytes": if has_disk_io { buffer_length as u64 } else { 0 },
"memory_bytes": buffer_length as u64,
"network_bytes": if has_network_io { buffer_length as u64 } else { 0 }
}
});
let json_string = CString::new(payload.to_string()).unwrap();
json_string.into_raw()
}
/// Frees a JSON string previously returned by `stave_ingest_frame`.
/// Guards against double-freeing the static error string.
///
/// # Safety
/// `ptr` must be either null or a pointer returned by `stave_ingest_frame`.
#[no_mangle]
pub unsafe extern "C" fn free_stave_string(ptr: *mut c_char) {
if !ptr.is_null() && ptr != STATIC_FFI_ERROR.as_ptr() as *mut c_char {
let _ = CString::from_raw(ptr);
}
}
/// Destroys the core engine context and releases all resources.
///
/// # Safety
/// `context` must be a valid pointer from `initialize_warlock_stave()`.
/// Do not use the pointer after calling this function.
#[no_mangle]
pub unsafe extern "C" fn destroy_warlock_stave(context: *mut StaveCoreContext) {
if !context.is_null() {
let _ = Box::from_raw(
context as *mut Arc<Mutex<HexHeatmapEngine>>,
);
}
}
// ============================================================================
// JAVA NATIVE INTERFACE (JNI) EXTENSION
// ============================================================================
/// Host integration entry point. Receives a DirectByteBuffer from the
/// JVM containing raw instruction bytes, passes them through the core
/// analysis pipeline, and returns a JSON telemetry string.
///
/// # Safety
/// - `context_ptr` must be a valid pointer from `initializeWarlockStave()`.
/// - `byte_buffer` must be a valid direct NIO ByteBuffer.
#[no_mangle]
pub unsafe extern "system" fn Java_org_stave_WarlockStaveBridge_staveIngestGhidraFrame(
env: JNIEnv,
_class: JClass,
context_ptr: jlong,
byte_buffer: JObject,
rip: jlong,
eflags: jint,
) -> jstring {
if context_ptr == 0 || byte_buffer.is_null() {
return env
.new_string("{\"status\":\"ERROR\",\"message\":\"Null parameter in JNI link\"}")
.unwrap()
.into_raw();
}
let jbb: jni::objects::JByteBuffer = byte_buffer.into();
let buffer_address = env.get_direct_buffer_address(&jbb).unwrap();
let buffer_capacity = env.get_direct_buffer_capacity(&jbb).unwrap();
let raw_memory_slice =
std::slice::from_raw_parts(buffer_address, buffer_capacity);
let res_ptr = stave_ingest_frame(
context_ptr as *mut StaveCoreContext,
raw_memory_slice.as_ptr(),
raw_memory_slice.len(),
rip as u64,
eflags as u32,
);
let cstr_result = CStr::from_ptr(res_ptr);
let output_string = env.new_string(cstr_result.to_str().unwrap()).unwrap();
free_stave_string(res_ptr);
output_string.into_raw()
}
// ============================================================================
// CROSS-PLATFORM SYSTEM INTELLIGENCE INTERFACES
// ============================================================================
/// Dispatches platform-specific telemetry hooks at engine startup.
pub struct MultiplatformTelemetryHub;
impl MultiplatformTelemetryHub {
pub fn start_native_hooks(&self) {
#[cfg(target_os = "linux")]
{
println!(
"[PLATFORM] Linux kernel environment. \
Mounting raw eBPF context tracking frames."
);
// NOTE: LinuxEbpfEngine::spawn_kernel_hooks() requires
// elevated permissions. Callers who need kernel tracing
// should invoke it explicitly after confirming permissions.
}
#[cfg(target_os = "windows")]
{
println!(
"[PLATFORM] Windows environment. \
Attaching Event Tracing for Windows (ETW) hooks."
);
unsafe { WindowsEtwEngine::spawn_windows_tap() };
}
#[cfg(target_os = "macos")]
{
println!(
"[PLATFORM] macOS environment. \
Endpoint Security Framework consumers reserved."
);
}
}
}
// ============================================================================
// STATE DECAY SYSTEM
// ============================================================================
/// Default number of frames a highlight persists before fading.
/// At 60 FPS this gives roughly 2 seconds of visible highlight.
const DEFAULT_HIGHLIGHT_TTL: u8 = 120;
/// Tracks a single register's mutation state and its decay counter.
#[derive(Debug, Clone)]
pub struct RegisterHighlight {
pub register_name: String,
pub old_value: u64,
pub new_value: u64,
pub frames_remaining: u8,
}
/// Tracks a single memory byte's mutation state and its decay counter.
#[derive(Debug, Clone)]
pub struct MemoryHighlight {
pub address: u64,
pub old_byte: u8,
pub new_byte: u8,
pub frames_remaining: u8,
}
/// Manages all active highlights and drives the per-frame decay cycle.
pub struct StateDecayEngine {
pub register_highlights: HashMap<String, RegisterHighlight>,
pub memory_highlights: HashMap<u64, MemoryHighlight>,
pub highlight_ttl: u8,
}
impl StateDecayEngine {
pub fn new() -> Self {
Self {
register_highlights: HashMap::new(),
memory_highlights: HashMap::new(),
highlight_ttl: DEFAULT_HIGHLIGHT_TTL,
}
}
pub fn mark_register_mutated(
&mut self,
name: &str,
old_val: u64,
new_val: u64,
) {
self.register_highlights.insert(
name.to_uppercase(),
RegisterHighlight {
register_name: name.to_uppercase(),
old_value: old_val,
new_value: new_val,
frames_remaining: self.highlight_ttl,
},
);
}
pub fn mark_memory_mutated(
&mut self,
address: u64,
old_byte: u8,
new_byte: u8,
) {
self.memory_highlights.insert(
address,
MemoryHighlight {
address,
old_byte,
new_byte,
frames_remaining: self.highlight_ttl,
},
);
}
/// Advances the decay clock by one frame. Returns the set of
/// register names and memory addresses that expired this tick.
pub fn tick(&mut self) -> (Vec<String>, Vec<u64>) {
let mut expired_registers = Vec::new();
let mut expired_memory = Vec::new();
self.register_highlights.retain(|name, highlight| {
highlight.frames_remaining = highlight.frames_remaining.saturating_sub(1);
if highlight.frames_remaining == 0 {
expired_registers.push(name.clone());
false
} else {
true
}
});
self.memory_highlights.retain(|addr, highlight| {
highlight.frames_remaining = highlight.frames_remaining.saturating_sub(1);
if highlight.frames_remaining == 0 {
expired_memory.push(*addr);
false
} else {
true
}
});
(expired_registers, expired_memory)
}
pub fn is_register_highlighted(&self, name: &str) -> bool {
self.register_highlights.contains_key(&name.to_uppercase())
}
pub fn is_memory_highlighted(&self, addr: u64) -> bool {
self.memory_highlights.contains_key(&addr)
}
}
// ============================================================================
// SHELLCODE SENTINEL (HEAP PERMISSION TRANSITION MONITOR)
// ============================================================================
/// Watches runtime memory segment permissions for transitions that
/// indicate shellcode injection or JIT-spray attacks. The canonical
/// indicator is a page transitioning from writable (RW-) to executable
/// (R-X or RWX) — legitimate code does not write to executable memory
/// at runtime.
/// Describes a tracked memory region and its observed permission state.
#[derive(Debug, Clone)]
pub struct TrackedMemoryRegion {
pub start_address: u64,
pub end_address: u64,
pub permissions: String,
pub pathname: Option<String>,
pub transition_count: u32,
pub last_transition_kind: Option<PermissionTransition>,
}
/// The kind of permission transition that was detected.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PermissionTransition {
WriteAddedToExecutable,
ExecuteAddedToWritable,
ExecuteRemoved,
}
/// A shellcode sentinel alert event.
#[derive(Debug, Clone)]
pub struct ShellcodeAlert {
pub pid: u32,
pub region: TrackedMemoryRegion,
pub transition: PermissionTransition,
pub timestamp: std::time::Instant,
}
/// The sentinel engine that monitors memory permission transitions.
pub struct ShellcodeSentinel {
previous_snapshots: HashMap<u32, Vec<TrackedMemoryRegion>>,
pub active_alerts: Vec<ShellcodeAlert>,
}
impl ShellcodeSentinel {
pub fn new() -> Self {
Self {
previous_snapshots: HashMap::new(),
active_alerts: Vec::new(),
}
}
/// Parses /proc/<pid>/maps into tracked memory regions.
fn parse_proc_maps(pid: u32) -> Vec<TrackedMemoryRegion> {
let mut regions = Vec::new();
let maps_path = format!("/proc/{}/maps", pid);
if let Ok(content) = std::fs::read_to_string(&maps_path) {
for line in content.lines() {
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() < 2 {
continue;
}
let addrs: Vec<&str> = parts[0].split('-').collect();
if addrs.len() != 2 {
continue;
}
let start = u64::from_str_radix(addrs[0], 16).unwrap_or(0);
let end = u64::from_str_radix(addrs[1], 16).unwrap_or(0);
let pathname = if parts.len() >= 6 {
Some(parts[5..].join(" "))
} else {
None
};
regions.push(TrackedMemoryRegion {
start_address: start,
end_address: end,
permissions: parts[1].to_string(),
pathname,
transition_count: 0,
last_transition_kind: None,
});
}
}
regions
}
/// Compares two permission strings and returns the transition
/// kind if a suspicious change is detected.
fn detect_suspicious_transition(
old_perms: &str,
new_perms: &str,
) -> Option<PermissionTransition> {
let old_w = old_perms.chars().nth(1).unwrap_or('-') == 'w';
let old_x = old_perms.chars().nth(2).unwrap_or('-') == 'x';
let new_w = new_perms.chars().nth(1).unwrap_or('-') == 'w';
let new_x = new_perms.chars().nth(2).unwrap_or('-') == 'x';
if !old_x && new_x && (new_w || old_w) {
return Some(PermissionTransition::ExecuteAddedToWritable);
}
if !old_w && new_w && (new_x || old_x) {
return Some(PermissionTransition::WriteAddedToExecutable);
}
if old_x && !new_x {
return Some(PermissionTransition::ExecuteRemoved);
}
None
}
/// Takes a fresh snapshot of the target process's memory map,
/// compares against the previous snapshot, and fires alerts.
pub fn scan_process(&mut self, pid: u32) {
let current_regions = Self::parse_proc_maps(pid);
let previous = self
.previous_snapshots
.entry(pid)
.or_insert_with(Vec::new);
for curr in &current_regions {
for prev in previous.iter_mut() {
if curr.start_address == prev.start_address
&& curr.end_address == prev.end_address
{
if let Some(transition) =
Self::detect_suspicious_transition(
&prev.permissions,
&curr.permissions,
)
{
prev.transition_count += 1;
prev.last_transition_kind = Some(transition.clone());
prev.permissions = curr.permissions.clone();
self.active_alerts.push(ShellcodeAlert {
pid,
region: prev.clone(),
transition,
timestamp: std::time::Instant::now(),
});
} else {
prev.permissions = curr.permissions.clone();
}
break;
}
}
}
*previous = current_regions;
}
/// Clears alerts older than the given duration.
pub fn expire_old_alerts(&mut self, max_age: std::time::Duration) {
let now = std::time::Instant::now();
self.active_alerts
.retain(|a| now.duration_since(a.timestamp) < max_age);
}
}
// ============================================================================
// UNIT TESTS
// ============================================================================
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_branch_evaluator_unconditional_jmp() {
// EB 05 = jmp rel8 +5
let bytes = [0xEB, 0x05];
let result = BranchEvaluator::evaluate_execution_flow(&bytes, 0x1000, 0);
assert_eq!(
result,
BranchOutcome::Unconditional { target_address: 0x1007 }
);
}
#[test]
fn test_branch_evaluator_conditional_je_taken() {
// 74 05 = je +5, with ZF=1
let bytes = [0x74, 0x05];
let result = BranchEvaluator::evaluate_execution_flow(&bytes, 0x2000, 1 << 6);
assert_eq!(
result,
BranchOutcome::ConditionMet { target_address: 0x2007 }
);
}
#[test]
fn test_branch_evaluator_conditional_je_not_taken() {
// 74 05 = je +5, with ZF=0
let bytes = [0x74, 0x05];
let result = BranchEvaluator::evaluate_execution_flow(&bytes, 0x2000, 0);
assert_eq!(result, BranchOutcome::ConditionNotMet);
}
#[test]
fn test_branch_evaluator_non_control_flow() {
// 48 31 C0 = xor rax, rax
let bytes = [0x48, 0x31, 0xC0];
let result = BranchEvaluator::evaluate_execution_flow(&bytes, 0x3000, 0);
assert_eq!(result, BranchOutcome::NonControlFlow);
}
#[test]
fn test_branch_evaluator_empty_buffer() {
let result = BranchEvaluator::evaluate_execution_flow(&[], 0x4000, 0);
assert_eq!(result, BranchOutcome::NonControlFlow);
}
#[test]
fn test_branch_evaluator_15_byte_clamp() {
// Should not panic even with a 20-byte buffer
let bytes = [0u8; 20];
let _ = BranchEvaluator::evaluate_execution_flow(&bytes, 0x5000, 0);
}
#[test]
fn test_dictionary_scanner_finds_http() {
let rules = DictionaryRule::get_default_hunting_library();
let buffer = b"GET http://example.com HTTP/1.1";
let matches = MemoryScanner::scan_buffer(buffer, &rules);
assert!(matches.iter().any(|m| m.label == "NET_URI_HTTP"));
}
#[test]
fn test_dictionary_scanner_empty_buffer() {
let rules = DictionaryRule::get_default_hunting_library();
let matches = MemoryScanner::scan_buffer(&[], &rules);
assert!(matches.is_empty());
}
#[test]
fn test_signature_db_load_and_evaluate() {
let mut db = SignatureDatabaseEngine::new();
db.load_default_heuristics();
assert!(db.active_rules_count > 0);
let buffer = b"UPX0";
let hits = db.evaluate_buffer(buffer);
assert!(hits.iter().any(|h| h.contains("SIG_UPX_PACKED")));
}
#[test]
fn test_structural_diff_insertion() {
let baseline = [(0x1000u64, 5u8), (0x1005u64, 3u8)];
let current = [(0x1000u64, 5u8), (0x1005u64, 3u8), (0x2000u64, 7u8)];
let diffs = calculate_structural_diff(&baseline, &current);
assert!(diffs.iter().any(|d| d.status == DiffStatus::Inserted));
}
#[test]
fn test_structural_diff_modification() {
let baseline = [(0x1000u64, 5u8)];
let current = [(0x1000u64, 7u8)];
let diffs = calculate_structural_diff(&baseline, &current);
assert_eq!(diffs.len(), 1);
assert_eq!(diffs[0].status, DiffStatus::Modified);
}
#[test]
fn test_shannon_entropy_uniform() {
let buffer = [0u8; 256];
let entropy = crate::polymorphic_ipc::calculate_shannon_entropy(&buffer);
assert_eq!(entropy, 0);
}
#[test]
fn test_shannon_entropy_empty() {
let entropy = crate::polymorphic_ipc::calculate_shannon_entropy(&[]);
assert_eq!(entropy, 0);
}
#[test]
fn test_state_decay_tick() {
let mut engine = StateDecayEngine::new();
engine.mark_register_mutated("RAX", 0, 1);
assert!(engine.is_register_highlighted("rax"));
// Decay until expired
for _ in 0..DEFAULT_HIGHLIGHT_TTL {
engine.tick();
}
assert!(!engine.is_register_highlighted("RAX"));
}
#[test]
fn test_scope_engine_function_boundary() {
let mut scope = ScopeEngine::new();
scope.register_function("main".to_string(), 0x1000, 0x1100);
scope.update_scope_for_rip(0x1050);
match scope.current_tier {
ScopeTier::Local { ref function_name, .. } => {
assert_eq!(function_name, "main");
}
_ => panic!("Expected Local scope"),
}
scope.update_scope_for_rip(0x2000);
assert_eq!(scope.current_tier, ScopeTier::Global);
}
#[test]
fn test_pixel_to_axial_roundtrip() {
let engine = HexHeatmapEngine::new();
// Center pixel should map to (0, 0)
let coord = engine.pixel_to_axial(0.0, 0.0, 18.0, 0.0, 0.0);
assert_eq!(coord.q, 0);
assert_eq!(coord.r, 0);
}
}