mirror of https://github.com/x64dbg/TitanEngine
Added an alternative memory breakpoint method that uses PAGE_NOACCESS instead of PAGE_GUARD
This commit is contained in:
parent
8d93135f38
commit
18a885a703
|
|
@ -57,6 +57,7 @@
|
|||
#define UE_ENGINE_CALL_PLUGIN_DEBUG_CALLBACK 8
|
||||
#define UE_ENGINE_SET_DEBUG_PRIVILEGE 9
|
||||
#define UE_ENGINE_SAFE_ATTACH 10
|
||||
#define UE_ENGINE_MEMBP_ALT 11
|
||||
|
||||
#define UE_OPTION_REMOVEALL 1
|
||||
#define UE_OPTION_DISABLEALL 2
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ const BYTE UE_ENGINE_RESET_CUSTOM_HANDLER = 7;
|
|||
const BYTE UE_ENGINE_CALL_PLUGIN_DEBUG_CALLBACK = 8;
|
||||
const BYTE UE_ENGINE_SET_DEBUG_PRIVILEGE = 9;
|
||||
const BYTE UE_ENGINE_SAFE_ATTACH = 10;
|
||||
const BYTE UE_ENGINE_MEMBP_ALT = 11;
|
||||
|
||||
const BYTE UE_OPTION_REMOVEALL = 1;
|
||||
const BYTE UE_OPTION_DISABLEALL = 2;
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ enum eEngineVariable : DWORD
|
|||
UE_ENGINE_CALL_PLUGIN_DEBUG_CALLBACK = UE::UE_ENGINE_CALL_PLUGIN_DEBUG_CALLBACK,
|
||||
UE_ENGINE_SET_DEBUG_PRIVILEGE = UE::UE_ENGINE_SET_DEBUG_PRIVILEGE,
|
||||
UE_ENGINE_SAFE_ATTACH = UE::UE_ENGINE_SAFE_ATTACH,
|
||||
UE_ENGINE_MEMBP_ALT = UE::UE_ENGINE_MEMBP_ALT;
|
||||
};
|
||||
|
||||
enum eBPRemoveOption : DWORD
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ bool engineExecutePluginCallBack = true;
|
|||
bool engineAutoHideFromDebugger = false; // hardcoded
|
||||
bool engineEnableDebugPrivilege = false;
|
||||
bool engineSafeAttach = false;
|
||||
bool engineMembpAlt = false;
|
||||
|
||||
char engineFoundDLLName[512] = {0};
|
||||
char engineFoundAPIName[512] = {0};
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ extern bool engineExecutePluginCallBack;
|
|||
extern bool engineAutoHideFromDebugger;
|
||||
extern bool engineEnableDebugPrivilege;
|
||||
extern bool engineSafeAttach;
|
||||
extern bool engineMembpAlt;
|
||||
|
||||
//Global.Engine.Functions
|
||||
void EngineInit();
|
||||
|
|
|
|||
|
|
@ -446,6 +446,7 @@ __declspec(dllexport) bool TITCALL SetMemoryBPXEx(ULONG_PTR MemoryStart, SIZE_T
|
|||
MEMORY_BASIC_INFORMATION MemInfo;
|
||||
ULONG_PTR NumberOfBytesReadWritten = 0;
|
||||
int bpcount = (int)BreakPointBuffer.size();
|
||||
DWORD OldProtect = 0;
|
||||
//search for breakpoint
|
||||
for(int i = 0; i < bpcount; i++)
|
||||
{
|
||||
|
|
@ -461,15 +462,32 @@ __declspec(dllexport) bool TITCALL SetMemoryBPXEx(ULONG_PTR MemoryStart, SIZE_T
|
|||
}
|
||||
//set PAGE_GUARD on all the pages separately
|
||||
size_t pages = SizeOfMemory / TITANENGINE_PAGESIZE;
|
||||
|
||||
for(size_t i = 0; i < pages; i++)
|
||||
{
|
||||
const LPVOID curPage = (LPVOID)(MemoryStart + i * TITANENGINE_PAGESIZE);
|
||||
|
||||
VirtualQueryEx(dbgProcessInformation.hProcess, curPage, &MemInfo, sizeof(MEMORY_BASIC_INFORMATION));
|
||||
DWORD OldProtect = MemInfo.Protect;
|
||||
if(!(OldProtect & PAGE_GUARD))
|
||||
|
||||
if (OldProtect == 0)
|
||||
OldProtect = MemInfo.Protect;
|
||||
|
||||
// Check if the alternative memory breakpoint method should be used
|
||||
if (engineMembpAlt)
|
||||
{
|
||||
DWORD NewProtect = OldProtect ^ PAGE_GUARD;
|
||||
VirtualProtectEx(dbgProcessInformation.hProcess, curPage, TITANENGINE_PAGESIZE, NewProtect, &OldProtect);
|
||||
if(!(MemInfo.Protect & PAGE_NOACCESS))
|
||||
{
|
||||
VirtualProtectEx(dbgProcessInformation.hProcess, curPage, TITANENGINE_PAGESIZE, PAGE_NOACCESS, &MemInfo.Protect);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Default to using PAGE_GUARD memory breakpoint
|
||||
if(!(MemInfo.Protect & PAGE_GUARD))
|
||||
{
|
||||
DWORD NewProtect = MemInfo.Protect ^ PAGE_GUARD;
|
||||
VirtualProtectEx(dbgProcessInformation.hProcess, curPage, TITANENGINE_PAGESIZE, NewProtect, &MemInfo.Protect);
|
||||
}
|
||||
}
|
||||
}
|
||||
//add new breakpoint
|
||||
|
|
@ -479,6 +497,7 @@ __declspec(dllexport) bool TITCALL SetMemoryBPXEx(ULONG_PTR MemoryStart, SIZE_T
|
|||
NewBreakPoint.BreakPointAddress = MemoryStart;
|
||||
NewBreakPoint.BreakPointType = BreakPointType;
|
||||
NewBreakPoint.BreakPointSize = SizeOfMemory;
|
||||
NewBreakPoint.OldProtect = OldProtect;
|
||||
NewBreakPoint.MemoryBpxRestoreOnHit = (BYTE)RestoreOnHit;
|
||||
NewBreakPoint.ExecuteCallBack = (ULONG_PTR)bpxCallBack;
|
||||
BreakPointBuffer.push_back(NewBreakPoint);
|
||||
|
|
@ -508,25 +527,45 @@ __declspec(dllexport) bool TITCALL RemoveMemoryBPX(ULONG_PTR MemoryStart, SIZE_T
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(found == -1) //not found
|
||||
return false;
|
||||
|
||||
if(!SizeOfMemory)
|
||||
SizeOfMemory = BreakPointBuffer.at(found).BreakPointSize;
|
||||
//remove PAGE_GUARD from all the pages in the range
|
||||
|
||||
// Revert to the original permission on all the pages in the range
|
||||
size_t pages = SizeOfMemory / TITANENGINE_PAGESIZE;
|
||||
|
||||
for(size_t i = 0; i < pages; i++)
|
||||
{
|
||||
const LPVOID curPage = (LPVOID)(MemoryStart + i * TITANENGINE_PAGESIZE);
|
||||
|
||||
VirtualQueryEx(dbgProcessInformation.hProcess, curPage, &MemInfo, sizeof(MEMORY_BASIC_INFORMATION));
|
||||
DWORD OldProtect = MemInfo.Protect;
|
||||
if((OldProtect & PAGE_GUARD))
|
||||
|
||||
// Check if the alternative memory breakpoint method is being used
|
||||
if (engineMembpAlt)
|
||||
{
|
||||
DWORD NewProtect = OldProtect ^ PAGE_GUARD;
|
||||
VirtualProtectEx(dbgProcessInformation.hProcess, curPage, TITANENGINE_PAGESIZE, NewProtect, &OldProtect);
|
||||
if(MemInfo.Protect & PAGE_NOACCESS)
|
||||
{
|
||||
VirtualProtectEx(dbgProcessInformation.hProcess, curPage, TITANENGINE_PAGESIZE,
|
||||
BreakPointBuffer.at(found).OldProtect, &MemInfo.Protect);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(MemInfo.Protect & PAGE_GUARD)
|
||||
{
|
||||
DWORD NewProtect = MemInfo.Protect ^ PAGE_GUARD;
|
||||
|
||||
VirtualProtectEx(dbgProcessInformation.hProcess, curPage, TITANENGINE_PAGESIZE, NewProtect, &MemInfo.Protect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//remove breakpoint from list
|
||||
BreakPointBuffer.erase(BreakPointBuffer.begin() + found);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -627,10 +627,36 @@ __declspec(dllexport) void TITCALL DebugLoop()
|
|||
if(ResetMemBPX) //restore memory breakpoint
|
||||
{
|
||||
ResetMemBPX = false;
|
||||
VirtualQueryEx(dbgProcessInformation.hProcess, (LPCVOID)ResetMemBPXAddress, &MemInfo, sizeof MEMORY_BASIC_INFORMATION);
|
||||
OldProtect = MemInfo.Protect;
|
||||
NewProtect = OldProtect | PAGE_GUARD; //guard page protection
|
||||
VirtualProtectEx(dbgProcessInformation.hProcess, (LPVOID)ResetMemBPXAddress, ResetMemBPXSize, NewProtect, &OldProtect);
|
||||
|
||||
// Check if the alternative memory breakpoint method should be used
|
||||
if (engineMembpAlt)
|
||||
{
|
||||
// Check if the breakpoint is still enabled/present and has not been removed
|
||||
for(int i = 0; i < BreakPointBuffer.size(); i++)
|
||||
{
|
||||
if (BreakPointBuffer.at(i).BreakPointAddress == ResetMemBPXAddress &&
|
||||
(BreakPointBuffer.at(i).BreakPointType == UE_MEMORY ||
|
||||
BreakPointBuffer.at(i).BreakPointType == UE_MEMORY_READ ||
|
||||
BreakPointBuffer.at(i).BreakPointType == UE_MEMORY_WRITE ||
|
||||
BreakPointBuffer.at(i).BreakPointType == UE_MEMORY_EXECUTE) &&
|
||||
BreakPointBuffer.at(i).BreakPointActive == UE_BPXACTIVE)
|
||||
{
|
||||
// Restore the breakpoint
|
||||
VirtualProtectEx(dbgProcessInformation.hProcess, (LPVOID)ResetMemBPXAddress,
|
||||
ResetMemBPXSize, PAGE_NOACCESS, &OldProtect);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
VirtualQueryEx(dbgProcessInformation.hProcess, (LPCVOID)ResetMemBPXAddress, &MemInfo, sizeof(MEMORY_BASIC_INFORMATION));
|
||||
OldProtect = MemInfo.Protect;
|
||||
NewProtect = OldProtect | PAGE_GUARD; //guard page protection
|
||||
VirtualProtectEx(dbgProcessInformation.hProcess, (LPVOID)ResetMemBPXAddress, ResetMemBPXSize, NewProtect, &OldProtect);
|
||||
}
|
||||
|
||||
if(engineStepActive)
|
||||
{
|
||||
if(engineStepCount == 0)
|
||||
|
|
@ -972,10 +998,182 @@ __declspec(dllexport) void TITCALL DebugLoop()
|
|||
|
||||
case STATUS_ACCESS_VIOLATION:
|
||||
{
|
||||
if(DBGCustomHandler->chAccessViolation != NULL)
|
||||
ULONG_PTR bpaddr;
|
||||
bool bFoundBreakPoint = false;
|
||||
bool bCallCustomHandler = false;
|
||||
BreakPointDetail FoundBreakPoint;
|
||||
int bpcount = (int)BreakPointBuffer.size();
|
||||
|
||||
for(int i = 0; i < bpcount; i++)
|
||||
{
|
||||
myCustomHandler = (fCustomHandler)((LPVOID)DBGCustomHandler->chAccessViolation);
|
||||
myCustomHandler(&DBGEvent.u.Exception.ExceptionRecord);
|
||||
ULONG_PTR addr = BreakPointBuffer.at(i).BreakPointAddress;
|
||||
bpaddr = (ULONG_PTR)DBGEvent.u.Exception.ExceptionRecord.ExceptionInformation[1]; //page accessed
|
||||
if(bpaddr >= addr && bpaddr < (addr + BreakPointBuffer.at(i).BreakPointSize) &&
|
||||
(BreakPointBuffer.at(i).BreakPointType == UE_MEMORY ||
|
||||
BreakPointBuffer.at(i).BreakPointType == UE_MEMORY_READ ||
|
||||
BreakPointBuffer.at(i).BreakPointType == UE_MEMORY_WRITE ||
|
||||
BreakPointBuffer.at(i).BreakPointType == UE_MEMORY_EXECUTE) &&
|
||||
BreakPointBuffer.at(i).BreakPointActive == UE_BPXACTIVE)
|
||||
{
|
||||
FoundBreakPoint = BreakPointBuffer.at(i);
|
||||
bFoundBreakPoint = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Most of the logic has been copied from the STATUS_GUARD_PAGE_VIOLATION handler
|
||||
|
||||
if(bFoundBreakPoint && engineMembpAlt) //found memory breakpoint
|
||||
{
|
||||
hActiveThread = EngineOpenThread(THREAD_GETSETSUSPEND, false, DBGEvent.dwThreadId);
|
||||
myDBGContext.ContextFlags = CONTEXT_CONTROL;
|
||||
GetThreadContext(hActiveThread, &myDBGContext);
|
||||
DBGCode = DBG_CONTINUE; //debugger handled the exception
|
||||
MemoryBpxCallBack = FoundBreakPoint.ExecuteCallBack;
|
||||
|
||||
if(FoundBreakPoint.BreakPointType == UE_MEMORY) //READ|WRITE|EXECUTE
|
||||
{
|
||||
if(FoundBreakPoint.MemoryBpxRestoreOnHit != 1)
|
||||
{
|
||||
RemoveMemoryBPX(FoundBreakPoint.BreakPointAddress, FoundBreakPoint.BreakPointSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
myDBGContext.EFlags |= UE_TRAP_FLAG;
|
||||
SetThreadContext(hActiveThread, &myDBGContext);
|
||||
ResetMemBPXAddress = FoundBreakPoint.BreakPointAddress;
|
||||
ResetMemBPXSize = FoundBreakPoint.BreakPointSize;
|
||||
ResetMemBPX = true;
|
||||
}
|
||||
|
||||
bCallCustomHandler = true;
|
||||
}
|
||||
else if(FoundBreakPoint.BreakPointType == UE_MEMORY_READ) //READ
|
||||
{
|
||||
if(FoundBreakPoint.MemoryBpxRestoreOnHit != 1) //do not restore the memory breakpoint
|
||||
{
|
||||
if(DBGEvent.u.Exception.ExceptionRecord.ExceptionInformation[0] == 0) //read operation
|
||||
RemoveMemoryBPX(FoundBreakPoint.BreakPointAddress, FoundBreakPoint.BreakPointSize);
|
||||
}
|
||||
else //restore the memory breakpoint
|
||||
{
|
||||
myDBGContext.EFlags |= UE_TRAP_FLAG;
|
||||
SetThreadContext(hActiveThread, &myDBGContext);
|
||||
ResetMemBPXAddress = FoundBreakPoint.BreakPointAddress;
|
||||
ResetMemBPXSize = FoundBreakPoint.BreakPointSize;
|
||||
ResetMemBPX = true;
|
||||
}
|
||||
if(DBGEvent.u.Exception.ExceptionRecord.ExceptionInformation[0] == 0) //read operation
|
||||
{
|
||||
bCallCustomHandler = true;
|
||||
}
|
||||
else //no read operation, restore breakpoint
|
||||
{
|
||||
myDBGContext.EFlags |= UE_TRAP_FLAG;
|
||||
SetThreadContext(hActiveThread, &myDBGContext);
|
||||
ResetMemBPXAddress = FoundBreakPoint.BreakPointAddress;
|
||||
ResetMemBPXSize = FoundBreakPoint.BreakPointSize;
|
||||
ResetMemBPX = true;
|
||||
}
|
||||
}
|
||||
else if(FoundBreakPoint.BreakPointType == UE_MEMORY_WRITE) //WRITE
|
||||
{
|
||||
if(FoundBreakPoint.MemoryBpxRestoreOnHit != 1) //remove breakpoint
|
||||
{
|
||||
if(DBGEvent.u.Exception.ExceptionRecord.ExceptionInformation[0] == 1) //write operation
|
||||
RemoveMemoryBPX(FoundBreakPoint.BreakPointAddress, FoundBreakPoint.BreakPointSize);
|
||||
}
|
||||
else //restore breakpoint after trap flag
|
||||
{
|
||||
myDBGContext.EFlags |= UE_TRAP_FLAG;
|
||||
SetThreadContext(hActiveThread, &myDBGContext);
|
||||
ResetMemBPXAddress = FoundBreakPoint.BreakPointAddress;
|
||||
ResetMemBPXSize = FoundBreakPoint.BreakPointSize;
|
||||
ResetMemBPX = true;
|
||||
}
|
||||
if(DBGEvent.u.Exception.ExceptionRecord.ExceptionInformation[0] == 1) //write operation
|
||||
{
|
||||
bCallCustomHandler = true;
|
||||
}
|
||||
else //no write operation, restore breakpoint
|
||||
{
|
||||
myDBGContext.EFlags |= UE_TRAP_FLAG;
|
||||
SetThreadContext(hActiveThread, &myDBGContext);
|
||||
ResetMemBPXAddress = FoundBreakPoint.BreakPointAddress;
|
||||
ResetMemBPXSize = FoundBreakPoint.BreakPointSize;
|
||||
ResetMemBPX = true;
|
||||
}
|
||||
}
|
||||
else if(FoundBreakPoint.BreakPointType == UE_MEMORY_EXECUTE) //EXECUTE
|
||||
{
|
||||
if(FoundBreakPoint.MemoryBpxRestoreOnHit != 1)
|
||||
{
|
||||
if((DBGEvent.u.Exception.ExceptionRecord.ExceptionInformation[0] == 8 || DBGEvent.u.Exception.ExceptionRecord.ExceptionInformation[0] == 0) && //data execution prevention (DEP) violation
|
||||
(ULONG_PTR)DBGEvent.u.Exception.ExceptionRecord.ExceptionAddress == DBGEvent.u.Exception.ExceptionRecord.ExceptionInformation[1]) //exception address == read address
|
||||
RemoveMemoryBPX(FoundBreakPoint.BreakPointAddress, FoundBreakPoint.BreakPointSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
myDBGContext.EFlags |= UE_TRAP_FLAG;
|
||||
SetThreadContext(hActiveThread, &myDBGContext);
|
||||
ResetMemBPXAddress = FoundBreakPoint.BreakPointAddress;
|
||||
ResetMemBPXSize = FoundBreakPoint.BreakPointSize;
|
||||
ResetMemBPX = true;
|
||||
}
|
||||
if((DBGEvent.u.Exception.ExceptionRecord.ExceptionInformation[0] == 8 || DBGEvent.u.Exception.ExceptionRecord.ExceptionInformation[0] == 0) && //data execution prevention (DEP) violation
|
||||
(ULONG_PTR)DBGEvent.u.Exception.ExceptionRecord.ExceptionAddress == DBGEvent.u.Exception.ExceptionRecord.ExceptionInformation[1]) //exception address == read address
|
||||
{
|
||||
bCallCustomHandler = true;
|
||||
}
|
||||
else //no execute operation, restore breakpoint
|
||||
{
|
||||
myDBGContext.EFlags |= UE_TRAP_FLAG;
|
||||
SetThreadContext(hActiveThread, &myDBGContext);
|
||||
ResetMemBPXAddress = FoundBreakPoint.BreakPointAddress;
|
||||
ResetMemBPXSize = FoundBreakPoint.BreakPointSize;
|
||||
ResetMemBPX = true;
|
||||
}
|
||||
}
|
||||
|
||||
// If the breakpoint has to be restored...
|
||||
if (ResetMemBPX)
|
||||
{
|
||||
// ...temporarily revert the PAGE_NOACCESS permission
|
||||
VirtualProtectEx(dbgProcessInformation.hProcess, (LPVOID)ResetMemBPXAddress,
|
||||
ResetMemBPXSize, FoundBreakPoint.OldProtect, &OldProtect);
|
||||
}
|
||||
|
||||
// Call the custom memory breakpoint handler
|
||||
if (bCallCustomHandler)
|
||||
{
|
||||
myCustomHandler = (fCustomHandler)(MemoryBpxCallBack);
|
||||
myCustomHandler((void*)bpaddr);
|
||||
}
|
||||
|
||||
EngineCloseHandle(hActiveThread);
|
||||
}
|
||||
else //no memory breakpoint found
|
||||
{
|
||||
DBGCode = DBG_EXCEPTION_NOT_HANDLED;
|
||||
}
|
||||
if(ResetMemBPX) //memory breakpoint hit
|
||||
{
|
||||
ULONG_PTR ueCurrentPosition = GetContextData(UE_CIP);
|
||||
unsigned char instr[16];
|
||||
MemoryReadSafe(dbgProcessInformation.hProcess, (void*)ueCurrentPosition, instr, sizeof(instr), 0);
|
||||
char* DisassembledString = (char*)StaticDisassembleEx(ueCurrentPosition, (LPVOID)instr);
|
||||
if(strstr(DisassembledString, "PUSHF"))
|
||||
PushfBPX = true;
|
||||
}
|
||||
|
||||
// Debuggee generated access violation exception
|
||||
if(DBGCode == DBG_EXCEPTION_NOT_HANDLED)
|
||||
{
|
||||
if(DBGCustomHandler->chAccessViolation != NULL)
|
||||
{
|
||||
myCustomHandler = (fCustomHandler)((LPVOID)DBGCustomHandler->chAccessViolation);
|
||||
myCustomHandler(&DBGEvent.u.Exception.ExceptionRecord);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -48,6 +48,10 @@ __declspec(dllexport) void TITCALL SetEngineVariable(DWORD VariableId, bool Vari
|
|||
{
|
||||
engineSafeAttach = VariableSet;
|
||||
}
|
||||
else if(VariableId == UE_ENGINE_MEMBP_ALT)
|
||||
{
|
||||
engineMembpAlt = VariableSet;
|
||||
}
|
||||
}
|
||||
|
||||
__declspec(dllexport) bool TITCALL EngineCreateMissingDependencies(char* szFileName, char* szOutputFolder, bool LogCreatedFiles)
|
||||
|
|
|
|||
|
|
@ -245,6 +245,7 @@ typedef struct
|
|||
int AdvancedBreakPointType;
|
||||
int MemoryBpxRestoreOnHit;
|
||||
ULONG_PTR ExecuteCallBack;
|
||||
DWORD OldProtect;
|
||||
} BreakPointDetail, *PBreakPointDetail;
|
||||
|
||||
typedef struct
|
||||
|
|
@ -470,6 +471,7 @@ typedef struct HOOK_ENTRY
|
|||
#define UE_ENGINE_CALL_PLUGIN_DEBUG_CALLBACK 8
|
||||
#define UE_ENGINE_SET_DEBUG_PRIVILEGE 9
|
||||
#define UE_ENGINE_SAFE_ATTACH 10
|
||||
#define UE_ENGINE_MEMBP_ALT 11
|
||||
|
||||
#define UE_OPTION_REMOVEALL 1
|
||||
#define UE_OPTION_DISABLEALL 2
|
||||
|
|
|
|||
Loading…
Reference in New Issue