track deleted breakpoints to handle stale events safely

This commit is contained in:
AzuLX 2026-01-10 12:36:40 +00:00
parent 8072f96a26
commit 5cc80cf3d9
No known key found for this signature in database
GPG Key ID: BED7E7DC23A637BC
5 changed files with 28 additions and 14 deletions

View File

@ -4,6 +4,7 @@
std::vector<BreakPointDetail> BreakPointBuffer; std::vector<BreakPointDetail> BreakPointBuffer;
std::unordered_map<ULONG_PTR, MemoryBreakpointPageDetail> MemoryBreakpointPages; std::unordered_map<ULONG_PTR, MemoryBreakpointPageDetail> MemoryBreakpointPages;
std::unordered_set<ULONG_PTR> recentlyDeletedBpx;
ULONG_PTR dr7uint(DR7* dr7) ULONG_PTR dr7uint(DR7* dr7)
{ {

View File

@ -3,6 +3,7 @@
#include <vector> #include <vector>
#include <unordered_map> #include <unordered_map>
#include <unordered_set>
#include "Global.Engine.Threading.h" #include "Global.Engine.Threading.h"
#include "Global.Engine.h" #include "Global.Engine.h"
@ -11,6 +12,7 @@
extern std::vector<BreakPointDetail> BreakPointBuffer; extern std::vector<BreakPointDetail> BreakPointBuffer;
extern std::unordered_map<ULONG_PTR, MemoryBreakpointPageDetail> MemoryBreakpointPages; extern std::unordered_map<ULONG_PTR, MemoryBreakpointPageDetail> MemoryBreakpointPages;
extern std::unordered_set<ULONG_PTR> recentlyDeletedBpx;
void uintdr7(ULONG_PTR dr7, DR7* ret); void uintdr7(ULONG_PTR dr7, DR7* ret);
ULONG_PTR dr7uint(DR7* dr7); ULONG_PTR dr7uint(DR7* dr7);

View File

@ -89,6 +89,7 @@ void DebuggerReset()
} }
std::vector<BreakPointDetail>().swap(BreakPointBuffer); std::vector<BreakPointDetail>().swap(BreakPointBuffer);
std::unordered_map<ULONG_PTR, MemoryBreakpointPageDetail>().swap(MemoryBreakpointPages); std::unordered_map<ULONG_PTR, MemoryBreakpointPageDetail>().swap(MemoryBreakpointPages);
recentlyDeletedBpx.clear();
} }
void ClearProcessList() void ClearProcessList()

View File

@ -303,6 +303,7 @@ __declspec(dllexport) bool TITCALL DeleteBPX(ULONG_PTR bpxAddress)
FlushInstructionCache(dbgProcessInformation.hProcess, NULL, 0); FlushInstructionCache(dbgProcessInformation.hProcess, NULL, 0);
VirtualProtectEx(dbgProcessInformation.hProcess, (LPVOID)bpxAddress, BreakPointBuffer.at(found).BreakPointSize, OldProtect, &OldProtect); VirtualProtectEx(dbgProcessInformation.hProcess, (LPVOID)bpxAddress, BreakPointBuffer.at(found).BreakPointSize, OldProtect, &OldProtect);
BreakPointBuffer.erase(BreakPointBuffer.begin() + found); BreakPointBuffer.erase(BreakPointBuffer.begin() + found);
recentlyDeletedBpx.insert(bpxAddress);
return true; return true;
} }

View File

@ -101,6 +101,8 @@ __declspec(dllexport) void TITCALL DebugLoop()
memset(&DLLDebugFileName, 0, sizeof(DLLDebugFileName)); memset(&DLLDebugFileName, 0, sizeof(DLLDebugFileName));
engineFileIsBeingDebugged = true; engineFileIsBeingDebugged = true;
uint32_t consecutiveTimeouts = 0;
while(!BreakDBG) //actual debug loop while(!BreakDBG) //actual debug loop
{ {
bool synchronizedStep = false; bool synchronizedStep = false;
@ -124,10 +126,17 @@ __declspec(dllexport) void TITCALL DebugLoop()
else else
{ {
// Regular timeout, wait again // Regular timeout, wait again
// After 2 consecutive timeouts, clear recently deleted breakpoints
consecutiveTimeouts++;
if(consecutiveTimeouts >= 2)
recentlyDeletedBpx.clear();
continue; continue;
} }
} }
// Event received, reset timeout counter
consecutiveTimeouts = 0;
if(IsDbgReplyLaterSupported) if(IsDbgReplyLaterSupported)
{ {
if(DBGEvent.dwDebugEventCode == EXCEPTION_DEBUG_EVENT) if(DBGEvent.dwDebugEventCode == EXCEPTION_DEBUG_EVENT)
@ -590,29 +599,29 @@ __declspec(dllexport) void TITCALL DebugLoop()
if(DebugAttachedToProcess || !FirstBPX) //program generated a breakpoint exception if(DebugAttachedToProcess || !FirstBPX) //program generated a breakpoint exception
{ {
ULONG_PTR exceptionAddress = (ULONG_PTR)DBGEvent.u.Exception.ExceptionRecord.ExceptionAddress; ULONG_PTR exceptionAddress = (ULONG_PTR)DBGEvent.u.Exception.ExceptionRecord.ExceptionAddress;
unsigned char currentByte = 0xCC;
MemoryReadSafe(dbgProcessInformation.hProcess, (void*)exceptionAddress, &currentByte, 1, nullptr);
if(currentByte != 0xCC) if(recentlyDeletedBpx.find(exceptionAddress) != recentlyDeletedBpx.end())
{ {
//breakpoint was deleted - the byte is no longer 0xCC //breakpoint was recently deleted - handle the stale event gracefully
//reset IP to exception address and continue gracefully
DBGCode = DBG_CONTINUE;
hActiveThread = EngineOpenThread(THREAD_GETSETSUSPEND, false, DBGEvent.dwThreadId); hActiveThread = EngineOpenThread(THREAD_GETSETSUSPEND, false, DBGEvent.dwThreadId);
CONTEXT myDBGContext; if(hActiveThread != NULL)
myDBGContext.ContextFlags = ContextControlFlags; {
GetThreadContext(hActiveThread, &myDBGContext); CONTEXT myDBGContext;
myDBGContext.ContextFlags = ContextControlFlags;
GetThreadContext(hActiveThread, &myDBGContext);
#if defined(_WIN64) #if defined(_WIN64)
myDBGContext.Rip = exceptionAddress; myDBGContext.Rip = exceptionAddress;
#else #else
myDBGContext.Eip = (DWORD)exceptionAddress; myDBGContext.Eip = (DWORD)exceptionAddress;
#endif #endif
SetThreadContext(hActiveThread, &myDBGContext); SetThreadContext(hActiveThread, &myDBGContext);
EngineCloseHandle(hActiveThread); EngineCloseHandle(hActiveThread);
DBGCode = DBG_CONTINUE;
}
} }
else else
{ {
//byte is still 0xCC - this is a real int3 in the original code!! //not a recently deleted breakpoint - pass to debuggee
DBGCode = DBG_EXCEPTION_NOT_HANDLED; DBGCode = DBG_EXCEPTION_NOT_HANDLED;
if(DBGCustomHandler->chBreakPoint != NULL) if(DBGCustomHandler->chBreakPoint != NULL)
{ {