From de135c0487dc78737c03803a7171848aa3cd6594 Mon Sep 17 00:00:00 2001 From: wesmar Date: Thu, 2 Oct 2025 01:08:10 +0200 Subject: [PATCH] Aktualizacja: 2025-10-02 01:08:10 --- kvc/Controller.h | 14 +- kvc/ControllerProcessOperations.cpp | 254 ++++---- kvc/HelpSystem.cpp | 25 + kvc/HelpSystem.h | 3 +- kvc/Kvc.cpp | 209 +++++-- kvc/Kvc.vcxproj | 2 + kvc/SessionManager.cpp | 876 ++++++++++++++++++++++++++++ kvc/SessionManager.h | 68 +++ kvc/common.h | 46 +- 9 files changed, 1327 insertions(+), 170 deletions(-) create mode 100644 kvc/SessionManager.cpp create mode 100644 kvc/SessionManager.h diff --git a/kvc/Controller.h b/kvc/Controller.h index 9a6a931..846c50c 100644 --- a/kvc/Controller.h +++ b/kvc/Controller.h @@ -1,5 +1,6 @@ #pragma once +#include "SessionManager.h" #include "kvcDrv.h" #include "OffsetFinder.h" #include "TrustedInstallerIntegrator.h" @@ -106,6 +107,14 @@ public: // Signer-based batch operations for mass unprotection scenarios bool UnprotectBySigner(const std::wstring& signerName) noexcept; bool ListProcessesBySigner(const std::wstring& signerName) noexcept; + + // Session state restoration + bool RestoreProtectionBySigner(const std::wstring& signerName) noexcept; + bool RestoreAllProtection() noexcept; + void ShowSessionHistory() noexcept; + + bool SetProcessProtection(ULONG_PTR addr, UCHAR protection) noexcept; + SessionManager m_sessionMgr; bool UnprotectAllProcesses() noexcept; bool UnprotectMultipleProcesses(const std::vector& targets) noexcept; @@ -175,7 +184,7 @@ public: private: // Core components TrustedInstallerIntegrator m_trustedInstaller; - std::unique_ptr m_rtc; + std::unique_ptr m_rtc; std::unique_ptr m_of; SQLiteAPI m_sqlite; @@ -227,8 +236,7 @@ private: // Internal kernel process management (implementation details) std::optional GetInitialSystemProcessAddress() noexcept; - bool SetProcessProtection(ULONG_PTR addr, UCHAR protection) noexcept; - + // Process pattern matching with regex support std::vector FindProcessesByName(const std::wstring& pattern) noexcept; bool IsPatternMatch(const std::wstring& processName, const std::wstring& pattern) noexcept; diff --git a/kvc/ControllerProcessOperations.cpp b/kvc/ControllerProcessOperations.cpp index 3a8e658..e90ab0e 100644 --- a/kvc/ControllerProcessOperations.cpp +++ b/kvc/ControllerProcessOperations.cpp @@ -847,116 +847,130 @@ bool Controller::SetProcessProtection(DWORD pid, const std::wstring& protectionL // MASS PROTECTION OPERATIONS // ============================================================================ -bool Controller::UnprotectAllProcesses() noexcept { +bool Controller::UnprotectAllProcesses() noexcept +{ if (!BeginDriverSession()) { - EndDriverSession(true); + EndDriverSession(true); + return false; + } + + auto processes = GetProcessList(); + + // Group processes by signer type + std::unordered_map> groupedProcesses; + + for (const auto& entry : processes) + { + if (entry.ProtectionLevel > 0) + { + std::wstring signerName = Utils::GetSignerTypeAsString(entry.SignerType); + groupedProcesses[signerName].push_back(entry); + } + } + + if (groupedProcesses.empty()) + { + INFO(L"No protected processes found"); + EndDriverSession(true); return false; } - auto processes = GetProcessList(); - DWORD totalCount = 0; - DWORD successCount = 0; + INFO(L"Starting mass unprotection (%zu signer groups)", groupedProcesses.size()); - INFO(L"Starting mass unprotection of all protected processes..."); + DWORD totalSuccess = 0; + DWORD totalProcessed = 0; - for (const auto& entry : processes) { - if (entry.ProtectionLevel > 0) { - totalCount++; + // Process each signer group + for (const auto& [signerName, processes] : groupedProcesses) + { + INFO(L"Processing signer group: %s (%zu processes)", signerName.c_str(), processes.size()); + + // Save state before modification + m_sessionMgr.SaveUnprotectOperation(signerName, processes); + + // Unprotect all in this group + for (const auto& entry : processes) + { + totalProcessed++; - if (SetProcessProtection(entry.KernelAddress, 0)) { - successCount++; + if (SetProcessProtection(entry.KernelAddress, 0)) + { + totalSuccess++; SUCCESS(L"Removed protection from PID %d (%s)", entry.Pid, entry.ProcessName.c_str()); - } else { + } + else + { ERROR(L"Failed to remove protection from PID %d (%s)", entry.Pid, entry.ProcessName.c_str()); } - if (g_interrupted) { + if (g_interrupted) + { INFO(L"Mass unprotection interrupted by user"); + EndDriverSession(true); + return false; + } + } + } + + INFO(L"Mass unprotection completed: %d/%d processes successfully unprotected", totalSuccess, totalProcessed); + + EndDriverSession(true); + return totalSuccess > 0; +} + +// ControllerProcessOperations.cpp +// DODAJ tę funkcję (po funkcji UnprotectAllProcesses, przed końcem pliku): + +bool Controller::UnprotectMultipleProcesses(const std::vector& targets) noexcept +{ + if (targets.empty()) + return false; + + if (!BeginDriverSession()) { + EndDriverSession(true); + return false; + } + + DWORD successCount = 0; + DWORD totalCount = static_cast(targets.size()); + + for (const auto& target : targets) + { + bool result = false; + + // Check if target is numeric (PID) + bool isNumeric = true; + for (wchar_t ch : target) + { + if (!iswdigit(ch)) + { + isNumeric = false; break; } } - } - - if (totalCount == 0) { - INFO(L"No protected processes found"); - } else { - INFO(L"Mass unprotection completed: %d/%d processes successfully unprotected", successCount, totalCount); - } - - EndDriverSession(true); - return successCount == totalCount; -} -bool Controller::UnprotectMultipleProcesses(const std::vector& targets) noexcept { - if (targets.empty()) { - ERROR(L"No targets specified for batch unprotection"); - EndDriverSession(true); - return false; - } - - if (!BeginDriverSession()) { - return false; - } - - DWORD successCount = 0; - DWORD totalCount = static_cast(targets.size()); - - INFO(L"Starting batch unprotection of %d targets...", totalCount); - - for (const auto& target : targets) { - bool result = false; - - // Check if target is numeric (PID) - if (Utils::IsNumeric(target)) { - auto pid = Utils::ParsePid(target); - if (pid) { - auto kernelAddr = GetCachedKernelAddress(pid.value()); - if (kernelAddr) { - auto currentProtection = GetProcessProtection(kernelAddr.value()); - if (currentProtection && currentProtection.value() > 0) { - if (SetProcessProtection(kernelAddr.value(), 0)) { - SUCCESS(L"Removed protection from PID %d", pid.value()); - result = true; - } else { - ERROR(L"Failed to remove protection from PID %d", pid.value()); - } - } else { - INFO(L"PID %d is not protected", pid.value()); - result = true; // Consider this a success - } - } - } else { - ERROR(L"Invalid PID format: %s", target.c_str()); + if (isNumeric) + { + try { + DWORD pid = std::stoul(target); + result = UnprotectProcess(pid); } - } else { - // Target is process name - auto matches = FindProcessesByName(target); - if (matches.size() == 1) { - auto match = matches[0]; - auto currentProtection = GetProcessProtection(match.KernelAddress); - if (currentProtection && currentProtection.value() > 0) { - if (SetProcessProtection(match.KernelAddress, 0)) { - SUCCESS(L"Removed protection from %s (PID %d)", match.ProcessName.c_str(), match.Pid); - result = true; - } else { - ERROR(L"Failed to remove protection from %s (PID %d)", match.ProcessName.c_str(), match.Pid); - } - } else { - INFO(L"%s (PID %d) is not protected", match.ProcessName.c_str(), match.Pid); - result = true; // Consider this a success - } - } else { - ERROR(L"Could not resolve process name: %s", target.c_str()); + catch (...) { + ERROR(L"Invalid PID: %s", target.c_str()); } } - + else + { + result = UnprotectProcessByName(target); + } + if (result) successCount++; } - + INFO(L"Batch unprotection completed: %d/%d targets successfully processed", successCount, totalCount); - + EndDriverSession(true); - + return successCount == totalCount; } @@ -968,31 +982,42 @@ bool Controller::UnprotectBySigner(const std::wstring& signerName) noexcept { } if (!BeginDriverSession()) { - EndDriverSession(true); + EndDriverSession(true); return false; } auto processes = GetProcessList(); + std::vector affectedProcesses; DWORD totalCount = 0; DWORD successCount = 0; INFO(L"Starting batch unprotection of processes signed by: %s", signerName.c_str()); + // Collect processes that will be affected for (const auto& entry : processes) { if (entry.ProtectionLevel > 0 && entry.SignerType == signerType.value()) { + affectedProcesses.push_back(entry); totalCount++; + } + } + + // Save state before modification + if (!affectedProcesses.empty()) { + m_sessionMgr.SaveUnprotectOperation(signerName, affectedProcesses); + } - if (SetProcessProtection(entry.KernelAddress, 0)) { - successCount++; - SUCCESS(L"Removed protection from PID %d (%s)", entry.Pid, entry.ProcessName.c_str()); - } else { - ERROR(L"Failed to remove protection from PID %d (%s)", entry.Pid, entry.ProcessName.c_str()); - } + // POPRAWIONE: Usunięte błędne zagnieżdżenie + for (const auto& entry : affectedProcesses) { + if (SetProcessProtection(entry.KernelAddress, 0)) { + successCount++; + SUCCESS(L"Removed protection from PID %d (%s)", entry.Pid, entry.ProcessName.c_str()); + } else { + ERROR(L"Failed to remove protection from PID %d (%s)", entry.Pid, entry.ProcessName.c_str()); + } - if (g_interrupted) { - INFO(L"Batch operation interrupted by user"); - break; - } + if (g_interrupted) { + INFO(L"Batch operation interrupted by user"); + break; } } @@ -1001,6 +1026,7 @@ bool Controller::UnprotectBySigner(const std::wstring& signerName) noexcept { } else { INFO(L"Batch unprotection completed: %d/%d processes successfully unprotected", successCount, totalCount); } + EndDriverSession(true); return successCount > 0; } @@ -1120,4 +1146,36 @@ bool Controller::SetProcessProtectionByName(const std::wstring& processName, con } return SetProcessProtection(match->Pid, protectionLevel, signerType); +} + +// Session state restoration operations +bool Controller::RestoreProtectionBySigner(const std::wstring& signerName) noexcept +{ + if (!BeginDriverSession()) { + EndDriverSession(true); + return false; + } + + bool result = m_sessionMgr.RestoreBySigner(signerName, this); + + EndDriverSession(true); + return result; +} + +bool Controller::RestoreAllProtection() noexcept +{ + if (!BeginDriverSession()) { + EndDriverSession(true); + return false; + } + + bool result = m_sessionMgr.RestoreAll(this); + + EndDriverSession(true); + return result; +} + +void Controller::ShowSessionHistory() noexcept +{ + m_sessionMgr.ShowHistory(); } \ No newline at end of file diff --git a/kvc/HelpSystem.cpp b/kvc/HelpSystem.cpp index 6d97633..54aa1ed 100644 --- a/kvc/HelpSystem.cpp +++ b/kvc/HelpSystem.cpp @@ -38,6 +38,7 @@ void HelpSystem::PrintUsage(std::wstring_view programName) noexcept PrintBasicCommands(); PrintProcessTerminationCommands(); PrintProtectionCommands(); + PrintSessionManagement(); PrintSystemCommands(); PrintBrowserCommands(); PrintDefenderCommands(); @@ -141,6 +142,10 @@ void HelpSystem::PrintProtectionCommands() noexcept PrintCommandLine(L"unprotect ", L"Remove protection from specific process"); PrintCommandLine(L"unprotect all", L"Remove protection from ALL processes"); PrintCommandLine(L"unprotect ", L"Remove protection from multiple processes"); + PrintCommandLine(L"restore ", L"Restore protection for specific signer group"); + PrintCommandLine(L"restore all", L"Restore all saved protection states"); + PrintCommandLine(L"history", L"Show saved session history (max 16 sessions)"); + PrintCommandLine(L"cleanup-sessions", L"Delete all sessions except current"); std::wcout << L"\n"; } @@ -181,6 +186,18 @@ void HelpSystem::PrintSecurityEngineCommands() noexcept std::wcout << L"\n"; } +void HelpSystem::PrintSessionManagement() noexcept +{ + PrintSectionHeader(L"Session Management System"); + std::wcout << L" - Automatic boot detection and session tracking (max 16 sessions)\n"; + std::wcout << L" - Each 'unprotect' operation saves process states grouped by signer\n"; + std::wcout << L" - 'restore' commands reapply protection from saved session state\n"; + std::wcout << L" - Session history persists across reboots until limit reached\n"; + std::wcout << L" - Oldest sessions auto-deleted when exceeding 16 session limit\n"; + std::wcout << L" - Manual cleanup available via 'cleanup-sessions' command\n"; + std::wcout << L" - Status tracking: UNPROTECTED (after unprotect) -> RESTORED (after restore)\n\n"; +} + void HelpSystem::PrintBrowserCommands() noexcept { PrintSectionHeader(L"Browser Password Extraction Commands"); @@ -330,6 +347,14 @@ void HelpSystem::PrintUsageExamples(std::wstring_view programName) noexcept 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"); diff --git a/kvc/HelpSystem.h b/kvc/HelpSystem.h index a5fcbc2..8f6772f 100644 --- a/kvc/HelpSystem.h +++ b/kvc/HelpSystem.h @@ -29,7 +29,8 @@ public: static void PrintTechnicalFeatures() noexcept; static void PrintDefenderNotes() noexcept; static void PrintSecurityEngineCommands() noexcept; - static void PrintStickyKeysInfo() noexcept; + static void PrintSessionManagement() noexcept; + static void PrintStickyKeysInfo() noexcept; static void PrintUndumpableProcesses() noexcept; static void PrintUsageExamples(std::wstring_view programName) noexcept; static void PrintSecurityNotice() noexcept; diff --git a/kvc/Kvc.cpp b/kvc/Kvc.cpp index 03d0e9e..1828d60 100644 --- a/kvc/Kvc.cpp +++ b/kvc/Kvc.cpp @@ -200,12 +200,31 @@ int wmain(int argc, wchar_t* argv[]) return success ? 0 : 1; } - else if (command == L"uninstall") - { - INFO(L"Uninstalling Kernel Vulnerability Capabilities Framework service..."); - bool success = ServiceManager::UninstallService(); - return success ? 0 : 1; - } + else if (command == L"uninstall") + { + INFO(L"Uninstalling Kernel Vulnerability Capabilities Framework service..."); + bool success = ServiceManager::UninstallService(); + + // Wyczyść całą konfigurację z rejestru + INFO(L"Cleaning up registry configuration..."); + HKEY hKey; + if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Software", 0, KEY_WRITE, &hKey) == ERROR_SUCCESS) + { + LONG result = RegDeleteTreeW(hKey, L"kvc"); + if (result == ERROR_SUCCESS) { + SUCCESS(L"Registry configuration cleaned successfully"); + } + else if (result == ERROR_FILE_NOT_FOUND) { + INFO(L"No registry configuration found to clean"); + } + else { + ERROR(L"Failed to clean registry configuration: %d", result); + } + RegCloseKey(hKey); + } + + return success ? 0 : 1; + } else if (command == L"service") { @@ -415,10 +434,12 @@ int wmain(int argc, wchar_t* argv[]) } // Process information commands with color-coded protection status output - else if (command == L"list") - { - return g_controller->ListProtectedProcesses() ? 0 : 2; - } + else if (command == L"list") + { + // Detect reboot and enforce session limit on first list after boot + g_controller->m_sessionMgr.DetectAndHandleReboot(); + return g_controller->ListProtectedProcesses() ? 0 : 2;; + } else if (command == L"get") { @@ -515,45 +536,105 @@ int wmain(int argc, wchar_t* argv[]) } // Process protection commands with atomic driver operations - else if (command == L"set" || command == L"protect") - { - if (argc < 5) - { - ERROR(L"Missing arguments: "); - return 1; - } + // Process protection commands with atomic driver operations + else if (command == L"set" || command == L"protect") + { + if (argc < 5) + { + ERROR(L"Missing arguments: "); + return 1; + } - std::wstring_view target = argv[2]; - std::wstring level = argv[3]; - std::wstring signer = argv[4]; + std::wstring_view target = argv[2]; + std::wstring level = argv[3]; + std::wstring signer = argv[4]; - bool result = false; - - if (IsNumeric(target)) - { - auto pid = ParsePid(target); - if (!pid) - { - ERROR(L"Invalid PID format: %s", target.data()); - return 1; - } - - // 'set' forces protection regardless of current state, 'protect' only protects unprotected processes - result = (command == L"set") ? - g_controller->SetProcessProtection(pid.value(), level, signer) : - g_controller->ProtectProcess(pid.value(), level, signer); - } - else - { - std::wstring processName(target); - - result = (command == L"set") ? - g_controller->SetProcessProtectionByName(processName, level, signer) : - g_controller->ProtectProcessByName(processName, level, signer); - } + // Handle comma-separated list of PIDs for batch operations + std::wstring targetStr(target); + if (targetStr.find(L',') != std::wstring::npos) + { + std::vector pids; + std::wstring current; + + // Parse comma-separated PIDs with whitespace handling + for (wchar_t ch : targetStr) + { + if (ch == L',') + { + if (!current.empty()) + { + if (IsNumeric(current)) + { + auto pid = ParsePid(current); + if (pid) pids.push_back(pid.value()); + } + current.clear(); + } + } + else if (ch != L' ' && ch != L'\t') + { + current += ch; + } + } + + // Last token + if (!current.empty() && IsNumeric(current)) + { + auto pid = ParsePid(current); + if (pid) pids.push_back(pid.value()); + } + + if (pids.empty()) + { + ERROR(L"No valid PIDs found in comma-separated list"); + return 1; + } + + // Batch operation + INFO(L"Batch %s operation: %zu processes", command.data(), pids.size()); + int successCount = 0; + + for (DWORD pid : pids) + { + bool result = (command == L"set") ? + g_controller->SetProcessProtection(pid, level, signer) : + g_controller->ProtectProcess(pid, level, signer); + + if (result) successCount++; + } + + INFO(L"Batch %s completed: %d/%zu processes", command.data(), successCount, pids.size()); + return successCount == pids.size() ? 0 : 2; + } - return result ? 0 : 2; - } + // Single target (PID or name) + bool result = false; + + if (IsNumeric(target)) + { + auto pid = ParsePid(target); + if (!pid) + { + ERROR(L"Invalid PID format: %s", target.data()); + return 1; + } + + // 'set' forces protection regardless of current state, 'protect' only protects unprotected processes + result = (command == L"set") ? + g_controller->SetProcessProtection(pid.value(), level, signer) : + g_controller->ProtectProcess(pid.value(), level, signer); + } + else + { + std::wstring processName(target); + + result = (command == L"set") ? + g_controller->SetProcessProtectionByName(processName, level, signer) : + g_controller->ProtectProcessByName(processName, level, signer); + } + + return result ? 0 : 2; + } else if (command == L"unprotect") { @@ -624,6 +705,42 @@ int wmain(int argc, wchar_t* argv[]) } } + // Restore process protection from saved session state + else if (command == L"restore") + { + if (argc < 3) + { + ERROR(L"Missing argument: "); + return 1; + } + + std::wstring_view target = argv[2]; + + if (target == L"all") + { + return g_controller->RestoreAllProtection() ? 0 : 2; + } + else + { + std::wstring signerName(target); + return g_controller->RestoreProtectionBySigner(signerName) ? 0 : 2; + } + } + + // Display session history + else if (command == L"history") + { + g_controller->ShowSessionHistory(); + return 0; + } + + // Cleanup all sessions except current + else if (command == L"cleanup-sessions") + { + g_controller->m_sessionMgr.CleanupAllSessionsExceptCurrent(); + return 0; + } + else if (command == L"list-signer") { if (argc < 3) { ERROR(L"Missing signer type argument"); diff --git a/kvc/Kvc.vcxproj b/kvc/Kvc.vcxproj index 87fff61..18bfea1 100644 --- a/kvc/Kvc.vcxproj +++ b/kvc/Kvc.vcxproj @@ -121,6 +121,7 @@ + @@ -137,6 +138,7 @@ + diff --git a/kvc/SessionManager.cpp b/kvc/SessionManager.cpp new file mode 100644 index 0000000..9720707 --- /dev/null +++ b/kvc/SessionManager.cpp @@ -0,0 +1,876 @@ +/******************************************************************************* + _ ____ ______ + | |/ /\ \ / / ___| + | ' / \ \ / / | + | . \ \ 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 + +*******************************************************************************/ + +// SessionManager.cpp +#include "SessionManager.h" +#include "Controller.h" +#include "Utils.h" +#include +#include +#include + +// Static cache cleared on reboot detection +static std::wstring g_cachedBootSession; + +// Calculate current boot time as unique session ID +std::wstring SessionManager::CalculateBootTime() noexcept +{ + FILETIME ftNow; + GetSystemTimeAsFileTime(&ftNow); + ULONGLONG currentTime = (static_cast(ftNow.dwHighDateTime) << 32) | ftNow.dwLowDateTime; + ULONGLONG tickCount = GetTickCount64(); + ULONGLONG bootTime = currentTime - (tickCount * 10000ULL); + + std::wostringstream oss; + oss << bootTime; + return oss.str(); +} + +ULONGLONG SessionManager::GetLastBootIdFromRegistry() noexcept +{ + std::wstring basePath = GetRegistryBasePath(); + HKEY hKey; + + if (RegOpenKeyExW(HKEY_CURRENT_USER, basePath.c_str(), 0, KEY_READ, &hKey) != ERROR_SUCCESS) + return 0; + + ULONGLONG lastBootId = 0; + DWORD dataSize = sizeof(ULONGLONG); + RegQueryValueExW(hKey, L"LastBootId", nullptr, nullptr, reinterpret_cast(&lastBootId), &dataSize); + + RegCloseKey(hKey); + return lastBootId; +} + +void SessionManager::SaveLastBootId(ULONGLONG bootId) noexcept +{ + std::wstring basePath = GetRegistryBasePath(); + HKEY hKey = OpenOrCreateKey(basePath); + + if (hKey) + { + RegSetValueExW(hKey, L"LastBootId", 0, REG_QWORD, reinterpret_cast(&bootId), sizeof(ULONGLONG)); + RegCloseKey(hKey); + } +} + +ULONGLONG SessionManager::GetLastTickCountFromRegistry() noexcept +{ + std::wstring basePath = GetRegistryBasePath(); + HKEY hKey; + + if (RegOpenKeyExW(HKEY_CURRENT_USER, basePath.c_str(), 0, KEY_READ, &hKey) != ERROR_SUCCESS) + return 0; + + ULONGLONG lastTickCount = 0; + DWORD dataSize = sizeof(ULONGLONG); + RegQueryValueExW(hKey, L"LastTickCount", nullptr, nullptr, reinterpret_cast(&lastTickCount), &dataSize); + + RegCloseKey(hKey); + return lastTickCount; +} + +void SessionManager::SaveLastTickCount(ULONGLONG tickCount) noexcept +{ + std::wstring basePath = GetRegistryBasePath(); + HKEY hKey = OpenOrCreateKey(basePath); + + if (hKey) + { + RegSetValueExW(hKey, L"LastTickCount", 0, REG_QWORD, reinterpret_cast(&tickCount), sizeof(ULONGLONG)); + RegCloseKey(hKey); + } +} + +std::wstring SessionManager::GetCurrentBootSession() noexcept +{ + if (!g_cachedBootSession.empty()) + return g_cachedBootSession; + + ULONGLONG lastBootId = GetLastBootIdFromRegistry(); + + if (lastBootId == 0) + { + // First run ever - calculate and save + std::wstring calculatedSession = CalculateBootTime(); + ULONGLONG calculatedBootId = std::stoull(calculatedSession); + SaveLastBootId(calculatedBootId); + g_cachedBootSession = calculatedSession; + return g_cachedBootSession; + } + + // Use LastBootId from registry as session ID + std::wostringstream oss; + oss << lastBootId; + g_cachedBootSession = oss.str(); + + return g_cachedBootSession; +} + +void SessionManager::DetectAndHandleReboot() noexcept +{ + ULONGLONG currentTick = GetTickCount64(); + ULONGLONG lastTick = GetLastTickCountFromRegistry(); + ULONGLONG lastBootId = GetLastBootIdFromRegistry(); + + if (lastBootId == 0) + { + // First run ever + std::wstring calculatedSession = CalculateBootTime(); + ULONGLONG calculatedBootId = std::stoull(calculatedSession); + SaveLastBootId(calculatedBootId); + SaveLastTickCount(currentTick); + g_cachedBootSession = calculatedSession; + return; + } + + // Detect reboot: tickCount decreased + if (currentTick < lastTick) + { + // New boot detected + std::wstring calculatedSession = CalculateBootTime(); + ULONGLONG calculatedBootId = std::stoull(calculatedSession); + SaveLastBootId(calculatedBootId); + SaveLastTickCount(currentTick); + g_cachedBootSession = calculatedSession; + + // Enforce session limit + EnforceSessionLimit(MAX_SESSIONS); + } + else + { + // Same boot - use LastBootId as session ID + SaveLastTickCount(currentTick); + std::wostringstream oss; + oss << lastBootId; + g_cachedBootSession = oss.str(); + } +} + +std::vector SessionManager::GetAllSessionIds() noexcept +{ + std::vector sessionIds; + std::wstring basePath = GetRegistryBasePath() + L"\\Sessions"; + + HKEY hSessions; + if (RegOpenKeyExW(HKEY_CURRENT_USER, basePath.c_str(), 0, KEY_READ, &hSessions) != ERROR_SUCCESS) + return sessionIds; + + DWORD index = 0; + wchar_t sessionName[256]; + DWORD sessionNameSize; + + while (true) + { + sessionNameSize = 256; + if (RegEnumKeyExW(hSessions, index, sessionName, &sessionNameSize, nullptr, nullptr, nullptr, nullptr) != ERROR_SUCCESS) + break; + + sessionIds.push_back(sessionName); + index++; + } + + RegCloseKey(hSessions); + return sessionIds; +} + +void SessionManager::EnforceSessionLimit(int maxSessions) noexcept +{ + auto sessions = GetAllSessionIds(); + + if (static_cast(sessions.size()) <= maxSessions) + return; + + // Sort sessions by ID (oldest first) + std::sort(sessions.begin(), sessions.end(), [](const std::wstring& a, const std::wstring& b) { + try { + return std::stoull(a) < std::stoull(b); + } catch (...) { + return a < b; + } + }); + + std::wstring currentSession = GetCurrentBootSession(); + std::wstring basePath = GetRegistryBasePath() + L"\\Sessions"; + + HKEY hSessions; + if (RegOpenKeyExW(HKEY_CURRENT_USER, basePath.c_str(), 0, KEY_WRITE, &hSessions) != ERROR_SUCCESS) + return; + + int toDelete = static_cast(sessions.size()) - maxSessions; + int deleted = 0; + + for (const auto& sessionId : sessions) + { + if (deleted >= toDelete) + break; + + if (sessionId != currentSession) + { + DeleteKeyRecursive(hSessions, sessionId); + DEBUG(L"Deleted old session: %s", sessionId.c_str()); + deleted++; + } + } + + RegCloseKey(hSessions); + + if (deleted > 0) + { + INFO(L"Enforced session limit: deleted %d old sessions", deleted); + } +} + +void SessionManager::CleanupAllSessionsExceptCurrent() noexcept +{ + std::wstring currentSession = GetCurrentBootSession(); + std::wstring basePath = GetRegistryBasePath() + L"\\Sessions"; + + HKEY hSessions; + if (RegOpenKeyExW(HKEY_CURRENT_USER, basePath.c_str(), 0, KEY_READ | KEY_WRITE, &hSessions) != ERROR_SUCCESS) + { + INFO(L"No sessions to cleanup"); + return; + } + + DWORD index = 0; + wchar_t subKeyName[256]; + DWORD subKeyNameSize; + std::vector keysToDelete; + + while (true) + { + subKeyNameSize = 256; + if (RegEnumKeyExW(hSessions, index, subKeyName, &subKeyNameSize, nullptr, nullptr, nullptr, nullptr) != ERROR_SUCCESS) + break; + + std::wstring keyName = subKeyName; + if (keyName != currentSession) + keysToDelete.push_back(keyName); + + index++; + } + + for (const auto& key : keysToDelete) + { + DeleteKeyRecursive(hSessions, key); + } + + RegCloseKey(hSessions); + + if (!keysToDelete.empty()) + { + SUCCESS(L"Cleaned up %zu old sessions (kept current session)", keysToDelete.size()); + } + else + { + INFO(L"No old sessions to cleanup"); + } +} + +std::wstring SessionManager::GetRegistryBasePath() noexcept +{ + return L"Software\\kvc"; +} + +std::wstring SessionManager::GetSessionPath(const std::wstring& sessionId) noexcept +{ + return GetRegistryBasePath() + L"\\Sessions\\" + sessionId; +} + +// Remove all session keys except current boot session +void SessionManager::CleanupStaleSessions() noexcept +{ + std::wstring currentSession = GetCurrentBootSession(); + std::wstring basePath = GetRegistryBasePath() + L"\\Sessions"; + + HKEY hSessions; + if (RegOpenKeyExW(HKEY_CURRENT_USER, basePath.c_str(), 0, KEY_READ | KEY_WRITE, &hSessions) != ERROR_SUCCESS) + return; + + DWORD index = 0; + wchar_t subKeyName[256]; + DWORD subKeyNameSize; + + std::vector keysToDelete; + + while (true) + { + subKeyNameSize = 256; + if (RegEnumKeyExW(hSessions, index, subKeyName, &subKeyNameSize, nullptr, nullptr, nullptr, nullptr) != ERROR_SUCCESS) + break; + + std::wstring keyName = subKeyName; + if (keyName != currentSession) + keysToDelete.push_back(keyName); + + index++; + } + + // Delete stale sessions + for (const auto& key : keysToDelete) + { + DeleteKeyRecursive(hSessions, key); + } + + RegCloseKey(hSessions); +} + +// Save process state before unprotect operation +bool SessionManager::SaveUnprotectOperation(const std::wstring& signerName, + const std::vector& affectedProcesses) noexcept +{ + if (affectedProcesses.empty()) + return true; + + // Use original signer name (no normalization) + std::wstring sessionPath = GetSessionPath(GetCurrentBootSession()); + std::wstring signerPath = sessionPath + L"\\" + signerName; + + HKEY hKey = OpenOrCreateKey(signerPath); + if (!hKey) + { + ERROR(L"Failed to create registry key for session state"); + return false; + } + + DWORD index = 0; + for (const auto& proc : affectedProcesses) + { + SessionEntry entry; + entry.Pid = proc.Pid; + entry.ProcessName = proc.ProcessName; + entry.OriginalProtection = Utils::GetProtection(proc.ProtectionLevel, proc.SignerType); + entry.SignatureLevel = proc.SignatureLevel; + entry.SectionSignatureLevel = proc.SectionSignatureLevel; + entry.Status = L"UNPROTECTED"; + + if (!WriteSessionEntry(signerName, index, entry)) + { + RegCloseKey(hKey); + return false; + } + + index++; + } + + // Write count + DWORD count = static_cast(affectedProcesses.size()); + RegSetValueExW(hKey, L"Count", 0, REG_DWORD, reinterpret_cast(&count), sizeof(DWORD)); + + RegCloseKey(hKey); + + SUCCESS(L"Session state saved to registry (%d processes tracked)", count); + return true; +} + +bool SessionManager::WriteSessionEntry(const std::wstring& signerName, DWORD index, const SessionEntry& entry) noexcept +{ + std::wstring sessionPath = GetSessionPath(GetCurrentBootSession()); + std::wstring signerPath = sessionPath + L"\\" + signerName; + + HKEY hKey; + if (RegOpenKeyExW(HKEY_CURRENT_USER, signerPath.c_str(), 0, KEY_WRITE, &hKey) != ERROR_SUCCESS) + return false; + + // Format: "PID|ProcessName|Protection|SigLevel|SecSigLevel|Status" + std::wostringstream oss; + oss << entry.Pid << L"|" + << entry.ProcessName << L"|" + << static_cast(entry.OriginalProtection) << L"|" + << static_cast(entry.SignatureLevel) << L"|" + << static_cast(entry.SectionSignatureLevel) << L"|" + << entry.Status; + + std::wstring valueName = L"Proc_" + std::to_wstring(index); + std::wstring valueData = oss.str(); + + LONG result = RegSetValueExW(hKey, valueName.c_str(), 0, REG_SZ, + reinterpret_cast(valueData.c_str()), + static_cast((valueData.length() + 1) * sizeof(wchar_t))); + + RegCloseKey(hKey); + return result == ERROR_SUCCESS; +} + +bool SessionManager::UpdateEntryStatus(const std::wstring& signerName, DWORD index, const std::wstring& newStatus) noexcept +{ + std::wstring sessionPath = GetSessionPath(GetCurrentBootSession()); + std::wstring signerPath = sessionPath + L"\\" + signerName; + + HKEY hKey; + if (RegOpenKeyExW(HKEY_CURRENT_USER, signerPath.c_str(), 0, KEY_READ | KEY_WRITE, &hKey) != ERROR_SUCCESS) + return false; + + std::wstring valueName = L"Proc_" + std::to_wstring(index); + wchar_t valueData[512]; + DWORD valueSize = sizeof(valueData); + + if (RegQueryValueExW(hKey, valueName.c_str(), nullptr, nullptr, + reinterpret_cast(valueData), &valueSize) != ERROR_SUCCESS) + { + RegCloseKey(hKey); + return false; + } + + // Parse existing entry + std::wstring data = valueData; + std::vector parts; + std::wstring current; + + for (wchar_t ch : data) + { + if (ch == L'|') + { + parts.push_back(current); + current.clear(); + } + else + { + current += ch; + } + } + if (!current.empty()) + parts.push_back(current); + + if (parts.size() < 5) + { + RegCloseKey(hKey); + return false; + } + + // Rebuild with new status + std::wostringstream oss; + oss << parts[0] << L"|" << parts[1] << L"|" << parts[2] << L"|" + << parts[3] << L"|" << parts[4] << L"|" << newStatus; + + std::wstring newValueData = oss.str(); + + LONG result = RegSetValueExW(hKey, valueName.c_str(), 0, REG_SZ, + reinterpret_cast(newValueData.c_str()), + static_cast((newValueData.length() + 1) * sizeof(wchar_t))); + + RegCloseKey(hKey); + return result == ERROR_SUCCESS; +} + +std::vector SessionManager::LoadSessionEntries(const std::wstring& signerName) noexcept +{ + std::vector entries; + + // Normalize signer name for case-insensitive comparison + std::wstring normalizedSigner = signerName; + std::transform(normalizedSigner.begin(), normalizedSigner.end(), + normalizedSigner.begin(), ::towlower); + + std::wstring sessionPath = GetSessionPath(GetCurrentBootSession()); + + HKEY hSession; + if (RegOpenKeyExW(HKEY_CURRENT_USER, sessionPath.c_str(), 0, KEY_READ, &hSession) != ERROR_SUCCESS) + return entries; + + // Search all subkeys for matching signer (case-insensitive) + DWORD index = 0; + wchar_t subKeyName[256]; + DWORD subKeyNameSize; + std::wstring foundSignerKey; + + while (true) + { + subKeyNameSize = 256; + LONG result = RegEnumKeyExW(hSession, index, subKeyName, &subKeyNameSize, nullptr, nullptr, nullptr, nullptr); + if (result != ERROR_SUCCESS) + break; + + std::wstring candidate = subKeyName; + std::wstring normalizedCandidate = candidate; + std::transform(normalizedCandidate.begin(), normalizedCandidate.end(), + normalizedCandidate.begin(), ::towlower); + + if (normalizedCandidate == normalizedSigner) { + foundSignerKey = candidate; + break; + } + + index++; + } + + if (foundSignerKey.empty()) { + RegCloseKey(hSession); + DEBUG(L"No signer key found for: %s (normalized: %s)", signerName.c_str(), normalizedSigner.c_str()); + return entries; + } + + // Use found key name + entries = LoadSessionEntriesFromPath(sessionPath, foundSignerKey); + RegCloseKey(hSession); + + DEBUG(L"Loaded %zu entries for signer: %s (key: %s)", entries.size(), signerName.c_str(), foundSignerKey.c_str()); + return entries; +} + +std::vector SessionManager::LoadSessionEntriesFromPath(const std::wstring& sessionPath, const std::wstring& signerName) noexcept +{ + std::vector entries; + + std::wstring signerPath = sessionPath + L"\\" + signerName; + + HKEY hKey; + if (RegOpenKeyExW(HKEY_CURRENT_USER, signerPath.c_str(), 0, KEY_READ, &hKey) != ERROR_SUCCESS) + return entries; + + DWORD count = 0; + DWORD dataSize = sizeof(DWORD); + if (RegQueryValueExW(hKey, L"Count", nullptr, nullptr, reinterpret_cast(&count), &dataSize) != ERROR_SUCCESS) { + count = 0; + } + + for (DWORD i = 0; i < count; i++) + { + std::wstring valueName = L"Proc_" + std::to_wstring(i); + wchar_t valueData[512]; + DWORD valueSize = sizeof(valueData); + + if (RegQueryValueExW(hKey, valueName.c_str(), nullptr, nullptr, + reinterpret_cast(valueData), &valueSize) == ERROR_SUCCESS) + { + // Parse: "PID|ProcessName|Protection|SigLevel|SecSigLevel|Status" + std::wstring data = valueData; + std::vector parts; + std::wstring current; + + for (wchar_t ch : data) + { + if (ch == L'|') + { + parts.push_back(current); + current.clear(); + } + else + { + current += ch; + } + } + if (!current.empty()) + parts.push_back(current); + + if (parts.size() >= 5) + { + SessionEntry entry; + entry.Pid = static_cast(std::stoul(parts[0])); + entry.ProcessName = parts[1]; + entry.OriginalProtection = static_cast(std::stoi(parts[2])); + entry.SignatureLevel = static_cast(std::stoi(parts[3])); + entry.SectionSignatureLevel = static_cast(std::stoi(parts[4])); + entry.Status = (parts.size() >= 6) ? parts[5] : L"UNPROTECTED"; + + entries.push_back(entry); + } + } + } + + RegCloseKey(hKey); + return entries; +} + +// Restore protection for specific signer group +bool SessionManager::RestoreBySigner(const std::wstring& signerName, Controller* controller) noexcept +{ + if (!controller) + { + ERROR(L"Controller not available for restoration"); + return false; + } + + // Find actual signer key name in registry (case-insensitive search) + std::wstring normalizedSigner = signerName; + std::transform(normalizedSigner.begin(), normalizedSigner.end(), + normalizedSigner.begin(), ::towlower); + + std::wstring sessionPath = GetSessionPath(GetCurrentBootSession()); + + HKEY hSession; + if (RegOpenKeyExW(HKEY_CURRENT_USER, sessionPath.c_str(), 0, KEY_READ, &hSession) != ERROR_SUCCESS) + { + INFO(L"No saved state found for signer: %s", signerName.c_str()); + return false; + } + + // Find actual key name in registry + DWORD index = 0; + wchar_t subKeyName[256]; + DWORD subKeyNameSize; + std::wstring foundSignerKey; + + while (true) + { + subKeyNameSize = 256; + LONG result = RegEnumKeyExW(hSession, index, subKeyName, &subKeyNameSize, nullptr, nullptr, nullptr, nullptr); + if (result != ERROR_SUCCESS) + break; + + std::wstring candidate = subKeyName; + std::wstring normalizedCandidate = candidate; + std::transform(normalizedCandidate.begin(), normalizedCandidate.end(), + normalizedCandidate.begin(), ::towlower); + + if (normalizedCandidate == normalizedSigner) { + foundSignerKey = candidate; + break; + } + + index++; + } + + RegCloseKey(hSession); + + if (foundSignerKey.empty()) + { + INFO(L"No saved state found for signer: %s", signerName.c_str()); + return false; + } + + // Load entries using actual key name + auto entries = LoadSessionEntriesFromPath(sessionPath, foundSignerKey); + + if (entries.empty()) + { + INFO(L"No saved state found for signer: %s", signerName.c_str()); + return false; + } + + INFO(L"Restoring protection for %s (%zu processes)", signerName.c_str(), entries.size()); + + DWORD successCount = 0; + DWORD skipCount = 0; + DWORD entryIndex = 0; + + for (const auto& entry : entries) + { + // Skip if already restored + if (entry.Status == L"RESTORED") + { + skipCount++; + entryIndex++; + continue; + } + + // Check if process still exists + auto kernelAddr = controller->GetProcessKernelAddress(entry.Pid); + if (!kernelAddr) + { + INFO(L"Skipping PID %d (%s) - process no longer exists", entry.Pid, entry.ProcessName.c_str()); + skipCount++; + entryIndex++; + continue; + } + + // Restore original protection + if (controller->SetProcessProtection(kernelAddr.value(), entry.OriginalProtection)) + { + UpdateEntryStatus(foundSignerKey, entryIndex, L"RESTORED"); + SUCCESS(L"Restored protection for PID %d (%s)", entry.Pid, entry.ProcessName.c_str()); + successCount++; + } + else + { + ERROR(L"Failed to restore protection for PID %d (%s)", entry.Pid, entry.ProcessName.c_str()); + } + + entryIndex++; + } + + INFO(L"Restoration completed: %d restored, %d skipped", successCount, skipCount); + return successCount > 0; +} + +// Restore all saved protection states +bool SessionManager::RestoreAll(Controller* controller) noexcept +{ + if (!controller) + { + ERROR(L"Controller not available for restoration"); + return false; + } + + std::wstring sessionPath = GetSessionPath(GetCurrentBootSession()); + + HKEY hSession; + if (RegOpenKeyExW(HKEY_CURRENT_USER, sessionPath.c_str(), 0, KEY_READ, &hSession) != ERROR_SUCCESS) + { + INFO(L"No saved session state found"); + return false; + } + + // Enumerate all signer subkeys + DWORD index = 0; + wchar_t subKeyName[256]; + DWORD subKeyNameSize; + std::vector signers; + + while (true) + { + subKeyNameSize = 256; + if (RegEnumKeyExW(hSession, index, subKeyName, &subKeyNameSize, nullptr, nullptr, nullptr, nullptr) != ERROR_SUCCESS) + break; + + signers.push_back(subKeyName); + index++; + } + + RegCloseKey(hSession); + + if (signers.empty()) + { + INFO(L"No saved state found in current session"); + return false; + } + + INFO(L"Restoring all protection states (%zu groups)", signers.size()); + + bool anySuccess = false; + for (const auto& signer : signers) + { + if (RestoreBySigner(signer, controller)) + anySuccess = true; + } + + return anySuccess; +} + +// Display saved session history +void SessionManager::ShowHistory() noexcept +{ + std::wstring basePath = GetRegistryBasePath() + L"\\Sessions"; + + HKEY hSessions; + if (RegOpenKeyExW(HKEY_CURRENT_USER, basePath.c_str(), 0, KEY_READ, &hSessions) != ERROR_SUCCESS) + { + INFO(L"No saved session state found (cannot open sessions key)"); + return; + } + + // Show current calculated boot session ID + std::wstring currentSession = GetCurrentBootSession(); + INFO(L"Current boot session ID: %s", currentSession.c_str()); + INFO(L"All sessions found in registry:"); + + DWORD index = 0; + wchar_t subKeyName[256]; + DWORD subKeyNameSize; + bool foundSessions = false; + + while (true) + { + subKeyNameSize = 256; + if (RegEnumKeyExW(hSessions, index, subKeyName, &subKeyNameSize, nullptr, nullptr, nullptr, nullptr) != ERROR_SUCCESS) + break; + + std::wstring sessionId = subKeyName; + std::wcout << L"\nSession: " << sessionId; + if (sessionId == currentSession) { + std::wcout << L" [CURRENT]"; + } + std::wcout << L"\n"; + + std::wstring sessionPath = basePath + L"\\" + sessionId; + HKEY hSession; + if (RegOpenKeyExW(HKEY_CURRENT_USER, sessionPath.c_str(), 0, KEY_READ, &hSession) == ERROR_SUCCESS) + { + DWORD signerIndex = 0; + wchar_t signerName[256]; + DWORD signerNameSize; + + while (true) + { + signerNameSize = 256; + if (RegEnumKeyExW(hSession, signerIndex, signerName, &signerNameSize, nullptr, nullptr, nullptr, nullptr) != ERROR_SUCCESS) + break; + + std::wstring signer = signerName; + auto entries = LoadSessionEntriesFromPath(sessionPath, signer); + std::wcout << L" [" << signer << L"] - " << entries.size() << L" processes\n"; + + for (const auto& entry : entries) + { + std::wcout << L" PID " << entry.Pid << L": " << entry.ProcessName + << L" (protection: 0x" << std::hex << static_cast(entry.OriginalProtection) + << std::dec << L", status: " << entry.Status << L")\n"; + } + + signerIndex++; + foundSessions = true; + } + RegCloseKey(hSession); + } + index++; + } + + RegCloseKey(hSessions); + + if (!foundSessions) { + INFO(L"No session data found in registry"); + } +} + +HKEY SessionManager::OpenOrCreateKey(const std::wstring& path) noexcept +{ + HKEY hKey; + DWORD disposition; + + if (RegCreateKeyExW(HKEY_CURRENT_USER, path.c_str(), 0, nullptr, + REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, nullptr, + &hKey, &disposition) != ERROR_SUCCESS) + { + return nullptr; + } + + return hKey; +} + +bool SessionManager::DeleteKeyRecursive(HKEY hKeyParent, const std::wstring& subKey) noexcept +{ + HKEY hKey; + if (RegOpenKeyExW(hKeyParent, subKey.c_str(), 0, KEY_READ | KEY_WRITE, &hKey) != ERROR_SUCCESS) + return false; + + // Delete all subkeys first + wchar_t childName[256]; + DWORD childNameSize; + + while (true) + { + childNameSize = 256; + if (RegEnumKeyExW(hKey, 0, childName, &childNameSize, nullptr, nullptr, nullptr, nullptr) != ERROR_SUCCESS) + break; + + DeleteKeyRecursive(hKey, childName); + } + + RegCloseKey(hKey); + RegDeleteKeyW(hKeyParent, subKey.c_str()); + + return true; +} \ No newline at end of file diff --git a/kvc/SessionManager.h b/kvc/SessionManager.h new file mode 100644 index 0000000..9b18e6a --- /dev/null +++ b/kvc/SessionManager.h @@ -0,0 +1,68 @@ +// SessionManager.h +#pragma once + +#include "common.h" +#include +#include +#include + +struct ProcessEntry; + +// Session state entry for restoration tracking +struct SessionEntry +{ + DWORD Pid; + std::wstring ProcessName; + UCHAR OriginalProtection; // Combined level + signer + UCHAR SignatureLevel; + UCHAR SectionSignatureLevel; + std::wstring Status; // "UNPROTECTED" or "RESTORED" +}; + +// Manages process protection state across boot sessions +class SessionManager +{ +public: + SessionManager() = default; + ~SessionManager() = default; + + // Session lifecycle management + void CleanupStaleSessions() noexcept; + void CleanupAllSessionsExceptCurrent() noexcept; + void DetectAndHandleReboot() noexcept; + void EnforceSessionLimit(int maxSessions) noexcept; + + // State tracking operations + bool SaveUnprotectOperation(const std::wstring& signerName, + const std::vector& affectedProcesses) noexcept; + + // Restoration operations + bool RestoreBySigner(const std::wstring& signerName, class Controller* controller) noexcept; + bool RestoreAll(class Controller* controller) noexcept; + + // Query operations + void ShowHistory() noexcept; + +private: + std::wstring GetCurrentBootSession() noexcept; + std::wstring CalculateBootTime() noexcept; + ULONGLONG GetLastBootIdFromRegistry() noexcept; + void SaveLastBootId(ULONGLONG bootId) noexcept; + + ULONGLONG GetLastTickCountFromRegistry() noexcept; + void SaveLastTickCount(ULONGLONG tickCount) noexcept; + + std::wstring GetRegistryBasePath() noexcept; + std::wstring GetSessionPath(const std::wstring& sessionId) noexcept; + + std::vector LoadSessionEntries(const std::wstring& signerName) noexcept; + std::vector LoadSessionEntriesFromPath(const std::wstring& sessionPath, const std::wstring& signerName) noexcept; + bool WriteSessionEntry(const std::wstring& signerName, DWORD index, const SessionEntry& entry) noexcept; + bool UpdateEntryStatus(const std::wstring& signerName, DWORD index, const std::wstring& newStatus) noexcept; + + std::vector GetAllSessionIds() noexcept; + + // Registry helpers + HKEY OpenOrCreateKey(const std::wstring& path) noexcept; + bool DeleteKeyRecursive(HKEY hKeyParent, const std::wstring& subKey) noexcept; +}; \ No newline at end of file diff --git a/kvc/common.h b/kvc/common.h index 72ca359..8edd4f2 100644 --- a/kvc/common.h +++ b/kvc/common.h @@ -15,6 +15,9 @@ #include #include +// Session management constants +inline constexpr int MAX_SESSIONS = 16; + #ifdef BUILD_DATE #define __DATE__ BUILD_DATE #endif @@ -29,7 +32,6 @@ #undef ERROR #endif - #ifndef SHTDN_REASON_MAJOR_SOFTWARE #define SHTDN_REASON_MAJOR_SOFTWARE 0x00030000 #endif @@ -56,7 +58,7 @@ struct SystemModuleDeleter { using ModuleHandle = std::unique_ptr, ModuleDeleter>; using SystemModuleHandle = std::unique_ptr, SystemModuleDeleter>; -// Logging system with message formatting +// Fixed logging system with proper buffer size and variadic handling template void PrintMessage(const wchar_t* prefix, const wchar_t* format, Args&&... args) { @@ -70,7 +72,7 @@ void PrintMessage(const wchar_t* prefix, const wchar_t* format, Args&&... args) else { wchar_t buffer[1024]; - swprintf_s(buffer, format, std::forward(args)...); + swprintf_s(buffer, 1024, format, std::forward(args)...); ss << buffer; } @@ -79,19 +81,19 @@ void PrintMessage(const wchar_t* prefix, const wchar_t* format, Args&&... args) } #if kvc_DEBUG_ENABLED - #define DEBUG(format, ...) PrintMessage(L"[DEBUG] ", format, __VA_ARGS__) + #define DEBUG(format, ...) PrintMessage(L"[DEBUG] ", format, ##__VA_ARGS__) #else #define DEBUG(format, ...) do {} while(0) #endif -#define ERROR(format, ...) PrintMessage(L"[-] ", format, __VA_ARGS__) -#define INFO(format, ...) PrintMessage(L"[*] ", format, __VA_ARGS__) -#define SUCCESS(format, ...) PrintMessage(L"[+] ", format, __VA_ARGS__) +#define ERROR(format, ...) PrintMessage(L"[-] ", format, ##__VA_ARGS__) +#define INFO(format, ...) PrintMessage(L"[*] ", format, ##__VA_ARGS__) +#define SUCCESS(format, ...) PrintMessage(L"[+] ", format, ##__VA_ARGS__) #define LASTERROR(f) \ do { \ wchar_t buf[256]; \ - swprintf_s(buf, L"[-] The function '%s' failed with error code 0x%08x.\r\n", L##f, GetLastError()); \ + swprintf_s(buf, 256, L"[-] The function '%s' failed with error code 0x%08x.\r\n", L##f, GetLastError()); \ std::wcout << buf; \ } while(0) @@ -119,22 +121,22 @@ enum class PS_PROTECTED_SIGNER : UCHAR // Service-related constants namespace ServiceConstants { - constexpr const wchar_t* SERVICE_NAME = L"KernelVulnerabilityControl"; - constexpr const wchar_t* SERVICE_DISPLAY_NAME = L"Kernel Vulnerability Capabilities Framework"; - constexpr const wchar_t* SERVICE_PARAM = L"--service"; + inline constexpr wchar_t SERVICE_NAME[] = L"KernelVulnerabilityControl"; + inline constexpr wchar_t SERVICE_DISPLAY_NAME[] = L"Kernel Vulnerability Capabilities Framework"; + inline constexpr wchar_t SERVICE_PARAM[] = L"--service"; // Keyboard hook settings - constexpr int CTRL_SEQUENCE_LENGTH = 5; - constexpr DWORD CTRL_SEQUENCE_TIMEOUT_MS = 2000; - constexpr DWORD CTRL_DEBOUNCE_MS = 50; + inline constexpr int CTRL_SEQUENCE_LENGTH = 5; + inline constexpr DWORD CTRL_SEQUENCE_TIMEOUT_MS = 2000; + inline constexpr DWORD CTRL_DEBOUNCE_MS = 50; } // DPAPI constants for password extraction namespace DPAPIConstants { - constexpr int SQLITE_OK = 0; - constexpr int SQLITE_ROW = 100; - constexpr int SQLITE_DONE = 101; - constexpr int SQLITE_OPEN_READONLY = 0x00000001; + inline constexpr int SQLITE_OK = 0; + inline constexpr int SQLITE_ROW = 100; + inline constexpr int SQLITE_DONE = 101; + inline constexpr int SQLITE_OPEN_READONLY = 0x00000001; inline std::string GetChromeV10Prefix() { return "v10"; } inline std::string GetChromeDPAPIPrefix() { return "DPAPI"; } @@ -259,7 +261,7 @@ inline std::wstring GetDriverStorePathSafe() noexcept { } // KVC combined binary processing constants -constexpr std::array KVC_XOR_KEY = { 0xA0, 0xE2, 0x80, 0x8B, 0xE2, 0x80, 0x8C }; -constexpr wchar_t KVC_DATA_FILE[] = L"kvc.dat"; -constexpr wchar_t KVC_PASS_FILE[] = L"kvc_pass.exe"; -constexpr wchar_t KVC_CRYPT_FILE[] = L"kvc_crypt.dll"; +inline constexpr std::array KVC_XOR_KEY = { 0xA0, 0xE2, 0x80, 0x8B, 0xE2, 0x80, 0x8C }; +inline constexpr wchar_t KVC_DATA_FILE[] = L"kvc.dat"; +inline constexpr wchar_t KVC_PASS_FILE[] = L"kvc_pass.exe"; +inline constexpr wchar_t KVC_CRYPT_FILE[] = L"kvc_crypt.dll"; \ No newline at end of file