一、硬件信息检测方法
虚拟环境模拟硬件设备并在其描述中留下特定的痕迹 - 可以查询这些痕迹并得出有关非主机操作系统的结论。
1.检查硬盘是否有特定名称
代码语言:javascript复制hDevs = SetupDiGetClassDevs(
&guid, // GUID_DEVCLASS(DEVINTERFACE)_DISKDRIVE
NULL,
NULL,
DIGCF_PRESENT);
SetupDiEnumDeviceInfo(
hDevsInfo,
0,
&devinfo); // PSP_DEVINFO_DATA
SetupDiGetDeviceRegistryProperty(
hDevs,
&devinfo,
SPDRP_FRIENDLYNAME,
&dword_1,
szFriendlyName, // HDD
dFriendlyNameSize,
&dword_2);
2.检查硬盘供应商 ID 是否具有特定值
代码语言:javascript复制bool GetHDDVendorId(std::string& outVendorId) {
HANDLE hDevice = CreateFileA(_T("\\.\PhysicalDrive0"),
0,
FILE_SHARE_READ | FILE_SHARE_WRITE,
0,
OPEN_EXISTING,
0,
0);
if (hDevice == INVALID_HANDLE_VALUE)
return false;
STORAGE_PROPERTY_QUERY storage_property_query = {};
storage_property_query.PropertyId = StorageDeviceProperty;
storage_property_query.QueryType = PropertyStandardQuery;
STORAGE_DESCRIPTOR_HEADER storage_descriptor_header = {};
DWORD BytesReturned = 0;
if (!DeviceIoControl(hDevice, IOCTL_STORAGE_QUERY_PROPERTY,
&storage_property_query, sizeof(storage_property_query),
&storage_descriptor_header, sizeof(storage_descriptor_header),
&BytesReturned, )) {
printf("DeviceIoControl() for size query failedn");
CloseHandle(hDevice);
return false;
}
if (!BytesReturned) {
CloseHandle(hDevice);
return false;
}
std::vector<char> buff(storage_descriptor_header.Size); //_STORAGE_DEVICE_DESCRIPTOR
if (!DeviceIoControl(hDevice, IOCTL_STORAGE_QUERY_PROPERTY,
&storage_property_query, sizeof(storage_property_query),
buff.data(), buff.size(), 0)) {
CloseHandle(hDevice);
return false;
}
CloseHandle(hDevice);
if (BytesReturned) {
STORAGE_DEVICE_DESCRIPTOR* device_descriptor = (STORAGE_DEVICE_DESCRIPTOR*)buff.data();
if (device_descriptor->VendorIdOffset)
outVendorId = &buff[device_descriptor->VendorIdOffset];
return true;
}
return false;
}
3.检查音频设备是否不存在
代码语言:javascript复制void AudioEvasion() {
PCWSTR wszfilterName = L"audio_device_random_name";
if (FAILED(CoInitialize(NULL)))
return;
IGraphBuilder *pGraph = nullptr;
if (FAILED(CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void**)&pGraph)))
return;
if (E_POINTER != pGraph->AddFilter(NULL, wszfilterName))
ExitProcess(-1);
IBaseFilter *pBaseFilter = nullptr;
CoCreateInstance(CLSID_AudioRender, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&pBaseFilter);
pGraph->AddFilter(pBaseFilter, wszfilterName);
IBaseFilter *pBaseFilter2 = nullptr;
pGraph->FindFilterByName(wszfilterName, &pBaseFilter2);
if (nullptr == pBaseFilter2)
ExitProcess(1);
FILTER_INFO info = { 0 };
pBaseFilter2->QueryFilterInfo(&info);
if (0 != wcscmp(info.achName, wszfilterName))
return;
IReferenceClock *pClock = nullptr;
if (0 != pBaseFilter2->GetSyncSource(&pClock))
return;
if (0 != pClock)
return;
CLSID clsID = { 0 };
pBaseFilter2->GetClassID(&clsID);
if (clsID.Data1 == 0)
ExitProcess(1);
if (nullptr == pBaseFilter2)
ExitProcess(-1);
IEnumPins *pEnum = nullptr;
if (0 != pBaseFilter2->EnumPins(&pEnum))
ExitProcess(-1);
if (0 == pBaseFilter2->AddRef())
ExitProcess(-1);
}
4.检查物理显示适配器的 IDirect3D9 接口
此方法检查实例化 IDirect3D9 接口时系统中存在的物理显示适配器。它适用于从Windows XP开始的所有Windows版本。
代码语言:javascript复制#include <d3d9.h>
void detect() {
typedef IDirect3D9* (WINAPI* PtrDirect3DCreate9)(UINT);
HMODULE d3d9lib = ::LoadLibraryA("d3d9");
if (!d3d9lib)
return;
PtrDirect3DCreate9 direct3DCreate9 = (PtrDirect3DCreate9)GetProcAddress(d3d9lib, "Direct3DCreate9");
if (!direct3DCreate9)
return;
IDirect3D9* direct3D9 = direct3DCreate9(D3D_SDK_VERSION);
if (!direct3D9)
return;
D3DADAPTER_IDENTIFIER9 adapterIdentifier;
const HRESULT hr = direct3D9->GetAdapterIdentifier(0, 0, &adapterIdentifier);
direct3D9->Release();
if (SUCCEEDED(hr)) {
printf("VendorId: 0x%xn", adapterIdentifier.VendorId);
printf("DeviceId: 0x%xn", adapterIdentifier.DeviceId);
printf("Driver: %sn", adapterIdentifier.Driver);
printf("Description: %sn", adapterIdentifier.Description);
}
}
二、固件表检测方法
操作系统使用的特殊内存区域包含特定的工件,如果操作系统在虚拟环境中运行。这些内存区域可能会使用不同的方法转储,具体取决于操作系统版本。
1.Windows Vista
代码语言:javascript复制SYSTEM_FIRMWARE_TABLE_INFORMATION *sfti =
(PSYSTEM_FIRMWARE_TABLE_INFORMATION)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, Length);
sfti->Action = SystemFirmwareTable_Get; // 1
sfti->ProviderSignature = 'FIRM';
sfti->TableID = 0xC0000;
sfti->TableBufferLength = Length;
// 初始化SYSTEM_FIRMWARE_TABLE_INFORMATION对象用作参数
// 系统信息调用方式如下,以便转储原始固件表:
NtQuerySystemInformation(
SystemFirmwareTableInformation, // 76
sfti,
Length,
&Length);
2.检查原始 SMBIOS 固件表中是否存在特定字符串
代码语言:javascript复制SYSTEM_FIRMWARE_TABLE_INFORMATION *sfti =
(PSYSTEM_FIRMWARE_TABLE_INFORMATION)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, Length);
sfti->Action = SystemFirmwareTable_Get; // 1
sfti->ProviderSignature = 'RSMB';
sfti->TableID = 0;
sfti->TableBufferLength = Length;
NtQuerySystemInformation(
SystemFirmwareTableInformation, // 76
sfti,
Length,
&Length);
三、钩子检测方法
此处描述的技术使用钩子来检测用户状态或作为检查是否安装了某些异常主机操作系统挂钩的方法。
1.检查系统功能内是否设置了钩子
恶意软件读取特定地址的内存,以检查 Windows API 函数是否挂钩。
此方法基于这样一个事实,即虚拟环境最有可能挂接这些函数,以便能够在仿真期间收集数据和统计信息。
代码语言:javascript复制HOOK_TYPE IsHooked(LPCVOID lpFuncAddress, DWORD_PTR *dwAddressOffset) {
LPCBYTE lpBytePtr = (LPCBYTE)lpFuncAddress;
if (lpBytePtr[0] == 0xE9) {
*dwAddressOffset = 1;
return HOOK_RELATIVE;
} else if (lpBytePtr[0] == 0x68 && lpBytePtr[5] == 0xC3) {
*dwAddressOffset = 1;
return HOOK_ABOLSUTE;
}
return HOOK_NONE;
}
LPVOID lpFunction = ...;
DWORD_PTR dwOffset = 0;
LPVOID dwHookAddress = 0;
HOOK_TYPE ht = IsHooked(lpFunction, &dwOffset);
if (ht == HOOK_ABSOLUTE) {
dwHookAddress = (LPVOID)(*(LPDWORD)((LPBYTE)lpFunction dwOffset));
} else if (ht == HOOK_RELATIVE) {
INT nJumpSize = (*(PINT)((LPBYTE)lpFunction dwOffset);
DWORD_PTR dwRelativeAddress = (DWORD_PTR)((LPBYTE)lpFunction dwOffset 4));
dwHookAddress = (LPVOID)(dwRelativeAddress nJumpSize);
}
2.通过鼠标钩子检查用户点击
恶意软件设置鼠标钩子以检测单击(或更多)是否发生。如果是这种情况,恶意软件会通常将主机视为主机,即最终用户在屏幕后面 - 而不是虚拟环境。如果未检测到鼠标单击,则很可能是虚拟环境。
代码语言:javascript复制HHOOK g_hhkMouseHook = NULL;
LRESULT CALLBACK mouseHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
switch (wParam)
{
case WM_MOUSEMOVE:
// ...
break;
case WM_NCLBUTTONDOWN:
// ...
break;
case WM_LBUTTONUP:
UnhookWindowsHookEx(g_hhkMouseHook);
CallMaliciousCode();
ExitProcess(0);
}
return CallNextHookEx(g_hhkMouseHook, nCode, wParam, lParam);
}
g_hhkMouseHook = SetWindowsHookEx(WH_MOUSE_LL, mouseHookProc, GetModuleHandleA(NULL), NULL);
代码语言:javascript复制std::thread t([]()
{
int count = 0;
while (true)
{
if (GetAsyncKeyState(VK_LBUTTON) || GetAsyncKeyState(VK_RBUTTON) || GetAsyncKeyState(VK_MBUTTON))
{
if ( count == 2)
break;
}
Sleep(100);
}
CallMaliciousCode();
});
t.join();
3.检查挂接不正确的函数
ntdll中有400多个本机API函数(或Nt函数.dll这些函数通常挂在沙箱中。在如此大的列表中,有足够的空间容纳不同类型的错误。检查了流行沙箱中的挂钩 Nt 函数 并发现了几个问题。
我们发现的另一个问题是钩子函数和原始函数中的参数数量存在差异。 如果函数挂接不正确,在内核模式下,这可能会导致操作系统崩溃。用户模式不正确 钩子没有那么关键。但是,它们可能会导致分析的应用程序崩溃或很容易检测到。 例如,让我们看一下 NtLoadKeyEx 函数。它最初是在Windows Server 2003中引入的,并且具有 只有 4 个参数。从Windows Vista到最新版本的Windows 10,它有8个参数。
代码语言:javascript复制; Exported entry 318. NtLoadKeyEx
; Exported entry 1450. ZwLoadKeyEx
; __stdcall NtLoadKeyEx(x, x, x, x, x, x, x, x)
public _NtLoadKeyEx@32
NtLoadKeyEx 声明仍然只有4个参数。
代码语言:javascript复制* POBJECT_ATTRIBUTES TargetKey
* POBJECT_ATTRIBUTES SourceFile
** ULONG Flags flags
** HANDLE TrustClassKey trust_class_key
我们发现这个遗留原型也用于其他来源。例如,CAPE 监视器具有相同的问题:
代码语言:javascript复制extern HOOKDEF(NTSTATUS, WINAPI, NtLoadKeyEx,
__in POBJECT_ATTRIBUTES TargetKey,
__in POBJECT_ATTRIBUTES SourceFile,
__in ULONG Flags,
__in_opt HANDLE TrustClassKey
);
因此,如果沙箱使用任何最新的 Windows 操作系统,则此函数的挂钩不正确。呼叫后不正确的挂钩函数,堆栈指针值变为无效。因此,对 RegLoadAppKeyW 函数的完全“合法”调用(调用 NtLoadKeyEx)会导致异常。这一事实可用于 只需调用一次 RegLoadAppKeyW 函数即可避开沙箱。
代码语言:javascript复制__try
{
_asm mov old_esp, esp
NtLoadKeyEx(&TargetKey, &SourceFile, 0, 0, 0, KEY_ALL_ACCESS, &hKey, &ioStatus);
_asm mov new_esp, esp
_asm mov esp, old_esp
if (old_esp != new_esp)
printf("Sandbox detected!");
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
printf("Sandbox detected!");
}
锦鲤安全
一个安全技术学习与工具分享平台
点分享
点收藏
点点赞
点在看