#include "generic.h" namespace SandboxEvasion { VEDetection* Generic::create_instance(const json_tiny &j) { return new Generic(j); } void Generic::CheckAllCustom() { bool d; std::pair report; std::string ce_name; ce_name = Config::cgen2s[Config::ConfigGeneric::SLEEP_DUMMY]; if (IsEnabled(ce_name, conf.get(ce_name + std::string(".") + Config::cg2s[Config::ConfigGlobal::ENABLED], ""))) { d = CheckSleepDummyPatch(); report = GenerateReportEntry(ce_name, json_tiny(conf.get(ce_name, pt::ptree())), d); log_message(LogMessageLevel::INFO, module_name, report.second, d ? RED : GREEN); } ce_name = Config::cgen2s[Config::ConfigGeneric::PERFORMANCE_COUNTER]; if (IsEnabled(ce_name, conf.get(ce_name + std::string(".") + Config::cg2s[Config::ConfigGlobal::ENABLED], ""))) { d = CheckPerformanceCounter(); report = GenerateReportEntry(ce_name, json_tiny(conf.get(ce_name, pt::ptree())), d); log_message(LogMessageLevel::INFO, module_name, report.second, d ? RED : GREEN); } ce_name = Config::cgen2s[Config::ConfigGeneric::DISK_SIZE]; if (IsEnabled(ce_name, conf.get(ce_name + std::string(".") + Config::cg2s[Config::ConfigGlobal::ENABLED], ""))) { d = CheckDiskSize(); report = GenerateReportEntry(ce_name, json_tiny(conf.get(ce_name, pt::ptree())), d); log_message(LogMessageLevel::INFO, module_name, report.second, d ? RED : GREEN); } ce_name = Config::cgen2s[Config::ConfigGeneric::DRIVE_SIZE]; if (IsEnabled(ce_name, conf.get(ce_name + std::string(".") + Config::cg2s[Config::ConfigGlobal::ENABLED], ""))) { d = CheckDriveSize(); report = GenerateReportEntry(ce_name, json_tiny(conf.get(ce_name, pt::ptree())), d); log_message(LogMessageLevel::INFO, module_name, report.second, d ? RED : GREEN); } ce_name = Config::cgen2s[Config::ConfigGeneric::DEVICE_NPF_NDIS]; if (IsEnabled(ce_name, conf.get(ce_name + std::string(".") + Config::cg2s[Config::ConfigGlobal::ENABLED], ""))) { d = CheckNDISFile(); report = GenerateReportEntry(ce_name, json_tiny(conf.get(ce_name, pt::ptree())), d); log_message(LogMessageLevel::INFO, module_name, report.second, d ? RED : GREEN); } ce_name = Config::cgen2s[Config::ConfigGeneric::MOUSE_ACTIVE]; if (IsEnabled(ce_name, conf.get(ce_name + std::string(".") + Config::cg2s[Config::ConfigGlobal::ENABLED], ""))) { d = CheckMouseActive(); report = GenerateReportEntry(ce_name, json_tiny(conf.get(ce_name, pt::ptree())), d); log_message(LogMessageLevel::INFO, module_name, report.second, d ? RED : GREEN); } ce_name = Config::cgen2s[Config::ConfigGeneric::RAM]; if (IsEnabled(ce_name, conf.get(ce_name + std::string(".") + Config::cg2s[Config::ConfigGlobal::ENABLED], ""))) { d = CheckRAM(); report = GenerateReportEntry(ce_name, json_tiny(conf.get(ce_name, pt::ptree())), d); log_message(LogMessageLevel::INFO, module_name, report.second, d ? RED : GREEN); } ce_name = Config::cgen2s[Config::ConfigGeneric::BIG_RAM_ALLOC]; if (IsEnabled(ce_name, conf.get(ce_name + "." + Config::cg2s[Config::ConfigGlobal::ENABLED], ""))) { d = CheckBigRAMAllocate(); report = GenerateReportEntry(ce_name, json_tiny(conf.get(ce_name, pt::ptree())), d); log_message(LogMessageLevel::INFO, module_name, report.second, d ? RED : GREEN); } ce_name = Config::cgen2s[Config::ConfigGeneric::SYSTEM_UPTIME]; if (IsEnabled(ce_name, conf.get(ce_name + std::string(".") + Config::cg2s[Config::ConfigGlobal::ENABLED], ""))) { d = CheckSystemUptime(); report = GenerateReportEntry(ce_name, json_tiny(conf.get(ce_name, pt::ptree())), d); log_message(LogMessageLevel::INFO, module_name, report.second, d ? RED : GREEN); } ce_name = Config::cgen2s[Config::ConfigGeneric::PROCESSORS_COUNT]; if (IsEnabled(ce_name, conf.get(ce_name + std::string(".") + Config::cg2s[Config::ConfigGlobal::ENABLED], ""))) { d = CheckNumberOfProcessors(); report = GenerateReportEntry(ce_name, json_tiny(conf.get(ce_name, pt::ptree())), d); log_message(LogMessageLevel::INFO, module_name, report.second, d ? RED : GREEN); } ce_name = Config::cgen2s[Config::ConfigGeneric::DNS_RESPONSE]; if (IsEnabled(ce_name, conf.get(ce_name + std::string(".") + Config::cg2s[Config::ConfigGlobal::ENABLED], ""))) { d = CheckDNSResponse(); report = GenerateReportEntry(ce_name, json_tiny(conf.get(ce_name, pt::ptree())), d); log_message(LogMessageLevel::INFO, module_name, report.second, d ? RED : GREEN); } ce_name = Config::cgen2s[Config::ConfigGeneric::TIME_TAMPERING]; if (IsEnabled(ce_name, conf.get(ce_name + std::string(".") + Config::cg2s[Config::ConfigGlobal::ENABLED], ""))) { d = CheckTimeTampering(ProcessWorkingMode::MASTER); report = GenerateReportEntry(ce_name, json_tiny(conf.get(ce_name, pt::ptree())), d); log_message(LogMessageLevel::INFO, module_name, report.second, d ? RED : GREEN); } ce_name = Config::cgen2s[Config::ConfigGeneric::MOUSE_RAW_ACTIVE]; if (IsEnabled(ce_name, conf.get(ce_name + std::string(".") + Config::cg2s[Config::ConfigGlobal::ENABLED], ""))) { d = CheckMouseRawActive(ProcessWorkingMode::MASTER); report = GenerateReportEntry(ce_name, json_tiny(conf.get(ce_name, pt::ptree())), d); log_message(LogMessageLevel::INFO, module_name, report.second, d ? RED : GREEN); } ce_name = Config::cgen2s[Config::ConfigGeneric::USER_INPUT_ACTIVITY]; if (IsEnabled(ce_name, conf.get(ce_name + "." + Config::cg2s[Config::ConfigGlobal::ENABLED], ""))) { d = CheckUserInputActivity(ProcessWorkingMode::MASTER); report = GenerateReportEntry(ce_name, json_tiny(conf.get(ce_name, pt::ptree())), d); log_message(LogMessageLevel::INFO, module_name, report.second, d ? RED : GREEN); } ce_name = Config::cgen2s[Config::ConfigGeneric::AUDIO_DEVICE_ABSENCE]; if (IsEnabled(ce_name, conf.get(ce_name + "." + Config::cg2s[Config::ConfigGlobal::ENABLED], ""))) { d = CheckAudioDeviceAbsence(); report = GenerateReportEntry(ce_name, json_tiny(conf.get(ce_name, pt::ptree())), d); log_message(LogMessageLevel::INFO, module_name, report.second, d ? RED : GREEN); } } bool Generic::CheckSystemUptime() const { // FIXME: make it configurable ? const DWORD uptime = 1000 * 60 * 12; // 12 minutes return GetTickCount() < uptime; } bool Generic::CheckRAM() const { MEMORYSTATUSEX ms = {}; ms.dwLength = sizeof(ms); // FIXME: make it configurable ? const DWORDLONG min_ram = 1024 * 1024 * 1024; // 1GB if (!GlobalMemoryStatusEx(&ms)) return false; return ms.ullTotalPhys < min_ram; } bool Generic::CheckBigRAMAllocate() const { void* mem = VirtualAllocEx(GetCurrentProcess(), nullptr, 0x279C6A13, MEM_COMMIT, PAGE_READWRITE); const bool rv = !mem; if (mem) { memset(mem, 0xBD, 0x279C6A13); VirtualFreeEx(GetCurrentProcess(), mem, 0, MEM_RELEASE); } return rv; } bool Generic::CheckDiskSize() const { HANDLE hDrive; GET_LENGTH_INFORMATION gli = {}; DWORD dwReturned; uint32_t min_disk_size_gb = 60; // FIXME: make it configurable ? hDrive = CreateFileA("\\\\.\\PhysicalDrive0", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); if (hDrive == INVALID_HANDLE_VALUE) return false; if (!DeviceIoControl(hDrive, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &gli, sizeof(gli), &dwReturned, NULL)) { CloseHandle(hDrive); return false; } CloseHandle(hDrive); return (gli.Length.QuadPart / (1024 * 1024 * 1024)) < min_disk_size_gb; } bool Generic::CheckDriveSize() const { ULARGE_INTEGER drive_size = {}; uint32_t min_disk_size_gb = 60; // FIXME: make it configurable ? if (GetDiskFreeSpaceExA("C:\\", NULL, &drive_size, NULL)) return (drive_size.QuadPart / (1024 * 1024 * 1024)) < min_disk_size_gb; return false; } bool Generic::CheckNDISFile() const { HANDLE hFile; const wchar_t ndis_wan_ip_fname[] = L"\\\\.\\NPF_NdisWanIp"; DWORD err; hFile = CreateFileW(ndis_wan_ip_fname, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (hFile == INVALID_HANDLE_VALUE) { err = GetLastError(); return err != ERROR_PATH_NOT_FOUND && err != ERROR_FILE_NOT_FOUND; } CloseHandle(hFile); return true; } bool Generic::CheckMouseActive() const { POINT pos_f, pos_s; const uint32_t timeout = 1000; // timeout in milliseconds const uint8_t tries = 5; for (uint8_t i = 0; i < tries; ++i) { GetCursorPos(&pos_f); Sleep(timeout); GetCursorPos(&pos_s); if ((pos_s.x - pos_f.x) || (pos_s.y - pos_f.y)) return false; } return true; } bool Generic::CheckSleepDummyPatch() const { DWORD tick_count_f, tick_count_s; const DWORD delay_ms = 900; // timeout in milliseconds tick_count_f = GetTickCount(); SleepEx(delay_ms, FALSE); tick_count_s = GetTickCount(); return (tick_count_s - tick_count_f) < (delay_ms - 50); } bool Generic::CheckPerformanceCounter() const { LARGE_INTEGER Frequency = { 0 }; LARGE_INTEGER StartingTime = { 0 }; LARGE_INTEGER EndingTime = { 0 }; LARGE_INTEGER ElapsedTimeMs = { 0 }; const DWORD delay_ms = 10000; // timeout in milliseconds const DWORD max_delta = 50; // delay in milliseconds QueryPerformanceFrequency(&Frequency); QueryPerformanceCounter(&StartingTime); // Activity to be timed Sleep(delay_ms); QueryPerformanceCounter(&EndingTime); ElapsedTimeMs.QuadPart = 1000ll * (EndingTime.QuadPart - StartingTime.QuadPart) / Frequency.QuadPart; // printf("Elapsed: %u. Waited: %u\n", ElapsedTimeMs.LowPart, delay_ms); return abs(static_cast(ElapsedTimeMs.LowPart) - static_cast(delay_ms)) > max_delta; } bool Generic::CheckNumberOfProcessors() const { const DWORD min_proc_count = 2; SYSTEM_INFO si = {}; if (get_number_of_processors() < min_proc_count) return true; GetSystemInfo(&si); return si.dwNumberOfProcessors < min_proc_count; } bool Generic::CheckDNSResponse() const { // Calling function DnsQuery to query Host or PTR records std::list ips; // FIXME: should it be configurable? std::map dns_r = { { "microsoft.com", 3 }, { "bbc.com", 3 }, { "amazon.com", 3 } }; bool sandbox_detected = false; unsigned char ip_addr[4]; for (auto &dns : dns_r) { ips = {}; if (!perform_dns_request(dns.first, ips)) continue; // perform check on number of received domains if (ips.size() < dns.second) { sandbox_detected = true; break; } // perform check on received IP-addresses for (auto &ip : ips) { memcpy(ip_addr, reinterpret_cast(&ip), sizeof(ip_addr)); if (!(((ip_addr[0] != 10) && (((ip_addr[0] != 0xac) || (ip_addr[1] <= 15)) || (ip_addr[1] >= 0x20))) && ((ip_addr[0] != 0xc0) || (ip_addr[1] != 0xa8)))) { sandbox_detected = true; break; } } } return sandbox_detected; } bool Generic::CheckTimeTampering(ProcessWorkingMode wm) const { switch (wm) { case ProcessWorkingMode::MASTER: return CheckTimeTamperingMaster(); case ProcessWorkingMode::SLAVE: return CheckTimeTamperingSlave(); default: return false; } } bool Generic::CheckTimeTamperingMaster() const { wchar_t app_params[] = L"--action --dtt"; PROCESS_INFORMATION pi = {}; if (!run_self_susp(app_params, &pi)) return false; ResumeThread(pi.hThread); // wait process for finish DWORD ec; do { if (!GetExitCodeProcess(pi.hProcess, &ec)) { TerminateProcess(pi.hProcess, 0xFF); CloseHandle(pi.hThread); CloseHandle(pi.hProcess); return false; } Sleep(100); } while (ec == STILL_ACTIVE); CloseHandle(pi.hThread); CloseHandle(pi.hProcess); return ec == 1; } bool Generic::CheckTimeTamperingSlave() const { const int delta = 5 * 1000; // 5 sec const int64_t k100NStoMSecs = 10000ll; bool sandboxDetected = false; const std::string host("google.com"); // FIXME: should be configurable? FILETIME ftLocalStart, ftLocalEnd, ftWebStart, ftWebEnd; GetSystemTimeAsFileTime(&ftLocalStart); if (!get_web_time(host, ftWebStart)) return false; int64_t totalMSec = 0; for (int i = 0; i < 10; ++i) { const int sleepSec = 1 + (rand() % 10); totalMSec += sleepSec * 1000; SleepEx(sleepSec * 1000, FALSE); } GetSystemTimeAsFileTime(&ftLocalEnd); if (!get_web_time(host, ftWebEnd)) return false; // PC's clock validation const int64_t localTimeDiff = std::abs(ftLocalEnd - ftLocalStart) / k100NStoMSecs; const int64_t webTimeDiff = std::abs(ftWebEnd - ftWebStart) / k100NStoMSecs; if (std::abs(localTimeDiff - webTimeDiff) > delta) sandboxDetected = true; // second check for proper sleep delay if (!sandboxDetected) { if (localTimeDiff < totalMSec) sandboxDetected = true; if (webTimeDiff < totalMSec) sandboxDetected = true; } return sandboxDetected; } bool Generic::CheckMouseRawActive(ProcessWorkingMode wm) { switch (wm) { case ProcessWorkingMode::MASTER: return CheckMouseRawActiveMaster(); case ProcessWorkingMode::SLAVE: return CheckMouseRawActiveSlave(); default: return false; } } bool Generic::CheckMouseRawActiveMaster() { wchar_t app_params[] = L"--action --mra"; PROCESS_INFORMATION pi = {}; const uint32_t timeout = 1000; // timeout in milliseconds const uint8_t tries = 10; if (!run_self_susp(app_params, &pi)) return false; ResumeThread(pi.hThread); // wait process for finish DWORD ec; for (uint8_t i = 0; i < tries; ++i) { GetExitCodeProcess(pi.hProcess, &ec); if (ec != STILL_ACTIVE) break; Sleep(timeout); } TerminateProcess(pi.hProcess, 0xFF); CloseHandle(pi.hThread); CloseHandle(pi.hProcess); return ec != 0; } bool Generic::CheckMouseRawActiveSlave() { MSG msg; HINSTANCE hInstance = GetModuleHandleA(NULL); if (MouseRawActiveRegisterClass(hInstance) == NULL) return false; if (MouseRawActiveInitInstance(hInstance) == FALSE) return false; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return false; } bool Generic::CheckUserInputActivity(ProcessWorkingMode wm) { switch (wm) { case ProcessWorkingMode::MASTER: return CheckUserInputActivityMaster(); case ProcessWorkingMode::SLAVE: return CheckUserInputActivitySlave(); default: return false; } } bool Generic::CheckUserInputActivityMaster() { wchar_t app_params[] = L"--action --user-input"; PROCESS_INFORMATION pi = {}; const uint32_t timeout = 1000; // timeout in milliseconds const uint8_t tries = 10; if (!run_self_susp(app_params, &pi)) return false; ResumeThread(pi.hThread); // wait process for finish DWORD ec; for (uint8_t i = 0; i < tries; ++i) { GetExitCodeProcess(pi.hProcess, &ec); if (ec != STILL_ACTIVE) break; Sleep(timeout); } TerminateProcess(pi.hProcess, 0xFF); CloseHandle(pi.hThread); CloseHandle(pi.hProcess); return ec != 0; } bool Generic::CheckUserInputActivitySlave() { int tries = 0xFFFAF99; LASTINPUTINFO lii = {}; int okTries = 0; DWORD lastTime = 0; while (1) { lii.cbSize = 8; lii.dwTime = 0; if (!GetLastInputInfo(&lii)) return true; if (lii.dwTime - lastTime >= 0x1F2) { ++okTries; lastTime = lii.dwTime; } SleepEx(0x11Bu, 0); if (okTries > 5) return false; if (!--tries) return true; } return true; } bool Generic::CheckAudioDeviceAbsence() const { return is_audio_device_absent(); } ATOM Generic::MouseRawActiveRegisterClass(HINSTANCE hInstance) { WNDCLASSEXA wcex; wcex.cbSize = sizeof(WNDCLASSEXA); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = Generic::MouseRawWndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = NULL; wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wcex.lpszMenuName = NULL; wcex.lpszClassName = mouse_raw_wnd_class.c_str(); wcex.hIconSm = NULL; return RegisterClassExA(&wcex); } BOOL Generic::MouseRawActiveInitInstance(HINSTANCE hInstance) { HWND hWnd; // hWnd = CreateWindowExA(WS_EX_TOOLWINDOW, mouse_raw_wnd_class.c_str(), "ABCD", 0, 0, 0, 680, 480, NULL, NULL, hInstance, NULL); hWnd = CreateWindowExA(WS_EX_TOOLWINDOW | WS_EX_TOPMOST, mouse_raw_wnd_class.c_str(), "ABCD", 0, 0, 0, 640, 480, NULL, NULL, hInstance, NULL); if (!hWnd) return FALSE; ShowWindow(hWnd, SW_SHOW); UpdateWindow(hWnd); // register raw input device RAWINPUTDEVICE Rid[1]; Rid[0].usUsagePage = 0x01; Rid[0].usUsage = 0x02; Rid[0].dwFlags = RIDEV_NOLEGACY; // adds HID mouse and also ignores legacy mouse messages Rid[0].hwndTarget = hWnd; return RegisterRawInputDevices(Rid, 1, sizeof(Rid[0])); } LRESULT CALLBACK Generic::MouseRawWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { PAINTSTRUCT ps; HDC hdc; const uint8_t total_tries = 20; const uint8_t real_after = 5; switch (message) { case WM_INPUT: { UINT dwSize; // puts("WM_INPUT"); GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &dwSize, sizeof(RAWINPUTHEADER)); LPBYTE lpb = new BYTE[dwSize]; if (lpb == NULL) return 0; if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &dwSize, sizeof(RAWINPUTHEADER)) != dwSize) { delete[] lpb; lpb = NULL; return 0; } RAWINPUT *raw = (RAWINPUT*)lpb; if (raw->header.dwType == RIM_TYPEMOUSE) { static LONG lastX = 0; static LONG lastY = 0; static uint8_t count = 0; static uint8_t tries = 0; ++tries; if (lastX != raw->data.mouse.lLastX && lastY != raw->data.mouse.lLastY) ++count; lastX = raw->data.mouse.lLastX; lastY = raw->data.mouse.lLastY; // not detected if (count >= real_after) { ExitProcess(0); } // detected if (tries >= total_tries) { ExitProcess(1); } } delete[] lpb; lpb = NULL; break; } case WM_PAINT: hdc = BeginPaint(hWnd, &ps); EndPaint(hWnd, &ps); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; } } // SandboxEvasion