273 lines
8.8 KiB
C++
273 lines
8.8 KiB
C++
// KVC kernel driver communication implementation- Implements low-level IOCTL communication with the KVC kernel driver
|
|
|
|
#include "kvcDrv.h"
|
|
#include "common.h"
|
|
|
|
// ============================================================================
|
|
// IOCTL COMMAND CODES (DRIVER-SPECIFIC)
|
|
// ============================================================================
|
|
|
|
// IOCTL code for kernel memory read operations
|
|
constexpr DWORD RTC_IOCTL_MEMORY_READ = 0x80002048;
|
|
|
|
// IOCTL code for kernel memory write operations
|
|
constexpr DWORD RTC_IOCTL_MEMORY_WRITE = 0x8000204c;
|
|
|
|
// ============================================================================
|
|
// CONSTRUCTION AND DESTRUCTION
|
|
// ============================================================================
|
|
|
|
// Default constructor - initializes empty driver object
|
|
kvc::kvc() = default;
|
|
|
|
// Destructor - ensures proper resource cleanup
|
|
kvc::~kvc()
|
|
{
|
|
Cleanup();
|
|
}
|
|
|
|
// ============================================================================
|
|
// DRIVER CONNECTION MANAGEMENT
|
|
// ============================================================================
|
|
|
|
// Cleans up driver resources by flushing buffers, closing handle and clearing device name
|
|
void kvc::Cleanup() noexcept
|
|
{
|
|
DEBUG(L"kvc::Cleanup() called");
|
|
|
|
if (m_deviceHandle) {
|
|
DEBUG(L"Closing device handle...");
|
|
|
|
// Flush buffers before closing to prevent data loss
|
|
FlushFileBuffers(m_deviceHandle.get());
|
|
|
|
// Reset smart handle - automatically closes via HandleDeleter
|
|
m_deviceHandle.reset();
|
|
}
|
|
|
|
m_deviceName.clear();
|
|
DEBUG(L"kvc cleanup completed");
|
|
}
|
|
|
|
// Checks if driver connection is active
|
|
bool kvc::IsConnected() const noexcept
|
|
{
|
|
return m_deviceHandle && m_deviceHandle.get() != INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
// Establishes connection to KVC kernel driver by opening device handle with read/write access
|
|
bool kvc::Initialize() noexcept
|
|
{
|
|
// Idempotent check - return early if already connected
|
|
if (IsConnected()) {
|
|
return true;
|
|
}
|
|
|
|
// Construct device name if not set
|
|
if (m_deviceName.empty()) {
|
|
m_deviceName = L"\\\\.\\" + GetServiceName();
|
|
}
|
|
|
|
// Initialize dynamic APIs (required for CreateFileW pointer)
|
|
if (!InitDynamicAPIs()) {
|
|
DEBUG(L"Failed to initialize dynamic APIs");
|
|
return false;
|
|
}
|
|
|
|
// Open driver device with read/write access
|
|
HANDLE rawHandle = g_pCreateFileW(
|
|
m_deviceName.c_str(),
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
0, // No sharing
|
|
nullptr, // Default security
|
|
OPEN_EXISTING, // Device must exist
|
|
0, // No special flags
|
|
nullptr // No template
|
|
);
|
|
|
|
// Silent failure if driver not loaded - this is expected behavior
|
|
if (rawHandle == INVALID_HANDLE_VALUE) {
|
|
DEBUG(L"Failed to open driver device: %s (error: %d)",
|
|
m_deviceName.c_str(), GetLastError());
|
|
return false;
|
|
}
|
|
|
|
// Wrap raw handle in smart pointer for automatic cleanup
|
|
m_deviceHandle = UniqueHandle(rawHandle);
|
|
|
|
DEBUG(L"Successfully opened driver device: %s", m_deviceName.c_str());
|
|
return true;
|
|
}
|
|
|
|
// ============================================================================
|
|
// MEMORY READ OPERATIONS (TYPE-SAFE WRAPPERS)
|
|
// ============================================================================
|
|
|
|
// Reads 8-bit value from kernel memory by extracting lowest byte from 32-bit read
|
|
std::optional<BYTE> kvc::Read8(ULONG_PTR address) noexcept
|
|
{
|
|
auto value = Read32(address);
|
|
if (!value.has_value()) {
|
|
return std::nullopt;
|
|
}
|
|
return static_cast<BYTE>(value.value() & 0xFF);
|
|
}
|
|
|
|
// Reads 16-bit value from kernel memory by extracting lowest 2 bytes from 32-bit read
|
|
std::optional<WORD> kvc::Read16(ULONG_PTR address) noexcept
|
|
{
|
|
auto value = Read32(address);
|
|
if (!value.has_value()) {
|
|
return std::nullopt;
|
|
}
|
|
return static_cast<WORD>(value.value() & 0xFFFF);
|
|
}
|
|
|
|
// Reads 32-bit value from kernel memory via direct IOCTL call
|
|
std::optional<DWORD> kvc::Read32(ULONG_PTR address) noexcept
|
|
{
|
|
return Read(address, sizeof(DWORD));
|
|
}
|
|
|
|
// Reads 64-bit value from kernel memory by performing two 32-bit reads and combining them
|
|
std::optional<DWORD64> kvc::Read64(ULONG_PTR address) noexcept
|
|
{
|
|
auto low = Read32(address);
|
|
auto high = Read32(address + 4);
|
|
|
|
if (!low || !high) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
// Combine low and high DWORDs into QWORD
|
|
return (static_cast<DWORD64>(high.value()) << 32) | low.value();
|
|
}
|
|
|
|
// Reads pointer-sized value from kernel memory (64-bit on x64, 32-bit on x86)
|
|
std::optional<ULONG_PTR> kvc::ReadPtr(ULONG_PTR address) noexcept
|
|
{
|
|
#ifdef _WIN64
|
|
auto value = Read64(address);
|
|
if (!value.has_value()) {
|
|
return std::nullopt;
|
|
}
|
|
return static_cast<ULONG_PTR>(value.value());
|
|
#else
|
|
auto value = Read32(address);
|
|
if (!value.has_value()) {
|
|
return std::nullopt;
|
|
}
|
|
return static_cast<ULONG_PTR>(value.value());
|
|
#endif
|
|
}
|
|
|
|
// ============================================================================
|
|
// MEMORY WRITE OPERATIONS (TYPE-SAFE WRAPPERS)
|
|
// ============================================================================
|
|
|
|
// Writes 8-bit value to kernel memory (WARNING: can cause system instability)
|
|
bool kvc::Write8(ULONG_PTR address, BYTE value) noexcept
|
|
{
|
|
return Write(address, sizeof(value), value);
|
|
}
|
|
|
|
// Writes 16-bit value to kernel memory (WARNING: can cause system instability)
|
|
bool kvc::Write16(ULONG_PTR address, WORD value) noexcept
|
|
{
|
|
return Write(address, sizeof(value), value);
|
|
}
|
|
|
|
// Writes 32-bit value to kernel memory (WARNING: can cause system instability)
|
|
bool kvc::Write32(ULONG_PTR address, DWORD value) noexcept
|
|
{
|
|
return Write(address, sizeof(value), value);
|
|
}
|
|
|
|
// Writes 64-bit value to kernel memory via two 32-bit writes (WARNING: non-atomic, can cause system instability)
|
|
bool kvc::Write64(ULONG_PTR address, DWORD64 value) noexcept
|
|
{
|
|
DWORD low = static_cast<DWORD>(value & 0xFFFFFFFF);
|
|
DWORD high = static_cast<DWORD>((value >> 32) & 0xFFFFFFFF);
|
|
|
|
// Both writes must succeed
|
|
return Write32(address, low) && Write32(address + 4, high);
|
|
}
|
|
|
|
// ============================================================================
|
|
// LOW-LEVEL IOCTL COMMUNICATION
|
|
// ============================================================================
|
|
|
|
// Low-level kernel memory read via IOCTL using aligned RTC_MEMORY_READ structure
|
|
std::optional<DWORD> kvc::Read(ULONG_PTR address, DWORD valueSize) noexcept
|
|
{
|
|
// Construct read request with proper alignment
|
|
RTC_MEMORY_READ memoryRead{};
|
|
memoryRead.Address = address;
|
|
memoryRead.Size = valueSize;
|
|
|
|
// Ensure driver connection
|
|
if (!Initialize()) {
|
|
DEBUG(L"Driver not initialized for read operation");
|
|
return std::nullopt;
|
|
}
|
|
|
|
DWORD bytesReturned = 0;
|
|
|
|
// Send IOCTL to driver
|
|
BOOL result = DeviceIoControl(
|
|
m_deviceHandle.get(), // Device handle
|
|
RTC_IOCTL_MEMORY_READ, // IOCTL code
|
|
&memoryRead, // Input buffer
|
|
sizeof(memoryRead), // Input size
|
|
&memoryRead, // Output buffer (in-place)
|
|
sizeof(memoryRead), // Output size
|
|
&bytesReturned, // Bytes returned
|
|
nullptr // No overlapped I/O
|
|
);
|
|
|
|
if (!result) {
|
|
DEBUG(L"DeviceIoControl failed for read at 0x%llx: %d",
|
|
address, GetLastError());
|
|
return std::nullopt;
|
|
}
|
|
|
|
return memoryRead.Value;
|
|
}
|
|
|
|
// Low-level kernel memory write via IOCTL (WARNING: can cause BSOD if address is invalid)
|
|
bool kvc::Write(ULONG_PTR address, DWORD valueSize, DWORD value) noexcept
|
|
{
|
|
// Construct write request with proper alignment
|
|
RTC_MEMORY_WRITE memoryWrite{};
|
|
memoryWrite.Address = address;
|
|
memoryWrite.Size = valueSize;
|
|
memoryWrite.Value = value;
|
|
|
|
// Ensure driver connection
|
|
if (!Initialize()) {
|
|
DEBUG(L"Driver not initialized for write operation");
|
|
return false;
|
|
}
|
|
|
|
DWORD bytesReturned = 0;
|
|
|
|
// Send IOCTL to driver
|
|
BOOL result = DeviceIoControl(
|
|
m_deviceHandle.get(), // Device handle
|
|
RTC_IOCTL_MEMORY_WRITE, // IOCTL code
|
|
&memoryWrite, // Input buffer
|
|
sizeof(memoryWrite), // Input size
|
|
&memoryWrite, // Output buffer (unused for write)
|
|
sizeof(memoryWrite), // Output size
|
|
&bytesReturned, // Bytes returned
|
|
nullptr // No overlapped I/O
|
|
);
|
|
|
|
if (!result) {
|
|
DEBUG(L"DeviceIoControl failed for write at 0x%llx: %d",
|
|
address, GetLastError());
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
} |