Skip to content

Commit b1d41ed

Browse files
author
Requiem
committed
added looking-glass detection
1 parent 3bdbaab commit b1d41ed

6 files changed

Lines changed: 123 additions & 125 deletions

File tree

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
@@ -7842,10 +7829,12 @@ struct VM {
78427829
* @implements VM::TIMER
78437830
*/
78447831
#if defined(MSVC)
7845-
#pragma optimize("", off)
7846-
#elif defined(__GNUC__)
7847-
#pragma GCC push_options
7848-
#pragma GCC optimize("O0")
7832+
#pragma optimize("", off)
7833+
#elif defined(CLANG)
7834+
#pragma clang optimize off
7835+
#elif defined(GCC)
7836+
#pragma GCC push_options
7837+
#pragma GCC optimize("O0")
78497838
#endif
78507839
[[nodiscard]]
78517840
#if (LINUX)
@@ -7855,17 +7844,21 @@ struct VM {
78557844
#if (ARM || !x86)
78567845
return false;
78577846
#else
7858-
constexpr u8 classicIterations = 10; // Number of iterations for the classic RDTSC check
7859-
constexpr u16 classicThreshold = 20000u; // Cycle threshold per iteration for classic RDTSC check
7860-
constexpr u8 requiredClassicSpikes = classicIterations / 2; // At least 50% of iterations must spike
7847+
if (util::hyper_x() == HYPERV_ARTIFACT_VM) {
7848+
return false;
7849+
}
7850+
7851+
constexpr u8 rdtscIterations = 10; // Number of iterations for the classic RDTSC check
7852+
constexpr u16 rdtscThreshold = 6000; // Cycle threshold per iteration for classic RDTSC check
7853+
constexpr u8 requiredClassicSpikes = rdtscIterations / 2; // At least 50% of iterations must spike
78617854

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

78657858
#if (WINDOWS)
7866-
constexpr u16 qpcRatioThreshold = 3000; // QPC ratio threshold
7859+
constexpr u16 qpcRatioThreshold = 70; // QPC ratio threshold
78677860
constexpr u8 tscIterations = 10; // Number of iterations for the TSC synchronization check
7868-
constexpr u16 tscSyncDiffThreshold = 500; // TSC difference threshold
7861+
constexpr u16 tscSyncDiffThreshold = 5000; // TSC difference threshold
78697862
#endif
78707863

78717864
// to minimize context switching/scheduling
@@ -7896,7 +7889,7 @@ struct VM {
78967889
#else
78977890
sched_setscheduler(0, oldPolicy, &oldParam);
78987891
#endif
7899-
};
7892+
};
79007893

79017894
// --- 1. Classic Timing Check (rdtsc + cpuid + rdtsc) ---
79027895
#ifdef __VMAWARE_DEBUG__
@@ -7913,8 +7906,10 @@ struct VM {
79137906
#endif
79147907

79157908
#if (WINDOWS)
7909+
bool notaligned = false;
79167910
flushBuffer = (char*)_aligned_malloc(kBufferSize, kAlignment);
79177911
if (!flushBuffer) {
7912+
notaligned = true;
79187913
flushBuffer = new (std::nothrow) char[kBufferSize];
79197914
}
79207915
#elif (LINUX || APPLE)
@@ -7930,7 +7925,7 @@ struct VM {
79307925
constexpr size_t segmentsCount = 8; // basically 1/8 of the buffer per iteration
79317926
constexpr size_t segmentSize = kBufferSize / segmentsCount;
79327927
int spikeCount = 0;
7933-
for (int i = 0; i < classicIterations; i++) {
7928+
for (int i = 0; i < rdtscIterations; i++) {
79347929
u64 start = __rdtsc();
79357930
#if (WINDOWS)
79367931
int cpu_info[4];
@@ -7946,7 +7941,7 @@ struct VM {
79467941
#ifdef __VMAWARE_DEBUG__
79477942
totalCycles += cycles;
79487943
#endif
7949-
if (cycles >= classicThreshold) {
7944+
if (cycles >= rdtscThreshold) {
79507945
spikeCount++;
79517946
}
79527947
// Instead of flushing the entire buffer every iteration (which would decrease performance a lot),
@@ -7956,25 +7951,30 @@ struct VM {
79567951
size_t offsetEnd = offsetStart + segmentSize;
79577952

79587953
// this detection works better when inducing cache flushing without thread sleeps
7959-
for (size_t j = offsetStart; j < offsetEnd; j += 64) {
7960-
flushBuffer[j] = static_cast<char>(j);
7954+
if (flushBuffer) {
7955+
for (size_t j = offsetStart; j < offsetEnd; j += 64) {
7956+
flushBuffer[j] = static_cast<char>(j);
79617957
#if defined(x86) && (defined(GCC) || defined(CLANG) || defined(MSVC))
7962-
COMPILER_BARRIER();
7963-
// _mm_clflushopt not available on some systems
7964-
_mm_clflush(reinterpret_cast<const void*>(&flushBuffer[j]));
7958+
COMPILER_BARRIER();
7959+
// _mm_clflushopt not available on some systems
7960+
_mm_clflush(reinterpret_cast<const void*>(&flushBuffer[j]));
79657961
#endif
7962+
}
79667963
}
79677964
}
79687965
#if (WINDOWS)
7969-
_aligned_free((void*)flushBuffer);
7966+
if (notaligned)
7967+
delete[] flushBuffer;
7968+
else
7969+
_aligned_free((void*)flushBuffer);
79707970
#else
79717971
free((void*)flushBuffer);
79727972
#endif
79737973

79747974
#ifdef __VMAWARE_DEBUG__
7975-
const double averageCycles = static_cast<double>(totalCycles) / classicIterations;
7975+
const double averageCycles = static_cast<double>(totalCycles) / rdtscIterations;
79767976
debug("TIMER: RDTSC check - Average cycles: ", averageCycles,
7977-
" (Threshold per sample: ", classicThreshold,
7977+
" (Threshold per sample: ", rdtscThreshold,
79787978
") - Spike count: ", spikeCount);
79797979
#endif
79807980

@@ -8182,9 +8182,11 @@ struct VM {
81828182
#endif
81838183
}
81848184
#if defined(MSVC)
8185-
#pragma optimize("", on)
8186-
#elif defined(__GNUC__)
8187-
#pragma GCC pop_options
8185+
#pragma optimize("", on)
8186+
#elif defined(CLANG)
8187+
#pragma clang optimize on
8188+
#elif defined(GCC)
8189+
#pragma GCC pop_options
81888190
#endif
81898191

81908192

@@ -11283,7 +11285,6 @@ struct VM {
1128311285
case IOREG_GREP: return "IOREG_GREP";
1128411286
case MAC_SIP: return "MAC_SIP";
1128511287
case HKLM_REGISTRIES: return "HKLM_REGISTRIES";
11286-
case QEMU_GA: return "QEMU_GA";
1128711288
case VPC_INVALID: return "VPC_INVALID";
1128811289
case SIDT: return "SIDT";
1128911290
case SGDT: return "SGDT";
@@ -11330,7 +11331,7 @@ struct VM {
1133011331
case WSL_PROC: return "WSL_PROC";
1133111332
case DRIVER_NAMES: return "DRIVER_NAMES";
1133211333
case VM_SIDT: return "VM_SIDT";
11333-
case HDD_SERIAL: return "HDD_SERIAL";
11334+
case DISK_SERIAL: return "DISK_SERIAL";
1133411335
case PORT_CONNECTORS: return "PORT_CONNECTORS";
1133511336
case GPU_VM_STRINGS: return "GPU_STRINGS";
1133611337
case GPU_CAPABILITIES: return "GPU_CAPABILITIES";
@@ -11843,7 +11844,6 @@ std::pair<VM::enum_flags, VM::core::technique> VM::core::technique_list[] = {
1184311844
std::make_pair(VM::IOREG_GREP, VM::core::technique(100, VM::ioreg_grep)),
1184411845
std::make_pair(VM::MAC_SIP, VM::core::technique(40, VM::mac_sip)),
1184511846
std::make_pair(VM::HKLM_REGISTRIES, VM::core::technique(25, VM::hklm_registries)),
11846-
std::make_pair(VM::QEMU_GA, VM::core::technique(10, VM::qemu_ga)),
1184711847
std::make_pair(VM::VPC_INVALID, VM::core::technique(75, VM::vpc_invalid)),
1184811848
std::make_pair(VM::SIDT, VM::core::technique(25, VM::sidt)),
1184911849
std::make_pair(VM::SGDT, VM::core::technique(30, VM::sgdt)),
@@ -11891,7 +11891,7 @@ std::pair<VM::enum_flags, VM::core::technique> VM::core::technique_list[] = {
1189111891
std::make_pair(VM::WSL_PROC, VM::core::technique(30, VM::wsl_proc_subdir)),
1189211892
std::make_pair(VM::DRIVER_NAMES, VM::core::technique(100, VM::driver_names)),
1189311893
std::make_pair(VM::VM_SIDT, VM::core::technique(100, VM::vm_sidt)),
11894-
std::make_pair(VM::HDD_SERIAL, VM::core::technique(100, VM::hdd_serial_number)),
11894+
std::make_pair(VM::DISK_SERIAL, VM::core::technique(100, VM::disk_serial_number)),
1189511895
std::make_pair(VM::PORT_CONNECTORS, VM::core::technique(25, VM::port_connectors)),
1189611896
std::make_pair(VM::GPU_VM_STRINGS, VM::core::technique(100, VM::gpu_vm_strings)),
1189711897
std::make_pair(VM::GPU_CAPABILITIES, VM::core::technique(100, VM::gpu_capabilities)),

0 commit comments

Comments
 (0)