虚拟机检测手段
??學習一下虛擬機檢測的手段,在網上找了一些,練習一下,順便學習一下涉及的其他知識。
??但是由于現在虛擬機技術愈發大的發展,虛擬機檢測技術作用也愈發的小了,然而學以致用,或許在其他方面有所幫助。
代碼編輯時的配置:
??UNICODE字符集;
??VS2015 WinXP平臺;
??靜態編譯。
??或許由于調用過多CRT函數,編譯出來的程序體積約幾百Kb。
??部分虛擬機的檢測方法對現在的虛擬機系統不起作用,但還是記錄一下,使用方式涉及的知識可以了解一下。
/* 各種虛擬機探測方法練習 *///#include "windows.h" #include "stdio.h" #include "Winsock2.h" #include "IPHlpapi.h" #include "tlhelp32.h" #pragma comment(lib, "iphlpapi.lib") // GetAdaptersAddresses/* 通過執行特權指令探測Vmware 因為在虛擬機中指定功能號0xa則會從指定端口獲取虛擬機版本信息到指定的目的操作數地址 另外0x14則是獲取虛擬機內存大小,當獲取的值大于0說明在虛擬機中*/ BOOL detectVmwareByPrivilegeAsmInstruction() {BOOL rv = FALSE;//DWORD val = 0, val_1 = 0;__try {__asm {pushadmov eax, 'VMXh' // magic valuemov ebx, 1 // 設置未非'VMXh'值,接收in指令的返回值(VMware 版本)// 如果程序運行在虛擬機中,magic值將被移至ebx中// https://www.aldeid.com/wiki/VMXh-Magic-Valuemov ecx, 0ah //功能號mov edx, 'VX' //端口號in eax, dx/*mov val, eaxmov val_1, ebx*/cmp ebx, 'VMXh'setz [rv]popad}}__except (EXCEPTION_EXECUTE_HANDLER) { // 不在虛擬機中則觸發異常rv = FALSE;}//printf_s("%#x %#x\n", val, val_1);return rv; }/* 通過IDT基址來判斷虛擬機, RedPill作者發現在Vmware虛擬機上的IDT地址高字節通常為0xff;VirtualPC則是0ze8; 在真是主機上通常為0x80。因此redpill利用判斷執行SIDT指令后返回的第一個字節是否大于0xd0來確定是否程序是否運行于虛擬機中 typedef struct {WORD IDTLimit; // IDT的大小WORD LowIDTbase; // IDT的低位地址WORD HiIDTbase; // IDT的高位地址 } IDTINFO; 但是需滿足機器上只有一個處理器,多核 而且經過測試似乎對于最近版本VMware(測試環境是VM 12.0,Winxp sp3)無效*/ BOOL detectVmByIDTBaseAddr() {/*\x0f\x01\x0d為sidt指令,\xc3 為 ret;相當于sidt[pos]retn*/unsigned char m[2+4], rpill[] = "\x0f\x01\x0d\x00\x00\x00\x00\xc3";*((unsigned*)&rpill[3]) = (unsigned)m; // 設置讀取的 sidt 地址保存位置地址DWORD oldProtec = NULL;VirtualProtect(rpill, sizeof(rpill), PAGE_EXECUTE_READWRITE, &oldProtec);((void(*)())&rpill)(); //執行 rpill 指令VirtualProtect(rpill, sizeof(rpill), oldProtec, &oldProtec);printf_s("\tidt base address: %#x\n", *(DWORD*)&m[2]); // idt前兩個字節為idt大小if (m[5] > 0xd0)return TRUE;return FALSE; }/*虛擬機中的GDT(全局描述表)和LDT(本地描述表)與這真實主機中的基址并不相同 可以通過SGDT和SLDT指令獲取; LDT基址位于0x0000(兩個字節)時為真實主機,否則為虛擬機;GDT位于0xffxxxxxx(四個字節)說明位于虛擬機中,反之真實主機*/ BOOL detectVmByGDTAndLDTBaseAddr() {INT cnt = 0;/*LDT經過測試,目前的方法得到的值 LDT base 同樣為0x0000,所以無效*/WORD ldtBase = 0;__asm sldt ldtBaseprintf("\tLDT base address: %#x\n", ldtBase);if (ldtBase)cnt++;/*GDT同樣無效*/CHAR gdt[6];__asm sgdt gdtDWORD gdtBase = *(unsigned int*)&gdt[2];printf("\tGDT base address: %#x\n", gdtBase);if (gdt[2] == 0xff)cnt++;if (cnt > 0)return TRUE;return FALSE; }/* 通過STR指令獲取TSS(task state segment)的段選擇器;虛擬機中,讀取的地址往往為0x0040xxxx, 否則為真實主機*/ BOOL detectVmBySTRGetTSSbase() {char tssSeg[4] = { 0 };/*測試無效*/__asm str tssSegprintf("\tGet base address base address: 0x");for (int i = 0; i < 4; i++)printf("%02x", tssSeg[i]);printf("\n");if(tssSeg[0] == 0x00 && tssSeg[1] == 0x40)return TRUE;return FALSE; }/* 通過在注冊表中查找和VMware的虛擬硬件或VMwareTools等相關表項進行判別, 可以通過在注冊表中搜索VMware或VMware Tool等關鍵詞查找路徑*/ BOOL detectVmByReg() {/*測試HKEY_CLASSES_ROOT\Applications\VMwareHostOpen.exe項*/HKEY phKey;if (ERROR_SUCCESS == RegOpenKey(HKEY_CLASSES_ROOT, L"Applications\\VMwareHostOpen.exe", &phKey))return TRUE;return FALSE; }/* 由于指令在虛擬機中執行速度遠遠不如宿主機中的,可以根據指令執行的時間差來識別; 通過 'rdtsc' 指令可以將計算機啟動以來的CPU運行周期( time-stamp counter)讀至edx:eax;time-stamp counter存在一個64-bit MSR中。獲取之后,高32 bits放入edx,低32 bits則是在eax中。比如xchg指令,在宿主機中測試運行之后明顯遠遠小于在虛擬機中的時間*/ BOOL detectVmByTimeInterval() {BOOL retu = FALSE;CHAR prin[] = "\ttime interval: %#x\n";__asm {pushadrdtscxchg eax, ecxrdtscsub eax, ecxpush eaxpush eaxlea edx, prinpush edxcall ds:printfadd esp, 0x8pop eaxcmp eax, 0xffpopadjb BACKmov retu, 1} BACK:return retu; }/* mac地址的前3個字節標識網絡硬件制造商; 相同虛擬機軟件中虛擬機的mac地址往往是相同的或者不變的,可以根據這個來識別虛擬機*/ BOOL detectVmByMacAddress() {BOOL flag = FALSE;CHAR buff[20] = { 0 };CONST ULONG MAX_TIMES = 3;ULONG family = AF_UNSPEC; // both ipv4 and ipv6ULONG flags = GAA_FLAG_INCLUDE_PREFIX;ULONG size = (ULONG)sizeof(IP_ADAPTER_ADDRESSES);PIP_ADAPTER_ADDRESSES pAdapterAddress = NULL;ULONG val = 0, ite = 0;do {pAdapterAddress = (PIP_ADAPTER_ADDRESSES)malloc(size);if (pAdapterAddress == NULL) {printf("Alloc Memory Failed!\n");return FALSE;}val = GetAdaptersAddresses(family, flags, 0, pAdapterAddress, &size);if (val == ERROR_BUFFER_OVERFLOW) {free(pAdapterAddress);pAdapterAddress = NULL;}} while ((val == ERROR_BUFFER_OVERFLOW) && (ite++<MAX_TIMES));/*嘗試一定次數,因為可能會出現分配空間不足,見https://docs.microsoft.com/zh-cn/windows/win32/api/iphlpapi/nf-iphlpapi-getadaptersaddresses#parameters*/if (val == ERROR_SUCCESS) {ite = 0;while(pAdapterAddress){printf_s("\tAdapter #%d MAC addr:", ite);if (pAdapterAddress->PhysicalAddressLength != 0) {sprintf_s(buff, "%02x-%02x-%02x-%02x-%02x-%02x", pAdapterAddress->PhysicalAddress[0],pAdapterAddress->PhysicalAddress[1], pAdapterAddress->PhysicalAddress[2], pAdapterAddress->PhysicalAddress[3], pAdapterAddress->PhysicalAddress[4], pAdapterAddress->PhysicalAddress[5]);if (!wcsstr(pAdapterAddress->Description, L"VMnet")) { // 排除可能是宿主機中虛擬網卡的情況printf_s("%s", buff);buff[8] = 0;if (!strcmp(buff, "00-05-69") || !strcmp(buff, "00-0c-29") || !strcmp(buff, "00-50-56") || /*VMware*/!strcmp(buff, "00-03-ff") || /*VirtualPC*/!strcmp(buff, "08-00-27") /*VirtualBox*/) { flag = TRUE;}}else printf("VmNet");printf_s("\n");}else printf_s("\n");pAdapterAddress = pAdapterAddress->Next;ite++;}}else {free(pAdapterAddress);pAdapterAddress = NULL;}return flag; }/* 通過判斷Vm相關的進程進行判斷,比如Vmware中的vmtoolsd.exe, 但是有時候進程并未在執行可能無法獲取*/ BOOL detectVmByVMProcess() {BOOL flag = FALSE;WCHAR detecProc[] = L"vmtoolsd.exe";PROCESSENTRY32 pe32;HANDLE hSnapshot = NULL;hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);if (hSnapshot == INVALID_HANDLE_VALUE) {printf_s("\tCreateToolhelp32Snapshot Failed!!");return FALSE;}pe32.dwSize = sizeof(PROCESSENTRY32); //調用前必須設置結構體中的dwSize為PROCESSENTRY32結構體的大小if (Process32First(hSnapshot, &pe32)) {do {if (!wcscmp(pe32.szExeFile, detecProc)) {printf_s("\trunning process:%ws\n", pe32.szExeFile);flag = TRUE;break;}//printf_s("\t%ws\n", pe32.szExeFile);} while (Process32Next(hSnapshot, &pe32));}CloseHandle(hSnapshot);return flag; }int main(int argc, char* argv[]) {BOOL flag = FALSE;/*通過執行特權指令實現探測 vmware*/printf("== detect by run `in` I/O instruction:\n");if (flag = detectVmwareByPrivilegeAsmInstruction())printf("\t\t[yes] detect vmware !\n\n");else printf("\t\t[no] nothing found !\n\n");flag = false;/*使用idt基址檢測虛擬機*/printf("=[x][invalid method now]= detect by retrieve idt base:\n");if (flag = detectVmByIDTBaseAddr()) printf("\t\t[yes] detected vm !\n\n");else printf("\t\t[no] nothing found !\n\n");flag = false;/*根據GDT和LDT基址檢測虛擬機*/printf("=[x][invalid method now]= detect by retrieve idt base:\n");if (flag = detectVmByGDTAndLDTBaseAddr())printf("\t\t[yes] detected vm !\n\n");elseprintf("\t\t[no] nothing found !\n\n");flag = false;/*運行STR(非特權)指令,獲取TR(任務寄存器)中的端選擇器,虛擬機和真實主機之間是不同的。當地址等于0x0040xxxx時,說明處于虛擬機中;否則為真實主機*/printf("=[x][invalid method now]= detect by retrieve TSS base:\n");if (flag = detectVmBySTRGetTSSbase())printf("\t\t[yes] detected vm !\n\n");elseprintf("\t\t[no] nothing found !\n\n");flag = false;/*通過檢測虛擬機注冊表和Vmware相關的表項判別*/printf("== detect by register:\n");if (flag = detectVmByReg())printf("\t\t[yes] detected vm !\n\n");elseprintf("\t\t[no] nothing found !\n\n");flag = false;/*由于指令在虛擬機中執行速度遠遠不如宿主機中的,可以根據此來識別*/printf("== detect by instruction execute interval:\n");if (flag = detectVmByTimeInterval())printf("\t\t[yes] detected vm !\n\n");elseprintf("\t\t[no] nothing found !\n\n");flag = false;/*通過虛擬機mac地址的前3字節(網絡硬件制造商編號)進行判別*/printf("== detect by MAC address:\n");if (flag = detectVmByMacAddress())printf("\t\t[yes] detected vm !\n\n");elseprintf("\t\t[no] nothing found !\n\n");flag = false;/*通過進程中是否有VM相關進程在執行,進行判別*/printf("== detect by Vm process:\n");if (flag = detectVmByVMProcess())printf("\t\t[yes] detected vm !\n\n");elseprintf("\t\t[no] nothing found !\n\n");return 0; }參考文章:
??[1].詳解反虛擬機技術
??[2].虛擬機檢測技術剖析
轉載于:https://www.cnblogs.com/zUotTe0/p/11184385.html
總結
- 上一篇: 进程同步和信号量
- 下一篇: JAVA字符串的替换replace、re