#include "helper.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "nt.h" #include #include #include #include #pragma comment(lib, "Shlwapi") #pragma comment(lib, "Iphlpapi") #pragma comment(lib, "ws2_32.lib") #pragma comment(lib, "wbemuuid.lib") #pragma comment(lib, "taskschd.lib") #pragma comment(lib, "Mstask.lib") #pragma comment(lib, "Dnsapi.lib") #pragma comment(lib, "Mpr.lib") #pragma comment(lib, "SetupAPI.lib") #pragma comment(lib, "Strmiids.lib") #define MAX_IDE_DRIVES 16 #define DFP_GET_VERSION 0x00074080 #define DFP_RECEIVE_DRIVE_DATA 0x0007c088 #define IDE_ATAPI_IDENTIFY 0xA1 // Returns ID sector for ATAPI. #define IDE_ATA_IDENTIFY 0xEC // Returns ID sector for ATA. using std::cout; using std::endl; std::map log_msg_levels = { { LogMessageLevel::NO, "" }, { LogMessageLevel::DBG, "DEBUG" }, { LogMessageLevel::INFO, "INFO" }, { LogMessageLevel::WARNING, "WARNING" }, { LogMessageLevel::ERR, "ERROR" }, { LogMessageLevel::PANIC, "PANIC" } }; std::map evasion_mode = { { EvasionMachineMode::REAL_PC, "Real Environment" }, { EvasionMachineMode::SANDBOX_CHLD_MON, "Child Monitored" }, { EvasionMachineMode::SANDBOX_EVADED, "Sandbox Evaded" }, { EvasionMachineMode::SANDBOX_NOT_EVADED, "Sandbox Not Evaded"} }; const std::map str2hkey = { { "HKCR", HKEY_CLASSES_ROOT }, { "HKCC", HKEY_CURRENT_CONFIG }, { "HKCU", HKEY_CURRENT_USER }, { "HKLM", HKEY_LOCAL_MACHINE }, { "HKUS", HKEY_USERS } }; bool g_verbose_mode = false; bool g_is_wow64 = false; RTL_OSVERSIONINFOW g_osver; void enable_verbose_mode() { g_verbose_mode = true; } void enable_wow64() { g_is_wow64 = true; } bool is_wow64() { return g_is_wow64; } HKEY get_hkey(const std::string &key) { const std::map::const_iterator it = str2hkey.find(key); return it == str2hkey.end() ? reinterpret_cast(INVALID_HKEY) : it->second; } void log_message(LogMessageLevel msg_l, const std::string &module, const std::string &msg, console_color_t cc) { HANDLE std_handle = INVALID_HANDLE_VALUE; if (!g_verbose_mode) return; if (log_msg_levels.find(msg_l) == log_msg_levels.end()) return; if (cc != DEFAULT) { std_handle = GetStdHandle(STD_OUTPUT_HANDLE); if (std_handle != INVALID_HANDLE_VALUE) { SetConsoleTextAttribute(std_handle, cc); } } if (msg_l == LogMessageLevel::NO) { if (module == "") cout << log_msg_levels[msg_l] << module << msg << std::endl; else cout << log_msg_levels[msg_l] << module << ": " << msg << std::endl; } else cout << "[" << log_msg_levels[msg_l] << "] " << module << ": " << msg << std::endl; if (cc != DEFAULT && std_handle != INVALID_HANDLE_VALUE) { SetConsoleTextAttribute(std_handle, FOREGROUND_INTENSITY); } } extern "C" BOOL cdtors(TORS_ROUTINE *p_ir, size_t ir_count) { size_t i; BOOL success = TRUE; BOOL tor_ret; if (!p_ir) return TRUE; // call all tors functions for (i = 0; i < ir_count && success; ++i) { if (!p_ir[i]) success &= FALSE; tor_ret = !reinterpret_cast(p_ir[i](NULL)); success &= tor_ret; } return success; } extern "C" BOOL ctors(TORS_ROUTINE *p_ir, size_t ir_count) { return cdtors(p_ir, ir_count); } extern "C" BOOL dtors(TORS_ROUTINE *p_ir, size_t ir_count) { return cdtors(p_ir, ir_count); } extern "C" LPVOID ctors_wsa(LPVOID arg) { WORD wVersionRequested; WSADATA wsaData; wVersionRequested = MAKEWORD(2, 2); if (WSAStartup(wVersionRequested, &wsaData)) { return (LPVOID)(1); } if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) { WSACleanup(); return (LPVOID)(1); } return NULL; } extern "C" LPVOID ctors_check_wow64(LPVOID arg) { FARPROC fpIsWow64Process = NULL; BOOL isWoW64 = FALSE; fpIsWow64Process = GetProcAddress(GetModuleHandleW(L"kernel32"), "IsWow64Process"); if (fpIsWow64Process && ((BOOL (WINAPI*)(HANDLE, PBOOL))fpIsWow64Process)(GetCurrentProcess(), &isWoW64) && isWoW64) enable_wow64(); return (LPVOID)(0); } /* * Code taken from VMDE project: https://github.com/hfiref0x/VMDE */ extern "C" LPVOID ctors_get_os_ver(LPVOID arg) { NTSTATUS Status; RtlSecureZeroMemory(&g_osver, sizeof(g_osver)); g_osver.dwOSVersionInfoSize = sizeof(g_osver); NTSTATUS(NTAPI *fnRtlGetVersion)(PRTL_OSVERSIONINFOW) = (NTSTATUS(NTAPI *)(PRTL_OSVERSIONINFOW))(GetProcAddress(GetModuleHandleW(L"ntdll"), "RtlGetVersion")); if (!fnRtlGetVersion) return (LPVOID)(1); Status = fnRtlGetVersion(&g_osver); if (NT_SUCCESS(Status)) { if (g_osver.dwMajorVersion < 6) { enable_privilege(SE_DEBUG_PRIVILEGE, TRUE); } return (LPVOID)(0); } return (LPVOID)(1); } extern "C" LPVOID dtors_wsa(LPVOID arg) { return reinterpret_cast(WSACleanup()); } extern "C" DWORD find_process_by_name(LPCSTR proc_name) { PROCESSENTRY32 entry; DWORD pid = INVALID_PID_VALUE; if (!proc_name) return pid; entry.dwSize = sizeof(PROCESSENTRY32); HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); if (Process32First(snapshot, &entry) == FALSE) { goto clean; } while (Process32Next(snapshot, &entry) == TRUE) if (!_stricmp(entry.szExeFile, proc_name)) { pid = entry.th32ProcessID; goto clean; } clean: CloseHandle(snapshot); return pid; } extern "C" HANDLE open_process_by_pid(DWORD pid, DWORD flags) { HANDLE hProcess; if (!(hProcess = OpenProcess(flags, FALSE, pid))) return INVALID_HANDLE_VALUE; return hProcess; } extern "C" HANDLE open_thread_by_tid(DWORD tid, DWORD flags) { HANDLE hThread; if (!(hThread = OpenThread(flags, FALSE, tid))) return INVALID_HANDLE_VALUE; return hThread; } extern "C" HANDLE open_process_by_name(LPCSTR proc_name, DWORD flags) { DWORD pid; pid = find_process_by_name(proc_name); if (pid == INVALID_PID_VALUE) return INVALID_HANDLE_VALUE; return open_process_by_pid(pid, flags); } extern "C" BOOL terminate_process(HANDLE proc) { if (proc == INVALID_HANDLE_VALUE) return FALSE; // FIXME: should be implemented return FALSE; } extern "C" BOOL check_current_parent_folder_w(const wchar_t *file_name) { if (!file_name) return FALSE; if (file_name[0] == '.') return file_name[1] == 0x00 || (file_name[1] == '.' && file_name[2] == 0x00); return FALSE; } /* * Responsible for enumerating recursively drive path */ extern "C" BOOL enumerate_directory_w(const wchar_t *path, SIZE_T max_rec_depth, SIZE_T rec_depth, FILE_ROUTINE proc_on_entry, PVOID proc_args, const std::list &filenames) { HANDLE hFile; WIN32_FIND_DATAW f_find_data; wchar_t full_path[MAX_PATH + 1] = { 0 }; wchar_t regexp[MAX_PATH * 2 + 1]; wchar_t file_path[MAX_PATH + 1]; BOOL found = FALSE; if (rec_depth > max_rec_depth) return FALSE; if (!PathCombineW(full_path, path, L"*.*")) return FALSE; hFile = FindFirstFileW(full_path, &f_find_data); if (hFile == INVALID_HANDLE_VALUE) return FALSE; do { if (check_current_parent_folder_w(f_find_data.cFileName)) continue; if (f_find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { if (!PathCombineW(full_path, path, f_find_data.cFileName)) continue; // TODO: should we call proc_on_entry for directory ??? if (enumerate_directory_w(full_path, max_rec_depth, rec_depth + 1, proc_on_entry, proc_args, filenames)) break; } // TODO: should we check other attributes ??? else { // check if file name matches at least one of the specified regexps pfi *ppfi = static_cast(proc_args); if (!ppfi) continue; found = FALSE; memset(file_path, 0, sizeof(file_path)); if (!PathCombineW(file_path, path, f_find_data.cFileName)) continue; for (auto &ri : filenames) { memset(regexp, 0, sizeof(regexp)); if (!PathCombineW(regexp, path, ri.c_str())) continue; // std::string re(regexp), fp(file_path); // TODO: escape charactes from regexp /* if (match_regexp(std::string(ri.c_str()), std::string(f_find_data.cFileName))) { found = TRUE; break; } */ if (std::wstring(f_find_data.cFileName) == ri) { found = TRUE; break; } } // no matching file found if (!found) continue; ppfi->matched = true; // check if we have any extra checks ? if (!proc_on_entry) break; ppfi->file_name = f_find_data.cFileName; // TODO: do we need short path and long path ??? proc_on_entry(proc_args); ppfi->file_name = NULL; if (ppfi->matched) break; } } while (FindNextFileW(hFile, &f_find_data)); FindClose(hFile); return found; } extern "C" LPCVOID inject_data(HANDLE hProcess, const data_t *data, SIZE_T code_size, DWORD protect) { LPVOID mem_chunk; DWORD dwNumberOfBytesWritten; if (code_size < 0) return NULL; // allocate memory + write data mem_chunk = VirtualAllocEx( hProcess, NULL, code_size, MEM_COMMIT | MEM_RESERVE, protect ); if (!mem_chunk) return NULL; if (WriteProcessMemory( hProcess, mem_chunk, data, code_size, &dwNumberOfBytesWritten ) == FALSE) return NULL; if (code_size != dwNumberOfBytesWritten) return NULL; // code was injected successfully return mem_chunk; } extern "C" LPCVOID inject_code(HANDLE hProcess, const code_t *code, SIZE_T code_size) { LPCVOID code_chunk; code_chunk = inject_data(hProcess, code, code_size, PAGE_EXECUTE_READ); if (!code_chunk) return NULL; return code_chunk; } extern "C" BOOL execute_code(HANDLE hProcess, LPTHREAD_START_ROUTINE start_addr, LPVOID args, HANDLE *phThread, DWORD dwCreationFlags) { HANDLE hThread; // just use stupid method ever CreateRemoteThread hThread = CreateRemoteThread( hProcess, NULL, 0, start_addr, args, dwCreationFlags, NULL ); if (phThread) *phThread = hThread; return hThread != 0; } extern "C" BOOL thread_context_execute_code(HANDLE hThread, LPTHREAD_START_ROUTINE routine, LPVOID args, BOOL suspended) { HMODULE hModule; FARPROC fpZwGetContextThread, fpZwResumeThread, fpZwSetContextThread; CONTEXT threadContext; hModule = GetModuleHandleW(L"ntdll"); if (!hModule) return FALSE; fpZwGetContextThread = GetProcAddress(hModule, "ZwGetContextThread"); if (!fpZwGetContextThread) return FALSE; fpZwSetContextThread = GetProcAddress(hModule, "ZwSetContextThread"); if (!fpZwSetContextThread) return FALSE; fpZwResumeThread = GetProcAddress(hModule, "ZwResumeThread"); if (!fpZwResumeThread) return FALSE; ZeroMemory(&threadContext, sizeof(threadContext)); threadContext.ContextFlags = CONTEXT_ALL; NTSTATUS s; const size_t max_retries = 10; size_t retries_count = 0; do { Sleep(10); s = reinterpret_cast(fpZwGetContextThread)(hThread, &threadContext); } while (s != STATUS_SUCCESS && s != STATUS_INVALID_PARAMETER && retries_count++ < max_retries); if (s != STATUS_SUCCESS) return FALSE; if (suspended) threadContext.Eax = reinterpret_cast(routine); else threadContext.Eip = reinterpret_cast(routine); if (reinterpret_cast(fpZwSetContextThread)(hThread, &threadContext) != STATUS_SUCCESS) return FALSE; if (reinterpret_cast(fpZwResumeThread)(hThread, NULL) != STATUS_SUCCESS) return FALSE; return TRUE; } /* * Retrieve specified environment variable * Achieved data should be freed later in case function succeeded */ bool get_envvar(const char *env, char **out) { if (!env | !out) return FALSE; char *envvar = NULL; size_t buff_size = 0; if (getenv_s(&buff_size, NULL, 0, env)) return FALSE; envvar = static_cast(calloc(buff_size + 1, sizeof(char))); if (!envvar) return FALSE; if (getenv_s(&buff_size, envvar, buff_size, env)) return FALSE; *out = envvar; return TRUE; } /* * Retrieve specified environment variable * Achieved data should be freed later in case function succeeded */ bool get_envvar_w(const wchar_t *env, wchar_t **out) { if (!env | !out) return FALSE; wchar_t *envvar = NULL; size_t buff_size = 0; if (_wgetenv_s(&buff_size, NULL, 0, env)) return FALSE; envvar = static_cast(calloc(buff_size + 1, sizeof(wchar_t))); if (!envvar) return FALSE; if (_wgetenv_s(&buff_size, envvar, buff_size, env)) return FALSE; *out = envvar; return TRUE; } /* * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * The contents of this file constitute Original Code as defined in and * are subject to the Apple Public Source License Version 1.1 (the * "License"). You may not use this file except in compliance with the * License. Please obtain a copy of the License at * http://www.apple.com/publicsource and read it before using this file. * * This Original Code and all software distributed under the License are * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Chris Torek. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ void* __memchr(const void *s, unsigned char c, size_t n) { if (n != 0) { const unsigned char *p = reinterpret_cast(s); do { if (*p++ == c) return ((void *)(p - 1)); } while (--n != 0); } return NULL; } // https://sourceware.org/ml/libc-alpha/2007-12/msg00000.html extern "C" unsigned char* __memmem(const unsigned char* haystack, size_t hlen, const unsigned char* needle, size_t nlen) { if (nlen > hlen) return 0; size_t i = 0, j = 0; switch (nlen) { // we have a few specialized compares for certain needle sizes case 0: // no needle? just give the haystack return const_cast(haystack); case 1: // just use memchr for 1-byte needle return reinterpret_cast(__memchr(reinterpret_cast(haystack), needle[0], hlen)); case 2: // use 16-bit compares for 2-byte needles for (i = 0; i < hlen - nlen + 1; ++i) { if (*reinterpret_cast(haystack + i) == *reinterpret_cast(needle)) { return const_cast(haystack + i); } } break; case 4: // use 32-bit compares for 4-byte needles for (i = 0; i < hlen - nlen + 1; ++i) { if (*reinterpret_cast(haystack + i) == *reinterpret_cast(needle)) { return const_cast(haystack + i); } } break; default: // generic compare for any other needle size // walk i through the haystack, matching j as long as needle[j] matches haystack[i] for (i = 0; i < hlen - nlen + 1; ++i) { if (haystack[i] == needle[j]) { if (j == nlen - 1) { // end of needle and it all matched? win. return const_cast(haystack + i - j); } else { // keep advancing j (and i, implicitly) j++; } } else { // no match, rewind i the length of the failed match (j), and reset j i -= j; j = 0; } } } return NULL; } extern "C" DWORD align_down(DWORD val, DWORD align) { if (!align) return val; if (val % align == 0) return val; return (val / align) * align; } extern "C" DWORD align_up(DWORD val, DWORD align) { if (!align) return val; if (val % align == 0) return val; return ((val / align) + 1) * align; } /* * Retrieve Ipv4 connection table handling current structure state * Return pointer to MIB_TCPTABLE. If pointer value is not NULL, then it should be freed */ extern "C" LPCVOID get_tcp_table() { PMIB_TCPTABLE pTcpTable; PMIB_TCPTABLE pTcpTable_old; DWORD dwSize; dwSize = sizeof(MIB_TCPTABLE); pTcpTable = reinterpret_cast(calloc(1, dwSize)); if (!pTcpTable) return NULL; // Make initial call in order to initialize Tcp Table if (GetTcpTable(pTcpTable, &dwSize, TRUE) == ERROR_INSUFFICIENT_BUFFER) { pTcpTable_old = pTcpTable; pTcpTable = reinterpret_cast(realloc(pTcpTable, dwSize)); if (!pTcpTable) { free(pTcpTable); return NULL; } } // get structures all tcp connections if (GetTcpTable(pTcpTable, &dwSize, TRUE) != NO_ERROR) { free(pTcpTable); return NULL; } return pTcpTable; } extern "C" char* hexlify(const unsigned char *data, size_t data_size) { size_t i; char *hex_data = reinterpret_cast(calloc((data_size * 2) + 1, sizeof(char))); if (!hex_data) return NULL; for (i = 0; i < data_size; ++i) { _snprintf_s(hex_data + (i * 2), data_size * 2 + 1, 3, "%.02x", data[i]); } return hex_data; } bool string_replace_substring(std::string &s, const std::string &what, const std::string &rep) { size_t i = s.find(what); if (i == s.npos) return false; s = s.substr(0, i) + rep + s.substr(i + what.length()); return true; } void get_tcp_entries(const MIB_TCPTABLE *p_tcp_table, network_endpoints_t &net_endpoints, DWORD state, bool remote) { size_t i; if (!p_tcp_table) return; for (i = 0; i < p_tcp_table->dwNumEntries; ++i) { if (p_tcp_table->table[i].dwState == state) { if(remote) net_endpoints.push_back(network_endpoint_t(p_tcp_table->table[i].dwRemoteAddr, p_tcp_table->table[i].dwRemotePort)); else net_endpoints.push_back(network_endpoint_t(p_tcp_table->table[i].dwLocalAddr, p_tcp_table->table[i].dwLocalPort)); } } return; } template bool match_regexp(const std::basic_string ®exp, const std::basic_string &str, std::vector> *matches) { std::basic_regex re(regexp, std::regex::ECMAScript | std::regex::icase); std::match_results::const_iterator> sm; if (!std::regex_match(str, sm, re)) return FALSE; if (matches) { for (size_t i = 1, e = sm.size(); i < e; ++i) matches->push_back(sm[i].str()); } return TRUE; } bool get_app_full_name(const wchar_t *app_params, wchar_t *app_name, size_t app_name_size, wchar_t *cur_dir, size_t cur_dir_size) { if (!GetCurrentDirectoryW(cur_dir_size, cur_dir)) return false; if (!GetModuleFileNameW(NULL, app_name, app_name_size)) return false; wcscat_s(app_name, app_name_size, L" "); if (app_params) wcscat_s(app_name, app_name_size, app_params); return true; } bool run_self_susp(const wchar_t *app_params, PROCESS_INFORMATION *ppi) { STARTUPINFOW si = {}; si.cb = sizeof(si); GetStartupInfoW(&si); wchar_t cur_dir[MAX_PATH + 1] = {}, app_name[MAX_PATH + 1] = {}; if (!get_app_full_name(app_params, app_name, _countof(app_name), cur_dir, _countof(cur_dir))) return false; // create process with parametres return !!CreateProcessW( NULL, app_name, NULL, NULL, TRUE, CREATE_SUSPENDED, NULL, cur_dir, &si, ppi ); } bool run_self_susp_wmi(const wchar_t *app_params, DWORD *ppid) { CComPtr wbemLocator; CComPtr wbemServices; CComPtr callResult; CComPtr oWin32Process; CComPtr oWin32ProcessStartup; CComPtr oMethCreate, oMethCreateSignature; CComPtr instWin32Process; CComPtr instWin32ProcessStartup; CComVariant varCreateFlags(CREATE_SUSPENDED); CComPtr pOutParams; wchar_t app_name[MAX_PATH + 1] = {}, cur_dir[MAX_PATH + 1] = {}; if (!ppid) return false; if (!get_app_full_name(app_params, app_name, _countof(app_name), cur_dir, _countof(cur_dir))) return false; CComVariant varCmdLine(app_name); CComVariant varCurDir(cur_dir); bool succ = false; HRESULT hres; do { // Initialize COM if (FAILED(CoInitializeEx(NULL, COINIT_MULTITHREADED))) break; // Set general COM security levels hres = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, 0, NULL); if (FAILED(hres) && hres != RPC_E_TOO_LATE) break; // create an instance of WbemLocator if (FAILED(CoCreateInstance(CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID*)&wbemLocator)) || !wbemLocator) break; // get services if (FAILED(wbemLocator->ConnectServer(CComBSTR("ROOT\\CIMV2"), NULL, NULL, NULL, 0, NULL, NULL, &wbemServices)) || !wbemServices) break; // set proxy blanket for services if (FAILED(CoSetProxyBlanket(wbemServices, RPC_C_AUTHN_WINNT, 0, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, 0))) break; // get Win32_Process object if (FAILED(wbemServices->GetObject(CComBSTR("Win32_Process"), 0, NULL, &oWin32Process, &callResult)) || !oWin32Process) break; // get Win32_ProcessStartup object if (FAILED(wbemServices->GetObject(CComBSTR("Win32_ProcessStartup"), 0, NULL, &oWin32ProcessStartup, &callResult)) || !oWin32ProcessStartup) break; if (FAILED(oWin32Process->GetMethod(CComBSTR("Create"), 0, &oMethCreate, &oMethCreateSignature)) || !oMethCreate) break; if (FAILED(oMethCreate->SpawnInstance(0, &instWin32Process)) || !instWin32Process) break; if (FAILED(oWin32ProcessStartup->SpawnInstance(0, &instWin32ProcessStartup)) || !instWin32ProcessStartup) break; // set startup information for process if (FAILED(instWin32ProcessStartup->Put(CComBSTR("CreateFlags"), 0, &varCreateFlags, 0))) break; if (FAILED(instWin32Process->Put(CComBSTR("CommandLine"), 0, &varCmdLine, 0))) break; if (FAILED(instWin32Process->Put(CComBSTR("CurrentDirectory"), 0, &varCurDir, 0))) break; CComVariant varStartupInfo(instWin32ProcessStartup); if (FAILED(instWin32Process->Put(CComBSTR("ProcessStartupInformation"), 0, &varStartupInfo, 0))) break; // start process if (FAILED(wbemServices->ExecMethod(CComBSTR("Win32_Process"), CComBSTR("Create"), 0, NULL, instWin32Process, &pOutParams, &callResult))) break; CComVariant pid(0); CIMTYPE pid_type(CIM_UINT32); // collect PID if (FAILED(pOutParams->Get(CComBSTR("ProcessId"), 0, &pid, &pid_type, NULL))) break; *ppid = reinterpret_cast(pid.puintVal); succ = true; } while (false); // TODO: implement cleanup // CoUninitialize(); return succ; } bool run_self_tsched(const wchar_t *app_params, DWORD *ppid) { DWORD major_version; major_version = (DWORD)(LOBYTE(LOWORD(GetVersion()))); if (major_version == 5) return run_self_tsched_xp_down(app_params, ppid); else if (major_version > 5) return run_self_tsched_vista_up(app_params, ppid); return false; } bool run_self_tsched_vista_up(const wchar_t *app_params, DWORD *ppid) { ITaskService *pService = NULL; ITaskFolder *pTaskRootFolder = NULL; ITaskDefinition *pTask = NULL; IRegistrationInfo *pRegInfo = NULL; IPrincipal *pPrincipal = NULL; ITaskSettings *pSettings = NULL; ITriggerCollection *pTriggerCollection = NULL; ITrigger *pTrigger = NULL; IRegistrationTrigger *pRegistrationTrigger = NULL; IActionCollection *pActionCollection = NULL; IAction *pAction = NULL; IExecAction *pExecAction = NULL; IRegisteredTask *pRegisteredTask = NULL; bool succ = false; wchar_t task_name[] = L"sandbox evasion tsrv"; wchar_t app_name[MAX_PATH + 1] = {}, cur_dir[MAX_PATH + 1] = {}; HRESULT hres; if (!ppid) return false; if (!get_app_full_name(NULL, app_name, _countof(app_name), cur_dir, _countof(cur_dir))) return false; do { // Initialize COM if (FAILED(CoInitializeEx(NULL, COINIT_MULTITHREADED))) break; // Set general COM security levels hres = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, 0, NULL); if (FAILED(hres) && hres != RPC_E_TOO_LATE) break; // create an instance of the Task Service if (FAILED(CoCreateInstance(CLSID_TaskScheduler, NULL, CLSCTX_INPROC_SERVER, IID_ITaskService, (void**)&pService))) break; // connect to task service if (FAILED(pService->Connect(_variant_t(), _variant_t(), _variant_t(), _variant_t()))) break; // get task root folder if (FAILED(pService->GetFolder(_bstr_t(L"\\"), &pTaskRootFolder))) break; // delete task if already exists pTaskRootFolder->DeleteTask(_bstr_t(task_name), 0); // create task object if (FAILED(pService->NewTask(0, &pTask))) break; // get task registration info if (FAILED(pTask->get_RegistrationInfo(&pRegInfo))) break; // set info if (FAILED(pRegInfo->put_Author(L"Sandbox Evasion"))) break; // create principal for the task if (FAILED(pTask->get_Principal(&pPrincipal))) break; // setup principal information pPrincipal->put_Id(_bstr_t(L"Principal Sandbox Evasion")); pPrincipal->put_LogonType(TASK_LOGON_INTERACTIVE_TOKEN); // run task with the least privileges if (FAILED(pPrincipal->put_RunLevel(TASK_RUNLEVEL_LUA))) break; // create settings for the task if (FAILED(pTask->get_Settings(&pSettings))) break; // set starting values for the task if (FAILED(pSettings->put_StartWhenAvailable(VARIANT_TRUE))) break; // get triggers collection if (FAILED(pTask->get_Triggers(&pTriggerCollection))) break; // add registration trigger to the task if (FAILED(pTriggerCollection->Create(TASK_TRIGGER_REGISTRATION, &pTrigger))) break; if (FAILED(pTrigger->QueryInterface(IID_IRegistrationTrigger, (void **)&pRegistrationTrigger))) break; pRegistrationTrigger->put_Id(_bstr_t(L"Trigger Sandbox Evasion")); if (FAILED(pRegistrationTrigger->put_Delay(_bstr_t(L"PT0S")))) break; // add action to created task if (FAILED(pTask->get_Actions(&pActionCollection))) break; if (FAILED(pActionCollection->Create(TASK_ACTION_EXEC, &pAction))) break; // get executable task pointer if (FAILED(pAction->QueryInterface(IID_IExecAction, (void**)&pExecAction))) break; // set path & working directory for executable if (FAILED(pExecAction->put_Path(_bstr_t(app_name)))) break; if (FAILED(pExecAction->put_Arguments(_bstr_t(app_params)))) break; if (FAILED(pExecAction->put_WorkingDirectory(_bstr_t(cur_dir)))) break; // save task in task root folder if (FAILED(pTaskRootFolder->RegisterTaskDefinition(_bstr_t(task_name), pTask, TASK_CREATE_OR_UPDATE, _variant_t(), _variant_t(), TASK_LOGON_INTERACTIVE_TOKEN, _variant_t(L""), &pRegisteredTask))) break; succ = true; } while (false); // Clean up if (pService) pService->Release(); if (pTaskRootFolder) pTaskRootFolder->Release(); if (pTask) pTask->Release(); if (pRegInfo) pRegInfo->Release(); if (pPrincipal) pPrincipal->Release(); if (pSettings) pSettings->Release(); if (pTriggerCollection) pTriggerCollection->Release(); if (pTrigger) pTrigger->Release(); if (pRegistrationTrigger) pRegistrationTrigger->Release(); if (pActionCollection) pActionCollection->Release(); if (pAction) pAction->Release(); if (pExecAction) pExecAction->Release(); if (pRegisteredTask) pRegisteredTask->Release(); CoUninitialize(); return succ; } bool run_self_tsched_xp_down(const wchar_t *app_params, DWORD *ppid) { ITaskScheduler *pTaskScheduler = NULL; ITask *pTask = NULL; IPersistFile *pPersistFile = NULL; ITrigger *pTrigger = NULL; ITaskTrigger *pTaskTrigger = NULL; WORD piNewTrigger; bool succ = false; wchar_t task_name[] = L"sandbox evasion tsched"; wchar_t app_name[MAX_PATH + 1] = {}, cur_dir[MAX_PATH + 1] = {}; HRESULT hres; if (!ppid) return false; if (!get_app_full_name(NULL, app_name, _countof(app_name), cur_dir, _countof(cur_dir))) return false; TASK_TRIGGER tt = {}; tt.wBeginDay = 1; tt.wBeginMonth = 1; tt.wBeginYear = 1900; tt.cbTriggerSize = 0x30; tt.MinutesDuration = -1; tt.TriggerType = TASK_TIME_TRIGGER_ONCE; do { // Initialize COM if (FAILED(CoInitializeEx(NULL, COINIT_MULTITHREADED))) break; // Set general COM security levels hres = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, 0, NULL); if (FAILED(hres) && hres != RPC_E_TOO_LATE) break; // create an instance of Task Scheduler if (FAILED(CoCreateInstance(CLSID_CTaskScheduler, NULL, CLSCTX_INPROC_SERVER, IID_ITaskScheduler, (void **)&pTaskScheduler))) break; // delete previous task pTaskScheduler->Delete(task_name); // create a new task if (FAILED(pTaskScheduler->NewWorkItem(task_name, CLSID_CTask, IID_ITask, (IUnknown**)&pTask))) break; // set trigger for task if (FAILED(pTask->CreateTrigger(&piNewTrigger, &pTaskTrigger))) break; if (FAILED(pTaskTrigger->SetTrigger(&tt))) break; // specify application & parametres to run if (FAILED(pTask->SetApplicationName(app_name))) break; if (FAILED(pTask->SetParameters(app_params))) break; if (FAILED(pTask->SetWorkingDirectory(cur_dir))) break; if (FAILED(pTask->SetFlags(TASK_FLAG_RUN_ONLY_IF_LOGGED_ON))) break; /* if (FAILED(pTask->SetApplicationName(L"notepad.exe"))) break; */ // save task to the disk if (FAILED(pTask->QueryInterface(IID_IPersistFile, (void **)&pPersistFile))) break; hres = pPersistFile->Save(NULL, TRUE); if (FAILED(hres)) break; hres = pTask->Run(); if (FAILED(hres)) break; // FIXME: remove Sleep HRESULT phrStatus; hres = pTask->GetStatus(&phrStatus); // fprintf(stdout, "{+} Task status: 0x%x\n", phrStatus); succ = true; } while (false); // cleanup if (pTaskScheduler) pTaskScheduler->Release(); if (pTask) pTask->Release(); if (pPersistFile) pPersistFile->Release(); if (pTrigger) pTrigger->Release(); if (pTaskTrigger) pTaskTrigger->Release(); CoUninitialize(); return succ; } bool pipe_server_get_pid(const wchar_t *pipe_name, uint32_t wait_timeout, DWORD *pid) { HANDLE hPipe; char buffer[sizeof(DWORD)] = {}; DWORD dwRead; DWORD dwTotalRead = 0; const uint32_t max_retries_count = 10; uint32_t retries_count; BOOL cnp; DWORD dwWritten = 0; const BOOL status = TRUE; BOOL write_status = FALSE; if (!max_retries_count) return false; if (!pid) return false; hPipe = CreateNamedPipeW( pipe_name, PIPE_ACCESS_DUPLEX, PIPE_NOWAIT | PIPE_TYPE_MESSAGE | PIPE_READMODE_BYTE, 1, sizeof(buffer), sizeof(buffer), wait_timeout, NULL ); if (hPipe == INVALID_HANDLE_VALUE) return false; // wait for connecting in non-blocking loop retries_count = 0; while (retries_count++ < max_retries_count) { cnp = ConnectNamedPipe(hPipe, NULL); if (cnp || (!cnp && GetLastError() == ERROR_PIPE_CONNECTED)) { // read input message packet dwTotalRead = 0; while (ReadFile(hPipe, buffer + dwTotalRead, sizeof(buffer) - dwTotalRead, &dwRead, NULL) && dwRead) dwTotalRead += dwRead; // write response back write_status = WriteFile(hPipe, &status, sizeof(status), &dwWritten, NULL); break; } Sleep(wait_timeout / max_retries_count); } DisconnectNamedPipe(hPipe); if (dwTotalRead != sizeof(buffer) || dwWritten != sizeof(BOOL) || !write_status) return false; memcpy(pid, buffer, sizeof(buffer)); return true; } bool pipe_server_send_pid(const wchar_t *pipe_name, uint32_t wait_timeout, DWORD pid) { char buffer_write[sizeof(DWORD)] = {}; BOOL buffer_read; DWORD dwRead; memcpy(buffer_write, &pid, sizeof(buffer_write)); return !!CallNamedPipeW(pipe_name, buffer_write, sizeof(buffer_write), &buffer_read, sizeof(buffer_read), &dwRead, NMPWAIT_USE_DEFAULT_WAIT); } bool get_parent_child_proc_pair(std::list &pc_proc, const std::list &proc_names) { PROCESSENTRY32 entry; bool ok = true; std::unordered_map cp_pids_m; std::unordered_map::const_iterator ci; DWORD pid; DWORD ppid, cpid; entry.dwSize = sizeof(PROCESSENTRY32); HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); if (Process32First(snapshot, &entry) == FALSE) { ok = false; goto clean; } while (Process32Next(snapshot, &entry) == TRUE) { pid = entry.th32ProcessID; // check if parent value is already present in our list value ci = std::find_if(cp_pids_m.begin(), cp_pids_m.end(), [&pid](const cp_pids& vt) { return vt.second == pid; }); // parent process is absent in parents if (ci == cp_pids_m.end()) { ci = cp_pids_m.find(entry.th32ParentProcessID); ppid = entry.th32ParentProcessID; cpid = entry.th32ProcessID; } else { ppid = ci->second; cpid = ci->first; } for (const auto &pn : proc_names) { if (strstr(entry.szExeFile, pn.c_str())) { // add parent to map cp_pids_m[entry.th32ProcessID] = entry.th32ParentProcessID; if (ci != cp_pids_m.end()) pc_proc.push_back(cp_pids(ppid, cpid)); break; } } } clean: CloseHandle(snapshot); return ok; } bool get_all_tids_by_pid(DWORD pid, std::vector &tids) { HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); THREADENTRY32 te; te.dwSize = sizeof(te); if (hSnapshot == INVALID_HANDLE_VALUE) return false; if (!Thread32First(hSnapshot, &te)) { CloseHandle(hSnapshot); return false; } do { if (te.th32OwnerProcessID == pid) tids.push_back(te.th32ThreadID); } while (Thread32Next(hSnapshot, &te)); CloseHandle(hSnapshot); return true; } bool check_regkey_exists(HKEY h_key, const std::string ®key) { HKEY h_regkey; if (RegOpenKeyExA(h_key, regkey.c_str(), 0, KEY_READ | (is_wow64() ? KEY_WOW64_64KEY : 0), &h_regkey) != ERROR_SUCCESS) return false; RegCloseKey(h_regkey); return true; } bool check_regkey_subkey_value(HKEY h_key, const std::string ®key, const std::string &value_name, const std::string &value_data, bool rec) { return rec ? check_regkey_subkey_value_rec(h_key, regkey, value_name, value_data) : check_regkey_subkey_value_nrec(h_key, regkey, value_name, value_data); } bool check_regkey_subkey_value_nrec(HKEY h_key, const std::string & regkey, const std::string & value_name, const std::string & value_data) { HKEY h_regkey; unsigned char regkey_buff[512] = {}; DWORD regkey_buff_size = sizeof(regkey_buff); if (RegOpenKeyExA(h_key, regkey.c_str(), 0, KEY_READ | (is_wow64() ? KEY_WOW64_64KEY : 0), &h_regkey) != ERROR_SUCCESS) return false; if (RegQueryValueExA(h_regkey, value_name.c_str(), NULL, NULL, regkey_buff, ®key_buff_size) != ERROR_SUCCESS) { RegCloseKey(h_regkey); return false; } RegCloseKey(h_regkey); return !!StrStrIA(reinterpret_cast(regkey_buff), value_data.c_str()); } bool check_regkey_subkey_value_rec(HKEY h_key, const std::string ®key, const std::string &value_name, const std::string &value_data) { HKEY h_regkey; LSTATUS status; DWORD i; char subkeyi[255] = {}; DWORD subkey_size; FILETIME ftLast; unsigned char regkey_buff[512] = {}; DWORD regkey_buff_size = sizeof(regkey_buff); if (RegOpenKeyExA(h_key, regkey.c_str(), 0, KEY_READ | (is_wow64() ? KEY_WOW64_64KEY : 0), &h_regkey) != ERROR_SUCCESS) return false; // check if there is value we want exists regkey_buff_size = sizeof(regkey_buff); if (RegQueryValueExA(h_regkey, value_name.c_str(), NULL, NULL, regkey_buff, ®key_buff_size) == ERROR_SUCCESS) { if (StrStrIA(reinterpret_cast(regkey_buff), value_data.c_str())) { RegCloseKey(h_regkey); return true; } } i = 0; do { subkey_size = _countof(subkeyi); status = RegEnumKeyExA(h_regkey, i++, subkeyi, &subkey_size, NULL, NULL, NULL, &ftLast); if (status == ERROR_SUCCESS) { if (check_regkey_subkey_value_rec(h_regkey, subkeyi, value_name, value_data)) { RegCloseKey(h_regkey); return true; } } } while (status == ERROR_SUCCESS); RegCloseKey(h_regkey); return false; } bool check_regkey_enum_keys(HKEY h_key, const std::string &key, const std::string &subkey) { HKEY h_regkey; LSTATUS status; DWORD i; char subkeyi[255] = {}; DWORD subkey_size; FILETIME ftLast; bool found; if (RegOpenKeyExA(h_key, key.c_str(), 0, KEY_READ | (is_wow64() ? KEY_WOW64_64KEY : 0), &h_regkey) != ERROR_SUCCESS) return false; i = 0; do { subkey_size = _countof(subkeyi); status = RegEnumKeyExA(h_regkey, i++, subkeyi, &subkey_size, NULL, NULL, NULL, &ftLast); if (status == ERROR_SUCCESS) if (StrStrIA(subkeyi, subkey.c_str())) { found = true; break; } } while (status == ERROR_SUCCESS); RegCloseKey(h_regkey); return found; } bool check_regkey_enum_values(HKEY h_key, const std::string &key, const std::string &value) { /* HKEY h_regkey; LSTATUS status; DWORD i; char value_name[16383] = {}; DWORD value_size; FILETIME ftLast; bool found; if (RegOpenKeyExA(h_key, key.c_str(), 0, KEY_READ | (is_wow64() ? KEY_WOW64_64KEY : 0), &h_regkey) != ERROR_SUCCESS) return false; i = 0; do { value_size = _countof(value_name); status = RegEnumKeyExA(h_regkey, i++, value_name, &value_size, NULL, NULL, NULL, &ftLast); if (status == ERROR_SUCCESS) if (StrStrIA(value_name, value.c_str())) { found = true; break; } } while (status == ERROR_SUCCESS); RegCloseKey(h_regkey); return found; */ // TODO: implement return false; } bool get_display_adapter_settings(std::map &settings) { typedef IDirect3D9* (WINAPI* PtrDirect3DCreate9)(UINT); HMODULE d3d9lib = ::LoadLibraryA("d3d9"); if (!d3d9lib) return false; PtrDirect3DCreate9 direct3DCreate9 = (PtrDirect3DCreate9)GetProcAddress(d3d9lib, "Direct3DCreate9"); if (!direct3DCreate9) return false; IDirect3D9* direct3D9 = direct3DCreate9(D3D_SDK_VERSION); if (!direct3D9) return false; D3DADAPTER_IDENTIFIER9 adapterIdentifier; const HRESULT hr = direct3D9->GetAdapterIdentifier(0, 0, &adapterIdentifier); direct3D9->Release(); if (SUCCEEDED(hr)) { std::stringstream vendorIdStr; vendorIdStr << "0x" << std::hex << adapterIdentifier.VendorId; settings["VendorId"] = vendorIdStr.str(); std::stringstream deviceIdStr; deviceIdStr << "0x" << std::hex << adapterIdentifier.DeviceId; settings["DeviceId"] = deviceIdStr.str(); settings["Driver"] = adapterIdentifier.Driver; settings["Description"] = adapterIdentifier.Description; return true; } return false; } bool check_file_exists(const file_name_t &fname) { if (!is_wow64()) return GetFileAttributesA(fname.c_str()) != INVALID_FILE_ATTRIBUTES; PVOID pOld = NULL; disable_wow64_fs_redirection(&pOld); bool present = GetFileAttributesA(fname.c_str()) != INVALID_FILE_ATTRIBUTES; revert_wow64_fs_redirection(pOld); return present; } bool check_device_exists(const file_name_t &devname) { HANDLE hDevice; if ((hDevice = CreateFileA(devname.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) != INVALID_HANDLE_VALUE) { CloseHandle(hDevice); return true; } return false; } bool disable_wow64_fs_redirection(PVOID pOld) { FARPROC fnWow64DisableWow64FsRedirection; fnWow64DisableWow64FsRedirection = GetProcAddress(GetModuleHandleW(L"kernel32"), "Wow64DisableWow64FsRedirection"); return fnWow64DisableWow64FsRedirection && reinterpret_cast(fnWow64DisableWow64FsRedirection)(&pOld); } bool revert_wow64_fs_redirection(PVOID pOld) { FARPROC fnWow64RevertWow64FsRedirection; fnWow64RevertWow64FsRedirection = GetProcAddress(GetModuleHandleW(L"kernel32"), "Wow64RevertWow64FsRedirection"); return fnWow64RevertWow64FsRedirection && reinterpret_cast(fnWow64RevertWow64FsRedirection)(pOld); } bool check_process_is_running(const process_name_t &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; } bool get_running_process_list(std::list &procList) { HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hSnapshot == INVALID_HANDLE_VALUE) return false; PROCESSENTRY32W pe = {sizeof(PROCESSENTRY32W), }; if (Process32FirstW(hSnapshot, &pe)) { do { std::wstring wproc = pe.szExeFile; std::transform(wproc.begin(), wproc.end(), wproc.begin(), towlower); procList.emplace_back(wproc); } while (Process32NextW(hSnapshot, &pe)); } CloseHandle(hSnapshot); return true; } bool check_mac_vendor(const std::string &ven_id) { // vendor id contains 3 bytes => if (ven_id.length() != 3 * 2) return false; unsigned char vendor_id[3] = {}; std::string s; char *p = NULL; IP_ADAPTER_ADDRESSES *pl, *hpl; bool found = false; for (unsigned char i = 0; i < _countof(vendor_id); ++i) { s = ""; s += ven_id[i * 2]; s += ven_id[i * 2 + 1]; vendor_id[i] = static_cast(strtol(s.c_str(), &p, 16)); if (!p || *p != 0) return false; } // check if mac id is one of forbidden pl = get_adapters_addresses(); if (!pl) return false; hpl = pl; do { if (pl->PhysicalAddressLength == 6) { if (!memcmp(pl->PhysicalAddress, vendor_id, sizeof(vendor_id))) { found = true; break; } } pl = pl->Next; } while (pl); free(hpl); return found; } bool check_adapter_name(const std::string &adapter_name) { bool found = false; IP_ADAPTER_ADDRESSES *pl, *hpl; std::wstring adapter_name_w; adapter_name_w.assign(adapter_name.begin(), adapter_name.end()); pl = get_adapters_addresses(); if (!pl) return false; hpl = pl; do { if (StrStrIW(pl->Description, adapter_name_w.c_str())) { found = true; break; } pl = pl->Next; } while (pl); free(hpl); return found; } PIP_ADAPTER_ADDRESSES get_adapters_addresses() { ULONG size = 0; IP_ADAPTER_ADDRESSES *l; // get size for the structure if (GetAdaptersAddresses(AF_UNSPEC, 0, 0, 0, &size) != ERROR_BUFFER_OVERFLOW) return NULL; l = reinterpret_cast(calloc(size, sizeof(char))); if (!l) return NULL; if (GetAdaptersAddresses(AF_UNSPEC, 0, 0, l, &size) != ERROR_SUCCESS) { free(l); return NULL; } return l; } /* * Source code taken from VMDE project: https://github.com/hfiref0x/VMDE */ extern "C" BOOL enable_privilege(DWORD PrivilegeName, BOOL fEnable) { BOOL bResult = FALSE; NTSTATUS status; HANDLE hToken; TOKEN_PRIVILEGES TokenPrivileges; HMODULE hNtdll; hNtdll = GetModuleHandleW(L"ntdll"); NTSTATUS(NTAPI *fnNtOpenProcessToken)(HANDLE, ACCESS_MASK, PHANDLE) = (NTSTATUS(NTAPI *)(HANDLE, ACCESS_MASK, PHANDLE))(GetProcAddress(hNtdll, "NtOpenProcessToken")); NTSTATUS(NTAPI *fnNtAdjustPrivilegesToken)(HANDLE, BOOLEAN, PTOKEN_PRIVILEGES, ULONG, PTOKEN_PRIVILEGES, PULONG) = (NTSTATUS(NTAPI *)(HANDLE, BOOLEAN, PTOKEN_PRIVILEGES, ULONG, PTOKEN_PRIVILEGES, PULONG))(GetProcAddress(hNtdll, "NtAdjustPrivilegesToken")); NTSTATUS(NTAPI *fnZwClose)(HANDLE) = (NTSTATUS(NTAPI *)(HANDLE))(GetProcAddress(hNtdll, "ZwClose")); if (!fnNtOpenProcessToken || !fnNtAdjustPrivilegesToken || !fnZwClose) return FALSE; status = fnNtOpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken); if (!NT_SUCCESS(status)) { return bResult; } TokenPrivileges.PrivilegeCount = 1; TokenPrivileges.Privileges[0].Luid.LowPart = PrivilegeName; TokenPrivileges.Privileges[0].Luid.HighPart = 0; TokenPrivileges.Privileges[0].Attributes = (fEnable) ? SE_PRIVILEGE_ENABLED : 0; status = fnNtAdjustPrivilegesToken(hToken, FALSE, &TokenPrivileges, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES)NULL, NULL); if (status == STATUS_NOT_ALL_ASSIGNED) { status = STATUS_PRIVILEGE_NOT_HELD; } bResult = NT_SUCCESS(status); fnZwClose(hToken); return bResult; } /* * Source code taken from VMDE project: https://github.com/hfiref0x/VMDE */ extern "C" PVOID get_firmware_table(PULONG pdwDataSize, DWORD dwSignature, DWORD dwTableID) { NTSTATUS Status; ULONG Length; HANDLE hProcess = NULL; ULONG uAddress; SYSTEM_FIRMWARE_TABLE_INFORMATION *sfti = NULL; SIZE_T memIO = 0; CLIENT_ID cid; OBJECT_ATTRIBUTES attr; MEMORY_REGION_INFORMATION memInfo; HMODULE hNtdll; hNtdll = GetModuleHandleW(L"ntdll"); NTSTATUS(NTAPI *fnNtQuerySystemInformation)(SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG) = (NTSTATUS(NTAPI *)(SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG))(GetProcAddress(hNtdll, "NtQuerySystemInformation")); NTSTATUS(NTAPI *fnZwQueryVirtualMemory)(HANDLE, PVOID, MEMORY_INFORMATION_CLASS, PVOID, SIZE_T, PSIZE_T) = (NTSTATUS(NTAPI *)(HANDLE, PVOID, MEMORY_INFORMATION_CLASS, PVOID, SIZE_T, PSIZE_T))(GetProcAddress(hNtdll, "ZwQueryVirtualMemory")); NTSTATUS(NTAPI *fnZwOpenProcess)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, PCLIENT_ID) = (NTSTATUS(NTAPI *)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, PCLIENT_ID))(GetProcAddress(hNtdll, "ZwOpenProcess")); NTSTATUS(NTAPI *fnNtReadVirtualMemory)(HANDLE, PVOID, PVOID, ULONG, PULONG) = (NTSTATUS(NTAPI *)(HANDLE, PVOID, PVOID, ULONG, PULONG))(GetProcAddress(hNtdll, "NtReadVirtualMemory")); NTSTATUS(NTAPI *fnZwClose)(HANDLE) = (NTSTATUS(NTAPI *)(HANDLE))(GetProcAddress(hNtdll, "ZwClose")); ULONG(NTAPI *fnCsrGetProcessId)() = (ULONG(NTAPI *)())(GetProcAddress(hNtdll, "CsrGetProcessId")); if (!fnNtQuerySystemInformation || !fnZwQueryVirtualMemory || !fnZwOpenProcess || !fnNtReadVirtualMemory || !fnZwClose || !fnCsrGetProcessId) return NULL; // Use documented GetSystemFirmwareTable instead, this is it raw implementation. if (g_osver.dwMajorVersion > 5) { Length = 0x1000; sfti = (PSYSTEM_FIRMWARE_TABLE_INFORMATION)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, Length); if (sfti != NULL) { sfti->Action = SystemFirmwareTable_Get; sfti->ProviderSignature = dwSignature; sfti->TableID = dwTableID; sfti->TableBufferLength = Length; // Query if info class available and if how many memory we need. Status = fnNtQuerySystemInformation(SystemFirmwareTableInformation, sfti, Length, &Length); if ( (Status == STATUS_INVALID_INFO_CLASS) || (Status == STATUS_INVALID_DEVICE_REQUEST) || (Status == STATUS_NOT_IMPLEMENTED) || (Length == 0) ) { HeapFree(GetProcessHeap(), 0, sfti); return NULL; } if ((!NT_SUCCESS(Status)) || (Status == STATUS_BUFFER_TOO_SMALL)) { HeapFree(GetProcessHeap(), 0, sfti); sfti = (PSYSTEM_FIRMWARE_TABLE_INFORMATION)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, Length); if (sfti != NULL) { sfti->Action = SystemFirmwareTable_Get; sfti->ProviderSignature = dwSignature; sfti->TableID = dwTableID; sfti->TableBufferLength = Length; Status = fnNtQuerySystemInformation(SystemFirmwareTableInformation, sfti, Length, &Length); if (!NT_SUCCESS(Status)) { HeapFree(GetProcessHeap(), 0, sfti); return NULL; } if (pdwDataSize) { *pdwDataSize = Length; } } } else { if (pdwDataSize) { *pdwDataSize = Length; } } } } else { // // On pre Vista systems the above info class unavailable, but all required information. // can be found inside csrss memory space (stored here for VDM purposes) at few fixed addresses. // if ((dwSignature != FIRM) && (dwSignature != RSMB)) { return NULL; } // we are interested only in two memory regions switch (dwSignature) { case FIRM: uAddress = 0xC0000; // FIRM analogue break; case RSMB: uAddress = 0xE0000; // RSMB analogue break; default: return NULL; break; } Length = 0; cid.UniqueProcess = (HANDLE)fnCsrGetProcessId(); cid.UniqueThread = 0; InitializeObjectAttributes(&attr, NULL, 0, 0, NULL); // open csrss, reg. client debug privilege set Status = fnZwOpenProcess(&hProcess, PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, &attr, &cid); if (NT_SUCCESS(Status)) { // get memory data region size for buffer allocation Status = fnZwQueryVirtualMemory(hProcess, (PVOID)uAddress, MemoryRegionInformation, &memInfo, sizeof(MEMORY_REGION_INFORMATION), &memIO); if (NT_SUCCESS(Status)) { sfti = (PSYSTEM_FIRMWARE_TABLE_INFORMATION)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, memInfo.RegionSize); if (sfti != NULL) { // read data to our allocated buffer Status = fnNtReadVirtualMemory(hProcess, (PVOID)uAddress, sfti, memInfo.RegionSize, &memIO); if (NT_SUCCESS(Status)) { if (pdwDataSize) { *pdwDataSize = (ULONG)memInfo.RegionSize; } } else { HeapFree(GetProcessHeap(), 0, sfti); return NULL; } } } fnZwClose(hProcess); } } return sfti; } /* * Source code taken from VMDE project: https://github.com/hfiref0x/VMDE */ extern "C" BOOL scan_mem(CHAR *Data, ULONG dwDataSize, CHAR *lpFindData, ULONG dwFindDataSize) { UINT i; SIZE_T(NTAPI *fnRtlCompareMemory)(const VOID *, const VOID *, SIZE_T) = (SIZE_T(NTAPI *)(const VOID *, const VOID *, SIZE_T))(GetProcAddress(GetModuleHandleW(L"ntdll"), "RtlCompareMemory")); if (!fnRtlCompareMemory) return FALSE; if ( (Data == NULL) || (lpFindData == NULL) ) { return FALSE; } if (dwFindDataSize > dwDataSize) { return FALSE; } for (i = 0; i < dwDataSize - dwFindDataSize; i++) { if (fnRtlCompareMemory(Data + i, lpFindData, dwFindDataSize) == dwFindDataSize) { return TRUE; } } return FALSE; } /* * Source code taken from VMDE project: https://github.com/hfiref0x/VMDE */ extern "C" BOOL check_system_objects(const std::wstring &directory, const std::wstring &name) { ULONG ctx, rlen; HANDLE hDirectory = NULL; OBJECT_ATTRIBUTES attr; UNICODE_STRING sname; BOOL found = FALSE; POBJECT_DIRECTORY_INFORMATION objinf; HMODULE hNtdll; hNtdll = GetModuleHandleW(L"ntdll"); VOID(NTAPI *fnRtlInitUnicodeString)(PUNICODE_STRING, PCWSTR) = (VOID(NTAPI *)(PUNICODE_STRING, PCWSTR))(GetProcAddress(hNtdll, "RtlInitUnicodeString")); NTSTATUS(WINAPI *fnNtOpenDirectoryObject)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES) = (NTSTATUS(WINAPI *)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES))(GetProcAddress(hNtdll, "NtOpenDirectoryObject")); NTSTATUS(WINAPI *fnNtQueryDirectoryObject)(HANDLE, PVOID, ULONG, BOOLEAN, BOOLEAN, PULONG, PULONG) = (NTSTATUS(WINAPI *)(HANDLE, PVOID, ULONG, BOOLEAN, BOOLEAN, PULONG, PULONG))(GetProcAddress(hNtdll, "NtQueryDirectoryObject")); NTSTATUS(NTAPI *fnZwClose)(HANDLE) = (NTSTATUS(NTAPI *)(HANDLE))(GetProcAddress(hNtdll, "ZwClose")); if (!fnRtlInitUnicodeString || !fnNtOpenDirectoryObject || !fnNtQueryDirectoryObject || !fnZwClose) return FALSE; __try { RtlSecureZeroMemory(&sname, sizeof(sname)); fnRtlInitUnicodeString(&sname, directory.c_str()); InitializeObjectAttributes(&attr, &sname, OBJ_CASE_INSENSITIVE, NULL, NULL); if (!NT_SUCCESS(fnNtOpenDirectoryObject(&hDirectory, DIRECTORY_QUERY, &attr))) return FALSE; // Enumerate objects in directory. ctx = 0; do { rlen = 0; if (fnNtQueryDirectoryObject(hDirectory, NULL, 0, TRUE, FALSE, &ctx, &rlen) != STATUS_BUFFER_TOO_SMALL) break; objinf = (POBJECT_DIRECTORY_INFORMATION)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, rlen); if (!objinf) break; if (!NT_SUCCESS(fnNtQueryDirectoryObject(hDirectory, objinf, rlen, TRUE, FALSE, &ctx, &rlen))) { HeapFree(GetProcessHeap(), 0, objinf); break; } // check if object name is forbidden if (StrStrIW(objinf->Name.Buffer, name.c_str())) { HeapFree(GetProcessHeap(), 0, objinf); found = TRUE; break; } HeapFree(GetProcessHeap(), 0, objinf); } while (TRUE); if (hDirectory) fnZwClose(hDirectory); } __except (EXCEPTION_EXECUTE_HANDLER) { found = FALSE; } return found; } __declspec(naked) bool is_hypervisor() { __asm { push ebp; mov ebp, esp; sub esp, 0x10; push ebx; push esi; push edi; xor eax, eax; lea edi, [ebp - 0xC]; stosd; stosd; stosd; mov eax, 1; xor ecx, ecx; cpuid; lea esi, [ebp - 0x10]; mov [esi], eax; mov [esi + 4], ebx; mov [esi + 8], ecx; mov [esi + 0xC], edx; mov eax, [ebp - 8]; pop edi; sar eax, 0x1F; pop esi; and al, 1; pop ebx; leave; retn; } } __declspec(naked) void get_cpu_hypevisor_id(char *vendor_id) { __asm { push ebp; mov ebp, esp; push ebx; push ecx; push edx; xor ebx, ebx; xor ecx, ecx; xor edx, edx; mov eax, 0x40000000; cpuid; mov eax, ebx; mov edi, vendor_id; stosd; mov eax, ecx; stosd; mov eax, edx; stosd; pop ebx; pop ecx; pop edx; pop ebp; retn; } } __declspec(naked) void get_cpu_vendor_id(char* vendor_id) { __asm { push ebp; mov ebp, esp; push ebx; push ecx; push edx; xor ebx, ebx; xor ecx, ecx; xor edx, edx; xor eax, eax; // eax = 0 cpuid; mov eax, ebx; mov edi, vendor_id; stosd; mov eax, edx; stosd; mov eax, ecx; stosd; pop ebx; pop ecx; pop edx; pop ebp; retn; } } __declspec(naked) DWORD get_number_of_processors() { __asm { push ebp; mov ebp, esp; mov eax, fs:0x18 ; TEB mov eax, [eax + 0x30]; PEB mov eax, [eax + 0x64]; pop ebp; retn; } } int64_t operator-(const FILETIME &endTime, const FILETIME &startTime) { return *reinterpret_cast(&endTime) - *reinterpret_cast(&startTime); } bool get_web_time(const std::string &net_resource, FILETIME & rv) { rv = {}; SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (INVALID_SOCKET == s) return false; sockaddr_in sin = {}; sin.sin_port = htons(80); sin.sin_family = AF_INET; hostent* he = gethostbyname(net_resource.c_str()); if (!he) { closesocket(s); return false; } memcpy(&sin.sin_addr.S_un.S_addr, he->h_addr, sizeof(sin.sin_addr.S_un.S_addr)); if (SOCKET_ERROR == connect(s, reinterpret_cast(&sin), sizeof(sin))) { closesocket(s); return false; } std::stringstream http_request; http_request << "GET / HTTP/1.1\r\n" "Accept: */*\r\n" "Accept-Language: en-us\r\n" "User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36\r\n" "Accept-Encoding: gzip, deflate\r\n" "Host: " << net_resource << "\r\n" "\r\n"; if (SOCKET_ERROR == send(s, http_request.str().c_str(), http_request.str().length(), 0)) { closesocket(s); return false; } char buff[1024] = {}; int total_bytes_recv = 0; int bytes_recv = 0; while ((bytes_recv = recv(s, buff + total_bytes_recv, _countof(buff) - total_bytes_recv - 1, MSG_PEEK)) && bytes_recv != SOCKET_ERROR) total_bytes_recv += bytes_recv; if (!total_bytes_recv || bytes_recv == SOCKET_ERROR) { closesocket(s); return false; } const auto err = WSAGetLastError(); closesocket(s); if (err == WSAECONNRESET || err == WSAEINTR || err == WSAEWOULDBLOCK) return false; std::string sBuff = buff; auto pos = sBuff.find("Date: "); if (sBuff.npos == pos) return false; sBuff = sBuff.substr(pos + 6); pos = sBuff.find("\r\n"); if (sBuff.npos == pos) return false; sBuff.resize(pos); SYSTEMTIME st = {}; if (!InternetTimeToSystemTimeA(sBuff.c_str(), &st, 0)) return false; return !!SystemTimeToFileTime(&st, &rv); } bool perform_dns_request(const std::string &domain_name, std::list &ips) { DNS_STATUS s; PDNS_RECORD dns_records, head_dns; // FIXME: should we use specific DNS service in 4th parameter s = DnsQuery_A(domain_name.c_str(), DNS_TYPE_A, DNS_QUERY_BYPASS_CACHE, NULL, &dns_records, NULL); if (s) return false; head_dns = dns_records; while (dns_records) { ips.push_back(dns_records->Data.A.IpAddress); dns_records = dns_records->pNext; } DnsRecordListFree(head_dns, DnsFreeRecordListDeep); return true; } bool get_disk_friendly_name(HDEVINFO hDevs, DWORD i, std::list &disk_names) { BOOL status; SP_DEVINFO_DATA deviceInfoData = {}; DWORD buff_size = 0; unsigned char *friendly_name = NULL; deviceInfoData.cbSize = sizeof(deviceInfoData); status = SetupDiEnumDeviceInfo(hDevs, i, &deviceInfoData); if (!status) return false; // calculate space needed for disk friendly name status = SetupDiGetDeviceRegistryPropertyA(hDevs, &deviceInfoData, SPDRP_FRIENDLYNAME, NULL, friendly_name, buff_size, &buff_size); if (!status && GetLastError() != ERROR_INSUFFICIENT_BUFFER) return false; // allocate space for disk friendly name friendly_name = reinterpret_cast(calloc(buff_size, sizeof(unsigned char))); if (!friendly_name) return false; if (!SetupDiGetDeviceRegistryPropertyA(hDevs, &deviceInfoData, SPDRP_FRIENDLYNAME, NULL, friendly_name, buff_size, &buff_size)) { free(friendly_name); return false; } disk_names.push_back(reinterpret_cast(friendly_name)); if (friendly_name) free(friendly_name); return true; } bool get_drive_print_names(std::list &disks) { HDEVINFO hDevs; DWORD i; if ((hDevs = SetupDiGetClassDevsA((LPGUID)&GUID_DEVCLASS_DISKDRIVE, NULL, NULL, DIGCF_PRESENT)) == INVALID_HANDLE_VALUE) return false; // enumerate all disk devices i = 0; do {} while (get_disk_friendly_name(hDevs, i++, disks)); SetupDiDestroyDeviceInfoList(hDevs); return true; } bool get_drive_models(std::list &drive_models) { std::list drive_model_names; const char fmt_device_name[] = "\\\\.\\PhysicalDrive%u"; char device_name[256] = { 0 }; for (BYTE i = 0; i < MAX_IDE_DRIVES; ++i) { memset(device_name, 0, _countof(device_name)); _snprintf_s(device_name, _countof(device_name), fmt_device_name, i); if (get_drive_model(device_name, SMART_RCV_DRIVE_DATA, i, drive_model_names)) { std::copy(drive_model_names.begin(), drive_model_names.end(), std::back_insert_iterator>(drive_models)); drive_model_names.clear(); } if (get_drive_model(device_name, IOCTL_STORAGE_QUERY_PROPERTY, i, drive_model_names)) { std::copy(drive_model_names.begin(), drive_model_names.end(), std::back_insert_iterator>(drive_models)); drive_model_names.clear(); } } return true; } bool get_drive_model(const std::string &device, ULONG ioctl, unsigned int drive, std::list& drive_model_names) { switch (ioctl) { case SMART_RCV_DRIVE_DATA: return get_drive_model_drv_d(device, drive, drive_model_names); case IOCTL_STORAGE_QUERY_PROPERTY: return get_drive_model_st_q(device, drive_model_names); default: return false; } return true; } /* * the following source code was used: http://codexpert.ro/blog/2013/10/26/get-physical-drive-serial-number-part-1/ */ bool get_drive_model_st_q(const std::string &device, std::list& drive_model_names) { // Get a handle to physical drive HANDLE hDevice = CreateFileA( device.c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ); if (hDevice == INVALID_HANDLE_VALUE) return false; bool ok = true; BYTE *pOutBuffer = NULL; do { // Set the input data structure STORAGE_PROPERTY_QUERY storagePropertyQuery; ZeroMemory(&storagePropertyQuery, sizeof(STORAGE_PROPERTY_QUERY)); storagePropertyQuery.PropertyId = StorageDeviceProperty; storagePropertyQuery.QueryType = PropertyStandardQuery; // Get the necessary output buffer size STORAGE_DESCRIPTOR_HEADER storageDescriptorHeader = { 0 }; DWORD dwBytesReturned = 0; if (!DeviceIoControl( hDevice, IOCTL_STORAGE_QUERY_PROPERTY, &storagePropertyQuery, sizeof(STORAGE_PROPERTY_QUERY), &storageDescriptorHeader, sizeof(STORAGE_DESCRIPTOR_HEADER), &dwBytesReturned, NULL)) { // error handling ok = false; break; } // Alloc the output buffer const DWORD dwOutBufferSize = storageDescriptorHeader.Size; pOutBuffer = new BYTE[dwOutBufferSize]; ZeroMemory(pOutBuffer, dwOutBufferSize); // Get the storage device descriptor if (!DeviceIoControl( hDevice, IOCTL_STORAGE_QUERY_PROPERTY, &storagePropertyQuery, sizeof(STORAGE_PROPERTY_QUERY), pOutBuffer, dwOutBufferSize, &dwBytesReturned, NULL)) { // error handling ok = false; break; } // Now, the output buffer points to a STORAGE_DEVICE_DESCRIPTOR structure // followed by additional info like vendor ID, product ID, serial number, and so on. STORAGE_DEVICE_DESCRIPTOR* pDeviceDescriptor = (STORAGE_DEVICE_DESCRIPTOR*)pOutBuffer; // const DWORD dwSerialNumberOffset = pDeviceDescriptor->SerialNumberOffset; const DWORD dwVendorIdOffset = pDeviceDescriptor->VendorIdOffset; const DWORD dwProdIdOffset = pDeviceDescriptor->ProductIdOffset; UCHAR *strVendorId, *strProdId; if (dwProdIdOffset > 0) { strProdId = pOutBuffer + dwProdIdOffset; drive_model_names.push_back(reinterpret_cast(strProdId)); } if (dwVendorIdOffset > 0) { strVendorId = pOutBuffer + dwVendorIdOffset; drive_model_names.push_back(reinterpret_cast(strVendorId)); } } while (false); CloseHandle(hDevice); if (pOutBuffer) { free(pOutBuffer); pOutBuffer = NULL; } return ok; } bool get_drive_model_drv_d(const std::string &device, unsigned int drive, std::list& drive_model_names) { // Get a handle to physical drive HANDLE hDevice = CreateFileA( device.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ); if (hDevice == INVALID_HANDLE_VALUE) return false; std::string drive_model; GETVERSIONOUTPARAMS VersionParams; DWORD cbBytesReturned = 0; BYTE IdOutCmd[sizeof(SENDCMDOUTPARAMS) + IDENTIFY_BUFFER_SIZE - 1]; // Get the version, etc of PhysicalDrive IOCTL memset((void*)&VersionParams, 0, sizeof(VersionParams)); if (!DeviceIoControl( hDevice, DFP_GET_VERSION, NULL, 0, &VersionParams, sizeof(VersionParams), &cbBytesReturned, NULL )) { CloseHandle(hDevice); return false; } BYTE bIDCmd = 0; // IDE or ATAPI IDENTIFY cmd SENDCMDINPARAMS scip; //SENDCMDOUTPARAMS OutCmd; // Now, get the ID sector for all IDE devices in the system. // If the device is ATAPI use the IDE_ATAPI_IDENTIFY command, // otherwise use the IDE_ATA_IDENTIFY command bIDCmd = (VersionParams.bIDEDeviceMap >> drive & 0x10) ? IDE_ATAPI_IDENTIFY : IDE_ATA_IDENTIFY; memset(&scip, 0, sizeof(scip)); memset(IdOutCmd, 0, sizeof(IdOutCmd)); if (!do_identify( hDevice, &scip, reinterpret_cast(&IdOutCmd), static_cast(bIDCmd), static_cast(drive), &cbBytesReturned)) { CloseHandle(hDevice); return false; } DWORD diskdata[256]; USHORT *pIdSector = reinterpret_cast((reinterpret_cast(IdOutCmd))->bBuffer); for (int ijk = 0; ijk < 256; ijk++) diskdata[ijk] = pIdSector[ijk]; // get drive model bool r = drv_convert_to_string(diskdata, _countof(diskdata), 27, 46, drive_model); if (r) drive_model_names.push_back(drive_model); return r; } bool drv_convert_to_string(DWORD diskdata[256], DWORD diskdata_size, unsigned int firstIndex, unsigned int lastIndex, std::string &buffer) { unsigned int index = 0; // int position = 0; // index check if (firstIndex > lastIndex || firstIndex >= diskdata_size || lastIndex >= diskdata_size) return false; // each integer has two characters stored in it backwards for (index = firstIndex; index <= lastIndex; index++) { // get high byte for 1st character buffer += static_cast((diskdata[index] / 256)); // get low byte for 2nd character buffer += static_cast((diskdata[index] % 256)); } // cut off the trailing blanks /* for (index = position - 1; index > 0 && isspace(buf[index]); index--) buf[index] = '\0'; */ return true; } /* * the following source code was used: https://www.winsim.com/diskid32/diskid32.cpp */ bool do_identify(HANDLE hPhysicalDriveIOCTL, PSENDCMDINPARAMS pSCIP, PSENDCMDOUTPARAMS pSCOP, BYTE bIDCmd, BYTE bDriveNum, PDWORD lpcbBytesReturned) { // Set up data structures for IDENTIFY command. pSCIP->cBufferSize = IDENTIFY_BUFFER_SIZE; pSCIP->irDriveRegs.bFeaturesReg = 0; pSCIP->irDriveRegs.bSectorCountReg = 1; //pSCIP -> irDriveRegs.bSectorNumberReg = 1; pSCIP->irDriveRegs.bCylLowReg = 0; pSCIP->irDriveRegs.bCylHighReg = 0; // Compute the drive number. pSCIP->irDriveRegs.bDriveHeadReg = 0xA0 | ((bDriveNum & 1) << 4); // The command can either be IDE identify or ATAPI identify. pSCIP->irDriveRegs.bCommandReg = bIDCmd; pSCIP->bDriveNumber = bDriveNum; pSCIP->cBufferSize = IDENTIFY_BUFFER_SIZE; return !!(DeviceIoControl( hPhysicalDriveIOCTL, DFP_RECEIVE_DRIVE_DATA, static_cast(pSCIP), sizeof(SENDCMDINPARAMS) - 1, static_cast(pSCOP), sizeof(SENDCMDOUTPARAMS) + IDENTIFY_BUFFER_SIZE - 1, lpcbBytesReturned, NULL)); } bool file_interface_save(const std::string &module, const std::string &name, bool detected) { HANDLE hFile; wchar_t file_path[MAX_PATH + 1] = {}, cur_dir[MAX_PATH + 1] = {}; if (!get_app_full_name(NULL, file_path, _countof(file_path), cur_dir, _countof(cur_dir))) return false; // as we have current directory, create now file name for the file std::wstringstream file_name; file_name << string_to_wstring(module) << L'_' << string_to_wstring(name) << L'_' << (detected ? L"detected" : L"notdetected"); // FIXME: delete this debug info // std::wcout << file_name.str(); if (!PathCombineW(file_path, cur_dir, file_name.str().c_str())) return false; hFile = CreateFileW(file_path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) return false; char file_buff[] = "c0de"; DWORD dwBytesWritten; BOOL b = WriteFile(hFile, file_buff, strlen(file_buff), &dwBytesWritten, NULL); CloseHandle(hFile); return !!b; } bool dns_interface_save(const std::string &module, const std::string &name, bool detected) { std::string domain_name = compose_domain(module, name, detected); PDNS_RECORD dns_records; DnsQuery_A(domain_name.c_str(), DNS_TYPE_A, DNS_QUERY_BYPASS_CACHE, NULL, &dns_records, NULL); return true; } std::string compose_domain(const std::string &module, const std::string &name, bool detected) { std::stringstream domain; std::string::const_iterator ci, cend; cend = module.cend(); for (ci = module.cbegin(); ci != cend; ++ci) if (isalnum(*ci)) domain << *ci; domain << '.'; cend = name.cend(); for (ci = name.cbegin(); ci != cend; ++ci) if (isalnum(*ci)) domain << *ci; domain << '.' << (detected ? "detected" : "notdetected"); return domain.str(); } EvasionMachineMode get_evasion_status(bool parent_hooked, bool child_hooked) { // 0 -> 0 ==> REAL MACHINE // 0 -> 1 ==> CHILD WITH MONITOR // 1 -> 0 ==> SANDBOX EVADED // 1 -> 1 ==> SANDBOX NOT EVADED return static_cast((!!parent_hooked) * 2 + !!child_hooked); } std::wstring string_to_wstring(const std::string &s) { std::wstring sw(s.length(), L' '); std::copy(s.begin(), s.end(), sw.begin()); return sw; } std::string remove_whitespaces(const std::string &s) { std::string sw; sw.reserve(sw.size()); for (size_t i = 0; i < s.length(); ++i) if (s[i] != ' ' && s[i] != '\t' && s[i] != '\n' && s[i] != '\r') sw += s[i]; return sw; } bool is_module_loaded(const std::string &module) { return !!GetModuleHandle(module.c_str()); } bool get_module_wfilename(std::wstring &result) { wchar_t wbuff[MAX_PATH + 1] = {}; const auto len = GetModuleFileNameW(nullptr, wbuff, MAX_PATH); result.assign(wbuff, len); return !result.empty(); } bool is_user_name_match(const std::string &s) { auto out_length = MAX_PATH; std::vector user_name(out_length, 0); ::GetUserNameA((LPSTR)user_name.data(), (LPDWORD)&out_length); return (!lstrcmpiA((LPCSTR)user_name.data(), s.c_str())); } bool is_computer_name_match(const std::string &s) { auto out_length = MAX_PATH; std::vector comp_name(out_length, 0); ::GetComputerNameA((LPSTR)comp_name.data(), (LPDWORD)&out_length); return (!lstrcmpiA((LPCSTR)comp_name.data(), s.c_str())); } bool is_host_name_match(const std::string &s) { auto out_length = MAX_PATH; std::vector dns_host_name(out_length, 0); ::GetComputerNameExA(ComputerNameDnsHostname, (LPSTR)dns_host_name.data(), (LPDWORD)&out_length); return (!lstrcmpiA((LPCSTR)dns_host_name.data(), s.c_str())); } /* * Source code taken from Joe Security blog: https://www.joesecurity.org/blog/6933341622592617830 */ bool is_audio_device_absent() { PCWSTR wszfilterName = L"audio_device_random_name"; if (FAILED(CoInitialize(NULL))) return false; IGraphBuilder *pGraph = nullptr; if (FAILED(CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void**)&pGraph))) return false; // First anti-emulation check: If AddFilter is called with NULL as a first argument it should return the E_POINTER error code. // Some emulators may implement unknown COM interfaces in a generic way, so they will probably fail here. if (E_POINTER != pGraph->AddFilter(NULL, wszfilterName)) return true; // Initializes a simple Audio Renderer, error code is not checked, // but pBaseFilter will be set to NULL upon failure and the code will eventually fail later. IBaseFilter *pBaseFilter = nullptr; CoCreateInstance(CLSID_AudioRender, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&pBaseFilter); // Adds the previously created Audio Renderer to the Filter Graph, no error checks pGraph->AddFilter(pBaseFilter, wszfilterName); // Tries to find the filter that was just added; in case of any previously not checked error (or wrong emulation) // this function won't find the filter and the sandbox/emulator will be successfully detected. IBaseFilter *pBaseFilter2 = nullptr; pGraph->FindFilterByName(wszfilterName, &pBaseFilter2); if (nullptr == pBaseFilter2) return true; // Checks if info.achName is equal to the previously added filterName, if not - poor API emulation FILTER_INFO info = { 0 }; pBaseFilter2->QueryFilterInfo(&info); if (0 != wcscmp(info.achName, wszfilterName)) return false; // Checks if the API sets a proper IReferenceClock pointer IReferenceClock *pClock = nullptr; if (0 != pBaseFilter2->GetSyncSource(&pClock)) return false; if (0 != pClock) return false; // Checks if CLSID is different from 0 CLSID clsID = { 0 }; pBaseFilter2->GetClassID(&clsID); if (clsID.Data1 == 0) return true; if (nullptr == pBaseFilter2) return true; // Just checks if the call was successful IEnumPins *pEnum = nullptr; if (0 != pBaseFilter2->EnumPins(&pEnum)) return true; // The reference count returned by AddRef has to be higher than 0 if (0 == pBaseFilter2->AddRef()) return true; return false; } std::string make_lowercase(const std::string &in) { std::string out; std::transform(in.begin(), in.end(), std::back_inserter(out), ::tolower); return out; }