最近在调试一个病毒样本,外壳加了VMP3.5,虚拟化工具和反调试使得在原虚拟机系统上不是那么的方便。由于我使用的是OD,分析绕过的只有VMP的反调试,所以我选择寻求去虚拟化系统来减少工作量(嘿嘿)。如果你的Ollydbg或者操作环境已经能够绕过虚拟化检测,那么恭喜你,你很强。
虚拟化检测通过
隔壁网安的告诉我,用ollydbg的都是低端人士,高手人人都用IDA。虽然心有不甘,但是我觉得对于这种强加密虚拟化的病毒样本,用Ollydbg来分析跟踪相比于IDA来说会更加方便一些,即使两者不是一个量级。
0x00 虚拟化检测工具
虚拟化检测工具字如其名是为了检测该程序是否存在于虚拟化环境中,先对虚拟机进行检测,防止在动态分析样本时出现虚拟化异常。
这里我在吾爱破解论坛上找到份18年的比赛作品,从8个方面检测虚拟机环境:
- 查询
I/O
通信端口 - 通过
MAC
地址检测 CPUID
检测- 通过主板序列号、型号、系统盘所在磁盘名称等其他硬件信息
- 搜索特定进程检测
- 通过注册表检测
- 通过服务检测
- 文件路径检测
- 通过时间差检测
检测工具
0x01 去虚拟化思路
其中较为难解决的是CPUID和进程、服务的隐藏,其他可以通过修改设备信息做到。
CPUID
CPUID检测是大多数检测程序都会用到的,这里我们可想.vmx配置文件中添加
代码语言:javascript复制cpuid.1.eax = "00001111001010111111101111110011"
在绕VMP3.5的虚拟机检测时也需要在cpuid
上断点置eax
为0
,这里CPUID就算完成了,接下来我们需要对关键进程和服务进行隐藏。
进程和服务隐藏
简单的解决办法就是不安装VMtool,自行安装虚拟机系统所需要的驱动,实在需要VMTOOL的话,可以使用Hidetool
对其进程进行隐藏,这种已经烂大街了,需要的可以直接在百度上看看。如果要深入研究Ring0
,Ring1
和Ring3
,可以看下当年Github大佬的驱动级隐藏服务进程。
点我前去
实在找不到的话,可以通过加载启动他人的驱动文件底层过滤掉检测函数,达到绕过进程服务检测,不过这样的话需要对系统进行PatchGuard
(对于64位系统而言),绕过的工具和文章百度值得拥有。
0x02 配置系统并进行检测
配置好系统和文件后,一定要对虚拟机系统进行虚拟化检测,避免在调试跟踪时出现难以预料的问题:
GIF
很好,虚拟化检测全部绕过,目前该操作环境与真实环境基本无区别,后续可以较为安心地调试程序或者其他样本。
0x03 比赛作品的部分代码
代码语言:javascript复制//1.查询I/O通信端口
BOOL CheckVMWare1()
{
BOOL bResult = TRUE;
__try
{
__asm
{
push edx
push ecx
push ebx //保存环境
mov eax, 'VMXh'
mov ebx, 0 //将ebx清零
mov ecx, 10 //指定功能号,用于获取VMWare版本,为0x14时获取VM内存大小
mov edx, 'VX' //端口号
in eax, dx //从端口edx 读取VMware到eax
cmp ebx, 'VMXh' //判断ebx中是否包含VMware版本’VMXh’,若是则在虚拟机中
setz [bResult] //设置返回值
pop ebx //恢复环境
pop ecx
pop edx
}
}
__except (EXCEPTION_EXECUTE_HANDLER) //如果未处于VMware中,则触发此异常
{
bResult = FALSE;
}
return bResult;
}
//2.通过MAC地址检测
BOOL CheckVMWare2()
{
string strMac;
getMacAddr(strMac);
if (strMac == "00-05-69" || strMac == "00-0c-29" || strMac == "00-50-56")
{
return TRUE;
}
else
{
return FALSE;
}
}
//3.CPUID检测
BOOL CheckVMWare3()
{
DWORD dwECX = 0;
bool b_IsVM = true;
_asm
{
pushad;
pushfd;
mov eax, 1;
cpuid;
mov dwECX, ecx;
and ecx, 0x80000000;//取最高位
test ecx, ecx; //检测ecx是否为0
setz[b_IsVM]; //为零 (ZF=1) 时设置字节
popfd;
popad;
}
if (b_IsVM) //宿主机
{
return FALSE;
}
else //虚拟机
{
return TRUE;
}
}
//4.通过主板序列号、型号、系统盘所在磁盘名称等其他硬件信息
BOOL CheckVMWare4()
{
string table = "Win32_DiskDrive";
wstring wcol = L"Caption";
string ret;
ManageWMIInfo(ret, table, wcol);
if (ret.find("VMware") != string::npos)
{
return TRUE;
}
else
{
return FALSE;
}
}
//5.搜索特定进程检测
BOOL CheckVMWare5()
{
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(pe32);
HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);//拍摄快照
if (hProcessSnap == INVALID_HANDLE_VALUE)
{
return FALSE;
}
BOOL bMore = Process32First(hProcessSnap, &pe32); //遍历第一个进程
while (bMore)
{
if (wcscmp(pe32.szExeFile, L"vmtoolsd.exe") == 0) //注意此处要用wcscmp(pe32.szExeFile是 WCHAR*)
{
return TRUE;
}
bMore = Process32Next(hProcessSnap, &pe32); //遍历下一个进程
}
CloseHandle(hProcessSnap);
return FALSE;
}
//6.通过注册表检测
BOOL CheckVMWare6()
{
HKEY hkey;
if (RegOpenKey(HKEY_CLASSES_ROOT, L"\Applications\VMwareHostOpen.exe", &hkey) == ERROR_SUCCESS)
{
return TRUE; //RegOpenKey函数打开给定键,如果存在该键返回ERROR_SUCCESS
}
else
{
return FALSE;
}
}
//7.通过服务检测
BOOL CheckVMWare7()
{
int menu = 0;
SC_HANDLE SCMan = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE); //打开服务控制管理器
if (SCMan == NULL)
{
cout << GetLastError() << endl;
printf("OpenSCManager Eorror/n");
return -1;
}
LPENUM_SERVICE_STATUSA service_status;
DWORD cbBytesNeeded = NULL;
DWORD ServicesReturned = NULL;
DWORD ResumeHandle = NULL;
service_status = (LPENUM_SERVICE_STATUSA)LocalAlloc(LPTR, 1024 * 64);
bool ESS = EnumServicesStatusA(SCMan, //遍历服务
SERVICE_WIN32,
SERVICE_STATE_ALL,
(LPENUM_SERVICE_STATUSA)service_status,
1024 * 64,
&cbBytesNeeded,
&ServicesReturned,
&ResumeHandle);
if (ESS == NULL)
{
printf("EnumServicesStatus Eorror/n");
return -1;
}
for (int i = 0; i < ServicesReturned; i )
{
if (strstr(service_status[i].lpDisplayName, "Virtual Disk") != NULL || strstr(service_status[i].lpDisplayName, "VMware Tools") != NULL)
{
return TRUE;
}
}
CloseServiceHandle(SCMan);
return FALSE;
}
//文件路径检测
BOOL CheckVMWare8()
{
if (PathIsDirectory(L"C:\Program Files\VMware\") == 0)
{
return FALSE;
}
else
{
return TRUE;
}
}
//通过时间差检测
BOOL CheckVMWareTmp()
{
__asm
{
rdtsc //RDTSC指令将计算机启动以来的CPU运行周期数存放到EDX:EAX里面,其中EDX是高位,而EAX是低位。
xchg ebx, eax //测试此条指令运行时间
rdtsc
sub eax, ebx //时间差
cmp eax, 0xFF
jg detected
}
return FALSE;
detected:
return TRUE;
}