Restore PAGE_GUARD memory breakpoints properly

This commit is contained in:
Duncan Ogilvie 2026-04-13 11:40:26 +02:00
parent 2cc003cdcf
commit e0a457965f
1 changed files with 25 additions and 46 deletions

View File

@ -260,16 +260,20 @@ namespace GleeBug
auto pageAddr = bpxPage->first; auto pageAddr = bpxPage->first;
auto pageProperties = bpxPage->second; auto pageProperties = bpxPage->second;
//TODO: If I only have a page with Read bp and the exception was not on read, I don't execute the callback. Because since this was implemented with PAGE_GUARD, writtes or executes still trigger // PAGE_GUARD is shared by access/read/write/execute breakpoints, so the exception type in
//This callback. // ExceptionInformation[0] decides whether this guard-page fault actually belongs to this breakpoint.
//FIX: If the memoryBreakpointPages for this page does not have a access flag and has a read flag, but the exception was not on read. Then we resume the debuggee. // Access breakpoints intentionally match every access type.
if((exceptionRecord.ExceptionInformation[0] != 0)) const auto accessType = exceptionRecord.ExceptionInformation[0];
{ const auto isAccessBreakpoint = (pageProperties.Type & 0x1) != 0;
//The bpx is solely on read. const auto matchesRead = accessType == 0 && (((pageProperties.Type & 0x2) != 0) || isAccessBreakpoint);
if(((pageProperties.Type & 0x2) != 0) && ((pageProperties.Type & 0x1) == 0)) const auto matchesWrite = accessType == 1 && (((pageProperties.Type & 0x4) != 0) || isAccessBreakpoint);
const auto matchesExecute = accessType == 8 && (((pageProperties.Type & 0x8) != 0) || isAccessBreakpoint);
if(!matchesRead && !matchesWrite && !matchesExecute)
{ {
mContinueStatus = DBG_CONTINUE; mContinueStatus = DBG_CONTINUE;
//We restore the protection // This page belongs to some memory breakpoint, but not one that should trigger on this access type.
// Restore the original protection, single-step the faulting instruction, then re-apply the guard/page
// permissions if the page is still tracked by any memory breakpoint.
if(!mProcess->MemProtect(pageAddr, PAGE_SIZE, pageProperties.OldProtect)) if(!mProcess->MemProtect(pageAddr, PAGE_SIZE, pageProperties.OldProtect))
{ {
sprintf_s(error, "MemProtect failed on 0x%p", (void*)pageAddr); sprintf_s(error, "MemProtect failed on 0x%p", (void*)pageAddr);
@ -278,39 +282,14 @@ namespace GleeBug
mProcess->StepInternal([this, pageAddr]() mProcess->StepInternal([this, pageAddr]()
{ {
//seek out the page address
auto found_page = mProcess->memoryBreakpointPages.find(pageAddr); auto found_page = mProcess->memoryBreakpointPages.find(pageAddr);
if(found_page == mProcess->memoryBreakpointPages.end()) if(found_page == mProcess->memoryBreakpointPages.end())
{
//no page being used by bpx? Then just return
return; return;
}
mProcess->MemProtect(pageAddr, PAGE_SIZE, found_page->second.NewProtect); mProcess->MemProtect(pageAddr, PAGE_SIZE, found_page->second.NewProtect);
return; return;
}); });
return; return;
} }
else if(((pageProperties.Type & 0x1) != 0))
{
//We are fine if the breakpoint is on Access and somethine other than a read occurred.
}
else
{
//This exception handler was called within a page that had no breakpoints on read or access. Probably the program generated this exception! what a 0x1337 brat.
//In this situation we return control to debuggee.
return;
}
}
else
{
//The generated exception is on read.
//If the page doesn't have a breakpoint on read or on access then something else must have gone wrong - we pass execution to debuggee.
if((!(pageProperties.Type & 0x2)) && (!(pageProperties.Type & 0x1)))
{
return;
}
}
/* /*