mirror of https://github.com/x64dbg/GleeBug
Merge 98ff237193 into 9cc0b8c756
This commit is contained in:
commit
57b9c6b543
|
|
@ -0,0 +1,11 @@
|
||||||
|
; Top-most EditorConfig file
|
||||||
|
root = true
|
||||||
|
|
||||||
|
; Windows-style newlines
|
||||||
|
[*]
|
||||||
|
end_of_line = CRLF
|
||||||
|
|
||||||
|
; Tab indentation
|
||||||
|
[*.{cpp,h}]
|
||||||
|
indent_style = space
|
||||||
|
tab_width = 4
|
||||||
|
|
@ -7,3 +7,4 @@ Release/
|
||||||
*.vcxproj.user
|
*.vcxproj.user
|
||||||
docs/
|
docs/
|
||||||
.vs/
|
.vs/
|
||||||
|
*.VC.db
|
||||||
|
|
@ -44,9 +44,10 @@ namespace GleeBug
|
||||||
|
|
||||||
enum class MemoryType
|
enum class MemoryType
|
||||||
{
|
{
|
||||||
Acess,
|
Access = 1,
|
||||||
Write,
|
Read = 2,
|
||||||
Execute
|
Write = 4,
|
||||||
|
Execute = 8
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -68,6 +69,7 @@ namespace GleeBug
|
||||||
HardwareSlot slot;
|
HardwareSlot slot;
|
||||||
HardwareType type;
|
HardwareType type;
|
||||||
HardwareSize size;
|
HardwareSize size;
|
||||||
|
bool enabled;
|
||||||
} hardware;
|
} hardware;
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
|
|
@ -83,11 +85,21 @@ namespace GleeBug
|
||||||
struct BreakpointInfo
|
struct BreakpointInfo
|
||||||
{
|
{
|
||||||
ptr address;
|
ptr address;
|
||||||
bool enabled;
|
|
||||||
bool singleshoot;
|
bool singleshoot;
|
||||||
BreakpointType type;
|
BreakpointType type;
|
||||||
BreakpointInternalInfo internal;
|
BreakpointInternalInfo internal;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
\brief Structure for memory breakpoint management.
|
||||||
|
*/
|
||||||
|
struct MemoryBreakpointData
|
||||||
|
{
|
||||||
|
uint32 Refcount;
|
||||||
|
uint32 Type;
|
||||||
|
DWORD OldProtect;
|
||||||
|
DWORD NewProtect;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //DEBUGGER_BREAKPOINT_H
|
#endif //DEBUGGER_BREAKPOINT_H
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
//defines
|
//defines
|
||||||
#define GLEEBUG_HWBP_COUNT 4
|
#define GLEEBUG_HWBP_COUNT 4
|
||||||
|
#define GLEEBUG_PAGE_SIZE 0x1000
|
||||||
|
|
||||||
namespace GleeBug
|
namespace GleeBug
|
||||||
{
|
{
|
||||||
|
|
@ -15,9 +16,11 @@ namespace GleeBug
|
||||||
class Thread;
|
class Thread;
|
||||||
enum class BreakpointType;
|
enum class BreakpointType;
|
||||||
struct BreakpointInfo;
|
struct BreakpointInfo;
|
||||||
|
struct MemoryBreakpointData;
|
||||||
|
|
||||||
//constants
|
//constants
|
||||||
const int HWBP_COUNT = GLEEBUG_HWBP_COUNT;
|
const int HWBP_COUNT = GLEEBUG_HWBP_COUNT;
|
||||||
|
const int PAGE_SIZE = GLEEBUG_PAGE_SIZE;
|
||||||
|
|
||||||
//key typedefs
|
//key typedefs
|
||||||
typedef std::pair<BreakpointType, ptr> BreakpointKey;
|
typedef std::pair<BreakpointType, ptr> BreakpointKey;
|
||||||
|
|
@ -33,6 +36,8 @@ namespace GleeBug
|
||||||
typedef std::map<BreakpointKey, BreakpointInfo> BreakpointMap;
|
typedef std::map<BreakpointKey, BreakpointInfo> BreakpointMap;
|
||||||
typedef std::map<BreakpointKey, BreakpointCallback> BreakpointCallbackMap;
|
typedef std::map<BreakpointKey, BreakpointCallback> BreakpointCallbackMap;
|
||||||
typedef std::unordered_map<ptr, BreakpointMap::iterator> SoftwareBreakpointMap;
|
typedef std::unordered_map<ptr, BreakpointMap::iterator> SoftwareBreakpointMap;
|
||||||
|
typedef std::set<Range, RangeCompare> MemoryBreakpointSet;
|
||||||
|
typedef std::unordered_map<ptr, MemoryBreakpointData> MemoryBreakpointMap;
|
||||||
|
|
||||||
//vector typedefs
|
//vector typedefs
|
||||||
typedef std::vector<StepCallback> StepCallbackVector;
|
typedef std::vector<StepCallback> StepCallbackVector;
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,25 @@ namespace GleeBug
|
||||||
mProcess->systemBreakpoint = true;
|
mProcess->systemBreakpoint = true;
|
||||||
mContinueStatus = DBG_CONTINUE;
|
mContinueStatus = DBG_CONTINUE;
|
||||||
|
|
||||||
|
//get process DEP policy
|
||||||
|
#ifndef _WIN64
|
||||||
|
typedef BOOL(WINAPI * GETPROCESSDEPPOLICY)(
|
||||||
|
_In_ HANDLE /*hProcess*/,
|
||||||
|
_Out_ LPDWORD /*lpFlags*/,
|
||||||
|
_Out_ PBOOL /*lpPermanent*/
|
||||||
|
);
|
||||||
|
static auto GPDP = GETPROCESSDEPPOLICY(GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "GetProcessDEPPolicy"));
|
||||||
|
if (GPDP)
|
||||||
|
{
|
||||||
|
DWORD lpFlags;
|
||||||
|
BOOL bPermanent;
|
||||||
|
if (GPDP(mProcess->hProcess, &lpFlags, &bPermanent))
|
||||||
|
mProcess->permanentDep = lpFlags && bPermanent;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
mProcess->permanentDep = true;
|
||||||
|
#endif //_WIN64
|
||||||
|
|
||||||
//call the attach callback if appropriate
|
//call the attach callback if appropriate
|
||||||
if(mAttachedToProcess && mProcess->dwProcessId == mMainProcess.dwProcessId)
|
if(mAttachedToProcess && mProcess->dwProcessId == mMainProcess.dwProcessId)
|
||||||
cbAttachBreakpoint();
|
cbAttachBreakpoint();
|
||||||
|
|
@ -148,6 +167,353 @@ namespace GleeBug
|
||||||
mProcess->DeleteGenericBreakpoint(info);
|
mProcess->DeleteGenericBreakpoint(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Debugger::exceptionGuardPage(const EXCEPTION_RECORD & exceptionRecord, bool firstChance)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
ASSUME:
|
||||||
|
exceptionAddress may or may not have been generated by your breakpoints.
|
||||||
|
*/
|
||||||
|
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())
|
||||||
|
{
|
||||||
|
//(this means that by our fault the program generated an exception, we should clean it)
|
||||||
|
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;
|
||||||
|
|
||||||
|
//We restore the protection
|
||||||
|
if (!mProcess->MemProtect(foundPage->first, PAGE_SIZE, foundPage->second.OldProtect))
|
||||||
|
{
|
||||||
|
sprintf_s(error, "MemProtect failed on 0x%p", foundPage->first);
|
||||||
|
cbInternalError(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
//However the following situations may occur:
|
||||||
|
// The instruction we singlestep to is a software breakpoint, which may execute a callback, that can :
|
||||||
|
// -actually delete a memory breakpoint that takes this page into account
|
||||||
|
// -add more memory breakpoints
|
||||||
|
//The solution: We just try to see if the page is mapped into memoryBreakpointPages. If the page is in deed being used by any memory breakpoint,
|
||||||
|
// then we ought to restore the protection.
|
||||||
|
mThread->StepInternal(std::bind([this, pBaseAddr]()
|
||||||
|
{
|
||||||
|
//seek out the page address
|
||||||
|
auto found_page = mProcess->memoryBreakpointPages.find(pBaseAddr);
|
||||||
|
if (found_page == mProcess->memoryBreakpointPages.end())
|
||||||
|
{
|
||||||
|
//no page being used by bpx? Then just return
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mProcess->MemProtect(pBaseAddr, PAGE_SIZE, found_page->second.NewProtect);
|
||||||
|
return;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
ASSUME:
|
||||||
|
exceptionAddress is indeed inside a breakpoint range you have defined.
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
|
||||||
|
//TODO: check if the right type is accessed (ExceptionInformation[0])
|
||||||
|
//FIXED:
|
||||||
|
auto bpxPage = mProcess->memoryBreakpointPages.find(exceptionAddress & ~(PAGE_SIZE - 1));
|
||||||
|
auto pageAddr = bpxPage->first;
|
||||||
|
auto pageProperties = bpxPage->second;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
//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
|
||||||
|
//This callback.
|
||||||
|
//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.
|
||||||
|
if ((exceptionRecord.ExceptionInformation[0] != 0))
|
||||||
|
{
|
||||||
|
//The bpx is solely on read.
|
||||||
|
if (((pageProperties.Type & 0x2) != 0) && ((pageProperties.Type & 0x1) == 0))
|
||||||
|
{
|
||||||
|
mContinueStatus = DBG_CONTINUE;
|
||||||
|
//We restore the protection
|
||||||
|
if (!mProcess->MemProtect(pageAddr, PAGE_SIZE, pageProperties.OldProtect))
|
||||||
|
{
|
||||||
|
sprintf_s(error, "MemProtect failed on 0x%p", pageAddr);
|
||||||
|
cbInternalError(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
mThread->StepInternal(std::bind([this, pageAddr]()
|
||||||
|
{
|
||||||
|
//seek out the page address
|
||||||
|
auto found_page = mProcess->memoryBreakpointPages.find(pageAddr);
|
||||||
|
if (found_page == mProcess->memoryBreakpointPages.end())
|
||||||
|
{
|
||||||
|
//no page being used by bpx? Then just return
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mProcess->MemProtect(pageAddr, PAGE_SIZE, found_page->second.NewProtect);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
ASSUME:
|
||||||
|
The breakpoint at exceptionAddress was indeed generated by me.
|
||||||
|
Its safe to call the callbacks.
|
||||||
|
*/
|
||||||
|
//generic breakpoint callback function.
|
||||||
|
cbBreakpoint(info);
|
||||||
|
|
||||||
|
//TODO: execute the user callback (if present)
|
||||||
|
//FIXED:
|
||||||
|
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(pageAddr, PAGE_SIZE, pageProperties.OldProtect))
|
||||||
|
{
|
||||||
|
sprintf_s(error, "MemProtect failed on 0x%p", pageAddr);
|
||||||
|
cbInternalError(error);
|
||||||
|
}
|
||||||
|
//Pass info as well
|
||||||
|
mThread->StepInternal(std::bind([this, pageAddr]()
|
||||||
|
{
|
||||||
|
//With page check this should work better: So when we reach this part of the code we are sure that:
|
||||||
|
//-The exception Address In deed corresponded to an existing (now possibly deleted) memory breakpoint range
|
||||||
|
//-memoryBreakpointPages was in deed consistent with this memory address that generated the exception (The data structure wasn't corrupted somehow)
|
||||||
|
//So our new technique basically checks if the page address is still inside memoryBreakpointRanges structure. If this is true, we simply apply the NewProtect.
|
||||||
|
//Wide variety of possible scenarios:
|
||||||
|
//-Bpx on this page and bpx is not singleshot: In the case of PAGE_GUARD page protection (handled by this exception handler), if the page permission map persists, we simply
|
||||||
|
// enforce the newProtect because this page belongs to a breakpoint somewhere.
|
||||||
|
//-Bpx is singleshot: Then it was deleted by the end of this call. If the refcount is zero, then we dont find the page on the Memory map, so assume no more memory breakpoints happen there.
|
||||||
|
// therefore, we do not enforce new protection.
|
||||||
|
//-Bpx was deleted on the handler: Again the page may or may not be mapped on memoryBreakpointPages. If the bp was deleted, and there are no more breakpoints in this page - The page does not exist on the map and therefore we do not restore old page protection.
|
||||||
|
//-Bpx was deleted on the handler AND a new breakpoint was added: if the bpx was deleted, and a new one was added on this page then, surely the page is mapped under memoryBreakpointPages.
|
||||||
|
//Check if the memory page is mapped
|
||||||
|
|
||||||
|
auto found_page = mProcess->memoryBreakpointPages.find(pageAddr);
|
||||||
|
if (found_page != mProcess->memoryBreakpointPages.end())
|
||||||
|
{
|
||||||
|
mProcess->MemProtect(pageAddr, PAGE_SIZE, found_page->second.NewProtect);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (info.singleshoot)
|
||||||
|
{
|
||||||
|
mProcess->DeleteMemoryBreakpoint(exceptionAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Debugger::exceptionAccessViolation(const EXCEPTION_RECORD & exceptionRecord, bool firstChance)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
ASSUME:
|
||||||
|
exceptionAddress may or may not have been generated by your breakpoints.
|
||||||
|
*/
|
||||||
|
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())
|
||||||
|
{
|
||||||
|
//(this means that by our fault the program generated an exception, we should clean it)
|
||||||
|
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;
|
||||||
|
|
||||||
|
//We restore the protection
|
||||||
|
if (!mProcess->MemProtect(foundPage->first, PAGE_SIZE, foundPage->second.OldProtect))
|
||||||
|
{
|
||||||
|
sprintf_s(error, "MemProtect failed on 0x%p", foundPage->first);
|
||||||
|
cbInternalError(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
//However the following situations may occur:
|
||||||
|
// The instruction we singlestep to is a software breakpoint, which may execute a callback, that can :
|
||||||
|
// -actually delete a memory breakpoint that takes this page into account
|
||||||
|
// -add more memory breakpoints
|
||||||
|
//The solution: We just try to see if the page is mapped into memoryBreakpointPages. If the page is in deed being used by any memory breakpoint,
|
||||||
|
// then we ought to restore the protection.
|
||||||
|
mThread->StepInternal(std::bind([this, pBaseAddr]()
|
||||||
|
{
|
||||||
|
//seek out the page address
|
||||||
|
auto found_page = mProcess->memoryBreakpointPages.find(pBaseAddr);
|
||||||
|
if (found_page == mProcess->memoryBreakpointPages.end())
|
||||||
|
{
|
||||||
|
//no page being used by bpx? Then just return
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mProcess->MemProtect(pBaseAddr, PAGE_SIZE, found_page->second.NewProtect);
|
||||||
|
return;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
ASSUME:
|
||||||
|
exceptionAddress is indeed inside a breakpoint range you have defined.
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
|
||||||
|
//TODO: check if the right type is accessed (ExceptionInformation[0])
|
||||||
|
//FIXED:
|
||||||
|
auto bpxPage = mProcess->memoryBreakpointPages.find(exceptionAddress & ~(PAGE_SIZE - 1));
|
||||||
|
auto pageAddr = bpxPage->first;
|
||||||
|
auto pageProperties = bpxPage->second;
|
||||||
|
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
//ExceptionInformation[0] should be considered as 1 or 8, because these are for exceptions generated on write or execute.
|
||||||
|
//Execute is only implemented with page guard if no Data-Execution-Prevention is implemented by the Kernel.
|
||||||
|
if ((exceptionRecord.ExceptionInformation[0] == 1) && (!(pageProperties.Type & 4)))
|
||||||
|
{
|
||||||
|
//The exception was on Write but there was no page breakpoint in Write? Then the program changed the page permissions, or naturally overwritten protected data. We do not interfere.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((exceptionRecord.ExceptionInformation[0] == 8) && (!(pageProperties.Type & 8)))
|
||||||
|
{
|
||||||
|
//The exception was on Execution but there was no page breakpoint in Execute? Then the program changed the page permissions, or naturally executed protected code. We do not interfere.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
ASSUME:
|
||||||
|
The breakpoint at exceptionAddress was indeed generated by me.
|
||||||
|
*/
|
||||||
|
//generic breakpoint callback function.
|
||||||
|
cbBreakpoint(info);
|
||||||
|
|
||||||
|
//TODO: execute the user callback (if present)
|
||||||
|
//FIXED:
|
||||||
|
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(pageAddr, PAGE_SIZE, pageProperties.OldProtect))
|
||||||
|
{
|
||||||
|
sprintf_s(error, "MemProtect failed on 0x%p", pageAddr);
|
||||||
|
cbInternalError(error);
|
||||||
|
}
|
||||||
|
//Pass info as well
|
||||||
|
mThread->StepInternal(std::bind([this, pageAddr]()
|
||||||
|
{
|
||||||
|
//With page check this should work better: So when we reach this part of the code we are sure that:
|
||||||
|
//-The exception Address In deed corresponded to an existing (now possibly deleted) memory breakpoint range
|
||||||
|
//-memoryBreakpointPages was in deed consistent with this memory address that generated the exception (The data structure wasn't corrupted somehow)
|
||||||
|
//So our new technique basically checks if the page address is still inside memoryBreakpointRanges structure. If this is true, we simply apply the NewProtect.
|
||||||
|
//Wide variety of possible scenarios:
|
||||||
|
//-Bpx on this page and bpx is not singleshot: In the case of PAGE_GUARD page protection (handled by this exception handler), if the page permission map persists, we simply
|
||||||
|
// enforce the newProtect because this page belongs to a breakpoint somewhere.
|
||||||
|
//-Bpx is singleshot: Then it was deleted by the end of this call. If the refcount is zero, then we dont find the page on the Memory map, so assume no more memory breakpoints happen there.
|
||||||
|
// therefore, we do not enforce new protection.
|
||||||
|
//-Bpx was deleted on the handler: Again the page may or may not be mapped on memoryBreakpointPages. If the bp was deleted, and there are no more breakpoints in this page - The page does not exist on the map and therefore we do not restore old page protection.
|
||||||
|
//-Bpx was deleted on the handler AND a new breakpoint was added: if the bpx was deleted, and a new one was added on this page then, surely the page is mapped under memoryBreakpointPages.
|
||||||
|
//Check if the memory page is mapped
|
||||||
|
|
||||||
|
auto found_page = mProcess->memoryBreakpointPages.find(pageAddr);
|
||||||
|
if (found_page != mProcess->memoryBreakpointPages.end())
|
||||||
|
{
|
||||||
|
mProcess->MemProtect(pageAddr, PAGE_SIZE, found_page->second.NewProtect);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (info.singleshoot)
|
||||||
|
{
|
||||||
|
mProcess->DeleteMemoryBreakpoint(exceptionAddress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Debugger::exceptionEvent(const EXCEPTION_DEBUG_INFO & exceptionInfo)
|
void Debugger::exceptionEvent(const EXCEPTION_DEBUG_INFO & exceptionInfo)
|
||||||
{
|
{
|
||||||
//let the debuggee handle exceptions per default
|
//let the debuggee handle exceptions per default
|
||||||
|
|
@ -159,7 +525,7 @@ namespace GleeBug
|
||||||
//call the debug event callback
|
//call the debug event callback
|
||||||
cbExceptionEvent(exceptionInfo);
|
cbExceptionEvent(exceptionInfo);
|
||||||
|
|
||||||
//dispatch the exception
|
//dispatch the exception (https://msdn.microsoft.com/en-us/library/windows/desktop/aa363082(v=vs.85).aspx)
|
||||||
switch (exceptionInfo.ExceptionRecord.ExceptionCode)
|
switch (exceptionInfo.ExceptionRecord.ExceptionCode)
|
||||||
{
|
{
|
||||||
case STATUS_BREAKPOINT:
|
case STATUS_BREAKPOINT:
|
||||||
|
|
@ -168,6 +534,12 @@ namespace GleeBug
|
||||||
case STATUS_SINGLE_STEP:
|
case STATUS_SINGLE_STEP:
|
||||||
exceptionSingleStep(exceptionRecord, firstChance);
|
exceptionSingleStep(exceptionRecord, firstChance);
|
||||||
break;
|
break;
|
||||||
|
case STATUS_GUARD_PAGE_VIOLATION:
|
||||||
|
exceptionGuardPage(exceptionRecord, firstChance);
|
||||||
|
break;
|
||||||
|
case STATUS_ACCESS_VIOLATION:
|
||||||
|
exceptionAccessViolation(exceptionRecord, firstChance);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
//call the unhandled exception callback
|
//call the unhandled exception callback
|
||||||
|
|
|
||||||
|
|
@ -44,8 +44,6 @@ namespace GleeBug
|
||||||
{
|
{
|
||||||
mThread = mProcess->thread = &threadFound->second;
|
mThread = mProcess->thread = &threadFound->second;
|
||||||
mRegisters = &mThread->registers;
|
mRegisters = &mThread->registers;
|
||||||
if (!mThread->RegReadContext())
|
|
||||||
cbInternalError("ThreadInfo::RegReadContext() failed!");
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -64,6 +62,10 @@ namespace GleeBug
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//read register contexts
|
||||||
|
if(mProcess && !mProcess->RegReadContext())
|
||||||
|
cbInternalError("Process::RegReadContext() failed!");
|
||||||
|
|
||||||
//call the pre debug event callback
|
//call the pre debug event callback
|
||||||
cbPreDebugEvent(mDebugEvent);
|
cbPreDebugEvent(mDebugEvent);
|
||||||
|
|
||||||
|
|
@ -120,12 +122,9 @@ namespace GleeBug
|
||||||
mThread->registers.TrapFlag = false;
|
mThread->registers.TrapFlag = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//write the register context
|
//write register contexts
|
||||||
if (mThread)
|
if(mProcess && !mProcess->RegWriteContext())
|
||||||
{
|
cbInternalError("Process::RegWriteContext() failed!");
|
||||||
if (!mThread->RegWriteContext())
|
|
||||||
cbInternalError("ThreadInfo::RegWriteContext() failed!");
|
|
||||||
}
|
|
||||||
|
|
||||||
//continue the debug event
|
//continue the debug event
|
||||||
if (!ContinueDebugEvent(mDebugEvent.dwProcessId, mDebugEvent.dwThreadId, mContinueStatus))
|
if (!ContinueDebugEvent(mDebugEvent.dwProcessId, mDebugEvent.dwThreadId, mContinueStatus))
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ namespace GleeBug
|
||||||
//setup the breakpoint information struct
|
//setup the breakpoint information struct
|
||||||
BreakpointInfo info = {};
|
BreakpointInfo info = {};
|
||||||
info.address = address;
|
info.address = address;
|
||||||
info.enabled = true;
|
|
||||||
info.singleshoot = singleshoot;
|
info.singleshoot = singleshoot;
|
||||||
info.type = BreakpointType::Software;
|
info.type = BreakpointType::Software;
|
||||||
|
|
||||||
|
|
@ -64,13 +63,10 @@ namespace GleeBug
|
||||||
return false;
|
return false;
|
||||||
const auto & info = found->second;
|
const auto & info = found->second;
|
||||||
|
|
||||||
//restore the breakpoint bytes if the breakpoint is enabled
|
//restore the breakpoint bytes
|
||||||
if (info.enabled)
|
|
||||||
{
|
|
||||||
if (!MemWriteUnsafe(address, info.internal.software.oldbytes, info.internal.software.size))
|
if (!MemWriteUnsafe(address, info.internal.software.oldbytes, info.internal.software.size))
|
||||||
return false;
|
return false;
|
||||||
FlushInstructionCache(hProcess, nullptr, 0);
|
FlushInstructionCache(hProcess, nullptr, 0);
|
||||||
}
|
|
||||||
|
|
||||||
//remove the breakpoint from the maps
|
//remove the breakpoint from the maps
|
||||||
softwareBreakpointReferences.erase(info.address);
|
softwareBreakpointReferences.erase(info.address);
|
||||||
|
|
@ -84,7 +80,7 @@ namespace GleeBug
|
||||||
//find a free hardware breakpoint slot
|
//find a free hardware breakpoint slot
|
||||||
for (int i = 0; i < HWBP_COUNT; i++)
|
for (int i = 0; i < HWBP_COUNT; i++)
|
||||||
{
|
{
|
||||||
if (!hardwareBreakpoints[i].enabled)
|
if (!hardwareBreakpoints[i].internal.hardware.enabled)
|
||||||
{
|
{
|
||||||
slot = HardwareSlot(i);
|
slot = HardwareSlot(i);
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -122,7 +118,6 @@ namespace GleeBug
|
||||||
//setup the breakpoint information struct
|
//setup the breakpoint information struct
|
||||||
BreakpointInfo info = {};
|
BreakpointInfo info = {};
|
||||||
info.address = address;
|
info.address = address;
|
||||||
info.enabled = true;
|
|
||||||
info.singleshoot = singleshoot;
|
info.singleshoot = singleshoot;
|
||||||
info.type = BreakpointType::Hardware;
|
info.type = BreakpointType::Hardware;
|
||||||
info.internal.hardware.slot = slot;
|
info.internal.hardware.slot = slot;
|
||||||
|
|
@ -160,7 +155,7 @@ namespace GleeBug
|
||||||
const auto & info = found->second;
|
const auto & info = found->second;
|
||||||
|
|
||||||
//delete the hardware breakpoint from the internal buffer
|
//delete the hardware breakpoint from the internal buffer
|
||||||
hardwareBreakpoints[int(info.internal.hardware.slot)].enabled = false;
|
hardwareBreakpoints[int(info.internal.hardware.slot)].internal.hardware.enabled = false;
|
||||||
|
|
||||||
//delete the hardware breakpoint from the registers
|
//delete the hardware breakpoint from the registers
|
||||||
bool success = true;
|
bool success = true;
|
||||||
|
|
@ -176,6 +171,236 @@ namespace GleeBug
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define PAGE_SHIFT (12)
|
||||||
|
#define PAGE_ALIGN(Va) ((ULONG_PTR)((ULONG_PTR)(Va) & ~(PAGE_SIZE - 1)))
|
||||||
|
#define BYTES_TO_PAGES(Size) (((Size) >> PAGE_SHIFT) + (((Size) & (PAGE_SIZE - 1)) != 0))
|
||||||
|
#define ROUND_TO_PAGES(Size) (((ULONG_PTR)(Size) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))
|
||||||
|
|
||||||
|
/*
|
||||||
|
#define PAGE_NOACCESS 0x01
|
||||||
|
#define PAGE_READONLY 0x02
|
||||||
|
#define PAGE_READWRITE 0x04
|
||||||
|
#define PAGE_WRITECOPY 0x08 <- not supported
|
||||||
|
|
||||||
|
#define PAGE_EXECUTE 0x10
|
||||||
|
#define PAGE_EXECUTE_READ 0x20
|
||||||
|
#define PAGE_EXECUTE_READWRITE 0x40
|
||||||
|
#define PAGE_EXECUTE_WRITECOPY 0x80 <- not supported
|
||||||
|
|
||||||
|
#define PAGE_GUARD 0x100 <- not supported with PAGE_NOACCESS
|
||||||
|
#define PAGE_NOCACHE 0x200 <- not supported with PAGE_GUARD or PAGE_WRITECOMBINE
|
||||||
|
#define PAGE_WRITECOMBINE 0x400 <- not supported with PAGE_GUARD or PAGE_NOCACHE
|
||||||
|
*/
|
||||||
|
|
||||||
|
static DWORD RemoveExecuteAccess(DWORD dwAccess)
|
||||||
|
{
|
||||||
|
//These settings can trigger access violation.
|
||||||
|
DWORD dwBase = dwAccess & 0xFF;
|
||||||
|
DWORD dwHigh = dwAccess & 0xFFFFFF00;
|
||||||
|
switch (dwBase)
|
||||||
|
{
|
||||||
|
case PAGE_EXECUTE:
|
||||||
|
return dwHigh | PAGE_READONLY;
|
||||||
|
case PAGE_EXECUTE_READ:
|
||||||
|
case PAGE_EXECUTE_READWRITE:
|
||||||
|
case PAGE_EXECUTE_WRITECOPY:
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static DWORD RemoveWriteAccess(DWORD dwAccess)
|
||||||
|
{
|
||||||
|
DWORD dwBase = dwAccess & 0xFF;
|
||||||
|
switch (dwBase)
|
||||||
|
{
|
||||||
|
case PAGE_READWRITE:
|
||||||
|
case PAGE_EXECUTE_READWRITE:
|
||||||
|
return (dwAccess & 0xFFFFFF00) | (dwBase >> 1);
|
||||||
|
default:
|
||||||
|
return dwAccess;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Process::SetNewPageProtection(ptr page, MemoryBreakpointData & data, MemoryType type)
|
||||||
|
{
|
||||||
|
//TODO: handle PAGE_NOACCESS and such correctly (since it cannot be combined with PAGE_GUARD)
|
||||||
|
|
||||||
|
auto found = memoryBreakpointPages.find(page);
|
||||||
|
if (found == memoryBreakpointPages.end())
|
||||||
|
{
|
||||||
|
data.Refcount = 1;
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case MemoryType::Access:
|
||||||
|
case MemoryType::Read:
|
||||||
|
data.NewProtect = data.OldProtect | PAGE_GUARD;
|
||||||
|
break;
|
||||||
|
case MemoryType::Write:
|
||||||
|
data.NewProtect = RemoveWriteAccess(data.OldProtect);
|
||||||
|
break;
|
||||||
|
case MemoryType::Execute:
|
||||||
|
data.NewProtect = permanentDep ? RemoveExecuteAccess(data.OldProtect) : data.OldProtect | PAGE_GUARD;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto & oldData = found->second;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
return MemProtect(page, PAGE_SIZE, data.NewProtect);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Process::SetMemoryBreakpoint(ptr address, ptr size, MemoryType type, bool singleshoot)
|
||||||
|
{
|
||||||
|
//TODO: error reporting
|
||||||
|
|
||||||
|
//basic checks
|
||||||
|
if (!MemIsValidPtr(address) || !size)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
//check if the range is unused for any previous memory breakpoints
|
||||||
|
auto range = Range(address, address + size - 1);
|
||||||
|
if (memoryBreakpointRanges.find(range) != memoryBreakpointRanges.end())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
//change page protections
|
||||||
|
bool success = true;
|
||||||
|
struct TempMemoryBreakpointData
|
||||||
|
{
|
||||||
|
ptr addr;
|
||||||
|
DWORD OldProtect;
|
||||||
|
MemoryBreakpointData data;
|
||||||
|
};
|
||||||
|
std::vector<TempMemoryBreakpointData> breakpointData;
|
||||||
|
{
|
||||||
|
breakpointData.reserve(size / PAGE_SIZE);
|
||||||
|
TempMemoryBreakpointData tempData;
|
||||||
|
MemoryBreakpointData data;
|
||||||
|
data.Type = uint32(type);
|
||||||
|
auto alignedAddress = PAGE_ALIGN(address);
|
||||||
|
for (auto page = alignedAddress; page < alignedAddress + BYTES_TO_PAGES(size); page += PAGE_SIZE)
|
||||||
|
{
|
||||||
|
MEMORY_BASIC_INFORMATION mbi;
|
||||||
|
if (!VirtualQueryEx(hProcess, LPCVOID(page), &mbi, sizeof(mbi)))
|
||||||
|
{
|
||||||
|
success = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
data.OldProtect = mbi.Protect;
|
||||||
|
if (!SetNewPageProtection(page, data, type))
|
||||||
|
{
|
||||||
|
success = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
tempData.addr = page;
|
||||||
|
tempData.OldProtect = mbi.Protect;
|
||||||
|
tempData.data = data;
|
||||||
|
breakpointData.push_back(tempData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//if changing the page protections failed, attempt to revert all protection changes
|
||||||
|
if (!success)
|
||||||
|
{
|
||||||
|
for (const auto & page : breakpointData)
|
||||||
|
MemProtect(page.addr, PAGE_SIZE, page.OldProtect);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//set the page data
|
||||||
|
for (const auto & page : breakpointData)
|
||||||
|
memoryBreakpointPages[page.addr] = page.data;
|
||||||
|
|
||||||
|
//setup the breakpoint information struct
|
||||||
|
BreakpointInfo info = {};
|
||||||
|
info.address = address;
|
||||||
|
info.singleshoot = singleshoot;
|
||||||
|
info.type = BreakpointType::Memory;
|
||||||
|
info.internal.memory.type = type;
|
||||||
|
info.internal.memory.size = size;
|
||||||
|
|
||||||
|
//insert in the breakpoint map
|
||||||
|
breakpoints.insert({ { info.type, info.address }, info });
|
||||||
|
memoryBreakpointRanges.insert(range);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Process::SetMemoryBreakpoint(ptr address, ptr size, const BreakpointCallback & cbBreakpoint, MemoryType type, bool singleshoot)
|
||||||
|
{
|
||||||
|
//check if a callback on this address was already found
|
||||||
|
if (breakpointCallbacks.find({ BreakpointType::Memory, address }) != breakpointCallbacks.end())
|
||||||
|
return false;
|
||||||
|
//set the memory breakpoint
|
||||||
|
if (!SetMemoryBreakpoint(address, size, type, singleshoot))
|
||||||
|
return false;
|
||||||
|
//insert the callback
|
||||||
|
breakpointCallbacks.insert({ { BreakpointType::Memory, address }, cbBreakpoint });
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Process::DeleteMemoryBreakpoint(ptr address)
|
||||||
|
{
|
||||||
|
//find the memory breakpoint range
|
||||||
|
auto range = memoryBreakpointRanges.find(Range(address, address));
|
||||||
|
if(range == memoryBreakpointRanges.end())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
//find the memory breakpoint
|
||||||
|
auto found = breakpoints.find({ BreakpointType::Memory, range->first });
|
||||||
|
if (found == breakpoints.end())
|
||||||
|
return false;
|
||||||
|
const auto & info = found->second;
|
||||||
|
|
||||||
|
//delete the memory breakpoint from the pages
|
||||||
|
bool success = true;
|
||||||
|
auto alignedAddress = PAGE_ALIGN(info.address);
|
||||||
|
for (auto page = alignedAddress; page < alignedAddress + BYTES_TO_PAGES(info.internal.memory.size); page += PAGE_SIZE)
|
||||||
|
{
|
||||||
|
auto foundData = memoryBreakpointPages.find(page);
|
||||||
|
if (foundData == memoryBreakpointPages.end())
|
||||||
|
continue; //TODO: error reporting
|
||||||
|
auto & data = foundData->second;
|
||||||
|
DWORD Protect;
|
||||||
|
data.Refcount--;
|
||||||
|
if (data.Refcount)
|
||||||
|
{
|
||||||
|
//TODO: properly determine the new protection flag
|
||||||
|
//Are there any other protections left?
|
||||||
|
//If so add the guard
|
||||||
|
if (data.Type & ~uint32(info.internal.memory.type))
|
||||||
|
data.NewProtect = data.OldProtect | PAGE_GUARD;
|
||||||
|
Protect = data.NewProtect;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Protect = data.OldProtect;
|
||||||
|
if (!MemProtect(page, PAGE_SIZE, Protect))
|
||||||
|
success = false;
|
||||||
|
if (!data.Refcount)
|
||||||
|
memoryBreakpointPages.erase(foundData);
|
||||||
|
}
|
||||||
|
|
||||||
|
//delete the breakpoint from the maps
|
||||||
|
breakpoints.erase(found);
|
||||||
|
breakpointCallbacks.erase({ BreakpointType::Hardware, address });
|
||||||
|
memoryBreakpointRanges.erase(Range(address, address));
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
bool Process::DeleteGenericBreakpoint(const BreakpointInfo & info)
|
bool Process::DeleteGenericBreakpoint(const BreakpointInfo & info)
|
||||||
{
|
{
|
||||||
switch (info.type)
|
switch (info.type)
|
||||||
|
|
@ -185,7 +410,7 @@ namespace GleeBug
|
||||||
case BreakpointType::Hardware:
|
case BreakpointType::Hardware:
|
||||||
return DeleteHardwareBreakpoint(info.address);
|
return DeleteHardwareBreakpoint(info.address);
|
||||||
case BreakpointType::Memory:
|
case BreakpointType::Memory:
|
||||||
return false; //TODO implement this
|
return DeleteMemoryBreakpoint(info.address);
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,17 @@ namespace GleeBug
|
||||||
return MemReadUnsafe(address, &byte, sizeof(byte));
|
return MemReadUnsafe(address, &byte, sizeof(byte));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Process::MemProtect(ptr address, ptr size, DWORD newProtect, DWORD* oldProtect)
|
||||||
|
{
|
||||||
|
DWORD dwOldProtect;
|
||||||
|
auto vps = VirtualProtectEx(hProcess, LPVOID(address), size, newProtect, &dwOldProtect);
|
||||||
|
if (!vps)
|
||||||
|
return false;
|
||||||
|
if (oldProtect)
|
||||||
|
*oldProtect = dwOldProtect;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
ptr Process::MemFindPattern(ptr data, size_t datasize, const Pattern::WildcardPattern & pattern, bool safe) const
|
ptr Process::MemFindPattern(ptr data, size_t datasize, const Pattern::WildcardPattern & pattern, bool safe) const
|
||||||
{
|
{
|
||||||
std::vector<uint8> buffer(datasize);
|
std::vector<uint8> buffer(datasize);
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,11 @@ namespace GleeBug
|
||||||
dwMainThreadId(dwMainThreadId),
|
dwMainThreadId(dwMainThreadId),
|
||||||
createProcessInfo(createProcessInfo),
|
createProcessInfo(createProcessInfo),
|
||||||
thread(nullptr),
|
thread(nullptr),
|
||||||
systemBreakpoint(false)
|
systemBreakpoint(false),
|
||||||
|
permanentDep(false)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < HWBP_COUNT; i++)
|
for (int i = 0; i < HWBP_COUNT; i++)
|
||||||
hardwareBreakpoints[i].enabled = false;
|
hardwareBreakpoints[i].internal.hardware.enabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Process::StepOver(const StepCallback & cbStep)
|
void Process::StepOver(const StepCallback & cbStep)
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ namespace GleeBug
|
||||||
|
|
||||||
Thread* thread;
|
Thread* thread;
|
||||||
bool systemBreakpoint;
|
bool systemBreakpoint;
|
||||||
|
bool permanentDep;
|
||||||
|
|
||||||
ThreadMap threads; //DO NOT COPY THESE OBJECTS!
|
ThreadMap threads; //DO NOT COPY THESE OBJECTS!
|
||||||
DllMap dlls;
|
DllMap dlls;
|
||||||
|
|
@ -30,6 +31,8 @@ namespace GleeBug
|
||||||
SoftwareBreakpointMap softwareBreakpointReferences;
|
SoftwareBreakpointMap softwareBreakpointReferences;
|
||||||
BreakpointCallbackMap breakpointCallbacks;
|
BreakpointCallbackMap breakpointCallbacks;
|
||||||
BreakpointInfo hardwareBreakpoints[4];
|
BreakpointInfo hardwareBreakpoints[4];
|
||||||
|
MemoryBreakpointSet memoryBreakpointRanges;
|
||||||
|
MemoryBreakpointMap memoryBreakpointPages;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\brief Constructor.
|
\brief Constructor.
|
||||||
|
|
@ -119,6 +122,16 @@ namespace GleeBug
|
||||||
*/
|
*/
|
||||||
bool MemIsValidPtr(ptr address) const;
|
bool MemIsValidPtr(ptr address) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
\brief Memory protect (execute VirtualProtect in the context of the process).
|
||||||
|
\param address The address to change protection for.
|
||||||
|
\param size The size to change protection for.
|
||||||
|
\param newProtect The new protection.
|
||||||
|
\param [out] oldProtect The old protection.
|
||||||
|
\return true if it succeeds, false if it fails.
|
||||||
|
*/
|
||||||
|
bool MemProtect(ptr address, ptr size, DWORD newProtect, DWORD* oldProtect = nullptr);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\brief Finds the first occurrence of a pattern in process memory.
|
\brief Finds the first occurrence of a pattern in process memory.
|
||||||
\param data The address to start searching from.
|
\param data The address to start searching from.
|
||||||
|
|
@ -300,6 +313,61 @@ namespace GleeBug
|
||||||
*/
|
*/
|
||||||
bool DeleteHardwareBreakpoint(ptr address);
|
bool DeleteHardwareBreakpoint(ptr address);
|
||||||
|
|
||||||
|
/**
|
||||||
|
\brief Sets new page protection to trigger an exception for certain memory breakpoint types.
|
||||||
|
\param page The page address.
|
||||||
|
\param data The current protection of the page.
|
||||||
|
\param type The memory breakpoint type to trigger an exception for.
|
||||||
|
\return true if it succeeds, false if it fails.
|
||||||
|
*/
|
||||||
|
bool SetNewPageProtection(ptr page, MemoryBreakpointData & data, MemoryType type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
\brief Sets a memory breakpoint.
|
||||||
|
\param address The address to set the memory breakpoint on.
|
||||||
|
\param size Size of the memory breakpoint (in bytes).
|
||||||
|
\param type (Optional) The memory breakpoint type.
|
||||||
|
\param singleshoot (Optional) True to remove the breakpoint after the first hit.
|
||||||
|
\return true if the memory breakpoint was set, false otherwise.
|
||||||
|
*/
|
||||||
|
bool SetMemoryBreakpoint(ptr address, ptr size, MemoryType type = MemoryType::Access, bool singleshoot = true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
\brief Sets a memory breakpoint.
|
||||||
|
\param address The address to set the memory breakpoint on.
|
||||||
|
\param size Size of the memory breakpoint (in bytes).
|
||||||
|
\param cbBreakpoint The breakpoint callback. Can be written using BIND1(this, MyDebugger::cb).
|
||||||
|
\param type (Optional) The memory breakpoint type.
|
||||||
|
\param singleshoot (Optional) True to remove the breakpoint after the first hit.
|
||||||
|
\return true if the memory breakpoint was set, false otherwise.
|
||||||
|
*/
|
||||||
|
bool SetMemoryBreakpoint(ptr address, ptr size, const BreakpointCallback & cbBreakpoint, MemoryType type = MemoryType::Access, bool singleshoot = true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
\brief Sets a hardware breakpoint.
|
||||||
|
\tparam T Generic type parameter. Must be a subclass of Debugger.
|
||||||
|
\param address The address to set the hardware breakpoint on.
|
||||||
|
\param size Size of the memory breakpoint (in bytes).
|
||||||
|
\param debugger This pointer to a subclass of Debugger.
|
||||||
|
\param callback Pointer to the callback. Written like: &MyDebugger::cb
|
||||||
|
\param type (Optional) The memory breakpoint type.
|
||||||
|
\param singleshoot (Optional) True to remove the breakpoint after the first hit.
|
||||||
|
\return true if the memory breakpoint was set, false otherwise.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
bool SetMemoryBreakpoint(ptr address, ptr size, T* debugger, void(T::*callback)(const BreakpointInfo & info), MemoryType type = MemoryType::Access, bool singleshoot = true)
|
||||||
|
{
|
||||||
|
static_cast<void>(static_cast<Debugger*>(debugger));
|
||||||
|
return SetMemoryBreakpoint(address, size, std::bind(callback, debugger, std::placeholders::_1), type, singleshoot);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\brief Deletes a hardware breakpoint.
|
||||||
|
\param address The address the hardware breakpoint is set on.
|
||||||
|
\return true if the hardware breakpoint was deleted, false otherwise.
|
||||||
|
*/
|
||||||
|
bool DeleteMemoryBreakpoint(ptr address);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\brief Deletes a breakpoint.
|
\brief Deletes a breakpoint.
|
||||||
\param info The breakpoint information.
|
\param info The breakpoint information.
|
||||||
|
|
@ -326,6 +394,24 @@ namespace GleeBug
|
||||||
StepOver(std::bind(callback, debugger));
|
StepOver(std::bind(callback, debugger));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool RegReadContext()
|
||||||
|
{
|
||||||
|
auto result = true;
|
||||||
|
for(auto & thread : this->threads)
|
||||||
|
if(!thread.second.RegReadContext())
|
||||||
|
result = false;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RegWriteContext()
|
||||||
|
{
|
||||||
|
auto result = true;
|
||||||
|
for(auto & thread : this->threads)
|
||||||
|
if(!thread.second.RegWriteContext())
|
||||||
|
result = false;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Capstone mCapstone;
|
Capstone mCapstone;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -54,8 +54,10 @@
|
||||||
|
|
||||||
namespace GleeBug
|
namespace GleeBug
|
||||||
{
|
{
|
||||||
ptr Registers::Get(R reg) const
|
ptr Registers::Get(R reg)
|
||||||
{
|
{
|
||||||
|
handleLazyContext();
|
||||||
|
|
||||||
switch (reg)
|
switch (reg)
|
||||||
{
|
{
|
||||||
case R::DR0:
|
case R::DR0:
|
||||||
|
|
@ -244,6 +246,8 @@ namespace GleeBug
|
||||||
|
|
||||||
void Registers::Set(R reg, ptr value)
|
void Registers::Set(R reg, ptr value)
|
||||||
{
|
{
|
||||||
|
handleLazyContext();
|
||||||
|
|
||||||
switch (reg)
|
switch (reg)
|
||||||
{
|
{
|
||||||
case R::DR0:
|
case R::DR0:
|
||||||
|
|
@ -513,21 +517,27 @@ namespace GleeBug
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Registers::GetFlag(F flag) const
|
bool Registers::GetFlag(F flag)
|
||||||
{
|
{
|
||||||
|
handleLazyContext();
|
||||||
|
|
||||||
return (mContext.EFlags & ptr(flag)) == ptr(flag);
|
return (mContext.EFlags & ptr(flag)) == ptr(flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Registers::SetFlag(F flag, bool set)
|
void Registers::SetFlag(F flag, bool set)
|
||||||
{
|
{
|
||||||
|
handleLazyContext();
|
||||||
|
|
||||||
if (set)
|
if (set)
|
||||||
mContext.EFlags |= ptr(flag);
|
mContext.EFlags |= ptr(flag);
|
||||||
else
|
else
|
||||||
mContext.EFlags &= ~ptr(flag);
|
mContext.EFlags &= ~ptr(flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
void* Registers::getPtr(R reg) const
|
void* Registers::getPtr(R reg)
|
||||||
{
|
{
|
||||||
|
handleLazyContext();
|
||||||
|
|
||||||
switch (reg)
|
switch (reg)
|
||||||
{
|
{
|
||||||
case R::DR0:
|
case R::DR0:
|
||||||
|
|
|
||||||
|
|
@ -102,13 +102,45 @@ namespace GleeBug
|
||||||
memset(&this->mContext, 0, sizeof(CONTEXT));
|
memset(&this->mContext, 0, sizeof(CONTEXT));
|
||||||
}
|
}
|
||||||
|
|
||||||
const CONTEXT* Registers::GetContext() const
|
const CONTEXT* Registers::GetContext()
|
||||||
{
|
{
|
||||||
|
handleLazyContext();
|
||||||
return &mContext;
|
return &mContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Registers::SetContext(const CONTEXT & context)
|
void Registers::SetContext(const CONTEXT & context)
|
||||||
{
|
{
|
||||||
|
handleLazyContext();
|
||||||
this->mContext = context;
|
this->mContext = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Registers::setContextLazy(CONTEXT* oldContext, HANDLE hThread)
|
||||||
|
{
|
||||||
|
this->mLazyOldContext = oldContext;
|
||||||
|
this->mLazyThread = hThread;
|
||||||
|
this->mLazySet = true;
|
||||||
|
this->mContext = *this->mLazyOldContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Registers::handleLazyContext()
|
||||||
|
{
|
||||||
|
if(!this->mLazySet)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if(!this->mLazyOldContext || !this->mLazyThread) //assert
|
||||||
|
__debugbreak();
|
||||||
|
|
||||||
|
//TODO: handle failure of GetThreadContext
|
||||||
|
auto result = false;
|
||||||
|
if(GetThreadContext(this->mLazyThread, this->mLazyOldContext))
|
||||||
|
{
|
||||||
|
this->mContext = *this->mLazyOldContext;
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->mLazyOldContext = nullptr;
|
||||||
|
this->mLazyThread = nullptr;
|
||||||
|
this->mLazySet = false;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -11,6 +11,7 @@ namespace GleeBug
|
||||||
class Registers
|
class Registers
|
||||||
{
|
{
|
||||||
friend class Register;
|
friend class Register;
|
||||||
|
friend class Thread;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
|
|
@ -123,7 +124,7 @@ namespace GleeBug
|
||||||
\param reg The register to get.
|
\param reg The register to get.
|
||||||
\return The register value.
|
\return The register value.
|
||||||
*/
|
*/
|
||||||
ptr Get(R reg) const;
|
ptr Get(R reg);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\brief Sets a given register.
|
\brief Sets a given register.
|
||||||
|
|
@ -137,7 +138,7 @@ namespace GleeBug
|
||||||
\param flag The flag to get.
|
\param flag The flag to get.
|
||||||
\return true if the flag is set, false otherwise.
|
\return true if the flag is set, false otherwise.
|
||||||
*/
|
*/
|
||||||
bool GetFlag(F flag) const;
|
bool GetFlag(F flag);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\brief Sets a flag.
|
\brief Sets a flag.
|
||||||
|
|
@ -149,7 +150,7 @@ namespace GleeBug
|
||||||
\brief Gets a pointer to the context object.
|
\brief Gets a pointer to the context object.
|
||||||
\return This function will never return a nullptr.
|
\return This function will never return a nullptr.
|
||||||
*/
|
*/
|
||||||
const CONTEXT* GetContext() const;
|
const CONTEXT* GetContext();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\brief Sets the CONTEXT.
|
\brief Sets the CONTEXT.
|
||||||
|
|
@ -160,7 +161,24 @@ namespace GleeBug
|
||||||
private:
|
private:
|
||||||
CONTEXT mContext;
|
CONTEXT mContext;
|
||||||
|
|
||||||
void* getPtr(R reg) const;
|
CONTEXT* mLazyOldContext = nullptr;
|
||||||
|
HANDLE mLazyThread = nullptr;
|
||||||
|
bool mLazySet = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
\brief Lazily set CONTEXT. This will only actually retrieve the context if a function in this thread is called.
|
||||||
|
\param oldContext Pointer to the old context, used to determine if updates are required.
|
||||||
|
\param hThread Handle of the thread to get the context from if required.
|
||||||
|
*/
|
||||||
|
void setContextLazy(CONTEXT* oldContext, HANDLE hThread);
|
||||||
|
|
||||||
|
/**
|
||||||
|
\brief Retrieve the actual context if setContextLazy has been called.
|
||||||
|
\return Whether retrieving the actual context was successful.
|
||||||
|
*/
|
||||||
|
bool handleLazyContext();
|
||||||
|
|
||||||
|
void* getPtr(R reg);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -44,21 +44,17 @@ namespace GleeBug
|
||||||
{
|
{
|
||||||
memset(&this->mOldContext, 0, sizeof(CONTEXT));
|
memset(&this->mOldContext, 0, sizeof(CONTEXT));
|
||||||
this->mOldContext.ContextFlags = CONTEXT_ALL; //TODO: granular control over what's required
|
this->mOldContext.ContextFlags = CONTEXT_ALL; //TODO: granular control over what's required
|
||||||
if (GetThreadContext(this->hThread, &this->mOldContext))
|
this->registers.setContextLazy(&this->mOldContext, this->hThread);
|
||||||
{
|
|
||||||
this->registers.SetContext(this->mOldContext);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Thread::RegWriteContext() const
|
bool Thread::RegWriteContext()
|
||||||
{
|
{
|
||||||
//check if something actually changed
|
//check if something actually changed
|
||||||
if (memcmp(&this->mOldContext, this->registers.GetContext(), sizeof(CONTEXT)) == 0)
|
if (memcmp(&this->mOldContext, &this->registers.mContext, sizeof(CONTEXT)) == 0)
|
||||||
return true;
|
return true;
|
||||||
//update the context
|
//update the context
|
||||||
return !!SetThreadContext(this->hThread, this->registers.GetContext());
|
return !!SetThreadContext(this->hThread, &this->registers.mContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Thread::StepInto()
|
void Thread::StepInto()
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ namespace GleeBug
|
||||||
\brief Write the register context to the thread. This does nothing if the registers did not change.
|
\brief Write the register context to the thread. This does nothing if the registers did not change.
|
||||||
\return true if it succeeds, false if it fails.
|
\return true if it succeeds, false if it fails.
|
||||||
*/
|
*/
|
||||||
bool RegWriteContext() const;
|
bool RegWriteContext();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\brief Step into.
|
\brief Step into.
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ namespace GleeBug
|
||||||
nullptr,
|
nullptr,
|
||||||
nullptr,
|
nullptr,
|
||||||
FALSE,
|
FALSE,
|
||||||
DEBUG_PROCESS | CREATE_NEW_CONSOLE,
|
DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS | CREATE_NEW_CONSOLE,
|
||||||
nullptr,
|
nullptr,
|
||||||
szCurrentDirectory,
|
szCurrentDirectory,
|
||||||
&mMainStartupInfo,
|
&mMainStartupInfo,
|
||||||
|
|
|
||||||
|
|
@ -271,6 +271,10 @@ namespace GleeBug
|
||||||
*/
|
*/
|
||||||
virtual void exceptionHardwareBreakpoint(ptr exceptionAddress);
|
virtual void exceptionHardwareBreakpoint(ptr exceptionAddress);
|
||||||
|
|
||||||
|
virtual void exceptionGuardPage(const EXCEPTION_RECORD & exceptionRecord, bool firstChance);
|
||||||
|
|
||||||
|
virtual void exceptionAccessViolation(const EXCEPTION_RECORD & exceptionRecord, bool firstChance);
|
||||||
|
|
||||||
protected: //variables
|
protected: //variables
|
||||||
STARTUPINFOW mMainStartupInfo;
|
STARTUPINFOW mMainStartupInfo;
|
||||||
PROCESS_INFORMATION mMainProcess;
|
PROCESS_INFORMATION mMainProcess;
|
||||||
|
|
|
||||||
464
LICENSE
464
LICENSE
|
|
@ -1,165 +1,373 @@
|
||||||
GNU LESSER GENERAL PUBLIC LICENSE
|
Mozilla Public License Version 2.0
|
||||||
Version 3, 29 June 2007
|
==================================
|
||||||
|
|
||||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
1. Definitions
|
||||||
Everyone is permitted to copy and distribute verbatim copies
|
--------------
|
||||||
of this license document, but changing it is not allowed.
|
|
||||||
|
|
||||||
|
1.1. "Contributor"
|
||||||
|
means each individual or legal entity that creates, contributes to
|
||||||
|
the creation of, or owns Covered Software.
|
||||||
|
|
||||||
This version of the GNU Lesser General Public License incorporates
|
1.2. "Contributor Version"
|
||||||
the terms and conditions of version 3 of the GNU General Public
|
means the combination of the Contributions of others (if any) used
|
||||||
License, supplemented by the additional permissions listed below.
|
by a Contributor and that particular Contributor's Contribution.
|
||||||
|
|
||||||
0. Additional Definitions.
|
1.3. "Contribution"
|
||||||
|
means Covered Software of a particular Contributor.
|
||||||
|
|
||||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
1.4. "Covered Software"
|
||||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
means Source Code Form to which the initial Contributor has attached
|
||||||
General Public License.
|
the notice in Exhibit A, the Executable Form of such Source Code
|
||||||
|
Form, and Modifications of such Source Code Form, in each case
|
||||||
|
including portions thereof.
|
||||||
|
|
||||||
"The Library" refers to a covered work governed by this License,
|
1.5. "Incompatible With Secondary Licenses"
|
||||||
other than an Application or a Combined Work as defined below.
|
means
|
||||||
|
|
||||||
An "Application" is any work that makes use of an interface provided
|
(a) that the initial Contributor has attached the notice described
|
||||||
by the Library, but which is not otherwise based on the Library.
|
in Exhibit B to the Covered Software; or
|
||||||
Defining a subclass of a class defined by the Library is deemed a mode
|
|
||||||
of using an interface provided by the Library.
|
|
||||||
|
|
||||||
A "Combined Work" is a work produced by combining or linking an
|
(b) that the Covered Software was made available under the terms of
|
||||||
Application with the Library. The particular version of the Library
|
version 1.1 or earlier of the License, but not also under the
|
||||||
with which the Combined Work was made is also called the "Linked
|
terms of a Secondary License.
|
||||||
Version".
|
|
||||||
|
|
||||||
The "Minimal Corresponding Source" for a Combined Work means the
|
1.6. "Executable Form"
|
||||||
Corresponding Source for the Combined Work, excluding any source code
|
means any form of the work other than Source Code Form.
|
||||||
for portions of the Combined Work that, considered in isolation, are
|
|
||||||
based on the Application, and not on the Linked Version.
|
|
||||||
|
|
||||||
The "Corresponding Application Code" for a Combined Work means the
|
1.7. "Larger Work"
|
||||||
object code and/or source code for the Application, including any data
|
means a work that combines Covered Software with other material, in
|
||||||
and utility programs needed for reproducing the Combined Work from the
|
a separate file or files, that is not Covered Software.
|
||||||
Application, but excluding the System Libraries of the Combined Work.
|
|
||||||
|
|
||||||
1. Exception to Section 3 of the GNU GPL.
|
1.8. "License"
|
||||||
|
means this document.
|
||||||
|
|
||||||
You may convey a covered work under sections 3 and 4 of this License
|
1.9. "Licensable"
|
||||||
without being bound by section 3 of the GNU GPL.
|
means having the right to grant, to the maximum extent possible,
|
||||||
|
whether at the time of the initial grant or subsequently, any and
|
||||||
|
all of the rights conveyed by this License.
|
||||||
|
|
||||||
2. Conveying Modified Versions.
|
1.10. "Modifications"
|
||||||
|
means any of the following:
|
||||||
|
|
||||||
If you modify a copy of the Library, and, in your modifications, a
|
(a) any file in Source Code Form that results from an addition to,
|
||||||
facility refers to a function or data to be supplied by an Application
|
deletion from, or modification of the contents of Covered
|
||||||
that uses the facility (other than as an argument passed when the
|
Software; or
|
||||||
facility is invoked), then you may convey a copy of the modified
|
|
||||||
version:
|
|
||||||
|
|
||||||
a) under this License, provided that you make a good faith effort to
|
(b) any new file in Source Code Form that contains any Covered
|
||||||
ensure that, in the event an Application does not supply the
|
Software.
|
||||||
function or data, the facility still operates, and performs
|
|
||||||
whatever part of its purpose remains meaningful, or
|
|
||||||
|
|
||||||
b) under the GNU GPL, with none of the additional permissions of
|
1.11. "Patent Claims" of a Contributor
|
||||||
this License applicable to that copy.
|
means any patent claim(s), including without limitation, method,
|
||||||
|
process, and apparatus claims, in any patent Licensable by such
|
||||||
|
Contributor that would be infringed, but for the grant of the
|
||||||
|
License, by the making, using, selling, offering for sale, having
|
||||||
|
made, import, or transfer of either its Contributions or its
|
||||||
|
Contributor Version.
|
||||||
|
|
||||||
3. Object Code Incorporating Material from Library Header Files.
|
1.12. "Secondary License"
|
||||||
|
means either the GNU General Public License, Version 2.0, the GNU
|
||||||
|
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||||
|
Public License, Version 3.0, or any later versions of those
|
||||||
|
licenses.
|
||||||
|
|
||||||
The object code form of an Application may incorporate material from
|
1.13. "Source Code Form"
|
||||||
a header file that is part of the Library. You may convey such object
|
means the form of the work preferred for making modifications.
|
||||||
code under terms of your choice, provided that, if the incorporated
|
|
||||||
material is not limited to numerical parameters, data structure
|
|
||||||
layouts and accessors, or small macros, inline functions and templates
|
|
||||||
(ten or fewer lines in length), you do both of the following:
|
|
||||||
|
|
||||||
a) Give prominent notice with each copy of the object code that the
|
1.14. "You" (or "Your")
|
||||||
Library is used in it and that the Library and its use are
|
means an individual or a legal entity exercising rights under this
|
||||||
covered by this License.
|
License. For legal entities, "You" includes any entity that
|
||||||
|
controls, is controlled by, or is under common control with You. For
|
||||||
|
purposes of this definition, "control" means (a) the power, direct
|
||||||
|
or indirect, to cause the direction or management of such entity,
|
||||||
|
whether by contract or otherwise, or (b) ownership of more than
|
||||||
|
fifty percent (50%) of the outstanding shares or beneficial
|
||||||
|
ownership of such entity.
|
||||||
|
|
||||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
2. License Grants and Conditions
|
||||||
document.
|
--------------------------------
|
||||||
|
|
||||||
4. Combined Works.
|
2.1. Grants
|
||||||
|
|
||||||
You may convey a Combined Work under terms of your choice that,
|
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||||
taken together, effectively do not restrict modification of the
|
non-exclusive license:
|
||||||
portions of the Library contained in the Combined Work and reverse
|
|
||||||
engineering for debugging such modifications, if you also do each of
|
|
||||||
the following:
|
|
||||||
|
|
||||||
a) Give prominent notice with each copy of the Combined Work that
|
(a) under intellectual property rights (other than patent or trademark)
|
||||||
the Library is used in it and that the Library and its use are
|
Licensable by such Contributor to use, reproduce, make available,
|
||||||
covered by this License.
|
modify, display, perform, distribute, and otherwise exploit its
|
||||||
|
Contributions, either on an unmodified basis, with Modifications, or
|
||||||
|
as part of a Larger Work; and
|
||||||
|
|
||||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||||
document.
|
for sale, have made, import, and otherwise transfer either its
|
||||||
|
Contributions or its Contributor Version.
|
||||||
|
|
||||||
c) For a Combined Work that displays copyright notices during
|
2.2. Effective Date
|
||||||
execution, include the copyright notice for the Library among
|
|
||||||
these notices, as well as a reference directing the user to the
|
|
||||||
copies of the GNU GPL and this license document.
|
|
||||||
|
|
||||||
d) Do one of the following:
|
The licenses granted in Section 2.1 with respect to any Contribution
|
||||||
|
become effective for each Contribution on the date the Contributor first
|
||||||
|
distributes such Contribution.
|
||||||
|
|
||||||
0) Convey the Minimal Corresponding Source under the terms of this
|
2.3. Limitations on Grant Scope
|
||||||
License, and the Corresponding Application Code in a form
|
|
||||||
suitable for, and under terms that permit, the user to
|
|
||||||
recombine or relink the Application with a modified version of
|
|
||||||
the Linked Version to produce a modified Combined Work, in the
|
|
||||||
manner specified by section 6 of the GNU GPL for conveying
|
|
||||||
Corresponding Source.
|
|
||||||
|
|
||||||
1) Use a suitable shared library mechanism for linking with the
|
The licenses granted in this Section 2 are the only rights granted under
|
||||||
Library. A suitable mechanism is one that (a) uses at run time
|
this License. No additional rights or licenses will be implied from the
|
||||||
a copy of the Library already present on the user's computer
|
distribution or licensing of Covered Software under this License.
|
||||||
system, and (b) will operate properly with a modified version
|
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||||
of the Library that is interface-compatible with the Linked
|
Contributor:
|
||||||
Version.
|
|
||||||
|
|
||||||
e) Provide Installation Information, but only if you would otherwise
|
(a) for any code that a Contributor has removed from Covered Software;
|
||||||
be required to provide such information under section 6 of the
|
or
|
||||||
GNU GPL, and only to the extent that such information is
|
|
||||||
necessary to install and execute a modified version of the
|
|
||||||
Combined Work produced by recombining or relinking the
|
|
||||||
Application with a modified version of the Linked Version. (If
|
|
||||||
you use option 4d0, the Installation Information must accompany
|
|
||||||
the Minimal Corresponding Source and Corresponding Application
|
|
||||||
Code. If you use option 4d1, you must provide the Installation
|
|
||||||
Information in the manner specified by section 6 of the GNU GPL
|
|
||||||
for conveying Corresponding Source.)
|
|
||||||
|
|
||||||
5. Combined Libraries.
|
(b) for infringements caused by: (i) Your and any other third party's
|
||||||
|
modifications of Covered Software, or (ii) the combination of its
|
||||||
|
Contributions with other software (except as part of its Contributor
|
||||||
|
Version); or
|
||||||
|
|
||||||
You may place library facilities that are a work based on the
|
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||||
Library side by side in a single library together with other library
|
its Contributions.
|
||||||
facilities that are not Applications and are not covered by this
|
|
||||||
License, and convey such a combined library under terms of your
|
|
||||||
choice, if you do both of the following:
|
|
||||||
|
|
||||||
a) Accompany the combined library with a copy of the same work based
|
This License does not grant any rights in the trademarks, service marks,
|
||||||
on the Library, uncombined with any other library facilities,
|
or logos of any Contributor (except as may be necessary to comply with
|
||||||
conveyed under the terms of this License.
|
the notice requirements in Section 3.4).
|
||||||
|
|
||||||
b) Give prominent notice with the combined library that part of it
|
2.4. Subsequent Licenses
|
||||||
is a work based on the Library, and explaining where to find the
|
|
||||||
accompanying uncombined form of the same work.
|
|
||||||
|
|
||||||
6. Revised Versions of the GNU Lesser General Public License.
|
No Contributor makes additional grants as a result of Your choice to
|
||||||
|
distribute the Covered Software under a subsequent version of this
|
||||||
|
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||||
|
permitted under the terms of Section 3.3).
|
||||||
|
|
||||||
The Free Software Foundation may publish revised and/or new versions
|
2.5. Representation
|
||||||
of the GNU Lesser General Public License from time to time. Such new
|
|
||||||
versions will be similar in spirit to the present version, but may
|
|
||||||
differ in detail to address new problems or concerns.
|
|
||||||
|
|
||||||
Each version is given a distinguishing version number. If the
|
Each Contributor represents that the Contributor believes its
|
||||||
Library as you received it specifies that a certain numbered version
|
Contributions are its original creation(s) or it has sufficient rights
|
||||||
of the GNU Lesser General Public License "or any later version"
|
to grant the rights to its Contributions conveyed by this License.
|
||||||
applies to it, you have the option of following the terms and
|
|
||||||
conditions either of that published version or of any later version
|
|
||||||
published by the Free Software Foundation. If the Library as you
|
|
||||||
received it does not specify a version number of the GNU Lesser
|
|
||||||
General Public License, you may choose any version of the GNU Lesser
|
|
||||||
General Public License ever published by the Free Software Foundation.
|
|
||||||
|
|
||||||
If the Library as you received it specifies that a proxy can decide
|
2.6. Fair Use
|
||||||
whether future versions of the GNU Lesser General Public License shall
|
|
||||||
apply, that proxy's public statement of acceptance of any version is
|
This License is not intended to limit any rights You have under
|
||||||
permanent authorization for you to choose that version for the
|
applicable copyright doctrines of fair use, fair dealing, or other
|
||||||
Library.
|
equivalents.
|
||||||
|
|
||||||
|
2.7. Conditions
|
||||||
|
|
||||||
|
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||||
|
in Section 2.1.
|
||||||
|
|
||||||
|
3. Responsibilities
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
3.1. Distribution of Source Form
|
||||||
|
|
||||||
|
All distribution of Covered Software in Source Code Form, including any
|
||||||
|
Modifications that You create or to which You contribute, must be under
|
||||||
|
the terms of this License. You must inform recipients that the Source
|
||||||
|
Code Form of the Covered Software is governed by the terms of this
|
||||||
|
License, and how they can obtain a copy of this License. You may not
|
||||||
|
attempt to alter or restrict the recipients' rights in the Source Code
|
||||||
|
Form.
|
||||||
|
|
||||||
|
3.2. Distribution of Executable Form
|
||||||
|
|
||||||
|
If You distribute Covered Software in Executable Form then:
|
||||||
|
|
||||||
|
(a) such Covered Software must also be made available in Source Code
|
||||||
|
Form, as described in Section 3.1, and You must inform recipients of
|
||||||
|
the Executable Form how they can obtain a copy of such Source Code
|
||||||
|
Form by reasonable means in a timely manner, at a charge no more
|
||||||
|
than the cost of distribution to the recipient; and
|
||||||
|
|
||||||
|
(b) You may distribute such Executable Form under the terms of this
|
||||||
|
License, or sublicense it under different terms, provided that the
|
||||||
|
license for the Executable Form does not attempt to limit or alter
|
||||||
|
the recipients' rights in the Source Code Form under this License.
|
||||||
|
|
||||||
|
3.3. Distribution of a Larger Work
|
||||||
|
|
||||||
|
You may create and distribute a Larger Work under terms of Your choice,
|
||||||
|
provided that You also comply with the requirements of this License for
|
||||||
|
the Covered Software. If the Larger Work is a combination of Covered
|
||||||
|
Software with a work governed by one or more Secondary Licenses, and the
|
||||||
|
Covered Software is not Incompatible With Secondary Licenses, this
|
||||||
|
License permits You to additionally distribute such Covered Software
|
||||||
|
under the terms of such Secondary License(s), so that the recipient of
|
||||||
|
the Larger Work may, at their option, further distribute the Covered
|
||||||
|
Software under the terms of either this License or such Secondary
|
||||||
|
License(s).
|
||||||
|
|
||||||
|
3.4. Notices
|
||||||
|
|
||||||
|
You may not remove or alter the substance of any license notices
|
||||||
|
(including copyright notices, patent notices, disclaimers of warranty,
|
||||||
|
or limitations of liability) contained within the Source Code Form of
|
||||||
|
the Covered Software, except that You may alter any license notices to
|
||||||
|
the extent required to remedy known factual inaccuracies.
|
||||||
|
|
||||||
|
3.5. Application of Additional Terms
|
||||||
|
|
||||||
|
You may choose to offer, and to charge a fee for, warranty, support,
|
||||||
|
indemnity or liability obligations to one or more recipients of Covered
|
||||||
|
Software. However, You may do so only on Your own behalf, and not on
|
||||||
|
behalf of any Contributor. You must make it absolutely clear that any
|
||||||
|
such warranty, support, indemnity, or liability obligation is offered by
|
||||||
|
You alone, and You hereby agree to indemnify every Contributor for any
|
||||||
|
liability incurred by such Contributor as a result of warranty, support,
|
||||||
|
indemnity or liability terms You offer. You may include additional
|
||||||
|
disclaimers of warranty and limitations of liability specific to any
|
||||||
|
jurisdiction.
|
||||||
|
|
||||||
|
4. Inability to Comply Due to Statute or Regulation
|
||||||
|
---------------------------------------------------
|
||||||
|
|
||||||
|
If it is impossible for You to comply with any of the terms of this
|
||||||
|
License with respect to some or all of the Covered Software due to
|
||||||
|
statute, judicial order, or regulation then You must: (a) comply with
|
||||||
|
the terms of this License to the maximum extent possible; and (b)
|
||||||
|
describe the limitations and the code they affect. Such description must
|
||||||
|
be placed in a text file included with all distributions of the Covered
|
||||||
|
Software under this License. Except to the extent prohibited by statute
|
||||||
|
or regulation, such description must be sufficiently detailed for a
|
||||||
|
recipient of ordinary skill to be able to understand it.
|
||||||
|
|
||||||
|
5. Termination
|
||||||
|
--------------
|
||||||
|
|
||||||
|
5.1. The rights granted under this License will terminate automatically
|
||||||
|
if You fail to comply with any of its terms. However, if You become
|
||||||
|
compliant, then the rights granted under this License from a particular
|
||||||
|
Contributor are reinstated (a) provisionally, unless and until such
|
||||||
|
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||||
|
ongoing basis, if such Contributor fails to notify You of the
|
||||||
|
non-compliance by some reasonable means prior to 60 days after You have
|
||||||
|
come back into compliance. Moreover, Your grants from a particular
|
||||||
|
Contributor are reinstated on an ongoing basis if such Contributor
|
||||||
|
notifies You of the non-compliance by some reasonable means, this is the
|
||||||
|
first time You have received notice of non-compliance with this License
|
||||||
|
from such Contributor, and You become compliant prior to 30 days after
|
||||||
|
Your receipt of the notice.
|
||||||
|
|
||||||
|
5.2. If You initiate litigation against any entity by asserting a patent
|
||||||
|
infringement claim (excluding declaratory judgment actions,
|
||||||
|
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||||
|
directly or indirectly infringes any patent, then the rights granted to
|
||||||
|
You by any and all Contributors for the Covered Software under Section
|
||||||
|
2.1 of this License shall terminate.
|
||||||
|
|
||||||
|
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||||
|
end user license agreements (excluding distributors and resellers) which
|
||||||
|
have been validly granted by You or Your distributors under this License
|
||||||
|
prior to termination shall survive termination.
|
||||||
|
|
||||||
|
************************************************************************
|
||||||
|
* *
|
||||||
|
* 6. Disclaimer of Warranty *
|
||||||
|
* ------------------------- *
|
||||||
|
* *
|
||||||
|
* Covered Software is provided under this License on an "as is" *
|
||||||
|
* basis, without warranty of any kind, either expressed, implied, or *
|
||||||
|
* statutory, including, without limitation, warranties that the *
|
||||||
|
* Covered Software is free of defects, merchantable, fit for a *
|
||||||
|
* particular purpose or non-infringing. The entire risk as to the *
|
||||||
|
* quality and performance of the Covered Software is with You. *
|
||||||
|
* Should any Covered Software prove defective in any respect, You *
|
||||||
|
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||||
|
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||||
|
* essential part of this License. No use of any Covered Software is *
|
||||||
|
* authorized under this License except under this disclaimer. *
|
||||||
|
* *
|
||||||
|
************************************************************************
|
||||||
|
|
||||||
|
************************************************************************
|
||||||
|
* *
|
||||||
|
* 7. Limitation of Liability *
|
||||||
|
* -------------------------- *
|
||||||
|
* *
|
||||||
|
* Under no circumstances and under no legal theory, whether tort *
|
||||||
|
* (including negligence), contract, or otherwise, shall any *
|
||||||
|
* Contributor, or anyone who distributes Covered Software as *
|
||||||
|
* permitted above, be liable to You for any direct, indirect, *
|
||||||
|
* special, incidental, or consequential damages of any character *
|
||||||
|
* including, without limitation, damages for lost profits, loss of *
|
||||||
|
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||||
|
* and all other commercial damages or losses, even if such party *
|
||||||
|
* shall have been informed of the possibility of such damages. This *
|
||||||
|
* limitation of liability shall not apply to liability for death or *
|
||||||
|
* personal injury resulting from such party's negligence to the *
|
||||||
|
* extent applicable law prohibits such limitation. Some *
|
||||||
|
* jurisdictions do not allow the exclusion or limitation of *
|
||||||
|
* incidental or consequential damages, so this exclusion and *
|
||||||
|
* limitation may not apply to You. *
|
||||||
|
* *
|
||||||
|
************************************************************************
|
||||||
|
|
||||||
|
8. Litigation
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Any litigation relating to this License may be brought only in the
|
||||||
|
courts of a jurisdiction where the defendant maintains its principal
|
||||||
|
place of business and such litigation shall be governed by laws of that
|
||||||
|
jurisdiction, without reference to its conflict-of-law provisions.
|
||||||
|
Nothing in this Section shall prevent a party's ability to bring
|
||||||
|
cross-claims or counter-claims.
|
||||||
|
|
||||||
|
9. Miscellaneous
|
||||||
|
----------------
|
||||||
|
|
||||||
|
This License represents the complete agreement concerning the subject
|
||||||
|
matter hereof. If any provision of this License is held to be
|
||||||
|
unenforceable, such provision shall be reformed only to the extent
|
||||||
|
necessary to make it enforceable. Any law or regulation which provides
|
||||||
|
that the language of a contract shall be construed against the drafter
|
||||||
|
shall not be used to construe this License against a Contributor.
|
||||||
|
|
||||||
|
10. Versions of the License
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
10.1. New Versions
|
||||||
|
|
||||||
|
Mozilla Foundation is the license steward. Except as provided in Section
|
||||||
|
10.3, no one other than the license steward has the right to modify or
|
||||||
|
publish new versions of this License. Each version will be given a
|
||||||
|
distinguishing version number.
|
||||||
|
|
||||||
|
10.2. Effect of New Versions
|
||||||
|
|
||||||
|
You may distribute the Covered Software under the terms of the version
|
||||||
|
of the License under which You originally received the Covered Software,
|
||||||
|
or under the terms of any subsequent version published by the license
|
||||||
|
steward.
|
||||||
|
|
||||||
|
10.3. Modified Versions
|
||||||
|
|
||||||
|
If you create software not governed by this License, and you want to
|
||||||
|
create a new license for such software, you may create and use a
|
||||||
|
modified version of this License if you rename the license and remove
|
||||||
|
any references to the name of the license steward (except to note that
|
||||||
|
such modified license differs from this License).
|
||||||
|
|
||||||
|
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||||
|
Licenses
|
||||||
|
|
||||||
|
If You choose to distribute Source Code Form that is Incompatible With
|
||||||
|
Secondary Licenses under the terms of this version of the License, the
|
||||||
|
notice described in Exhibit B of this License must be attached.
|
||||||
|
|
||||||
|
Exhibit A - Source Code Form License Notice
|
||||||
|
-------------------------------------------
|
||||||
|
|
||||||
|
This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
If it is not possible or desirable to put the notice in a particular
|
||||||
|
file, then You may include the notice in a location (such as a LICENSE
|
||||||
|
file in a relevant directory) where a recipient would be likely to look
|
||||||
|
for such a notice.
|
||||||
|
|
||||||
|
You may add additional accurate notices of copyright ownership.
|
||||||
|
|
||||||
|
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||||
|
---------------------------------------------------------
|
||||||
|
|
||||||
|
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||||
|
defined by the Mozilla Public License, v. 2.0.
|
||||||
|
|
@ -8,11 +8,64 @@ using namespace GleeBug;
|
||||||
class MyDebugger : public Debugger
|
class MyDebugger : public Debugger
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
|
void cbMemoryBreakpoint2(const BreakpointInfo & info)
|
||||||
|
{
|
||||||
|
printf("Reached memory breakpoint#2! GIP: 0x%p\n",
|
||||||
|
mRegisters->Gip());
|
||||||
|
}
|
||||||
|
|
||||||
|
void cbMemoryBreakpoint(const BreakpointInfo & info)
|
||||||
|
{
|
||||||
|
unsigned char dataToExec[4];
|
||||||
|
const char tmp[] = "aaaa";
|
||||||
|
|
||||||
|
|
||||||
|
printf("Reached memory breakpoint! GIP: 0x%p\n",
|
||||||
|
mRegisters->Gip());
|
||||||
|
|
||||||
|
mProcess->MemReadUnsafe(mRegisters->Gip(), dataToExec, 4);
|
||||||
|
printf("\n What are my bytes? I am so lost.. Dump: ");
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
printf("%02X ", dataToExec[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
mProcess->DeleteMemoryBreakpoint(mRegisters->Gip());
|
||||||
|
memcpy(dataToExec, tmp, 4);
|
||||||
|
mProcess->MemReadUnsafe(mRegisters->Gip(), dataToExec, 4);
|
||||||
|
printf("\n What are my bytes? I am so lost.. Dump: ");
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
printf("%02X ", dataToExec[i]);
|
||||||
|
}
|
||||||
|
mProcess->SetMemoryBreakpoint(mRegisters->Gip() + 1, 0x1, this, &MyDebugger::cbMemoryBreakpoint2, MemoryType::Access, false);
|
||||||
|
memcpy(dataToExec, tmp, 4);
|
||||||
|
mProcess->MemReadUnsafe(mRegisters->Gip(), dataToExec, 4);
|
||||||
|
printf("\n What are my bytes? I am so lost.. Dump: ");
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
printf("%02X ", dataToExec[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void cbEntryBreakpoint(const BreakpointInfo & info)
|
void cbEntryBreakpoint(const BreakpointInfo & info)
|
||||||
{
|
{
|
||||||
printf("Reached entry breakpoint! GIP: 0x%p\n",
|
printf("Reached entry breakpoint! GIP: 0x%p\n",
|
||||||
mRegisters->Gip());
|
mRegisters->Gip());
|
||||||
if (mProcess->DeleteBreakpoint(info.address))
|
#ifdef _WIN64
|
||||||
|
auto addr = mRegisters->Rbx();
|
||||||
|
#else
|
||||||
|
auto addr = mRegisters->Esi();
|
||||||
|
#endif //_WIN64
|
||||||
|
printf("Addr: 0x%p\n", addr);
|
||||||
|
if (mProcess->SetMemoryBreakpoint(addr, 0x10000, this, &MyDebugger::cbMemoryBreakpoint, MemoryType::Execute, false))
|
||||||
|
puts("Memory breakpoint set!");
|
||||||
|
else
|
||||||
|
puts("Failed to set memory breakpoint...");
|
||||||
|
|
||||||
|
//system("pause");
|
||||||
|
|
||||||
|
/*if (mProcess->DeleteBreakpoint(info.address))
|
||||||
printf("Entry breakpoint deleted!\n");
|
printf("Entry breakpoint deleted!\n");
|
||||||
else
|
else
|
||||||
printf("Failed to delete entry breakpoint...\n");
|
printf("Failed to delete entry breakpoint...\n");
|
||||||
|
|
@ -20,7 +73,7 @@ protected:
|
||||||
{
|
{
|
||||||
printf("Step after entry breakpoint! GIP: 0x%p\n",
|
printf("Step after entry breakpoint! GIP: 0x%p\n",
|
||||||
mRegisters->Gip());
|
mRegisters->Gip());
|
||||||
});
|
});*/
|
||||||
}
|
}
|
||||||
|
|
||||||
void cbEntryHardwareBreakpoint(const BreakpointInfo & info)
|
void cbEntryHardwareBreakpoint(const BreakpointInfo & info)
|
||||||
|
|
@ -61,7 +114,13 @@ protected:
|
||||||
else
|
else
|
||||||
printf("No free hardware breakpoint slot...\n");*/
|
printf("No free hardware breakpoint slot...\n");*/
|
||||||
|
|
||||||
if(mProcess->SetBreakpoint(entry, this, &MyDebugger::cbEntryBreakpoint))
|
//https://github.com/mrexodia/GleeBugMembpTest
|
||||||
|
#ifdef _WIN64
|
||||||
|
entry = ptr(createProcess.lpBaseOfImage) + 0x1060; //MembpTest, main.cpp:43 (x64)
|
||||||
|
#else
|
||||||
|
entry = ptr(createProcess.lpBaseOfImage) + 0x108F; //MembpTest, main.cpp:43 (x32)
|
||||||
|
#endif //_WIN64
|
||||||
|
if(mProcess->SetBreakpoint(entry, this, &MyDebugger::cbEntryBreakpoint, true))
|
||||||
printf("Breakpoint set at 0x%p!\n", entry);
|
printf("Breakpoint set at 0x%p!\n", entry);
|
||||||
else
|
else
|
||||||
printf("Failed to set breakpoint at 0x%p...\b", entry);
|
printf("Failed to set breakpoint at 0x%p...\b", entry);
|
||||||
|
|
@ -119,6 +178,8 @@ protected:
|
||||||
exceptionType,
|
exceptionType,
|
||||||
exceptionInfo.ExceptionRecord.ExceptionCode,
|
exceptionInfo.ExceptionRecord.ExceptionCode,
|
||||||
exceptionInfo.ExceptionRecord.ExceptionAddress);
|
exceptionInfo.ExceptionRecord.ExceptionAddress);
|
||||||
|
for (DWORD i = 0; i < exceptionInfo.ExceptionRecord.NumberParameters; i++)
|
||||||
|
printf(" ExceptionInformation[%d] = 0x%p\n", i, exceptionInfo.ExceptionRecord.ExceptionInformation[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cbDebugStringEvent(const OUTPUT_DEBUG_STRING_INFO & debugString) override
|
void cbDebugStringEvent(const OUTPUT_DEBUG_STRING_INFO & debugString) override
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,9 @@
|
||||||
static void testDebugger()
|
static void testDebugger()
|
||||||
{
|
{
|
||||||
#ifdef _WIN64
|
#ifdef _WIN64
|
||||||
wchar_t szFilePath[256] = L"c:\\test64.exe";
|
wchar_t szFilePath[256] = L"c:\\MembpTest_x64.exe";
|
||||||
#else //x86
|
#else //x86
|
||||||
wchar_t szFilePath[256] = L"c:\\test32.exe";
|
wchar_t szFilePath[256] = L"c:\\MembpTest_x32.exe";
|
||||||
#endif //_WIN64
|
#endif //_WIN64
|
||||||
wchar_t szCommandLine[256] = L"";
|
wchar_t szCommandLine[256] = L"";
|
||||||
wchar_t szCurrentDir[256] = L"c:\\";
|
wchar_t szCurrentDir[256] = L"c:\\";
|
||||||
|
|
@ -161,7 +161,7 @@ static void testCorkami()
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
testPeFile(L"C:\\test64.exe");
|
testDebugger();
|
||||||
//testCorkami();
|
//testCorkami();
|
||||||
//testPeFile(L"c:\\!exclude\\pe\\bin\\appendedhdr.exe");
|
//testPeFile(L"c:\\!exclude\\pe\\bin\\appendedhdr.exe");
|
||||||
puts("");
|
puts("");
|
||||||
|
|
|
||||||
|
|
@ -186,6 +186,12 @@ public:
|
||||||
return OpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId);
|
return OpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HANDLE TitanOpenThread(DWORD dwDesiredAccess, bool bInheritHandle, DWORD dwThreadId)
|
||||||
|
{
|
||||||
|
//TODO
|
||||||
|
return OpenThread(dwDesiredAccess, bInheritHandle, dwThreadId);
|
||||||
|
}
|
||||||
|
|
||||||
ULONG_PTR ImporterGetRemoteAPIAddress(HANDLE hProcess, ULONG_PTR APIAddress)
|
ULONG_PTR ImporterGetRemoteAPIAddress(HANDLE hProcess, ULONG_PTR APIAddress)
|
||||||
{
|
{
|
||||||
//TODO
|
//TODO
|
||||||
|
|
@ -229,6 +235,8 @@ public:
|
||||||
auto thread = threadFromHandle(hActiveThread);
|
auto thread = threadFromHandle(hActiveThread);
|
||||||
if (!thread)
|
if (!thread)
|
||||||
return 0;
|
return 0;
|
||||||
|
if(mIsRunning)
|
||||||
|
thread->RegReadContext();
|
||||||
return thread->registers.Get(registerFromDword(IndexOfRegister));
|
return thread->registers.Get(registerFromDword(IndexOfRegister));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -237,7 +245,11 @@ public:
|
||||||
auto thread = threadFromHandle(hActiveThread);
|
auto thread = threadFromHandle(hActiveThread);
|
||||||
if (!thread)
|
if (!thread)
|
||||||
return false;
|
return false;
|
||||||
|
if(mIsRunning)
|
||||||
|
thread->RegReadContext();
|
||||||
thread->registers.Set(registerFromDword(IndexOfRegister), NewRegisterValue);
|
thread->registers.Set(registerFromDword(IndexOfRegister), NewRegisterValue);
|
||||||
|
if(mIsRunning)
|
||||||
|
thread->RegWriteContext();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -246,6 +258,8 @@ public:
|
||||||
auto thread = threadFromHandle(hActiveThread);
|
auto thread = threadFromHandle(hActiveThread);
|
||||||
if (!thread || !titcontext)
|
if (!thread || !titcontext)
|
||||||
return false;
|
return false;
|
||||||
|
if(mIsRunning)
|
||||||
|
thread->RegReadContext();
|
||||||
memset(titcontext, 0, sizeof(TITAN_ENGINE_CONTEXT_t));
|
memset(titcontext, 0, sizeof(TITAN_ENGINE_CONTEXT_t));
|
||||||
auto context = thread->registers.GetContext();
|
auto context = thread->registers.GetContext();
|
||||||
titcontext->cax = thread->registers.Gax();
|
titcontext->cax = thread->registers.Gax();
|
||||||
|
|
@ -288,6 +302,8 @@ public:
|
||||||
auto thread = threadFromHandle(hActiveThread);
|
auto thread = threadFromHandle(hActiveThread);
|
||||||
if (!thread || !titcontext)
|
if (!thread || !titcontext)
|
||||||
return false;
|
return false;
|
||||||
|
if(mIsRunning)
|
||||||
|
thread->RegReadContext();
|
||||||
thread->registers.Gax = titcontext->cax;
|
thread->registers.Gax = titcontext->cax;
|
||||||
thread->registers.Gcx = titcontext->ccx;
|
thread->registers.Gcx = titcontext->ccx;
|
||||||
thread->registers.Gdx = titcontext->cdx;
|
thread->registers.Gdx = titcontext->cdx;
|
||||||
|
|
@ -322,6 +338,8 @@ public:
|
||||||
context.SegCs = titcontext->cs;
|
context.SegCs = titcontext->cs;
|
||||||
context.SegSs = titcontext->ss;
|
context.SegSs = titcontext->ss;
|
||||||
thread->registers.SetContext(context);
|
thread->registers.SetContext(context);
|
||||||
|
if(mIsRunning)
|
||||||
|
thread->RegWriteContext();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -423,14 +441,19 @@ public:
|
||||||
//Memory Breakpoints
|
//Memory Breakpoints
|
||||||
bool SetMemoryBPXEx(ULONG_PTR MemoryStart, SIZE_T SizeOfMemory, DWORD BreakPointType, bool RestoreOnHit, LPVOID bpxCallBack)
|
bool SetMemoryBPXEx(ULONG_PTR MemoryStart, SIZE_T SizeOfMemory, DWORD BreakPointType, bool RestoreOnHit, LPVOID bpxCallBack)
|
||||||
{
|
{
|
||||||
//TODO
|
if (!mProcess)
|
||||||
return false;
|
return false;
|
||||||
|
return mProcess->SetMemoryBreakpoint(ptr(MemoryStart), ptr(SizeOfMemory), [bpxCallBack](const BreakpointInfo & info)
|
||||||
|
{
|
||||||
|
(MEMBPCALLBACK(bpxCallBack))((const void*)info.address);
|
||||||
|
}, memtypeFromTitan(BreakPointType), !RestoreOnHit);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RemoveMemoryBPX(ULONG_PTR MemoryStart, SIZE_T SizeOfMemory)
|
bool RemoveMemoryBPX(ULONG_PTR MemoryStart, SIZE_T SizeOfMemory)
|
||||||
{
|
{
|
||||||
//TODO
|
if (!mProcess)
|
||||||
return false;
|
return false;
|
||||||
|
return mProcess->DeleteMemoryBreakpoint(ptr(MemoryStart));
|
||||||
}
|
}
|
||||||
|
|
||||||
//Hardware Breakpoints
|
//Hardware Breakpoints
|
||||||
|
|
@ -438,11 +461,17 @@ public:
|
||||||
{
|
{
|
||||||
if (!mProcess)
|
if (!mProcess)
|
||||||
return false;
|
return false;
|
||||||
return mProcess->SetHardwareBreakpoint(bpxAddress,
|
if(mIsRunning)
|
||||||
|
mProcess->RegReadContext();
|
||||||
|
if(!mProcess->SetHardwareBreakpoint(bpxAddress,
|
||||||
(HardwareSlot)IndexOfRegister, [bpxCallBack](const BreakpointInfo & info)
|
(HardwareSlot)IndexOfRegister, [bpxCallBack](const BreakpointInfo & info)
|
||||||
{
|
{
|
||||||
(HWBPCALLBACK(bpxCallBack))((const void*)info.address);
|
(HWBPCALLBACK(bpxCallBack))((const void*)info.address);
|
||||||
}, hwtypeFromTitan(bpxType), hwsizeFromTitan(bpxSize));
|
}, hwtypeFromTitan(bpxType), hwsizeFromTitan(bpxSize)))
|
||||||
|
return false;
|
||||||
|
if(mIsRunning)
|
||||||
|
mProcess->RegWriteContext();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DeleteHardwareBreakPoint(DWORD IndexOfRegister)
|
bool DeleteHardwareBreakPoint(DWORD IndexOfRegister)
|
||||||
|
|
@ -660,12 +689,30 @@ private: //functions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static MemoryType memtypeFromTitan(DWORD type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case UE_MEMORY:
|
||||||
|
return MemoryType::Access;
|
||||||
|
case UE_MEMORY_READ:
|
||||||
|
return MemoryType::Read;
|
||||||
|
case UE_MEMORY_WRITE:
|
||||||
|
return MemoryType::Write;
|
||||||
|
case UE_MEMORY_EXECUTE:
|
||||||
|
return MemoryType::Execute;
|
||||||
|
default:
|
||||||
|
return MemoryType::Access;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private: //variables
|
private: //variables
|
||||||
bool mSetDebugPrivilege = false;
|
bool mSetDebugPrivilege = false;
|
||||||
typedef void(*CUSTOMHANDLER)(const void*);
|
typedef void(*CUSTOMHANDLER)(const void*);
|
||||||
typedef void(*STEPCALLBACK)();
|
typedef void(*STEPCALLBACK)();
|
||||||
typedef STEPCALLBACK BPCALLBACK;
|
typedef STEPCALLBACK BPCALLBACK;
|
||||||
typedef CUSTOMHANDLER HWBPCALLBACK;
|
typedef CUSTOMHANDLER HWBPCALLBACK;
|
||||||
|
typedef CUSTOMHANDLER MEMBPCALLBACK;
|
||||||
CUSTOMHANDLER mCbCREATEPROCESS = nullptr;
|
CUSTOMHANDLER mCbCREATEPROCESS = nullptr;
|
||||||
CUSTOMHANDLER mCbEXITPROCESS = nullptr;
|
CUSTOMHANDLER mCbEXITPROCESS = nullptr;
|
||||||
CUSTOMHANDLER mCbCREATETHREAD = nullptr;
|
CUSTOMHANDLER mCbCREATETHREAD = nullptr;
|
||||||
|
|
|
||||||
|
|
@ -1012,6 +1012,7 @@ __declspec(dllexport) long TITCALL GetActiveProcessId(const char* szImageName);
|
||||||
__declspec(dllexport) long TITCALL GetActiveProcessIdW(const wchar_t* szImageName);
|
__declspec(dllexport) long TITCALL GetActiveProcessIdW(const wchar_t* szImageName);
|
||||||
__declspec(dllexport) void TITCALL EnumProcessesWithLibrary(const char* szLibraryName, void* EnumFunction);
|
__declspec(dllexport) void TITCALL EnumProcessesWithLibrary(const char* szLibraryName, void* EnumFunction);
|
||||||
__declspec(dllexport) HANDLE TITCALL TitanOpenProcess(DWORD dwDesiredAccess, bool bInheritHandle, DWORD dwProcessId);
|
__declspec(dllexport) HANDLE TITCALL TitanOpenProcess(DWORD dwDesiredAccess, bool bInheritHandle, DWORD dwProcessId);
|
||||||
|
__declspec(dllexport) HANDLE TITCALL TitanOpenThread(DWORD dwDesiredAccess, bool bInheritHandle, DWORD dwThreadId);
|
||||||
// TitanEngine.TLSFixer.functions:
|
// TitanEngine.TLSFixer.functions:
|
||||||
__declspec(dllexport) bool TITCALL TLSBreakOnCallBack(LPVOID ArrayOfCallBacks, DWORD NumberOfCallBacks, LPVOID bpxCallBack);
|
__declspec(dllexport) bool TITCALL TLSBreakOnCallBack(LPVOID ArrayOfCallBacks, DWORD NumberOfCallBacks, LPVOID bpxCallBack);
|
||||||
__declspec(dllexport) bool TITCALL TLSGrabCallBackData(const char* szFileName, LPVOID ArrayOfCallBacks, LPDWORD NumberOfCallBacks);
|
__declspec(dllexport) bool TITCALL TLSGrabCallBackData(const char* szFileName, LPVOID ArrayOfCallBacks, LPDWORD NumberOfCallBacks);
|
||||||
|
|
|
||||||
|
|
@ -117,6 +117,11 @@ __declspec(dllexport) HANDLE TITCALL TitanOpenProcess(DWORD dwDesiredAccess, boo
|
||||||
return emu.TitanOpenProces(dwDesiredAccess, bInheritHandle, dwProcessId);
|
return emu.TitanOpenProces(dwDesiredAccess, bInheritHandle, dwProcessId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__declspec(dllexport) HANDLE TITCALL TitanOpenThread(DWORD dwDesiredAccess, bool bInheritHandle, DWORD dwThreadId)
|
||||||
|
{
|
||||||
|
return emu.TitanOpenThread(dwDesiredAccess, bInheritHandle, dwThreadId);
|
||||||
|
}
|
||||||
|
|
||||||
__declspec(dllexport) ULONG_PTR TITCALL ImporterGetRemoteAPIAddress(HANDLE hProcess, ULONG_PTR APIAddress)
|
__declspec(dllexport) ULONG_PTR TITCALL ImporterGetRemoteAPIAddress(HANDLE hProcess, ULONG_PTR APIAddress)
|
||||||
{
|
{
|
||||||
return emu.ImporterGetRemoteAPIAddress(hProcess, APIAddress);
|
return emu.ImporterGetRemoteAPIAddress(hProcess, APIAddress);
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit 619f5e0a0c43b19ca9dabf34da4ddd19cb84a704
|
Subproject commit 2034d2a0151866ae56fa51171788217d59c47a7b
|
||||||
Loading…
Reference in New Issue