Files
chkp-alexeybu d6fa72cb4d Cuckoo: check for invalid hook of NtLoadKeyEx added
Cuckoo: lack of argument checks in NtDelayExecution evasion added
2022-02-07 14:22:33 +03:00

2182 lines
62 KiB
C++

#include "cuckoo.h"
#include <WinNT.h>
#include <map>
#include <Ntsecapi.h>
#include "helper.h"
#include <time.h> // FIXME: should be removed
#include <list>
#include <vector>
#include <set>
#include <memory>
#include <string.h>
#include <Iphlpapi.h>
#include <WinInet.h>
#include <iostream>
#include "config.h"
#include "nt.h"
#include <math.h>
#define SLAVE_EXIT_CODE_SUCCESS 0
#define SLAVE_EXIT_CODE_FAILED 1
#define EXCEPTION_CHECK_EXC 0xE0000001
#define TASK_HOOKS_NOT_DETECTED 0x40035000
#define TASK_HOOKS_DETECTED 0x40035001
#define LOCALHOST 0x0100007f
#ifndef KI_USER_SHARED_DATA
#define KI_USER_SHARED_DATA 0x7FFE0000
#endif
#ifndef SharedUserData
#define SharedUserData ((KUSER_SHARED_DATA * const) KI_USER_SHARED_DATA)
#endif
#pragma comment(lib, "wininet.lib")
#pragma data_seg(".whtld")
#pragma section(".whtl", read,write,execute)
#pragma comment(linker, "/section:.whtl,RWE")
#pragma comment(linker, "/merge:.whtld=.whtl")
namespace se = SandboxEvasion;
#define DATA_SECTION_ALLOC __declspec(allocate(".whtld"))
#define WHTL_CODE_SEG __declspec(code_seg(".whtl"))
// function should reside in separate section in order to be easily copied to external process
WHTL_CODE_SEG
static DWORD WINAPI process_check_hooks_impl() {
HANDLE hEvent;
bool b_set_event;
bool b_hooks_found;
size_t i;
// should be patched while writing a memory
LPVOID args = reinterpret_cast<LPVOID>(SandboxEvasion::MAGIC_WHTL_PROC_ARGS);
se::pchta *_pta = static_cast<se::pchta *>(args);
if (!_pta) {
// just crash application
__asm {
ud2
}
return NULL;
}
HMODULE (WINAPI *__GetModuleHandle__)(LPCWSTR) = reinterpret_cast<HMODULE(WINAPI *)(LPCWSTR)>(_pta->__func_ptrs[static_cast<unsigned int>(se::ProcessCheckHooksFunc::GET_MODULE_HANDLE_W)]);
FARPROC(WINAPI *__GetProcAddress__)(HMODULE, LPCSTR) = reinterpret_cast<FARPROC(WINAPI *)(HMODULE, LPCSTR)>(_pta->__func_ptrs[static_cast<unsigned int>(se::ProcessCheckHooksFunc::GET_PROC_ADDRESS)]);
HANDLE (WINAPI *__OpenEventW__)(DWORD, BOOL, LPCWSTR) = reinterpret_cast<HANDLE(WINAPI *)(DWORD, BOOL, LPCWSTR)>(_pta->__func_ptrs[static_cast<unsigned int>(se::ProcessCheckHooksFunc::OPEN_EVENT_W)]);
BOOL (WINAPI *__SetEvent__)(HANDLE) = reinterpret_cast<BOOL(WINAPI *)(HANDLE)>(_pta->__func_ptrs[static_cast<unsigned int>(se::ProcessCheckHooksFunc::SET_EVENT)]);
BOOL (WINAPI *__CloseHandle__)(HANDLE) = reinterpret_cast<BOOL(WINAPI *)(HANDLE)>(_pta->__func_ptrs[static_cast<unsigned int>(se::ProcessCheckHooksFunc::CLOSE_HANDLE)]);
VOID (WINAPI *__ExitProcess__)(UINT) = reinterpret_cast<VOID(WINAPI *)(UINT)>(_pta->__func_ptrs[static_cast<unsigned int>(se::ProcessCheckHooksFunc::EXIT_PROCESS)]);
BOOL (WINAPI *__ReadProcessMemory__)(HANDLE, LPCVOID, LPVOID, SIZE_T, SIZE_T *) = reinterpret_cast<BOOL(WINAPI *)(HANDLE, LPCVOID, LPVOID, SIZE_T, SIZE_T *)>(_pta->__func_ptrs[static_cast<unsigned int>(se::ProcessCheckHooksFunc::READ_PROCESS_MEMORY)]);
HANDLE (WINAPI *__GetCurrentProcess__)(void) = reinterpret_cast<HANDLE(WINAPI *)(void)>(_pta->__func_ptrs[static_cast<unsigned int>(se::ProcessCheckHooksFunc::GET_CURRENT_PROCESS)]);
if (!(hEvent = __OpenEventW__(EVENT_ALL_ACCESS, FALSE, _pta->event_name)))
__ExitProcess__(SLAVE_EXIT_CODE_FAILED);
b_hooks_found = false;
const size_t fn_count = 4;
LPVOID func_addresses[fn_count] = {};
for (i = 0; i < fn_count; ++i)
func_addresses[i] = _pta->__func_ptrs[static_cast<unsigned int>(se::ProcessCheckHooksFunc::ZW_DELAY_EXECUTION) + i];
// check function hooks here
HANDLE hProcess = __GetCurrentProcess__();
BYTE code[0x10];
DWORD dwNumberOfBytesRead;
size_t code_i;
for (i = 0; i < fn_count; ++i) {
memset(code, 0x0, sizeof(code));
if (!__ReadProcessMemory__(
hProcess,
func_addresses[i],
code,
sizeof(code),
&dwNumberOfBytesRead))
continue;
// check trampolines
code_i = 0;
// skip all nops at the beggining
while (code_i < sizeof(code) && code[code_i] == 0x90)
++code_i;
// check jmp opcodes
b_hooks_found = (i < sizeof(code)) && (code[code_i] == 0xe9 || code[code_i] == 0xeb);
if (b_hooks_found)
break;
// check push retn
code_i = 0;
// skip all nops at the beggining
while (code_i < sizeof(code) && code[code_i] == 0x90)
++code_i;
if (code_i + 5 < sizeof(code)) {
// check push/retn instructions
b_hooks_found = code[i] == 0x68 && code[i + 5] == 0xc3;
}
if (b_hooks_found)
break;
}
// check if hooks were found
if (!b_hooks_found) {
b_set_event = !__SetEvent__(hEvent);
__CloseHandle__(hEvent);
__ExitProcess__(SLAVE_EXIT_CODE_SUCCESS ? b_set_event : SLAVE_EXIT_CODE_FAILED);
}
__CloseHandle__(hEvent);
__ExitProcess__(SLAVE_EXIT_CODE_SUCCESS);
return 0;
}
WHTL_CODE_SEG
__declspec(naked)
static void process_check_hooks(void) {
__asm {
// magic
ud2;
ud2;
int 3;
int 3;
int 3;
int 3;
ud2;
ud2;
int 3;
int 3;
int 3;
int 3;
// call to function
call process_check_hooks_impl;
}
}
namespace {
static LARGE_INTEGER kli = { 0xFFFFFFFFFD8F0000ll }; // FIXME: change
static struct {
BOOLEAN Alertable;
PLARGE_INTEGER DelayInterval;
} kZwDelayExecutionArgs = {0, &kli};
} // anonymous
#define ARG_ITEM(x) sizeof(x), &x
namespace SandboxEvasion {
VEDetection* Cuckoo::create_instance(const json_tiny &j) {
return new Cuckoo(j);
}
void Cuckoo::CheckAllCustom() {
bool d;
std::pair<std::string, std::string> report;
std::string ce_name;
ce_name = Config::cc2s[Config::ConfigCuckoo::INVALID_HOOK];
if (IsEnabled(ce_name, conf.get<std::string>(ce_name + std::string(".") + Config::cg2s[Config::ConfigGlobal::ENABLED], ""))) {
d = IsInvalidHook();
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::cc2s[Config::ConfigCuckoo::LACK_ARGCHECKS];
if (IsEnabled(ce_name, conf.get<std::string>(ce_name + std::string(".") + Config::cg2s[Config::ConfigGlobal::ENABLED], ""))) {
d = IsLackArgChecks();
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::cc2s[Config::ConfigCuckoo::UNBALANCED_STACK];
if (IsEnabled(ce_name, conf.get<std::string>(ce_name + std::string(".") + Config::cg2s[Config::ConfigGlobal::ENABLED], ""))) {
d = CheckUnbalancedStack();
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::cc2s[Config::ConfigCuckoo::DELAYS_ACCUMULATION];
if (IsEnabled(ce_name, conf.get<std::string>(ce_name + std::string(".") + Config::cg2s[Config::ConfigGlobal::ENABLED], ""))) {
d = CheckDelaysAccumulation();
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::cc2s[Config::ConfigCuckoo::SOCKET_TIMEOUT];
if (IsEnabled(ce_name, conf.get<std::string>(ce_name + std::string(".") + Config::cg2s[Config::ConfigGlobal::ENABLED], ""))) {
d = CheckSocketTimeout();
report = GenerateReportEntry(ce_name + std::string("_probe1"), json_tiny(conf.get(ce_name, pt::ptree())), d);
log_message(LogMessageLevel::INFO, module_name, report.second, d ? RED : GREEN);
}
ce_name = Config::cc2s[Config::ConfigCuckoo::INFINITE_DELAY];
if (IsEnabled(ce_name, conf.get<std::string>(ce_name + std::string(".") + Config::cg2s[Config::ConfigGlobal::ENABLED], ""))) {
d = CheckInfiniteSleep();
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::cc2s[Config::ConfigCuckoo::DELAY_INTERVAL];
if (IsEnabled(ce_name, conf.get<std::string>(ce_name + std::string(".") + Config::cg2s[Config::ConfigGlobal::ENABLED], ""))) {
d = IsDelayIntervalModified();
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::cc2s[Config::ConfigCuckoo::TICK_COUNT];
if (IsEnabled(ce_name, conf.get<std::string>(ce_name + std::string(".") + Config::cg2s[Config::ConfigGlobal::ENABLED], ""))) {
d = CheckTickCountIntegrity();
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::cc2s[Config::ConfigCuckoo::FUNCTION_HOOKS];
if (IsEnabled(ce_name, conf.get<std::string>(ce_name + std::string(".") + Config::cg2s[Config::ConfigGlobal::ENABLED], ""))) {
d = CheckFunctionHooks();
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::cc2s[Config::ConfigCuckoo::AGENT_ARTIFACTS];
if (IsEnabled(ce_name, conf.get<std::string>(ce_name + std::string(".") + Config::cg2s[Config::ConfigGlobal::ENABLED], ""))) {
d = CheckAgentArtifacts();
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::cc2s[Config::ConfigCuckoo::CUCKOOMON_CONFIGURATION];
if (IsEnabled(ce_name, conf.get<std::string>(ce_name + std::string(".") + Config::cg2s[Config::ConfigGlobal::ENABLED], ""))) {
d = IsConfigurationPresent();
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::cc2s[Config::ConfigCuckoo::WHITELISTED_PROCESS];
if (IsEnabled(ce_name, conf.get<std::string>(ce_name + std::string(".") + Config::cg2s[Config::ConfigGlobal::ENABLED], ""))) {
d = IsWhitelistedNotTracked();
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::cc2s[Config::ConfigCuckoo::EVENT_NAME];
if (IsEnabled(ce_name, conf.get<std::string>(ce_name + std::string(".") + Config::cg2s[Config::ConfigGlobal::ENABLED], ""))) {
d = CheckEventName();
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::cc2s[Config::ConfigCuckoo::RAISED_EXCEPTIONS];
if (IsEnabled(ce_name, conf.get<std::string>(ce_name + std::string(".") + Config::cg2s[Config::ConfigGlobal::ENABLED], ""))) {
d = CheckExceptionsNumber(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::cc2s[Config::ConfigCuckoo::WMI_PROCESS];
if (IsEnabled(ce_name, conf.get<std::string>(ce_name + std::string(".") + Config::cg2s[Config::ConfigGlobal::ENABLED], ""))) {
d = IsWMINotTracked(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::cc2s[Config::ConfigCuckoo::TASK_SCHED_PROCESS];
if (IsEnabled(ce_name, conf.get<std::string>(ce_name + std::string(".") + Config::cg2s[Config::ConfigGlobal::ENABLED], ""))) {
d = IsTaskSchedNotTracked(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::cc2s[Config::ConfigCuckoo::PID_REUSE];
if (IsEnabled(ce_name, conf.get<std::string>(ce_name + std::string(".") + Config::cg2s[Config::ConfigGlobal::ENABLED], ""))) {
d = IsPidReusedNotTracked(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::cc2s[Config::ConfigCuckoo::AGENT_LISTENER];
if (IsEnabled(ce_name, conf.get<std::string>(ce_name + std::string(".") + Config::cg2s[Config::ConfigGlobal::ENABLED], ""))) {
d = IsAgentPresent();
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::cc2s[Config::ConfigCuckoo::SUSPENDED_THREAD];
if (IsEnabled(ce_name, conf.get<std::string>(ce_name + std::string(".") + Config::cg2s[Config::ConfigGlobal::ENABLED], ""))) {
d = IsSuspendedThreadNotTracked();
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::cc2s[Config::ConfigCuckoo::SOCKET_TIMEOUT];
if (IsEnabled(ce_name, conf.get<std::string>(ce_name + std::string(".") + Config::cg2s[Config::ConfigGlobal::ENABLED], ""))) {
d = CheckSocketTimeout();
report = GenerateReportEntry(ce_name + std::string("_probe2"), json_tiny(conf.get(ce_name, pt::ptree())), d);
log_message(LogMessageLevel::INFO, module_name, report.second, d ? RED : GREEN);
}
ce_name = Config::cc2s[Config::ConfigCuckoo::RESULTSERVER_CONNECTION];
if (IsEnabled(ce_name, conf.get<std::string>(ce_name + std::string(".") + Config::cg2s[Config::ConfigGlobal::ENABLED], ""))) {
d = IsResultserverConnected();
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::cc2s[Config::ConfigCuckoo::DEAD_ANALYZER];
if (IsEnabled(ce_name, conf.get<std::string>(ce_name + std::string(".") + Config::cg2s[Config::ConfigGlobal::ENABLED], ""))) {
d = IsAnalyzerDeadNotTracked(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);
}
}
/*
* Check for the hooked functions using the canary on the lower addresses of stack
*/
bool Cuckoo::CheckUnbalancedStack() const {
// FIXME: add 4 more functions that are hooked by cuckoomon
// TODO: make this configurable ???
usf_t f = {
{ lib_name_t(L"ntdll"), {
{sizeof(void *), NULL, "ZwDelayExecution", ARG_ITEM(kZwDelayExecutionArgs) }
} }
};
const uint8_t canary[8] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF };
uint32_t args_size;
const void *args_buff;
uint32_t reserved_size;
uint32_t reserved_size_after_call;
uint32_t canary_size;
FARPROC func;
bool us_detected;
void *canary_addr = (void *)&canary[0];
static_assert((sizeof(canary) % sizeof(void *)) == 0, "Invalid canary alignement");
for (auto it = f.begin(), end = f.end(); it != end; ++it) {
for (auto &vi : it->second) {
vi.func_addr = GetProcAddress(GetModuleHandleW(it->first.c_str()), vi.func_name.c_str());
// call to Unbalanced Stack
args_size = vi.args_size;
args_buff = vi.args_buff;
canary_size = sizeof(canary);
reserved_size = sizeof(void *) + vi.local_vars_size + canary_size;
reserved_size_after_call = reserved_size + args_size;
func = vi.func_addr;
us_detected = false;
__asm {
pusha
mov ecx, args_size
sub esp, ecx
mov esi, args_buff
mov edi, esp
cld
rep movsb
sub esp, reserved_size
mov ecx, canary_size
mov esi, canary_addr
mov edi, esp
rep movsb
add esp, reserved_size
mov eax, func
call eax
sub esp, reserved_size_after_call
mov ecx, canary_size
mov esi, canary_addr
mov edi, esp
repz cmpsb
cmp ecx, 0
setnz us_detected
add esp, reserved_size_after_call
popa
}
if (us_detected)
return true;
}
}
return false;
}
/*
* Check for INFINITE delay sleeping bug in cuckoomon
* TODO: method should be called within first N seconds of execution process (5 by default in cuckoomon)
*/
bool Cuckoo::CheckInfiniteSleep() {
return RunMasterSlaveThreads(&Cuckoo::ThreadInfiniteSleepMaster, &Cuckoo::ThreadInfiniteSleepSlave);
}
/*
* Check if the values for sleeps are accumulated
* TODO: method should be called within first N seconds of execution process (5 by default in cuckoomon)
*/
bool Cuckoo::CheckDelaysAccumulation() {
return RunMasterSlaveThreads(&Cuckoo::ThreadDelaysAccumulationMaster, &Cuckoo::ThreadDelaysAccumulationSlave);
}
/*
* Check if socket timeout equals to the normal delay sleep
*/
bool Cuckoo::CheckSocketTimeout() {
return RunMasterSlaveThreads(&Cuckoo::ThreadCheckSocketTimeoutMaster, &Cuckoo::ThreadCheckSocketTimeoutSlave);
}
bool Cuckoo::CheckDelaysSkip() const {
// TODO: implement
return false;
}
bool Cuckoo::IsAgentPresent() const {
// TODO: add support for IPv6, GetTcp6Table
// get all current IPv4 TCP connections
const MIB_TCPTABLE *p_tcp_table = reinterpret_cast<const MIB_TCPTABLE *>(get_tcp_table());
network_endpoints_t net_endpoints;
const size_t http_response_size = 1024;
unsigned char agent_response[http_response_size + 1] = {};
size_t http_resp_size;
if (!p_tcp_table)
return false;
// retrieve all LISTENING sockets information
get_tcp_entries(p_tcp_table, net_endpoints, MIB_TCP_STATE_LISTEN, false);
free(const_cast<MIB_TCPTABLE *>(p_tcp_table));
/*
size_t nep_c = net_endpoints.size();
size_t i = 1;
*/
for (auto & ne : net_endpoints) {
http_resp_size = http_response_size;
// fprintf(stdout, "{+} Communicating with %u/%u endpoints\n", i++, nep_c);
// establish connection & send crafted requests and wait for response
if (!CommunicateWithAgent(ne, agent_response, &http_resp_size))
continue;
// check if received response belongs to agent
if (CheckResponseIsAgent(agent_response, http_resp_size))
return true;
}
return false;
}
bool Cuckoo::IsResultserverConnected() const {
// TODO: add support for IPv6, GetTcp6Table
// get all current IPv4 TCP connections
const MIB_TCPTABLE *p_tcp_table = reinterpret_cast<const MIB_TCPTABLE *>(get_tcp_table());
network_endpoints_t net_endpoints;
if (!p_tcp_table)
return false;
// retrieve all ESTABLISHED connections
get_tcp_entries(p_tcp_table, net_endpoints, MIB_TCP_STATE_ESTAB, true);
free(const_cast<MIB_TCPTABLE *>(p_tcp_table));
for (auto & ne : net_endpoints) {
if(ntohs(static_cast<INTERNET_PORT>(ne.second)) == 2042)
return true;
}
return false;
}
bool Cuckoo::CheckFunctionHooks() const {
// return True if functions are hooked
// TODO: make this configurable ???
func_hooked_t func_hooked = {{
lib_name_t(L"ntdll"), {
func_name_t("ZwDelayExecution"),
func_name_t("ZwCreateProcess"),
func_name_t("ZwCreateThread"),
func_name_t("ZwOpenThread"),
func_name_t("NtOpenProcess"),
func_name_t("NtTerminateProcess"),
func_name_t("NtCreateSection"),
func_name_t("NtMapViewOfSection"),
func_name_t("NtUnmapViewOfSection"),
func_name_t("NtClose"),
func_name_t("NtAllocateVirtualMemory"),
func_name_t("NtFreeVirtualMemory"),
func_name_t("NtWriteVirtualMemory"),
func_name_t("LdrLoadDll"),
func_name_t("RtlInitUnicodeString"),
func_name_t("RtlDecompressBuffer"),
func_name_t("RtlMoveMemory"),
func_name_t("RtlZeroMemory"),
func_name_t("strstr"),
func_name_t("tolower")
}
}
};
return CheckFunctionHooks(GetCurrentProcess(), func_hooked);
}
bool Cuckoo::IsConfigurationPresent() const {
// TODO: do we need to create a thread here ???
// my implementation
/*
char temp_path[MAX_PATH + 1] = { 0 };
if (!GetTempPathA(sizeof(temp_path) - 1, temp_path))
return false;
*/
wchar_t app_params[] = L"--action --cfg";
PROCESS_INFORMATION pi = {};
const size_t max_retries = 5;
const DWORD retry_delay = 500;
if (!run_self_susp(app_params, &pi))
return false;
// original cuckoo implementation (except of wchars usage)
wchar_t *temp_path = NULL;
if (!get_envvar_w(L"TEMP", &temp_path))
return false;
pfi _pfi = { NULL, false, NULL };
wchar_t old_filename[MAX_PATH + 1] = {};
wchar_t new_filename[MAX_PATH + 1] = {};
_snwprintf_s(old_filename, _countof(old_filename), _TRUNCATE, L"%d.ini", pi.dwProcessId);
_snwprintf_s(new_filename, _countof(new_filename), _TRUNCATE, L"cuckoo_%lu.ini", pi.dwProcessId);
size_t mri = 0;
while (mri++ < max_retries) {
// check for configuration file of cuckoomon
memset(&_pfi, 0, sizeof(_pfi));
enumerate_directory_w(temp_path, 0, 0, NULL, &_pfi, { file_name_w_t(old_filename) });
if (_pfi.matched)
break;
// check for configuration file of monitor
memset(&_pfi, 0, sizeof(_pfi));
enumerate_directory_w(L"C:\\", 0, 0, NULL, &_pfi, { file_name_w_t(new_filename) });
if (_pfi.matched)
break;
Sleep(retry_delay);
}
free(temp_path);
temp_path = NULL;
// process termination and objects closing
TerminateProcess(pi.hProcess, 1);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
return _pfi.matched;
}
bool Cuckoo::CheckEventName() {
return RunMasterSlaveThreadsEventName(&Cuckoo::ThreadCheckEventNameMaster, &Cuckoo::ThreadCheckEventNameSlave);
}
bool Cuckoo::CheckAgentArtifacts() const {
// TODO: should user some generic function and be configurable from the JSON file ???
// TODO: should we do only regexp as a file to find and use callback function for performing some extra checks???
std::list<file_name_w_t> cuckoo_files {
file_name_w_t(L"analyzer.py"),
file_name_w_t(L"analysis.conf")
};
pfi _pfi = { NULL, false, NULL };
enumerate_directory_w(L"C:\\", 1, 0, NULL, &_pfi, cuckoo_files);
return _pfi.matched;
}
bool Cuckoo::IsPidReusedNotTracked(ProcessWorkingMode wm) const {
switch (wm) {
case ProcessWorkingMode::MASTER:
return IsPidReusedNotTrackedMaster();
case ProcessWorkingMode::SLAVE:
return IsPidReusedNotTrackedSlave();
default:
return false;
}
}
bool Cuckoo::IsWMINotTracked(ProcessWorkingMode wm) const {
switch (wm) {
case ProcessWorkingMode::MASTER:
return IsWMINotTrackedMaster();
case ProcessWorkingMode::SLAVE:
return IsWMINotTrackedSlave();
default:
return false;
}
}
bool Cuckoo::IsTaskSchedNotTracked(ProcessWorkingMode wm) const {
switch (wm) {
case ProcessWorkingMode::MASTER:
return IsTaskSchedNotTrackedMaster();
case ProcessWorkingMode::SLAVE:
return IsTaskSchedNotTrackedSlave();
default:
return false;
}
}
bool Cuckoo::IsServiceNotTracked() const {
// TODO: implement
return false;
}
bool Cuckoo::IsWhitelistedNotTracked() const {
STARTUPINFOW si = {};
si.cb = sizeof(si);
GetStartupInfoW(&si);
PROCESS_INFORMATION pi = {};
bool wla_detected = false;
LPCVOID inj_code, inj_data;
LPVOID section_code;
unsigned char *ppatch;
bool whitelisted_proc_detected = false;
event_name_t event_name;
pchta pta_args = {};
// TODO: should be taken from configuration ???
std::list<file_path_t> whitelisted_processes {
file_path_t(L"C:\\WINDOWS\\system32\\dwwin.exe"),
file_path_t(L"C:\\WINDOWS\\system32\\dumprep.exe"),
file_path_t(L"C:\\WINDOWS\\system32\\drwtsn32.exe")
};
SYSTEM_INFO sys_i = {};
GetSystemInfo(&sys_i);
DWORD section_va;
LPCVOID process_check_hooks_ep;
bool cp_mode = CheckFunctionHooks();
// init function addresses
if (!resolve_func_addresses({
{ L"kernel32", { "GetModuleHandleW", "GetProcAddress", "OpenEventW", "SetEvent", "CloseHandle", "ExitProcess", "ReadProcessMemory", "GetCurrentProcess" } },
{ L"ntdll", { "ZwDelayExecution", "ZwCreateProcess", "ZwCreateThread", "ZwOpenThread" } }
}, &pta_args))
return false;
for (auto &wp : whitelisted_processes) {
// prepare non-const string in non-ugly way
std::vector<wchar_t> app(wp.begin(), wp.end());
app.push_back(0);
// create process specified in the whitelist
if (!CreateProcessW(
NULL,
&app[0],
NULL,
NULL,
TRUE,
CREATE_SUSPENDED,
NULL,
NULL,
&si,
&pi
)) {
continue;
}
// if process was successfully created, then inject a stub there that will be responsible for checking if process is hooked
// in case is not hooked, then we are cool and cuckoo is evaded
// create event name used for communication with child process
event_name = GeneratePrintableBuffer(EVENT_NAME_MAX_LEN - 1, pi.dwProcessId);
// copy event name to structure
memset(pta_args.event_name, 0x0, sizeof(pta_args.event_name));
if (wcscpy_s(pta_args.event_name, EVENT_NAME_MAX_LEN, event_name.c_str())) {
TerminateProcess(pi.hProcess, 0);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
continue;
}
// patch code that will be responsible for testing if functions are hooked, prepare a stub that will be responsible for checking
inj_data = inject_data(pi.hProcess, reinterpret_cast<const data_t *>(&pta_args), sizeof(pta_args));
// patch address of argument for the thread
section_code = calloc(sys_i.dwPageSize, sizeof(code_t));
if (!section_code) {
TerminateProcess(pi.hProcess, 0);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
continue;
}
// find virtual address where section is loaded
section_va = align_down(reinterpret_cast<DWORD>(process_check_hooks), sys_i.dwPageSize);
memcpy(section_code, reinterpret_cast<const void*>(section_va), sys_i.dwPageSize);
ppatch = __memmem(reinterpret_cast<const code_t*>(section_code), sys_i.dwPageSize, reinterpret_cast<const unsigned char *>(&SandboxEvasion::MAGIC_WHTL_PROC_ARGS), sizeof(SandboxEvasion::MAGIC_WHTL_PROC_ARGS));
if (!ppatch) {
TerminateProcess(pi.hProcess, 0);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
free(section_code);
continue;
}
memcpy(ppatch, &inj_data, sizeof(SandboxEvasion::MAGIC_WHTL_PROC_ARGS));
process_check_hooks_ep = __memmem(reinterpret_cast<const code_t*>(section_va), sys_i.dwPageSize, SandboxEvasion::I_magic, sizeof(SandboxEvasion::I_magic));
inj_code = inject_code(pi.hProcess, reinterpret_cast<const code_t *>(section_code), sys_i.dwPageSize);
// align process_check_hooks_ep as far as it is copied to another address in another process
process_check_hooks_ep = reinterpret_cast<LPCVOID>(reinterpret_cast<DWORD>(process_check_hooks_ep) + (reinterpret_cast<DWORD>(inj_code) - section_va) + sizeof(SandboxEvasion::I_magic));
if (!inj_data || !inj_code || !process_check_hooks_ep) {
TerminateProcess(pi.hProcess, 0);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
free(section_code);
continue;
}
// execute injected code to the process
if (!thread_context_execute_code(pi.hThread, reinterpret_cast<LPTHREAD_START_ROUTINE>(process_check_hooks_ep), const_cast<LPVOID>(inj_data), TRUE)) {
TerminateProcess(pi.hProcess, 0);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
free(section_code);
continue;
}
// FIXME: should timeout be configurable?
wla_detected = WaitForNotificationFromSlaveUsingEvent(event_name, pi.hProcess, pi.hThread, 100);
// process has terminated itself
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
free(section_code);
if (wla_detected) {
break;
}
// TODO: do we need to collect error code of terminated process here???
}
EvasionMachineMode es = get_evasion_status(cp_mode, !wla_detected);
return es == EvasionMachineMode::SANDBOX_EVADED;
}
bool Cuckoo::IsSuspendedThreadNotTracked() const {
STARTUPINFOW si = {};
si.cb = sizeof(si);
GetStartupInfoW(&si);
PROCESS_INFORMATION pi = {};
bool st_detected = false;
LPCVOID inj_code, inj_data;
LPVOID section_code = NULL;
unsigned char *ppatch;
event_name_t event_name;
pchta pta_args = {};
SYSTEM_INFO sys_i = {};
GetSystemInfo(&sys_i);
DWORD section_va;
LPCVOID process_check_hooks_ep;
bool ok = true;
HANDLE hHelperThread = INVALID_HANDLE_VALUE;
bool cp_mode = CheckFunctionHooks();
// init function addresses
if (!resolve_func_addresses({
{ L"kernel32",{ "GetModuleHandleW", "GetProcAddress", "OpenEventW", "SetEvent", "CloseHandle", "ExitProcess", "ReadProcessMemory", "GetCurrentProcess" } },
{ L"ntdll",{ "ZwDelayExecution", "ZwCreateProcess", "ZwCreateThread", "ZwOpenThread" } }
}, &pta_args))
return false;
if (!run_self_susp(NULL, &pi))
return false;
do {
// if process was successfully created, then inject a stub there that will be responsible for checking if process is hooked
// in case is not hooked, then we are cool and cuckoo is evaded
// create event name used for communication with child process
event_name = GeneratePrintableBuffer(EVENT_NAME_MAX_LEN - 1, pi.dwProcessId);
// copy event name to structure
memset(pta_args.event_name, 0x0, sizeof(pta_args.event_name));
if (wcscpy_s(pta_args.event_name, EVENT_NAME_MAX_LEN, event_name.c_str())) {
ok = false;
break;
}
// patch code that will be responsible for testing if functions are hooked, prepare a stub that will be responsible for checking
inj_data = inject_data(pi.hProcess, reinterpret_cast<const data_t *>(&pta_args), sizeof(pta_args));
// patch address of argument for the thread
section_code = calloc(sys_i.dwPageSize, sizeof(code_t));
if (!section_code) {
ok = false;
break;
}
// find virtual address where section is loaded
section_va = align_down(reinterpret_cast<DWORD>(process_check_hooks), sys_i.dwPageSize);
memcpy(section_code, reinterpret_cast<const void*>(section_va), sys_i.dwPageSize);
ppatch = __memmem(reinterpret_cast<const code_t*>(section_code), sys_i.dwPageSize, reinterpret_cast<const unsigned char *>(&SandboxEvasion::MAGIC_WHTL_PROC_ARGS), sizeof(SandboxEvasion::MAGIC_WHTL_PROC_ARGS));
if (!ppatch) {
ok = false;
break;
}
memcpy(ppatch, &inj_data, sizeof(SandboxEvasion::MAGIC_WHTL_PROC_ARGS));
process_check_hooks_ep = __memmem(reinterpret_cast<const code_t*>(section_va), sys_i.dwPageSize, SandboxEvasion::I_magic, sizeof(SandboxEvasion::I_magic));
inj_code = inject_code(pi.hProcess, reinterpret_cast<const code_t*>(section_code), sys_i.dwPageSize);
// align process_check_hooks_ep as far as it is copied to another address in another process
process_check_hooks_ep = reinterpret_cast<LPCVOID>(reinterpret_cast<DWORD>(process_check_hooks_ep) + (reinterpret_cast<DWORD>(inj_code) - section_va) + sizeof(SandboxEvasion::I_magic));
if (!inj_data || !inj_code || !process_check_hooks_ep) {
ok = false;
break;
}
// create a new thread that will not be tracked
if (!execute_code(pi.hProcess, reinterpret_cast<LPTHREAD_START_ROUTINE>(process_check_hooks_ep), const_cast<LPVOID>(inj_data), &hHelperThread, CREATE_SUSPENDED)) {
ok = false;
break;
}
// FIXME: should timeout be configurable?
st_detected = WaitForNotificationFromSlaveUsingEvent(event_name, pi.hProcess, hHelperThread, 100);
} while (false);
// clean
TerminateProcess(pi.hProcess, 0);
CloseHandle(pi.hThread);
CloseHandle(hHelperThread);
CloseHandle(pi.hProcess);
if (section_code) {
free(section_code);
section_code = NULL;
}
if (!ok)
return false;
EvasionMachineMode es = get_evasion_status(cp_mode, !st_detected);
return es == EvasionMachineMode::SANDBOX_EVADED;
}
// ATTENTION: Should be called as last method withon Cuckoo Environment test, because ruins all environment
bool Cuckoo::IsAnalyzerDeadNotTracked(ProcessWorkingMode wm) const {
switch (wm) {
case ProcessWorkingMode::MASTER:
return IsAnalyzerDeadNotTrackedMaster();
case ProcessWorkingMode::SLAVE:
return IsAnalyzerDeadNotTrackedSlave();
default:
return false;
}
}
bool Cuckoo::CheckExceptionsNumber(ProcessWorkingMode wm) const {
switch (wm) {
case ProcessWorkingMode::MASTER:
return CheckExceptionsNumberMaster();
case ProcessWorkingMode::SLAVE:
return CheckExceptionsNumberSlave();
default:
return false;
}
}
bool Cuckoo::IsDelayIntervalModified() const {
LARGE_INTEGER DelayInterval = { 0 };
LARGE_INTEGER cDelayInterval = { 0 };
cDelayInterval.QuadPart = 0xFFFFFFFFFD8F0000ll;
NTSTATUS (NTAPI *lpZwDelayExecution)(BOOLEAN, PLARGE_INTEGER) = reinterpret_cast<NTSTATUS(NTAPI *)(BOOLEAN, PLARGE_INTEGER)>(GetProcAddress(GetModuleHandleW(L"ntdll"), "ZwDelayExecution"));
if (!lpZwDelayExecution)
return false;
DelayInterval = cDelayInterval;
lpZwDelayExecution(FALSE, &DelayInterval);
return !!memcmp(&DelayInterval, &cDelayInterval, sizeof(LARGE_INTEGER));;
}
bool Cuckoo::CheckTickCountIntegrity() const {
DWORD tick_count_f, tick_count_ku;
DWORD tick_count_diff;
const uint32_t delay_ms = 4000; // timeout in milliseconds
KUSER_SHARED_DATA * t = SharedUserData;
DWORD major_version;
// sleep before applying GetTickCount check
SleepEx(delay_ms, FALSE);
major_version = static_cast<DWORD>(LOBYTE(LOWORD(GetVersion())));
// check both tick count & kuser_shared_data
tick_count_f = GetTickCount();
if (major_version == 5)
tick_count_ku = static_cast<ULONG>((static_cast<ULONGLONG>(SharedUserData->TickCountLowDeprecated) * SharedUserData->TickCountMultiplier) >> 24);
else if (major_version > 5)
tick_count_ku = SharedUserData->TickCountMultiplier * (SharedUserData->TickCount.High1Time << 8) +
(SharedUserData->TickCount.LowPart * (unsigned __int64)SharedUserData->TickCountMultiplier >> 24);
tick_count_diff = abs(static_cast<long>(tick_count_ku - tick_count_f));
return tick_count_diff > 100;
}
DWORD Cuckoo::ThreadInfiniteSleepMaster(LPVOID thread_params) {
ptpsi p_thread_param_si = static_cast<ptpsi>(thread_params);
HANDLE hEvent;
DWORD event_state;
if (!p_thread_param_si)
return FALSE;
// barrier sync
InterlockedIncrement(&dwThreadMasterSlaveBarrier);
while (p_thread_param_si->threads_count != dwThreadMasterSlaveBarrier);
if (!(hEvent = CreateEventW(NULL, FALSE, FALSE, p_thread_param_si->event_name)))
return FALSE;
event_state = WaitForSingleObject(hEvent, 100); // FIXME: dwMilliseconds constant
if (event_state == WAIT_OBJECT_0) {
p_thread_param_si->detected = TRUE;
CloseHandle(hEvent);
return TRUE;
}
else if (event_state == WAIT_TIMEOUT) {
p_thread_param_si->detected = FALSE;
TerminateThread(p_thread_param_si->h_slave_thread, 0);
CloseHandle(hEvent);
return TRUE;
}
return FALSE;
}
DWORD Cuckoo::ThreadInfiniteSleepSlave(LPVOID thread_params) {
ptpsi p_thread_param_si = static_cast<ptpsi>(thread_params);
HANDLE hEvent;
BOOL b_set_event;
if (!p_thread_param_si)
return FALSE;
// barrier sync
InterlockedIncrement(&dwThreadMasterSlaveBarrier);
while (p_thread_param_si->threads_count != dwThreadMasterSlaveBarrier);
// perform infinite delay sleep
Sleep(INFINITE);
// in case INFINITE delay is skipped, the code will continue execution from the following instructions
if (!(hEvent = OpenEventW(EVENT_ALL_ACCESS, FALSE, p_thread_param_si->event_name)))
return FALSE;
b_set_event = !SetEvent(hEvent);
CloseHandle(hEvent);
return b_set_event;
}
DWORD Cuckoo::ThreadDelaysAccumulationMaster(LPVOID thread_params) {
ptpsi p_thread_param_si = static_cast<ptpsi>(thread_params);
HANDLE hEvent;
DWORD event_state;
// SYSTEMTIME st_start, st_end;
LARGE_INTEGER st_start, st_end;
if (!p_thread_param_si)
return FALSE;
NTSTATUS(NTAPI *fnNtQuerySystemTime)(OUT PLARGE_INTEGER SystemTime) = (NTSTATUS(NTAPI *)(OUT PLARGE_INTEGER SystemTime))GetProcAddress(GetModuleHandleW(L"ntdll"), "NtQuerySystemTime");
if (!fnNtQuerySystemTime)
return FALSE;
// get current system time at the begining of a function
fnNtQuerySystemTime(&st_start);
// FIXME: should be deleted
// printf("Seconds passed: %llu\n", st_start.QuadPart / (10000 * 1000));
// barrier sync
InterlockedIncrement(&dwThreadMasterSlaveBarrier);
while (p_thread_param_si->threads_count != dwThreadMasterSlaveBarrier);
if (!(hEvent = CreateEventW(NULL, FALSE, FALSE, p_thread_param_si->event_name)))
return FALSE;
event_state = WaitForSingleObject(hEvent, 100); // FIXME: dwMilliseconds constant
if (event_state == WAIT_OBJECT_0) {
// sleep was definitely skipped, but let's check the current date
fnNtQuerySystemTime(&st_end);
// printf("Seconds passed: %llu\n", st_end.QuadPart / (10000 * 1000));
unsigned long long diff = (st_end.QuadPart - st_start.QuadPart) / (10000 * 1000);
// printf("Difference between two intervals: %llu\n", diff);
// FIXME: value should not be taken from ass :)
p_thread_param_si->detected = diff > 2 * 24 * 60 * 60;
CloseHandle(hEvent);
return TRUE;
}
else if (event_state == WAIT_TIMEOUT) {
p_thread_param_si->detected = FALSE;
TerminateThread(p_thread_param_si->h_slave_thread, 0);
CloseHandle(hEvent);
return TRUE;
}
return FALSE;
}
DWORD Cuckoo::ThreadDelaysAccumulationSlave(LPVOID thread_params) {
ptpsi p_thread_param_si = static_cast<ptpsi>(thread_params);
HANDLE hEvent;
BOOL b_set_event;
if (!p_thread_param_si)
return FALSE;
// barrier sync
InterlockedIncrement(&dwThreadMasterSlaveBarrier);
while (p_thread_param_si->threads_count != dwThreadMasterSlaveBarrier);
// perform sleep for a few days, like from 2 till 5
// FIXME: generate random number of days to sleep
Sleep(3 * 24 * 60 * 60 * 1000); // 3 days to Zzz
// in case delay is skipped, the code will continue execution from the following instructions
if (!(hEvent = OpenEventW(EVENT_ALL_ACCESS, FALSE, p_thread_param_si->event_name)))
return FALSE;
b_set_event = !SetEvent(hEvent);
CloseHandle(hEvent);
return b_set_event;
}
/*
* Generates random buffer using printable characters.
* If suceeded, then pointer to generated buffer is returned.
* Returned buffer should be freed after usage.
*/
const event_name_t Cuckoo::GeneratePrintableBuffer(SIZE_T length, DWORD seed) {
// FARPROC fpRtlGenRandom;
event_name_t event_name(L"");
if (length <= 0)
return event_name;
// FIXME: add RtlGenRandom & hexlify usage
/*
if (!RtlGenRandom((LPVOID)pEventName, length))
return NULL;
*/
srand(static_cast<unsigned int>(time(NULL) ? seed == SEED_DEFAULT : seed));
for (SIZE_T i = 0; i < length; ++i)
event_name += rand() % 26 + 'a';
return event_name;
}
bool Cuckoo::IsAnalyzerDeadNotTrackedMaster() const {
wchar_t app_params[] = L"--action --dan";
event_name_t event_name;
bool dan_escape_detected = false;
PROCESS_INFORMATION pi = { 0 };
bool cp_mode = CheckFunctionHooks();
if (!KillSuspiciousProcesses())
return false;
if (!run_self_susp(app_params, &pi))
return false;
event_name = GeneratePrintableBuffer(8, pi.dwProcessId);
dan_escape_detected = WaitForNotificationFromSlaveUsingEvent(event_name, pi.hProcess, pi.hThread, 100);
TerminateProcess(pi.hProcess, 0);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
EvasionMachineMode es = get_evasion_status(cp_mode, !dan_escape_detected);
return es == EvasionMachineMode::SANDBOX_EVADED;
}
bool Cuckoo::IsAnalyzerDeadNotTrackedSlave() const {
return NotifyFunctionHooks();
}
bool Cuckoo::IsInvalidHook() const {
DWORD old_esp, new_esp;
NTSTATUS(NTAPI *NtLoadKeyEx)(POBJECT_ATTRIBUTES, POBJECT_ATTRIBUTES, ULONG, HANDLE, HANDLE, DWORD, PHKEY, PVOID) =
reinterpret_cast<NTSTATUS(NTAPI *)(POBJECT_ATTRIBUTES, POBJECT_ATTRIBUTES, ULONG, HANDLE, HANDLE, DWORD, PHKEY, PVOID)>(GetProcAddress(GetModuleHandleW(L"ntdll"), "NtLoadKeyEx"));
if (!NtLoadKeyEx)
return false;
__try
{
_asm mov old_esp, esp
NtLoadKeyEx(nullptr, nullptr, 0, nullptr, nullptr, 0, nullptr, nullptr);
_asm mov new_esp, esp
_asm mov esp, old_esp
if (old_esp != new_esp)
return true;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return true;
}
return false;
}
bool Cuckoo::IsLackArgChecks() const {
DWORD tick_start, time_elapsed_ms;
__declspec(align(8)) BYTE aligned[sizeof(LARGE_INTEGER) * 2];
PLARGE_INTEGER DelayInterval = reinterpret_cast<PLARGE_INTEGER>(aligned + 1); //unaligned
DelayInterval->QuadPart = 1000 * (-10000LL);
NTSTATUS (NTAPI *NtDelayExecution)(BOOLEAN, PLARGE_INTEGER) =
reinterpret_cast<NTSTATUS(NTAPI *)(BOOLEAN, PLARGE_INTEGER)>(GetProcAddress(GetModuleHandleW(L"ntdll"), "NtDelayExecution"));
if (!NtDelayExecution)
return false;
__try
{
if (NtDelayExecution(FALSE, nullptr) != STATUS_ACCESS_VIOLATION)
return true;
if (NtDelayExecution(FALSE, reinterpret_cast<PLARGE_INTEGER>(0xFFDF0000)) != STATUS_ACCESS_VIOLATION)
return true;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return true;
}
tick_start = GetTickCount();
if (NtDelayExecution(FALSE, DelayInterval) != STATUS_DATATYPE_MISALIGNMENT)
return true;
// If the pointer is not aligned the delay should not be performed
time_elapsed_ms = GetTickCount() - tick_start;
if (time_elapsed_ms > 500)
return true;
return false;
}
bool Cuckoo::RunMasterSlaveThreads( DWORD(WINAPI SandboxEvasion::Cuckoo::*thread_master)(LPVOID),
DWORD(WINAPI SandboxEvasion::Cuckoo::*thread_slave)(LPVOID)) {
DWORD tid_master, tid_slave;
HANDLE hThread_master, hThread_slave;
event_name_t event_name;
event_name = GeneratePrintableBuffer(7);
dwThreadMasterSlaveBarrier = 0;
tpsi thread_param_si = { event_name.c_str(), 2, FALSE };
thread_wrapper
tw_m = { this, thread_master, &thread_param_si },
tw_s = { this, thread_slave, &thread_param_si };
// create slave thread
if (!(hThread_slave = CreateThread(
NULL,
0,
thread_wrapper_routine,
&tw_s,
0,
&tid_slave
))) {
return false;
}
thread_param_si.h_slave_thread = hThread_slave;
// create master thread
if (!(hThread_master = CreateThread(
NULL,
0,
thread_wrapper_routine,
&tw_m,
0,
&tid_master
))) {
TerminateThread(hThread_slave, 0);
return false;
}
// wait for threads to finish
HANDLE hThreads[] = { hThread_master, hThread_slave };
DWORD dwWaitObjects;
bool si_detected = false;
dwWaitObjects = WaitForMultipleObjects(sizeof(hThreads) / sizeof(HANDLE), hThreads, TRUE, INFINITE);
if (dwWaitObjects == WAIT_OBJECT_0) {
si_detected = thread_param_si.detected;
}
CloseHandle(hThread_master);
CloseHandle(hThread_slave);
return si_detected;
}
bool Cuckoo::RunMasterSlaveThreadsEventName(DWORD(WINAPI SandboxEvasion::Cuckoo::*thread_master)(LPVOID),
DWORD(WINAPI SandboxEvasion::Cuckoo::*thread_slave)(LPVOID)) {
PROCESS_INFORMATION pi = {};
wchar_t app_params[] = L"--action --evt";
DWORD event_name_detected = false;
if (!run_self_susp(app_params, &pi))
return false;
// check if event with specific name is present implement
tpen thread_param_en = { 2, pi.hProcess, pi.hThread, pi.dwProcessId, 0, false };
DWORD tid_master, tid_slave;
HANDLE hThread_master, hThread_slave;
dwThreadMasterSlaveBarrier = 0;
thread_wrapper
tw_m = { this, thread_master, &thread_param_en },
tw_s = { this, thread_slave, &thread_param_en };
// create slave thread
if (!(hThread_slave = CreateThread(
NULL,
0,
thread_wrapper_routine,
&tw_s,
0,
&tid_slave
))) {
return false;
}
thread_param_en.h_slave_thread = hThread_slave;
// create master thread
if (!(hThread_master = CreateThread(
NULL,
0,
thread_wrapper_routine,
&tw_m,
0,
&tid_master
))) {
TerminateThread(hThread_slave, 0);
return false;
}
// wait for threads to finish
HANDLE hThreads[] = { hThread_master, hThread_slave };
DWORD dwWaitObjects;
bool en_detected = false;
dwWaitObjects = WaitForMultipleObjects(sizeof(hThreads) / sizeof(HANDLE), hThreads, TRUE, INFINITE);
if (dwWaitObjects == WAIT_OBJECT_0) {
en_detected = thread_param_en.detected;
}
CloseHandle(hThread_master);
CloseHandle(hThread_slave);
return en_detected;
}
bool Cuckoo::CheckFunctionHooks(HANDLE hProcess, const func_hooked_t &func_hooked) const {
FARPROC fpProcAddr;
for (auto lib_i = func_hooked.cbegin(), lib_e = func_hooked.cend(); lib_i != lib_e; ++lib_i) {
for (auto &func_i : lib_i->second) {
// enumerate over each function and get its address
fpProcAddr = GetProcAddress(GetModuleHandleW(lib_i->first.c_str()), func_i.c_str());
if (!fpProcAddr)
continue;
if (IsFunctionHooked(hProcess, fpProcAddr))
return true;
}
}
return false;
}
bool Cuckoo::IsFunctionHooked(HANDLE hProcess, FARPROC fpProcAddr) const {
DWORD dwNumberOfBytesRead;
BYTE code[0x10] = { };
std::list<bool (SandboxEvasion::Cuckoo::*)(const BYTE*, SIZE_T) const> hook_check {
&Cuckoo::IsCodeTrampoline,
&Cuckoo::IsCodePushRet
};
if (!ReadProcessMemory(
hProcess,
fpProcAddr,
code,
sizeof(code),
&dwNumberOfBytesRead
))
return false;
// enumerate all hooking check functions
for (auto &hc : hook_check) {
if (((const_cast<SandboxEvasion::Cuckoo*>(this))->*(hc))(code, dwNumberOfBytesRead))
return true;
}
return false;
}
bool Cuckoo::IsCodeTrampoline(const BYTE *code, SIZE_T code_size) const {
SIZE_T i = 0;
// skip all nops at the beggining
while (i < code_size && code[i] == 0x90)
++i;
// check `jmp xxx` opcodes and `jmp dword ptr [xxx]`
return ((i < code_size) && (code[i] == 0xe9 || code[i] == 0xeb))
|| (i + 1 < code_size && *LPWORD(code) == 0x25FF);
}
bool Cuckoo::IsCodePushRet(const BYTE *code, SIZE_T code_size) const {
SIZE_T i = 0;
// skip all nops at the beggining
while (i < code_size && code[i] == 0x90)
++i;
if (i + 5 >= code_size)
return false;
// check push/retn instructions
return code[i] == 0x68 && code[i + 5] == 0xc3;
}
bool Cuckoo::IsPidReusedNotTrackedMaster() const {
std::set<DWORD> pids;
PROCESS_INFORMATION pi = {};
wchar_t app_params[] = L"--action --pid";
event_name_t event_name;
bool pid_escape_detected = false;
bool cp_mode = CheckFunctionHooks();
while (true) {
// create process with parametres
if (!run_self_susp(app_params, &pi))
break;
// process is created in suspended state, check if pid is reused
const bool found = pids.find(pi.dwProcessId) != pids.end();
if (found) {
event_name = GeneratePrintableBuffer(8, pi.dwProcessId);
// FIXME: should timeout be configurable
pid_escape_detected = WaitForNotificationFromSlaveUsingEvent(event_name, pi.hProcess, pi.hThread, 100);
}
else {
TerminateProcess(pi.hProcess, 0);
pids.insert(pi.dwProcessId);
}
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
if (found)
break;
}
EvasionMachineMode es = get_evasion_status(cp_mode, !pid_escape_detected);
return es == EvasionMachineMode::SANDBOX_EVADED;
}
bool Cuckoo::IsPidReusedNotTrackedSlave() const {
return NotifyFunctionHooks();
}
bool Cuckoo::NotifyFunctionHooks() const {
event_name_t event_name;
HANDLE hEvent;
event_name = GeneratePrintableBuffer(8, GetCurrentProcessId());
if (!(hEvent = OpenEventW(EVENT_ALL_ACCESS, FALSE, event_name.c_str()))) {
return false;
}
// check if functions are hooked by cuckoomon
if (!CheckFunctionHooks()) {
const bool b_set_event = !SetEvent(hEvent);
CloseHandle(hEvent);
return b_set_event;
}
CloseHandle(hEvent);
return true;
}
bool Cuckoo::CheckExceptionsNumberMaster() const {
PROCESS_INFORMATION pi = {};
wchar_t app_params[] = L"--action --exc";
DWORD ec;
bool exception_escape_detected = false;
if (!run_self_susp(app_params, &pi))
return false;
if (ResumeThread(pi.hThread) == -1) {
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
return false;
}
// implement wait for application to finish
do {
if (!GetExitCodeProcess(pi.hProcess, &ec)) {
ec = STILL_ACTIVE;
break;
}
} while (ec == STILL_ACTIVE);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
// fprintf(stdout, "{+} Exceptions process exit code: 0x%x\n", ec);
// check if exit code is a specific one
return ec == SLAVE_EXIT_CODE_FAILED;
}
bool Cuckoo::CheckExceptionsNumberSlave() const {
uint32_t exceptions_count;
// disable message boxes
SetErrorMode(SEM_NOGPFAULTERRORBOX | SEM_FAILCRITICALERRORS);
// generate specific number of exceptions in order to ExitProcess and thus check if we are running in sandbox environment
for (exceptions_count = 0; exceptions_count < SandboxEvasion::EXCEPTION_MAXCOUNT; ++exceptions_count) {
__try {
RaiseException(EXCEPTION_CHECK_EXC + exceptions_count, 0, 0, NULL);
}
__except (EXCEPTION_EXECUTE_HANDLER) {
// fprintf(stdout, "[+] Exception raised for %u time\n", exceptions_count + 1);
}
}
ExitProcess(SLAVE_EXIT_CODE_SUCCESS);
return true;
}
bool Cuckoo::IsWMINotTrackedMaster() const {
wchar_t app_params[] = L"--action --wmi";
DWORD pid;
HANDLE hProcess;
HANDLE hThread;
std::vector<DWORD> tids;
event_name_t event_name;
bool wmi_escape_detected;
bool cp_mode = CheckFunctionHooks();
if (!run_self_susp_wmi(app_params, &pid))
return false;
// get handles to process and main thread
if (!get_all_tids_by_pid(pid, tids) || tids.size() != 1)
return false;
hThread = open_thread_by_tid(tids[0], THREAD_SUSPEND_RESUME);
if (hThread == INVALID_HANDLE_VALUE)
return false;
hProcess = open_process_by_pid(pid, PROCESS_QUERY_INFORMATION);
if (hProcess == INVALID_HANDLE_VALUE) {
// FIXME: try to open for termination ???
CloseHandle(hThread);
return false;
}
// waiting for notification from the child process
event_name = GeneratePrintableBuffer(8, pid);
// FIXME: should timeout be configurable
wmi_escape_detected = WaitForNotificationFromSlaveUsingEvent(event_name, hProcess, hThread, 100);
CloseHandle(hThread);
CloseHandle(hProcess);
EvasionMachineMode es = get_evasion_status(cp_mode, !wmi_escape_detected);
return es == EvasionMachineMode::SANDBOX_EVADED;
}
bool Cuckoo::IsWMINotTrackedSlave() const {
event_name_t event_name;
HANDLE hEvent;
event_name = GeneratePrintableBuffer(8, GetCurrentProcessId());
if (!(hEvent = OpenEventW(EVENT_ALL_ACCESS, FALSE, event_name.c_str())))
return false;
// check if functions are hooked by cuckoomon
if (!CheckFunctionHooks()) {
const bool b_set_event = !SetEvent(hEvent);
CloseHandle(hEvent);
return b_set_event;
}
CloseHandle(hEvent);
return false;
}
bool Cuckoo::IsTaskSchedNotTrackedMaster() const {
wchar_t app_params[] = L"--action --tsh";
DWORD pid;
HANDLE hProc;
DWORD ec;
bool cp_mode = CheckFunctionHooks();
if (!run_self_tsched(app_params, &pid))
return false;
// waiting for the task notification
if (!pipe_server_get_pid(se::task_pipe_name, se::task_proc_wait_timeout, &pid))
return false;
hProc = open_process_by_pid(pid, PROCESS_QUERY_INFORMATION);
if (hProc == INVALID_HANDLE_VALUE)
return false;
// check process exit code
do {
if (!GetExitCodeProcess(hProc, &ec)) {
CloseHandle(hProc);
return false;
}
} while (ec == STILL_ACTIVE);
EvasionMachineMode es = get_evasion_status(cp_mode, ec != TASK_HOOKS_NOT_DETECTED);
return es == EvasionMachineMode::SANDBOX_EVADED;
}
bool Cuckoo::IsTaskSchedNotTrackedSlave() const {
const DWORD delay_timeout = 5000;
if (!pipe_server_send_pid(se::task_pipe_name, se::task_proc_wait_timeout, GetCurrentProcessId())) {
Sleep(delay_timeout);
ExitProcess(0xFEEDDCCB); // just random exit code
}
Sleep(delay_timeout);
ExitProcess(CheckFunctionHooks() ? TASK_HOOKS_DETECTED : TASK_HOOKS_NOT_DETECTED);
return true;
}
bool Cuckoo::WaitForNotificationFromSlaveUsingEvent(const event_name_t &event_name, HANDLE hProcess, HANDLE hThread, DWORD timeout) const {
HANDLE hEvent;
DWORD event_state;
DWORD child_exit_code;
bool is_notified = false;
if (!(hEvent = CreateEventW(NULL, FALSE, FALSE, event_name.c_str())))
return false;
// resume thread, then wait for notification status
ResumeThread(hThread);
// wait for the response from the child process about its current state
while (true) {
event_state = WaitForSingleObject(hEvent, timeout); // FIXME: dwMilliseconds constant
if (event_state == WAIT_OBJECT_0) {
is_notified = true;
break;
}
else if (event_state != WAIT_TIMEOUT) {
break;
}
else {
// check process state if is finished, then detection failed, otherwise wait one more time in loop
if (!GetExitCodeProcess(hProcess, &child_exit_code) || child_exit_code != STILL_ACTIVE) {
// we should check here for the last time of event object was not signaled, as far as it may be signaled before exiting
is_notified = WaitForSingleObject(hEvent, timeout) == WAIT_OBJECT_0; // FIXME: dwMilliseconds constant
break;
}
}
}
CloseHandle(hEvent);
return is_notified;
}
DWORD Cuckoo::ThreadCheckEventNameSlave(LPVOID thread_params) {
ptpen p_thread_params_en = static_cast<ptpen>(thread_params);
if (!p_thread_params_en)
return FALSE;
HANDLE hProc = p_thread_params_en->hProcess;
DWORD pid = p_thread_params_en->pid;
char event_name[64] = {};
HANDLE hEvent;
const DWORD wait_timeout = 50;
DWORD ec;
_snprintf_s(event_name, _countof(event_name), "CuckooEvent%d", pid);
// barrier sync
InterlockedIncrement(&dwThreadMasterSlaveBarrier);
while (p_thread_params_en->threads_count != dwThreadMasterSlaveBarrier);
// wait in loop for specific event
while (true) {
hEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, event_name);
if (hEvent) {
CloseHandle(hEvent);
p_thread_params_en->detected = true;
break;
}
if (!GetExitCodeProcess(hProc, &ec) || ec != STILL_ACTIVE) {
p_thread_params_en->detected = false;
break;
}
Sleep(wait_timeout);
}
return TRUE;
}
DWORD Cuckoo::ThreadCheckEventNameMaster(LPVOID thread_params) {
ptpen p_thread_params_en = static_cast<ptpen>(thread_params);
if (!p_thread_params_en)
return FALSE;
// barrier sync
InterlockedIncrement(&dwThreadMasterSlaveBarrier);
while (p_thread_params_en->threads_count != dwThreadMasterSlaveBarrier);
// resume thread execution from the created process
if (ResumeThread(p_thread_params_en->hThread) == -1) {
return TerminateThread(p_thread_params_en->h_slave_thread, 0);
}
return TRUE;
}
DWORD WINAPI Cuckoo::ThreadCheckSocketTimeoutMaster(LPVOID thread_params) {
ptpsi p_thread_params_en = static_cast<ptpsi>(thread_params);
DWORD tick_start, tick_end, time_elapsed_ms;
const DWORD timeout = 20000; // should be multiple of 1000! The same as in slave thread
const DWORD max_error = 100; // max error is 100 ms
// DWORD thread_max_wait = timeout << 1;
DWORD thread_slave_wait_state;
DWORD thread_slave_exit_code;
if (!p_thread_params_en)
return FALSE;
// barrier sync
InterlockedIncrement(&dwThreadMasterSlaveBarrier);
while (p_thread_params_en->threads_count != dwThreadMasterSlaveBarrier);
tick_start = GetTickCount();
// puts("Sleep...");
Sleep(timeout); // on normal systems should sleep around timeout
// puts("Sleep finished");
thread_slave_wait_state = WaitForSingleObject(p_thread_params_en->h_slave_thread, INFINITE);
if (thread_slave_wait_state != WAIT_OBJECT_0)
return FALSE;
if (GetExitCodeThread(p_thread_params_en->h_slave_thread, &thread_slave_exit_code) == FALSE)
return FALSE;
if (thread_slave_exit_code == FALSE)
return FALSE;
// normally we should receive a notification just after sleep, because delay is the same as for socket
// however in the Cuckoo the delays must probably will be skipped, thus time flow will not be the same
tick_end = GetTickCount();
time_elapsed_ms = tick_end - tick_start;
// printf("\tRequested %d elapsed %d\n", timeout, time_elapsed_ms);
p_thread_params_en->detected = abs(static_cast<long>(time_elapsed_ms) - static_cast<long>(timeout)) > max_error;
return TRUE;
}
DWORD WINAPI Cuckoo::ThreadCheckSocketTimeoutSlave(LPVOID thread_params) {
ptpsi p_thread_params_en = static_cast<ptpsi>(thread_params);
if (!p_thread_params_en)
return FALSE;
// barrier sync
InterlockedIncrement(&dwThreadMasterSlaveBarrier);
while (p_thread_params_en->threads_count != dwThreadMasterSlaveBarrier);
int iResult;
DWORD timeout = 20000; // should be multiple of 1000!
DWORD OK = TRUE;
SOCKADDR_IN sa = { 0 };
SOCKET sock = INVALID_SOCKET;
// this code snippet should take around lTimeout milliseconds, take a look on Master thread for the better understanding
do {
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
// sa.sin_addr.s_addr = inet_addr("132.249.0.22"); // TODO: how to choose IP address ?
sa.sin_addr.s_addr = inet_addr("8.8.4.3"); // WARNING: we should have a route to this IP address, however we should not be able to connect to it
sa.sin_port = htons(64523);
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock == INVALID_SOCKET) {
// wprintf(L"socket function failed with error: %ld\n", WSAGetLastError());
OK = FALSE;
break;
}
// setting socket timeout
unsigned long iMode = 1;
iResult = ioctlsocket(sock, FIONBIO, &iMode);
iResult = connect(sock, (SOCKADDR*)&sa, sizeof(sa));
if (iResult == false) {
// wprintf(L"\tconnect failed with error: %d\n", WSAGetLastError());
OK = FALSE;
break;
}
// restart the socket mode
iMode = 0;
iResult = ioctlsocket(sock, FIONBIO, &iMode);
if (iResult != NO_ERROR) {
// printf("ioctlsocket failed with error: %ld\n", iResult);
OK = FALSE;
break;
}
// fd set data
fd_set Write, Err;
FD_ZERO(&Write);
FD_ZERO(&Err);
FD_SET(sock, &Write);
FD_SET(sock, &Err);
timeval tv = { 0 };
tv.tv_sec = timeout / 1000;
// check if the socket is ready, this call should take lTimeout milliseconds
// puts("select...");
select(0, NULL, &Write, &Err, &tv);
// puts("select finished");
if (FD_ISSET(sock, &Err)) {
// printf("Last socket error: %d\n", WSAGetLastError());
OK = FALSE;
break;
}
} while (false);
if (sock != INVALID_SOCKET)
closesocket(sock);
return OK;
}
bool Cuckoo::CommunicateWithAgent(const network_endpoint_t &net_endpoint, unsigned char *agent_response, size_t *agent_response_size) const {
HINTERNET hInternet;
HINTERNET hSession;
HINTERNET hRequest;
PCSTR rgpszAcceptTypes[] = { "gzip", NULL};
DWORD bytes_read;
DWORD bytes_total_read;
const DWORD net_timeout = 3000;
struct in_addr ia;
ia.S_un.S_addr = net_endpoint.first ? net_endpoint.first : LOCALHOST;
char *addr = inet_ntoa(ia);
if (!addr)
return false;
const INTERNET_PORT port = ntohs(static_cast<INTERNET_PORT>(net_endpoint.second));
// fprintf(stdout, "{+}\t%s:%u\n", addr, port);
if (!(hInternet = InternetOpenA("xmlrpclib.py/1.0.1 (by www.pythonware.com)", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0)))
return false;
// set timeout options for the connection and send operations
InternetSetOptionA(hInternet, INTERNET_OPTION_RECEIVE_TIMEOUT, const_cast<DWORD *>(&net_timeout), sizeof(net_timeout));
InternetSetOptionA(hInternet, INTERNET_OPTION_SEND_TIMEOUT, const_cast<DWORD *>(&net_timeout), sizeof(net_timeout));
InternetSetOptionA(hInternet, INTERNET_OPTION_CONNECT_TIMEOUT, const_cast<DWORD *>(&net_timeout), sizeof(net_timeout));
// connect to service
if (!(hSession = InternetConnectA(hInternet, addr, port, NULL, NULL, INTERNET_SERVICE_HTTP, 0, NULL))) {
InternetCloseHandle(hInternet);
return false;
}
if (!(hRequest = HttpOpenRequestA(hSession, "POST", "RPC2", "HTTP/1.1", NULL, rgpszAcceptTypes, INTERNET_FLAG_NO_AUTH | INTERNET_FLAG_NO_COOKIES | INTERNET_FLAG_RELOAD, 0))) {
InternetCloseHandle(hInternet);
InternetCloseHandle(hSession);
return false;
}
// FIXME: do we need to add more methods than just one get_status
std::string headers("Content-Type: text/xml\n");
std::string request("<?xml version='1.0'?>\n<methodCall>\n<methodName>get_status</methodName>\n<params>\n</params>\n</methodCall>\n");
if (!HttpSendRequestA(hRequest, headers.c_str(), headers.length(), const_cast<char *>(request.c_str()), request.length())) {
// fprintf(stdout, "[+] HttpSendRequest last error: %x\n", GetLastError());
InternetCloseHandle(hInternet);
InternetCloseHandle(hSession);
InternetCloseHandle(hRequest);
return false;
}
bytes_read = 0;
bytes_total_read = 0;
while (InternetReadFile(hRequest, agent_response + bytes_total_read, *agent_response_size - bytes_total_read, &bytes_read) && bytes_read) {
bytes_total_read += bytes_read;
agent_response[bytes_total_read] = 0;
bytes_read = 0;
}
InternetCloseHandle(hInternet);
InternetCloseHandle(hSession);
InternetCloseHandle(hRequest);
*agent_response_size = bytes_total_read;
return bytes_total_read != 0;
}
bool Cuckoo::CheckResponseIsAgent(const unsigned char *response, size_t response_size) const {
/*
HTTP/1.0 200 OK
Server: BaseHTTP/0.3 Python/2.7.10
Date: Fri, 06 May 2016 17:30:50 GMT
Content-type: text/xml
Content-length: 121
<?xml version='1.0'?>
<methodResponse>
<params>
<param>
<value><int>1</int></value>
</param>
</params>
</methodResponse>
*/
/*
char *hex_response = hexlify(response, response_size);
if (!hex_response)
return false;
fprintf(stdout, "[+] HttpResponse: %s\n", hex_response);
free(hex_response);
*/
std::vector<std::basic_string<char>> matches;
// FIXME: should be moved as a class member ?
const std::string agent_response_re("<\\?xml version='1\\.0'\\?><methodResponse><params><param><value><int>([[:digit:]]+)</int></value></param></params></methodResponse>");
std::string s_response(reinterpret_cast<const char *>(response));
s_response.erase(std::remove(s_response.begin(), s_response.end(), '\r'), s_response.end());
s_response.erase(std::remove(s_response.begin(), s_response.end(), '\n'), s_response.end());
if (!match_regexp<char>(agent_response_re, s_response, &matches))
return false;
return matches.size() == 1;
}
/*
* Try to kill Analyzer
*/
bool Cuckoo::KillSuspiciousProcesses() const {
STARTUPINFO si = { 0 };
PROCESS_INFORMATION pi = { 0 };
std::list<std::string> proc_names { "python", "pythonw" };
std::list<cp_pids> pc_proc;
HANDLE hProcess;
char proc_name[MAX_PATH + 1] = { 0 };
char proc_args[0x80] = { 0 };
if (get_parent_child_proc_pair(pc_proc, proc_names) == FALSE)
return false;
if (!pc_proc.size())
return false;
// try to terminate Analyzer process
for (const auto &pc : pc_proc) {
// kill all child processes
if ((hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pc.second)) != NULL) {
TerminateProcess(hProcess, 0xDEAD);
CloseHandle(hProcess);
}
}
return true;
}
/*
* Calculate difference between two SYSTEMTIMEs
* Return value in milliseconds
*/
ULARGE_INTEGER Cuckoo::CompareDatetime(SYSTEMTIME *st_1, SYSTEMTIME *st_2) {
FILETIME ft;
ULARGE_INTEGER st_diff;
ULONGLONG v_1, v_2;
SystemTimeToFileTime(st_1, &ft);
st_diff.LowPart = ft.dwLowDateTime;
st_diff.HighPart = ft.dwHighDateTime;
v_1 = st_diff.QuadPart;
SystemTimeToFileTime(st_2, &ft);
st_diff.LowPart = ft.dwLowDateTime;
st_diff.HighPart = ft.dwHighDateTime;
v_2 = st_diff.QuadPart;
st_diff.QuadPart = v_1 - v_2;
return st_diff;
}
/*
bool Cuckoo::IsAgentArtifact(PVOID proc_args) {
pfi *ppfi = static_cast<pfi *>(proc_args);
if (!ppfi)
return false;
for (const auto &bs : ppfi->file_names) {
if (!bs.compare(ppfi->file_name)) {
ppfi->matched = true;
return true;
}
}
return false;
}
*/
extern "C"
DWORD WINAPI thread_wrapper_routine(LPVOID _ptw) {
thread_wrapper *pTW = static_cast<thread_wrapper *>(_ptw);
if (!pTW)
return NULL;
if (!pTW->thread_routine)
return NULL;
// return (((SandboxEvasion::Cuckoo*)(pTW->obj))->*(pTW->thread_routine))(pTW->thread_params);
return ((const_cast<SandboxEvasion::Cuckoo*>(pTW->obj))->*(pTW->thread_routine))(pTW->thread_params);
}
bool resolve_func_addresses(const func_hooked_t &fn, pchta *pta_args) {
size_t cnt = 0;
FARPROC fp;
HMODULE hModule;
bool loaded;
if (!pta_args)
return false;
for (auto &l : fn) {
loaded = false;
hModule = GetModuleHandleW(l.first.c_str());
if (!hModule) {
hModule = LoadLibraryW(l.first.c_str());
if (!hModule)
return false;
}
for (auto &f : l.second) {
fp = GetProcAddress(hModule, f.c_str());
if (!fp) {
if (loaded) {
FreeLibrary(hModule);
return false;
}
}
pta_args->__func_ptrs[cnt++] = fp;
}
if (loaded)
FreeLibrary(hModule);
}
return true;
}
} // SandboxEvasion