feat: Universal Linux build + redesigned ostp-guard
- Build static musl binaries (work on any Linux distro) - Redesign ostp-guard with weighted scoring system (threshold: 4 points) - HIGH (2pts): Analysis tools (gdb/ida/ghidra), sandbox artifacts - MEDIUM (1pt): Low resources (<1GB RAM), suspicious env vars - Production VPS safe (1-2 points), sandbox blocked (4+ points) - Anti-debug: Windows (IsDebuggerPresent), Linux (/proc/self/status) - Deployment packages for Linux + Windows with SHA256 checksums
This commit is contained in:
@@ -8,6 +8,7 @@ description = "OSTP Anti-Reverse Engineering & Protection Module"
|
||||
rand.workspace = true
|
||||
tokio.workspace = true
|
||||
tracing.workspace = true
|
||||
num_cpus = "1.16"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi = { version = "0.3", features = ["debugapi", "processthreadsapi", "winnt", "sysinfoapi", "libloaderapi"] }
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Anti-Debugging Detection Module
|
||||
//! Anti-Debugging Detection Module - Redesigned
|
||||
//!
|
||||
//! Detects if the process is being traced/debugged
|
||||
//! and takes evasive action without obvious crashes.
|
||||
//! Detects if the process is being actively debugged
|
||||
//! Focuses on active debugging, not passive analysis
|
||||
|
||||
/// Check if any debugger is attached
|
||||
#[inline(never)]
|
||||
@@ -22,16 +22,16 @@ pub fn is_debugger_present() -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
/// Multiple Windows anti-debug techniques
|
||||
/// Windows anti-debug - only active debugging
|
||||
#[cfg(windows)]
|
||||
fn windows_debugger_check() -> bool {
|
||||
unsafe {
|
||||
// Method 1: IsDebuggerPresent API
|
||||
// IsDebuggerPresent - reliable for active debugging
|
||||
if winapi::um::debugapi::IsDebuggerPresent() != 0 {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Method 2: CheckRemoteDebuggerPresent
|
||||
// CheckRemoteDebuggerPresent - detects remote debuggers
|
||||
let mut is_debugged: i32 = 0;
|
||||
let process = winapi::um::processthreadsapi::GetCurrentProcess();
|
||||
if winapi::um::debugapi::CheckRemoteDebuggerPresent(process, &mut is_debugged) != 0 {
|
||||
@@ -39,123 +39,16 @@ fn windows_debugger_check() -> bool {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Method 3: NtGlobalFlag check (PEB)
|
||||
// The NtGlobalFlag in PEB is set to 0x70 when debugged
|
||||
if check_peb_being_debugged() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Method 4: Timing check - debugger breakpoints cause delays
|
||||
if timing_check() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
unsafe fn check_peb_being_debugged() -> bool {
|
||||
// Access PEB through TEB
|
||||
// This is a low-level check that many debuggers don't hide
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
{
|
||||
let peb: *const u8;
|
||||
unsafe {
|
||||
std::arch::asm!(
|
||||
"mov {}, gs:[0x60]",
|
||||
out(reg) peb,
|
||||
options(nostack, nomem)
|
||||
);
|
||||
}
|
||||
|
||||
if !peb.is_null() {
|
||||
// BeingDebugged flag at offset 0x2
|
||||
let being_debugged = unsafe { *peb.add(0x2) };
|
||||
if being_debugged != 0 {
|
||||
return true;
|
||||
}
|
||||
|
||||
// NtGlobalFlag at offset 0xBC (x64)
|
||||
let nt_global_flag = unsafe { *(peb.add(0xBC) as *const u32) };
|
||||
// FLG_HEAP_ENABLE_TAIL_CHECK | FLG_HEAP_ENABLE_FREE_CHECK | FLG_HEAP_VALIDATE_PARAMETERS
|
||||
if nt_global_flag & 0x70 != 0 {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn timing_check() -> bool {
|
||||
use std::time::Instant;
|
||||
|
||||
// Simple operation that should be instant
|
||||
let start = Instant::now();
|
||||
|
||||
// Do some trivial work
|
||||
let mut x: u64 = 0;
|
||||
for i in 0..1000 {
|
||||
x = x.wrapping_add(i);
|
||||
}
|
||||
|
||||
// Prevent optimization
|
||||
std::hint::black_box(x);
|
||||
|
||||
let elapsed = start.elapsed();
|
||||
|
||||
// If this takes more than 100ms, likely being single-stepped
|
||||
elapsed.as_millis() > 100
|
||||
}
|
||||
|
||||
/// Linux/Unix anti-debug techniques
|
||||
/// Linux/Unix anti-debug - only /proc/self/status check
|
||||
#[cfg(unix)]
|
||||
fn unix_debugger_check() -> bool {
|
||||
// Method 1: ptrace self-attach trick
|
||||
if ptrace_check() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Method 2: Check /proc/self/status for TracerPid
|
||||
if proc_status_check() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Method 3: Check parent process name
|
||||
if parent_process_check() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Method 4: Timing check
|
||||
if timing_check_unix() {
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn ptrace_check() -> bool {
|
||||
// PTRACE_TRACEME = 0
|
||||
// If we're already being traced, this will fail
|
||||
unsafe {
|
||||
let result = libc::ptrace(libc::PTRACE_TRACEME, 0, 0, 0);
|
||||
if result == -1 {
|
||||
// Already being traced
|
||||
return true;
|
||||
}
|
||||
// Detach from ourselves
|
||||
libc::ptrace(libc::PTRACE_DETACH, 0, 0, 0);
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn proc_status_check() -> bool {
|
||||
// Read /proc/self/status and check TracerPid
|
||||
// Check /proc/self/status for TracerPid
|
||||
// Non-invasive, works everywhere
|
||||
if let Ok(status) = std::fs::read_to_string("/proc/self/status") {
|
||||
for line in status.lines() {
|
||||
if line.starts_with("TracerPid:") {
|
||||
@@ -172,57 +65,16 @@ fn proc_status_check() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn parent_process_check() -> bool {
|
||||
// Check if parent is a known debugger
|
||||
let debuggers = ["gdb", "lldb", "strace", "ltrace", "radare2", "r2", "ida", "x64dbg", "ollydbg"];
|
||||
|
||||
if let Ok(ppid_str) = std::fs::read_to_string("/proc/self/stat") {
|
||||
let parts: Vec<&str> = ppid_str.split_whitespace().collect();
|
||||
if parts.len() > 3 {
|
||||
if let Ok(ppid) = parts[3].parse::<i32>() {
|
||||
let parent_exe = format!("/proc/{}/exe", ppid);
|
||||
if let Ok(path) = std::fs::read_link(&parent_exe) {
|
||||
let name = path.file_name()
|
||||
.and_then(|n| n.to_str())
|
||||
.unwrap_or("");
|
||||
|
||||
for debugger in &debuggers {
|
||||
if name.contains(debugger) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn timing_check_unix() -> bool {
|
||||
use std::time::Instant;
|
||||
|
||||
let start = Instant::now();
|
||||
let mut x: u64 = 0;
|
||||
for i in 0..1000 {
|
||||
x = x.wrapping_add(i);
|
||||
}
|
||||
std::hint::black_box(x);
|
||||
|
||||
start.elapsed().as_millis() > 100
|
||||
}
|
||||
|
||||
/// Continuous background monitor (call from separate thread)
|
||||
pub fn start_background_monitor() -> std::thread::JoinHandle<()> {
|
||||
/// Background monitoring thread
|
||||
pub fn start_background_monitor() {
|
||||
std::thread::spawn(|| {
|
||||
loop {
|
||||
std::thread::sleep(std::time::Duration::from_secs(5));
|
||||
|
||||
if is_debugger_present() {
|
||||
// Enter decoy mode silently
|
||||
crate::decoy_loop();
|
||||
// Silent exit
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,161 +1,30 @@
|
||||
//! Anti-VM and Sandbox Detection
|
||||
//! Anti-VM and Sandbox Detection - Redesigned
|
||||
//!
|
||||
//! Detects common virtualization artifacts to prevent
|
||||
//! analysis in controlled environments.
|
||||
//! Smart heuristic approach to detect analysis environments
|
||||
//! without blocking production VPS servers
|
||||
|
||||
/// Check if running in a virtual machine or sandbox
|
||||
/// Check if running in a sandbox/analysis environment
|
||||
/// Uses weighted scoring to avoid false positives on production VPS
|
||||
#[inline(never)]
|
||||
pub fn is_virtual_machine() -> bool {
|
||||
// Check multiple indicators - any single check might be bypassed
|
||||
let checks = [
|
||||
check_vm_mac_addresses,
|
||||
check_vm_hardware_ids,
|
||||
check_vm_processes,
|
||||
check_vm_files,
|
||||
check_vm_registry,
|
||||
check_low_resources,
|
||||
];
|
||||
let mut score = 0;
|
||||
|
||||
// If more than 2 checks trigger, likely a VM
|
||||
let score: u32 = checks.iter()
|
||||
.map(|check| if check() { 1 } else { 0 })
|
||||
.sum();
|
||||
// HIGH risk indicators (2 points each)
|
||||
if check_analysis_tools() { score += 2; }
|
||||
if check_sandbox_artifacts() { score += 2; }
|
||||
|
||||
score >= 2
|
||||
// MEDIUM risk (1 point each)
|
||||
if check_low_resources() { score += 1; }
|
||||
if check_suspicious_environment() { score += 1; }
|
||||
|
||||
// Threshold: 4+ points = very likely sandbox
|
||||
// Production VPS: max 1-2 points
|
||||
// Analysis sandbox: 4+ points (2 HIGH or 1 HIGH + 2 MEDIUM)
|
||||
score >= 4
|
||||
}
|
||||
|
||||
/// Check for known VM MAC address prefixes
|
||||
fn check_vm_mac_addresses() -> bool {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
// Get network adapter info and check MAC prefixes
|
||||
// VMware: 00:0C:29, 00:50:56
|
||||
// VirtualBox: 08:00:27
|
||||
// Hyper-V: 00:15:5D
|
||||
// Parallels: 00:1C:42
|
||||
|
||||
// Simplified check via ipconfig output patterns
|
||||
if let Ok(output) = std::process::Command::new("ipconfig")
|
||||
.arg("/all")
|
||||
.output()
|
||||
{
|
||||
let stdout = String::from_utf8_lossy(&output.stdout).to_lowercase();
|
||||
let vm_macs = ["00-0c-29", "00-50-56", "08-00-27", "00-15-5d", "00-1c-42"];
|
||||
for mac in &vm_macs {
|
||||
if stdout.contains(mac) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
if let Ok(output) = std::process::Command::new("ip")
|
||||
.args(["link", "show"])
|
||||
.output()
|
||||
{
|
||||
let stdout = String::from_utf8_lossy(&output.stdout).to_lowercase();
|
||||
let vm_macs = ["00:0c:29", "00:50:56", "08:00:27", "00:15:5d", "00:1c:42"];
|
||||
for mac in &vm_macs {
|
||||
if stdout.contains(mac) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// Check for VM-specific hardware IDs
|
||||
fn check_vm_hardware_ids() -> bool {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
// Check WMI for VM indicators
|
||||
let vm_indicators = [
|
||||
"vmware", "virtualbox", "vbox", "qemu", "xen",
|
||||
"virtual", "hyperv", "parallels", "kvm"
|
||||
];
|
||||
|
||||
// Check computer name/model via WMI
|
||||
if let Ok(output) = std::process::Command::new("wmic")
|
||||
.args(["computersystem", "get", "model"])
|
||||
.output()
|
||||
{
|
||||
let stdout = String::from_utf8_lossy(&output.stdout).to_lowercase();
|
||||
for indicator in &vm_indicators {
|
||||
if stdout.contains(indicator) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check BIOS
|
||||
if let Ok(output) = std::process::Command::new("wmic")
|
||||
.args(["bios", "get", "serialnumber"])
|
||||
.output()
|
||||
{
|
||||
let stdout = String::from_utf8_lossy(&output.stdout).to_lowercase();
|
||||
for indicator in &vm_indicators {
|
||||
if stdout.contains(indicator) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
// Check /sys/class/dmi/id/
|
||||
let dmi_paths = [
|
||||
"/sys/class/dmi/id/product_name",
|
||||
"/sys/class/dmi/id/sys_vendor",
|
||||
"/sys/class/dmi/id/board_vendor",
|
||||
];
|
||||
|
||||
let vm_indicators = [
|
||||
"vmware", "virtualbox", "vbox", "qemu", "xen",
|
||||
"virtual", "hyperv", "parallels", "kvm", "bochs"
|
||||
];
|
||||
|
||||
for path in &dmi_paths {
|
||||
if let Ok(content) = std::fs::read_to_string(path) {
|
||||
let lower = content.to_lowercase();
|
||||
for indicator in &vm_indicators {
|
||||
if lower.contains(indicator) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// Check for VM-related processes
|
||||
fn check_vm_processes() -> bool {
|
||||
let vm_processes = [
|
||||
"vmtoolsd", "vmwaretray", "vmwareuser", // VMware
|
||||
"vboxservice", "vboxtray", "vboxclient", // VirtualBox
|
||||
"xenservice", // Xen
|
||||
"qemu-ga", // QEMU
|
||||
"prl_tools", "prl_cc", // Parallels
|
||||
];
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
if let Ok(output) = std::process::Command::new("tasklist").output() {
|
||||
let stdout = String::from_utf8_lossy(&output.stdout).to_lowercase();
|
||||
for proc in &vm_processes {
|
||||
if stdout.contains(proc) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// HIGH: Check for reverse engineering tools in memory
|
||||
fn check_analysis_tools() -> bool {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
if let Ok(output) = std::process::Command::new("ps")
|
||||
@@ -163,8 +32,35 @@ fn check_vm_processes() -> bool {
|
||||
.output()
|
||||
{
|
||||
let stdout = String::from_utf8_lossy(&output.stdout).to_lowercase();
|
||||
for proc in &vm_processes {
|
||||
if stdout.contains(proc) {
|
||||
let tools = [
|
||||
"gdb", "lldb", "strace", "ltrace", "radare2", "r2",
|
||||
"ida", "ida64", "x64dbg", "ghidra", "binaryninja",
|
||||
"frida-server", "rizin", "cutter", "hopper",
|
||||
"qemu-user" // QEMU user-mode for cross-arch analysis
|
||||
];
|
||||
|
||||
for tool in &tools {
|
||||
if stdout.contains(tool) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
if let Ok(output) = std::process::Command::new("tasklist")
|
||||
.output()
|
||||
{
|
||||
let stdout = String::from_utf8_lossy(&output.stdout).to_lowercase();
|
||||
let tools = [
|
||||
"x64dbg", "x32dbg", "ollydbg", "windbg", "ida", "ida64",
|
||||
"ghidra", "binaryninja", "pestudio", "processhacker",
|
||||
"procmon", "procexp", "wireshark", "fiddler"
|
||||
];
|
||||
|
||||
for tool in &tools {
|
||||
if stdout.contains(tool) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -174,37 +70,14 @@ fn check_vm_processes() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Check for VM-specific files
|
||||
fn check_vm_files() -> bool {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
let vm_files = [
|
||||
r"C:\Windows\System32\drivers\vmmouse.sys",
|
||||
r"C:\Windows\System32\drivers\vmhgfs.sys",
|
||||
r"C:\Windows\System32\drivers\VBoxMouse.sys",
|
||||
r"C:\Windows\System32\drivers\VBoxGuest.sys",
|
||||
r"C:\Windows\System32\drivers\VBoxSF.sys",
|
||||
];
|
||||
|
||||
for file in &vm_files {
|
||||
if std::path::Path::new(file).exists() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// HIGH: Sandbox-specific artifacts
|
||||
fn check_sandbox_artifacts() -> bool {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
let vm_files = [
|
||||
"/usr/bin/vmtoolsd",
|
||||
"/usr/bin/VBoxService",
|
||||
"/usr/bin/VBoxClient",
|
||||
"/.dockerenv",
|
||||
"/run/.containerenv",
|
||||
];
|
||||
|
||||
for file in &vm_files {
|
||||
if std::path::Path::new(file).exists() {
|
||||
// Wine (used for Windows malware analysis on Linux)
|
||||
let wine_paths = ["/tmp/.wine-", "/tmp/.X11-unix"];
|
||||
for path in &wine_paths {
|
||||
if std::fs::read_dir(path).is_ok() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -213,24 +86,20 @@ fn check_vm_files() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Check Windows registry for VM indicators
|
||||
fn check_vm_registry() -> bool {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
// Check via reg query
|
||||
let registry_keys = [
|
||||
r"HKLM\SOFTWARE\VMware, Inc.\VMware Tools",
|
||||
r"HKLM\SOFTWARE\Oracle\VirtualBox Guest Additions",
|
||||
];
|
||||
|
||||
for key in ®istry_keys {
|
||||
if let Ok(output) = std::process::Command::new("reg")
|
||||
.args(["query", key])
|
||||
.output()
|
||||
{
|
||||
if output.status.success() {
|
||||
return true;
|
||||
}
|
||||
/// MEDIUM: Suspicious environment variables
|
||||
fn check_suspicious_environment() -> bool {
|
||||
let suspicious = [
|
||||
"WINE_",
|
||||
"QEMU_",
|
||||
"SANDBOX_",
|
||||
"AFL_", // American Fuzzy Lop fuzzer
|
||||
"ASAN_", // AddressSanitizer
|
||||
];
|
||||
|
||||
for (key, _) in std::env::vars() {
|
||||
for pattern in &suspicious {
|
||||
if key.starts_with(pattern) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -238,75 +107,44 @@ fn check_vm_registry() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Check for suspiciously low resources (sandbox indicator)
|
||||
/// MEDIUM: Very low resources (< 1GB RAM or 1 CPU)
|
||||
fn check_low_resources() -> bool {
|
||||
// Sandboxes often have minimal resources
|
||||
|
||||
// Check CPU count
|
||||
let cpus = std::thread::available_parallelism()
|
||||
.map(|n| n.get())
|
||||
.unwrap_or(1);
|
||||
|
||||
if cpus < 2 {
|
||||
// Single CPU = very suspicious
|
||||
let cpu_count = num_cpus::get();
|
||||
if cpu_count < 2 {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check available disk space (simplified)
|
||||
#[cfg(windows)]
|
||||
// Check available memory
|
||||
#[cfg(unix)]
|
||||
{
|
||||
if let Ok(output) = std::process::Command::new("wmic")
|
||||
.args(["logicaldisk", "get", "size"])
|
||||
.output()
|
||||
{
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
// Very small disk = sandbox
|
||||
if let Some(size_str) = stdout.lines().nth(1) {
|
||||
if let Ok(size) = size_str.trim().parse::<u64>() {
|
||||
// Less than 50GB
|
||||
if size < 50_000_000_000 {
|
||||
return true;
|
||||
if let Ok(meminfo) = std::fs::read_to_string("/proc/meminfo") {
|
||||
for line in meminfo.lines() {
|
||||
if line.starts_with("MemTotal:") {
|
||||
if let Some(kb_str) = line.split_whitespace().nth(1) {
|
||||
if let Ok(kb) = kb_str.parse::<u64>() {
|
||||
let mb = kb / 1024;
|
||||
// Less than 1GB = sandbox
|
||||
return mb < 1024;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// Check for analysis tools
|
||||
pub fn check_analysis_tools() -> bool {
|
||||
let tools = [
|
||||
"wireshark", "fiddler", "burp", "charles", // Network
|
||||
"x64dbg", "x32dbg", "ollydbg", "windbg", // Debuggers
|
||||
"ida", "ida64", "ghidra", "radare2", "r2", // Disassemblers
|
||||
"procmon", "procexp", "processhacker", // Process monitors
|
||||
"pestudio", "die", "exeinfope", // PE analyzers
|
||||
];
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
if let Ok(output) = std::process::Command::new("tasklist").output() {
|
||||
let stdout = String::from_utf8_lossy(&output.stdout).to_lowercase();
|
||||
for tool in &tools {
|
||||
if stdout.contains(tool) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
if let Ok(output) = std::process::Command::new("ps")
|
||||
.args(["aux"])
|
||||
.output()
|
||||
{
|
||||
let stdout = String::from_utf8_lossy(&output.stdout).to_lowercase();
|
||||
for tool in &tools {
|
||||
if stdout.contains(tool) {
|
||||
return true;
|
||||
}
|
||||
use std::mem::MaybeUninit;
|
||||
unsafe {
|
||||
let mut mem_status = MaybeUninit::<winapi::um::sysinfoapi::MEMORYSTATUSEX>::uninit();
|
||||
let mem_status_ptr = mem_status.as_mut_ptr();
|
||||
(*mem_status_ptr).dwLength = std::mem::size_of::<winapi::um::sysinfoapi::MEMORYSTATUSEX>() as u32;
|
||||
|
||||
if winapi::um::sysinfoapi::GlobalMemoryStatusEx(mem_status_ptr) != 0 {
|
||||
let mem_status = mem_status.assume_init();
|
||||
let total_mb = mem_status.ullTotalPhys / (1024 * 1024);
|
||||
return total_mb < 1024;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user