mirror of https://github.com/x64dbg/GleeBug
. Bugfix : If there is a page exception risen upon a page that is indeed being used by one of our breakpoints, then we simply restore the old page protections, singlestep and then apply the new page protection.
. exceptionAccessViolation implementation - the code looks almost equal to exceptionGuardPage except for the verifications made to try to spot on cases where the program uses unusual page permissions (in this case the program should then be granted the right to handle the self generated exception).
This commit is contained in:
parent
b57b716449
commit
9c4bab40ac
|
|
@ -172,56 +172,6 @@ namespace GleeBug
|
|||
|
||||
void Debugger::exceptionGuardPage(const EXCEPTION_RECORD & exceptionRecord, bool firstChance)
|
||||
{
|
||||
/* old code ~Duncan~
|
||||
char error[128] = "";
|
||||
auto exceptionAddress = ptr(exceptionRecord.ExceptionInformation[1]);
|
||||
//check if the exception address is directly in the range of a memory breakpoint
|
||||
auto foundRange = mProcess->memoryBreakpointRanges.find(Range(exceptionAddress, exceptionAddress));
|
||||
if (foundRange == mProcess->memoryBreakpointRanges.end())
|
||||
{
|
||||
//if not in range, check if a memory breakpoint is in the accessed page
|
||||
auto foundPage = mProcess->memoryBreakpointPages.find(exceptionAddress & ~(PAGE_SIZE - 1));
|
||||
if (foundPage != mProcess->memoryBreakpointPages.end())
|
||||
{
|
||||
//if the page contains a memory breakpoint we have to restore the old protection to correctly resume the debuggee
|
||||
const auto & page = foundPage->second;
|
||||
//TODO: single step and page protection changes
|
||||
if (!mProcess->MemProtect(foundPage->first, PAGE_SIZE, foundPage->second.NewProtect))
|
||||
{
|
||||
sprintf_s(error, "MemProtect failed on 0x%p", foundPage->first);
|
||||
cbInternalError(error);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
//find the breakpoint associated with the hit breakpoint range
|
||||
auto foundInfo = mProcess->breakpoints.find({ BreakpointType::Memory, foundRange->first });
|
||||
if (foundInfo == mProcess->breakpoints.end())
|
||||
{
|
||||
sprintf_s(error, "inconsistent memory breakpoint at 0x%p", exceptionAddress);
|
||||
cbInternalError(error);
|
||||
return;
|
||||
}
|
||||
//check if the memory breakpoint is disabled (meaning we shouldn't intercept the exception)
|
||||
//TODO: think about what happens with multiple breakpoints in one page where only one is disabled
|
||||
const auto info = foundInfo->second;
|
||||
if (!info.enabled)
|
||||
return;
|
||||
printf("memory breakpoint: 0x%p (size: %d)\n", info.address, info.internal.memory.size);
|
||||
//TODO: check if the right type is accessed (ExceptionInformation[0])
|
||||
//TODO: execute the user callback (if present)
|
||||
//TODO: single step and restore page protection
|
||||
*/
|
||||
//New code ~Marques~
|
||||
/* 0xcc breakpoing
|
||||
mThread->StepInternal(std::bind([this, info]()
|
||||
{
|
||||
//only restore the bytes if the breakpoint still exists
|
||||
if (mProcess->breakpoints.find({ BreakpointType::Software, info.address }) != mProcess->breakpoints.end())
|
||||
mProcess->MemWriteUnsafe(info.address, info.internal.software.newbytes, info.internal.software.size);
|
||||
}));
|
||||
*/
|
||||
|
||||
char error[128] = "";
|
||||
auto exceptionAddress = ptr(exceptionRecord.ExceptionInformation[1]);
|
||||
|
||||
|
|
@ -233,18 +183,21 @@ namespace GleeBug
|
|||
auto foundPage = mProcess->memoryBreakpointPages.find(exceptionAddress & ~(PAGE_SIZE - 1));
|
||||
if (foundPage != mProcess->memoryBreakpointPages.end())
|
||||
{
|
||||
mContinueStatus = DBG_CONTINUE;
|
||||
//if the page contains a memory breakpoint we have to restore the old protection to correctly resume the debuggee
|
||||
const auto & page = foundPage->second;
|
||||
const auto pBaseAddr = foundPage->first;
|
||||
//TODO: single step and page protection changes
|
||||
//FIXED (marques):
|
||||
//restored *OLD* page protection settings
|
||||
//FIXED
|
||||
if (!mProcess->MemProtect(foundPage->first, PAGE_SIZE, foundPage->second.OldProtect))
|
||||
{
|
||||
sprintf_s(error, "MemProtect failed on 0x%p", foundPage->first);
|
||||
cbInternalError(error);
|
||||
}
|
||||
mThread->StepInternal(std::bind([this]()
|
||||
//step + restore new protection to keep bp
|
||||
mThread->StepInternal(std::bind([this, page, pBaseAddr]()
|
||||
{
|
||||
mProcess->MemProtect(pBaseAddr, PAGE_SIZE, page.NewProtect);
|
||||
return;
|
||||
}));
|
||||
}
|
||||
|
|
@ -285,24 +238,13 @@ namespace GleeBug
|
|||
Execute = 8
|
||||
*/
|
||||
//Read but our bpx page is not bp on Read
|
||||
if ((exceptionRecord.ExceptionInformation[0]) && (!(bpxPage->second.Type & 0x2)))
|
||||
//We shouldn't care about other stuff such as Write or Execute since these breakpoints are implemented with Access Violation.
|
||||
if ((exceptionRecord.ExceptionInformation[0]==0) && (!(bpxPage->second.Type & 0x2)))
|
||||
{
|
||||
//perhaps the program generated such exception
|
||||
return;
|
||||
}
|
||||
|
||||
//Exception on write but page bp information does not have bp on write
|
||||
if ((exceptionRecord.ExceptionInformation[0] == 1) && (!(bpxPage->second.Type & 0x4)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//caused by data execution prevention but page bp information does not have bp on exec
|
||||
if ((exceptionRecord.ExceptionInformation[0] == 8) && (!(bpxPage->second.Type & 0x8)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//generic breakpoint callback function.
|
||||
cbBreakpoint(info);
|
||||
|
||||
|
|
@ -347,7 +289,125 @@ namespace GleeBug
|
|||
|
||||
void Debugger::exceptionAccessViolation(const EXCEPTION_RECORD & exceptionRecord, bool firstChance)
|
||||
{
|
||||
//TODO: memory breakpoint code
|
||||
//Same shit as before
|
||||
|
||||
char error[128] = "";
|
||||
auto exceptionAddress = ptr(exceptionRecord.ExceptionInformation[1]);
|
||||
|
||||
//check if the exception address is directly in the range of a memory breakpoint
|
||||
auto foundRange = mProcess->memoryBreakpointRanges.find(Range(exceptionAddress, exceptionAddress));
|
||||
if (foundRange == mProcess->memoryBreakpointRanges.end())
|
||||
{
|
||||
//if not in range, check if a memory breakpoint is in the accessed page
|
||||
auto foundPage = mProcess->memoryBreakpointPages.find(exceptionAddress & ~(PAGE_SIZE - 1));
|
||||
if (foundPage != mProcess->memoryBreakpointPages.end())
|
||||
{
|
||||
mContinueStatus = DBG_CONTINUE;
|
||||
//if the page contains a memory breakpoint we have to restore the old protection to correctly resume the debuggee
|
||||
const auto & page = foundPage->second;
|
||||
const auto pBaseAddr = foundPage->first;
|
||||
//TODO: single step and page protection changes
|
||||
//FIXED
|
||||
if (!mProcess->MemProtect(foundPage->first, PAGE_SIZE, foundPage->second.OldProtect))
|
||||
{
|
||||
sprintf_s(error, "MemProtect failed on 0x%p", foundPage->first);
|
||||
cbInternalError(error);
|
||||
}
|
||||
//step + restore new protection to keep bp
|
||||
mThread->StepInternal(std::bind([this, page, pBaseAddr]()
|
||||
{
|
||||
mProcess->MemProtect(pBaseAddr, PAGE_SIZE, page.NewProtect);
|
||||
return;
|
||||
}));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//find the breakpoint associated with the hit breakpoint range
|
||||
auto foundInfo = mProcess->breakpoints.find({ BreakpointType::Memory, foundRange->first });
|
||||
if (foundInfo == mProcess->breakpoints.end())
|
||||
{
|
||||
sprintf_s(error, "inconsistent memory breakpoint at 0x%p", exceptionAddress);
|
||||
cbInternalError(error);
|
||||
return;
|
||||
}
|
||||
|
||||
//check if the memory breakpoint is disabled (meaning we shouldn't intercept the exception)
|
||||
//TODO: think about what happens with multiple breakpoints in one page where only one is disabled
|
||||
//There is really no problem about this because enabled is a property of a range and ranges do not overlap.
|
||||
const auto info = foundInfo->second;
|
||||
if (!info.enabled)
|
||||
return;
|
||||
|
||||
printf("memory breakpoint: 0x%p (size: %d)\n", info.address, info.internal.memory.size);
|
||||
|
||||
//TODO: check if the right type is accessed (ExceptionInformation[0])
|
||||
//FIXED: Marques
|
||||
auto bpxPage = mProcess->memoryBreakpointPages.find(exceptionAddress & ~(PAGE_SIZE - 1));
|
||||
if (bpxPage == mProcess->memoryBreakpointPages.end())
|
||||
{
|
||||
sprintf_s(error, "Process::memoryBreakPointPages data structure is incosistent, should dump page at 0x%p", exceptionAddress & ~(PAGE_SIZE - 1));
|
||||
cbInternalError(error);
|
||||
return;
|
||||
}
|
||||
/*
|
||||
Access = 1,
|
||||
Read = 2,
|
||||
Write = 4,
|
||||
Execute = 8
|
||||
*/
|
||||
//Write but the bpx was not on write
|
||||
//We shouldn't care about other stuff such as read or access because those were implemented with page guards.
|
||||
if ((exceptionRecord.ExceptionInformation[0] == 1) && (!(bpxPage->second.Type & 0x4)))
|
||||
{
|
||||
//perhaps the program generated such exception
|
||||
return;
|
||||
}
|
||||
|
||||
//Exec but bpx was not on exec.
|
||||
if ((exceptionRecord.ExceptionInformation[0] == 8) && (!(bpxPage->second.Type & 0x8)))
|
||||
{
|
||||
//perhaps the program generated such exception
|
||||
return;
|
||||
}
|
||||
//generic breakpoint callback function.
|
||||
cbBreakpoint(info);
|
||||
|
||||
//TODO: execute the user callback (if present)
|
||||
//FIXED: Marques
|
||||
auto bpxCb = mProcess->breakpointCallbacks.find({ BreakpointType::Memory, info.address });
|
||||
if (bpxCb != mProcess->breakpointCallbacks.end())
|
||||
{
|
||||
bpxCb->second(info);
|
||||
}
|
||||
|
||||
|
||||
mContinueStatus = DBG_CONTINUE;
|
||||
//TODO: single step and restore page protection
|
||||
//FIXED:
|
||||
if (!mProcess->MemProtect(bpxPage->first, PAGE_SIZE, bpxPage->second.OldProtect))
|
||||
{
|
||||
sprintf_s(error, "MemProtect failed on 0x%p", bpxPage->first);
|
||||
cbInternalError(error);
|
||||
}
|
||||
//Pass info as well
|
||||
auto pageAddr = bpxPage->first;
|
||||
auto pageProperties = bpxPage->second;
|
||||
mThread->StepInternal(std::bind([this, info, pageAddr, pageProperties]()
|
||||
{
|
||||
//Check if the bpx still exists
|
||||
auto found_range = mProcess->memoryBreakpointRanges.find(Range(info.address, info.address));
|
||||
if (found_range != mProcess->memoryBreakpointRanges.end())
|
||||
{
|
||||
mProcess->MemProtect(pageAddr, PAGE_SIZE, pageProperties.NewProtect);
|
||||
}
|
||||
return;
|
||||
}));
|
||||
|
||||
if (foundInfo->second.singleshoot)
|
||||
{
|
||||
mProcess->DeleteMemoryBreakpoint(exceptionAddress);
|
||||
}
|
||||
}
|
||||
|
||||
void Debugger::exceptionEvent(const EXCEPTION_DEBUG_INFO & exceptionInfo)
|
||||
|
|
|
|||
|
|
@ -199,6 +199,7 @@ namespace GleeBug
|
|||
|
||||
static DWORD RemoveExecuteAccess(DWORD dwAccess)
|
||||
{
|
||||
//These settings can trigger access violation.
|
||||
DWORD dwBase = dwAccess & 0xFF;
|
||||
DWORD dwHigh = dwAccess & 0xFFFFFF00;
|
||||
switch (dwBase)
|
||||
|
|
@ -208,7 +209,7 @@ namespace GleeBug
|
|||
case PAGE_EXECUTE_READ:
|
||||
case PAGE_EXECUTE_READWRITE:
|
||||
case PAGE_EXECUTE_WRITECOPY:
|
||||
return dwHigh | (dwBase >> 4);
|
||||
return dwHigh | (dwBase >> 4); //This removes execute in deed; https://msdn.microsoft.com/en-us/library/windows/desktop/aa366786(v=vs.85).aspx - 0x1337 tricks
|
||||
default:
|
||||
return dwAccess;
|
||||
}
|
||||
|
|
@ -252,12 +253,16 @@ namespace GleeBug
|
|||
else
|
||||
{
|
||||
auto & oldData = found->second;
|
||||
data.Type = oldData.Type | uint32(type);
|
||||
data.OldProtect = oldData.OldProtect;
|
||||
data.Refcount = oldData.Refcount + 1;
|
||||
if (data.Type & uint32(MemoryType::Access) || data.Type & uint32(MemoryType::Read)) //Access/Read always becomes PAGE_GUARD
|
||||
data.NewProtect = data.OldProtect | PAGE_GUARD;
|
||||
else if (data.Type & (uint32(MemoryType::Write) | uint32(MemoryType::Execute))) //Write + Execute becomes either PAGE_GUARD or both write and execute flags removed
|
||||
data.Type = oldData.Type | uint32(type); //combines new protection
|
||||
data.OldProtect = oldData.OldProtect; // old protection remains the same
|
||||
data.Refcount = oldData.Refcount + 1; //increment reference count
|
||||
if (oldData.Type == uint32(type)) // Edge case for when you need to set a mem bpx on a same page with the same type, you just leave newProtect = OldProtect.
|
||||
{
|
||||
data.NewProtect = data.OldProtect;
|
||||
}
|
||||
else if (data.Type & uint32(MemoryType::Access) || data.Type & uint32(MemoryType::Read)) // Access/Read always becomes PAGE_GUARD ; This page cannot access or Read?
|
||||
data.NewProtect = data.OldProtect | PAGE_GUARD; //as before
|
||||
else if (data.Type & (uint32(MemoryType::Write) | uint32(MemoryType::Execute))) // Write + Execute becomes either PAGE_GUARD or both write and execute flags removed
|
||||
data.NewProtect = permanentDep ? RemoveExecuteAccess(RemoveWriteAccess(data.OldProtect)) : data.OldProtect | PAGE_GUARD;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,8 +10,16 @@ class MyDebugger : public Debugger
|
|||
protected:
|
||||
void cbMemoryBreakpoint(const BreakpointInfo & info)
|
||||
{
|
||||
unsigned char dataToExec[4];
|
||||
printf("Reached memory breakpoint! GIP: 0x%p\n",
|
||||
mRegisters->Gip());
|
||||
|
||||
mProcess->MemReadUnsafe(mRegisters->Gip(), dataToExec, 4);
|
||||
printf("\n You Bpxed Correctly: ");
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
printf("%02X ", dataToExec[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void cbEntryBreakpoint(const BreakpointInfo & info)
|
||||
|
|
@ -24,7 +32,7 @@ protected:
|
|||
auto addr = mRegisters->Esi();
|
||||
#endif //_WIN64
|
||||
printf("Addr: 0x%p\n", addr);
|
||||
if (mProcess->SetMemoryBreakpoint(addr, 0x1000, this, &MyDebugger::cbMemoryBreakpoint, MemoryType::Access))
|
||||
if (mProcess->SetMemoryBreakpoint(addr, 0x1000, this, &MyDebugger::cbMemoryBreakpoint, MemoryType::Execute))
|
||||
puts("Memory breakpoint set!");
|
||||
else
|
||||
puts("Failed to set memory breakpoint...");
|
||||
|
|
|
|||
Loading…
Reference in New Issue