Added an alternative memory breakpoint method that uses PAGE_NOACCESS instead of PAGE_GUARD

This commit is contained in:
the_janitor 2021-08-31 02:34:31 +02:00
parent 8d93135f38
commit 18a885a703
9 changed files with 264 additions and 16 deletions

View File

@ -57,6 +57,7 @@
#define UE_ENGINE_CALL_PLUGIN_DEBUG_CALLBACK 8 #define UE_ENGINE_CALL_PLUGIN_DEBUG_CALLBACK 8
#define UE_ENGINE_SET_DEBUG_PRIVILEGE 9 #define UE_ENGINE_SET_DEBUG_PRIVILEGE 9
#define UE_ENGINE_SAFE_ATTACH 10 #define UE_ENGINE_SAFE_ATTACH 10
#define UE_ENGINE_MEMBP_ALT 11
#define UE_OPTION_REMOVEALL 1 #define UE_OPTION_REMOVEALL 1
#define UE_OPTION_DISABLEALL 2 #define UE_OPTION_DISABLEALL 2

View File

@ -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_CALL_PLUGIN_DEBUG_CALLBACK = 8;
const BYTE UE_ENGINE_SET_DEBUG_PRIVILEGE = 9; const BYTE UE_ENGINE_SET_DEBUG_PRIVILEGE = 9;
const BYTE UE_ENGINE_SAFE_ATTACH = 10; const BYTE UE_ENGINE_SAFE_ATTACH = 10;
const BYTE UE_ENGINE_MEMBP_ALT = 11;
const BYTE UE_OPTION_REMOVEALL = 1; const BYTE UE_OPTION_REMOVEALL = 1;
const BYTE UE_OPTION_DISABLEALL = 2; const BYTE UE_OPTION_DISABLEALL = 2;

View File

@ -73,6 +73,7 @@ enum eEngineVariable : DWORD
UE_ENGINE_CALL_PLUGIN_DEBUG_CALLBACK = UE::UE_ENGINE_CALL_PLUGIN_DEBUG_CALLBACK, 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_SET_DEBUG_PRIVILEGE = UE::UE_ENGINE_SET_DEBUG_PRIVILEGE,
UE_ENGINE_SAFE_ATTACH = UE::UE_ENGINE_SAFE_ATTACH, UE_ENGINE_SAFE_ATTACH = UE::UE_ENGINE_SAFE_ATTACH,
UE_ENGINE_MEMBP_ALT = UE::UE_ENGINE_MEMBP_ALT;
}; };
enum eBPRemoveOption : DWORD enum eBPRemoveOption : DWORD

View File

@ -19,6 +19,7 @@ bool engineExecutePluginCallBack = true;
bool engineAutoHideFromDebugger = false; // hardcoded bool engineAutoHideFromDebugger = false; // hardcoded
bool engineEnableDebugPrivilege = false; bool engineEnableDebugPrivilege = false;
bool engineSafeAttach = false; bool engineSafeAttach = false;
bool engineMembpAlt = false;
char engineFoundDLLName[512] = {0}; char engineFoundDLLName[512] = {0};
char engineFoundAPIName[512] = {0}; char engineFoundAPIName[512] = {0};

View File

@ -22,6 +22,7 @@ extern bool engineExecutePluginCallBack;
extern bool engineAutoHideFromDebugger; extern bool engineAutoHideFromDebugger;
extern bool engineEnableDebugPrivilege; extern bool engineEnableDebugPrivilege;
extern bool engineSafeAttach; extern bool engineSafeAttach;
extern bool engineMembpAlt;
//Global.Engine.Functions //Global.Engine.Functions
void EngineInit(); void EngineInit();

View File

@ -446,6 +446,7 @@ __declspec(dllexport) bool TITCALL SetMemoryBPXEx(ULONG_PTR MemoryStart, SIZE_T
MEMORY_BASIC_INFORMATION MemInfo; MEMORY_BASIC_INFORMATION MemInfo;
ULONG_PTR NumberOfBytesReadWritten = 0; ULONG_PTR NumberOfBytesReadWritten = 0;
int bpcount = (int)BreakPointBuffer.size(); int bpcount = (int)BreakPointBuffer.size();
DWORD OldProtect = 0;
//search for breakpoint //search for breakpoint
for(int i = 0; i < bpcount; i++) 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 //set PAGE_GUARD on all the pages separately
size_t pages = SizeOfMemory / TITANENGINE_PAGESIZE; size_t pages = SizeOfMemory / TITANENGINE_PAGESIZE;
for(size_t i = 0; i < pages; i++) for(size_t i = 0; i < pages; i++)
{ {
const LPVOID curPage = (LPVOID)(MemoryStart + i * TITANENGINE_PAGESIZE); const LPVOID curPage = (LPVOID)(MemoryStart + i * TITANENGINE_PAGESIZE);
VirtualQueryEx(dbgProcessInformation.hProcess, curPage, &MemInfo, sizeof(MEMORY_BASIC_INFORMATION)); 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; if(!(MemInfo.Protect & PAGE_NOACCESS))
VirtualProtectEx(dbgProcessInformation.hProcess, curPage, TITANENGINE_PAGESIZE, NewProtect, &OldProtect); {
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 //add new breakpoint
@ -479,6 +497,7 @@ __declspec(dllexport) bool TITCALL SetMemoryBPXEx(ULONG_PTR MemoryStart, SIZE_T
NewBreakPoint.BreakPointAddress = MemoryStart; NewBreakPoint.BreakPointAddress = MemoryStart;
NewBreakPoint.BreakPointType = BreakPointType; NewBreakPoint.BreakPointType = BreakPointType;
NewBreakPoint.BreakPointSize = SizeOfMemory; NewBreakPoint.BreakPointSize = SizeOfMemory;
NewBreakPoint.OldProtect = OldProtect;
NewBreakPoint.MemoryBpxRestoreOnHit = (BYTE)RestoreOnHit; NewBreakPoint.MemoryBpxRestoreOnHit = (BYTE)RestoreOnHit;
NewBreakPoint.ExecuteCallBack = (ULONG_PTR)bpxCallBack; NewBreakPoint.ExecuteCallBack = (ULONG_PTR)bpxCallBack;
BreakPointBuffer.push_back(NewBreakPoint); BreakPointBuffer.push_back(NewBreakPoint);
@ -508,25 +527,45 @@ __declspec(dllexport) bool TITCALL RemoveMemoryBPX(ULONG_PTR MemoryStart, SIZE_T
break; break;
} }
} }
if(found == -1) //not found if(found == -1) //not found
return false; return false;
if(!SizeOfMemory) if(!SizeOfMemory)
SizeOfMemory = BreakPointBuffer.at(found).BreakPointSize; 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; size_t pages = SizeOfMemory / TITANENGINE_PAGESIZE;
for(size_t i = 0; i < pages; i++) for(size_t i = 0; i < pages; i++)
{ {
const LPVOID curPage = (LPVOID)(MemoryStart + i * TITANENGINE_PAGESIZE); const LPVOID curPage = (LPVOID)(MemoryStart + i * TITANENGINE_PAGESIZE);
VirtualQueryEx(dbgProcessInformation.hProcess, curPage, &MemInfo, sizeof(MEMORY_BASIC_INFORMATION)); 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; if(MemInfo.Protect & PAGE_NOACCESS)
VirtualProtectEx(dbgProcessInformation.hProcess, curPage, TITANENGINE_PAGESIZE, NewProtect, &OldProtect); {
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 //remove breakpoint from list
BreakPointBuffer.erase(BreakPointBuffer.begin() + found); BreakPointBuffer.erase(BreakPointBuffer.begin() + found);
return true; return true;
} }

View File

@ -627,10 +627,36 @@ __declspec(dllexport) void TITCALL DebugLoop()
if(ResetMemBPX) //restore memory breakpoint if(ResetMemBPX) //restore memory breakpoint
{ {
ResetMemBPX = false; ResetMemBPX = false;
VirtualQueryEx(dbgProcessInformation.hProcess, (LPCVOID)ResetMemBPXAddress, &MemInfo, sizeof MEMORY_BASIC_INFORMATION);
OldProtect = MemInfo.Protect; // Check if the alternative memory breakpoint method should be used
NewProtect = OldProtect | PAGE_GUARD; //guard page protection if (engineMembpAlt)
VirtualProtectEx(dbgProcessInformation.hProcess, (LPVOID)ResetMemBPXAddress, ResetMemBPXSize, NewProtect, &OldProtect); {
// 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(engineStepActive)
{ {
if(engineStepCount == 0) if(engineStepCount == 0)
@ -972,10 +998,182 @@ __declspec(dllexport) void TITCALL DebugLoop()
case STATUS_ACCESS_VIOLATION: 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); ULONG_PTR addr = BreakPointBuffer.at(i).BreakPointAddress;
myCustomHandler(&DBGEvent.u.Exception.ExceptionRecord); 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; break;

View File

@ -48,6 +48,10 @@ __declspec(dllexport) void TITCALL SetEngineVariable(DWORD VariableId, bool Vari
{ {
engineSafeAttach = VariableSet; engineSafeAttach = VariableSet;
} }
else if(VariableId == UE_ENGINE_MEMBP_ALT)
{
engineMembpAlt = VariableSet;
}
} }
__declspec(dllexport) bool TITCALL EngineCreateMissingDependencies(char* szFileName, char* szOutputFolder, bool LogCreatedFiles) __declspec(dllexport) bool TITCALL EngineCreateMissingDependencies(char* szFileName, char* szOutputFolder, bool LogCreatedFiles)

View File

@ -245,6 +245,7 @@ typedef struct
int AdvancedBreakPointType; int AdvancedBreakPointType;
int MemoryBpxRestoreOnHit; int MemoryBpxRestoreOnHit;
ULONG_PTR ExecuteCallBack; ULONG_PTR ExecuteCallBack;
DWORD OldProtect;
} BreakPointDetail, *PBreakPointDetail; } BreakPointDetail, *PBreakPointDetail;
typedef struct typedef struct
@ -470,6 +471,7 @@ typedef struct HOOK_ENTRY
#define UE_ENGINE_CALL_PLUGIN_DEBUG_CALLBACK 8 #define UE_ENGINE_CALL_PLUGIN_DEBUG_CALLBACK 8
#define UE_ENGINE_SET_DEBUG_PRIVILEGE 9 #define UE_ENGINE_SET_DEBUG_PRIVILEGE 9
#define UE_ENGINE_SAFE_ATTACH 10 #define UE_ENGINE_SAFE_ATTACH 10
#define UE_ENGINE_MEMBP_ALT 11
#define UE_OPTION_REMOVEALL 1 #define UE_OPTION_REMOVEALL 1
#define UE_OPTION_DISABLEALL 2 #define UE_OPTION_DISABLEALL 2