1use crate::engine::TechniqueResult;
4
5#[cfg(target_os = "linux")]
6use crate::brands;
7#[cfg(target_os = "linux")]
8use crate::util;
9
10pub fn chassis_vendor() -> TechniqueResult {
12 #[cfg(target_os = "linux")]
13 {
14 if let Some(vendor) = util::linux::read_dmi_field("chassis_vendor") {
15 let v = vendor.to_lowercase();
16 if v.contains("qemu") { return TechniqueResult::detected_with_brand(brands::QEMU); }
17 if v.contains("vmware") { return TechniqueResult::detected_with_brand(brands::VMWARE); }
18 if v.contains("virtualbox") || v.contains("oracle") || v.contains("innotek") {
19 return TechniqueResult::detected_with_brand(brands::VBOX);
20 }
21 if v.contains("microsoft") { return TechniqueResult::detected_with_brand(brands::HYPERV); }
22 if v.contains("xen") { return TechniqueResult::detected_with_brand(brands::XEN); }
23 if v.contains("parallels") { return TechniqueResult::detected_with_brand(brands::PARALLELS); }
24 if v.contains("bochs") { return TechniqueResult::detected_with_brand(brands::BOCHS); }
25 }
26 }
27 TechniqueResult::not_detected()
28}
29
30pub fn chassis_type() -> TechniqueResult {
32 #[cfg(target_os = "linux")]
33 {
34 if let Some(ctype) = util::linux::read_dmi_field("chassis_type") {
35 if let Ok(t) = ctype.parse::<u32>() {
36 if t == 1 {
38 return TechniqueResult::detected();
39 }
40 }
41 }
42 }
43 TechniqueResult::not_detected()
44}
45
46pub fn dockerenv() -> TechniqueResult {
48 #[cfg(target_os = "linux")]
49 {
50 if util::file_exists("/.dockerenv") || util::file_exists("/.dockerinit") {
51 return TechniqueResult::detected_with_brand(brands::DOCKER);
52 }
53 }
54 TechniqueResult::not_detected()
55}
56
57pub fn dmidecode() -> TechniqueResult {
59 #[cfg(target_os = "linux")]
60 {
61 if let Some(output) = util::run_command("dmidecode", &["-s", "system-manufacturer"]) {
62 let lower = output.to_lowercase();
63 if lower.contains("vmware") { return TechniqueResult::detected_with_brand(brands::VMWARE); }
64 if lower.contains("qemu") { return TechniqueResult::detected_with_brand(brands::QEMU); }
65 if lower.contains("virtualbox") || lower.contains("innotek") {
66 return TechniqueResult::detected_with_brand(brands::VBOX);
67 }
68 if lower.contains("microsoft") { return TechniqueResult::detected_with_brand(brands::HYPERV); }
69 if lower.contains("xen") { return TechniqueResult::detected_with_brand(brands::XEN); }
70 if lower.contains("parallels") { return TechniqueResult::detected_with_brand(brands::PARALLELS); }
71 if lower.contains("bochs") { return TechniqueResult::detected_with_brand(brands::BOCHS); }
72 }
73 }
74 TechniqueResult::not_detected()
75}
76
77pub fn dmesg() -> TechniqueResult {
79 #[cfg(target_os = "linux")]
80 {
81 if let Some(output) = util::run_command("dmesg", &[]) {
82 let lower = output.to_lowercase();
83 if lower.contains("vmware") { return TechniqueResult::detected_with_brand(brands::VMWARE); }
84 if lower.contains("virtualbox") || lower.contains("vbox") {
85 return TechniqueResult::detected_with_brand(brands::VBOX);
86 }
87 if lower.contains("qemu") { return TechniqueResult::detected_with_brand(brands::QEMU); }
88 if lower.contains("hyper-v") { return TechniqueResult::detected_with_brand(brands::HYPERV); }
89 if lower.contains("xen") { return TechniqueResult::detected_with_brand(brands::XEN); }
90 if lower.contains("kvm") { return TechniqueResult::detected_with_brand(brands::KVM); }
91 }
92 }
93 TechniqueResult::not_detected()
94}
95
96pub fn hwmon() -> TechniqueResult {
98 #[cfg(target_os = "linux")]
99 {
100 if !util::dir_exists("/sys/class/hwmon") {
101 return TechniqueResult::detected();
102 }
103 if let Ok(entries) = std::fs::read_dir("/sys/class/hwmon") {
105 if entries.count() == 0 {
106 return TechniqueResult::detected();
107 }
108 }
109 }
110 TechniqueResult::not_detected()
111}
112
113pub fn systemd_virt() -> TechniqueResult {
115 #[cfg(target_os = "linux")]
116 {
117 if let Some(output) = util::run_command("systemd-detect-virt", &[]) {
118 let trimmed = output.trim().to_lowercase();
119 if trimmed != "none" && !trimmed.is_empty() {
120 if trimmed.contains("vmware") { return TechniqueResult::detected_with_brand(brands::VMWARE); }
122 if trimmed.contains("oracle") || trimmed.contains("vbox") {
123 return TechniqueResult::detected_with_brand(brands::VBOX);
124 }
125 if trimmed.contains("kvm") { return TechniqueResult::detected_with_brand(brands::KVM); }
126 if trimmed.contains("qemu") { return TechniqueResult::detected_with_brand(brands::QEMU); }
127 if trimmed.contains("microsoft") || trimmed.contains("hyper") {
128 return TechniqueResult::detected_with_brand(brands::HYPERV);
129 }
130 if trimmed.contains("xen") { return TechniqueResult::detected_with_brand(brands::XEN); }
131 if trimmed.contains("docker") { return TechniqueResult::detected_with_brand(brands::DOCKER); }
132 if trimmed.contains("podman") { return TechniqueResult::detected_with_brand(brands::PODMAN); }
133 if trimmed.contains("wsl") { return TechniqueResult::detected_with_brand(brands::WSL); }
134 if trimmed.contains("openvz") { return TechniqueResult::detected_with_brand(brands::OPENVZ); }
135 return TechniqueResult::detected();
136 }
137 }
138 }
139 TechniqueResult::not_detected()
140}
141
142pub fn linux_user_host() -> TechniqueResult {
144 #[cfg(target_os = "linux")]
145 {
146 let vm_usernames = ["user", "admin", "test", "vm", "sandbox", "virus", "malware"];
147 let vm_hostnames = ["ubuntu", "debian", "centos", "fedora", "sandbox", "vm", "virtual"];
148
149 if let Some(user) = util::get_username() {
150 let lower = user.to_lowercase();
151 for &vu in &vm_usernames {
152 if lower == vu {
153 return TechniqueResult::detected();
154 }
155 }
156 }
157 if let Some(host) = util::get_hostname() {
158 let lower = host.to_lowercase();
159 for &vh in &vm_hostnames {
160 if lower == vh {
161 return TechniqueResult::detected();
162 }
163 }
164 }
165 }
166 TechniqueResult::not_detected()
167}
168
169pub fn vmware_iomem() -> TechniqueResult {
171 #[cfg(target_os = "linux")]
172 {
173 if let Some(content) = util::read_file("/proc/iomem") {
174 if util::find_ci(&content, "vmware") {
175 return TechniqueResult::detected_with_brand(brands::VMWARE);
176 }
177 }
178 }
179 TechniqueResult::not_detected()
180}
181
182pub fn vmware_ioports() -> TechniqueResult {
184 #[cfg(target_os = "linux")]
185 {
186 if let Some(content) = util::read_file("/proc/ioports") {
187 if util::find_ci(&content, "vmware") {
188 return TechniqueResult::detected_with_brand(brands::VMWARE);
189 }
190 }
191 }
192 TechniqueResult::not_detected()
193}
194
195pub fn vmware_scsi() -> TechniqueResult {
197 #[cfg(target_os = "linux")]
198 {
199 if let Some(content) = util::read_file("/proc/scsi/scsi") {
200 if util::find_ci(&content, "vmware") {
201 return TechniqueResult::detected_with_brand(brands::VMWARE);
202 }
203 }
204 }
205 TechniqueResult::not_detected()
206}
207
208pub fn vmware_dmesg() -> TechniqueResult {
210 #[cfg(target_os = "linux")]
211 {
212 if let Some(output) = util::run_command("dmesg", &[]) {
213 if output.contains("VMware") || output.contains("vmw_") {
214 return TechniqueResult::detected_with_brand(brands::VMWARE);
215 }
216 }
217 }
218 TechniqueResult::not_detected()
219}
220
221pub fn qemu_virtual_dmi() -> TechniqueResult {
223 #[cfg(target_os = "linux")]
224 {
225 let dmi_fields = ["sys_vendor", "product_name", "board_vendor", "board_name", "bios_vendor"];
226 for field in &dmi_fields {
227 if let Some(val) = util::linux::read_dmi_field(field) {
228 if util::find_ci(&val, "qemu") {
229 return TechniqueResult::detected_with_brand(brands::QEMU);
230 }
231 }
232 }
233 }
234 TechniqueResult::not_detected()
235}
236
237pub fn qemu_usb() -> TechniqueResult {
239 #[cfg(target_os = "linux")]
240 {
241 if let Some(content) = util::read_file("/sys/kernel/debug/usb/devices") {
242 if util::find_ci(&content, "qemu") {
243 return TechniqueResult::detected_with_brand(brands::QEMU);
244 }
245 }
246 }
247 TechniqueResult::not_detected()
248}
249
250pub fn hypervisor_dir() -> TechniqueResult {
252 #[cfg(target_os = "linux")]
253 {
254 if util::dir_exists("/sys/hypervisor") {
255 if let Ok(entries) = std::fs::read_dir("/sys/hypervisor") {
256 if entries.count() > 0 {
257 if util::file_exists("/sys/hypervisor/type") {
259 if let Some(hv_type) = util::read_file("/sys/hypervisor/type") {
260 if hv_type.trim() == "xen" {
261 return TechniqueResult::detected_with_brand(brands::XEN);
262 }
263 }
264 }
265 return TechniqueResult::detected();
266 }
267 }
268 }
269 }
270 TechniqueResult::not_detected()
271}
272
273pub fn uml_cpu() -> TechniqueResult {
275 #[cfg(target_os = "linux")]
276 {
277 if let Some(cpuinfo) = util::read_file("/proc/cpuinfo") {
278 for line in cpuinfo.lines() {
279 if line.starts_with("model name") && line.contains("UML") {
280 return TechniqueResult::detected_with_brand(brands::UML);
281 }
282 }
283 }
284 }
285 TechniqueResult::not_detected()
286}
287
288pub fn kmsg() -> TechniqueResult {
290 #[cfg(target_os = "linux")]
291 {
292 if let Some(content) = util::read_file("/dev/kmsg") {
293 let lower = content.to_lowercase();
294 if lower.contains("hypervisor") || lower.contains("vmware")
295 || lower.contains("virtualbox") || lower.contains("kvm") {
296 return TechniqueResult::detected();
297 }
298 }
299 }
300 TechniqueResult::not_detected()
301}
302
303pub fn vbox_module() -> TechniqueResult {
305 #[cfg(target_os = "linux")]
306 {
307 let modules = ["vboxguest", "vboxsf", "vboxvideo"];
308 for module in &modules {
309 if util::linux::is_module_loaded(module) {
310 return TechniqueResult::detected_with_brand(brands::VBOX);
311 }
312 }
313 }
314 TechniqueResult::not_detected()
315}
316
317pub fn sysinfo_proc() -> TechniqueResult {
319 #[cfg(target_os = "linux")]
320 {
321 if let Some(content) = util::read_file("/proc/sysinfo") {
322 let lower = content.to_lowercase();
323 if lower.contains("vm") || lower.contains("lpar") {
324 return TechniqueResult::detected_with_brand(brands::POWERVM);
325 }
326 }
327 }
328 TechniqueResult::not_detected()
329}
330
331pub fn dmi_scan() -> TechniqueResult {
333 #[cfg(target_os = "linux")]
334 {
335 let dmi_files = [
336 "sys_vendor", "product_name", "product_version",
337 "board_vendor", "board_name", "bios_vendor", "bios_version",
338 ];
339
340 let vm_strings: &[(&str, &str)] = &[
341 ("vmware", brands::VMWARE),
342 ("virtualbox", brands::VBOX),
343 ("innotek", brands::VBOX),
344 ("oracle", brands::VBOX),
345 ("qemu", brands::QEMU),
346 ("bochs", brands::BOCHS),
347 ("microsoft", brands::HYPERV),
348 ("hyper-v", brands::HYPERV),
349 ("xen", brands::XEN),
350 ("parallels", brands::PARALLELS),
351 ("kvm", brands::KVM),
352 ("bhyve", brands::BHYVE),
353 ("google", brands::GCE),
354 ("amazon", brands::AWS_NITRO),
355 ("openstack", brands::OPENSTACK),
356 ];
357
358 for field in &dmi_files {
359 if let Some(val) = util::linux::read_dmi_field(field) {
360 let lower = val.to_lowercase();
361 for &(pattern, brand) in vm_strings {
362 if lower.contains(pattern) {
363 return TechniqueResult::detected_with_brand(brand);
364 }
365 }
366 }
367 }
368 }
369 TechniqueResult::not_detected()
370}
371
372pub fn smbios_vm_bit() -> TechniqueResult {
374 #[cfg(target_os = "linux")]
375 {
376 if let Some(content) = util::read_file("/sys/firmware/dmi/tables/smbios_entry_point") {
378 if content.len() > 0 {
379 return TechniqueResult::not_detected();
381 }
382 }
383 }
384 TechniqueResult::not_detected()
385}
386
387pub fn podman_file() -> TechniqueResult {
389 #[cfg(target_os = "linux")]
390 {
391 if util::file_exists("/run/.containerenv") {
392 return TechniqueResult::detected_with_brand(brands::PODMAN);
393 }
394 }
395 TechniqueResult::not_detected()
396}
397
398pub fn wsl_proc() -> TechniqueResult {
400 #[cfg(target_os = "linux")]
401 {
402 if let Some(version) = util::read_file("/proc/version") {
403 let lower = version.to_lowercase();
404 if lower.contains("microsoft") || lower.contains("wsl") {
405 return TechniqueResult::detected_with_brand(brands::WSL);
406 }
407 }
408 if let Some(osrelease) = util::read_file("/proc/sys/kernel/osrelease") {
409 let lower = osrelease.to_lowercase();
410 if lower.contains("microsoft") || lower.contains("wsl") {
411 return TechniqueResult::detected_with_brand(brands::WSL);
412 }
413 }
414 }
415 TechniqueResult::not_detected()
416}
417
418pub fn qemu_fw_cfg() -> TechniqueResult {
420 #[cfg(target_os = "linux")]
421 {
422 let paths = [
423 "/sys/firmware/qemu_fw_cfg",
424 "/sys/module/qemu_fw_cfg",
425 ];
426 for path in &paths {
427 if util::dir_exists(path) {
428 return TechniqueResult::detected_with_brand(brands::QEMU);
429 }
430 }
431 if let Some(compatible) = util::read_file("/sys/firmware/devicetree/base/hypervisor/compatible") {
433 if util::find_ci(&compatible, "qemu") {
434 return TechniqueResult::detected_with_brand(brands::QEMU);
435 }
436 }
437 }
438 TechniqueResult::not_detected()
439}
440
441pub fn file_access_history() -> TechniqueResult {
443 #[cfg(target_os = "linux")]
444 {
445 let home = std::env::var("HOME").unwrap_or_default();
446 if home.is_empty() {
447 return TechniqueResult::not_detected();
448 }
449
450 let dirs_to_check = [
451 format!("{}/Desktop", home),
452 format!("{}/Documents", home),
453 format!("{}/Downloads", home),
454 ];
455
456 let mut total_files = 0usize;
457 for dir in &dirs_to_check {
458 if let Ok(entries) = std::fs::read_dir(dir) {
459 total_files += entries.count();
460 }
461 }
462
463 if total_files <= 3 {
465 return TechniqueResult::detected();
466 }
467 }
468 TechniqueResult::not_detected()
469}
470
471pub fn mac_address_check() -> TechniqueResult {
473 #[cfg(target_os = "linux")]
474 {
475 if let Some(mac) = util::linux::get_mac_address() {
476 let upper = mac.to_uppercase();
477 let vm_mac_prefixes: &[(&str, &str)] = &[
478 ("00:05:69", brands::VMWARE),
479 ("00:0C:29", brands::VMWARE),
480 ("00:1C:14", brands::VMWARE),
481 ("00:50:56", brands::VMWARE),
482 ("08:00:27", brands::VBOX),
483 ("0A:00:27", brands::VBOX),
484 ("00:03:FF", brands::HYPERV),
485 ("00:15:5D", brands::HYPERV),
486 ("00:1A:4A", brands::QEMU),
487 ("52:54:00", brands::QEMU),
488 ("00:16:3E", brands::XEN),
489 ("00:1C:42", brands::PARALLELS),
490 ];
491
492 for &(prefix, brand) in vm_mac_prefixes {
493 if upper.starts_with(prefix) {
494 return TechniqueResult::detected_with_brand(brand);
495 }
496 }
497 }
498 }
499 TechniqueResult::not_detected()
500}
501
502pub fn nsjail_pid() -> TechniqueResult {
504 #[cfg(target_os = "linux")]
505 {
506 if let Some(comm) = util::read_file("/proc/1/comm") {
508 let name = comm.trim();
509 if name != "init" && name != "systemd" && name != "launchd" {
510 if let Some(status) = util::read_file("/proc/1/status") {
512 if status.contains("nsjail") {
513 return TechniqueResult::detected_with_brand(brands::NSJAIL);
514 }
515 }
516 }
517 }
518 }
519 TechniqueResult::not_detected()
520}
521
522pub fn bluestacks_folders() -> TechniqueResult {
524 #[cfg(target_os = "linux")]
525 {
526 let paths = [
527 "/sdcard/windows/BstSharedFolder",
528 "/mnt/windows/BstSharedFolder",
529 ];
530 for path in &paths {
531 if util::dir_exists(path) {
532 return TechniqueResult::detected_with_brand(brands::BLUESTACKS);
533 }
534 }
535 }
536 TechniqueResult::not_detected()
537}
538
539pub fn amd_sev() -> TechniqueResult {
541 #[cfg(target_os = "linux")]
542 {
543 if let Some(val) = util::read_file("/sys/module/kvm_amd/parameters/sev") {
545 if val.trim() == "Y" || val.trim() == "1" {
546 return TechniqueResult::detected_with_brand(brands::AMD_SEV);
547 }
548 }
549 if let Some(dmesg_out) = util::run_command("dmesg", &[]) {
551 if dmesg_out.contains("AMD SEV") {
552 return TechniqueResult::detected_with_brand(brands::AMD_SEV);
553 }
554 }
555 }
556 TechniqueResult::not_detected()
557}
558
559pub fn temperature() -> TechniqueResult {
561 #[cfg(target_os = "linux")]
562 {
563 let temp_paths = [
565 "/sys/class/thermal/thermal_zone0/temp",
566 "/sys/class/hwmon/hwmon0/temp1_input",
567 ];
568
569 let mut has_sensor = false;
570 for path in &temp_paths {
571 if let Some(val) = util::read_file(path) {
572 if let Ok(temp) = val.trim().parse::<i64>() {
573 if temp > 0 {
574 has_sensor = true;
575 break;
576 }
577 }
578 }
579 }
580
581 if !has_sensor {
582 return TechniqueResult::detected();
583 }
584 }
585 TechniqueResult::not_detected()
586}
587
588pub fn processes() -> TechniqueResult {
590 #[cfg(target_os = "linux")]
591 {
592 let vm_processes: &[(&str, &str)] = &[
593 ("VBoxService", brands::VBOX),
594 ("VBoxClient", brands::VBOX),
595 ("vmtoolsd", brands::VMWARE),
596 ("vmwaretray", brands::VMWARE),
597 ("vmwareuser", brands::VMWARE),
598 ("vmware-vmblock-fuse", brands::VMWARE),
599 ("qemu-ga", brands::QEMU),
600 ("spice-vdagent", brands::QEMU),
601 ("xe-daemon", brands::XEN),
602 ("prl_cc", brands::PARALLELS),
603 ("prl_tools", brands::PARALLELS),
604 ];
605
606 let running = util::linux::list_processes();
607 for &(proc_name, brand) in vm_processes {
608 if running.iter().any(|p| p == proc_name) {
609 return TechniqueResult::detected_with_brand(brand);
610 }
611 }
612 }
613 TechniqueResult::not_detected()
614}