Aktualizacja: 2025-10-02 21:28:41
This commit is contained in:
@@ -40,6 +40,7 @@ void HelpSystem::PrintUsage(std::wstring_view programName) noexcept
|
||||
PrintProtectionCommands();
|
||||
PrintSessionManagement();
|
||||
PrintSystemCommands();
|
||||
PrintRegistryCommands();
|
||||
PrintBrowserCommands();
|
||||
PrintDefenderCommands();
|
||||
PrintSecurityEngineCommands();
|
||||
@@ -160,6 +161,19 @@ void HelpSystem::PrintSystemCommands() noexcept
|
||||
std::wcout << L"\n";
|
||||
}
|
||||
|
||||
void HelpSystem::PrintRegistryCommands() noexcept
|
||||
{
|
||||
PrintSectionHeader(L"Registry Backup & Defragmentation");
|
||||
PrintCommandLine(L"registry backup", L"Backup all registry hives to Downloads");
|
||||
PrintCommandLine(L"registry backup C:\\backup", L"Backup to custom directory");
|
||||
PrintCommandLine(L"registry restore C:\\backup", L"Restore hives from backup");
|
||||
PrintCommandLine(L"registry defrag", L"Defragment registry (backup+compact)");
|
||||
PrintNote(L"Backs up: BCD, SAM, SECURITY, SOFTWARE, SYSTEM, NTUSER, etc.");
|
||||
PrintNote(L"Default path: Downloads\\Registry_Backup_YYYYMMDD_HHMMSS");
|
||||
PrintNote(L"Defrag compacts hives through RegSaveKeyEx (no fragmentation)");
|
||||
std::wcout << L"\n";
|
||||
}
|
||||
|
||||
void HelpSystem::PrintDefenderCommands() noexcept
|
||||
{
|
||||
PrintSectionHeader(L"Enhanced Windows Defender Exclusion Management");
|
||||
@@ -320,71 +334,86 @@ void HelpSystem::PrintUndumpableProcesses() noexcept
|
||||
void HelpSystem::PrintUsageExamples(std::wstring_view programName) noexcept
|
||||
{
|
||||
PrintSectionHeader(L"Usage Examples");
|
||||
const int commandWidth = 50;
|
||||
|
||||
auto printLine = [&](const std::wstring& command, const std::wstring& description) {
|
||||
std::wcout << L" " << std::left << std::setw(commandWidth)
|
||||
<< (std::wstring(programName) + L" " + command)
|
||||
<< L"# " << description << L"\n";
|
||||
const int commandWidth = 60;
|
||||
|
||||
auto printLine = [commandWidth](const std::wstring& command, const std::wstring& description) {
|
||||
std::wcout << L" " << std::left << std::setw(commandWidth)
|
||||
<< command << L"# " << description << L"\n";
|
||||
};
|
||||
|
||||
// Service and system management examples
|
||||
printLine(L"shift", L"Install sticky keys backdoor");
|
||||
printLine(L"unshift", L"Remove sticky keys backdoor");
|
||||
printLine(L"install", L"Install as NT service (advanced)");
|
||||
printLine(L"service start", L"Start the service");
|
||||
printLine(L"uninstall", L"Remove service");
|
||||
|
||||
// Memory dumping examples
|
||||
printLine(L"dump lsass C:\\dumps", L"Dump LSASS to specific folder");
|
||||
printLine(L"dump 1044", L"Dump PID 1044 to Downloads folder");
|
||||
// Process inspection and monitoring
|
||||
printLine(L"kvc list", L"Show all protected processes");
|
||||
printLine(L"kvc info lsass", L"Detailed info with dumpability analysis");
|
||||
|
||||
// Process information and protection examples
|
||||
printLine(L"list", L"Show all protected processes");
|
||||
printLine(L"info lsass", L"Detailed info with dumpability analysis");
|
||||
printLine(L"protect 1044 PPL Antimalware", L"Protect process with PPL-Antimalware");
|
||||
printLine(L"set 5678 PP Windows", L"Force set PP-Windows protection");
|
||||
printLine(L"unprotect lsass", L"Remove protection from LSASS");
|
||||
printLine(L"unprotect 1,2,3,lsass", L"Batch unprotect multiple targets");
|
||||
|
||||
// Session restoration examples
|
||||
printLine(L"unprotect Antimalware", L"Remove protection from all Antimalware processes");
|
||||
printLine(L"unprotect all", L"Remove protection from ALL processes (grouped by signer)");
|
||||
printLine(L"history", L"Show saved sessions (max 16, with status tracking)");
|
||||
printLine(L"restore Antimalware", L"Restore protection for Antimalware group");
|
||||
printLine(L"restore all", L"Restore all saved protection states from current session");
|
||||
printLine(L"cleanup-sessions", L"Delete all old sessions (keep only current)");
|
||||
|
||||
// Process termination examples
|
||||
printLine(L"kill 1234", L"Terminate process with PID 1234");
|
||||
printLine(L"kill total", L"Terminate Total Commander by name");
|
||||
printLine(L"kill 1234,5678,9012", L"Terminate multiple processes");
|
||||
printLine(L"kill lsass", L"Terminate protected process (auto-elevation)");
|
||||
// Process protection management
|
||||
printLine(L"kvc protect 1044 PPL Antimalware", L"Protect process with PPL-Antimalware");
|
||||
printLine(L"kvc set 5678 PP Windows", L"Force set PP-Windows protection");
|
||||
printLine(L"kvc unprotect lsass", L"Remove protection from LSASS");
|
||||
printLine(L"kvc unprotect 1,2,3,lsass", L"Batch unprotect multiple targets");
|
||||
printLine(L"kvc unprotect Antimalware", L"Remove protection from all Antimalware processes");
|
||||
printLine(L"kvc unprotect all", L"Remove protection from ALL processes (grouped by signer)");
|
||||
|
||||
// TrustedInstaller examples
|
||||
printLine(L"trusted cmd", L"Run command as TrustedInstaller");
|
||||
printLine(L"trusted \"C:\\app.exe\" --arg", L"Run application with arguments");
|
||||
printLine(L"install-context", L"Add right-click menu entries");
|
||||
// Session state management
|
||||
printLine(L"kvc history", L"Show saved sessions (max 16, with status tracking)");
|
||||
printLine(L"kvc restore Antimalware", L"Restore protection for Antimalware group");
|
||||
printLine(L"kvc restore all", L"Restore all saved protection states from current session");
|
||||
printLine(L"kvc cleanup-sessions", L"Delete all old sessions (keep only current)");
|
||||
|
||||
// Defender exclusion examples
|
||||
printLine(L"add-exclusion", L"Add current program to exclusions");
|
||||
printLine(L"add-exclusion C:\\malware.exe", L"Add specific file to exclusions");
|
||||
printLine(L"add-exclusion Paths C:\\temp", L"Add folder to path exclusions");
|
||||
printLine(L"add-exclusion Processes cmd.exe", L"Add process to exclusions");
|
||||
printLine(L"add-exclusion Extensions .tmp", L"Add extension to exclusions");
|
||||
printLine(L"add-exclusion IpAddresses 1.1.1.1", L"Add IP to exclusions");
|
||||
printLine(L"remove-exclusion Processes cmd.exe", L"Remove process exclusion");
|
||||
// Process termination
|
||||
printLine(L"kvc kill 1234", L"Terminate process with PID 1234");
|
||||
printLine(L"kvc kill total", L"Terminate Total Commander by name");
|
||||
printLine(L"kvc kill 1234,5678,9012", L"Terminate multiple processes");
|
||||
printLine(L"kvc kill lsass", L"Terminate protected process (auto-elevation)");
|
||||
|
||||
// Security engine management examples
|
||||
printLine(L"secengine status", L"Check Windows Defender status");
|
||||
printLine(L"secengine disable", L"Disable Windows Defender engine");
|
||||
printLine(L"secengine enable", L"Re-enable Windows Defender engine");
|
||||
printLine(L"secengine disable --restart", L"Disable Defender and restart system");
|
||||
printLine(L"secengine enable --restart", L"Enable Defender and restart system");
|
||||
// Memory dumping
|
||||
printLine(L"kvc dump lsass C:\\dumps", L"Dump LSASS to specific folder");
|
||||
printLine(L"kvc dump 1044", L"Dump PID 1044 to Downloads folder");
|
||||
|
||||
// Data extraction examples
|
||||
printLine(L"export secrets", L"Export secrets to Downloads folder");
|
||||
printLine(L"export secrets C:\\reports", L"Export secrets to specific folder");
|
||||
// Service installation and management
|
||||
printLine(L"kvc install", L"Install as NT service (advanced)");
|
||||
printLine(L"kvc service start", L"Start the service");
|
||||
printLine(L"kvc uninstall", L"Remove service");
|
||||
|
||||
// System backdoors
|
||||
printLine(L"kvc shift", L"Install sticky keys backdoor");
|
||||
printLine(L"kvc unshift", L"Remove sticky keys backdoor");
|
||||
|
||||
// TrustedInstaller elevation
|
||||
printLine(L"kvc trusted cmd", L"Run command as TrustedInstaller");
|
||||
printLine(L"kvc trusted \"C:\\app.exe\" --arg", L"Run application with arguments");
|
||||
printLine(L"kvc install-context", L"Add right-click menu entries");
|
||||
|
||||
// Windows Defender exclusions
|
||||
printLine(L"kvc add-exclusion", L"Add current program to exclusions");
|
||||
printLine(L"kvc add-exclusion C:\\malware.exe", L"Add specific file to exclusions");
|
||||
printLine(L"kvc add-exclusion Paths C:\\temp", L"Add folder to path exclusions");
|
||||
printLine(L"kvc add-exclusion Processes cmd.exe", L"Add process to exclusions");
|
||||
printLine(L"kvc add-exclusion Extensions .tmp", L"Add extension to exclusions");
|
||||
printLine(L"kvc add-exclusion IpAddresses 1.1.1.1", L"Add IP to exclusions");
|
||||
printLine(L"kvc remove-exclusion Processes cmd.exe", L"Remove process exclusion");
|
||||
|
||||
// Security engine control
|
||||
printLine(L"kvc secengine status", L"Check Windows Defender status");
|
||||
printLine(L"kvc secengine disable", L"Disable Windows Defender engine");
|
||||
printLine(L"kvc secengine enable", L"Re-enable Windows Defender engine");
|
||||
printLine(L"kvc secengine disable --restart", L"Disable Defender and restart system");
|
||||
printLine(L"kvc secengine enable --restart", L"Enable Defender and restart system");
|
||||
|
||||
// Credential extraction
|
||||
printLine(L"kvc export secrets", L"Export secrets to Downloads folder");
|
||||
printLine(L"kvc export secrets C:\\reports", L"Export secrets to specific folder");
|
||||
|
||||
// Registry operations
|
||||
printLine(L"kvc registry backup", L"Backup all hives to Downloads");
|
||||
printLine(L"kvc registry backup C:\\backup", L"Backup to custom directory");
|
||||
printLine(L"kvc registry restore C:\\backup\\Registry_Backup_*", L"Restore from backup");
|
||||
printLine(L"kvc registry defrag", L"Defragment registry (backup+restore)");
|
||||
|
||||
// Browser password extraction
|
||||
printLine(L"kvc bp --edge", L"Edge only (works standalone, no kvc_pass needed)");
|
||||
printLine(L"kvc bp --all", L"Extract all browsers (requires kvc_pass.exe)");
|
||||
printLine(L"kvc bp --edge -o C:\\passwords", L"Edge with custom output directory");
|
||||
|
||||
std::wcout << L"\n";
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ public:
|
||||
static void PrintPatternMatching() noexcept;
|
||||
static void PrintTechnicalFeatures() noexcept;
|
||||
static void PrintDefenderNotes() noexcept;
|
||||
static void PrintRegistryCommands() noexcept;
|
||||
static void PrintSecurityEngineCommands() noexcept;
|
||||
static void PrintSessionManagement() noexcept;
|
||||
static void PrintStickyKeysInfo() noexcept;
|
||||
|
||||
683
kvc/HiveManager.cpp
Normal file
683
kvc/HiveManager.cpp
Normal file
@@ -0,0 +1,683 @@
|
||||
/*******************************************************************************
|
||||
_ ____ ______
|
||||
| |/ /\ \ / / ___|
|
||||
| ' / \ \ / / |
|
||||
| . \ \ V /| |___
|
||||
|_|\_\ \_/ \____|
|
||||
|
||||
The **Kernel Vulnerability Capabilities (KVC)** framework represents a paradigm shift in Windows security research,
|
||||
offering unprecedented access to modern Windows internals through sophisticated ring-0 operations. Originally conceived
|
||||
as "Kernel Process Control," the framework has evolved to emphasize not just control, but the complete **exploitation
|
||||
of kernel-level primitives** for legitimate security research and penetration testing.
|
||||
|
||||
KVC addresses the critical gap left by traditional forensic tools that have become obsolete in the face of modern Windows
|
||||
security hardening. Where tools like ProcDump and Process Explorer fail against Protected Process Light (PPL) and Antimalware
|
||||
Protected Interface (AMSI) boundaries, KVC succeeds by operating at the kernel level, manipulating the very structures
|
||||
that define these protections.
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
Author : Marek Wesołowski
|
||||
Email : marek@wesolowski.eu.org
|
||||
Phone : +48 607 440 283 (Tel/WhatsApp)
|
||||
Date : 04-09-2025
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
// HiveManager.cpp
|
||||
#include "HiveManager.h"
|
||||
#include "common.h"
|
||||
#include "TrustedInstallerIntegrator.h"
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <chrono>
|
||||
#include <shlobj.h>
|
||||
#include <sddl.h>
|
||||
#include <lmcons.h>
|
||||
|
||||
#pragma comment(lib, "advapi32.lib")
|
||||
|
||||
HiveManager::HiveManager()
|
||||
: m_tiToken(nullptr)
|
||||
, m_tiIntegrator(nullptr)
|
||||
{
|
||||
m_currentUserSid = GetCurrentUserSid();
|
||||
m_currentUsername = GetCurrentUsername();
|
||||
InitializeHiveLists();
|
||||
ResetStats();
|
||||
}
|
||||
|
||||
HiveManager::~HiveManager()
|
||||
{
|
||||
if (m_tiToken) {
|
||||
RevertToSelf();
|
||||
m_tiToken = nullptr;
|
||||
}
|
||||
|
||||
if (m_tiIntegrator) {
|
||||
delete m_tiIntegrator;
|
||||
m_tiIntegrator = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::wstring HiveManager::GetCurrentUserSid()
|
||||
{
|
||||
HANDLE hToken;
|
||||
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) {
|
||||
return L"";
|
||||
}
|
||||
|
||||
DWORD dwSize = 0;
|
||||
GetTokenInformation(hToken, TokenUser, nullptr, 0, &dwSize);
|
||||
|
||||
std::vector<BYTE> buffer(dwSize);
|
||||
TOKEN_USER* pTokenUser = reinterpret_cast<TOKEN_USER*>(buffer.data());
|
||||
|
||||
std::wstring sidString;
|
||||
if (GetTokenInformation(hToken, TokenUser, pTokenUser, dwSize, &dwSize)) {
|
||||
LPWSTR stringSid;
|
||||
if (ConvertSidToStringSidW(pTokenUser->User.Sid, &stringSid)) {
|
||||
sidString = stringSid;
|
||||
LocalFree(stringSid);
|
||||
}
|
||||
}
|
||||
|
||||
CloseHandle(hToken);
|
||||
return sidString;
|
||||
}
|
||||
|
||||
std::wstring HiveManager::GetCurrentUsername()
|
||||
{
|
||||
wchar_t username[UNLEN + 1];
|
||||
DWORD size = UNLEN + 1;
|
||||
|
||||
if (GetUserNameW(username, &size)) {
|
||||
return std::wstring(username);
|
||||
}
|
||||
|
||||
return L"";
|
||||
}
|
||||
|
||||
// HiveManager.cpp - poprawiona funkcja GetHivePhysicalPath
|
||||
|
||||
fs::path HiveManager::GetHivePhysicalPath(const std::wstring& hiveName)
|
||||
{
|
||||
wchar_t winDir[MAX_PATH];
|
||||
wchar_t sysDir[MAX_PATH];
|
||||
|
||||
GetWindowsDirectoryW(winDir, MAX_PATH);
|
||||
GetSystemDirectoryW(sysDir, MAX_PATH);
|
||||
|
||||
fs::path windowsPath(winDir);
|
||||
fs::path systemPath(sysDir);
|
||||
|
||||
if (hiveName == L"DEFAULT") {
|
||||
return systemPath / L"config" / L"DEFAULT";
|
||||
}
|
||||
else if (hiveName == L"SAM") {
|
||||
return systemPath / L"config" / L"SAM";
|
||||
}
|
||||
else if (hiveName == L"SECURITY") {
|
||||
return systemPath / L"config" / L"SECURITY";
|
||||
}
|
||||
else if (hiveName == L"SOFTWARE") {
|
||||
return systemPath / L"config" / L"SOFTWARE";
|
||||
}
|
||||
else if (hiveName == L"SYSTEM") {
|
||||
return systemPath / L"config" / L"SYSTEM";
|
||||
}
|
||||
else if (hiveName == L"NTUSER" && !m_currentUsername.empty()) {
|
||||
// Get user profile directory dynamically
|
||||
wchar_t profileDir[MAX_PATH];
|
||||
if (SUCCEEDED(SHGetFolderPathW(nullptr, CSIDL_PROFILE, nullptr, 0, profileDir))) {
|
||||
return fs::path(profileDir) / L"NTUSER.DAT";
|
||||
}
|
||||
}
|
||||
else if (hiveName == L"UsrClass" && !m_currentUsername.empty()) {
|
||||
// Get user AppData\Local dynamically
|
||||
wchar_t localAppData[MAX_PATH];
|
||||
if (SUCCEEDED(SHGetFolderPathW(nullptr, CSIDL_LOCAL_APPDATA, nullptr, 0, localAppData))) {
|
||||
return fs::path(localAppData) / L"Microsoft" / L"Windows" / L"UsrClass.dat";
|
||||
}
|
||||
}
|
||||
|
||||
return L"";
|
||||
}
|
||||
|
||||
void HiveManager::InitializeHiveLists()
|
||||
{
|
||||
// Build user-specific paths
|
||||
std::wstring userHivePath = L"HKU\\" + m_currentUserSid;
|
||||
std::wstring userClassPath = userHivePath + L"_Classes";
|
||||
|
||||
// Critical registry hives (all operations require TrustedInstaller elevation)
|
||||
m_registryHives = {
|
||||
{ L"BCD", L"HKLM\\BCD00000000", false }, // Bootloader, cannot restore
|
||||
{ L"DEFAULT", L"HKU\\.DEFAULT", true },
|
||||
{ L"NTUSER", userHivePath, true }, // User hive with real SID
|
||||
{ L"SAM", L"HKLM\\SAM", true },
|
||||
{ L"SECURITY", L"HKLM\\SECURITY", true },
|
||||
{ L"SOFTWARE", L"HKLM\\SOFTWARE", true },
|
||||
{ L"SYSTEM", L"HKLM\\SYSTEM", true },
|
||||
{ L"UsrClass", userClassPath, true } // User classes with real SID
|
||||
};
|
||||
}
|
||||
|
||||
void HiveManager::ResetStats()
|
||||
{
|
||||
m_lastStats = BackupStats{};
|
||||
}
|
||||
|
||||
std::wstring HiveManager::GetTimestamp()
|
||||
{
|
||||
auto now = std::chrono::system_clock::now();
|
||||
auto time = std::chrono::system_clock::to_time_t(now);
|
||||
|
||||
std::tm tm;
|
||||
localtime_s(&tm, &time);
|
||||
|
||||
std::wstringstream ss;
|
||||
ss << std::put_time(&tm, L"%Y.%m.%d_%H.%M.%S");
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
fs::path HiveManager::GenerateDefaultBackupPath()
|
||||
{
|
||||
wchar_t downloadsPath[MAX_PATH];
|
||||
if (SUCCEEDED(SHGetFolderPathW(nullptr, CSIDL_PROFILE, nullptr, 0, downloadsPath))) {
|
||||
fs::path basePath = fs::path(downloadsPath) / L"Downloads";
|
||||
std::wstring folderName = L"Registry_Backup_" + GetTimestamp();
|
||||
return basePath / folderName;
|
||||
}
|
||||
|
||||
// Fallback to temp if Downloads not found
|
||||
return fs::temp_directory_path() / (L"Registry_Backup_" + GetTimestamp());
|
||||
}
|
||||
|
||||
bool HiveManager::ValidateBackupDirectory(const fs::path& path)
|
||||
{
|
||||
std::error_code ec;
|
||||
|
||||
// Normalize path
|
||||
fs::path normalizedPath = fs::absolute(path, ec);
|
||||
if (ec) {
|
||||
ERROR(L"Failed to normalize path: %s", path.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create directory if it doesn't exist
|
||||
if (!fs::exists(normalizedPath, ec)) {
|
||||
if (!fs::create_directories(normalizedPath, ec)) {
|
||||
ERROR(L"Failed to create backup directory: %s", normalizedPath.c_str());
|
||||
return false;
|
||||
}
|
||||
INFO(L"Created backup directory: %s", normalizedPath.c_str());
|
||||
}
|
||||
|
||||
// Verify it's a directory
|
||||
if (!fs::is_directory(normalizedPath, ec)) {
|
||||
ERROR(L"Path is not a directory: %s", normalizedPath.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HiveManager::ValidateRestoreDirectory(const fs::path& path)
|
||||
{
|
||||
std::error_code ec;
|
||||
|
||||
fs::path normalizedPath = fs::absolute(path, ec);
|
||||
if (ec) {
|
||||
ERROR(L"Failed to normalize path: %s", path.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!fs::exists(normalizedPath, ec) || !fs::is_directory(normalizedPath, ec)) {
|
||||
ERROR(L"Restore directory does not exist: %s", normalizedPath.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HiveManager::ElevateToTrustedInstaller()
|
||||
{
|
||||
if (m_tiToken) {
|
||||
return true; // Already elevated
|
||||
}
|
||||
|
||||
if (!m_tiIntegrator) {
|
||||
m_tiIntegrator = new TrustedInstallerIntegrator();
|
||||
}
|
||||
|
||||
INFO(L"Acquiring TrustedInstaller token...");
|
||||
m_tiToken = m_tiIntegrator->GetCachedTrustedInstallerToken();
|
||||
|
||||
if (!m_tiToken) {
|
||||
ERROR(L"Failed to acquire TrustedInstaller token - ensure running as Administrator");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Impersonate using TrustedInstaller token
|
||||
if (!ImpersonateLoggedOnUser(m_tiToken)) {
|
||||
ERROR(L"Failed to impersonate TrustedInstaller: %d", GetLastError());
|
||||
m_tiToken = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
SUCCESS(L"Elevated to TrustedInstaller");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HiveManager::PromptYesNo(const wchar_t* question)
|
||||
{
|
||||
std::wcout << L"\n" << question << L" ";
|
||||
std::wstring response;
|
||||
std::getline(std::wcin, response);
|
||||
|
||||
if (response.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
wchar_t first = towlower(response[0]);
|
||||
return (first == L'y' || first == L't'); // Y/y or T/t (Polish "tak")
|
||||
}
|
||||
|
||||
bool HiveManager::SaveRegistryHive(const std::wstring& registryPath, const fs::path& destFile)
|
||||
{
|
||||
// Parse registry path to get root key
|
||||
HKEY hRootKey = nullptr;
|
||||
std::wstring subKey;
|
||||
|
||||
if (registryPath.starts_with(L"HKLM\\") || registryPath.starts_with(L"HKEY_LOCAL_MACHINE\\")) {
|
||||
hRootKey = HKEY_LOCAL_MACHINE;
|
||||
size_t pos = registryPath.find(L'\\');
|
||||
subKey = registryPath.substr(pos + 1);
|
||||
}
|
||||
else if (registryPath.starts_with(L"HKU\\") || registryPath.starts_with(L"HKEY_USERS\\")) {
|
||||
hRootKey = HKEY_USERS;
|
||||
size_t pos = registryPath.find(L'\\');
|
||||
subKey = registryPath.substr(pos + 1);
|
||||
}
|
||||
else if (registryPath.starts_with(L"HKCU") || registryPath.starts_with(L"HKEY_CURRENT_USER")) {
|
||||
hRootKey = HKEY_CURRENT_USER;
|
||||
size_t pos = registryPath.find(L'\\');
|
||||
if (pos != std::wstring::npos) {
|
||||
subKey = registryPath.substr(pos + 1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
ERROR(L"Invalid registry path format: %s", registryPath.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Open registry key with backup privilege
|
||||
HKEY hKey;
|
||||
LONG result = RegOpenKeyExW(hRootKey, subKey.empty() ? nullptr : subKey.c_str(),
|
||||
0, KEY_READ, &hKey);
|
||||
|
||||
if (result != ERROR_SUCCESS) {
|
||||
ERROR(L"Failed to open registry key %s: %d", registryPath.c_str(), result);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Save the hive using latest format (compresses and defragments)
|
||||
result = RegSaveKeyExW(hKey, destFile.c_str(), nullptr, REG_LATEST_FORMAT);
|
||||
|
||||
RegCloseKey(hKey);
|
||||
|
||||
if (result != ERROR_SUCCESS) {
|
||||
ERROR(L"RegSaveKeyEx failed for %s: %d", registryPath.c_str(), result);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HiveManager::BackupRegistryHives(const fs::path& targetDir)
|
||||
{
|
||||
INFO(L"Backing up registry hives...");
|
||||
|
||||
for (const auto& hive : m_registryHives) {
|
||||
m_lastStats.totalHives++;
|
||||
|
||||
fs::path destFile = targetDir / hive.name;
|
||||
|
||||
INFO(L" Saving %s -> %s", hive.name.c_str(), destFile.filename().c_str());
|
||||
|
||||
if (SaveRegistryHive(hive.registryPath, destFile)) {
|
||||
m_lastStats.successfulHives++;
|
||||
|
||||
// Get file size
|
||||
std::error_code ec;
|
||||
auto size = fs::file_size(destFile, ec);
|
||||
if (!ec) {
|
||||
m_lastStats.totalBytes += size;
|
||||
}
|
||||
|
||||
SUCCESS(L" Saved %s (%llu bytes)", hive.name.c_str(), size);
|
||||
}
|
||||
else {
|
||||
m_lastStats.failedHives++;
|
||||
ERROR(L" Failed to save %s", hive.name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
return m_lastStats.successfulHives > 0;
|
||||
}
|
||||
|
||||
void HiveManager::PrintStats(const std::wstring& operation)
|
||||
{
|
||||
std::wcout << L"\n";
|
||||
INFO(L"=== %s Statistics ===", operation.c_str());
|
||||
INFO(L"Registry Hives: %zu/%zu successful", m_lastStats.successfulHives, m_lastStats.totalHives);
|
||||
INFO(L"Total Size: %.2f MB", static_cast<double>(m_lastStats.totalBytes) / (1024.0 * 1024.0));
|
||||
|
||||
if (m_lastStats.failedHives > 0) {
|
||||
ERROR(L"Failed: %zu hives", m_lastStats.failedHives);
|
||||
}
|
||||
}
|
||||
|
||||
bool HiveManager::Backup(const std::wstring& targetPath)
|
||||
{
|
||||
ResetStats();
|
||||
|
||||
// Determine target directory BEFORE elevation (to get real user profile)
|
||||
fs::path backupDir;
|
||||
if (targetPath.empty()) {
|
||||
backupDir = GenerateDefaultBackupPath();
|
||||
INFO(L"Using default backup path: %s", backupDir.c_str());
|
||||
}
|
||||
else {
|
||||
backupDir = targetPath;
|
||||
}
|
||||
|
||||
// Validate and create directory (before elevation)
|
||||
if (!ValidateBackupDirectory(backupDir)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// NOW elevate to TrustedInstaller for unrestricted registry access
|
||||
if (!ElevateToTrustedInstaller()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
INFO(L"Starting registry backup to: %s", backupDir.c_str());
|
||||
|
||||
// Backup registry hives
|
||||
bool success = BackupRegistryHives(backupDir);
|
||||
|
||||
// Print summary
|
||||
PrintStats(L"Backup");
|
||||
|
||||
if (success) {
|
||||
SUCCESS(L"Backup completed: %s", backupDir.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
ERROR(L"Backup failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HiveManager::RestoreRegistryHives(const fs::path& sourceDir)
|
||||
{
|
||||
INFO(L"Validating backup files...");
|
||||
|
||||
for (const auto& hive : m_registryHives) {
|
||||
fs::path sourceFile = sourceDir / hive.name;
|
||||
|
||||
std::error_code ec;
|
||||
if (fs::exists(sourceFile, ec)) {
|
||||
INFO(L" Found: %s", hive.name.c_str());
|
||||
m_lastStats.successfulHives++;
|
||||
|
||||
auto size = fs::file_size(sourceFile, ec);
|
||||
if (!ec) {
|
||||
m_lastStats.totalBytes += size;
|
||||
}
|
||||
}
|
||||
else {
|
||||
ERROR(L" Missing: %s", hive.name.c_str());
|
||||
m_lastStats.failedHives++;
|
||||
}
|
||||
}
|
||||
|
||||
return m_lastStats.failedHives == 0;
|
||||
}
|
||||
|
||||
bool HiveManager::ApplyRestoreAndReboot(const fs::path& sourceDir)
|
||||
{
|
||||
// Enable restore privileges BEFORE attempting any restore operations
|
||||
HANDLE token;
|
||||
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) {
|
||||
TOKEN_PRIVILEGES tp;
|
||||
LUID luid;
|
||||
|
||||
// SE_RESTORE_NAME - critical for RegRestoreKeyW
|
||||
if (LookupPrivilegeValueW(nullptr, SE_RESTORE_NAME, &luid)) {
|
||||
tp.PrivilegeCount = 1;
|
||||
tp.Privileges[0].Luid = luid;
|
||||
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
||||
AdjustTokenPrivileges(token, FALSE, &tp, 0, nullptr, nullptr);
|
||||
}
|
||||
|
||||
// SE_BACKUP_NAME - for good measure
|
||||
if (LookupPrivilegeValueW(nullptr, SE_BACKUP_NAME, &luid)) {
|
||||
tp.PrivilegeCount = 1;
|
||||
tp.Privileges[0].Luid = luid;
|
||||
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
||||
AdjustTokenPrivileges(token, FALSE, &tp, 0, nullptr, nullptr);
|
||||
}
|
||||
|
||||
CloseHandle(token);
|
||||
}
|
||||
|
||||
INFO(L"Applying registry restore using RegRestoreKeyW...");
|
||||
|
||||
size_t restoredLive = 0;
|
||||
size_t restoredPending = 0;
|
||||
|
||||
for (const auto& hive : m_registryHives) {
|
||||
// Skip non-restorable hives
|
||||
if (!hive.canRestore) {
|
||||
INFO(L" Skipping %s (cannot restore)", hive.name.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
fs::path sourceFile = sourceDir / hive.name;
|
||||
|
||||
std::error_code ec;
|
||||
if (!fs::exists(sourceFile, ec)) {
|
||||
ERROR(L" Missing backup file: %s", hive.name.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Parse registry path to get root key and subkey
|
||||
HKEY hRootKey = nullptr;
|
||||
std::wstring subKey;
|
||||
|
||||
if (hive.registryPath.starts_with(L"HKLM\\")) {
|
||||
hRootKey = HKEY_LOCAL_MACHINE;
|
||||
size_t pos = hive.registryPath.find(L'\\');
|
||||
subKey = hive.registryPath.substr(pos + 1);
|
||||
}
|
||||
else if (hive.registryPath.starts_with(L"HKU\\")) {
|
||||
hRootKey = HKEY_USERS;
|
||||
size_t pos = hive.registryPath.find(L'\\');
|
||||
subKey = hive.registryPath.substr(pos + 1);
|
||||
}
|
||||
else {
|
||||
ERROR(L" Invalid path format for %s", hive.name.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Open the target key
|
||||
HKEY hKey;
|
||||
LONG result = RegOpenKeyExW(hRootKey, subKey.c_str(), 0, KEY_WRITE, &hKey);
|
||||
|
||||
if (result != ERROR_SUCCESS) {
|
||||
ERROR(L" Failed to open key %s: %d", hive.name.c_str(), result);
|
||||
continue;
|
||||
}
|
||||
|
||||
INFO(L" Restoring %s...", hive.name.c_str());
|
||||
|
||||
// Try live restore using REG_FORCE_RESTORE
|
||||
result = RegRestoreKeyW(hKey, sourceFile.c_str(), REG_FORCE_RESTORE);
|
||||
|
||||
RegCloseKey(hKey);
|
||||
|
||||
if (result == ERROR_SUCCESS) {
|
||||
SUCCESS(L" Restored %s (live)", hive.name.c_str());
|
||||
restoredLive++;
|
||||
}
|
||||
else if (result == ERROR_ACCESS_DENIED) {
|
||||
// Live restore failed - schedule for next boot
|
||||
INFO(L" Live restore failed (error 5) - scheduling for next boot...");
|
||||
|
||||
fs::path physicalPath = GetHivePhysicalPath(hive.name);
|
||||
if (physicalPath.empty()) {
|
||||
ERROR(L" Cannot determine physical path for %s", hive.name.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Schedule file replacement on next boot
|
||||
if (MoveFileExW(sourceFile.c_str(), physicalPath.c_str(),
|
||||
MOVEFILE_DELAY_UNTIL_REBOOT | MOVEFILE_REPLACE_EXISTING)) {
|
||||
SUCCESS(L" Scheduled %s for next boot", hive.name.c_str());
|
||||
restoredPending++;
|
||||
}
|
||||
else {
|
||||
ERROR(L" Failed to schedule %s: %d", hive.name.c_str(), GetLastError());
|
||||
}
|
||||
}
|
||||
else {
|
||||
ERROR(L" Failed to restore %s: %d", hive.name.c_str(), result);
|
||||
}
|
||||
}
|
||||
|
||||
if (restoredLive == 0 && restoredPending == 0) {
|
||||
ERROR(L"No hives were restored successfully");
|
||||
return false;
|
||||
}
|
||||
|
||||
SUCCESS(L"Successfully restored %zu hives (live: %zu, pending: %zu)",
|
||||
restoredLive + restoredPending, restoredLive, restoredPending);
|
||||
|
||||
if (restoredPending > 0) {
|
||||
INFO(L"Note: %zu hives scheduled for next boot (will replace on-disk files)", restoredPending);
|
||||
}
|
||||
|
||||
INFO(L"System restart required for changes to take effect");
|
||||
INFO(L"Initiating system reboot in 10 seconds...");
|
||||
|
||||
// Enable shutdown privilege
|
||||
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) {
|
||||
TOKEN_PRIVILEGES tp;
|
||||
LUID luid;
|
||||
|
||||
if (LookupPrivilegeValueW(nullptr, SE_SHUTDOWN_NAME, &luid)) {
|
||||
tp.PrivilegeCount = 1;
|
||||
tp.Privileges[0].Luid = luid;
|
||||
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
||||
AdjustTokenPrivileges(token, FALSE, &tp, 0, nullptr, nullptr);
|
||||
}
|
||||
CloseHandle(token);
|
||||
}
|
||||
|
||||
// Initiate system shutdown
|
||||
if (!InitiateSystemShutdownExW(
|
||||
nullptr,
|
||||
const_cast<LPWSTR>(L"Registry restore complete - system restart required"),
|
||||
10,
|
||||
TRUE, // Force apps closed
|
||||
TRUE, // Reboot after shutdown
|
||||
SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_RECONFIG | SHTDN_REASON_FLAG_PLANNED
|
||||
)) {
|
||||
ERROR(L"Failed to initiate shutdown: %d", GetLastError());
|
||||
INFO(L"Please restart the system manually");
|
||||
return false;
|
||||
}
|
||||
|
||||
SUCCESS(L"System reboot initiated");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HiveManager::Restore(const std::wstring& sourcePath)
|
||||
{
|
||||
ResetStats();
|
||||
|
||||
fs::path restoreDir = sourcePath;
|
||||
|
||||
// Validate source directory BEFORE elevation
|
||||
if (!ValidateRestoreDirectory(restoreDir)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// NOW elevate to TrustedInstaller
|
||||
if (!ElevateToTrustedInstaller()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
INFO(L"Starting registry restore from: %s", restoreDir.c_str());
|
||||
|
||||
// Validate backup files
|
||||
bool validated = RestoreRegistryHives(restoreDir);
|
||||
|
||||
// Print summary
|
||||
PrintStats(L"Restore Validation");
|
||||
|
||||
if (!validated) {
|
||||
ERROR(L"Restore validation failed - missing backup files");
|
||||
return false;
|
||||
}
|
||||
|
||||
INFO(L"All backup files validated successfully");
|
||||
INFO(L"WARNING: Registry restore will modify system hives and requires restart");
|
||||
|
||||
// Prompt user
|
||||
if (PromptYesNo(L"Apply restore and reboot now? (Y/N):")) {
|
||||
return ApplyRestoreAndReboot(restoreDir);
|
||||
}
|
||||
|
||||
INFO(L"Restore cancelled by user");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HiveManager::Defrag(const std::wstring& tempPath)
|
||||
{
|
||||
INFO(L"Starting registry defragmentation (backup with compression)");
|
||||
|
||||
// Generate temp backup path BEFORE any elevation (to get real user temp)
|
||||
fs::path defragPath;
|
||||
if (tempPath.empty()) {
|
||||
defragPath = fs::temp_directory_path() / (L"Registry_Defrag_" + GetTimestamp());
|
||||
}
|
||||
else {
|
||||
defragPath = tempPath;
|
||||
}
|
||||
|
||||
INFO(L"Using temporary path: %s", defragPath.c_str());
|
||||
|
||||
// Backup automatically elevates to TrustedInstaller and uses REG_LATEST_FORMAT
|
||||
// which provides compression and defragmentation
|
||||
if (!Backup(defragPath.wstring())) {
|
||||
ERROR(L"Defrag failed at backup stage");
|
||||
return false;
|
||||
}
|
||||
|
||||
INFO(L"Defragmented backup created successfully");
|
||||
INFO(L"Backup location: %s", defragPath.c_str());
|
||||
INFO(L"To complete defragmentation, defragmented hives must be restored");
|
||||
INFO(L"WARNING: This will modify system hives and requires restart");
|
||||
|
||||
// Prompt user
|
||||
if (PromptYesNo(L"Apply defragmented hives and reboot now? (Y/N):")) {
|
||||
return ApplyRestoreAndReboot(defragPath);
|
||||
}
|
||||
|
||||
SUCCESS(L"Defragmentation backup completed");
|
||||
INFO(L"You can manually restore from: %s", defragPath.c_str());
|
||||
return true;
|
||||
}
|
||||
73
kvc/HiveManager.h
Normal file
73
kvc/HiveManager.h
Normal file
@@ -0,0 +1,73 @@
|
||||
// HiveManager.h
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <filesystem>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
// Forward declaration
|
||||
class TrustedInstallerIntegrator;
|
||||
|
||||
// Registry hive backup, restore and defragmentation manager
|
||||
class HiveManager
|
||||
{
|
||||
public:
|
||||
HiveManager();
|
||||
~HiveManager();
|
||||
|
||||
// Main operations
|
||||
bool Backup(const std::wstring& targetPath = L"");
|
||||
bool Restore(const std::wstring& sourcePath);
|
||||
bool Defrag(const std::wstring& tempPath = L"");
|
||||
|
||||
// Statistics
|
||||
struct BackupStats {
|
||||
size_t totalHives = 0;
|
||||
size_t successfulHives = 0;
|
||||
size_t failedHives = 0;
|
||||
uint64_t totalBytes = 0;
|
||||
};
|
||||
|
||||
const BackupStats& GetLastStats() const { return m_lastStats; }
|
||||
|
||||
private:
|
||||
// Hive definitions
|
||||
struct RegistryHive {
|
||||
std::wstring name;
|
||||
std::wstring registryPath;
|
||||
bool canRestore; // Can be restored with RegRestoreKeyW
|
||||
};
|
||||
|
||||
// Internal operations
|
||||
bool BackupRegistryHives(const fs::path& targetDir);
|
||||
bool RestoreRegistryHives(const fs::path& sourceDir);
|
||||
bool ApplyRestoreAndReboot(const fs::path& sourceDir);
|
||||
|
||||
bool SaveRegistryHive(const std::wstring& registryPath, const fs::path& destFile);
|
||||
bool ElevateToTrustedInstaller();
|
||||
bool PromptYesNo(const wchar_t* question);
|
||||
|
||||
fs::path GenerateDefaultBackupPath();
|
||||
std::wstring GetTimestamp();
|
||||
std::wstring GetCurrentUserSid();
|
||||
std::wstring GetCurrentUsername();
|
||||
fs::path GetHivePhysicalPath(const std::wstring& hiveName);
|
||||
bool ValidateBackupDirectory(const fs::path& path);
|
||||
bool ValidateRestoreDirectory(const fs::path& path);
|
||||
|
||||
void InitializeHiveLists();
|
||||
void ResetStats();
|
||||
void PrintStats(const std::wstring& operation);
|
||||
|
||||
// Data members
|
||||
std::vector<RegistryHive> m_registryHives;
|
||||
BackupStats m_lastStats;
|
||||
|
||||
HANDLE m_tiToken;
|
||||
TrustedInstallerIntegrator* m_tiIntegrator;
|
||||
std::wstring m_currentUserSid;
|
||||
std::wstring m_currentUsername;
|
||||
};
|
||||
49
kvc/Kvc.cpp
49
kvc/Kvc.cpp
@@ -28,6 +28,7 @@ that define these protections.
|
||||
#include "DefenderManager.h"
|
||||
#include "ProcessManager.h"
|
||||
#include "ServiceManager.h"
|
||||
#include "HiveManager.h"
|
||||
#include "HelpSystem.h"
|
||||
#include <string_view>
|
||||
#include <charconv>
|
||||
@@ -976,7 +977,53 @@ int wmain(int argc, wchar_t* argv[])
|
||||
INFO(L"Loading and processing kvc.dat combined binary...");
|
||||
return g_controller->LoadAndSplitCombinedBinaries() ? 0 : 2;
|
||||
}
|
||||
|
||||
|
||||
// Registry backup and defragmentation operations
|
||||
else if (command == L"registry")
|
||||
{
|
||||
if (argc < 3)
|
||||
{
|
||||
ERROR(L"Missing registry subcommand: backup, restore, or defrag");
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::wstring_view subcommand = argv[2];
|
||||
HiveManager hiveManager;
|
||||
|
||||
if (subcommand == L"backup")
|
||||
{
|
||||
std::wstring targetPath;
|
||||
if (argc >= 4)
|
||||
targetPath = argv[3];
|
||||
|
||||
return hiveManager.Backup(targetPath) ? 0 : 2;
|
||||
}
|
||||
else if (subcommand == L"restore")
|
||||
{
|
||||
if (argc < 4)
|
||||
{
|
||||
ERROR(L"Missing source path for restore operation");
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::wstring sourcePath = argv[3];
|
||||
return hiveManager.Restore(sourcePath) ? 0 : 2;
|
||||
}
|
||||
else if (subcommand == L"defrag")
|
||||
{
|
||||
std::wstring tempPath;
|
||||
if (argc >= 4)
|
||||
tempPath = argv[3];
|
||||
|
||||
return hiveManager.Defrag(tempPath) ? 0 : 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR(L"Unknown registry subcommand: %s", subcommand.data());
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
ERROR(L"Unknown command: %s", command.data());
|
||||
|
||||
@@ -121,6 +121,7 @@
|
||||
<ClCompile Include="kvcDrv.cpp" />
|
||||
<ClCompile Include="Utils.cpp" />
|
||||
<ClCompile Include="Common.cpp" />
|
||||
<ClCompile Include="HiveManager.cpp" />
|
||||
<ClCompile Include="ReportExporter.cpp" />
|
||||
<ClCompile Include="SessionManager.cpp" />
|
||||
<ClCompile Include="ServiceManager.cpp" />
|
||||
@@ -138,6 +139,7 @@
|
||||
<ClInclude Include="OffsetFinder.h" />
|
||||
<ClInclude Include="kvcDrv.h" />
|
||||
<ClInclude Include="Utils.h" />
|
||||
<ClCompile Include="HiveManager.h" />
|
||||
<ClInclude Include="ReportExporter.h" />
|
||||
<ClCompile Include="SessionManager.h" />
|
||||
<ClInclude Include="ServiceManager.h" />
|
||||
|
||||
Reference in New Issue
Block a user