一、进程和库检测方法
虚拟环境启动一些特定的程序进程,这些进程在通常的主机操作系统中未执行。还有一些特定的模块被加载到进程地址空间中。
1. 检查特定正在运行的进程和加载的库
1.1 检查特定进程是否正在运行
代码语言:javascript复制check_process_is_running("vmtoolsd.exe");
bool check_process_is_running(const std::string& proc_name) {
HANDLE hSnapshot;
PROCESSENTRY32 pe = {};
pe.dwSize = sizeof(pe);
bool present = false;
hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapshot == INVALID_HANDLE_VALUE)
return false;
if (Process32First(hSnapshot, &pe)) {
do {
if (!StrCmpI(pe.szExeFile, proc_name.c_str())) {
present = true;
break;
}
} while (Process32Next(hSnapshot, &pe));
}
CloseHandle(hSnapshot);
return present;
}
1.2 检查进程地址空间中是否加载了特定的库
代码语言:javascript复制VOID loaded_dlls()
{
/* Some vars */
HMODULE hDll;
/* Array of strings of blacklisted dlls */
TCHAR* szDlls[] = {
_T("sbiedll.dll"),
_T("dbghelp.dll"),
_T("api_log.dll"),
_T("dir_watch.dll"),
_T("pstorec.dll"),
_T("vmcheck.dll"),
_T("wpespy.dll"),
};
WORD dwlength = sizeof(szDlls) / sizeof(szDlls[0]);
for (int i = 0; i < dwlength; i )
{
TCHAR msg[256] = _T("");
_stprintf_s(msg, sizeof(msg) / sizeof(TCHAR), _T("Checking if process loaded modules contains: %s "),
szDlls[i]);
/* Check if process loaded modules contains the blacklisted dll */
hDll = GetModuleHandle(szDlls[i]);
if (hDll == NULL)
print_results(FALSE, msg);
else
print_results(TRUE, msg);
}
}
1.3 检查特定库中是否存在特定函数
代码语言:javascript复制BOOL wine_exports()
{
/* Some vars */
HMODULE hKernel32;
/* Get kernel32 module handle */
hKernel32 = GetModuleHandle(_T("kernel32.dll"));
if (hKernel32 == NULL) {
print_last_error(_T("GetModuleHandle"));
return FALSE;
}
/* Check if wine_get_unix_file_name is exported by this dll */
if (GetProcAddress(hKernel32, "wine_get_unix_file_name") == NULL)
return FALSE;
else
return TRUE;
}
1.4 检查某些库是否可以加载,其他库是否可以加载
这种技术依赖于这样的假设,即在通常的系统中有一些可以加载的通用系统库 - 也有一些假的,它们不应该真正存在于通常的系统中。但是,在沙盒中,当尝试加载某些假库时,它们可能会被报告为已加载 - 这与普通主机上的情况不同。
换句话说,如果未加载通常在物理机存在(但未广泛使用)的系统库,则应用程序可能位于沙箱中。如果报告加载了假 DLL,那么它很可能是一个沙箱,因为这样的 DLL 不会加载到正常物理机器中。
代码语言:javascript复制bool Generic::CheckLoadedDLLs() const {
std::vector<std::string> real_dlls = {
"kernel32.dll",
"networkexplorer.dll",
"NlsData0000.dll"
};
std::vector<std::string> false_dlls = {
"NetProjW.dll",
"Ghofr.dll",
"fg122.dll"
};
HMODULE lib_inst;
for (auto& dll : real_dlls) {
lib_inst = LoadLibraryA(dll.c_str());
if (lib_inst == nullptr) {
return true;
}
FreeLibrary(lib_inst);
}
for (auto& dll : false_dlls) {
lib_inst = LoadLibraryA(dll.c_str());
if (lib_inst != nullptr) {
return true;
}
}
return false;
}
2.检查进程地址空间中是否存在特定工件(仅限沙盒)
代码语言:javascript复制BOOL AmISandboxied(LPVOID lpMinimumApplicationAddress, LPVOID lpMaximumApplicationAddress)
{
BOOL IsSB = FALSE;
MEMORY_BASIC_INFORMATION RegionInfo;
ULONG_PTR i, k;
SIZE_T Length = 0L;
i = (ULONG_PTR)lpMinimumApplicationAddress;
do {
NTSTATUS Status = NtQueryVirtualMemory(GetCurrentProcess(),
(PVOID)i,
MemoryBasicInformation,
&RegionInfo,
sizeof(MEMORY_BASIC_INFORMATION),
&Length);
if (NT_SUCCESS(Status)) {
if (((RegionInfo.AllocationProtect & PAGE_EXECUTE_READWRITE) == PAGE_EXECUTE_READWRITE) &&
((RegionInfo.State & MEM_COMMIT) == MEM_COMMIT)) {
for (k = i; k < i RegionInfo.RegionSize; k = sizeof(DWORD)) {
if (
(*(PDWORD)k == 'kuzt') ||
(*(PDWORD)k == 'xobs')
)
{
IsSB = TRUE;
break;
}
}
}
i = RegionInfo.RegionSize;
}
else {
i = 0x1000;
}
} while (i < (ULONG_PTR)lpMaximumApplicationAddress);
return IsSB;
}
二、网络检测方法
这个群体中的逃避技术在这个或那个意义上与网络有关。要么使用与网络相关的功能,要么检查网络参数 — 如果它们与通常的主机操作系统不同,则可能会检测到虚拟环境。
1.特定网络属性
不同虚拟环境的供应商为其产品硬编码一些值(MAC 地址)和名称(网络适配器)——因此,例如 可以通过检查相应对象的属性来检测环境。
1.1 检查 MAC 地址是否特定
代码语言:javascript复制int pafish_check_mac_vendor(char* mac_vendor) {
unsigned long alist_size = 0, ret;
ret = GetAdaptersAddresses(AF_UNSPEC, 0, 0, 0, &alist_size);
if (ret == ERROR_BUFFER_OVERFLOW) {
IP_ADAPTER_ADDRESSES* palist = (IP_ADAPTER_ADDRESSES*)LocalAlloc(LMEM_ZEROINIT, alist_size);
void* palist_free = palist;
if (palist) {
GetAdaptersAddresses(AF_UNSPEC, 0, 0, palist, &alist_size);
char mac[6] = { 0 };
while (palist) {
if (palist->PhysicalAddressLength == 0x6) {
memcpy(mac, palist->PhysicalAddress, 0x6);
if (!memcmp(mac_vendor, mac, 3)) {
LocalFree(palist_free);
return TRUE;
}
}
palist = palist->Next;
}
LocalFree(palist_free);
}
}
return FALSE;
}
代码语言:javascript复制BOOL check_mac_addr(TCHAR* szMac)
{
BOOL bResult = FALSE;
PIP_ADAPTER_INFO pAdapterInfo;
ULONG ulOutBufLen = sizeof(IP_ADAPTER_INFO);
pAdapterInfo = (PIP_ADAPTER_INFO)MALLOC(sizeof(IP_ADAPTER_INFO));
if (pAdapterInfo == NULL)
{
_tprintf(_T("Error allocating memory needed to call GetAdaptersinfo.n"));
return -1;
}
//对 GetAdaptersInfo 进行初始调用,以便在 ulOutBufLen 变量中获取必要的大小
if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW)
{
FREE(pAdapterInfo);
pAdapterInfo = (PIP_ADAPTER_INFO)MALLOC(ulOutBufLen);
if (pAdapterInfo == NULL) {
printf("Error allocating memory needed to call GetAdaptersinfon");
return 1;
}
}
if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_SUCCESS)
{
// 将给定的 mac 地址转换为多字节字符数组
CHAR szMacMultiBytes[4];
for (int i = 0; i < 4; i ) {
szMacMultiBytes[i] = (CHAR)szMac[i];
}
while (pAdapterInfo)
{
if (pAdapterInfo->AddressLength == 6 && !memcmp(szMacMultiBytes, pAdapterInfo->Address, 3))
{
bResult = TRUE;
break;
}
pAdapterInfo = pAdapterInfo->Next;
}
}
return bResult;
}
1.2 检查适配器名称是否特定
代码语言:javascript复制int pafish_check_adapter_name(char* name) {
unsigned long alist_size = 0, ret;
wchar_t aux[1024];
mbstowcs(aux, name, sizeof(aux) - sizeof(aux[0]));
ret = GetAdaptersAddresses(AF_UNSPEC, 0, 0, 0, &alist_size);
if (ret == ERROR_BUFFER_OVERFLOW) {
IP_ADAPTER_ADDRESSES* palist = (IP_ADAPTER_ADDRESSES*)LocalAlloc(LMEM_ZEROINIT, alist_size);
void* palist_free = palist;
if (palist) {
if (GetAdaptersAddresses(AF_UNSPEC, 0, 0, palist, &alist_size) == ERROR_SUCCESS) {
while (palist) {
if (wcsstr(palist->Description, aux)) {
LocalFree(palist_free);
return TRUE;
}
palist = palist->Next;
}
}
LocalFree(palist_free);
}
}
return FALSE;
}
代码语言:javascript复制BOOL check_adapter_name(TCHAR* szName)
{
BOOL bResult = FALSE;
PIP_ADAPTER_INFO pAdapterInfo;
ULONG ulOutBufLen = sizeof(IP_ADAPTER_INFO);
pAdapterInfo = (PIP_ADAPTER_INFO)MALLOC(sizeof(IP_ADAPTER_INFO));
if (pAdapterInfo == NULL)
{
_tprintf(_T("Error allocating memory needed to call GetAdaptersinfo.n"));
return -1;
}
// Make an initial call to GetAdaptersInfo to get the necessary size into the ulOutBufLen variable
if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW)
{
FREE(pAdapterInfo);
pAdapterInfo = (PIP_ADAPTER_INFO)MALLOC(ulOutBufLen);
if (pAdapterInfo == NULL) {
printf("Error allocating memory needed to call GetAdaptersinfon");
return 1;
}
}
if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_SUCCESS)
{
while (pAdapterInfo)
{
if (StrCmpI(ascii_to_wide_str(pAdapterInfo->Description), szName) == 0)
{
bResult = TRUE;
break;
}
pAdapterInfo = pAdapterInfo->Next;
}
}
return bResult;
}
1.3 检查提供商的网络共享名称是否特定
代码语言:javascript复制int vbox_network_share() {
unsigned long pnsize = 0x1000;
char provider[pnsize];
int retv = WNetGetProviderName(WNNC_NET_RDR2SAMPLE, provider, &pnsize);
if (retv == NO_ERROR) {
if (lstrcmpi(provider, "VirtualBox Shared Folders") == 0)
return TRUE;
else
return FALSE;
}
return FALSE;
}
2.检查网络是否属于安全边界
恶意软件向 https[:]//www.maxmind.com/geoip/v2.1/city/me 发出请求,通常需要某种身份验证或 API 密钥。为了满足这一要求,恶意软件通过将 HTTP 引用程序设置为 https[:]//www.maxmind.com/en/locate-my-ip-address 并将 User-Agent 设置为Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)此技巧允许示例检索有关运行它的计算机的 IP 地址的信息。
响应以 JSON 格式返回,包含有关国家/地区、城市以及与 IP 地址关联的组织的信息。如果在响应中发现一些“坏”字符串,恶意软件就会知道它是在某种安全边界/组织内启动的。
代码语言:javascript复制Amazon
anonymous
BitDefender
BlackOakComputers
Blue Coat
BlueCoat
Cisco
cloud
Data Center
DataCenter
DataCentre
dedicated
ESET, Spol
FireEye
ForcePoint
Fortinet
Hetzner
hispeed.ch
hosted
Hosting
Iron Port
IronPort
LeaseWeb
MessageLabs
Microsoft
MimeCast
NForce
Ovh Sas
Palo Alto
ProofPoint
Rackspace
security
Server
Strong Technologies
Trend Micro
TrendMicro
TrustWave
VMVault
Zscaler
3.基于网络验证名称结果的反仿真技术
最初,这种技术是为绕过AV检测而设计的。它本身不是一种逃避技术——相反,它在调用函数后滥用有趣的副作用。
主要思想是使用具有无效参数的 NetValidateName API 函数调用的确定结果作为服务器名称(例如“123”)来动态计算跳转地址。这种跳跃通常指向某些指令的中间,以绕过AV软件的启发式分析。但这种技术也(至少)有一个副作用。
如果在操作系统中设置了默认的 NetBIOS 设置(启用了基于 TCP/IP 的 NetBIOS),则返回代码始终等于 ERROR_BAD_NETPATH (0x35)。
如果 NetBIOS over TCP/IP 关闭,则返回代码ERROR_NETWORK_UNREACHABLE (0x4CF)。
因此,跳转地址的计算将不正确,并将导致样本崩溃。因此,此技术可用于在沙箱中中断仿真,其中基于 TCP/IP 的 NetBIOS 已关闭,以防止操作系统生成垃圾流量。注意:通过 TCP/IP 的 NetBIOS 已关闭,以便在通过 DNS 解析服务器 IP 时不生成其他网络请求。
代码语言:javascript复制void EntryPoint(void)
{
HANDLE NetApi32 = LoadLibraryW(L"netapi32.dll");
TD_NetValidateName NetValidateName = (TD_NetValidateName)GetProcAddress(NetApi32, "NetValidateName");
DWORD Result = NetValidateName(L"123", L"", L"", L"", 1);
__asm
{
call dword ptr ds : [GetLastError]
add eax, offset TrueEntryPoint
sub eax, 0xCB // ERROR_ENVVAR_NOT_FOUND
call eax
}
}
三、使用的 CPU 检测方法
该组中的技术使用特定的处理器指令来获取有关CPU的特定信息,或者执行预定义的指令序列,这些指令序列在通常的主机操作系统和虚拟环境中的行为不同。
1.通过 CPUID 指令检查供应商 ID 字符串
CPUID 指令是将处理器标识和功能信息返回给 EBX、ECX、EDX 的指令。接收到这些登记册的信息可用于识别供应商。
代码语言:javascript复制__declspec(naked) void get_cpuid_vendor(char* vendor_id) {
__asm {
; save non - volatile register
push ebx
; nullify output registers
xor ebx, ebx
xor ecx, ecx
xor edx, edx
; call cpuid with argument in EAX
mov eax, 0x40000000
cpuid
; store vendor_id ptr to destination
mov edi, vendor_id
; move string parts to destination
mov eax, ebx; part 1 of 3 from EBX
stosd
mov eax, ecx; part 2 of 3 from ECX
stosd
mov eax, edx; part 3 of 3 from EDX
stosd
; restore saved non - volatile register
pop ebx
; return from function
retn
}
}
2.检查是否通过 CPUID 指令在虚拟机管理程序中运行
检测程序是否在虚拟机监控程序中运行的另一种方法是以另一种方式使用 CPUID 指令。
EAX 不是将 EAX(CPU id 的参数)设置为 0x40000000,而是设置为 1。
当 EAX 设置为 1 时,将设置 ECX 中的第 31 位(CPUID 的返回值),表示程序正在虚拟机监控程序中运行。
代码语言:javascript复制__declspec(naked) bool is_run_in_hypervisor() {
__asm {
; nullify output register
xor ecx, ecx
; call cpuid with argument in EAX
mov eax, 1
cpuid
; set CF equal to 31st bit in ECX
bt ecx, 31
; set AL to the value of CF
setc al
; return from function
retn
}
}
3. 检查全局表位置:IDT/GDT/LDT
此技术不适用于最新的 VMware 版本(所有受影响的 Windows 版本)。但是,为了完整起见,此处对其进行了描述。
此技巧涉及查看指向通常在虚拟机上重新定位的关键操作系统表的指针。这就是所谓的“红色药丸”,最初由Joanna Rutkowska引入。
每个 CPU 有一个本地描述符表寄存器 (LDTR)、一个全局描述符表寄存器 (GDTR) 和一个中断描述符表寄存器 (IDTR)。当用户操作系统运行时,必须将它们移动到其他位置,以避免与主机发生冲突。
代码语言:javascript复制idt_vm_detect = ((get_idt_base() >> 24) == 0xff);
ldt_vm_detect = (get_ldt_base() == 0xdead0000);
gdt_vm_detect = ((get_gdt_base >> 24) == 0xff);
// sidt instruction stores the contents of the IDT Register
// (the IDTR which points to the IDT) in a processor register.
ULONG get_idt_base() {
UCHAR idtr[6];
#if defined (ENV32BIT)
_asm sidt idtr
#endif
return *((unsigned long*)&idtr[2]);
}
// sldt instruction stores the contents of the LDT Register
// (the LDTR which points to the LDT) in a processor register.
ULONG get_ldt_base() {
UCHAR ldtr[5] = "xefxbexadxde";
#if defined (ENV32BIT)
_asm sldt ldtr
#endif
return *((unsigned long*)&ldtr[0]);
}
// sgdt instruction stores the contents of the GDT Register
// (the GDTR which points to the GDT) in a processor register.
ULONG get_gdt_base() {
UCHAR gdtr[6];
#if defined (ENV32BIT)
_asm sgdt gdtr
#endif
return gdt = *((unsigned long*)&gdtr[2]);
}
4.通过执行非法指令检测环境(仅限虚拟电脑)
恶意软件执行非法指令,这些指令应该在实际CPU上生成异常,但在虚拟环境中正常执行 - 或以某种不同的方式执行。
代码语言:javascript复制push ebx
xor ebx, ebx
mov eax, 1
; the following 4 bytes below generate #ud exception
db 0x0F
db 0x3F
db 0x0D
db 0x00
test ebx, ebx
setz al
pop ebx
代码语言:javascript复制// Use this code to detect if Windows XP is running inside Virtual PC 2007
#include "stdafx.h"
#include "windows.h"
#include "stdio.h"
#define CONTEXT_ALL 0x1003F
int dummy(int);
unsigned long gf = 0;
int __cdecl Handler(EXCEPTION_RECORD* pRec, void* est, unsigned char* pContext, void* disp)
{
if (pRec->ExceptionCode == 0xC0000096) //Privileged instruction
{
//---------------------Installing the trick--------------------------------------
*(unsigned long*)(pContext) = CONTEXT_ALL;/*CONTEXT_DEBUG_REGISTERS|CONTEXT_FULL*/
*(unsigned long*)(pContext 0x4) = (unsigned long)(&dummy);
*(unsigned long*)(pContext 0x8) = (unsigned long)(&dummy);
*(unsigned long*)(pContext 0xC) = (unsigned long)(&dummy);
*(unsigned long*)(pContext 0x10) = (unsigned long)(&dummy);
*(unsigned long*)(pContext 0x14) = 0;
*(unsigned long*)(pContext 0x18) = 0x155; //Enable the four DRx On-Execute
//---------------------------------------------------------------------------------
(*(unsigned long*)(pContext 0xB8)) ;
return ExceptionContinueExecution;
}
else if (pRec->ExceptionCode == EXCEPTION_SINGLE_STEP)
{
if (gf == 1)
{
MessageBox(0, "Expected behavior (XP)", "waliedassar", 0);
ExitProcess(0);
}
gf ;
(*(unsigned long*)(pContext 0xC0)) |= 0x00010000; //Set the RF (Resume Flag)
return ExceptionContinueExecution;
}
return ExceptionContinueSearch;
}
int dummy(int x)
{
x = 0x100;
return x;
}
int main(int shitArg)
{
unsigned long ver_ = GetVersion();
unsigned long major = ver_ & 0xFF;
unsigned long minor = (ver_ >> 0x8) & 0xFF;
if (major == 0x05 & minor == 0x01) //Windows XP
{
unsigned long x = 0;
__asm
{
push offset Handler
push dword ptr fs : [0x0]
mov dword ptr fs : [0x0] , esp
STI; Triggers an exception(privileged instruction)
}
dummy(0xFF);
__asm
{
pop dword ptr fs : [0x0]
pop ebx
}
MessageBox(0, "Virtual PC 2007 detected (XP)", "waliedassar", 0);
}
return 0;
}
代码语言:javascript复制#include "stdafx.h"
#include "windows.h"
#include "stdio.h"
bool flag = false;
int __cdecl Handler(EXCEPTION_RECORD* pRec, void* est, unsigned char* pContext, void* disp)
{
if (pRec->ExceptionCode == 0xC000001D || pRec->ExceptionCode == 0xC000001E || pRec->ExceptionCode == 0xC0000005)
{
flag = true;
(*(unsigned long*)(pContext 0xB8)) = 5;
return ExceptionContinueExecution;
}
return ExceptionContinueSearch;
}
int main(int argc, char* argv[])
{
__asm
{
push offset Handler
push dword ptr fs : [0x0]
mov dword ptr fs : [0x0] , esp
}
flag = false;
__asm
{
__emit 0x0F
__emit 0xC7
__emit 0xC8
__emit 0x05
__emit 0x00
}
if (flag == false)
{
MessageBox(0, "VirtualPC detected", "waliedassar", 0);
}
__asm
{
pop dword ptr fs : [0x0]
pop eax
}
return 0;
}
6.通过 IN 指令检测环境 - 后门端口(仅限 VMware)
代码语言:javascript复制bool VMWare::CheckHypervisorPort() const {
bool is_vm = false;
__try {
__asm {
push edx
push ecx
push ebx
mov eax, 'VMXh'
mov ebx, 0
mov ecx, 10
mov edx, 'VX'
in eax, dx // <- key point is here
cmp ebx, 'VMXh'
setz[is_vm]
pop ebx
pop ecx
pop edx
}
}
__except (EXCEPTION_EXECUTE_HANDLER) {
is_vm = false;
}
return is_vm;
}
代码语言:javascript复制bool VMWare::CheckHypervisorPortEnum() const {
bool is_vm = false;
short ioports[] = { 'VX' , 'VY' };
short ioport;
for (short i = 0; i < _countof(ioports); i) {
ioport = ioports[i];
for (unsigned char cmd = 0; cmd < 0x2c; cmd) {
__try {
__asm {
push eax
push ebx
push ecx
push edx
mov eax, 'VMXh'
movzx ecx, cmd
mov dx, ioport
in eax, dx // <- key point is here
pop edx
pop ecx
pop ebx
pop eax
}
is_vm = true;
break;
}
__except (EXCEPTION_EXECUTE_HANDLER) {}
}
if (is_vm)
break;
}
return is_vm;
}
锦鲤安全
一个安全技术学习与工具分享平台
点分享
点收藏
点点赞
点在看