Files
GTASource/game/security/plugins/debuggercheckplugin.cpp
expvintl 419f2e4752 init
2025-02-23 17:40:52 +08:00

459 lines
12 KiB
C++

#include "security/ragesecengine.h"
#if USE_RAGESEC
#include "security/ragesecwinapi.h"
#include "net/status.h"
#include "net/task.h"
#include "security/ragesecplugin.h"
#include "security/ragesecenginetasks.h"
#include "system/bootmgr.h"
#include "security/plugins//debuggercheckplugin.h"
using namespace rage;
#if RAGE_SEC_TASK_BASIC_DEBUGGER_CHECK
#pragma warning( disable : 4100)
PARAM(enable_debugger_detection_tests, "N/A");
#define DEBUGGER_CHECK_POLLING_MS 60*1000
RAGE_DEFINE_SUBCHANNEL(net, debugger_check, DIAG_SEVERITY_DEBUG3)
#undef __net_channel
#define __net_channel net_debugger_check
#define DEBUGGER_CHECK_ID 0x9E2BC798
static unsigned int sm_ddTrashValue = 0;
enum Detections
{
DD_NONE = 0xB54CE0B3,
DD_ISDEBUGGER = 0xEB672B51, // Needs testing/Support
DD_ISDEBUGPEB = 0x40B85F02, // Needs testing
DD_TIMEDSTEPOVER = 0xE88A8F02,
DD_NTQUERYINF = 0x7C4989AA,
DD_NTGLOBALFLAGS = 0x8624A9A6, // Needs testing
DD_DEBUGREGISTERS = 0xEDA03F2F, // Needs testing/Support
DD_PROCESSDEBUGFLAGS = 0xDFD5974A, // Implemented
DD_PARENTPROCESS = 0x802F2597,
DD_SYSTEMKERNELDEBUGGERINFORMATION = 0x942B6D4D, // Implemented
DD_DEBUGOBJECTFOUND = 0xC95A8A84, // Implemented
DD_VEHBREAKPOINT = 0xF07A7FC3,
DD_IATHOOK = 0x2015F6E4,
DD_CLOSEHANDLE = 0xC96CB241, // Implemented
DD_INT3 = 0x11493BE8, // Implemented
DD_INT2C = 0x0153572B, // Implemented
DD_DEBUGACTIVEPROCESS = 0x59E8C2FB,
DD_HEAPFLAGS = 0x1D5D7268, // Implemented
DD_HEAPFORCEFLAGS = 0x5C21E644, // Implemented
DD_OUTPUTDEBUGSTRING = 0xC7999CC7,
DD_FINDWINDOWCLASS = 0x979E36B5,
DD_RAISE_RIP_EXCEPTION = 0xF5F55140, // Implemented
DD_ALL = 0xADC0EF8D
};
bool DebuggerCheckPlugin_BeingDebuggedPeb()
{
DWORD_PTR pPeb64 = __readgsqword(0x60);
if(pPeb64)
{
PBYTE BeingDebugged = (PBYTE)pPeb64 + 0x2;
if(*BeingDebugged)
{
return true;
}
}
return false;
}
bool DebuggerCheckPlugin_NtGlobalFlag()
{
DWORD_PTR pPeb64 = __readgsqword(0x60);
if (pPeb64){
PDWORD pNtGlobalFlag = (PDWORD)((PBYTE)pPeb64 + 0xBC);
if ((*pNtGlobalFlag & 0x70) == 0x70)
{
return true;
}
}
return false;
}
bool DebuggerCheckPlugin_CheckDebugObjects()
{
HANDLE hDebugObject = 0;
NTSTATUS status = (NtDll::NtQueryInformationProcess)(
GetCurrentProcess(),
0x1E,
&hDebugObject,
8,
0);
if (status == 0 && NULL != hDebugObject)
{
return true;
}
return false;
}
bool DebuggerCheckPlugin_CheckProcessDebugFlags()
{
DWORD NoDebugInherit = 0;
NTSTATUS status = (NtDll::NtQueryInformationProcess)(
GetCurrentProcess(),
0x1F,
&NoDebugInherit,
8,
0);
if (status == 0 && NoDebugInherit == FALSE)
{
return true;
}
return false;
}
bool DebuggerCheckPlugin_CheckSystemKernelDebuggerInformation()
{
typedef long NTSTATUS;
SYSTEM_KERNEL_DEBUGGER_INFORMATION Info;
if (0 == (NtDll::NtQuerySystemInformation)(
SystemKernelDebuggerInformation,
&Info,
sizeof(Info),
NULL))
{
if (Info.DebuggerEnabled && !Info.DebuggerNotPresent)
{
return true;
}
}
return false;
}
bool DebuggerCheckPlugin_CheckINT3Handler()
{
__try
{
__debugbreak();
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return false;;
}
return true;
}
#pragma warning(disable : 4996)
bool DebuggerCheckPlugin_CheckINT2CHandler()
{
OSVERSIONINFO osvi;
ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&osvi);
if (osvi.dwMajorVersion < 6) //checks to see if we are on at least vista: NT 6.0
return false;
__try
{
__int2c();
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return false;
}
return true;
}
#pragma warning(default: 4996)
bool DebuggerCheckPlugin_CheckHeapFlags()
{
DWORD_PTR pPeb64 = __readgsqword(0x60);
PVOID heap = 0;
PDWORD heapFlagsPtr = 0;
if (pPeb64)
{
heap = (PVOID)*(PDWORD_PTR)((PBYTE)pPeb64 + 0x30);
heapFlagsPtr = (PDWORD)((PBYTE)heap + 0x70);
if (*heapFlagsPtr == 0x40000062) // Source: Ultimate Anti-Reversing Reference
{
return true;
}
}
return false;
}
bool DebuggerCheckPlugin_CheckHeapForceFlags()
{
DWORD_PTR pPeb64 = __readgsqword(0x60);
PVOID heap = 0;
PDWORD heapForceFlagsPtr = 0;
if (pPeb64)
{
heap = (PVOID)*(PDWORD_PTR)((PBYTE)pPeb64 + 0x30);
heapForceFlagsPtr = (PDWORD)((PBYTE)heap + 0x74);
if (*heapForceFlagsPtr == 0x40000060) //Found abnormal heap flag
{
return true;
}
}
return false;
}
bool DebuggerCheckPlugin_CheckRaiseException()
{
__try
{
RaiseException(DBG_RIPEXCEPTION, 0, 0, 0);
}
__except (1)
{
return false;
}
return true;
}
bool DebuggerCheckPlugin_CheckCloseHandle()
{
HANDLE Handle = (HANDLE)0x8000;
__try
{
CloseHandle(Handle);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return true;
}
return false;
}
// Testing functions for the RAG interface
// Designed to test the anti-debugger measures without generating a
// RageSecGameReactionObject for minimal footprint.
#if __BANK
// Tests checks relying on the BeingDebugged flag in the PEB
bool DebuggerCheckPlugin_Test_BeingDebugged()
{
bool result = true;
DWORD_PTR pPeb64 = __readgsqword(0x60);
if (pPeb64)
{
PBYTE pBeingDebugged = (PBYTE)pPeb64 + 0x2;
BYTE prevValue = *pBeingDebugged;
*pBeingDebugged = 0x1;
if(!DebuggerCheckPlugin_BeingDebuggedPeb())
{
RageSecDebugf3("Failed BeingDebuggedPeb Test");
result = false;
}
*pBeingDebugged = prevValue;
}
return result;
}
// Tests checks relying on the Heap related flags in the PEB
bool DebuggerCheckPlugin_Test_HeapFlags()
{
bool result = true;
DWORD_PTR pPeb64 = __readgsqword(0x60);
if (pPeb64)
{
PDWORD pHeap = (PDWORD)((PBYTE)pPeb64 + 0x30);
PDWORD pHeapFlags = (PDWORD)((PBYTE)pHeap + 0x70);
PDWORD pHeapForceFlags = (PDWORD)((PBYTE)pHeap + 0x74);
DWORD prevHeapFlags = *pHeapFlags;
DWORD prevHeapForceFlags = *pHeapForceFlags;
*pHeapFlags = 0x0;
*pHeapForceFlags = 0x1;
if (!DebuggerCheckPlugin_CheckHeapFlags())
{
RageSecDebugf3("Failed Heap Flag Test");
result = false;
}
if (!DebuggerCheckPlugin_CheckHeapForceFlags())
{
RageSecDebugf3("Failed Heap Force Flag Test");
result = false;
}
*pHeapFlags = prevHeapFlags;
*pHeapForceFlags = prevHeapForceFlags;
}
return result;
}
// Tests checks relying on the NtGlobalFlag in the PEB
bool DebuggerCheckPlugin_Test_NtGlobalFlag()
{
bool result = true;
DWORD_PTR pPeb64 = __readgsqword(0x60);
if (pPeb64)
{
PDWORD pNtGlobalFlag = (PDWORD)((PBYTE)pPeb64 + 0xbc);
DWORD prevNtGlobalFlag = *pNtGlobalFlag;
*pNtGlobalFlag = 0x70;
if (!DebuggerCheckPlugin_NtGlobalFlag())
{
RageSecDebugf3("Failed NtGlobalFlag Test");
result = false;
}
*pNtGlobalFlag = prevNtGlobalFlag;
}
return result;
}
void DebuggerCheckPlugin_Test()
{
RageSecDebugf2("PERFORMING ANTI DEBUGGER TESTS");
RageSecDebugf2("[*] BeingDebugged Test......%s", DebuggerCheckPlugin_Test_BeingDebugged() ? "SUCCESS" : "FAILED");
RageSecDebugf2("[*] Heap Flag Test......%s", DebuggerCheckPlugin_Test_HeapFlags() ? "SUCCESS" : "FAILED");
RageSecDebugf2("[*] NtGlobal Flag Test......%s", DebuggerCheckPlugin_Test_NtGlobalFlag() ? "SUCCESS" : "FAILED");
RageSecDebugf2("COMPLETED ANTI DEBUGGER TESTS");
return;
}
#endif //__BANK
bool DebuggerCheckPlugin_Work()
{
//@@: location DEBUGGERCHECKPLUGIN_WORK
bool isMultiplayer = NetworkInterface::IsAnySessionActive()
&& NetworkInterface::IsGameInProgress();
//@@: range DEBUGGERCHECKPLUGIN_WORK_BODY {
if(isMultiplayer)
{
//@@: location DEBUGGERCHECKPLUGIN_WORK_BODY_ENTRY
RageSecPluginGameReactionObject obj(
REACT_GAMESERVER,
CLOCK_GUARD_ID,
fwRandom::GetRandomNumber(),
fwRandom::GetRandomNumber(),
0);
gnetDebug3("0x%08X - Running Tests", DEBUGGER_CHECK_ID);
#if !__NO_OUTPUT
if(PARAM_enable_debugger_detection_tests.Get())
{
#endif
if(DebuggerCheckPlugin_BeingDebuggedPeb())
{
obj.data = (u32)DD_ISDEBUGPEB;
rageSecGamePluginManager::GetRageSecGameReactionObjectQueue()->Push(obj);
gnetDebug3("0x%08X - Debugger Status 0x%016X", DEBUGGER_CHECK_ID, obj.data.Get());
}
if(DebuggerCheckPlugin_NtGlobalFlag())
{
obj.data = (u32)DD_NTGLOBALFLAGS;
rageSecGamePluginManager::GetRageSecGameReactionObjectQueue()->Push(obj);
gnetDebug3("0x%08X - Debugger Status 0x%016X", DEBUGGER_CHECK_ID, obj.data.Get());
}
if(DebuggerCheckPlugin_CheckDebugObjects())
{
obj.data = (u32)DD_DEBUGOBJECTFOUND;
rageSecGamePluginManager::GetRageSecGameReactionObjectQueue()->Push(obj);
gnetDebug3("0x%08X - Debugger Status 0x%016X", DEBUGGER_CHECK_ID, obj.data.Get());
}
if(DebuggerCheckPlugin_CheckProcessDebugFlags())
{
obj.data = (u32)DD_PROCESSDEBUGFLAGS;
rageSecGamePluginManager::GetRageSecGameReactionObjectQueue()->Push(obj);
gnetDebug3("0x%08X - Debugger Status 0x%016X", DEBUGGER_CHECK_ID, obj.data.Get());
}
if(DebuggerCheckPlugin_CheckSystemKernelDebuggerInformation())
{
obj.data = (u32)DD_SYSTEMKERNELDEBUGGERINFORMATION;
rageSecGamePluginManager::GetRageSecGameReactionObjectQueue()->Push(obj);
gnetDebug3("0x%08X - Debugger Status 0x%016X", DEBUGGER_CHECK_ID, obj.data.Get());
}
//@@: location DEBUGGERCHECKPLUGIN_WORK_BODY_MID
if(DebuggerCheckPlugin_CheckHeapFlags())
{
obj.data = (u32)DD_HEAPFLAGS;
rageSecGamePluginManager::GetRageSecGameReactionObjectQueue()->Push(obj);
gnetDebug3("0x%08X - Debugger Status 0x%016X", DEBUGGER_CHECK_ID, obj.data.Get());
}
if(DebuggerCheckPlugin_CheckHeapForceFlags())
{
obj.data = (u32)DD_HEAPFORCEFLAGS;
rageSecGamePluginManager::GetRageSecGameReactionObjectQueue()->Push(obj);
gnetDebug3("0x%08X - Debugger Status 0x%016X", DEBUGGER_CHECK_ID, obj.data.Get());
}
#if !__NO_OUTPUT
}
#endif
gnetAssertf(obj.data.Get() == 0, "0x%08X - Debugger Detected 0x%016X", DEBUGGER_CHECK_ID, obj.data.Get());
}
return true;
//@@: } DEBUGGERCHECKPLUGIN_WORK_BODY
}
bool DebuggerCheckPlugin_OnSuccess()
{
gnetDebug3("0x%08X - OnSuccess", DEBUGGER_CHECK_ID);
// Make this different enough so the compiler doesn't collapse this with another function
//@@: location DEBUGGERCHECKPLUGIN_ONSUCCESS
sm_ddTrashValue += sysTimer::GetSystemMsTime() - fwRandom::GetRandomNumber();
return true;
}
bool DebuggerCheckPlugin_OnFailure()
{
gnetDebug3("0x%08X - OnFailure", DEBUGGER_CHECK_ID);
// Make this different enough so the compiler doesn't collapse this with another function
//@@: location DEBUGGERCHECKPLUGIN_ONFAILURE
sm_ddTrashValue *= sysTimer::GetSystemMsTime() - fwRandom::GetRandomNumber();
return true;
}
bool DebuggerCheckPlugin_Init()
{
// Since there's no real initialization here necessary, we can just assign our
// creation function, our destroy function, and just register the object
// accordingly
RageSecPluginParameters rs;
RageSecPluginExtendedInformation ei;
ei.startTime = sysTimer::GetSystemMsTime();
ei.frequencyInMs = DEBUGGER_CHECK_POLLING_MS;
rs.version.major = PLUGIN_VERSION_MAJOR;
rs.version.minor = PLUGIN_VERSION_MINOR;
rs.type = EXECUTE_PERIODIC;
rs.extendedInfo = ei;
rs.Create = NULL;
rs.Configure = NULL;
rs.Destroy = NULL;
rs.DoWork = &DebuggerCheckPlugin_Work;
rs.OnSuccess = &DebuggerCheckPlugin_OnSuccess;
rs.OnCancel = NULL;
rs.OnFailure = &DebuggerCheckPlugin_OnFailure;
// Register the function
rageSecPluginManager::RegisterPluginFunction(&rs, DEBUGGER_CHECK_ID);
return true;
}
#pragma warning( error : 4100)
#endif // RAGE_SEC_TASK_BASIC_DEBUGGER_CHECK
#endif // RAGE_SEC