diff --git a/SDK/C/TitanEngine.h b/SDK/C/TitanEngine.h index 9c0a2b4..7b181cf 100644 --- a/SDK/C/TitanEngine.h +++ b/SDK/C/TitanEngine.h @@ -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 diff --git a/SDK/CPP/TitanEngine.h b/SDK/CPP/TitanEngine.h index 73a3052..f5fa595 100644 --- a/SDK/CPP/TitanEngine.h +++ b/SDK/CPP/TitanEngine.h @@ -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; diff --git a/SDK/CPP/TitanEngine.hpp b/SDK/CPP/TitanEngine.hpp index 1c56a53..e4ae837 100644 --- a/SDK/CPP/TitanEngine.hpp +++ b/SDK/CPP/TitanEngine.hpp @@ -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 diff --git a/TitanEngine/Global.Engine.cpp b/TitanEngine/Global.Engine.cpp index 1c936b4..eb62543 100644 --- a/TitanEngine/Global.Engine.cpp +++ b/TitanEngine/Global.Engine.cpp @@ -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}; diff --git a/TitanEngine/Global.Engine.h b/TitanEngine/Global.Engine.h index 3904818..a769fd3 100644 --- a/TitanEngine/Global.Engine.h +++ b/TitanEngine/Global.Engine.h @@ -22,6 +22,7 @@ extern bool engineExecutePluginCallBack; extern bool engineAutoHideFromDebugger; extern bool engineEnableDebugPrivilege; extern bool engineSafeAttach; +extern bool engineMembpAlt; //Global.Engine.Functions void EngineInit(); diff --git a/TitanEngine/TitanEngine.Breakpoints.cpp b/TitanEngine/TitanEngine.Breakpoints.cpp index f4ca10e..e285a82 100644 --- a/TitanEngine/TitanEngine.Breakpoints.cpp +++ b/TitanEngine/TitanEngine.Breakpoints.cpp @@ -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; } diff --git a/TitanEngine/TitanEngine.Debugger.DebugLoop.cpp b/TitanEngine/TitanEngine.Debugger.DebugLoop.cpp index 8b0864c..e41c68b 100644 --- a/TitanEngine/TitanEngine.Debugger.DebugLoop.cpp +++ b/TitanEngine/TitanEngine.Debugger.DebugLoop.cpp @@ -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; diff --git a/TitanEngine/TitanEngine.Engine.cpp b/TitanEngine/TitanEngine.Engine.cpp index 6305a20..8180a84 100644 --- a/TitanEngine/TitanEngine.Engine.cpp +++ b/TitanEngine/TitanEngine.Engine.cpp @@ -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) diff --git a/TitanEngine/stdafx.h b/TitanEngine/stdafx.h index 24d921a..50e18da 100644 --- a/TitanEngine/stdafx.h +++ b/TitanEngine/stdafx.h @@ -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