Init commit
This commit is contained in:
+329
@@ -0,0 +1,329 @@
|
||||
#include "Common.h"
|
||||
#include "Exclusions.h"
|
||||
#include "Paths.h"
|
||||
#include "Logger.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
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<wchar_t> 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<std::wstring> DefaultExclusionPaths()
|
||||
{
|
||||
std::vector<std::wstring> 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<std::wstring>& 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<std::wstring> QueryExclusions(std::wstring& report)
|
||||
{
|
||||
std::vector<std::wstring> 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<std::wstring> LoadExclusionList()
|
||||
{
|
||||
std::vector<std::wstring> 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<std::wstring>& 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<std::wstring>& 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();
|
||||
}
|
||||
Reference in New Issue
Block a user