Skip to content

Commit 2c32c71

Browse files
authored
Merge pull request #576 from kernelwernel/dev
Dev
2 parents a5ea749 + 587a0f1 commit 2c32c71

File tree

7 files changed

+247
-8
lines changed

7 files changed

+247
-8
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
<div align="center">
1313
<b>VMAware</b> (VM + Aware) is a cross-platform C++ library for virtual machine detection.
1414
<br>
15+
<br>
1516
<a href="README_CN.md">中文 🇨🇳</a> | <a href="README_FR.md">Français 🇫🇷</a> | <a href="README_KR.md">한국어 🇰🇷</a>
1617
</div>
1718
</p>
@@ -301,6 +302,7 @@ And if you found this project useful, a star would be appreciated :)
301302
- [Kyun-J](https://github.com/Kyun-J)
302303
- [luukjp](https://github.com/luukjp)
303304
- [Randark](https://github.com/Randark-JMT)
305+
- [Scrut1ny](https://github.com/Scrut1ny)
304306

305307
<br>
306308

README_CN.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
<div align="center">
1313
<b>VMAware</b> (VM + Aware) 是一个跨平台的C++虚拟机检测库。
1414
<br>
15+
<br>
1516
<a href="README.md">English 🇬🇧</a> | <a href="README_FR.md">Français 🇫🇷</a> | <a href="README_KR.md">한국어 🇰🇷</a>
1617
</div>
1718
</p>
@@ -295,6 +296,7 @@ endif()
295296
- [Kyun-J](https://github.com/Kyun-J)
296297
- [luukjp](https://github.com/luukjp)
297298
- [Randark](https://github.com/Randark-JMT)
299+
- [Scrut1ny](https://github.com/Scrut1ny)
298300

299301
<br>
300302

README_FR.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
<div align="center">
1313
<b>VMAware</b> (VM + Aware) est une bibliothèque C++ multiplateforme pour la détection des machines virtuelles.
1414
<br>
15+
<br>
1516
<a href="README_CN.md">中文 🇨🇳</a> | <a href="README_FR.md">Français 🇫🇷</a> | <a href="README_KR.md">한국어 🇰🇷</a>
1617
</div>
1718
</p>
@@ -290,6 +291,7 @@ Et si ce projet vous a été utile, un star serait très apprécié :)
290291
- [Kyun-J](https://github.com/Kyun-J)
291292
- [luukjp](https://github.com/luukjp)
292293
- [Randark](https://github.com/Randark-JMT)
294+
- [Scrut1ny](https://github.com/Scrut1ny)
293295

294296
<br>
295297

README_KR.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
<div align="center">
1313
<b>VMAware</b> (VM + Aware)는 가상 머신 탐지를 위한 크로스 플랫폼 C++ 라이브러리 입니다.
1414
<br>
15+
<br>
1516
<a href="README.md">English 🇬🇧</a> | <a href="README_CN.md">中文 🇨🇳</a> | <a href="README_FR.md">Français 🇫🇷</a>
1617
</div>
1718
</p>

TODO.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,4 @@
2222
- add a python version of the library (or any other lang for that matter)
2323
- add a GUI version of the lib
2424
- add a rust version of the lib
25-
- submit the project to oss-fuzz
25+
- submit the project to oss-fuzz

src/cli.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -823,13 +823,13 @@ static void general() {
823823
checker(VM::DBVM, "Dark Byte's hypervisor");
824824
checker(VM::BOOT_LOGO, "boot logo");
825825
checker(VM::MAC_SYS, "system profiler");
826-
checker(VM::OBJECTS, "Windows object entities");
826+
checker(VM::OBJECTS, "kernel objects");
827827
checker(VM::NVRAM, "NVRAM");
828828
checker(VM::SMBIOS_INTEGRITY, "SMBIOS integrity");
829829
checker(VM::EDID, "EDID");
830830
checker(VM::CPU_HEURISTIC, "CPU heuristics");
831831
checker(VM::CLOCK, "system timers");
832-
checker(VM::LBR, "correct LBR MSRs");
832+
checker(VM::LBR, "LBR virtualization");
833833
// ADD NEW TECHNIQUE CHECKER HERE
834834

835835
const auto t2 = std::chrono::high_resolution_clock::now();

src/vmaware.hpp

Lines changed: 237 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4387,11 +4387,11 @@ struct VM {
43874387
return t2 - t1;
43884388
};
43894389

4390-
constexpr u8 N = 100;
4390+
constexpr u16 N = 1000;
43914391

43924392
auto sample_avg = [&]() -> u64 {
43934393
u64 sum = 0;
4394-
for (u8 i = 0; i < N; ++i) {
4394+
for (u16 i = 0; i < N; ++i) {
43954395
sum += cpuid();
43964396
}
43974397
return (sum + N / 2) / N;
@@ -4405,7 +4405,11 @@ struct VM {
44054405
else if (avg >= cycleThreshold) {
44064406
avg = sample_avg();
44074407
debug("TIMER: 2nd pass average -> ", avg, " cycles");
4408-
if (avg >= cycleThreshold) return true; // some CPUs like Intel's Emerald Rapids have much more cycles when executing CPUID than average, we should accept a high threshold
4408+
if (avg >= cycleThreshold) {
4409+
avg = sample_avg();
4410+
debug("TIMER: 3rd pass average -> ", avg, " cycles");
4411+
if (avg >= cycleThreshold) return true; // some CPUs like Intel's Emerald Rapids have much more cycles when executing CPUID than average, we should accept a high threshold
4412+
}
44094413
}
44104414

44114415
#if (WINDOWS)
@@ -4532,7 +4536,7 @@ struct VM {
45324536
);
45334537
}
45344538

4535-
if (difference >= 30) {
4539+
if (difference >= 100) {
45364540
debug("TIMER: An hypervisor has been detected intercepting RDTSC");
45374541
return true; // both ratios will always differ if a RDTSC trap is present, since the hypervisor can't account for the XOR/NOP loop
45384542
}
@@ -9835,6 +9839,232 @@ struct VM {
98359839
pNtFreeVirtualMemory(hCurrentProcess, &freeBase, &freeSize, MEM_RELEASE);
98369840
amd_target_mem = nullptr;
98379841
}
9842+
9843+
if (spoofed) return spoofed;
9844+
9845+
// ok so if the CPU is intel, the motherboard should be intel aswell (and same with AMD)
9846+
// this doesnt happen in most public hardened configs out there so lets abuse it
9847+
constexpr unsigned int VID_INTEL = 0x8086;
9848+
constexpr unsigned int VID_AMD_ATI = 0x1002;
9849+
constexpr unsigned int VID_AMD_MICRO = 0x1022;
9850+
9851+
enum class MBVendor { Unknown = 0, Intel = 1, AMD = 2 };
9852+
9853+
auto detect_motherboard = []() -> MBVendor {
9854+
static constexpr const char* TOKENS[] = {
9855+
"host bridge", "northbridge", "southbridge", "pci bridge", "chipset", "pch", "fch",
9856+
"platform controller", "lpc", "sata controller", "ahci", "ide controller", "usb controller",
9857+
"xhci", "usb3", "usb 3.0", "usb 3", "pcie root", "pci express", " sata", nullptr
9858+
};
9859+
9860+
static bool meta_ready = false;
9861+
static const char* token_ptrs[32];
9862+
static int token_lens[32];
9863+
static int token_count = 0;
9864+
static unsigned char first_unique[128];
9865+
static int first_unique_count = 0;
9866+
9867+
auto build_meta = [&]() {
9868+
if (meta_ready) return;
9869+
int i = 0;
9870+
for (; TOKENS[i]; ++i) {}
9871+
token_count = i;
9872+
assert(token_count < 32);
9873+
bool seen[128] = {};
9874+
for (int t = 0; t < token_count; ++t) {
9875+
token_ptrs[t] = TOKENS[t];
9876+
token_lens[t] = static_cast<int>(std::strlen(TOKENS[t]));
9877+
unsigned char fc = static_cast<unsigned char>(token_ptrs[t][0]);
9878+
if (fc < 128 && !seen[fc]) {
9879+
first_unique[first_unique_count++] = fc;
9880+
seen[fc] = true;
9881+
}
9882+
}
9883+
meta_ready = true;
9884+
};
9885+
9886+
auto ascii_lower = [](unsigned char c) -> unsigned char {
9887+
if (c >= 'A' && c <= 'Z') return static_cast<unsigned char>(c + ('a' - 'A'));
9888+
return c;
9889+
};
9890+
9891+
const size_t STACK_CAP = 4096;
9892+
auto wide_to_ascii = [&](const wchar_t* wptr, char* stackBuf, size_t stackCap, std::vector<char>* heapBuf, size_t& outLen) -> const char* {
9893+
outLen = 0;
9894+
if (!wptr || *wptr == L'\0') return nullptr;
9895+
const wchar_t* p = wptr;
9896+
char* out = stackBuf;
9897+
size_t cap = stackCap;
9898+
while (*p) {
9899+
wchar_t wc = *p++;
9900+
unsigned char c;
9901+
if (wc <= 127) {
9902+
c = ascii_lower(static_cast<unsigned char>(wc));
9903+
}
9904+
else {
9905+
c = 0;
9906+
}
9907+
if (outLen >= cap) {
9908+
if (!heapBuf) return nullptr;
9909+
heapBuf->assign(stackBuf, stackBuf + cap);
9910+
out = heapBuf->data();
9911+
cap = heapBuf->capacity();
9912+
}
9913+
out[outLen++] = static_cast<char>(c);
9914+
}
9915+
return out;
9916+
};
9917+
9918+
using u32 = unsigned int;
9919+
auto __memchr = [&](const char* data, size_t len) -> u32 {
9920+
if (!data || len == 0) return 0;
9921+
build_meta();
9922+
u32 mask = 0;
9923+
for (int fi = 0; fi < first_unique_count; ++fi) {
9924+
unsigned char fc = first_unique[fi];
9925+
const void* cur = data;
9926+
size_t remaining = len;
9927+
while (remaining > 0) {
9928+
const void* found = memchr(cur, fc, remaining);
9929+
if (!found) break;
9930+
const char* pos = static_cast<const char*>(found);
9931+
long long idx = pos - data;
9932+
for (int t = 0; t < token_count; ++t) {
9933+
if (static_cast<unsigned char>(token_ptrs[t][0]) != fc) continue;
9934+
int tlen = token_lens[t];
9935+
if (idx + static_cast<size_t>(tlen) <= len) {
9936+
if (memcmp(data + idx, token_ptrs[t], static_cast<size_t>(tlen)) == 0) {
9937+
mask |= (1u << t);
9938+
}
9939+
}
9940+
}
9941+
remaining = len - (idx + 1);
9942+
cur = data + idx + 1;
9943+
}
9944+
}
9945+
return mask;
9946+
};
9947+
9948+
auto find_vendor_hex = [&](const wchar_t* wptr) -> u32 {
9949+
if (!wptr) return 0;
9950+
const wchar_t* p = wptr;
9951+
while (*p) {
9952+
const wchar_t c = *p;
9953+
if ((c | 0x20) == L'v') {
9954+
if ((p[1] | 0x20) == L'e' && (p[2] | 0x20) == L'n' && p[3] == L'_') {
9955+
const wchar_t* q = p + 4;
9956+
u32 val = 0;
9957+
int got = 0;
9958+
while (got < 4 && *q) {
9959+
wchar_t wc = *q;
9960+
u32 nib = 0;
9961+
if (wc >= L'0' && wc <= L'9') nib = static_cast<u32>(wc - L'0');
9962+
else if ((wc | 0x20) >= L'a' && (wc | 0x20) <= L'f') nib = static_cast<u32>((wc | 0x20) - L'a' + 10);
9963+
else break;
9964+
val = static_cast<u32>((val << 4) | nib);
9965+
++got; ++q;
9966+
}
9967+
if (got == 4) return val;
9968+
}
9969+
}
9970+
++p;
9971+
}
9972+
return 0;
9973+
};
9974+
9975+
// setupapi stuff
9976+
int intel_hits = 0;
9977+
int amd_hits = 0;
9978+
char stack_buf[4096]{};
9979+
std::vector<char> heap_buf;
9980+
std::vector<BYTE> prop_buf;
9981+
9982+
auto scan_devices = [&](const GUID* classGuid, DWORD flags) {
9983+
HDEVINFO hDevInfo = SetupDiGetClassDevsW(classGuid, nullptr, nullptr, flags);
9984+
if (hDevInfo == INVALID_HANDLE_VALUE) return;
9985+
9986+
SP_DEVINFO_DATA devInfoData{};
9987+
devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
9988+
DWORD reqSize = 0;
9989+
9990+
for (DWORD i = 0; SetupDiEnumDeviceInfo(hDevInfo, i, &devInfoData); ++i) {
9991+
9992+
if (!SetupDiGetDeviceRegistryPropertyW(hDevInfo, &devInfoData, SPDRP_DEVICEDESC, nullptr, nullptr, 0, &reqSize)) {
9993+
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) continue;
9994+
}
9995+
9996+
if (prop_buf.size() < reqSize) prop_buf.resize(reqSize);
9997+
9998+
if (SetupDiGetDeviceRegistryPropertyW(hDevInfo, &devInfoData, SPDRP_DEVICEDESC, nullptr, prop_buf.data(), reqSize, nullptr)) {
9999+
10000+
const wchar_t* wDesc = reinterpret_cast<const wchar_t*>(prop_buf.data());
10001+
size_t asciiLen = 0;
10002+
const char* asciiDesc = wide_to_ascii(wDesc, stack_buf, STACK_CAP, &heap_buf, asciiLen);
10003+
10004+
// check if the description contains any interesting stuff
10005+
if (__memchr(asciiDesc, asciiLen)) {
10006+
// if interesting get hwid to get vendor
10007+
if (!SetupDiGetDeviceRegistryPropertyW(hDevInfo, &devInfoData, SPDRP_HARDWAREID, nullptr, nullptr, 0, &reqSize)) {
10008+
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) continue;
10009+
}
10010+
10011+
if (prop_buf.size() < reqSize) prop_buf.resize(reqSize);
10012+
10013+
if (SetupDiGetDeviceRegistryPropertyW(hDevInfo, &devInfoData, SPDRP_HARDWAREID, nullptr, prop_buf.data(), reqSize, nullptr)) {
10014+
const wchar_t* wHwId = reinterpret_cast<const wchar_t*>(prop_buf.data());
10015+
const u32 vid = find_vendor_hex(wHwId);
10016+
10017+
if (vid == VID_INTEL) intel_hits++;
10018+
else if (vid == VID_AMD_ATI || vid == VID_AMD_MICRO) amd_hits++;
10019+
}
10020+
}
10021+
}
10022+
}
10023+
SetupDiDestroyDeviceInfoList(hDevInfo);
10024+
};
10025+
10026+
// GUID_DEVCLASS_SYSTEM covers Host Bridges, LPC, PCI bridges Chipset/CPU etc
10027+
// GUID_DEVCLASS_USB covers USB controller stuff
10028+
// GUID_DEVCLASS_HDC covers SATA/IDE
10029+
const GUID* interesting_classes[] = {
10030+
&GUID_DEVCLASS_SYSTEM,
10031+
&GUID_DEVCLASS_USB,
10032+
&GUID_DEVCLASS_HDC
10033+
};
10034+
10035+
for (const GUID* guid : interesting_classes) {
10036+
scan_devices(guid, DIGCF_PRESENT);
10037+
}
10038+
10039+
// if no stuff then mybe query all devices in the system?
10040+
if (intel_hits == 0 && amd_hits == 0) {
10041+
scan_devices(nullptr, DIGCF_ALLCLASSES | DIGCF_PRESENT);
10042+
}
10043+
10044+
if (intel_hits > amd_hits) return MBVendor::Intel;
10045+
if (amd_hits > intel_hits) return MBVendor::AMD;
10046+
return MBVendor::Unknown;
10047+
};
10048+
10049+
const MBVendor vendor = detect_motherboard();
10050+
10051+
switch (vendor) {
10052+
case MBVendor::Intel:
10053+
if (claimed_amd && !claimed_intel) {
10054+
debug("CPU_HEURISTIC: CPU reports AMD but chipset looks Intel");
10055+
spoofed = true;
10056+
}
10057+
break;
10058+
case MBVendor::AMD:
10059+
if (claimed_intel && !claimed_amd) {
10060+
debug("CPU_HEURISTIC: CPU reports Intel but chipset looks AMD");
10061+
spoofed = true;
10062+
}
10063+
break;
10064+
default:
10065+
debug("CPU_HEURISTIC: Could not determine chipset vendor");
10066+
break;
10067+
}
983810068
#endif
983910069
return spoofed;
984010070
}
@@ -9937,6 +10167,7 @@ struct VM {
993710167
* @brief Check if Last Branch Record MSRs are correctly virtualized
993810168
* @category Windows
993910169
* @implements VM::LBR
10170+
* @note Currently investigating possible false flags with this
994010171
*/
994110172
[[nodiscard]] static bool lbr() {
994210173
#if (x86)
@@ -10123,6 +10354,7 @@ struct VM {
1012310354
pNtFreeVirtualMemory(hCurrentProcess, &controlBase, &tmpSize, MEM_RELEASE);
1012410355
g_control_slot = nullptr;
1012510356

10357+
// a breakpoint set anywhere in this function before slot_val is read will cause the kernel to not deliver any LBR info, thereby returning true
1012610358
return (slot_val == nullptr);
1012710359
#else
1012810360
return false;
@@ -11757,7 +11989,7 @@ std::pair<VM::enum_flags, VM::core::technique> VM::core::technique_list[] = {
1175711989
std::make_pair(VM::CLOCK, VM::core::technique(100, VM::clock)),
1175811990
std::make_pair(VM::POWER_CAPABILITIES, VM::core::technique(45, VM::power_capabilities)),
1175911991
std::make_pair(VM::CPU_HEURISTIC, VM::core::technique(90, VM::cpu_heuristic)),
11760-
std::make_pair(VM::LBR, VM::core::technique(100, VM::lbr)),
11992+
std::make_pair(VM::LBR, VM::core::technique(95, VM::lbr)),
1176111993
std::make_pair(VM::EDID, VM::core::technique(100, VM::edid)),
1176211994
std::make_pair(VM::BOOT_LOGO, VM::core::technique(100, VM::boot_logo)),
1176311995
std::make_pair(VM::GPU_CAPABILITIES, VM::core::technique(45, VM::gpu_capabilities)),

0 commit comments

Comments
 (0)