From a3929ff50c81dfa0fb2a9c13c3ec1b544aa8a031 Mon Sep 17 00:00:00 2001 From: Print3M <92022497+Print3M@users.noreply.github.com> Date: Fri, 15 Sep 2023 22:06:35 +0200 Subject: [PATCH] init --- README.md | 24 +++++++ detectors.cpp | 158 +++++++++++++++++++++++++++++++++++++++++++ detectors.hpp | 18 +++++ main.cpp | 25 +++++++ utils.cpp | 35 ++++++++++ utils.hpp | 10 +++ wmi.cpp | 181 ++++++++++++++++++++++++++++++++++++++++++++++++++ wmi.hpp | 8 +++ 8 files changed, 459 insertions(+) create mode 100644 README.md create mode 100644 detectors.cpp create mode 100644 detectors.hpp create mode 100644 main.cpp create mode 100644 utils.cpp create mode 100644 utils.hpp create mode 100644 wmi.cpp create mode 100644 wmi.hpp diff --git a/README.md b/README.md new file mode 100644 index 0000000..8199361 --- /dev/null +++ b/README.md @@ -0,0 +1,24 @@ +# Anti-VM techniques + +Basic implementation of several anti-vm techniques (Windows) for educational purpose. They rely on checking different parts of OS and hardware to find out if the script is running in a VM. + +## Implemented techniques + +[x] CPU hypervisor bit (CPUID) +[x] CPU id string (CPUID) +[x] CPU brand string (CPUID) +[x] BIOS manufacturer string +[X] BIOS version string +[x] Screen resolution +[x] Amount of physical memory +[x] Number of CPU cores +[x] Amount of disk space + +## To be implemented + +[ ] Global Descriptor Table location +[ ] Local Descriptor Table location +[ ] Interrupt Descriptor Table location +[ ] ACPI VM-based string checks +[ ] VM-based hostnames and usernames +[ ] VM-based MAC addresses diff --git a/detectors.cpp b/detectors.cpp new file mode 100644 index 0000000..c6c56d8 --- /dev/null +++ b/detectors.cpp @@ -0,0 +1,158 @@ +#include +#include "detectors.hpp" +#include +#include +#include +#include +#include "wmi.hpp" +#include +#include +#include "utils.hpp" +#include + +std::vector detect::vm_names = { "virtual", "qemu", "vmware", "oracle", "innotek" }; +std::vector detect::legit_cpu_ids = { "AuthenticAMD", "GenuineIntel" }; + +bool detect::cpu_hypervisor_bit() { + /* + Check for hypervisor - enabled bit. + eax = 0x1, 31st-bit of ECX + */ + std::array cpuInfo = { 0, 0, 0, 0 }; + __cpuid(cpuInfo.data(), 0x1); + + return (cpuInfo[2] >> 31) & 0x1; +} + +bool detect::cpu_id() { + /* + Check for fake-vm CPU ID string. + eax = 0x0, EBX + ECX + EDX (12 bytes) + */ + + // TODO: I think there is something wrong with the regs order. + std::array cpuInfo = { 0, 0, 0, 0 }; + std::string cpu = ""; + __cpuid(cpuInfo.data(), 0x0); + + for (int i = 1; i <= 3; i++) { + cpu += cpuInfo[i] & 0xff; + cpu += (cpuInfo[i] >> 8) & 0xff; + cpu += (cpuInfo[i] >> 16) & 0xff; + cpu += (cpuInfo[i] >> 24) & 0xff; + } + + return !utils::str_includes(cpu, detect::legit_cpu_ids); +} + + +bool detect::cpu_brand() { + /* + Check for fake-vm CPU brand name. + eax = 0x80000002, 0x80000003, 0x80000004 + (EAX + EBX + ECX + EDX) x 3 = 36 bytes + */ + std::array cpuInfo = { 0, 0, 0, 0 }; + std::string cpu = ""; + + for (int id = 2; id <= 4; id++) { + __cpuid(cpuInfo.data(), 0x80000000 + id); + + for (auto i : cpuInfo) { + cpu += std::tolower(i & 0xff); + cpu += std::tolower((i >> 8) & 0xff); + cpu += std::tolower((i >> 16) & 0xff); + cpu += std::tolower((i >> 24) & 0xff); + } + } + + return utils::str_includes(cpu, detect::vm_names); +} + + +bool detect::bios_manufacturer() { + /* + Check for fake-vm BIOS manufacturer string. + */ + std::string manufacturer = ""; + wmi::get_wmi("select * from Win32_BIOS", [&](IWbemClassObject* pclsObj, VARIANT* vtProp) { + HRESULT hr = pclsObj->Get(L"Manufacturer", 0, vtProp, 0, 0); + manufacturer = utils::lowercase(utils::bstr_to_str(vtProp->bstrVal)); + }); + + std::cout << manufacturer << std::endl; + + return utils::str_includes(manufacturer, detect::vm_names); +} + +bool detect::bios_version() { + /* + Check for fake-vm BIOS version string. + */ + std::string version = ""; + wmi::get_wmi("select * from Win32_BIOS", [&](IWbemClassObject* pclsObj, VARIANT* vtProp) { + HRESULT hr = pclsObj->Get(L"SMBIOSBIOSVersion", 0, vtProp, 0, 0); + version = utils::lowercase(utils::bstr_to_str(vtProp->bstrVal)); + }); + + return utils::str_includes(version, detect::vm_names); +} + +bool detect::screen_resolution() { + /* + Check for uncommon screen resolutions. + */ + auto w = GetSystemMetrics(SM_CXSCREEN); + auto h = GetSystemMetrics(SM_CYSCREEN); + + // Standard and common resolutions + if (w == 1600 && h == 900) return false; + if (w == 1920 && h == 1080) return false; + if (w == 1920 && h == 1200) return false; + if (w == 2560 && h == 1440) return false; + if (w == 3840 && h == 2160) return false; + + return true; +} + +bool detect::memory_amount() { + /* + Check for suspicious amount of physical memory. + */ + MEMORYSTATUSEX statex; + statex.dwLength = sizeof(statex); + + if (GlobalMemoryStatusEx(&statex) == FALSE) { + return true; + } + + // Minimum 4 GB + return statex.ullTotalPhys / 1024 / 1024 < 4096; +} + +bool detect::cpu_cores() { + /* + Check for suspicious amount of CPU cores. + */ + SYSTEM_INFO info; + GetSystemInfo(&info); + + return info.dwNumberOfProcessors < 4; +} + +bool detect::disk_space() { + /* + Check for suspicious amount of total disk space. + */ + ULARGE_INTEGER bytes; + BOOL result = GetDiskFreeSpaceExA( + "\\\\?\\c:\\", + NULL, + &bytes, + NULL + ); + + if (result == FALSE) return true; + + return bytes.QuadPart / 1024 / 1024 / 1024 < 100; +} \ No newline at end of file diff --git a/detectors.hpp b/detectors.hpp new file mode 100644 index 0000000..8b4015a --- /dev/null +++ b/detectors.hpp @@ -0,0 +1,18 @@ +#pragma once +#include +#include + +namespace detect { + extern std::vector vm_names; + extern std::vector legit_cpu_ids; + + bool cpu_hypervisor_bit(); + bool cpu_id(); + bool cpu_brand(); + bool bios_manufacturer(); + bool bios_version(); + bool screen_resolution(); + bool memory_amount(); + bool cpu_cores(); + bool disk_space(); +}; \ No newline at end of file diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..0c74135 --- /dev/null +++ b/main.cpp @@ -0,0 +1,25 @@ +#include +#include +#include +#include +#include +#include +#include +#include "detectors.hpp" + +/* + TODO: + - memory && CPU resources +*/ + +int main(int argc, char* argv[]) { + printf("CPU Brand Name: %d\n", detect::cpu_brand()); + printf("CPU ID: %d\n", detect::cpu_id()); + printf("CPU Hypervisor bit: %d\n", detect::cpu_hypervisor_bit()); + printf("BIOS Manufacturer: %d\n", detect::bios_manufacturer()); + printf("BIOS Version: %d\n", detect::bios_version()); + printf("Scren resolution: %d\n", detect::screen_resolution()); + printf("Memory amount: %d\n", detect::memory_amount()); + printf("CPU cores: %d\n", detect::cpu_cores()); + printf("Disk space: %d\n", detect::disk_space()); +} diff --git a/utils.cpp b/utils.cpp new file mode 100644 index 0000000..ebef131 --- /dev/null +++ b/utils.cpp @@ -0,0 +1,35 @@ +#include "utils.hpp" +#include "detectors.hpp" +#include +#include +#include + +std::string utils::bstr_to_str(BSTR bstr) { + if (!bstr) return std::string(""); + + std::wstring ws(bstr, SysStringLen(bstr)); + std::wstring_convert> converter; + std::string narrow = converter.to_bytes(ws); + + return narrow; +} + +std::string utils::lowercase(std::string str) { + std::string result = ""; + + for (auto c : str) { + result += std::tolower(c); + } + + return result; +} + +bool utils::str_includes(std::string str, std::vector includes) { + for (auto i : includes) { + if (str.find(i) != std::string::npos) { + return true; + } + } + + return false; +} \ No newline at end of file diff --git a/utils.hpp b/utils.hpp new file mode 100644 index 0000000..34df68c --- /dev/null +++ b/utils.hpp @@ -0,0 +1,10 @@ +#pragma once +#include +#include +#include + +namespace utils { + std::string bstr_to_str(BSTR bstr); + std::string lowercase(std::string str); + bool str_includes(std::string str, std::vector includes); +}; \ No newline at end of file diff --git a/wmi.cpp b/wmi.cpp new file mode 100644 index 0000000..d5796e8 --- /dev/null +++ b/wmi.cpp @@ -0,0 +1,181 @@ +#define _WIN32_DCOM +#include +using namespace std; +#include +#include +#include +#include "wmi.hpp" + +#pragma comment(lib, "wbemuuid.lib") + +int wmi::get_wmi(std::string wql_query, std::function get_property) { + HRESULT hres; + + // Step 1: -------------------------------------------------- + // Initialize COM. ------------------------------------------ + + hres = CoInitializeEx(0, COINIT_MULTITHREADED); + if (FAILED(hres)) + { + cout << "Failed to initialize COM library. Error code = 0x" + << hex << hres << endl; + return 1; // Program has failed. + } + + // Step 2: -------------------------------------------------- + // Set general COM security levels -------------------------- + + hres = CoInitializeSecurity( + NULL, + -1, // COM authentication + NULL, // Authentication services + NULL, // Reserved + RPC_C_AUTHN_LEVEL_DEFAULT, // Default authentication + RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation + NULL, // Authentication info + EOAC_NONE, // Additional capabilities + NULL // Reserved + ); + + + if (FAILED(hres)) + { + cout << "Failed to initialize security. Error code = 0x" + << hex << hres << endl; + CoUninitialize(); + return 1; // Program has failed. + } + + // Step 3: --------------------------------------------------- + // Obtain the initial locator to WMI ------------------------- + + IWbemLocator* pLoc = NULL; + + hres = CoCreateInstance( + CLSID_WbemLocator, + 0, + CLSCTX_INPROC_SERVER, + IID_IWbemLocator, (LPVOID*)&pLoc); + + if (FAILED(hres)) + { + cout << "Failed to create IWbemLocator object." + << " Err code = 0x" + << hex << hres << endl; + CoUninitialize(); + return 1; // Program has failed. + } + + // Step 4: ----------------------------------------------------- + // Connect to WMI through the IWbemLocator::ConnectServer method + + IWbemServices* pSvc = NULL; + + // Connect to the root\cimv2 namespace with + // the current user and obtain pointer pSvc + // to make IWbemServices calls. + hres = pLoc->ConnectServer( + _bstr_t(L"ROOT\\CIMV2"), // Object path of WMI namespace + NULL, // User name. NULL = current user + NULL, // User password. NULL = current + 0, // Locale. NULL indicates current + NULL, // Security flags. + 0, // Authority (for example, Kerberos) + 0, // Context object + &pSvc // pointer to IWbemServices proxy + ); + + if (FAILED(hres)) + { + cout << "Could not connect. Error code = 0x" + << hex << hres << endl; + pLoc->Release(); + CoUninitialize(); + return 1; // Program has failed. + } + + // Step 5: -------------------------------------------------- + // Set security levels on the proxy ------------------------- + + hres = CoSetProxyBlanket( + pSvc, // Indicates the proxy to set + RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx + RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx + NULL, // Server principal name + RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx + RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx + NULL, // client identity + EOAC_NONE // proxy capabilities + ); + + if (FAILED(hres)) + { + cout << "Could not set proxy blanket. Error code = 0x" + << hex << hres << endl; + pSvc->Release(); + pLoc->Release(); + CoUninitialize(); + return 1; // Program has failed. + } + + // Step 6: -------------------------------------------------- + // Use the IWbemServices pointer to make requests of WMI ---- + + // For example, get the name of the operating system + IEnumWbemClassObject* pEnumerator = NULL; + hres = pSvc->ExecQuery( + bstr_t("WQL"), + bstr_t(wql_query.c_str()), + WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, + NULL, + &pEnumerator); + + if (FAILED(hres)) + { + cout << "WMI query failed." << " Error code = 0x" << hex << hres << endl; + pSvc->Release(); + pLoc->Release(); + CoUninitialize(); + return 1; // Program has failed. + } + + // Step 7: ------------------------------------------------- + // Get the data from the query in step 6 ------------------- + + IWbemClassObject* pclsObj = NULL; + ULONG uReturn = 0; + + while (pEnumerator) + { + HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, + &pclsObj, &uReturn); + + if (0 == uReturn) + { + break; + } + + VARIANT vtProp; + + VariantInit(&vtProp); + // Get the value of the property + get_property(pclsObj, &vtProp); + //hr = pclsObj->Get(L"SMBIOSBIOSVersion", 0, &vtProp, 0, 0); + //wcout << "SMBIOSBIOSVersion : " << vtProp.bstrVal << endl; + VariantClear(&vtProp); + + pclsObj->Release(); + } + + // Cleanup + // ======== + + pSvc->Release(); + pLoc->Release(); + pEnumerator->Release(); + CoUninitialize(); + + return 0; // Program successfully completed. + +} + diff --git a/wmi.hpp b/wmi.hpp new file mode 100644 index 0000000..1620cca --- /dev/null +++ b/wmi.hpp @@ -0,0 +1,8 @@ +#pragma once +#include +#include +#include + +namespace wmi { + int get_wmi(std::string wql_query, std::function get_property); +}; \ No newline at end of file