// ============================================================================ // 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> // 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, pub total_disk_bytes: u64, pub total_mem_bytes: u64, pub total_net_bytes: u64, pub has_breakpoint: bool, pub markers: HashSet, } /// Central analysis engine. Thread-safe via Arc> wrapping. /// Owns the heatmap grid, dictionary scanner, and signature database. pub struct HexHeatmapEngine { pub cell_matrix: HashMap, pub max_intensity_found: u64, pub hunting_dictionary: Vec, 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, 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 { 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 { 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, 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, } 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 { 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, pub file_b_offset: u64, pub file_b_bytes: Vec, pub per_byte_status: Vec, 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 { 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 { 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, pub implicit_memory_writes: Vec, 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, 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> /// for thread-safe access to the core engine from multiple IPC clients. pub fn launch( core_context: Arc>, ) -> std::io::Result<()> { let transport = Self::auto_assemble_transport(); std::thread::spawn(move || { let mut struct_buffer = vec![0u8; std::mem::size_of::()]; 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>, ) { 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> 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>); 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::>(), "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>, ); } } // ============================================================================ // 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, pub memory_highlights: HashMap, 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, Vec) { 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, pub transition_count: u32, pub last_transition_kind: Option, } /// 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>, pub active_alerts: Vec, } impl ShellcodeSentinel { pub fn new() -> Self { Self { previous_snapshots: HashMap::new(), active_alerts: Vec::new(), } } /// Parses /proc//maps into tracked memory regions. fn parse_proc_maps(pid: u32) -> Vec { 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 { 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 ¤t_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, ¤t); 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, ¤t); 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); } }