#include "Common.h" #include "Exclusions.h" #include "Paths.h" #include "Logger.h" #include #include static const wchar_t* kConfigKey = L"SOFTWARE\\ArDrvLoader"; static const wchar_t* kExclusionValue = L"ExclusionPaths"; static std::wstring PowerShellPath() { wchar_t sys[MAX_PATH]{}; if (GetSystemDirectoryW(sys, _countof(sys)) == 0) return L"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe"; return std::wstring(sys) + L"\\WindowsPowerShell\\v1.0\\powershell.exe"; } // Runs a PowerShell one-liner and captures stdout. static bool RunPowerShell(const std::wstring& script, std::wstring& output) { output.clear(); SECURITY_ATTRIBUTES sa{}; sa.nLength = sizeof(sa); sa.bInheritHandle = TRUE; HANDLE readEnd = nullptr; HANDLE writeEnd = nullptr; if (!CreatePipe(&readEnd, &writeEnd, &sa, 0)) return false; SetHandleInformation(readEnd, HANDLE_FLAG_INHERIT, 0); STARTUPINFOW si{}; si.cb = sizeof(si); si.dwFlags = STARTF_USESTDHANDLES; si.hStdOutput = writeEnd; si.hStdError = writeEnd; si.hStdInput = nullptr; PROCESS_INFORMATION pi{}; std::wstring cmd = L"\"" + PowerShellPath() + L"\" -NoProfile -NonInteractive -ExecutionPolicy Bypass -Command \"" + script + L"\""; std::vector mutableCmd(cmd.begin(), cmd.end()); mutableCmd.push_back(L'\0'); BOOL ok = CreateProcessW( nullptr, mutableCmd.data(), nullptr, nullptr, TRUE, CREATE_NO_WINDOW, nullptr, nullptr, &si, &pi); CloseHandle(writeEnd); if (!ok) { CloseHandle(readEnd); return false; } std::string bytes; char buf[4096]{}; DWORD read = 0; while (ReadFile(readEnd, buf, sizeof(buf), &read, nullptr) && read > 0) bytes.append(buf, read); WaitForSingleObject(pi.hProcess, 60000); CloseHandle(readEnd); CloseHandle(pi.hThread); CloseHandle(pi.hProcess); int need = MultiByteToWideChar(CP_UTF8, 0, bytes.c_str(), (int)bytes.size(), nullptr, 0); if (need > 0) { output.resize(need); MultiByteToWideChar(CP_UTF8, 0, bytes.c_str(), (int)bytes.size(), output.data(), need); } return true; } bool DefenderPresent() { HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (snap == INVALID_HANDLE_VALUE) return false; PROCESSENTRY32W pe{}; pe.dwSize = sizeof(pe); bool found = false; if (Process32First(snap, &pe)) { do { if (_wcsicmp(pe.szExeFile, L"MsMpEng.exe") == 0) { found = true; break; } } while (Process32Next(snap, &pe)); } CloseHandle(snap); return found; } std::vector DefaultExclusionPaths() { std::vector paths; paths.push_back(ArdrvRoot()); paths.push_back(ArdrvStageDir()); wchar_t module[MAX_PATH]{}; DWORD len = GetModuleFileNameW(nullptr, module, _countof(module)); if (len > 0 && len < _countof(module)) paths.push_back(module); return paths; } static std::wstring EscapePowerShell(const std::wstring& s) { std::wstring out; for (wchar_t c : s) { if (c == L'\'') out += L"''"; else out += c; } return out; } bool ApplyExclusions(const std::vector& paths, std::wstring& report) { report.clear(); if (paths.empty()) { report = L"no paths"; return false; } std::wstring script = L"$ErrorActionPreference='Stop'; "; for (const auto& p : paths) script += L"try { Add-MpPreference -ExclusionPath '" + EscapePowerShell(p) + L"' -ErrorAction Stop; Write-Output ('APPLIED ' + '" + EscapePowerShell(p) + L"') } " + L"catch { Write-Output ('REFUSED ' + '" + EscapePowerShell(p) + L"' + ' :: ' + " + L"$_.Exception.GetType().Name) }; "; script += L"try { (Get-MpPreference).ExclusionPath | ForEach-Object { 'NOW ' + $_ } } catch { 'NOW unavailable' }"; std::wstring output; if (!RunPowerShell(script, output)) { report = L"powershell failed"; return false; } report = output; bool applied = output.find(L"APPLIED") != std::wstring::npos; Log(L"[excl] apply result: %s", applied ? L"applied" : L"refused by policy"); return applied; } std::vector QueryExclusions(std::wstring& report) { std::vector paths; report.clear(); std::wstring output; if (!RunPowerShell(L"try { (Get-MpPreference).ExclusionPath | ForEach-Object { $_ } } catch { 'ERR ' + $_.Exception.Message }", output)) { report = L"powershell failed"; return paths; } std::wistringstream in(output); std::wstring line; while (std::getline(in, line)) { while (!line.empty() && (line.back() == L'\r' || line.back() == L'\n')) line.pop_back(); if (line.empty()) continue; if (line.rfind(L"ERR", 0) == 0) { report = line; continue; } // Defender answers with a pseudo-entry when the caller is not elevated. if (line.rfind(L"N/A", 0) == 0) { report = line; continue; } paths.push_back(line); } report = paths.empty() ? L"none" : (std::to_wstring(paths.size()) + L" entries"); return paths; } std::vector LoadExclusionList() { std::vector paths; HKEY key = nullptr; if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, kConfigKey, 0, KEY_READ, &key) != ERROR_SUCCESS) return paths; wchar_t buf[8192]{}; DWORD size = sizeof(buf); DWORD type = 0; if (RegQueryValueExW(key, kExclusionValue, nullptr, &type, (LPBYTE)buf, &size) == ERROR_SUCCESS && (type == REG_SZ || type == REG_EXPAND_SZ)) { std::wstring all = buf; std::wstring cur; for (wchar_t c : all) { if (c == L'|') { if (!cur.empty()) paths.push_back(cur); cur.clear(); } else { cur += c; } } if (!cur.empty()) paths.push_back(cur); } RegCloseKey(key); return paths; } bool SaveExclusionList(const std::vector& paths) { std::wstring joined; for (const auto& p : paths) { if (!joined.empty()) joined += L"|"; joined += p; } HKEY key = nullptr; if (RegCreateKeyExW(HKEY_LOCAL_MACHINE, kConfigKey, 0, nullptr, 0, KEY_SET_VALUE, nullptr, &key, nullptr) != ERROR_SUCCESS) { return false; } DWORD bytes = (DWORD)((joined.size() + 1) * sizeof(wchar_t)); bool ok = RegSetValueExW( key, kExclusionValue, 0, REG_SZ, (const BYTE*)joined.c_str(), bytes) == ERROR_SUCCESS; RegCloseKey(key); return ok; } std::wstring FormatExclusionList(const std::vector& paths) { if (paths.empty()) return L"none"; std::wostringstream os; os << paths.size() << L" paths"; for (const auto& p : paths) os << L"\r\n " << p; return os.str(); }