Loading... [参考网址](https://www.freesion.com/article/786049765/) https://www.dazhuanlan.com/2020/05/19/5ec3905cdbfb0/ https://blog.csdn.net/u012395622/article/details/47026523 https://blog.csdn.net/a815064247/article/details/80322440 ### PEB->BeingDebugged 位于PEB结构的0x2处, BeingDebugged 当进程被调试器附加时,操作系统会自动设置这个标志位。(1代表被调试,0代表没有被调试) ### PEB->ProcessHeap 位于PEB结构的0x18处,第一个堆头部都有一个属性来指明这个堆是否在调试中创建。这些属性叫ForceFlags(堆头部偏移量为0x10/0x44处)和Flags(堆头部偏移量为0x0c/0x40处)。 ### PEB->NtGlobalFlag NtGlobalFlag位于PEB的0x068h处,如果BeingDebugged被设置为TRUE,则NtGlobalFlag的值为70h。 ### API - BOOL WINAPI IsDebuggerPresent(void); 这个函数来检测是否有调试器存在,返回非0值代表被调试,如果返回0代表没有被调试。 - CheckRemoteDebuggerPresent() CheckRemoteDebuggerPresent( )与IsDebuggerPresent( )类似,同为Windows API,可以直接调用,`CheckRemoteDebuggerPresent()`实际调用了NtQueryInformationProcess(),查询某个进程的ProcessDebugPort即系统与调试器通信的端口句柄,CheckRemoteDebuggerPresent()通过查询这个值来确定程序是否处于调试状态。 - NtSetInformationThread(ThreadHideFormDebugger(0x11)) 攻击调试器 * 查询调试信息:NtQueryInformationProcess() * 在TLS回调函数内:检查父进程、窗口名、进程名、文件和路径、注册表 ### 其他方法基于系统痕迹 #### 父进程检测 一个进程被正常启动时,其父进程一般是Explore.exe文件资源管理器、cmd.exe、或者Services.exe系统服务。如果一个进程的父进程不是这些进程,可以怀疑其被调试了。 进程vmtoolsd、服务VMTools、vmvss和VMWare Physical Disk Helper。首先我们先针对vmtoolsd进行分析。 #### 堆数据检测 由于BeingDebugged被设置为TRUE,NtGlobalFlag设置了FLG_HEAP_VALIDATE_PARAMETERS,RtlCreateHeap函数用RtlDebugCteateHeap函数创建堆,与此同时在堆中填充数据:`BA AD F0 0D`、`FE EE FE EE`、`AB AB AB AB`,如果这些数据出现的次数较多(大于10次)则说明被调试了。 #### 注册表检测 下面是调试器在注册表中的一个常用位置。 SOFTWAREMicrosoftWindows NTCurrentVersionAeDebug(32位系统) SOFTWAREWow6432NodeMicrosoftWindowsNTCurrentVersionAeDebug(64位系统) 该注册表项指定当应用程序发生错误时,触发哪一个调试器。默认情况下,它被设置为Dr.Watson。如果该这册表的键值被修改为OllyDbg,则恶意代码就可能确定它正在被调试。 #### 进程遍历 枚举进程中是否有调试器进程。 #### 窗口遍历 枚举主窗口的标题,判断是否有调试器窗口,与上面的进程检测方法一样,这样的反调试很容易绕过。 #### 硬件断点检测 调试器使用DR0~Dr3作为硬件断点,通过检查这几个寄存器的值是否为空来确定当前程序是否被调试。 ## 反虚拟机 检测特定的文件夹或文件信息 检测当前进程信息 检测特定的服务名 ### 检测特定的注册表信息 项名:HKEY_CLASSES_ROOT\Applications\VMwareHostOpen.exe 项名:HKEY_CLASSES_ROOT\Installer\Products\C2A6F2EFE6910124C940B2B12CF170FE\ProductName 键值“VMware Tools” 项名:HKEY_CLASSES_ROOT\Installer\Products\C2A6F2EFE6910124C940B2B12CF170FE\SourceList\PackageName 键值:VMware Tools.msi 项名:HKEY_CURRENT_USER\Printers\DeviceOld 键值:_#VMwareVirtualPrinter,winspool,TPVM: 项名:HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\Scsi\Scsi Port 0\Scsi Bus 0\Target Id 0\Logical Unit Id 0\Identifier 键值:VMware Virtual IDE Hard Drive 项名:HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\Scsi\Scsi Port 1\Scsi Bus 0\Target Id 0\Logical Unit Id 0\Identifier 键值:NECVMWar VMware IDE CDR10 项名:HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Installer\Products\C2A6F2EFE6910124C940B2B12CF170FE\ProductName 键值:VMware Tools 项名:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\C2A6F2EFE6910124C940B2B12CF170FE\InstallProperties\DisplayName 键值:VMware Tools 项名:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Reinstall\0002\DeviceDesc 键值:VMware SVGA II 项名:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkCards\2\Description 键值:VMware Accelerated AMD PCNet Adapter 项名:HKEY_LOCAL_MACHINE\SOFTWARE\VMware, Inc.\VMware Tools 项名:HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Class\{4D36E968-E325-11CE-BFC1-08002BE10318}\0000\DriverDesc 键值:VMware SVGA II 项名:HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Class\{4D36E968-E325-11CE-BFC1- 08002BE10318}\0000\ProviderName 键值:VMware, Inc. 项名:HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Class\{4D36E972-E325-11CE-BFC1-08002bE10318}\0001\DriverDesc 键值:VMware Accelerated AMD PCNet Adapter 项名:HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Class\{4D36E97B-E325-11CE-BFC1-08002BE10318}\0000\DriverDesc 键值:VMware SCSI Controller 项名:HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Print\Monitors\ThinPrint Print Port Monitor for VMWare ``` BOOL DetectVM() { HKEY hKey; char szBuffer[64]; unsigned long hSize= sizeof(szBuffer) - 1; if( RegOpenKeyEx( HKEY_LOCAL_MACHINE, "HARDWARE\\DESCRIPTION\\System\\BIOS\\", 0, KEY_READ, &hKey )==ERROR_SUCCESS ) { RegQueryValueEx( hKey, "SystemManufacturer", NULL, NULL, (unsigned char *)szBuffer, &hSize ); if( strstr( szBuffer, "VMWARE" )) { RegCloseKey( hKey ); return TRUE; } RegCloseKey( hKey ); } return FALSE; } ``` ### 通过特权指令检测虚拟机 Vmware为真主机与 虚拟机之间提供了相互沟通的通讯机制,它使用“IN”指令来读取特定端口的数据以进行两机通讯,但由于IN指令属于特权指令,在处于保护模式下的真机上执行此指令时,除非权限允许,否则将会触发类型为“EXCEPTION_PRIV_INSTRUCTION”的异常,而在 虚拟机中并不会发生异常,在指定功能号0A(获取VMware版本)的情况下,它会在EBX中返回其版本号“VMXH”;而当功能号为0x14时,可用于获取VMware内存大小,当大于0时则说明处于 虚拟机中。VMDetect正是利用前一种方法来检测VMware的存在 ``` bool IsInsideVMWare() { bool rc = true; __try { __asm { push edx push ecx push ebx mov eax, 'VMXh' mov ebx, 0 // 将ebx设置为非幻数’VMXH’的其它值 mov ecx, 10 // 指定功能号,用于获取VMWare版本,当它为0x14时用于获取VMware内存大小 mov edx, 'VX' // 端口号 in eax, dx // 从端口dx读取VMware版本到eax //若上面指定功能号为0x14时,可通过判断eax中的值是否大于0,若是则说明处于虚拟机中 cmp ebx, 'VMXh' // 判断ebx中是否包含VMware版本’VMXh’,若是则在虚拟机中 setz [rc] // 设置返回值 pop ebx pop ecx pop edx } } __except(EXCEPTION_EXECUTE_HANDLER) //如果未处于VMware中,则触发此异常 { rc = false; } return rc; } ``` ### 利用IDT基址检测虚拟机 利用IDT基址检测 虚拟机的方法是一种通用方式,对VMware和Virtual PC均适用。中断描述符表IDT(Interrupt Descriptor Table)用于查找处理中断时所用的软件函数,它是一个由256项组成的数据,其中每一中断对应一项函数。为了读取IDT基址,我们需要通过SIDT指令来读取IDTR(中断描述符表寄存器,用于IDT在内存中的基址),SIDT指令是以如下格式来存储IDTR的内容 ``` typedef struct { WORD IDTLimit; // IDT的大小 WORD LowIDTbase; // IDT的低位地址 WORD HiIDTbase; // IDT的高位地址 } IDTINFO; ``` 由于只存在一个IDTR,但又存在两个操作系统,即 虚拟机系统和真主机系统。为了防止发生冲突,VMM( 虚拟机监控器)必须更改 虚拟机中的IDT地址,利用真主机与 虚拟机环境中执行sidt指令的差异即可用于检测 虚拟机是否存在。著名的“红丸”(redpill)正是利用此原理来检测VMware的。Redpill作者在VMware上发现 虚拟机系统上的IDT地址通常位于0xFFXXXXXX,而Virtual PC通常位于0xE8XXXXXX,而在真实主机都位于0x80xxxxxx。Redpill仅仅是通过判断执行SIDT指令后返回的第一字节是否大于0xD0,若是则说明它处于 虚拟机,否则处于真实主机中。 ``` #include <stdio.h> int main () { unsigned char m[2+4], rpill[] = "\x0f\x01\x0d\x00\x00\x00\x00\xc3"; //相当于SIDT[adrr],其中addr用于保存IDT地址 *((unsigned*)&rpill[3]) = (unsigned)m; //将sidt[addr]中的addr设为m的地址 ((void(*)())&rpill)(); //执行SIDT指令,并将读取后IDT地址保存在数组m中 printf ("idt base: %#x\n", *((unsigned*)&m[2])); //由于前2字节为IDT大小,因此从m[2]开始即为IDT地址 if (m[5]>0xd0) printf ("Inside Matrix!\n", m[5]); //当IDT基址大于0xd0xxxxxx时则说明程序处于VMware中 else printf ("Not in Matrix.\n"); return 0; } ``` 利用此IDT检测的方法存在一个缺陷,由于IDT的值只针对处于正在运行的处理器而言,在单CPU中它是个常量,但当它处于多CPU时就可能会受到影响了,因为每个CPU都有其自己的IDT,这样问题就自然而然的产生了。针对此问题,Offensive Computing组织成员提出了两种应对方法,其中一种方法就是利用Redpill反复地在系统上循环执行任务,以此构造出一张当前系统的IDT值变化统计图,但这会增加CPU负担;另一种方法就是windows API函数SetThreadAffinityMask()将线程限制在单处理器上执行,当执行此测试时只能准确地将线程执行环境限制在本地处理器,而对于将线程限制在VM处理器上就可能行不通了,因为VM是计划在各处理器上运行的,VM线程在不同的处理器上执行时,IDT值将会发生变化,因此此方法也是很少被使用的。为此,有人提出了使用LDT的检测方法,它在具有多个CPU的环境下检测 虚拟机明显优于IDT检测方法, ### 利用LDT和GDT 在保护模式下,所有的内存访问都要通过全局描述符表(GDT)或者本地描述符表(LDT)才能进行。这些表包含有段描述符的调用入口。各个段描述符都包含有各段的基址,访问权限,类型和使用信息,而且每个段描述符都拥有一个与之相匹配的段选择子,各个段选择子都为软件程序提供一个GDT或LDT索引(与之相关联的段描述符偏移量),一个全局/本地标志(决定段选择子是指向GDT还是LDT),以及访问权限信息。 若想访问段中的某一字节,必须同时提供一个段选择子和一个偏移量。段选择子为段提供可访问的段描述符地址(在GDT 或者LDT 中)。通过段描述符,处理器从中获取段在线性地址空间里的基址,而偏移量用于确定字节地址相对基址的位置。假定处理器在当前权限级别(CPL)可访问这个段,那么通过这种机制就可以访问在GDT 或LDT 中的各种有效代码、数据或者堆栈段,这里的CPL是指当前可执行代码段的保护级别。 GDT的线性基址被保存在GDT寄存器(GDTR)中,而LDT的线性基址被保存在LDT寄存器(LDTR)中。 由于 虚拟机与真实主机中的GDT和LDT并不能相同,这与使用IDT的检测方法一样,因此 虚拟机必须为它们提供一个“复制体”。关于GDT和LDT的基址可通过SGDT和SLDT指令获取。 虚拟机检测工具Scoopy suite的作者Tobias Klein经测试发现,当LDT基址位于0x0000(只有两字节)时为真实主机,否则为 虚拟机,而当GDT基址位于0xFFXXXXXX时说明处于 虚拟机中,否则为真实主机。具体实现代码如下: ``` #include <stdio.h> void LDTDetect(void) { unsigned short ldt_addr = 0; unsigned char ldtr[2]; _asm sldt ldtr ldt_addr = *((unsigned short *)&ldtr); printf("LDT BaseAddr: 0x%x\n", ldt_addr); if(ldt_addr == 0x0000) { printf("Native OS\n"); } else printf("Inside VMware\n"); } void GDTDetect(void) { unsigned int gdt_addr = 0; unsigned char gdtr[4]; _asm sgdt gdtr gdt_addr = *((unsigned int *)&gdtr[2]); printf("GDT BaseAddr:0x%x\n", gdt_addr); if((gdt_addr >> 24) == 0xff) { printf("Inside VMware\n"); } else printf("Native OS\n"); } int main(void) { LDTDetect(); GDTDetect(); return 0; } ``` 基于时间差检测虚拟机 利用IO端口检测虚拟机 ### 利用虚拟硬件指纹检测虚拟机 利用 虚拟硬件指纹也可用于检测 虚拟机的存在,比如VMware默认的网卡MAC地址前缀为“00-05-69,00-0C-29或者00-50-56”,这前3节是由VMware分配的唯一标识符OUI,以供它的 虚拟化适配器使用。在我的VMWare WinXP下的MAC地址为00-0C-29-5B-D7-67 00-0C-29开头的MAC地址其实已经相当于告诉了大家:我用的就是VMWare有本事你来搞我啊! 但由于这些可经过修改配置文件来绕过检测。另外,还可通过检测特定的硬件控制器,BIOS,USB控制器,显卡,网卡等特征字符串进行检测, C++代码 ``` 通过IOCTL_STORAGE_QUERY_PROPERTY typedef enum _STORAGE_QUERY_TYPE {PropertyStandardQuery = 0,PropertyExistsQuery,PropertyMaskQuery,PropertyQueryMaxDefined} STORAGE_QUERY_TYPE, *PSTORAGE_QUERY_TYPE; typedef enum _STORAGE_PROPERTY_ID {StorageDeviceProperty = 0,StorageAdapterProperty} STORAGE_PROPERTY_ID, *PSTORAGE_PROPERTY_ID; typedef struct _STORAGE_PROPERTY_QUERY { STORAGE_PROPERTY_ID PropertyId; STORAGE_QUERY_TYPE QueryType; UCHAR AdditionalParameters[1]; } STORAGE_PROPERTY_QUERY, *PSTORAGE_PROPERTY_QUERY; typedef struct _STORAGE_DEVICE_DESCRIPTOR { ULONG Version; ULONG Size; UCHAR DeviceType; UCHAR DeviceTypeModifier; BOOLEAN RemovableMedia; BOOLEAN CommandQueueing; ULONG VendorIdOffset; ULONG ProductIdOffset; } STORAGE_DEVICE_DESCRIPTOR, *PSTORAGE_DEVICE_DESCRIPTOR; #define IOCTL_STORAGE_QUERY_PROPERTY CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS) bool IsSandboxed() { HANDLE hPhysicalDriveIOCTL = 0; int j = 0,k = 0; char szModel[128],szBuffer[128]; char *szDrives[] = { "qemu", "virtual", "vmware", NULL }; hPhysicalDriveIOCTL = CreateFile ("\\\\.\\PhysicalDrive0", 0,FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,OPEN_EXISTING, 0, NULL); if (hPhysicalDriveIOCTL != INVALID_HANDLE_VALUE) { STORAGE_PROPERTY_QUERY query; DWORD cbBytesReturned = 0; memset ((void *) & query, 0, sizeof (query)); query.PropertyId = StorageDeviceProperty; memset (szBuffer, 0, sizeof (szBuffer)); memset (szModel, 0, sizeof (szModel)); if (DeviceIoControl(hPhysicalDriveIOCTL, IOCTL_STORAGE_QUERY_PROPERTY,& query,sizeof (query),& szBuffer,sizeof (szBuffer),& cbBytesReturned, NULL)){ STORAGE_DEVICE_DESCRIPTOR *descrip = (STORAGE_DEVICE_DESCRIPTOR*)&szBuffer; int pos = descrip->ProductIdOffset; int m = 0; for(int g = pos;szBuffer[g] != '\0';g++){ szModel[m++] = szBuffer[g]; } CharLowerBuff(szModel,strlen(szModel)); for (int i = 0; i < (sizeof(szDrives)/sizeof(LPSTR)) - 1; i++ ) { if (szDrives[i][0] != 0) { if(strstr(szModel,szDrives[i])) return TRUE; } } } CloseHandle (hPhysicalDriveIOCTL); } return FALSE; } ``` # 伪装VM ``` isolation.tools.getPtrLocation.disable = "TRUE" isolation.tools.setPtrLocation.disable = "TRUE" isolation.tools.getVersion.disable = "TRUE" isolation.tools.setVersion.disable = "TRUE" monitor_control.disable_directexec = "TRUE" monitor_control.disable_chksimd = "TRUE" monitor_control.disable_ntreloc = "TRUE" monitor_control.disable_selfmod = "TRUE" monitor_control.disable_reloc = "TRUE" monitor_control.disable_btinout = "TRUE" monitor_control.disable_btmemspace = "TRUE" monitor_control.disable_btpriv = "TRUE" monitor_control.disable_btseg = "TRUE" ``` 除此之外,还建议卸载VMware Tools。 最后修改:2021 年 03 月 30 日 © 转载自他站 赞 0 如果觉得我的文章对你有用,请随意赞赏