mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-04 20:57:49 +00:00
223 lines
7.3 KiB
C++
223 lines
7.3 KiB
C++
// This is here as some additional security measures that only exist inside RCC.
|
|
#include <random>
|
|
#include <windows.h>
|
|
#include "security/JunkCode.h"
|
|
#include "util/Analytics.h"
|
|
|
|
// p1 -- crash if reflection area written to
|
|
// p2 -- log probability = p2 / 100;
|
|
// p3 -- master enable
|
|
FASTFLAGVARIABLE(US30484p1, false)
|
|
FASTINTVARIABLE(US30484p2, 0)
|
|
FASTFLAGVARIABLE(US30484p3, false)
|
|
|
|
FASTFLAG(US31006)
|
|
|
|
namespace
|
|
{
|
|
|
|
template<int N> __forceinline void rccBigFunc()
|
|
{
|
|
_asm
|
|
{
|
|
RBX_NOP6;
|
|
RBX_NOP6;
|
|
RBX_NOP6;
|
|
RBX_NOP6;
|
|
RBX_NOP6;
|
|
RBX_NOP6;
|
|
RBX_NOP6;
|
|
RBX_NOP6;
|
|
RBX_NOP6;
|
|
RBX_NOP6;
|
|
RBX_NOP3; // 4 bytes
|
|
}
|
|
rccBigFunc<N-1>();
|
|
}
|
|
|
|
template<> __forceinline void rccBigFunc<0>() {}
|
|
|
|
// __declspec(align(4096)) didn't work, so this should be 8192 bytes large to ensure it fills one
|
|
// page fully.
|
|
void rccBigFuncStorage()
|
|
{
|
|
#if !defined(_NOOPT) && !defined(_DEBUG)
|
|
rccBigFunc<128>();
|
|
#endif
|
|
}
|
|
|
|
static const size_t kPageSize = 4096;
|
|
uintptr_t getGuardPageBase()
|
|
{
|
|
uintptr_t funcBase = reinterpret_cast<uintptr_t>(&rccBigFuncStorage);
|
|
return kPageSize*((funcBase + kPageSize - 1)/kPageSize);
|
|
}
|
|
|
|
typedef std::vector<IMAGE_SECTION_HEADER*> SectionPtrVector;
|
|
|
|
bool getSectionInfo(const SectionPtrVector& sections, const char* name, uintptr_t& baseAddr, size_t& size)
|
|
{
|
|
const size_t kPeSectionNameLimit = 9;
|
|
for (size_t i = 0; i < sections.size(); ++i)
|
|
{
|
|
if (strncmp(reinterpret_cast<const char*>(sections[i]->Name), name, kPeSectionNameLimit) == 0) //
|
|
{
|
|
baseAddr = sections[i]->VirtualAddress + reinterpret_cast<size_t>(GetModuleHandle(NULL));
|
|
size = sections[i]->Misc.VirtualSize;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int getSections(void* pImageBase, SectionPtrVector& sections)
|
|
{
|
|
IMAGE_DOS_HEADER *pDosHdr = nullptr;
|
|
IMAGE_SECTION_HEADER *pSection = nullptr;
|
|
|
|
// Get DOS header
|
|
pDosHdr = reinterpret_cast<IMAGE_DOS_HEADER*>(pImageBase);
|
|
|
|
// File not a valid PE file
|
|
if (pDosHdr->e_magic != IMAGE_DOS_SIGNATURE)
|
|
return -1;
|
|
|
|
// Get image header
|
|
IMAGE_NT_HEADERS32* pImageHdr32 = reinterpret_cast<IMAGE_NT_HEADERS32*>(
|
|
reinterpret_cast<BYTE*>(pDosHdr) + pDosHdr->e_lfanew);
|
|
|
|
// File not a valid PE file
|
|
if (pImageHdr32->Signature != IMAGE_NT_SIGNATURE)
|
|
return -2;
|
|
|
|
pSection = reinterpret_cast<IMAGE_SECTION_HEADER*>(pImageHdr32 + 1);
|
|
|
|
// Sections
|
|
for (int i = 0; i < pImageHdr32->FileHeader.NumberOfSections; ++i, ++pSection)
|
|
sections.push_back( pSection );
|
|
|
|
return 0;
|
|
}
|
|
|
|
static uintptr_t luaBase = 0;
|
|
static size_t luaSize = 0;
|
|
static void* luaVehHandle = nullptr;
|
|
LONG CALLBACK rccReflectionModifiedVeh(EXCEPTION_POINTERS* rec)
|
|
{
|
|
DWORD unused;
|
|
uintptr_t codeAddr = reinterpret_cast<uintptr_t>(rec->ExceptionRecord->ExceptionAddress);
|
|
uintptr_t dataAddr = rec->ExceptionRecord->ExceptionInformation[1];
|
|
uintptr_t rccBaseAddress = reinterpret_cast<uintptr_t>(GetModuleHandleA(NULL));
|
|
if ((rec->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) // is access violation
|
|
&& (dataAddr - luaBase < luaSize) // data is in .lua
|
|
&& (codeAddr - luaBase >= luaSize)) // code is not in .lua
|
|
{
|
|
RBX::Analytics::InfluxDb::Points analyticsPoints;
|
|
analyticsPoints.addPoint("code", RBX::format("0x%08X", codeAddr-rccBaseAddress).c_str());
|
|
analyticsPoints.addPoint("data", RBX::format("0x%08X", dataAddr-rccBaseAddress).c_str());
|
|
analyticsPoints.report("RccReflectionAv", FInt::US30484p2, true); // add fflag
|
|
if (!FFlag::US30484p1)
|
|
{
|
|
VirtualProtect(reinterpret_cast<void*>(luaBase), luaSize, PAGE_READWRITE, &unused);
|
|
return EXCEPTION_CONTINUE_EXECUTION;
|
|
}
|
|
}
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
}
|
|
|
|
}
|
|
|
|
namespace RBX
|
|
{
|
|
extern bool gCrashIsSpecial;
|
|
extern std::string specialCrashType;
|
|
LONG CALLBACK rccBigFuncExceptionHandler(EXCEPTION_POINTERS* rec)
|
|
{
|
|
uintptr_t funcBase = reinterpret_cast<uintptr_t>(&rccBigFuncStorage);
|
|
uintptr_t guardPage = kPageSize*((funcBase + kPageSize - 1)/kPageSize);
|
|
// the location of the data that was accessed is in ExceptionInformation[1]
|
|
// the location of the code that accessed it is in ExceptionAddress
|
|
if ((rec->ExceptionRecord->ExceptionCode == EXCEPTION_GUARD_PAGE)
|
|
&& (rec->ExceptionRecord->ExceptionInformation[1] - getGuardPageBase() < kPageSize))
|
|
{
|
|
gCrashIsSpecial = true;
|
|
specialCrashType = "page";
|
|
}
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
}
|
|
|
|
LONG CALLBACK rccHwbpExceptionHandler(EXCEPTION_POINTERS* rec)
|
|
{
|
|
if (rec->ExceptionRecord->ExceptionCode == EXCEPTION_SINGLE_STEP)
|
|
{
|
|
gCrashIsSpecial = true;
|
|
}
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
}
|
|
|
|
void initAntiMemDump()
|
|
{
|
|
#if !defined(_NOOPT) && !defined(_DEBUG)
|
|
// Add the exception handler that will process page guard exceptions in a specific address range.
|
|
AddVectoredExceptionHandler(1, &rccBigFuncExceptionHandler);
|
|
|
|
// If someone was scanning RAM, seeing repeating patterns would be suspicious and detectable.
|
|
DWORD unused;
|
|
VirtualProtect(&rccBigFuncStorage, 2*kPageSize, PAGE_EXECUTE_READWRITE, &unused);
|
|
unsigned char* fakeCode = reinterpret_cast<unsigned char*>(GetModuleHandleA(NULL)) + 0x1000;
|
|
memcpy(&rccBigFuncStorage, fakeCode, 2*kPageSize);
|
|
VirtualProtect(&rccBigFuncStorage, 2*kPageSize, PAGE_EXECUTE_READ, &unused);
|
|
|
|
// Finally, set the guard page.
|
|
VirtualProtect(reinterpret_cast<void*>(getGuardPageBase()), kPageSize, PAGE_GUARD | PAGE_EXECUTE_READ, &unused);
|
|
#endif
|
|
}
|
|
|
|
void initHwbpVeh()
|
|
{
|
|
if (!FFlag::US31006)
|
|
{
|
|
return;
|
|
}
|
|
AddVectoredExceptionHandler(1, &rccHwbpExceptionHandler);
|
|
}
|
|
|
|
void initLuaReadOnly()
|
|
{
|
|
if (!FFlag::US30484p3)
|
|
{
|
|
return;
|
|
}
|
|
|
|
SectionPtrVector sections;
|
|
getSections(reinterpret_cast<void*>(GetModuleHandleA(NULL)), sections);
|
|
getSectionInfo(sections, ".lua", luaBase, luaSize);
|
|
if (luaBase && luaSize)
|
|
{
|
|
DWORD unused;
|
|
luaVehHandle = AddVectoredExceptionHandler(0, &rccReflectionModifiedVeh);
|
|
VirtualProtect(reinterpret_cast<void*>(luaBase), luaSize, PAGE_READONLY, &unused);
|
|
}
|
|
}
|
|
|
|
// From testing, it looks like the crash reporter results in a crash and does not call
|
|
// all destructors, etc... However, in the normal case, this would prevent a crash at exit.
|
|
void clearLuaReadOnly()
|
|
{
|
|
if (!FFlag::US30484p3)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (luaBase && luaSize)
|
|
{
|
|
DWORD unused;
|
|
RemoveVectoredExceptionHandler(luaVehHandle);
|
|
VirtualProtect(reinterpret_cast<void*>(luaBase), luaSize, PAGE_READWRITE, &unused);
|
|
luaBase = 0;
|
|
luaSize = 0;
|
|
luaVehHandle = nullptr;
|
|
}
|
|
}
|
|
}
|