Детектим работу ПО в VMWare
Введение
Уже несколько лет тема использования виртуальных машин является одной из актуальных в мире ИБ комьюнити. А с началом пандемии коронавируса и перехода большого количества компаний на удаленку – можно сказать ВМ вышли на 1-й план (после денег конечно).Введение
При проведении тестирования на проникновение важно понимать, где именно сейчас ты находишься: в гостевой или хостовой операционной системе. Как минимум это даст возможность спланировать дальнейшие вектора развития пентеста, а как максимум – защитить свой пейлоад от возможного исследования. Ведь ни для кого не секрет, что ни один вирусный аналитик не будет запускать образец на хостовой ОС, все используют виртуальные машины и эмуляторы.
В данной скромной статье показано как можно, обладая не очень крутыми навыками в программировании на C++, определить работу своего ПО в гостевой ОС на VMWare. Принципы, рассмотренные, универсальны (в большинстве своём), поэтому замена некоторых строк и условий с VMWare на, например, Virtual Box или QEMU приведет к аналогичному результату по отношению к уже другим средствам виртуализации.
Исходя из приоритета конкурса по поводу практической значимости и собственного нежелания разводить демагогию по поводу теоретических аспектов – в статье минимум теории и максимум кода. Думаю, что по коду и так будет понятно, как и что работает)
Поехали!
Итак, пойдем «снизу вверх» и первым делом давайте рассмотрим такие (многим наверняка непонятные вещи), как idtr, ldtr, gdtr, tr, SMSW, VX.НИЗ
idtr - Interrupt Descriptor Table Register – регистр таблицы векторов прерываний, которая используется для обработки исключений и прерываний (ТОЛЬКО В архитектуре x86!)
ldtr - Local Descriptor Table Register - регистр локальной дескрипторной таблицы, которая содержит дускрипторы, используемые конкретным процессом.
gdtr - Global Descriptor Table Register – регистр глобальной дескрипторной таблицы, которая содержит дескрипторы, доступ к которым имеют все процессы.
tr – Table Rigister – регистр дескрипторной таблицы, которая содержит указатели на IDT, LDT и GDT.
К чему всё это? Среда виртуализации держит эти регистры в ежовых рукавицах и хранит там свои значения, а в случает с ldtr – не дает его изменить. (РАБОТАЕТ НА VMWare версии 14 и ниже)
Ниже представил примеры функций, с помощью которых можно, используя вышеописанную особенность ВМ, задетектить виртуалочку (return true – детект! return false – всё в норме)
C++:
bool idtr()
{
unsigned char idtr[6];
_asm sidt idtr
unsigned int idt_base = 0;
idt_base = *((unsigned long*)&idtr[2]);
if ((idt_base >> 24) == 0xff) {
return true;
}
return false;
}
bool ldtr()
{
unsigned char ldtr[5] = "\xef\xbe\xad\xde";
unsigned long ldt = 0;
_asm sldt ldtr
ldt = *((unsigned long*)&ldtr[0]);
if (ldt != 0xdead0000)
{
return true;
}
return false;
}
bool gdtr()
{
unsigned char gdtr[6];
unsigned long gdt = 0;
_asm sgdt gdtr
gdt = *((unsigned long*)&gdtr[2]);
if ((gdt >> 24) == 0xff)
{
return true;
}
return false;
}
bool tr()
{
unsigned char mem[4] = { 0, 0, 0, 0 };
__asm str mem;
if ((mem[0] == 0x00) && (mem[1] == 0x40))
{
return true;
}
return false;
}
Теперь рассмотрим SMSW: Store Machine Status Word (дословно – Хранить слово (2 байта) состояния машины). Не вдаваясь в подробности скажу, что виртуалка его также подменяет. Код ниже, пользуемся)
C++:
bool smsw()
{
unsigned int reax = 0;
__asm
{
mov eax, 0xCCCCCCCC;
smsw eax;
mov DWORD PTR[reax], eax;
}
if ((((reax >> 24) & 0xFF) == 0xcc) && (((reax >> 16) & 0xFF) == 0xcc))
{
return true;
}
return false;
}
Перейдем к VX. Это, так называемый, «порт виртуализации». Если верить интернетам – через него происходит низкоуровневое общение общение хостовой и гостевой ОС. Скажу сразу: сам не проверял как там и что происходит, но точно знаю, что если записать в него 0x564d5868 (в символах - VMXh) и исключения не возникнет – то добро пожаловать в виртуальную машину)
C++:
bool VX_check() {
__try {
__asm {
mov eax, 0x564d5868
mov ecx, 0x0A
mov edx, 0x5658
in eax, dx }
return true;
} __except (EXCEPTION_EXECUTE_HANDLER) {
return false;
}
}
НЕМНОГО ВЫШЕ
Современные процессоры указывают на использование виртуализации. При этом, о виртуальном процессоре, также, как и о реальном, хранится большой объем информации, включая его производителя. В связи с этим – проверяем и делаем выводы)
C++:
bool cpu_check() {
int* cpuinfo = new int[4];
int f_id = 0x40000000;
__cpuid(cpuinfo, f_id);
char hyper_vendor_id[13];
memcpy(hyper_vendor_id + 0, (const void*)&(cpuinfo[1]), 4);
memcpy(hyper_vendor_id + 4, (const void*)&(cpuinfo[2]), 4);
memcpy(hyper_vendor_id + 8, (const void*)&(cpuinfo[3]), 4);
hyper_vendor_id[12] = '\0';
cout << "hyper_vendor_id: " << hyper_vendor_id << endl;
if (strstr(hyper_vendor_id, "VMware"))
return true;
return false;
}
По аналогии с процессором – mac адрес, первые 3 байта которого – производитель.
C++:
bool MAC_check() {
DWORD OutBufLen = 8192;
PIP_ADAPTER_INFO AdapterInfo = (PIP_ADAPTER_INFO) new(char[OutBufLen]);
ULONG err = GetAdaptersInfo(AdapterInfo, &OutBufLen);
while (AdapterInfo) {
printf("MAC[%s: %s] = %X:%X:%X:%X:%X:%X\n", AdapterInfo->AdapterName, AdapterInfo->Description, (BYTE)AdapterInfo->Address[0], (BYTE)AdapterInfo->Address[1], (BYTE)AdapterInfo->Address[2],
(BYTE)AdapterInfo->Address[3], (BYTE)AdapterInfo->Address[4], (BYTE)AdapterInfo->Address[5]);
if ((((BYTE)AdapterInfo->Address[0] == 0x00) && ((BYTE)AdapterInfo->Address[1] == 0x05) && ((BYTE)AdapterInfo->Address[2] == 0x69)) ||
(((BYTE)AdapterInfo->Address[0] == 0x00) && ((BYTE)AdapterInfo->Address[1] == 0x0c) && ((BYTE)AdapterInfo->Address[2] == 0x29)) ||
(((BYTE)AdapterInfo->Address[0] == 0x00) && ((BYTE)AdapterInfo->Address[1] == 0x1c) && ((BYTE)AdapterInfo->Address[2] == 0x14)) ||
(((BYTE)AdapterInfo->Address[0] == 0x00) && ((BYTE)AdapterInfo->Address[1] == 0x50) && ((BYTE)AdapterInfo->Address[2] == 0x56))) {
delete(AdapterInfo);
return true;
}
AdapterInfo = AdapterInfo->Next;
}
delete(AdapterInfo);
return false;
}
Также производителя хранит ОС и BIOS. Используя wmic (может можно и по другому, но я сделал так), получаем вендора, сравниваем и делаем выводы.
C++:
bool BIOS_check()
{
bool flag = false;
const char* cmd = "wmic bios get serialnumber";
shared_ptr<FILE> pipe(_popen(cmd, "r"), _pclose);
cout << "DMI_check:" << endl;
if (pipe)
{
char buffer[1024];
double diskSpace = 0;
while (!feof(pipe.get())) {
if (fgets(buffer, 128, pipe.get()) != NULL)
{
cout << buffer << endl;
if (strstr(buffer, "VMware") || strstr(buffer, "WMW"))
return true;
}
}
}
return false;
}
C++:
bool Win_check()
{
bool flag = false;
const char* cmd = "wmic csproduct get vendor, version";
shared_ptr<FILE> pipe(_popen(cmd, "r"), _pclose);
if (pipe)
{
char buffer[128];
string result = "";
while (!feof(pipe.get()))
if (fgets(buffer, 128, pipe.get()) != NULL)
result += buffer;
cout << "Vendor_Version: " << result << endl;
if (strstr(result.c_str(), "VMware"))
return true;
}
return false;
}
ВВЕРХ!
Среды виртуализации размещают на гостевых операционных системах свои сервисы, а как следствие – появляются ключи в реестре, процессы и файлы.
Детект через реестр Windows
C++:
bool vmRegVal()
{
bool res = false;
std::string stringVmRegVals[25][4] =
{
{ "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", "VMware User Process", "", "" },
{ "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\SharedDlls", "C:\\WINDOWS\\system32\\VMUpgradeAtShutdownWXP.dll", "", "" },
{ "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E968-E325-11CE-BFC1-08002BE10318}\\0000",
"DriverDesc", "vmware svga ii", "" },
{ "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E968-E325-11CE-BFC1-08002BE10318}\\0000",
"DriverDesc", "vmware svga 3d", "" },
{ "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E96F-E325-11CE-BFC1-08002BE10318}\\0000",
"InfSection", "vmmouse", "" },
{ "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E968-E325-11CE-BFC1-08002BE10318}\\0000",
"DriverDesc", "vmware vmscsi controller", "" },
{ "SYSTEM\\CurrentControlSet\\Control\\Video\\{4BEF3D64-1F2B-4026-9EE4-B6D8CD9FEA1B}\\0000",
"Device Description", "vmware svga ii", "" },
{ "SYSTEM\\CurrentControlSet\\Control\\Video\\{3A8088C5-4419-4572-801C-A10BA858952F}\\0000",
"Device Description", "vmware svga 3d", "" },
{ "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E96F-E325-11CE-BFC1-08002BE10318}\\0000",
"DriverDesc", "VMware Pointing Device", "" },
{ "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E96F-E325-11CE-BFC1-08002BE10318}\\0000",
"ProviderName", "VMware, Inc.", "" },
{ "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E96F-E325-11CE-BFC1-08002BE10318}\\0001",
"DriverDesc", "VMware USB Pointing Device", "" },
{ "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E96F-E325-11CE-BFC1-08002BE10318}\\0001",
"InfSection", "VMUsbMouse", "" },
{ "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E96F-E325-11CE-BFC1-08002BE10318}\\0001",
"ProviderName", "VMware, Inc.", "" },
{ "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E968-E325-11CE-BFC1-08002BE10318}\\0000",
"HardwareInformation.AdapterString", "VMware SVGA 3D", "" },
{ "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E968-E325-11CE-BFC1-08002BE10318}\\0000",
"HardwareInformation.ChipType", "VMware Virtual SVGA 3D Graphics Adapter", "" },
{ "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E968-E325-11CE-BFC1-08002BE10318}\\0000",
"InfSection", "VM3D_AMD64", "" },
{ "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E968-E325-11CE-BFC1-08002BE10318}\\0000",
"InstalledDisplayDrivers", "vm3dum64", "" },
{ "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E968-E325-11CE-BFC1-08002BE10318}\\0000",
"InstalledDisplayDrivers", "vm3dum", "" },
{ "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E968-E325-11CE-BFC1-08002BE10318}\\0000",
"InstalledDisplayDrivers", "vm3dgl64", "" },
{ "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E968-E325-11CE-BFC1-08002BE10318}\\0000",
"InstalledDisplayDrivers", "vm3dgl", "" },
{ "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E968-E325-11CE-BFC1-08002BE10318}\\0000",
"OpenGLDriverName", "vm3dgl64.dll", "" },
{ "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E968-E325-11CE-BFC1-08002BE10318}\\0000",
"OpenGLDriverNameWow", "vm3dgl.dll", "" },
{ "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E968-E325-11CE-BFC1-08002BE10318}\\0000",
"ProviderName", "VMware, Inc.", "" },
{ "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E968-E325-11CE-BFC1-08002BE10318}\\0000",
"UserModeDriverName", "vm3dum64.dll", "" },
{ "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E968-E325-11CE-BFC1-08002BE10318}\\0000",
"UserModeDriverNameWow", "vm3dum.dll", "" }
};
string val1[] = { "Scsi Port 0", "Scsi Port 1", "Scsi Port 2", "Scsi Port 3", "Scsi Port 4" };
string val2[] = { "Scsi Bus 0", "Scsi Bus 1", "Scsi Bus 2", "Scsi Bus 3", "Scsi Bus 4", "Scsi Bus 5", "Scsi Bus 6" };
for (size_t i = 0; i < sizeof(val1) / sizeof(val1[0]); i++)
{
for (size_t j = 0; j < sizeof(val2) / sizeof(val2[0]); j++)
{
string str;
str = "HARDWARE\\DEVICEMAP\\Scsi\\" + val1[i] + "\\" + val2[j] +
"\\Target Id 0\\Logical Unit Id 0";
if (CheckReg(str, "Identifier", "vmware", ""))
{
cout << " detected " << str << endl;
res = true;
}
}
}
for (size_t i = 0; i < sizeof(stringVmRegVals) / sizeof(stringVmRegVals[0]); i++)
{
if (CheckReg(stringVmRegVals[i][0], stringVmRegVals[i][1], stringVmRegVals[i][2],
stringVmRegVals[i][3]))
{
return true;
}
}
return res;
}
bool CheckReg(string value, string valueName, string key, string key2)
{
HKEY HK = 0;
bool flag = false;
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, value.c_str(), 0, KEY_READ, &HK) == ERROR_SUCCESS)
{
unsigned long type = 0;
unsigned long size = 0x100;
char* reg_value = (char*)LocalAlloc(LMEM_ZEROINIT, size + 10);
if (ERROR_SUCCESS == RegQueryValueExA(HK, valueName.c_str(), 0, &type, (unsigned char*)reg_value, &size))
{
if (key != "")
{
if (key2 != "")
{
if (type == REG_MULTI_SZ)
{
char* video = reg_value;
while (*(unsigned char*)video)
{
string res = resultKey(video);
if (res.find(key) != std::string::npos || res.find(key2) != std::string::npos)
flag = true;
video = &video[strlen(video) + 1];
}
}
} else
{
string res = resultKey(reg_value);
if (type == REG_SZ || type == REG_EXPAND_SZ)
{
if (res.find(key) != std::string::npos)
flag = true;
}
if (type == REG_MULTI_SZ)
{
char* video = reg_value;
while (*(unsigned char*)video)
{
string res = resultKey(video);
if (res.find(key) != std::string::npos)
flag = true;
video = &video[strlen(video) + 1];
}
}
}
} else
flag = true;
}
LocalFree(reg_value);
RegCloseKey(HK);
}
return flag;
}
Теперь файлы
C++:
bool vmFile()
{
bool res = false;
string stringVmFiles[20] =
{
"C:\\WINDOWS\\system32\\vm3dgl64.dll",
"C:\\WINDOWS\\system32\\vm3dgl.dll",
"C:\\WINDOWS\\system32\\vm3dum64.dll",
"C:\\WINDOWS\\system32\\vm3dum.dll",
"C:\\WINDOWS\\system32\\VmbuxCoinstaller.dll",
"C:\\WINDOWS\\system32\\vmGuestLib.dll",
"C:\\WINDOWS\\system32\\vmGuestLibJava.dll",
"C:\\WINDOWS\\system32\\vmhgfs.dll",
"C:\\WINDOWS\\system32\\vmwogl32.dll",
"C:\\WINDOWS\\system32\\vmmreg32.dll",
"C:\\WINDOWS\\system32\\vmx_fb.dll",
"C:\\WINDOWS\\system32\\vmx_mode.dll",
"C:\\WINDOWS\\system32\\VMUpgradeAtShutdownWXP.dll"
};
for (size_t i = 0; i < sizeof(stringVmFiles) / sizeof(stringVmFiles[0]); i++)
{
if (fileExist((char*)stringVmFiles[i].c_str()) != INVALID_FILE_ATTRIBUTES)
{
cout << " detected " << stringVmFiles[i] << endl;
res = true;
}
}
return res;
}
DWORD fileExist(char* filename)
{
return GetFileAttributesA(filename);
}
Ну и процессы напоследок
C++:
bool Process_check()
{
bool res = false;
DWORD aProcesses[1024], cbNeeded, cProcesses;
unsigned int i;
if (!EnumProcesses(aProcesses, sizeof(aProcesses), &cbNeeded))
{
return 1;
}
cProcesses = cbNeeded / sizeof(DWORD);
for (i = 0; i < cProcesses; i++)
{
if (aProcesses[i] != 0)
{
CHAR szProcessName[MAX_PATH] = "null";
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION |
PROCESS_VM_READ,
FALSE, aProcesses[i]);
if (NULL != hProcess)
{
HMODULE hMod;
DWORD cbNeeded;
if (EnumProcessModules(hProcess, &hMod, sizeof(hMod),
&cbNeeded))
{
GetModuleBaseNameA(hProcess, hMod, szProcessName,
sizeof(szProcessName) / sizeof(CHAR));
}
CloseHandle(hProcess);
if (strstr(szProcessName, "vmtool") ||
strstr(szProcessName, "vm3dservice") ||
strstr(szProcessName, "vmhgfs") ||
strstr(szProcessName, "VMMEMCTL") ||
strstr(szProcessName, "vmmouse") ||
strstr(szProcessName, "vmrawdsk") ||
strstr(szProcessName, "VMTools") ||
strstr(szProcessName, "vmusbmouse") ||
strstr(szProcessName, "vmvss") ||
strstr(szProcessName, "vmscsi") ||
strstr(szProcessName, "vmxnet") ||
strstr(szProcessName, "vmx_svga") ||
strstr(szProcessName, "VMware Physical Disk Helper Service") ||
strstr(szProcessName, "vmtoolsd") ||
strstr(szProcessName, "vmacthlp")) {
cout << " detected " << szProcessName << endl;
res = true;
}
}
}
}
return res;
}
ЭКЗОТИКА
Теперь немного порассуждаем. Каждый из нас, создавая виртуалку, зачастую не выделяет ей такое же количество ресурсов, как у реальной машины. Более того, не всегда виртуалка подключена к сети «Интернет». Исходя из этого ниже приведены методы, которые можно использовать в качестве дополнительных по детекту. Все они основаны на сугубо моих предположениях о том, как пользователи используют ВМ.
Проверка количества ядер процессора. Я не думаю, что в 2022 году еще можно встретить машину с 2-х ядерным процессором, а вот виртуалку – вполне.
C++:
void checkCoreNumber()
{
const char* cmd = "wmic cpu get NumberOfCores";
FILE* fp;
char line[1000];
string result[100][101];
int count = 0;
bool flag = false, flag2 = false, result_flag = false;
fp = _popen(cmd, "r");
while (fgets(line, sizeof line, fp) && count < 100)
{
count = count + 1;
if (count == 2)
{
if (strstr(line, "2"))
{
printf("Core number is 2\n");
} else {
printf("Core number is more than 2\n");
}
}
}
_pclose(fp);
}
Тоже самое касается и жесткого диска. У кого диск на реальном ПК меньше или равен по размеру 60 Гб? Думаю, таких не много.
C++:
void DiskSpace()
{
bool flag = false;
const char* cmd = "wmic logicaldisk get size";
shared_ptr<FILE> pipe(_popen(cmd, "r"), _pclose);
if (pipe)
{
char buffer[128];
string result = "";
double diskSpace = 0;
while (!feof(pipe.get())) {
if (fgets(buffer, 128, pipe.get()) != NULL)
{
result = buffer;
result = eraseString(result, ' ');
result = eraseString(result, '\r');
result = eraseString(result, '\n');
if (result != "Size" && result != "" && result != " ")
{
diskSpace = stod(result) / (1024 * 1024 * 1024);
printf("Disk space is %f\n", diskSpace);
break;
}
}
}
}
}
Проверяем подключение к интернету
C++:
void checkInternet()
{
bool bConnect = InternetCheckConnectionA("https://www.google.com.tr/", FLAG_ICC_FORCE_CONNECTION, 0);
int a = GetLastError();
if (!bConnect)
{
printf("No internet\n");
} else {
printf("Internet is connected\n");
}
}
Ну и можно еще чекнуть имя машины, вдруг кто назовет её «VM-1» или как-то так)
C++:
string getComputerName()
{
CHAR nameBuf[MAX_COMPUTERNAME_LENGTH + 2];
DWORD nameBufSize = (sizeof nameBuf) - 1;
GetComputerNameA(nameBuf, &nameBufSize);
string arr_s(nameBuf);
return arr_s;
}
Можно еще проверить подключенные устройства (мышь, клавиатура и т.д.). Часто к ПК подключено больше 2-х периферийных устройств)
C++:
void InitDIAndEnumAllDevices(HWND hWnd)
{
if (DirectInput8Create(GetModuleHandle(0), DIRECTINPUT_VERSION,
IID_IDirectInput8W, (void**)&g_pDI, NULL) == S_OK)
g_pDI->EnumDevices(DI8DEVCLASS_ALL, EnumDevices,
(LPVOID)hWnd, DIEDFL_ALLDEVICES);
}
BOOL CALLBACK EnumDevices(LPCDIDEVICEINSTANCE pdInst,
LPVOID pvRef)
{
LPOLESTR guidChar = NULL;
guidChar = (LPOLESTR)malloc(128);
if (StringFromGUID2(pdInst->guidProduct, guidChar, 64)) {
USES_CONVERSION;
string guid = OLE2CA(guidChar);
char name[256];
WideCharToMultiByte(CP_OEMCP, 0, pdInst->tszProductName, -1, name, 260, NULL, NULL);
cout << name << ": " << guid << endl;
}
return DIENUM_CONTINUE;
}
В заключении
В данной статье постарался изложить всё, что знаю по теме детекта виртуалок (в частности VMWare). Конечно наверняка есть еще много много различных способов, которые знают другие или которые еще неизвестны человечеству. Если вдруг знаете что-то еще – напишите обязательно в комментариях, будет интересно попробовать и проверить.На счет кода – его вариаций можно сделать бесконечное множество. Я изложил то, что когда-то писал. Если кто-то хочет написать код по-другому – пожалуйста, пишите в комментариях, тоже интересно глянуть)
Это мой первый опыт в написании такой большой статьи на форуме и уж тем более участии в конкурсе. Надеюсь, кому-то данная статья пригодится)
Всем спасибо за внимание)