diff --git a/SDK/C/TitanEngine.h b/SDK/C/TitanEngine.h index d65ec8b..9c0a2b4 100644 --- a/SDK/C/TitanEngine.h +++ b/SDK/C/TitanEngine.h @@ -56,6 +56,7 @@ #define UE_ENGINE_RESET_CUSTOM_HANDLER 7 #define UE_ENGINE_CALL_PLUGIN_DEBUG_CALLBACK 8 #define UE_ENGINE_SET_DEBUG_PRIVILEGE 9 +#define UE_ENGINE_SAFE_ATTACH 10 #define UE_OPTION_REMOVEALL 1 #define UE_OPTION_DISABLEALL 2 diff --git a/SDK/CPP/TitanEngine.h b/SDK/CPP/TitanEngine.h index fbd9cf6..73a3052 100644 --- a/SDK/CPP/TitanEngine.h +++ b/SDK/CPP/TitanEngine.h @@ -52,6 +52,7 @@ const BYTE UE_ENGINE_CALL_PLUGIN_CALLBACK = 6; 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_OPTION_REMOVEALL = 1; const BYTE UE_OPTION_DISABLEALL = 2; diff --git a/SDK/CPP/TitanEngine.hpp b/SDK/CPP/TitanEngine.hpp index 2538de8..1c56a53 100644 --- a/SDK/CPP/TitanEngine.hpp +++ b/SDK/CPP/TitanEngine.hpp @@ -71,7 +71,8 @@ enum eEngineVariable : DWORD UE_ENGINE_CALL_PLUGIN_CALLBACK = UE::UE_ENGINE_CALL_PLUGIN_CALLBACK, UE_ENGINE_RESET_CUSTOM_HANDLER = UE::UE_ENGINE_RESET_CUSTOM_HANDLER, 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, }; enum eBPRemoveOption : DWORD diff --git a/SDK/Delphi/TitanEngine.pas b/SDK/Delphi/TitanEngine.pas index bf73377..7378b41 100644 --- a/SDK/Delphi/TitanEngine.pas +++ b/SDK/Delphi/TitanEngine.pas @@ -336,7 +336,8 @@ const UE_ENGINE_CALL_PLUGIN_CALLBACK = 6; UE_ENGINE_RESET_CUSTOM_HANDLER = 7; UE_ENGINE_CALL_PLUGIN_DEBUG_CALLBACK = 8; - UE_ENGINE_SET_DEBUG_PRIVILEGE = 9; + UE_ENGINE_SET_DEBUG_PRIVILEGE = 9; + UE_ENGINE_SAFE_ATTACH = 10; UE_OPTION_REMOVEALL = 1; UE_OPTION_DISABLEALL = 2; diff --git a/SDK/LUA/TitanEngine.lua b/SDK/LUA/TitanEngine.lua index 351fdca..4faaad8 100644 --- a/SDK/LUA/TitanEngine.lua +++ b/SDK/LUA/TitanEngine.lua @@ -37,6 +37,7 @@ UE_ENGINE_CALL_PLUGIN_CALLBACK = 6 UE_ENGINE_RESET_CUSTOM_HANDLER = 7 UE_ENGINE_CALL_PLUGIN_DEBUG_CALLBACK = 8 UE_ENGINE_SET_DEBUG_PRIVILEGE = 9 +UE_ENGINE_SAFE_ATTACH = 10 UE_OPTION_REMOVEALL = 1 UE_OPTION_DISABLEALL = 2 diff --git a/SDK/MASM/TitanEngine.INC b/SDK/MASM/TitanEngine.INC index 1330db8..0479dcd 100644 --- a/SDK/MASM/TitanEngine.INC +++ b/SDK/MASM/TitanEngine.INC @@ -23,6 +23,7 @@ UE_ENGINE_BACKUP_FOR_CRITICAL_FUNCTIONS EQU 5 UE_ENGINE_CALL_PLUGIN_CALLBACK EQU 6 UE_ENGINE_RESET_CUSTOM_HANDLER EQU 7 UE_ENGINE_CALL_PLUGIN_DEBUG_CALLBACK EQU 8 +UE_ENGINE_SAFE_ATTACH EQU 10 UE_ENGINE_SET_DEBUG_PRIVILEGE EQU 9 UE_OPTION_REMOVEALL EQU 1 UE_OPTION_DISABLEALL EQU 2 diff --git a/SDK/Python/TitanEngine.py b/SDK/Python/TitanEngine.py index ea13188..cf7139b 100644 --- a/SDK/Python/TitanEngine.py +++ b/SDK/Python/TitanEngine.py @@ -28,6 +28,7 @@ UE_ENGINE_CALL_PLUGIN_CALLBACK = 6 UE_ENGINE_RESET_CUSTOM_HANDLER = 7 UE_ENGINE_CALL_PLUGIN_DEBUG_CALLBACK = 8 UE_ENGINE_SET_DEBUG_PRIVILEGE = 9 +UE_ENGINE_SAFE_ATTACH = 10 UE_OPTION_REMOVEALL = 1 UE_OPTION_DISABLEALL = 2 diff --git a/TitanEngine/Global.Debugger.cpp b/TitanEngine/Global.Debugger.cpp index 047d388..765e1ea 100644 --- a/TitanEngine/Global.Debugger.cpp +++ b/TitanEngine/Global.Debugger.cpp @@ -93,3 +93,141 @@ void StepOutStepCallBack() else StepOver(StepOutStepCallBack); } + +static DWORD BaseSetLastNTError(IN NTSTATUS Status) +{ + DWORD dwErrCode; + dwErrCode = RtlNtStatusToDosError(Status); + SetLastError(dwErrCode); + return dwErrCode; +} + +static HANDLE WINAPI ProcessIdToHandle(IN DWORD dwProcessId) +{ + NTSTATUS Status; + OBJECT_ATTRIBUTES ObjectAttributes; + HANDLE Handle; + CLIENT_ID ClientId; + + /* If we don't have a PID, look it up */ + //if (dwProcessId == MAXDWORD) dwProcessId = (DWORD_PTR)CsrGetProcessId(); + + /* Open a handle to the process */ + ClientId.UniqueThread = NULL; + ClientId.UniqueProcess = UlongToHandle(dwProcessId); + InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL); + Status = NtOpenProcess(&Handle, + PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | + PROCESS_VM_WRITE | PROCESS_VM_READ | + PROCESS_SUSPEND_RESUME | PROCESS_QUERY_INFORMATION, + &ObjectAttributes, + &ClientId); + if(!NT_SUCCESS(Status)) + { + /* Fail */ + BaseSetLastNTError(Status); + return 0; + } + + /* Return the handle */ + return Handle; +} + +static NTSTATUS NTAPI DbgUiIssueRemoteBreakin_(IN HANDLE Process) +{ + HANDLE hThread; + CLIENT_ID ClientId; + NTSTATUS Status; + + PUSER_THREAD_START_ROUTINE RemoteBreakFunction = (PUSER_THREAD_START_ROUTINE)DbgUiRemoteBreakin; + + LPVOID RemoteMemory = VirtualAllocEx(Process, 0, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READ); + if(RemoteMemory) + { + SIZE_T written = 0; + unsigned char payload[] = { 0xCC, 0xC3 }; + if(WriteProcessMemory(Process, RemoteMemory, payload, sizeof(payload), &written)) + { + RemoteBreakFunction = (PUSER_THREAD_START_ROUTINE)RemoteMemory; + } + else + { + VirtualFreeEx(Process, RemoteMemory, 0, MEM_RELEASE); + } + } + + /* Create the thread that will do the breakin */ + Status = RtlCreateUserThread(Process, + NULL, + FALSE, + 0, + 0, + 0x1000 /* PAGE_SIZE */, + RemoteBreakFunction, + NULL, + &hThread, + &ClientId); + + /* Close the handle on success */ + if(NT_SUCCESS(Status)) NtClose(hThread); + + /* Return status */ + return Status; +} + +static NTSTATUS NTAPI DbgUiDebugActiveProcess_(IN HANDLE Process) +{ + NTSTATUS Status; + + /* Tell the kernel to start debugging */ + Status = NtDebugActiveProcess(Process, NtCurrentTeb()->DbgSsReserved[1]); + if(NT_SUCCESS(Status)) + { + /* Now break-in the process */ + Status = DbgUiIssueRemoteBreakin_(Process); + if(!NT_SUCCESS(Status)) + { + /* We couldn't break-in, cancel debugging */ + DbgUiStopDebugging(Process); + } + } + + /* Return status */ + return Status; +} + +// Source: https://github.com/mirror/reactos/blob/c6d2b35ffc91e09f50dfb214ea58237509329d6b/reactos/dll/win32/kernel32/client/debugger.c#L480 +BOOL WINAPI DebugActiveProcess_(IN DWORD dwProcessId) +{ + NTSTATUS Status, Status1; + HANDLE Handle; + + /* Connect to the debugger */ + Status = DbgUiConnectToDbg(); + if(!NT_SUCCESS(Status)) + { + BaseSetLastNTError(Status); + return FALSE; + } + + /* Get the process handle */ + Handle = ProcessIdToHandle(dwProcessId); + if(!Handle) return FALSE; + + /* Now debug the process */ + Status = DbgUiDebugActiveProcess_(Handle); + + /* Close the handle since we're done */ + Status1 = NtClose(Handle); + + /* Check if debugging worked */ + if(!NT_SUCCESS(Status)) + { + /* Fail */ + BaseSetLastNTError(Status); + return FALSE; + } + + /* Success */ + return TRUE; +} \ No newline at end of file diff --git a/TitanEngine/Global.Debugger.h b/TitanEngine/Global.Debugger.h index 99bf8a0..237272a 100644 --- a/TitanEngine/Global.Debugger.h +++ b/TitanEngine/Global.Debugger.h @@ -46,5 +46,6 @@ void DebuggerReset(); void ClearProcessList(); void ClearTlsCallBackList(); void StepOutStepCallBack(); +BOOL WINAPI DebugActiveProcess_(IN DWORD dwProcessId); #endif //_GLOBAL_DEBUGGER_H diff --git a/TitanEngine/Global.Engine.cpp b/TitanEngine/Global.Engine.cpp index caf12c4..85a6abe 100644 --- a/TitanEngine/Global.Engine.cpp +++ b/TitanEngine/Global.Engine.cpp @@ -18,6 +18,7 @@ bool enginePassAllExceptions = true; bool engineExecutePluginCallBack = true; bool engineAutoHideFromDebugger = false; // hardcoded bool engineEnableDebugPrivilege = false; +bool engineSafeAttach = false; char engineFoundDLLName[512] = {0}; char engineFoundAPIName[512] = {0}; diff --git a/TitanEngine/Global.Engine.h b/TitanEngine/Global.Engine.h index 8c9b368..3904818 100644 --- a/TitanEngine/Global.Engine.h +++ b/TitanEngine/Global.Engine.h @@ -21,6 +21,7 @@ extern bool enginePassAllExceptions; extern bool engineExecutePluginCallBack; extern bool engineAutoHideFromDebugger; extern bool engineEnableDebugPrivilege; +extern bool engineSafeAttach; //Global.Engine.Functions void EngineInit(); diff --git a/TitanEngine/TitanEngine.Debugger.cpp b/TitanEngine/TitanEngine.Debugger.cpp index 3dc4ecd..1231ed1 100644 --- a/TitanEngine/TitanEngine.Debugger.cpp +++ b/TitanEngine/TitanEngine.Debugger.cpp @@ -550,7 +550,7 @@ __declspec(dllexport) bool TITCALL AttachDebugger(DWORD ProcessId, bool KillOnEx EngineSetDebugPrivilege(GetCurrentProcess(), true); DebugRemoveDebugPrivilege = true; } - if(DebugActiveProcess(ProcessId)) + if((engineSafeAttach ? DebugActiveProcess_ : DebugActiveProcess)(ProcessId)) { if(engineEnableDebugPrivilege) EngineSetDebugPrivilege(GetCurrentProcess(), false); diff --git a/TitanEngine/TitanEngine.Engine.cpp b/TitanEngine/TitanEngine.Engine.cpp index 0272a9b..75945e4 100644 --- a/TitanEngine/TitanEngine.Engine.cpp +++ b/TitanEngine/TitanEngine.Engine.cpp @@ -42,6 +42,10 @@ __declspec(dllexport) void TITCALL SetEngineVariable(DWORD VariableId, bool Vari { engineEnableDebugPrivilege = VariableSet; } + else if(VariableId == UE_ENGINE_SAFE_ATTACH) + { + engineSafeAttach = VariableSet; + } } __declspec(dllexport) bool TITCALL EngineCreateMissingDependencies(char* szFileName, char* szOutputFolder, bool LogCreatedFiles) diff --git a/TitanEngine/stdafx.h b/TitanEngine/stdafx.h index 8008444..6cc319e 100644 --- a/TitanEngine/stdafx.h +++ b/TitanEngine/stdafx.h @@ -469,6 +469,7 @@ typedef struct HOOK_ENTRY #define UE_ENGINE_RESET_CUSTOM_HANDLER 7 #define UE_ENGINE_CALL_PLUGIN_DEBUG_CALLBACK 8 #define UE_ENGINE_SET_DEBUG_PRIVILEGE 9 +#define UE_ENGINE_SAFE_ATTACH 10 #define UE_OPTION_REMOVEALL 1 #define UE_OPTION_DISABLEALL 2