Skip to content

Commit 6570157

Browse files
authored
Merge pull request #342 from NotRequiem/dev
Added looking-glass detection
2 parents 08cf38d + 44fbe27 commit 6570157

File tree

6 files changed

+227
-229
lines changed

6 files changed

+227
-229
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
The library is:
1515
- Very easy to use
1616
- Cross-platform (Windows + MacOS + Linux)
17-
- Features up to 115+ unique VM detection techniques [[list](https://github.com/kernelwernel/VMAware/blob/main/docs/documentation.md#flag-table)]
17+
- Features up to 115 unique VM detection techniques [[list](https://github.com/kernelwernel/VMAware/blob/main/docs/documentation.md#flag-table)]
1818
- Features the most cutting-edge techniques
1919
- Able to detect 65+ VM brands including VMware, VirtualBox, QEMU, Hyper-V, and much more [[list](https://github.com/kernelwernel/VMAware/blob/main/docs/documentation.md#brand-table)]
2020
- Able to beat VM hardeners
@@ -239,7 +239,7 @@ You can view the full docs [here](docs/documentation.md). All the details such a
239239

240240
> I would've made it strictly MIT so proprietary software can make use of the library, but some of the techniques employed are from GPL projects, and I have no choice but to use the same license for legal reasons.
241241
>
242-
> This gave me an idea to make an MIT version without all of the GPL code so it can also be used without forcing your code to be open source. It should be noted that the MIT version removes <b>7</b> techniques out of 116 (as of 2.0 version), and the lesser the number of techniques, the less accurate the overall result might be.
242+
> This gave me an idea to make an MIT version without all of the GPL code so it can also be used without forcing your code to be open source. It should be noted that the MIT version removes <b>7</b> techniques out of 115 (as of 2.0 version), and the lesser the number of techniques, the less accurate the overall result might be.
243243
244244
</details>
245245

docs/documentation.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -465,7 +465,6 @@ VMAware provides a convenient way to not only check for VMs, but also have the f
465465
| `VM::IOREG_GREP` | Check for VM-strings in ioreg commands for MacOS | MacOS | 100% | | | | |
466466
| `VM::MAC_SIP` | Check if System Integrity Protection is disabled (likely a VM if it is) | MacOS | 40% | | | | |
467467
| `VM::HKLM_REGISTRIES` | Check HKLM registries for specific VM strings | Windows | 25% | | | | |
468-
| `VM::QEMU_GA` | Check for "qemu-ga" process | Linux | 10% | | | | |
469468
| `VM::VPC_INVALID` | Check for official VPC method | Windows | 75% | | | 32-bit | |
470469
| `VM::SIDT` | Check for sidt instruction method | Windows | 25% | | | | |
471470
| `VM::SGDT` | Check for sgdt instruction method | Windows | 30% | | | 32-bit | |
@@ -514,7 +513,7 @@ VMAware provides a convenient way to not only check for VMs, but also have the f
514513
| `VM::ANYRUN_DIRECTORY` | Check for any.run directory and handle the status code | Windows | 35% | | | | | Removed from the lib, only available in the CLI |
515514
| `VM::DRIVER_NAMES` | Check for VM-specific names for drivers | Windows | 100% | | | | |
516515
| `VM::VM_SIDT` | Check for unknown IDT base address | Windows | 100% | | | | |
517-
| `VM::HDD_SERIAL` | Check for serial numbers of virtual disks | Windows | 100% | | | | |
516+
| `VM::DISK_SERIAL` | Check for serial numbers of virtual disks | Windows | 100% | | | | |
518517
| `VM::PORT_CONNECTORS` | Check for physical connection ports | Windows | 25% | | | | This technique is known to false flag on devices like Surface Pro |
519518
| `VM::GPU_CAPABILITIES` | Check for GPU capabilities related to VMs | Windows | 100% | Admin | | | Admin only needed for some heuristics |
520519
| `VM::GPU_VM_STRINGS` | Check for specific GPU string signatures related to VMs | Windows | 100% | | | | If GPU_CAPABILITIES also flags, the overall score will be 50 instead of 100 |

src/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
|------|---------|
33
| `cli.cpp` | Entire CLI tool code |
44
| `vmaware.hpp` | Official and original library header in GPL-3.0, most likely what you're looking for. |
5-
| `vmaware_MIT.hpp` | Same as above but in MIT. But this removes 7 techniques out of 116 |
5+
| `vmaware_MIT.hpp` | Same as above but in MIT. But this removes 7 techniques out of 115 |
66

77
<br>
88

src/cli.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -353,9 +353,9 @@ bool is_unsupported(VM::enum_flags flag) {
353353
case VM::SIDT5:
354354
case VM::DISK_SIZE:
355355
case VM::VBOX_DEFAULT:
356+
case VM::VM_PROCESSES:
356357
case VM::LINUX_USER_HOST:
357358
case VM::BOCHS_CPU:
358-
case VM::QEMU_GA:
359359
case VM::SIDT:
360360
case VM::VMWARE_IOMEM:
361361
case VM::VMWARE_IOPORTS:
@@ -451,7 +451,7 @@ bool is_unsupported(VM::enum_flags flag) {
451451
case VM::KGT_SIGNATURE:
452452
case VM::DRIVER_NAMES:
453453
case VM::VM_SIDT:
454-
case VM::HDD_SERIAL:
454+
case VM::DISK_SERIAL:
455455
case VM::PORT_CONNECTORS:
456456
case VM::GPU_VM_STRINGS:
457457
case VM::GPU_CAPABILITIES:
@@ -907,7 +907,6 @@ void general() {
907907
checker(VM::KVM_DIRS, "KVM directories");
908908
checker(VM::HKLM_REGISTRIES, "registry values");
909909
checker(VM::AUDIO, "audio device");
910-
checker(VM::QEMU_GA, "qemu-ga process");
911910
checker(VM::QEMU_DIR, "QEMU directories");
912911
checker(VM::VPC_INVALID, "VPC invalid instructions");
913912
checker(VM::SIDT, "SIDT");
@@ -957,7 +956,7 @@ void general() {
957956
checker(anyrun_directory, "ANY.RUN directory");
958957
checker(VM::DRIVER_NAMES, "driver names");
959958
checker(VM::VM_SIDT, "VM SIDT");
960-
checker(VM::HDD_SERIAL, "HDD serial number");
959+
checker(VM::DISK_SERIAL, "disk serial number");
961960
checker(VM::PORT_CONNECTORS, "physical connection ports");
962961
checker(VM::GPU_CAPABILITIES, "GPU capabilities");
963962
checker(VM::GPU_VM_STRINGS, "GPU strings");

src/vmaware.hpp

Lines changed: 59 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -589,7 +589,6 @@ struct VM {
589589
IOREG_GREP,
590590
MAC_SIP,
591591
HKLM_REGISTRIES,
592-
QEMU_GA,
593592
VPC_INVALID,
594593
SIDT,
595594
SGDT,
@@ -636,7 +635,7 @@ struct VM {
636635
WSL_PROC,
637636
DRIVER_NAMES,
638637
VM_SIDT,
639-
HDD_SERIAL,
638+
DISK_SERIAL,
640639
PORT_CONNECTORS,
641640
GPU_VM_STRINGS,
642641
GPU_CAPABILITIES,
@@ -1743,7 +1742,7 @@ struct VM {
17431742
#endif
17441743
}
17451744

1746-
// get available memory space
1745+
// Get available memory space
17471746
[[nodiscard]] static u64 get_memory_space() {
17481747
#if (WINDOWS)
17491748
MEMORYSTATUSEX statex = { 0 };
@@ -3655,9 +3654,7 @@ struct VM {
36553654
* @implements VM::VM_PROCESSES
36563655
*/
36573656
[[nodiscard]] static bool vm_processes() {
3658-
#if (!WINDOWS)
3659-
return false;
3660-
#else
3657+
#if (WINDOWS)
36613658
const auto runningProcesses = util::get_running_process_names();
36623659

36633660
if (runningProcesses.count("joeboxserver.exe") || runningProcesses.count("joeboxcontrol.exe")) {
@@ -3670,7 +3667,7 @@ struct VM {
36703667
return core::add(brands::PARALLELS);
36713668
}
36723669

3673-
if (runningProcesses.count("vboxservice.exe") || runningProcesses.count("vboxtray.exe")) {
3670+
if (runningProcesses.count("vboxservice.exe") || runningProcesses.count("vboxtray.exe") || runningProcesses.count("VBoxControl.exe")) {
36743671
debug("VM_PROCESSES: Detected VBox process.");
36753672
return core::add(brands::VBOX);
36763673
}
@@ -3694,13 +3691,23 @@ struct VM {
36943691

36953692
if (runningProcesses.count("vdagent.exe") ||
36963693
runningProcesses.count("vdservice.exe") ||
3697-
runningProcesses.count("qemuwmi.exe")) {
3694+
runningProcesses.count("qemuwmi.exe") ||
3695+
runningProcesses.count("looking-glass-host.exe")) {
36983696
debug("VM_PROCESSES: Detected QEMU process.");
36993697
return core::add(brands::QEMU);
37003698
}
37013699

3700+
if (runningProcesses.count("VDDSysTray.exe")) {
3701+
return true;
3702+
}
3703+
3704+
#elif (LINUX)
3705+
if (util::is_proc_running("qemu_ga")) {
3706+
debug("VM_PROCESSES: Detected QEMU guest agent process.");
3707+
return core::add(brands::QEMU);
3708+
}
3709+
#endif
37023710
return false;
3703-
#endif
37043711
}
37053712

37063713

@@ -4332,26 +4339,6 @@ struct VM {
43324339
}
43334340

43344341

4335-
/**
4336-
* @brief Check for "qemu-ga" process
4337-
* @category Linux
4338-
* @implements VM::QEMU_GA
4339-
*/
4340-
[[nodiscard]] static bool qemu_ga() {
4341-
#if (!LINUX)
4342-
return false;
4343-
#else
4344-
constexpr const char* process = "qemu-ga";
4345-
4346-
if (util::is_proc_running(process)) {
4347-
return core::add(brands::QEMU);
4348-
}
4349-
4350-
return false;
4351-
#endif
4352-
}
4353-
4354-
43554342
/**
43564343
* @brief Check for official VPC method
43574344
* @category Windows, x86
@@ -7398,9 +7385,9 @@ struct VM {
73987385
* @category Windows
73997386
* @author Requiem (https://github.com/NotRequiem)
74007387
* @note VMware can't be flagged without also flagging legitimate devices
7401-
* @implements VM::HDD_SERIAL
7388+
* @implements VM::DISK_SERIAL
74027389
*/
7403-
[[nodiscard]] static bool hdd_serial_number() {
7390+
[[nodiscard]] static bool disk_serial_number() {
74047391
#if (!WINDOWS)
74057392
return false;
74067393
#else
@@ -7849,10 +7836,12 @@ struct VM {
78497836
* @implements VM::TIMER
78507837
*/
78517838
#if defined(MSVC)
7852-
#pragma optimize("", off)
7853-
#elif defined(__GNUC__)
7854-
#pragma GCC push_options
7855-
#pragma GCC optimize("O0")
7839+
#pragma optimize("", off)
7840+
#elif defined(CLANG)
7841+
#pragma clang optimize off
7842+
#elif defined(GCC)
7843+
#pragma GCC push_options
7844+
#pragma GCC optimize("O0")
78567845
#endif
78577846
[[nodiscard]]
78587847
#if (LINUX)
@@ -7862,17 +7851,21 @@ struct VM {
78627851
#if (ARM || !x86)
78637852
return false;
78647853
#else
7865-
constexpr u8 classicIterations = 10; // Number of iterations for the classic RDTSC check
7866-
constexpr u16 classicThreshold = 20000u; // Cycle threshold per iteration for classic RDTSC check
7867-
constexpr u8 requiredClassicSpikes = classicIterations / 2; // At least 50% of iterations must spike
7854+
if (util::hyper_x() == HYPERV_ARTIFACT_VM) {
7855+
return false;
7856+
}
7857+
7858+
constexpr u8 rdtscIterations = 10; // Number of iterations for the classic RDTSC check
7859+
constexpr u16 rdtscThreshold = 6000; // Cycle threshold per iteration for classic RDTSC check
7860+
constexpr u8 requiredClassicSpikes = rdtscIterations / 2; // At least 50% of iterations must spike
78687861

78697862
constexpr u16 spammerIterations = 1000; // Iterations for the multi-CPU/spammer check
7870-
constexpr u16 spammerAvgThreshold = 20000u; // Average cycle threshold for the spammer check
7863+
constexpr u16 spammerAvgThreshold = 1500; // Average cycle threshold for the spammer check
78717864

78727865
#if (WINDOWS)
7873-
constexpr u16 qpcRatioThreshold = 3000; // QPC ratio threshold
7866+
constexpr u16 qpcRatioThreshold = 70; // QPC ratio threshold
78747867
constexpr u8 tscIterations = 10; // Number of iterations for the TSC synchronization check
7875-
constexpr u16 tscSyncDiffThreshold = 500; // TSC difference threshold
7868+
constexpr u16 tscSyncDiffThreshold = 5000; // TSC difference threshold
78767869
#endif
78777870

78787871
// to minimize context switching/scheduling
@@ -7903,7 +7896,7 @@ struct VM {
79037896
#else
79047897
sched_setscheduler(0, oldPolicy, &oldParam);
79057898
#endif
7906-
};
7899+
};
79077900

79087901
// --- 1. Classic Timing Check (rdtsc + cpuid + rdtsc) ---
79097902
#ifdef __VMAWARE_DEBUG__
@@ -7920,8 +7913,10 @@ struct VM {
79207913
#endif
79217914

79227915
#if (WINDOWS)
7916+
bool notaligned = false;
79237917
flushBuffer = (char*)_aligned_malloc(kBufferSize, kAlignment);
79247918
if (!flushBuffer) {
7919+
notaligned = true;
79257920
flushBuffer = new (std::nothrow) char[kBufferSize];
79267921
}
79277922
#elif (LINUX || APPLE)
@@ -7937,7 +7932,7 @@ struct VM {
79377932
constexpr size_t segmentsCount = 8; // basically 1/8 of the buffer per iteration
79387933
constexpr size_t segmentSize = kBufferSize / segmentsCount;
79397934
int spikeCount = 0;
7940-
for (int i = 0; i < classicIterations; i++) {
7935+
for (int i = 0; i < rdtscIterations; i++) {
79417936
u64 start = __rdtsc();
79427937
#if (WINDOWS)
79437938
int cpu_info[4];
@@ -7953,7 +7948,7 @@ struct VM {
79537948
#ifdef __VMAWARE_DEBUG__
79547949
totalCycles += cycles;
79557950
#endif
7956-
if (cycles >= classicThreshold) {
7951+
if (cycles >= rdtscThreshold) {
79577952
spikeCount++;
79587953
}
79597954
// Instead of flushing the entire buffer every iteration (which would decrease performance a lot),
@@ -7963,25 +7958,30 @@ struct VM {
79637958
size_t offsetEnd = offsetStart + segmentSize;
79647959

79657960
// this detection works better when inducing cache flushing without thread sleeps
7966-
for (size_t j = offsetStart; j < offsetEnd; j += 64) {
7967-
flushBuffer[j] = static_cast<char>(j);
7961+
if (flushBuffer) {
7962+
for (size_t j = offsetStart; j < offsetEnd; j += 64) {
7963+
flushBuffer[j] = static_cast<char>(j);
79687964
#if defined(x86) && (defined(GCC) || defined(CLANG) || defined(MSVC))
7969-
COMPILER_BARRIER();
7970-
// _mm_clflushopt not available on some systems
7971-
_mm_clflush(reinterpret_cast<const void*>(&flushBuffer[j]));
7965+
COMPILER_BARRIER();
7966+
// _mm_clflushopt not available on some systems
7967+
_mm_clflush(reinterpret_cast<const void*>(&flushBuffer[j]));
79727968
#endif
7969+
}
79737970
}
79747971
}
79757972
#if (WINDOWS)
7976-
_aligned_free((void*)flushBuffer);
7973+
if (notaligned)
7974+
delete[] flushBuffer;
7975+
else
7976+
_aligned_free((void*)flushBuffer);
79777977
#else
79787978
free((void*)flushBuffer);
79797979
#endif
79807980

79817981
#ifdef __VMAWARE_DEBUG__
7982-
const double averageCycles = static_cast<double>(totalCycles) / classicIterations;
7982+
const double averageCycles = static_cast<double>(totalCycles) / rdtscIterations;
79837983
debug("TIMER: RDTSC check - Average cycles: ", averageCycles,
7984-
" (Threshold per sample: ", classicThreshold,
7984+
" (Threshold per sample: ", rdtscThreshold,
79857985
") - Spike count: ", spikeCount);
79867986
#endif
79877987

@@ -8189,9 +8189,11 @@ struct VM {
81898189
#endif
81908190
}
81918191
#if defined(MSVC)
8192-
#pragma optimize("", on)
8193-
#elif defined(__GNUC__)
8194-
#pragma GCC pop_options
8192+
#pragma optimize("", on)
8193+
#elif defined(CLANG)
8194+
#pragma clang optimize on
8195+
#elif defined(GCC)
8196+
#pragma GCC pop_options
81958197
#endif
81968198

81978199

@@ -11290,7 +11292,6 @@ struct VM {
1129011292
case IOREG_GREP: return "IOREG_GREP";
1129111293
case MAC_SIP: return "MAC_SIP";
1129211294
case HKLM_REGISTRIES: return "HKLM_REGISTRIES";
11293-
case QEMU_GA: return "QEMU_GA";
1129411295
case VPC_INVALID: return "VPC_INVALID";
1129511296
case SIDT: return "SIDT";
1129611297
case SGDT: return "SGDT";
@@ -11337,7 +11338,7 @@ struct VM {
1133711338
case WSL_PROC: return "WSL_PROC";
1133811339
case DRIVER_NAMES: return "DRIVER_NAMES";
1133911340
case VM_SIDT: return "VM_SIDT";
11340-
case HDD_SERIAL: return "HDD_SERIAL";
11341+
case DISK_SERIAL: return "DISK_SERIAL";
1134111342
case PORT_CONNECTORS: return "PORT_CONNECTORS";
1134211343
case GPU_VM_STRINGS: return "GPU_STRINGS";
1134311344
case GPU_CAPABILITIES: return "GPU_CAPABILITIES";
@@ -11850,7 +11851,6 @@ std::pair<VM::enum_flags, VM::core::technique> VM::core::technique_list[] = {
1185011851
std::make_pair(VM::IOREG_GREP, VM::core::technique(100, VM::ioreg_grep)),
1185111852
std::make_pair(VM::MAC_SIP, VM::core::technique(40, VM::mac_sip)),
1185211853
std::make_pair(VM::HKLM_REGISTRIES, VM::core::technique(25, VM::hklm_registries)),
11853-
std::make_pair(VM::QEMU_GA, VM::core::technique(10, VM::qemu_ga)),
1185411854
std::make_pair(VM::VPC_INVALID, VM::core::technique(75, VM::vpc_invalid)),
1185511855
std::make_pair(VM::SIDT, VM::core::technique(25, VM::sidt)),
1185611856
std::make_pair(VM::SGDT, VM::core::technique(30, VM::sgdt)),
@@ -11898,7 +11898,7 @@ std::pair<VM::enum_flags, VM::core::technique> VM::core::technique_list[] = {
1189811898
std::make_pair(VM::WSL_PROC, VM::core::technique(30, VM::wsl_proc_subdir)),
1189911899
std::make_pair(VM::DRIVER_NAMES, VM::core::technique(100, VM::driver_names)),
1190011900
std::make_pair(VM::VM_SIDT, VM::core::technique(100, VM::vm_sidt)),
11901-
std::make_pair(VM::HDD_SERIAL, VM::core::technique(100, VM::hdd_serial_number)),
11901+
std::make_pair(VM::DISK_SERIAL, VM::core::technique(100, VM::disk_serial_number)),
1190211902
std::make_pair(VM::PORT_CONNECTORS, VM::core::technique(25, VM::port_connectors)),
1190311903
std::make_pair(VM::GPU_VM_STRINGS, VM::core::technique(100, VM::gpu_vm_strings)),
1190411904
std::make_pair(VM::GPU_CAPABILITIES, VM::core::technique(100, VM::gpu_capabilities)),

0 commit comments

Comments
 (0)