1#![cfg(target_os = "linux")]
4
5use crate::core::add_brand_score;
6use crate::types::VMBrand;
7use crate::util;
8
9pub fn smbios_vm_bit() -> bool {
13 if let Some(ct) = util::read_file("/sys/class/dmi/id/chassis_type") {
15 let ct = ct.trim().parse::<u32>().unwrap_or(0);
16 if ct == 1 || ct == 2 {
18 return true;
19 }
20 }
21 false
22}
23
24pub fn kmsg() -> bool {
28 static VM_STRINGS: &[(&str, VMBrand)] = &[
29 ("VMware VMCI", VMBrand::VMware),
30 ("VirtualBox", VMBrand::VBox),
31 ("QEMU", VMBrand::QEMU),
32 ("Booting paravirtualized kernel", VMBrand::KVM),
33 ("Hypervisor detected", VMBrand::Invalid),
34 ("kvm-clock", VMBrand::KVM),
35 ("hv_vmbus", VMBrand::HyperV),
36 ];
37
38 let paths = ["/proc/kmsg", "/var/log/kern.log", "/var/log/syslog"];
39 for path in &paths {
40 if let Some(content) = util::read_file(path) {
41 for &(kw, brand) in VM_STRINGS {
42 if content.contains(kw) {
43 if brand != VMBrand::Invalid {
44 add_brand_score(brand, 0);
45 }
46 return true;
47 }
48 }
49 }
50 }
51 false
52}
53
54pub fn cvendor() -> bool {
58 static PATHS: &[(&str, &[(&str, VMBrand)])] = &[
59 ("/sys/class/dmi/id/sys_vendor", &[
60 ("QEMU", VMBrand::QEMU),
61 ("VMware", VMBrand::VMware),
62 ("VirtualBox", VMBrand::VBox),
63 ("innotek GmbH", VMBrand::VBox),
64 ("Xen", VMBrand::Xen),
65 ("Microsoft", VMBrand::HyperV),
66 ("KVM", VMBrand::KVM),
67 ("Parallels", VMBrand::Parallels),
68 ("OpenStack", VMBrand::OpenStack),
69 ("Google", VMBrand::GCE),
70 ("Amazon", VMBrand::AWSNitro),
71 ("bhyve", VMBrand::Bhyve),
72 ]),
73 ];
74
75 for &(path, table) in PATHS {
76 if let Some(content) = util::read_file(path) {
77 let lower = content.to_lowercase();
78 for &(kw, brand) in table {
79 if lower.contains(&kw.to_lowercase()) {
80 add_brand_score(brand, 0);
81 return true;
82 }
83 }
84 }
85 }
86 false
87}
88
89pub fn qemu_fw_cfg() -> bool {
93 util::exists("/sys/firmware/qemu_fw_cfg") || util::exists("/dev/mem")
94 && util::file_contains("/sys/firmware/qemu_fw_cfg/by_name/opt/qemu_fw_cfg_version/raw", "")
95}
96
97pub fn systemd() -> bool {
101 if let Some(v) = util::read_file("/run/systemd/detect-virt") {
102 let v = v.trim();
103 return v != "none" && !v.is_empty();
104 }
105 if let Ok(out) = std::process::Command::new("systemd-detect-virt").output() {
107 let s = String::from_utf8_lossy(&out.stdout).to_lowercase();
108 return s.trim() != "none" && !s.trim().is_empty() && out.status.success();
109 }
110 false
111}
112
113pub fn ctype() -> bool {
117 if let Some(cpuinfo) = util::read_file("/proc/cpuinfo") {
118 return cpuinfo.contains("hypervisor");
119 }
120 false
121}
122
123pub fn dockerenv() -> bool {
127 if util::exists("/.dockerenv") {
128 add_brand_score(VMBrand::Docker, 0);
129 return true;
130 }
131 if util::exists("/.dockerinit") {
132 add_brand_score(VMBrand::Docker, 0);
133 return true;
134 }
135 false
136}
137
138pub fn dmidecode() -> bool {
142 if let Ok(out) = std::process::Command::new("dmidecode").arg("-t").arg("system").output() {
143 let s = String::from_utf8_lossy(&out.stdout).to_lowercase();
144 static VM_KW: &[(&str, VMBrand)] = &[
145 ("virtualbox", VMBrand::VBox),
146 ("vmware", VMBrand::VMware),
147 ("qemu", VMBrand::QEMU),
148 ("xen", VMBrand::Xen),
149 ("kvm", VMBrand::KVM),
150 ("bochs", VMBrand::Bochs),
151 ("microsoft", VMBrand::HyperV),
152 ("parallels", VMBrand::Parallels),
153 ];
154 for &(kw, brand) in VM_KW {
155 if s.contains(kw) {
156 add_brand_score(brand, 0);
157 return true;
158 }
159 }
160 }
161 false
162}
163
164pub fn dmesg() -> bool {
168 if let Ok(out) = std::process::Command::new("dmesg").output() {
169 let s = String::from_utf8_lossy(&out.stdout).to_lowercase();
170 static KW: &[(&str, VMBrand)] = &[
171 ("hypervisor", VMBrand::Invalid),
172 ("vmware", VMBrand::VMware),
173 ("virtualbox", VMBrand::VBox),
174 ("kvm", VMBrand::KVM),
175 ("xen", VMBrand::Xen),
176 ("virt", VMBrand::Invalid),
177 ("paravirt", VMBrand::KVM),
178 ("hv_vmbus", VMBrand::HyperV),
179 ];
180 for &(kw, brand) in KW {
181 if s.contains(kw) {
182 if brand != VMBrand::Invalid {
183 add_brand_score(brand, 0);
184 }
185 return true;
186 }
187 }
188 }
189 false
190}
191
192pub fn hwmon() -> bool {
196 if let Ok(rd) = std::fs::read_dir("/sys/class/hwmon") {
197 return rd.count() == 0;
198 }
199 true }
201
202pub fn linux_user_host() -> bool {
206 static VM_HOSTNAMES: &[&str] = &[
207 "sandbox", "cuckoo", "malware", "analysis", "lab",
208 "honeypot", "box", "win7vm", "vm-",
209 ];
210
211 let hostname = util::read_file("/etc/hostname").unwrap_or_default();
212 let lower = hostname.to_lowercase();
213 for kw in VM_HOSTNAMES {
214 if lower.contains(kw) {
215 return true;
216 }
217 }
218 false
219}
220
221pub fn vmware_iomem() -> bool {
224 util::file_contains("/proc/iomem", "VMware")
225}
226
227pub fn vmware_ioports() -> bool {
230 util::file_contains("/proc/ioports", "VMware")
231}
232
233pub fn vmware_scsi() -> bool {
236 util::file_contains("/proc/scsi/scsi", "VMware")
237}
238
239pub fn vmware_dmesg() -> bool {
242 if let Ok(out) = std::process::Command::new("dmesg").output() {
243 let s = String::from_utf8_lossy(&out.stdout);
244 return s.contains("VMware") || s.contains("VMWARE");
245 }
246 false
247}
248
249pub fn qemu_virtual_dmi() -> bool {
252 util::file_contains("/sys/firmware/dmi/tables/DMI", "QEMU")
253 || util::file_contains("/sys/class/dmi/id/bios_vendor", "QEMU")
254}
255
256pub fn qemu_usb() -> bool {
259 if let Ok(rd) = std::fs::read_dir("/sys/bus/usb/devices") {
260 for entry in rd.flatten() {
261 let idv = entry.path().join("idVendor");
262 if let Ok(v) = std::fs::read_to_string(&idv) {
263 if v.trim() == "1234" {
264 add_brand_score(VMBrand::QEMU, 0);
266 return true;
267 }
268 }
269 }
270 }
271 false
272}
273
274pub fn hypervisor_dir() -> bool {
277 static DIRS: &[(&str, VMBrand)] = &[
278 ("/proc/vz", VMBrand::OpenVZ),
279 ("/proc/bc", VMBrand::OpenVZ),
280 ("/proc/xen", VMBrand::Xen),
281 ("/proc/xenolinux-banner", VMBrand::Xen),
282 ];
283 for &(path, brand) in DIRS {
284 if util::exists(path) {
285 add_brand_score(brand, 0);
286 return true;
287 }
288 }
289 false
290}
291
292pub fn uml_cpu() -> bool {
295 if let Some(cpuinfo) = util::read_file("/proc/cpuinfo") {
296 if cpuinfo.contains("User Mode Linux") || cpuinfo.contains("UML") {
297 add_brand_score(VMBrand::UML, 0);
298 return true;
299 }
300 }
301 false
302}
303
304pub fn vbox_module() -> bool {
307 if let Some(mods) = util::read_file("/proc/modules") {
308 let vm_mods = ["vboxdrv", "vboxguest", "vboxpci", "vboxnetflt", "vboxnetadp"];
309 for m in &vm_mods {
310 if mods.contains(m) {
311 add_brand_score(VMBrand::VBox, 0);
312 return true;
313 }
314 }
315 }
316 false
317}
318
319pub fn sysinfo_proc() -> bool {
322 util::file_contains("/proc/sysinfo", "VM00") || util::file_contains("/proc/sysinfo", "z/VM")
323}
324
325pub fn dmi_scan() -> bool {
328 cvendor() }
330
331pub fn podman_file() -> bool {
334 if util::exists("/run/.containerenv") {
335 add_brand_score(VMBrand::Podman, 0);
336 return true;
337 }
338 false
339}
340
341pub fn wsl_proc() -> bool {
344 if util::file_contains("/proc/version", "Microsoft")
345 || util::file_contains("/proc/version", "WSL")
346 {
347 add_brand_score(VMBrand::WSL, 0);
348 return true;
349 }
350 if util::exists("/proc/sys/fs/binfmt_misc/WSLInterop") {
351 add_brand_score(VMBrand::WSL, 0);
352 return true;
353 }
354 false
355}
356
357pub fn file_access_history() -> bool {
360 util::exists("/root/.bash_history") && {
362 util::read_file("/root/.bash_history")
363 .map(|h| h.lines().count() == 0)
364 .unwrap_or(false)
365 }
366}
367
368pub fn mac() -> bool {
371 if let Ok(rd) = std::fs::read_dir("/sys/class/net") {
373 for entry in rd.flatten() {
374 let mac_path = entry.path().join("address");
375 if let Ok(addr) = std::fs::read_to_string(&mac_path) {
376 let addr = addr.trim().to_lowercase();
377 static VM_MACS: &[(&str, VMBrand)] = &[
383 ("00:0c:29", VMBrand::VMware),
384 ("00:50:56", VMBrand::VMware),
385 ("00:05:69", VMBrand::VMware),
386 ("08:00:27", VMBrand::VBox),
387 ("52:54:00", VMBrand::QEMUKVM),
388 ("00:1c:42", VMBrand::Parallels),
389 ("00:15:5d", VMBrand::HyperV),
390 ];
391 for &(prefix, brand) in VM_MACS {
392 if addr.starts_with(prefix) {
393 add_brand_score(brand, 0);
394 return true;
395 }
396 }
397 }
398 }
399 }
400 false
401}
402
403pub fn nsjail_pid() -> bool {
406 if let Some(comm) = util::read_file("/proc/1/comm") {
408 let c = comm.trim();
409 if c != "systemd" && c != "init" && c != "upstart" && c != "svscan" {
410 if util::file_contains("/proc/1/environ", "NSJAIL") {
412 add_brand_score(VMBrand::NSJail, 0);
413 return true;
414 }
415 }
416 }
417 false
418}
419
420pub fn bluestacks_folders() -> bool {
423 static PATHS: &[&str] = &[
424 "/sdcard",
425 "/data/app",
426 "/system/priv-app",
427 ];
428 let count = PATHS.iter().filter(|p| util::exists(p)).count();
430 if count >= 2 {
431 add_brand_score(VMBrand::BlueStacks, 0);
432 return true;
433 }
434 false
435}
436
437pub fn amd_sev_msr() -> bool {
440 use crate::cpu::{cpuid, is_amd, is_leaf_supported};
441 if !is_amd() {
442 return false;
443 }
444
445 let leaf1 = cpuid(1, 0);
459 let hypervisor_present = (leaf1.ecx >> 31) & 1 != 0;
460 if !hypervisor_present {
461 return false;
462 }
463
464 if is_leaf_supported(0x8000_001F) {
465 let r = cpuid(0x8000_001F, 0);
466 let sev_snp = (r.eax >> 4) & 1 != 0;
467 let sev_es = (r.eax >> 3) & 1 != 0;
468 let sev = r.eax & 1 != 0;
469 if sev_snp {
470 add_brand_score(VMBrand::AMDSEVsnp, 0);
471 return true;
472 } else if sev_es {
473 add_brand_score(VMBrand::AMDSEVes, 0);
474 return true;
475 } else if sev {
476 add_brand_score(VMBrand::AMDSEV, 0);
477 return true;
478 }
479 }
480 false
481}
482
483pub fn temperature() -> bool {
487 if let Ok(rd) = std::fs::read_dir("/sys/class/thermal") {
488 return rd.count() == 0;
489 }
490 true
491}
492
493pub fn processes() -> bool {
497 static VM_PROCS: &[(&str, VMBrand)] = &[
498 ("vmtoolsd", VMBrand::VMware),
499 ("vmwaretray", VMBrand::VMware),
500 ("vmwareuser", VMBrand::VMware),
501 ("VBoxService", VMBrand::VBox),
502 ("VBoxClient", VMBrand::VBox),
503 ("prl_tools", VMBrand::Parallels),
504 ("qemu-ga", VMBrand::QEMU),
505 ("cuckoo", VMBrand::Cuckoo),
506 ];
507
508 if let Ok(rd) = std::fs::read_dir("/proc") {
509 for entry in rd.flatten() {
510 let comm = entry.path().join("comm");
511 if let Ok(name) = std::fs::read_to_string(&comm) {
512 let name = name.trim().to_lowercase();
513 for &(proc_name, brand) in VM_PROCS {
514 if name.contains(&proc_name.to_lowercase()) {
515 add_brand_score(brand, 0);
516 return true;
517 }
518 }
519 }
520 }
521 }
522 false
523}
524
525pub fn thread_count() -> bool {
528 crate::techniques::cross::thread_count()
529}