1use crate::memo;
7use crate::techniques;
8use crate::types::{Flagset, Technique, VMBrand, HIGH_THRESHOLD_SCORE, THRESHOLD_SCORE};
9use crate::util;
10
11use std::sync::Mutex;
12
13#[derive(Clone, Default)]
16struct BrandEntry {
17 brand: VMBrand,
18 score: u32,
19}
20
21static BRAND_SCOREBOARD: Mutex<Vec<BrandEntry>> = Mutex::new(Vec::new());
22
23pub fn add_brand(brand: VMBrand) {
25 let mut board = BRAND_SCOREBOARD.lock().unwrap();
26 if !board.iter().any(|e| e.brand == brand) {
27 board.push(BrandEntry { brand, score: 0 });
28 }
29}
30
31pub fn add_brand_score(brand: VMBrand, score: u32) {
33 let mut board = BRAND_SCOREBOARD.lock().unwrap();
34 if let Some(e) = board.iter_mut().find(|e| e.brand == brand) {
35 e.score += score;
36 } else {
37 board.push(BrandEntry { brand, score });
38 }
39}
40
41pub fn reset() {
43 BRAND_SCOREBOARD.lock().unwrap().clear();
44 memo::reset_all();
45}
46
47struct TechEntry {
50 id: Technique,
51 points: u32,
52 func: fn() -> bool,
53}
54
55macro_rules! entry {
56 ($id:expr, $pts:expr, $fn:expr) => {
57 TechEntry { id: $id, points: $pts, func: $fn }
58 };
59}
60
61fn build_table() -> Vec<TechEntry> {
62 use techniques::cross::*;
63#[cfg(windows)]
64 use techniques::win::*;
65
66 #[cfg(target_os = "linux")]
67 use techniques::linux::*;
68
69 vec![
70 entry!(Technique::Vmid, 100, vmid),
72 entry!(Technique::CpuBrand, 95, cpu_brand),
73 entry!(Technique::HypervisorBit, 100, hypervisor_bit),
74 entry!(Technique::HypervisorStr, 100, hypervisor_str),
75 entry!(Technique::BochsCpu, 100, bochs_cpu),
76 entry!(Technique::Timer, 100, timer),
77 entry!(Technique::ThreadMismatch, 50, thread_mismatch),
78 entry!(Technique::CpuidSignature, 95, cpuid_signature),
79 entry!(Technique::KgtSignature, 80, kgt_signature),
80
81 #[cfg(windows)]
83 entry!(Technique::Dll, 45, dll),
84 #[cfg(windows)]
85 entry!(Technique::Wine, 85, wine),
86 #[cfg(windows)]
87 entry!(Technique::PowerCapabilities, 35, power_capabilities),
88 #[cfg(windows)]
89 entry!(Technique::Gamarue, 40, gamarue),
90 #[cfg(windows)]
91 entry!(Technique::VpcInvalid, 75, vpc_invalid),
92 #[cfg(windows)]
93 entry!(Technique::VmwareStr, 45, vmware_str),
94 #[cfg(windows)]
95 entry!(Technique::VmwareBackdoor, 100, vmware_backdoor),
96 #[cfg(windows)]
97 entry!(Technique::Mutex, 85, mutex),
98 #[cfg(windows)]
99 entry!(Technique::CuckooDir, 15, cuckoo_dir),
100 #[cfg(windows)]
101 entry!(Technique::CuckooPipe, 20, cuckoo_pipe),
102 #[cfg(windows)]
103 entry!(Technique::Display, 35, display),
104 #[cfg(windows)]
105 entry!(Technique::DeviceString, 35, device_string),
106 #[cfg(windows)]
107 entry!(Technique::Drivers, 65, drivers),
108 #[cfg(windows)]
109 entry!(Technique::DiskSerial, 60, disk_serial),
110 #[cfg(windows)]
111 entry!(Technique::VirtualRegistry, 65, virtual_registry),
112 #[cfg(windows)]
113 entry!(Technique::Audio, 35, audio),
114 #[cfg(windows)]
115 entry!(Technique::AcpiSignature, 80, acpi_signature),
116 #[cfg(windows)]
117 entry!(Technique::Trap, 100, trap),
118 #[cfg(windows)]
119 entry!(Technique::Ud, 100, ud),
120 #[cfg(windows)]
121 entry!(Technique::Blockstep, 100, blockstep),
122 #[cfg(windows)]
123 entry!(Technique::BootLogo, 10, boot_logo),
124 #[cfg(windows)]
125 entry!(Technique::KernelObjects, 50, kernel_objects),
126 #[cfg(windows)]
127 entry!(Technique::Nvram, 100, nvram),
128 #[cfg(windows)]
129 entry!(Technique::Edid, 55, edid),
130 #[cfg(windows)]
131 entry!(Technique::Clock, 65, clock),
132 #[cfg(windows)]
133 entry!(Technique::Handles, 25, handles),
134 #[cfg(windows)]
135 entry!(Technique::VirtualProcessors, 30, virtual_processors),
136 #[cfg(windows)]
137 entry!(Technique::HypervisorQuery, 65, hypervisor_query),
138 #[cfg(windows)]
139 entry!(Technique::Ivshmem, 65, ivshmem),
140 #[cfg(windows)]
141 entry!(Technique::GpuCapabilities, 35, gpu_capabilities),
142 #[cfg(windows)]
143 entry!(Technique::CpuHeuristic, 30, cpu_heuristic),
144 #[cfg(windows)]
145 entry!(Technique::DbvmHypercall, 100, dbvm_hypercall),
146 #[cfg(windows)]
147 entry!(Technique::Msr, 65, msr),
148 #[cfg(windows)]
149 entry!(Technique::KvmInterception, 65, kvm_interception),
150 #[cfg(windows)]
151 entry!(Technique::Breakpoint, 65, breakpoint),
152
153 #[cfg(any(windows, target_os = "linux"))]
155 entry!(Technique::SystemRegisters, 35, system_registers),
156 #[cfg(any(windows, target_os = "linux"))]
157 entry!(Technique::Firmware, 80, firmware),
158 #[cfg(any(windows, target_os = "linux"))]
159 entry!(Technique::Devices, 35, devices),
160 #[cfg(any(windows, target_os = "linux"))]
161 entry!(Technique::Azure, 25, azure),
162
163 #[cfg(target_os = "linux")]
165 entry!(Technique::SmbiosVmBit, 35, smbios_vm_bit),
166 #[cfg(target_os = "linux")]
167 entry!(Technique::Kmsg, 30, kmsg),
168 #[cfg(target_os = "linux")]
169 entry!(Technique::Cvendor, 65, cvendor),
170 #[cfg(target_os = "linux")]
171 entry!(Technique::QemuFwCfg, 40, qemu_fw_cfg),
172 #[cfg(target_os = "linux")]
173 entry!(Technique::Systemd, 30, systemd),
174 #[cfg(target_os = "linux")]
175 entry!(Technique::Ctype, 25, ctype),
176 #[cfg(target_os = "linux")]
177 entry!(Technique::Dockerenv, 95, dockerenv),
178 #[cfg(target_os = "linux")]
179 entry!(Technique::Dmidecode, 55, dmidecode),
180 #[cfg(target_os = "linux")]
181 entry!(Technique::Dmesg, 65, dmesg),
182 #[cfg(target_os = "linux")]
183 entry!(Technique::Hwmon, 25, hwmon),
184 #[cfg(target_os = "linux")]
185 entry!(Technique::LinuxUserHost, 35, linux_user_host),
186 #[cfg(target_os = "linux")]
187 entry!(Technique::VmwareIomem, 65, vmware_iomem),
188 #[cfg(target_os = "linux")]
189 entry!(Technique::VmwareIoports, 65, vmware_ioports),
190 #[cfg(target_os = "linux")]
191 entry!(Technique::VmwareScsi, 40, vmware_scsi),
192 #[cfg(target_os = "linux")]
193 entry!(Technique::VmwareDmesg, 50, vmware_dmesg),
194 #[cfg(target_os = "linux")]
195 entry!(Technique::QemuVirtualDmi, 40, qemu_virtual_dmi),
196 #[cfg(target_os = "linux")]
197 entry!(Technique::QemuUsb, 20, qemu_usb),
198 #[cfg(target_os = "linux")]
199 entry!(Technique::HypervisorDir, 40, hypervisor_dir),
200 #[cfg(target_os = "linux")]
201 entry!(Technique::UmlCpu, 80, uml_cpu),
202 #[cfg(target_os = "linux")]
203 entry!(Technique::VboxModule, 65, vbox_module),
204 #[cfg(target_os = "linux")]
205 entry!(Technique::SysinfoProc, 25, sysinfo_proc),
206 #[cfg(target_os = "linux")]
207 entry!(Technique::DmiScan, 55, dmi_scan),
208 #[cfg(target_os = "linux")]
209 entry!(Technique::PodmanFile, 95, podman_file),
210 #[cfg(target_os = "linux")]
211 entry!(Technique::WslProc, 95, wsl_proc),
212 #[cfg(target_os = "linux")]
213 entry!(Technique::FileAccessHistory, 15, file_access_history),
214 #[cfg(target_os = "linux")]
215 entry!(Technique::Mac, 30, mac),
216 #[cfg(target_os = "linux")]
217 entry!(Technique::NsjailPid, 95, nsjail_pid),
218 #[cfg(target_os = "linux")]
219 entry!(Technique::BluestacksFolders, 60, bluestacks_folders),
220 #[cfg(target_os = "linux")]
221 entry!(Technique::AmdSevMsr, 95, amd_sev_msr),
222 #[cfg(target_os = "linux")]
223 entry!(Technique::Temperature, 25, temperature),
224 #[cfg(target_os = "linux")]
225 entry!(Technique::Processes, 30, processes),
226 #[cfg(any(target_os = "linux", target_os = "macos"))]
228 entry!(Technique::ThreadCount, 35, techniques::cross::thread_count),
229 ]
230}
231
232pub fn run_all(flags: Flagset, shortcut: bool) -> u32 {
238 let table = build_table();
239 let mut score: u32 = 0;
240
241 for entry in &table {
242 if !flags.is_empty() && !flags.is_set(entry.id) {
243 continue;
244 }
245
246 if util::is_unsupported(entry.id) {
248 continue;
249 }
250
251 let tech_id = entry.id as u8;
252
253 if let Some(cached) = memo::cache_fetch(tech_id) {
255 if cached.result {
256 score += cached.points;
257 }
258 if shortcut && score >= THRESHOLD_SCORE {
259 return score;
260 }
261 continue;
262 }
263
264 let result = (entry.func)();
266 let pts = if result { entry.points } else { 0 };
267 score += pts;
268
269 let brand = best_brand();
271 memo::cache_store(tech_id, result, entry.points, brand);
272
273 if shortcut && score >= THRESHOLD_SCORE {
274 return score;
275 }
276 }
277
278 score
279}
280
281pub fn detect(flags: Flagset) -> bool {
283 run_all(flags, true) >= THRESHOLD_SCORE
284}
285
286pub fn get_percentage(flags: Flagset) -> u8 {
288 let score = run_all(flags, false);
289 let pct = (score * 100) / HIGH_THRESHOLD_SCORE;
290 pct.min(100) as u8
291}
292
293pub fn get_brand(flags: Flagset) -> VMBrand {
295 run_all(flags, false);
296 best_brand()
297}
298
299fn best_brand() -> VMBrand {
300 let board = BRAND_SCOREBOARD.lock().unwrap();
301 board
302 .iter()
303 .max_by_key(|e| e.score)
304 .map(|e| e.brand)
305 .unwrap_or(VMBrand::Invalid)
306}
307
308pub fn get_detected_brands(flags: Flagset) -> Vec<VMBrand> {
310 run_all(flags, false);
311 let board = BRAND_SCOREBOARD.lock().unwrap();
312 board
313 .iter()
314 .filter(|e| e.score > 0)
315 .map(|e| e.brand)
316 .collect()
317}
318
319pub fn detected_technique_count(flags: Flagset) -> usize {
321 run_all(flags, false);
322 let mut count = 0usize;
324 for id in 0..Technique::COUNT as u8 {
325 if let Some(r) = memo::cache_fetch(id) {
326 if r.result {
327 count += 1;
328 }
329 }
330 }
331 count
332}