Anti VM专题
1、通过Mac地址检测虚拟机
一、课程目标
本节课主要学习如何通过检查网络适配器的MAC地址来检测虚拟机环境。虚拟机软件在创建虚拟网络适配器时通常会使用特定的OUI(Organizationally Unique Identifier)标识符,通过识别这些特征可以判断程序是否运行在虚拟机中。通过本课的学习,你将能够:
- 理解MAC地址的结构和组织唯一标识符(OUI)的概念
- 掌握获取系统网络适配器MAC地址的方法
- 学会识别虚拟机软件特有的MAC地址特征
- 实现基于MAC地址的虚拟机检测代码
- 了解该技术的局限性和绕过方法
二、名词解释表
| 名词 | 解释 |
|---|---|
| MAC地址 | Media Access Control Address,网络设备的物理地址 |
| OUI | Organizationally Unique Identifier,组织唯一标识符 |
| NIC | Network Interface Card,网络接口卡 |
| 虚拟机检测 | 识别程序是否运行在虚拟化环境中的技术 |
| GetAdaptersInfo | Windows API函数,用于获取网络适配器信息 |
| GetAdaptersAddresses | Windows API函数,用于获取网络适配器地址信息 |
| 虚拟化环境 | 通过软件模拟硬件环境的系统,如VMware、VirtualBox等 |
三、技术原理
3.1 MAC地址概述
MAC(Media Access Control)地址是网络设备的唯一标识符,由48位(6字节)组成,通常表示为12个十六进制数字,格式为XX-XX-XX-XX-XX-XX。
MAC地址分为两部分:
- OUI(前24位):由IEEE分配给设备制造商的唯一标识符
- 厂商特定部分(后24位):由制造商自行分配的设备标识符
3.2 虚拟机MAC地址特征
常见的虚拟机软件使用的OUI标识符:
- VMware:00-05-69、00-0C-29、00-1C-14、00-50-56
- VirtualBox:08-00-27
- Hyper-V:00-15-5D
- Parallels:00-1C-42
- Xen:00-16-3E
3.3 检测原理
通过枚举系统中的所有网络适配器,获取它们的MAC地址,并检查这些地址的OUI部分是否匹配已知的虚拟机软件标识符。
四、代码实现
4.1 基础MAC地址检测
#include <windows.h>
#include <iphlpapi.h>
#include <stdio.h>
#include <vector>
#pragma comment(lib, "IPHLPAPI.lib")
// 虚拟机OUI列表
struct VMOUI {
BYTE oui[3];
const char* vendor;
};
VMOUI g_vmOUIs[] = {
{{0x00, 0x05, 0x69}, "VMware"},
{{0x00, 0x0C, 0x29}, "VMware"},
{{0x00, 0x1C, 0x14}, "VMware"},
{{0x00, 0x50, 0x56}, "VMware"},
{{0x08, 0x00, 0x27}, "VirtualBox"},
{{0x00, 0x15, 0x5D}, "Hyper-V"},
{{0x00, 0x1C, 0x42}, "Parallels"},
{{0x00, 0x16, 0x3E}, "Xen"}
};
// 检查MAC地址是否属于虚拟机
BOOL IsVirtualMachineMAC(BYTE mac[6]) {
printf("检查MAC地址: %02X-%02X-%02X-%02X-%02X-%02X\n",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
// 检查是否为全零地址
if (mac[0] == 0 && mac[1] == 0 && mac[2] == 0 &&
mac[3] == 0 && mac[4] == 0 && mac[5] == 0) {
printf("检测到全零MAC地址,可能是虚拟机。\n");
return TRUE;
}
// 检查是否为广播地址
if (mac[0] == 0xFF && mac[1] == 0xFF && mac[2] == 0xFF &&
mac[3] == 0xFF && mac[4] == 0xFF && mac[5] == 0xFF) {
printf("检测到广播MAC地址。\n");
return FALSE;
}
// 检查OUI是否匹配虚拟机特征
for (int i = 0; i < sizeof(g_vmOUIs) / sizeof(g_vmOUIs[0]); i++) {
if (mac[0] == g_vmOUIs[i].oui[0] &&
mac[1] == g_vmOUIs[i].oui[1] &&
mac[2] == g_vmOUIs[i].oui[2]) {
printf("检测到%s虚拟机MAC地址特征。\n", g_vmOUIs[i].vendor);
return TRUE;
}
}
return FALSE;
}
// 基础MAC地址检测
BOOL DetectVMViaMACAddress() {
printf("=== 基础MAC地址检测 ===\n");
// 使用GetAdaptersInfo获取网络适配器信息
PIP_ADAPTER_INFO pAdapterInfo = NULL;
ULONG ulOutBufLen = 0;
// 第一次调用获取所需缓冲区大小
if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) != ERROR_BUFFER_OVERFLOW) {
printf("无法获取适配器信息大小。\n");
return FALSE;
}
// 分配内存
pAdapterInfo = (IP_ADAPTER_INFO*)malloc(ulOutBufLen);
if (pAdapterInfo == NULL) {
printf("内存分配失败。\n");
return FALSE;
}
// 第二次调用获取适配器信息
if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) != ERROR_SUCCESS) {
printf("获取适配器信息失败。\n");
free(pAdapterInfo);
return FALSE;
}
BOOL vmDetected = FALSE;
// 遍历所有适配器
PIP_ADAPTER_INFO pAdapter = pAdapterInfo;
while (pAdapter) {
printf("适配器名称: %s\n", pAdapter->AdapterName);
printf("描述: %s\n", pAdapter->Description);
printf("地址长度: %d\n", pAdapter->AddressLength);
// 检查MAC地址长度
if (pAdapter->AddressLength == 6) {
if (IsVirtualMachineMAC(pAdapter->Address)) {
vmDetected = TRUE;
}
} else {
printf("MAC地址长度不正确: %d\n", pAdapter->AddressLength);
}
printf("\n");
pAdapter = pAdapter->Next;
}
free(pAdapterInfo);
return vmDetected;
}
4.2 改进的MAC地址检测
// 使用GetAdaptersAddresses的改进版本
BOOL ImprovedVMMACDetection() {
printf("=== 改进版MAC地址检测 ===\n");
// 使用GetAdaptersAddresses获取更详细的网络适配器信息
PIP_ADAPTER_ADDRESSES pAddresses = NULL;
ULONG ulOutBufLen = 0;
// 第一次调用获取所需缓冲区大小
DWORD dwRetVal = GetAdaptersAddresses(AF_UNSPEC,
GAA_FLAG_INCLUDE_PREFIX,
NULL,
pAddresses,
&ulOutBufLen);
if (dwRetVal != ERROR_BUFFER_OVERFLOW) {
printf("无法获取适配器地址大小。\n");
return FALSE;
}
// 分配内存
pAddresses = (IP_ADAPTER_ADDRESSES*)malloc(ulOutBufLen);
if (pAddresses == NULL) {
printf("内存分配失败。\n");
return FALSE;
}
// 第二次调用获取适配器地址信息
dwRetVal = GetAdaptersAddresses(AF_UNSPEC,
GAA_FLAG_INCLUDE_PREFIX,
NULL,
pAddresses,
&ulOutBufLen);
if (dwRetVal != ERROR_SUCCESS) {
printf("获取适配器地址信息失败。\n");
free(pAddresses);
return FALSE;
}
BOOL vmDetected = FALSE;
// 遍历所有适配器
PIP_ADAPTER_ADDRESSES pCurrAddresses = pAddresses;
while (pCurrAddresses) {
printf("适配器名称: %ws\n", pCurrAddresses->FriendlyName);
printf("描述: %ws\n", pCurrAddresses->Description);
// 检查物理地址
if (pCurrAddresses->PhysicalAddressLength == 6) {
BYTE mac[6];
memcpy(mac, pCurrAddresses->PhysicalAddress, 6);
printf("MAC地址: %02X-%02X-%02X-%02X-%02X-%02X\n",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
if (IsVirtualMachineMAC(mac)) {
vmDetected = TRUE;
}
} else {
printf("物理地址长度不正确: %d\n", pCurrAddresses->PhysicalAddressLength);
}
// 检查适配器类型
switch (pCurrAddresses->IfType) {
case IF_TYPE_SOFTWARE_LOOPBACK:
printf("适配器类型: 回环适配器\n");
break;
case IF_TYPE_ETHERNET_CSMACD:
printf("适配器类型: 以太网适配器\n");
break;
default:
printf("适配器类型: %lu\n", pCurrAddresses->IfType);
break;
}
printf("操作状态: %lu\n", pCurrAddresses->OperStatus);
printf("\n");
pCurrAddresses = pCurrAddresses->Next;
}
free(pAddresses);
return vmDetected;
}
// 获取所有MAC地址
std::vector<std::vector<BYTE>> GetAllMACAddresses() {
std::vector<std::vector<BYTE>> macAddresses;
PIP_ADAPTER_ADDRESSES pAddresses = NULL;
ULONG ulOutBufLen = 0;
// 获取适配器地址信息
if (GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, NULL, pAddresses, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW) {
pAddresses = (IP_ADAPTER_ADDRESSES*)malloc(ulOutBufLen);
if (pAddresses != NULL) {
if (GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, NULL, pAddresses, &ulOutBufLen) == ERROR_SUCCESS) {
PIP_ADAPTER_ADDRESSES pCurrAddresses = pAddresses;
while (pCurrAddresses) {
if (pCurrAddresses->PhysicalAddressLength == 6) {
std::vector<BYTE> mac(6);
memcpy(&mac[0], pCurrAddresses->PhysicalAddress, 6);
macAddresses.push_back(mac);
}
pCurrAddresses = pCurrAddresses->Next;
}
}
free(pAddresses);
}
}
return macAddresses;
}
4.3 高级MAC地址检测技术
// 检查MAC地址的统计特征
BOOL AnalyzeMACStatistics() {
printf("=== MAC地址统计分析 ===\n");
auto macAddresses = GetAllMACAddresses();
if (macAddresses.empty()) {
printf("未找到有效的MAC地址。\n");
return FALSE;
}
printf("找到 %zu 个网络适配器。\n", macAddresses.size());
int vmCount = 0;
for (const auto& mac : macAddresses) {
if (IsVirtualMachineMAC(const_cast<BYTE*>(mac.data()))) {
vmCount++;
}
}
// 如果大部分适配器都是虚拟机特征,则很可能是虚拟机环境
if (vmCount > 0 && vmCount == macAddresses.size()) {
printf("所有网络适配器都具有虚拟机特征。\n");
return TRUE;
}
if (vmCount > 0) {
printf("发现 %d 个具有虚拟机特征的适配器。\n", vmCount);
return TRUE;
}
return FALSE;
}
// 检查MAC地址的熵值
double CalculateMACEntropy(const BYTE mac[6]) {
// 计算MAC地址的熵值,用于检测是否为随机生成的地址
int frequency[256] = {0};
for (int i = 0; i < 6; i++) {
frequency[mac[i]]++;
}
double entropy = 0.0;
for (int i = 0; i < 256; i++) {
if (frequency[i] > 0) {
double p = (double)frequency[i] / 6.0;
entropy -= p * log2(p);
}
}
return entropy;
}
// 检查MAC地址熵值异常
BOOL CheckMACAddressEntropy() {
printf("=== MAC地址熵值检查 ===\n");
auto macAddresses = GetAllMACAddresses();
for (const auto& mac : macAddresses) {
double entropy = CalculateMACEntropy(mac.data());
printf("MAC地址熵值: %.2f\n", entropy);
// 正常的MAC地址熵值通常在1.5-2.5之间
// 如果熵值过高,可能是随机生成的虚拟机地址
if (entropy > 3.0) {
printf("检测到异常高熵值MAC地址,可能是虚拟机。\n");
return TRUE;
}
}
return FALSE;
}
// 综合MAC地址检测
BOOL ComprehensiveMACDetection() {
printf("=== 综合MAC地址检测 ===\n");
BOOL result1 = DetectVMViaMACAddress();
BOOL result2 = ImprovedVMMACDetection();
BOOL result3 = AnalyzeMACStatistics();
BOOL result4 = CheckMACAddressEntropy();
return result1 || result2 || result3 || result4;
}
4.4 反虚拟机实现
// 简单的MAC地址反虚拟机检测
VOID SimpleMACAntiVM() {
if (ComprehensiveMACDetection()) {
printf("通过MAC地址检测到虚拟机环境!程序即将退出。\n");
ExitProcess(1);
}
}
// 多层次MAC地址检测
BOOL MultiLayerMACDetection() {
// 第一层:基础检测
if (DetectVMViaMACAddress()) {
return TRUE;
}
// 第二层:改进检测
if (ImprovedVMMACDetection()) {
return TRUE;
}
// 第三层:统计分析
if (AnalyzeMACStatistics()) {
return TRUE;
}
// 第四层:熵值检查
if (CheckMACAddressEntropy()) {
return TRUE;
}
return FALSE;
}
// 增强版反虚拟机检测
VOID EnhancedMACAntiVM() {
// 多次检测
for (int i = 0; i < 3; i++) {
if (MultiLayerMACDetection()) {
printf("第%d次MAC地址检测发现虚拟机环境!\n", i + 1);
// 随机化响应
int response = rand() % 4;
switch (response) {
case 0:
ExitProcess(0);
case 1:
printf("发生未知错误。\n");
Sleep(5000);
exit(1);
case 2:
// 执行错误指令
__debugbreak();
case 3:
// 进入无限循环
while (1) {
Sleep(1000);
}
}
}
// 随机延迟
Sleep(rand() % 100 + 50);
}
printf("MAC地址反虚拟机检测通过。\n");
}
4.5 绕过MAC地址检测的方法
// MAC地址检测绕过技术
class MACObfuscator {
public:
// 修改网络适配器MAC地址
static BOOL ChangeMACAddress() {
printf("修改网络适配器MAC地址...\n");
// 实际应用中需要管理员权限,并且可能被安全软件阻止
// 这里仅作为概念演示
return FALSE;
}
// 隐藏虚拟机特征的MAC地址
static BOOL HideVMCharacteristics() {
printf("隐藏虚拟机特征的MAC地址...\n");
// 可以通过修改注册表或驱动程序来隐藏虚拟机特征
return FALSE;
}
// 生成随机MAC地址
static BOOL GenerateRandomMAC() {
printf("生成随机MAC地址...\n");
// 生成看起来像真实设备的MAC地址
return FALSE;
}
};
// 综合绕过方法
VOID ComprehensiveMACBypass() {
// 修改MAC地址
MACObfuscator::ChangeMACAddress();
// 隐藏虚拟机特征
MACObfuscator::HideVMCharacteristics();
// 生成随机MAC地址
MACObfuscator::GenerateRandomMAC();
printf("MAC地址检测绕过完成。\n");
}
4.6 完整测试程序
#include <windows.h>
#include <iphlpapi.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <vector>
#include <cmath>
#pragma comment(lib, "IPHLPAPI.lib")
// 前面实现的函数声明
BOOL DetectVMViaMACAddress();
BOOL ImprovedVMMACDetection();
BOOL AnalyzeMACStatistics();
BOOL CheckMACAddressEntropy();
BOOL MultiLayerMACDetection();
// 显示网络适配器信息
VOID DisplayNetworkAdapterInfo() {
printf("=== 网络适配器信息 ===\n");
auto macAddresses = GetAllMACAddresses();
printf("系统中共有 %zu 个网络适配器。\n", macAddresses.size());
for (size_t i = 0; i < macAddresses.size(); i++) {
const auto& mac = macAddresses[i];
printf("适配器 %zu: %02X-%02X-%02X-%02X-%02X-%02X\n",
i + 1, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
printf("\n");
}
// 性能测试
VOID PerformanceTest() {
const int iterations = 5;
printf("=== 性能测试 (%d次调用) ===\n", iterations);
// 测试基础MAC地址检测
DWORD start = GetTickCount();
for (int i = 0; i < iterations; i++) {
DetectVMViaMACAddress();
Sleep(100);
}
DWORD basicTime = GetTickCount() - start;
// 测试改进版检测
start = GetTickCount();
for (int i = 0; i < iterations; i++) {
ImprovedVMMACDetection();
Sleep(100);
}
DWORD improvedTime = GetTickCount() - start;
printf("基础MAC地址检测耗时: %lu ms\n", basicTime);
printf("改进版MAC地址检测耗时: %lu ms\n", improvedTime);
printf("\n");
}
// 主程序
int main() {
srand((unsigned int)time(NULL));
printf("通过Mac地址检测虚拟机演示程序\n");
printf("==========================\n\n");
// 显示网络适配器信息
DisplayNetworkAdapterInfo();
// 基础MAC地址检测
DetectVMViaMACAddress();
// 改进版检测
ImprovedVMMACDetection();
// 统计分析
AnalyzeMACStatistics();
// 熵值检查
CheckMACAddressEntropy();
// 性能测试
PerformanceTest();
// 实际应用示例
printf("=== 反虚拟机检测 ===\n");
if (MultiLayerMACDetection()) {
printf("检测到虚拟机环境,执行反虚拟机措施。\n");
// 这里可以执行各种反虚拟机措施
// 为演示目的,我们只是显示信息而不真正退出
printf("(演示模式:不实际退出程序)\n");
} else {
printf("未检测到虚拟机环境,程序正常运行。\n");
MessageBoxW(NULL, L"MAC地址检测通过,程序正常运行", L"提示", MB_OK);
}
// 演示绕过方法
printf("\n=== 绕过演示 ===\n");
printf("执行MAC地址绕过...\n");
// ComprehensiveMACBypass(); // 注释掉以避免实际修改系统
printf("绕过完成后再次检测:\n");
if (MultiLayerMACDetection()) {
printf("仍然检测到虚拟机环境。\n");
} else {
printf("检测结果显示未发现虚拟机异常。\n");
}
return 0;
}
4.7 高级技巧和注意事项
// 抗干扰版本(防止简单的Hook)
BOOL AntiTamperMACDetection() {
// 多次调用并验证
BOOL results[3];
for (int i = 0; i < 3; i++) {
results[i] = MultiLayerMACDetection();
Sleep(10); // 简短延迟
}
// 检查结果一致性
for (int i = 1; i < 3; i++) {
if (results[i] != results[0]) {
// 结果不一致,可能是被干扰了
return TRUE; // 假设存在虚拟机环境
}
}
return results[0];
}
// 综合检测函数
BOOL ComprehensiveMACDetectionEnhanced() {
// 抗干扰检测
if (AntiTamperMACDetection()) {
return TRUE;
}
// 多层检测
if (MultiLayerMACDetection()) {
return TRUE;
}
return FALSE;
}
// 动态获取网络API地址(避免静态导入)
FARPROC GetDynamicNetworkAPIAddress(LPCSTR functionName) {
// 动态加载iphlpapi.dll
HMODULE hIpHlpApi = GetModuleHandle(L"iphlpapi.dll");
if (hIpHlpApi == NULL) {
return NULL;
}
// 获取函数地址
FARPROC pfn = GetProcAddress(hIpHlpApi, functionName);
return pfn;
}
// 检查网络API调用的完整性
BOOL ValidateNetworkAPICall() {
// 可以通过检查相关函数代码的完整性来验证未被修改
// 这需要更高级的技术,如代码校验和检查
return TRUE;
}
// 多线程环境下的MAC地址检测
BOOL MultiThreadMACDetection() {
printf("=== 多线程MAC地址检测 ===\n");
// 在多线程环境中进行检测可以增加检测的可靠性
return FALSE;
}
五、课后作业
-
基础练习:
- 在不同的虚拟机软件中测试MAC地址检测的准确性
- 研究更多虚拟机软件的OUI标识符
- 实现对MAC地址格式的完整验证
-
进阶练习:
- 实现一个完整的MAC地址行为监控器
- 研究如何通过API Hook绕过MAC地址检测
- 设计一个多层检测机制,结合MAC地址和其他反虚拟机技术
-
思考题:
- MAC地址检测方法有哪些明显的局限性?
- 如何提高MAC地址检测的准确性和隐蔽性?
- 现代虚拟机采用了哪些技术来对抗MAC地址检测?
-
扩展阅读:
- 研究IEEE OUI数据库和MAC地址分配机制
- 了解网络适配器虚拟化技术
- 学习现代反虚拟机技术