mirror of
https://github.com/Print3M/Anti-VM
synced 2026-06-21 16:41:58 +00:00
init
This commit is contained in:
@@ -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
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
#include <string>
|
||||
#include "detectors.hpp"
|
||||
#include <windows.h>
|
||||
#include <intrin.h>
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include "wmi.hpp"
|
||||
#include <Wbemidl.h>
|
||||
#include <iostream>
|
||||
#include "utils.hpp"
|
||||
#include <vector>
|
||||
|
||||
std::vector<std::string> detect::vm_names = { "virtual", "qemu", "vmware", "oracle", "innotek" };
|
||||
std::vector<std::string> detect::legit_cpu_ids = { "AuthenticAMD", "GenuineIntel" };
|
||||
|
||||
bool detect::cpu_hypervisor_bit() {
|
||||
/*
|
||||
Check for hypervisor - enabled bit.
|
||||
eax = 0x1, 31st-bit of ECX
|
||||
*/
|
||||
std::array<int, 4> 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<int, 4> 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<int, 4> 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;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace detect {
|
||||
extern std::vector<std::string> vm_names;
|
||||
extern std::vector<std::string> 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();
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
#include <stdio.h>
|
||||
#include <windows.h>
|
||||
#include <intrin.h>
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <Wbemidl.h>
|
||||
#include <iostream>
|
||||
#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());
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#include "utils.hpp"
|
||||
#include "detectors.hpp"
|
||||
#include <string>
|
||||
#include <codecvt>
|
||||
#include <vector>
|
||||
|
||||
std::string utils::bstr_to_str(BSTR bstr) {
|
||||
if (!bstr) return std::string("");
|
||||
|
||||
std::wstring ws(bstr, SysStringLen(bstr));
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> 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<std::string> includes) {
|
||||
for (auto i : includes) {
|
||||
if (str.find(i) != std::string::npos) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <wtypes.h>
|
||||
|
||||
namespace utils {
|
||||
std::string bstr_to_str(BSTR bstr);
|
||||
std::string lowercase(std::string str);
|
||||
bool str_includes(std::string str, std::vector<std::string> includes);
|
||||
};
|
||||
@@ -0,0 +1,181 @@
|
||||
#define _WIN32_DCOM
|
||||
#include <iostream>
|
||||
using namespace std;
|
||||
#include <comdef.h>
|
||||
#include <Wbemidl.h>
|
||||
#include <string>
|
||||
#include "wmi.hpp"
|
||||
|
||||
#pragma comment(lib, "wbemuuid.lib")
|
||||
|
||||
int wmi::get_wmi(std::string wql_query, std::function<void(IWbemClassObject*, VARIANT*)> 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.
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user