feat(security): add Win32 VirtualLock memory protection, CFA error diagnostics, and ShellBag OpSec

This commit is contained in:
2026-09-10 12:27:57 +02:00
parent 90cf4927af
commit 99813ae2a3
6 changed files with 122 additions and 3 deletions
+11
View File
@@ -345,6 +345,13 @@ impl CarrierFsInner {
}
}
impl Drop for CarrierFsInner {
fn drop(&mut self) {
crate::windows::unlock_memory(self.dek_outer.as_ptr(), 32);
crate::windows::unlock_memory(self.dek_inner.as_ptr(), 32);
}
}
/// WebDAV-Filesystem-Treiber für den steganografischen Alibi-Carrier (Hidden Vault).
#[derive(Clone)]
pub struct CarrierFs {
@@ -380,6 +387,10 @@ impl CarrierFs {
.map(|d| d.as_secs())
.unwrap_or(0);
// Forensischer RAM-Paging-Schutz via VirtualLock
crate::windows::lock_memory(dek_outer.as_ptr(), 32);
crate::windows::lock_memory(dek_inner.as_ptr(), 32);
let inner = CarrierFsInner {
db,
carrier_node_id,
+10 -2
View File
@@ -239,9 +239,17 @@ pub async fn mount_container(
return Err(e);
}
// Optional automatisch im Windows Explorer öffnen
// Optional automatisch im Windows Explorer öffnen (mit DFIR ShellBag-Schutz für Hidden Vault)
if open_explorer {
let _ = crate::windows::open_in_explorer(drive_letter);
if vault_id == 1 {
println!(
" {} {}",
ui::yellow("[!]"),
ui::dim("OpSec-Schutz: Explorer-Auto-Open für Hidden Vault unterdrückt (verhindert persistente ShellBag-Spuren in UsrClass.dat).")
);
} else {
let _ = crate::windows::open_in_explorer(drive_letter);
}
}
// System-Tray Initialisierung
+19 -1
View File
@@ -158,7 +158,25 @@ fn current_timestamp() -> u64 {
impl Database {
/// Öffnet oder erstellt die Container-Datenbank und initialisiert die Pragmas.
pub fn open<P: AsRef<Path>>(path: P) -> Result<Self> {
let conn = Connection::open(path)?;
let path_ref = path.as_ref();
let conn = match Connection::open(path_ref) {
Ok(c) => c,
Err(e) => {
let err_str = e.to_string();
if err_str.contains("Access is denied")
|| err_str.contains("permission denied")
|| err_str.contains("os error 5")
{
bail!(
"Zugriff auf '{}' verweigert (OS Fehler 5 / Access Denied).\n\
[!] Möglicherweise blockiert durch Windows Defender 'Überwachter Ordnerzugriff' (Controlled Folder Access).\n\
[i] Abhilfe: Fügen Sie 'sanctum.exe' in den Windows-Sicherheitseinstellungen (Viren- & Bedrohungsschutz -> Ransomware-Schutz -> Überwachter Ordnerzugriff) als erlaubte App hinzu, oder platzieren Sie den Container außerhalb geschützter Benutzerordner.",
path_ref.display()
);
}
return Err(e.into());
}
};
let db = Self {
conn: Arc::new(Mutex::new(conn)),
};
+15
View File
@@ -469,6 +469,12 @@ impl SanctumFs {
None
};
// Forensischer RAM-Paging-Schutz via VirtualLock (verhindert Auslagerung in pagefile.sys)
crate::windows::lock_memory(dek_arc.as_ptr(), 32);
if let Some(ref c_dek) = carrier_dek_arc {
crate::windows::lock_memory(c_dek.as_ptr(), 32);
}
Self {
db,
dek: dek_arc,
@@ -550,6 +556,15 @@ impl SanctumFs {
}
}
impl Drop for SanctumFs {
fn drop(&mut self) {
crate::windows::unlock_memory(self.dek.as_ptr(), 32);
if let Some(ref c_dek) = self.carrier_dek {
crate::windows::unlock_memory(c_dek.as_ptr(), 32);
}
}
}
impl DavFileSystem for SanctumFs {
fn open<'a>(
&'a self,
+46
View File
@@ -527,10 +527,56 @@ pub fn start_console_ctrl_monitor(
}
}
/// Verriegelt einen Speicherbereich im physischen RAM (verhindert Paging in pagefile.sys / swapfile.sys).
pub fn lock_memory(ptr: *const u8, len: usize) -> bool {
#[cfg(windows)]
{
extern "system" {
fn VirtualLock(lpaddress: *const std::ffi::c_void, dwsize: usize) -> i32;
}
if ptr.is_null() || len == 0 {
return false;
}
unsafe { VirtualLock(ptr as *const std::ffi::c_void, len) != 0 }
}
#[cfg(not(windows))]
{
let _ = (ptr, len);
false
}
}
/// Entriegelt einen zuvor mit `lock_memory` geschützten Speicherbereich im RAM.
pub fn unlock_memory(ptr: *const u8, len: usize) -> bool {
#[cfg(windows)]
{
extern "system" {
fn VirtualUnlock(lpaddress: *const std::ffi::c_void, dwsize: usize) -> i32;
}
if ptr.is_null() || len == 0 {
return false;
}
unsafe { VirtualUnlock(ptr as *const std::ffi::c_void, len) != 0 }
}
#[cfg(not(windows))]
{
let _ = (ptr, len);
false
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_virtual_lock_memory_lifecycle() {
let buffer = vec![0x42u8; 4096];
let _ = lock_memory(buffer.as_ptr(), buffer.len());
let _ = unlock_memory(buffer.as_ptr(), buffer.len());
assert_eq!(buffer[0], 0x42);
}
#[test]
fn test_find_next_available_drive() {
let drive = find_next_available_drive().expect("Find next drive");