diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 19cd75f0..e96a3398 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -39,7 +39,7 @@ jobs: uses: microsoft/setup-msbuild@v1.1 - name: Generate project files - run: tools/premake5 vs2022 --ac-disable + run: tools/premake5 vs2022 - name: Set up problem matching uses: ammaraskar/msvc-problem-matcher@master diff --git a/README.md b/README.md index fb5dc10a..9c9a36b6 100644 --- a/README.md +++ b/README.md @@ -19,9 +19,6 @@ |:----------------------------|:-----------------------------------------------| | `--copy-to=PATH` | Optional, copy the DLL to a custom folder after build, define the path here if wanted. | | `--copy-pdb` | Copy debug information for binaries as well to the path given via --copy-to. | -| `--ac-disable` | Disable anticheat. | -| `--ac-debug-detections` | Log anticheat detections. | -| `--ac-debug-load-library` | Log libraries that get loaded. | | `--force-unit-tests` | Always compile unit tests. | | `--force-exception-handler` | Install custom unhandled exception handler even for Debug builds. | | `--force-minidump-upload` | Upload minidumps even for Debug builds. | diff --git a/generate.bat b/generate.bat index b3823287..2bad27bb 100644 --- a/generate.bat +++ b/generate.bat @@ -1,4 +1,4 @@ @echo off echo Updating submodules... call git submodule update --init --recursive -call tools\premake5 %* vs2022 --ac-disable +call tools\premake5 %* vs2022 diff --git a/lib/bin/VMProtectSDK32.dll b/lib/bin/VMProtectSDK32.dll deleted file mode 100644 index 5b32d087..00000000 Binary files a/lib/bin/VMProtectSDK32.dll and /dev/null differ diff --git a/lib/bin/VMProtectSDK32.lib b/lib/bin/VMProtectSDK32.lib deleted file mode 100644 index 1111795a..00000000 Binary files a/lib/bin/VMProtectSDK32.lib and /dev/null differ diff --git a/lib/include/VMProtect/VMProtectSDK.h b/lib/include/VMProtect/VMProtectSDK.h deleted file mode 100644 index 3368ddac..00000000 --- a/lib/include/VMProtect/VMProtectSDK.h +++ /dev/null @@ -1,102 +0,0 @@ -#pragma once - -#if defined(__APPLE__) || defined(__unix__) -#define VMP_IMPORT -#define VMP_API -#define VMP_WCHAR unsigned short -#else -#define VMP_IMPORT __declspec(dllimport) -#define VMP_API __stdcall -#define VMP_WCHAR wchar_t -#ifdef _WIN64 - #pragma comment(lib, "VMProtectSDK64.lib") -#else - #pragma comment(lib, "VMProtectSDK32.lib") -#endif // _WIN64 -#endif // __APPLE__ || __unix__ - -#ifdef __cplusplus -extern "C" { -#endif - -// protection -VMP_IMPORT void VMP_API VMProtectBegin(const char *); -VMP_IMPORT void VMP_API VMProtectBeginVirtualization(const char *); -VMP_IMPORT void VMP_API VMProtectBeginMutation(const char *); -VMP_IMPORT void VMP_API VMProtectBeginUltra(const char *); -VMP_IMPORT void VMP_API VMProtectBeginVirtualizationLockByKey(const char *); -VMP_IMPORT void VMP_API VMProtectBeginUltraLockByKey(const char *); -VMP_IMPORT void VMP_API VMProtectEnd(void); - -// utils -VMP_IMPORT bool VMP_API VMProtectIsProtected(); -VMP_IMPORT bool VMP_API VMProtectIsDebuggerPresent(bool); -VMP_IMPORT bool VMP_API VMProtectIsVirtualMachinePresent(void); -VMP_IMPORT bool VMP_API VMProtectIsValidImageCRC(void); -VMP_IMPORT const char * VMP_API VMProtectDecryptStringA(const char *value); -VMP_IMPORT const VMP_WCHAR * VMP_API VMProtectDecryptStringW(const VMP_WCHAR *value); -VMP_IMPORT bool VMP_API VMProtectFreeString(const void *value); - -// licensing -enum VMProtectSerialStateFlags -{ - SERIAL_STATE_SUCCESS = 0, - SERIAL_STATE_FLAG_CORRUPTED = 0x00000001, - SERIAL_STATE_FLAG_INVALID = 0x00000002, - SERIAL_STATE_FLAG_BLACKLISTED = 0x00000004, - SERIAL_STATE_FLAG_DATE_EXPIRED = 0x00000008, - SERIAL_STATE_FLAG_RUNNING_TIME_OVER = 0x00000010, - SERIAL_STATE_FLAG_BAD_HWID = 0x00000020, - SERIAL_STATE_FLAG_MAX_BUILD_EXPIRED = 0x00000040, -}; - -#pragma pack(push, 1) -typedef struct -{ - unsigned short wYear; - unsigned char bMonth; - unsigned char bDay; -} VMProtectDate; - -typedef struct -{ - int nState; // VMProtectSerialStateFlags - VMP_WCHAR wUserName[256]; // user name - VMP_WCHAR wEMail[256]; // email - VMProtectDate dtExpire; // date of serial number expiration - VMProtectDate dtMaxBuild; // max date of build, that will accept this key - int bRunningTime; // running time in minutes - unsigned char nUserDataLength; // length of user data in bUserData - unsigned char bUserData[255]; // up to 255 bytes of user data -} VMProtectSerialNumberData; -#pragma pack(pop) - -VMP_IMPORT int VMP_API VMProtectSetSerialNumber(const char *serial); -VMP_IMPORT int VMP_API VMProtectGetSerialNumberState(); -VMP_IMPORT bool VMP_API VMProtectGetSerialNumberData(VMProtectSerialNumberData *data, int size); -VMP_IMPORT int VMP_API VMProtectGetCurrentHWID(char *hwid, int size); - -// activation -enum VMProtectActivationFlags -{ - ACTIVATION_OK = 0, - ACTIVATION_SMALL_BUFFER, - ACTIVATION_NO_CONNECTION, - ACTIVATION_BAD_REPLY, - ACTIVATION_BANNED, - ACTIVATION_CORRUPTED, - ACTIVATION_BAD_CODE, - ACTIVATION_ALREADY_USED, - ACTIVATION_SERIAL_UNKNOWN, - ACTIVATION_EXPIRED, - ACTIVATION_NOT_AVAILABLE -}; - -VMP_IMPORT int VMP_API VMProtectActivateLicense(const char *code, char *serial, int size); -VMP_IMPORT int VMP_API VMProtectDeactivateLicense(const char *serial); -VMP_IMPORT int VMP_API VMProtectGetOfflineActivationString(const char *code, char *buf, int size); -VMP_IMPORT int VMP_API VMProtectGetOfflineDeactivationString(const char *serial, char *buf, int size); - -#ifdef __cplusplus -} -#endif diff --git a/premake5.lua b/premake5.lua index 93fa8ce9..47c2b42f 100644 --- a/premake5.lua +++ b/premake5.lua @@ -71,21 +71,6 @@ newoption { description = "Copy debug information for binaries as well to the path given via --copy-to." } -newoption { - trigger = "ac-disable", - description = "Disable anticheat." -} - -newoption { - trigger = "ac-debug-detections", - description = "Log anticheat detections." -} - -newoption { - trigger = "ac-debug-load-library", - description = "Log libraries that get loaded." -} - newoption { trigger = "force-unit-tests", description = "Always compile unit tests." @@ -269,23 +254,11 @@ workspace "iw4x" "./src", "./lib/include", } - syslibdirs { - "./lib/bin", - } resincludedirs { "$(ProjectDir)src" -- fix for VS IDE } -- Debug flags - if _OPTIONS["ac-disable"] then - defines {"DISABLE_ANTICHEAT"} - end - if _OPTIONS["ac-debug-detections"] then - defines {"DEBUG_DETECTIONS"} - end - if _OPTIONS["ac-debug-load-library"] then - defines {"DEBUG_LOAD_LIBRARY"} - end if _OPTIONS["force-unit-tests"] then defines {"FORCE_UNIT_TESTS"} end diff --git a/src/Components/Modules/AntiCheat.cpp b/src/Components/Modules/AntiCheat.cpp deleted file mode 100644 index 496ef495..00000000 --- a/src/Components/Modules/AntiCheat.cpp +++ /dev/null @@ -1,944 +0,0 @@ -#include - -namespace Components -{ - Utils::Time::Interval AntiCheat::LastCheck; - Utils::Hook AntiCheat::CreateThreadHook; - Utils::Hook AntiCheat::LoadLibHook[6]; - Utils::Hook AntiCheat::VirtualProtectHook[2]; - unsigned long AntiCheat::Flags = NO_FLAG; - - std::mutex AntiCheat::ThreadMutex; - std::vector AntiCheat::OwnThreadIds; - std::map> AntiCheat::ThreadHookMap; - - // This function does nothing, it only adds the two passed variables and returns the value - // The only important thing it does is to clean the first parameter, and then return - // By returning, the crash procedure will be called, as it hasn't been cleaned from the stack - __declspec(naked) void AntiCheat::NullSub() - { - __asm - { - push ebp - push ecx - mov ebp, esp - - xor eax, eax - mov eax, [ebp + 8h] - mov ecx, [ebp + 0Ch] - add eax, ecx - - pop ecx - pop ebp - retn 4 - } - } - - void AntiCheat::CrashClient() - { - __VMProtectBeginUltra(""); -#ifdef DEBUG_DETECTIONS - Logger::Flush(); - MessageBoxA(nullptr, "Check the log for more information!", "AntiCheat triggered", MB_ICONERROR); - ExitProcess(0xFFFFFFFF); -#else - static std::thread triggerThread; - if (!triggerThread.joinable()) - { - triggerThread = std::thread([]() - { - std::this_thread::sleep_for(43s); - Utils::Hook::Set(0x41BA2C, 0xEB); - }); - } -#endif - __VMProtectEnd; - } - - void AntiCheat::AssertCalleeModule(void* callee) - { - __VMProtectBeginUltra(""); - HMODULE hModuleSelf = nullptr, hModuleTarget = nullptr, hModuleProcess = GetModuleHandleA(nullptr); - GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, reinterpret_cast(callee), &hModuleTarget); - GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, reinterpret_cast(AntiCheat::AssertCalleeModule), &hModuleSelf); - - if (!hModuleSelf || !hModuleTarget || !hModuleProcess || (hModuleTarget != hModuleSelf && hModuleTarget != hModuleProcess)) - { -#ifdef DEBUG_DETECTIONS - char buffer[MAX_PATH] = { 0 }; - GetModuleFileNameA(hModuleTarget, buffer, sizeof buffer); - - Logger::Print(Utils::String::VA("AntiCheat: Callee assertion failed: %X %s", reinterpret_cast(callee), buffer)); -#endif - - AntiCheat::CrashClient(); - } - __VMProtectEnd; - } - - void AntiCheat::InitLoadLibHook() - { - __VMProtectBeginUltra(""); - static uint8_t kernel32Str[] = { 0xB4, 0x9A, 0x8D, 0xB1, 0x9A, 0x93, 0xCC, 0xCD, 0xD1, 0x9B, 0x93, 0x93 }; // KerNel32.dll - static uint8_t loadLibAStr[] = { 0xB3, 0x90, 0x9E, 0x9B, 0xB3, 0x96, 0x9D, 0x8D, 0x9E, 0x8D, 0x86, 0xBE }; // LoadLibraryA - static uint8_t loadLibWStr[] = { 0xB3, 0x90, 0x9E, 0x9B, 0xB3, 0x96, 0x9D, 0x8D, 0x9E, 0x8D, 0x86, 0xA8 }; // LoadLibraryW - - HMODULE kernel32 = GetModuleHandleA(Utils::String::XOR(std::string(reinterpret_cast(kernel32Str), sizeof kernel32Str), -1).data()); - if (kernel32) - { - FARPROC loadLibA = GetProcAddress(kernel32, Utils::String::XOR(std::string(reinterpret_cast(loadLibAStr), sizeof loadLibAStr), -1).data()); - FARPROC loadLibW = GetProcAddress(kernel32, Utils::String::XOR(std::string(reinterpret_cast(loadLibWStr), sizeof loadLibWStr), -1).data()); - - std::string libExA = Utils::String::XOR(std::string(reinterpret_cast(loadLibAStr), sizeof loadLibAStr), -1); - std::string libExW = Utils::String::XOR(std::string(reinterpret_cast(loadLibWStr), sizeof loadLibWStr), -1); - - libExA.insert(libExA.end() - 1, 'E'); - libExA.insert(libExA.end() - 1, 'x'); - - libExW.insert(libExW.end() - 1, 'E'); - libExW.insert(libExW.end() - 1, 'x'); - - FARPROC loadLibExA = GetProcAddress(kernel32, libExA.data()); - FARPROC loadLibExW = GetProcAddress(kernel32, libExW.data()); - - if (loadLibA && loadLibW && loadLibExA && loadLibExW) - { -#ifdef DEBUG_LOAD_LIBRARY - AntiCheat::LoadLibHook[0].initialize(loadLibA, LoadLibaryAStub, HOOK_JUMP); - AntiCheat::LoadLibHook[1].initialize(loadLibW, LoadLibaryWStub, HOOK_JUMP); - AntiCheat::LoadLibHook[2].initialize(loadLibExA, LoadLibaryExAStub, HOOK_JUMP); - AntiCheat::LoadLibHook[3].initialize(loadLibExW, LoadLibaryExWStub, HOOK_JUMP); -#else - static uint8_t loadLibStub[] = { 0x33, 0xC0, 0xC2, 0x04, 0x00 }; // xor eax, eax; retn 04h - static uint8_t loadLibExStub[] = { 0x33, 0xC0, 0xC2, 0x0C, 0x00 }; // xor eax, eax; retn 0Ch - AntiCheat::LoadLibHook[0].initialize(loadLibA, loadLibStub, HOOK_JUMP); - AntiCheat::LoadLibHook[1].initialize(loadLibW, loadLibStub, HOOK_JUMP); - AntiCheat::LoadLibHook[2].initialize(loadLibExA, loadLibExStub, HOOK_JUMP); - AntiCheat::LoadLibHook[3].initialize(loadLibExW, loadLibExStub, HOOK_JUMP); -#endif - } - } - - static uint8_t ldrLoadDllStub[] = { 0x33, 0xC0, 0xC2, 0x10, 0x00 }; - static uint8_t ldrLoadDll[] = { 0xB3, 0x9B, 0x8D, 0xB3, 0x90, 0x9E, 0x9B, 0xBB, 0x93, 0x93 }; // LdrLoadDll - - HMODULE ntdll = Utils::GetNTDLL(); - //AntiCheat::LoadLibHook[4].initialize(GetProcAddress(ntdll, Utils::String::XOR(std::string(reinterpret_cast(ldrLoadDll), sizeof ldrLoadDll), -1).data()), ldrLoadDllStub, HOOK_JUMP); - - // Patch LdrpLoadDll - Utils::Hook::Signature::Container container; - container.signature = "\x8B\xFF\x55\x8B\xEC\x83\xE4\xF8\x81\xEC\x00\x00\x00\x00\xA1\x00\x00\x00\x00\x33\xC4\x89\x84\x24\x00\x00\x00\x00\x53\x8B\x5D\x10\x56\x57"; - container.mask = "xxxxxxxxxx????x????xxxxx????xxxxxx"; - container.callback = [](char* addr) - { - static uint8_t ldrpLoadDllStub[] = { 0x33, 0xC0, 0xC2, 0x0C, 0x00 }; - AntiCheat::LoadLibHook[5].initialize(addr, ldrpLoadDllStub, HOOK_JUMP); - }; - - Utils::Hook::Signature signature(ntdll, Utils::GetModuleSize(ntdll)); - signature.add(container); - //signature.process(); - - __VMProtectEnd; - } - - void AntiCheat::ReadIntegrityCheck() - { - __VMProtectBeginUltra(""); -#ifdef PROCTECT_PROCESS - static Utils::Time::Interval check; - - if (check.elapsed(20s)) - { - check.update(); - - if (HANDLE h = OpenProcess(PROCESS_VM_READ, FALSE, GetCurrentProcessId())) - { -#ifdef DEBUG_DETECTIONS - Logger::Print("AntiCheat: Process integrity check failed"); -#endif - - CloseHandle(h); - AntiCheat::CrashClient(); - } - } - - // Set the integrity flag - AntiCheat::Flags |= AntiCheat::IntergrityFlag::READ_INTEGRITY_CHECK; -#endif - __VMProtectEnd; - } - - void AntiCheat::FlagIntegrityCheck() - { - __VMProtectBeginUltra(""); - static Utils::Time::Interval check; - - if (check.elapsed(30s)) - { - check.update(); - - unsigned long flags = ((AntiCheat::IntergrityFlag::MAX_FLAG - 1) << 1) - 1; - - if (AntiCheat::Flags != flags) - { -#ifdef DEBUG_DETECTIONS - Logger::Print(Utils::String::VA("AntiCheat: Flag integrity check failed: %X", AntiCheat::Flags)); -#endif - - AntiCheat::CrashClient(); - } - } - __VMProtectEnd; - } - - void AntiCheat::ScanIntegrityCheck() - { - __VMProtectBeginUltra(""); - // If there was no check within the last 40 seconds, crash! - if (AntiCheat::LastCheck.elapsed(40s)) - { -#ifdef DEBUG_DETECTIONS - Logger::Print("AntiCheat: Integrity check failed"); -#endif - - AntiCheat::CrashClient(); - } - - // Set the integrity flag - AntiCheat::Flags |= AntiCheat::IntergrityFlag::SCAN_INTEGRITY_CHECK; - __VMProtectEnd; - } - - void AntiCheat::PerformScan() - { - __VMProtectBeginUltra(""); - static std::optional hashVal; - - // Perform check only every 20 seconds - if (!AntiCheat::LastCheck.elapsed(20s)) return; - AntiCheat::LastCheck.update(); - - // Hash .text segment - // Add 1 to each value, so searching in memory doesn't reveal anything - size_t textSize = 0x2D6001; - char* textBase = reinterpret_cast(0x401001); - - unsigned int hash = Utils::Cryptography::JenkinsOneAtATime::Compute(textBase - 1, textSize - 1); - - // Set the hash, if none is set - if (!hashVal.has_value()) - { - hashVal.emplace(hash); - } - // Crash if the hashes don't match - else if (hashVal.value() != hash) - { -#ifdef DEBUG_DETECTIONS - Logger::Print("AntiCheat: Memory scan failed"); -#endif - - AntiCheat::CrashClient(); - } - - // Set the memory scan flag - AntiCheat::Flags |= AntiCheat::IntergrityFlag::MEMORY_SCAN; - __VMProtectEnd; - } - - void AntiCheat::QuickCodeScanner1() - { - __VMProtectBeginUltra(""); - static Utils::Time::Interval interval; - static std::optional hashVal; - - if (!interval.elapsed(32s)) return; - interval.update(); - - // Hash .text segment - // Add 1 to each value, so searching in memory doesn't reveal anything - size_t textSize = 0x2D5FFF; - char* textBase = reinterpret_cast(0x400FFF); - unsigned int hash = Utils::Cryptography::JenkinsOneAtATime::Compute(textBase + 1, textSize + 1); - - if (hashVal.has_value() && hash != hashVal.value()) - { - Utils::Hook::Set(0x42A667, 0x90); // Crash - } - - hashVal.emplace(hash); - __VMProtectEnd; - } - - void AntiCheat::QuickCodeScanner2() - { - __VMProtectBeginUltra(""); - static Utils::Time::Interval interval; - static std::optional hashVal; - - if (!interval.elapsed(42s)) return; - interval.update(); - - // Hash .text segment - unsigned int hash = Utils::Cryptography::JenkinsOneAtATime::Compute(reinterpret_cast(0x401000), 0x2D6000); - if (hashVal.has_value() && hash != hashVal.value()) - { - Utils::Hook::Set(0x40797C, 0x90); // Crash - } - - hashVal.emplace(hash); - __VMProtectEnd; - } - -#ifdef DEBUG_LOAD_LIBRARY - HANDLE AntiCheat::LoadLibary(std::wstring library, HANDLE file, DWORD flags, void* callee) - { - HMODULE module; - char buffer[MAX_PATH] = { 0 }; - - GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, reinterpret_cast(callee), &module); - GetModuleFileNameA(module, buffer, sizeof buffer); - - MessageBoxA(nullptr, Utils::String::VA("Loading library %s via %s %X", std::string(library.begin(), library.end()).data(), buffer, reinterpret_cast(callee)), nullptr, 0); - - AntiCheat::LoadLibHook[3].uninstall(); - HANDLE h = LoadLibraryExW(library.data(), file, flags); - AntiCheat::LoadLibHook[3].install(); - return h; - } - - HANDLE WINAPI AntiCheat::LoadLibaryAStub(const char* library) - { - std::string lib(library); - return AntiCheat::LoadLibary(std::wstring(lib.begin(), lib.end()), nullptr, 0, _ReturnAddress()); - } - - HANDLE WINAPI AntiCheat::LoadLibaryWStub(const wchar_t* library) - { - return AntiCheat::LoadLibary(library, nullptr, 0, _ReturnAddress()); - } - - HANDLE WINAPI AntiCheat::LoadLibaryExAStub(const char* library, HANDLE file, DWORD flags) - { - std::string lib(library); - return AntiCheat::LoadLibary(std::wstring(lib.begin(), lib.end()), file, flags, _ReturnAddress()); - } - - HANDLE WINAPI AntiCheat::LoadLibaryExWStub(const wchar_t* library, HANDLE file, DWORD flags) - { - return AntiCheat::LoadLibary(library, file, flags, _ReturnAddress()); - } -#endif - - void AntiCheat::UninstallLibHook() - { - for (int i = 0; i < ARRAYSIZE(AntiCheat::LoadLibHook); ++i) - { - AntiCheat::LoadLibHook[i].uninstall(); - } - } - - void AntiCheat::InstallLibHook() - { - for (int i = 0; i < ARRAYSIZE(AntiCheat::LoadLibHook); ++i) - { - AntiCheat::LoadLibHook[i].install(); - } - } - - void AntiCheat::PatchWinAPI() - { - LibUnlocker _; - - // Initialize directx - Utils::Hook::Call(0x5078C0)(); - } - - void AntiCheat::SoundInitStub(int a1, int a2, int a3) - { - LibUnlocker _; - Game::SND_Init(a1, a2, a3); - } - - void AntiCheat::SoundInitDriverStub() - { - LibUnlocker _; - Game::SND_InitDriver(); - } - - void AntiCheat::LostD3DStub() - { - LibUnlocker _; - - // Reset directx - Utils::Hook::Call(0x508070)(); - } - - __declspec(naked) void AntiCheat::CinematicStub() - { - __asm - { - pushad - call AntiCheat::UninstallLibHook - popad - - call Game::R_Cinematic_StartPlayback_Now - - pushad - call AntiCheat::InstallLibHook - popad - - retn - } - } - - __declspec(naked) void AntiCheat::DObjGetWorldTagPosStub() - { - __asm - { - pushad - push [esp + 20h] - - call AntiCheat::AssertCalleeModule - - pop esi - popad - - push ecx - mov ecx, [esp + 10h] - - push 426585h - retn - } - } - - __declspec(naked) void AntiCheat::AimTargetGetTagPosStub() - { - __asm - { - pushad - push [esp + 20h] - - call AntiCheat::AssertCalleeModule - - pop esi - popad - - sub esp, 14h - cmp dword ptr[esi + 0E0h], 1 - push 56AC6Ah - ret - } - } - - bool AntiCheat::IsPageChangeAllowed(void* callee, void* addr, size_t len) - { - __VMProtectBeginUltra(""); - HMODULE hModuleSelf = nullptr, hModuleTarget = nullptr, hModuleMain = GetModuleHandle(nullptr); - GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, reinterpret_cast(callee), &hModuleTarget); - GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, reinterpret_cast(AntiCheat::IsPageChangeAllowed), &hModuleSelf); - - size_t mainSize = Utils::GetModuleSize(hModuleMain), selfSize = Utils::GetModuleSize(hModuleSelf); - DWORD self = DWORD(hModuleSelf), main = DWORD(hModuleMain), address = DWORD(addr); - - // If the address that should be changed is within our module or the main binary, then we need to check if we are changing it or someone else - if (Utils::HasIntercection(self, selfSize, address, len) || Utils::HasIntercection(main, mainSize, address, len)) - { - if (!hModuleSelf || !hModuleTarget || (hModuleTarget != hModuleSelf)) - { - return false; - } - } - - __VMProtectEnd; - return true; - } - - BOOL WINAPI AntiCheat::VirtualProtectStub(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect) - { - __VMProtectBeginUltra(""); - if (!AntiCheat::IsPageChangeAllowed(_ReturnAddress(), lpAddress, dwSize)) return FALSE; - - AntiCheat::VirtualProtectHook[0].uninstall(false); - BOOL result = VirtualProtect(lpAddress, dwSize, flNewProtect, lpflOldProtect); - AntiCheat::VirtualProtectHook[0].install(false); - - __VMProtectEnd; - return result; - } - - BOOL WINAPI AntiCheat::VirtualProtectExStub(HANDLE hProcess, LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect) - { - __VMProtectBeginUltra(""); - if (GetCurrentProcessId() == GetProcessId(hProcess) && !AntiCheat::IsPageChangeAllowed(_ReturnAddress(), lpAddress, dwSize)) return FALSE; - - AntiCheat::VirtualProtectHook[1].uninstall(false); - BOOL result = VirtualProtectEx(hProcess, lpAddress, dwSize, flNewProtect, lpflOldProtect); - AntiCheat::VirtualProtectHook[1].install(false); - - __VMProtectEnd; - return result; - } - - unsigned long AntiCheat::ProtectProcess() - { -#ifdef PROCTECT_PROCESS - __VMProtectBeginUltra(""); - - Utils::Memory::Allocator allocator; - - HANDLE hToken = nullptr; - if (!OpenProcessToken(GetCurrentProcess(), /*TOKEN_ADJUST_PRIVILEGES | */TOKEN_READ, &hToken)) - { - if (!OpenThreadToken(GetCurrentThread(), /*TOKEN_ADJUST_PRIVILEGES | */TOKEN_READ, TRUE, &hToken)) - { - return GetLastError(); - } - } - - auto freeSid = [](void* sid) - { - if (sid) - { - FreeSid(reinterpret_cast(sid)); - } - }; - - allocator.reference(hToken, [](void* hToken) - { - if (hToken) - { - CloseHandle(hToken); - } - }); - - //AntiCheat::AcquireDebugPrivilege(hToken); - - DWORD dwSize = 0; - PVOID pTokenInfo = nullptr; - if (GetTokenInformation(hToken, TokenUser, nullptr, 0, &dwSize) || GetLastError() != ERROR_INSUFFICIENT_BUFFER) return GetLastError(); - - if (dwSize) - { - pTokenInfo = allocator.allocate(dwSize); - if (!pTokenInfo) return GetLastError(); - } - - if (!GetTokenInformation(hToken, TokenUser, pTokenInfo, dwSize, &dwSize) || !pTokenInfo) return GetLastError(); - - PSID psidCurUser = reinterpret_cast(pTokenInfo)->User.Sid; - - PSID psidEveryone = nullptr; - SID_IDENTIFIER_AUTHORITY sidEveryone = SECURITY_WORLD_SID_AUTHORITY; - if (!AllocateAndInitializeSid(&sidEveryone, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &psidEveryone) || !psidEveryone) return GetLastError(); - allocator.reference(psidEveryone, freeSid); - - PSID psidSystem = nullptr; - SID_IDENTIFIER_AUTHORITY sidSystem = SECURITY_NT_AUTHORITY; - if (!AllocateAndInitializeSid(&sidSystem, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &psidSystem) || !psidSystem) return GetLastError(); - allocator.reference(psidSystem, freeSid); - - PSID psidAdmins = nullptr; - SID_IDENTIFIER_AUTHORITY sidAdministrators = SECURITY_NT_AUTHORITY; - if (!AllocateAndInitializeSid(&sidAdministrators, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &psidAdmins) || !psidAdmins) return GetLastError(); - allocator.reference(psidAdmins, freeSid); - - const PSID psidArray[] = - { - psidEveryone, /* Deny most rights to everyone */ - psidCurUser, /* Allow what was not denied */ - psidSystem, /* Full control */ - psidAdmins, /* Full control */ - }; - - // Determine required size of the ACL - dwSize = sizeof(ACL); - - // First the DENY, then the ALLOW - dwSize += GetLengthSid(psidArray[0]); - dwSize += sizeof(ACCESS_DENIED_ACE) - sizeof(DWORD); - - for (UINT i = 1; i < _countof(psidArray); ++i) - { - // DWORD is the SidStart field, which is not used for absolute format - dwSize += GetLengthSid(psidArray[i]); - dwSize += sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD); - } - - PACL pDacl = reinterpret_cast(allocator.allocate(dwSize)); - if (!pDacl || !InitializeAcl(pDacl, dwSize, ACL_REVISION)) return GetLastError(); - - // Just give access to what steam needs - //static const DWORD dwPoison = 0UL | ~(SYNCHRONIZE | GENERIC_EXECUTE | GENERIC_ALL); - static const DWORD dwPoison = - /*READ_CONTROL |*/ WRITE_DAC | WRITE_OWNER | - PROCESS_CREATE_PROCESS | PROCESS_CREATE_THREAD | - PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION | - PROCESS_SET_QUOTA | PROCESS_SET_INFORMATION | - PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE | - // In addition to protected process - PROCESS_SUSPEND_RESUME | PROCESS_TERMINATE; - - if (!AddAccessDeniedAce(pDacl, ACL_REVISION, dwPoison, psidArray[0])) return GetLastError(); - - // Standard and specific rights not explicitly denied - //static const DWORD dwAllowed = 0UL | SYNCHRONIZE; - static const DWORD dwAllowed = (~dwPoison & 0x1FFF) | SYNCHRONIZE; - if (!AddAccessAllowedAce(pDacl, ACL_REVISION, dwAllowed, psidArray[1])) return GetLastError(); - - // Because of ACE ordering, System will effectively have dwAllowed even - // though the ACE specifies PROCESS_ALL_ACCESS (unless software uses - // SeDebugPrivilege or SeTcbName and increases access). - // As an exercise, check behavior of tools such as Process Explorer under XP, - // Vista, and above. Vista and above should exhibit slightly different behavior - // due to Restricted tokens. - if (!AddAccessAllowedAce(pDacl, ACL_REVISION, PROCESS_ALL_ACCESS, psidArray[2])) return GetLastError(); - - // Because of ACE ordering, Administrators will effectively have dwAllowed - // even though the ACE specifies PROCESS_ALL_ACCESS (unless the Administrator - // invokes 'discretionary security' by taking ownership and increasing access). - // As an exercise, check behavior of tools such as Process Explorer under XP, - // Vista, and above. Vista and above should exhibit slightly different behavior - // due to Restricted tokens. - if (!AddAccessAllowedAce(pDacl, ACL_REVISION, PROCESS_ALL_ACCESS, psidArray[3])) return GetLastError(); - - PSECURITY_DESCRIPTOR pSecDesc = allocator.allocate(); - if (!pSecDesc) return GetLastError(); - - // InitializeSecurityDescriptor initializes a security descriptor in - // absolute format, rather than self-relative format. See - // http://msdn.microsoft.com/en-us/library/aa378863(VS.85).aspx - if (!InitializeSecurityDescriptor(pSecDesc, SECURITY_DESCRIPTOR_REVISION)) return GetLastError(); - if (!SetSecurityDescriptorDacl(pSecDesc, TRUE, pDacl, FALSE)) return GetLastError(); - - __VMProtectEnd; - - return SetSecurityInfo( - GetCurrentProcess(), - SE_KERNEL_OBJECT, // process object - OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, - psidCurUser, // NULL, // Owner SID - nullptr, // Group SID - pDacl, - nullptr // SACL - ); -#else - return 0; -#endif - } - - void AntiCheat::AcquireDebugPrivilege(HANDLE hToken) - { - __VMProtectBeginUltra(""); - - LUID luid; - TOKEN_PRIVILEGES tp = { 0 }; - DWORD cb = sizeof(TOKEN_PRIVILEGES); - if (!LookupPrivilegeValueA(nullptr, SE_DEBUG_NAME, &luid)) return; - - tp.PrivilegeCount = 1; - tp.Privileges[0].Luid = luid; - tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; - AdjustTokenPrivileges(hToken, FALSE, &tp, cb, nullptr, nullptr); - //if (GetLastError() != ERROR_SUCCESS) return; - - __VMProtectEnd; - } - - void AntiCheat::PatchVirtualProtect(void* vp, void* vpex) - { - __VMProtectBeginUltra(""); - AntiCheat::VirtualProtectHook[1].initialize(vpex, AntiCheat::VirtualProtectExStub, HOOK_JUMP)->install(true, true); - AntiCheat::VirtualProtectHook[0].initialize(vp, AntiCheat::VirtualProtectStub, HOOK_JUMP)->install(true, true); - __VMProtectEnd; - } - - NTSTATUS NTAPI AntiCheat::NtCreateThreadExStub(PHANDLE phThread, ACCESS_MASK desiredAccess, LPVOID objectAttributes, HANDLE processHandle, LPTHREAD_START_ROUTINE startAddress, LPVOID parameter, BOOL createSuspended, DWORD stackZeroBits, DWORD sizeOfStackCommit, DWORD sizeOfStackReserve, LPVOID bytesBuffer) - { - __VMProtectBeginUltra(""); - - HANDLE hThread = nullptr; - std::lock_guard _(AntiCheat::ThreadMutex); - - AntiCheat::CreateThreadHook.uninstall(); - NTSTATUS result = NtCreateThreadEx_t(AntiCheat::CreateThreadHook.getAddress())(&hThread, desiredAccess, objectAttributes, processHandle, startAddress, parameter, createSuspended, stackZeroBits, sizeOfStackCommit, sizeOfStackReserve, bytesBuffer); - AntiCheat::CreateThreadHook.install(); - - if (phThread) *phThread = hThread; - - if (GetProcessId(processHandle) == GetCurrentProcessId()) - { - AntiCheat::OwnThreadIds.push_back(GetThreadId(hThread)); - } - - __VMProtectEnd; - - return result; - } - - void AntiCheat::PatchThreadCreation() - { - __VMProtectBeginUltra(""); - - HMODULE ntdll = Utils::GetNTDLL(); - if (ntdll) - { - static uint8_t ntCreateThreadEx[] = { 0xB1, 0x8B, 0xBC, 0x8D, 0x9A, 0x9E, 0x8B, 0x9A, 0xAB, 0x97, 0x8D, 0x9A, 0x9E, 0x9B, 0xBA, 0x87 }; // NtCreateThreadEx - FARPROC createThread = GetProcAddress(ntdll, Utils::String::XOR(std::string(reinterpret_cast(ntCreateThreadEx), sizeof ntCreateThreadEx), -1).data()); - if (createThread) - { - AntiCheat::CreateThreadHook.initialize(createThread, AntiCheat::NtCreateThreadExStub, HOOK_JUMP)->install(); - } - } - - __VMProtectEnd; - } - - int AntiCheat::ValidateThreadTermination(void* addr) - { - __VMProtectBeginUltra(""); - { - std::lock_guard _(AntiCheat::ThreadMutex); - - DWORD id = GetCurrentThreadId(); - auto threadHook = AntiCheat::ThreadHookMap.find(id); - if (threadHook != AntiCheat::ThreadHookMap.end()) - { - threadHook->second->uninstall(false); - AntiCheat::ThreadHookMap.erase(threadHook); // Uninstall and delete the hook - return 1; // Kill - } - } - - while (true) - { - std::lock_guard _(AntiCheat::ThreadMutex); - - // It would be better to wait for the thread - // but we don't know if there are multiple hooks at the same address - bool found = false; - for (auto threadHook : AntiCheat::ThreadHookMap) - { - if (threadHook.second->getAddress() == addr) - { - found = true; - break; - } - } - - if (!found) break; - std::this_thread::sleep_for(10ms); - } - - __VMProtectEnd; - - return 0; // Don't kill - } - - __declspec(naked) void AntiCheat::ThreadEntryPointStub() - { - __asm - { - push eax - push eax - pushad - - // Reinitialize the return address - mov eax, [esp + 28h] - sub eax, 5 - mov [esp + 28h], eax - - push eax - call AntiCheat::ValidateThreadTermination - add esp, 4h - - mov [esp + 20h], eax - - popad - - pop eax - - test eax, eax - jz dontKill - - pop eax - add esp, 4h // Remove return address (simulate a jump hook) - retn - - dontKill: - pop eax - retn - } - } - - void AntiCheat::VerifyThreadIntegrity() - { - __VMProtectBeginUltra(""); - bool kill = true; - { - std::lock_guard _(AntiCheat::ThreadMutex); - - auto threadHook = std::find(AntiCheat::OwnThreadIds.begin(), AntiCheat::OwnThreadIds.end(), GetCurrentThreadId()); - if (threadHook != AntiCheat::OwnThreadIds.end()) - { - AntiCheat::OwnThreadIds.erase(threadHook); - kill = false; - } - } - - if (kill) - { - static bool first = true; - if (first) first = false; // We can't control the main thread, as it's spawned externally - else - { - std::lock_guard _(AntiCheat::ThreadMutex); - - HMODULE ntdll = Utils::GetNTDLL(), targetModule; - if (!ntdll) return; // :( - - void* address = Utils::GetThreadStartAddress(GetCurrentThread()); - if (address) - { - GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, reinterpret_cast(address), &targetModule); - if (targetModule == ntdll) return; // Better not kill kernel threads - - DWORD id = GetCurrentThreadId(); - { - auto threadHook = AntiCheat::ThreadHookMap.find(id); - if (threadHook != AntiCheat::ThreadHookMap.end()) - { - threadHook->second->uninstall(false); - AntiCheat::ThreadHookMap.erase(threadHook); - } - } - - std::shared_ptr hook = std::make_shared(); - AntiCheat::ThreadHookMap[id] = hook; - - // Hook the entry point of the thread to properly terminate it - hook->initialize(address, AntiCheat::ThreadEntryPointStub, HOOK_CALL)->install(true, true); - } - } - } - __VMProtectEnd; - } - - void AntiCheat::SystemTimeDiff(LPSYSTEMTIME stA, LPSYSTEMTIME stB, LPSYSTEMTIME stC) { - FILETIME ftA, ftB, ftC; - ULARGE_INTEGER uiA, uiB, uiC; - - SystemTimeToFileTime(stA, &ftA); - SystemTimeToFileTime(stB, &ftB); - uiA.HighPart = ftA.dwHighDateTime; - uiA.LowPart = ftA.dwLowDateTime; - uiB.HighPart = ftB.dwHighDateTime; - uiB.LowPart = ftB.dwLowDateTime; - - uiC.QuadPart = uiA.QuadPart - uiB.QuadPart; - - ftC.dwHighDateTime = uiC.HighPart; - ftC.dwLowDateTime = uiC.LowPart; - FileTimeToSystemTime(&ftC, stC); - } - - void AntiCheat::CheckStartupTime() - { - __VMProtectBeginUltra(""); - FILETIME creation, exit, kernel, user; - SYSTEMTIME current, creationSt, diffSt; - - GetSystemTime(¤t); - GetProcessTimes(GetCurrentProcess(), &creation, &exit, &kernel, &user); - - FileTimeToSystemTime(&creation, &creationSt); - AntiCheat::SystemTimeDiff(¤t, &creationSt, &diffSt); - -#ifdef DEBUG - char buf[512]; - snprintf(buf, 512, "creation: %d:%d:%d:%d\n", creationSt.wHour, creationSt.wMinute, creationSt.wSecond, creationSt.wMilliseconds); - OutputDebugStringA(buf); - - snprintf(buf, 512, "current: %d:%d:%d:%d\n", current.wHour, current.wMinute, current.wSecond, current.wMilliseconds); - OutputDebugStringA(buf); - - snprintf(buf, 512, "diff: %d:%d:%d:%d\n", diffSt.wHour, diffSt.wMinute, diffSt.wSecond, diffSt.wMilliseconds); - OutputDebugStringA(buf); -#endif - - // crash client if they are using process suspension to inject dlls during startup (aka before we got to here) - // maybe tweak this value depending on what the above logging reveals during testing, - // but 5 seconds seems about right for now - int time = diffSt.wMilliseconds + (diffSt.wSecond * 1000) + (diffSt.wMinute * 1000 * 60); - if (time > 5000) { - Components::AntiCheat::CrashClient(); - } - - // use below for logging when using StartSuspended.exe - // FILE* f = fopen("times.txt", "a"); - // fwrite(buf, 1, strlen(buf), f); - // fclose(f); - - __VMProtectEnd; - } - - AntiCheat::AntiCheat() - { - __VMProtectBeginUltra(""); - - time(nullptr); - AntiCheat::Flags = NO_FLAG; - -#ifndef DISABLE_ANTICHEAT - - Utils::Hook(0x507BD5, AntiCheat::PatchWinAPI, HOOK_CALL).install()->quick(); - Utils::Hook(0x5082FD, AntiCheat::LostD3DStub, HOOK_CALL).install()->quick(); - Utils::Hook(0x51C76C, AntiCheat::CinematicStub, HOOK_CALL).install()->quick(); - Utils::Hook(0x418209, AntiCheat::SoundInitStub, HOOK_CALL).install()->quick(); - Utils::Hook(0x60BE9D, AntiCheat::SoundInitStub, HOOK_CALL).install()->quick(); - Utils::Hook(0x60BE8E, AntiCheat::SoundInitDriverStub, HOOK_CALL).install()->quick(); - Utils::Hook(0x418204, AntiCheat::SoundInitDriverStub, HOOK_CALL).install()->quick(); - Scheduler::OnFrame(AntiCheat::PerformScan, true); - - // Detect aimbots - Utils::Hook(0x426580, AntiCheat::DObjGetWorldTagPosStub, HOOK_JUMP).install()->quick(); - Utils::Hook(0x56AC60, AntiCheat::AimTargetGetTagPosStub, HOOK_JUMP).install()->quick(); - - // TODO: Probably move that :P - if (!Dedicated::IsEnabled()) - { - AntiCheat::InitLoadLibHook(); - } - - // Prevent external processes from accessing our memory - AntiCheat::ProtectProcess(); - Renderer::OnDeviceRecoveryEnd([]() - { - AntiCheat::ProtectProcess(); - }); - - // Set the integrity flag - AntiCheat::Flags |= AntiCheat::IntergrityFlag::INITIALIZATION; - -#endif - - __VMProtectEnd; - } - - AntiCheat::~AntiCheat() - { - AntiCheat::Flags = NO_FLAG; - AntiCheat::OwnThreadIds.clear(); - AntiCheat::ThreadHookMap.clear(); - - for (int i = 0; i < ARRAYSIZE(AntiCheat::LoadLibHook); ++i) - { - AntiCheat::LoadLibHook[i].uninstall(); - } - - for (int i = 0; i < ARRAYSIZE(AntiCheat::VirtualProtectHook); ++i) - { - AntiCheat::VirtualProtectHook[i].uninstall(false); - } - } -} diff --git a/src/Components/Modules/AntiCheat.hpp b/src/Components/Modules/AntiCheat.hpp deleted file mode 100644 index 9cbb2ff3..00000000 --- a/src/Components/Modules/AntiCheat.hpp +++ /dev/null @@ -1,120 +0,0 @@ -#pragma once - -#ifndef DEBUG -// Hide AntiCheat in embeded symbol names -#define AntiCheat SubComponent -#else -# ifndef DISABLE_ANTICHEAT -# define DISABLE_ANTICHEAT -# endif -#endif - -// Uncomment to enable process protection (conflicts with steam!) -#define PROCTECT_PROCESS - -namespace Components -{ - class AntiCheat : public Component - { - public: - AntiCheat(); - ~AntiCheat(); - - class LibUnlocker - { - public: - LibUnlocker() - { - UninstallLibHook(); - } - ~LibUnlocker() - { - InstallLibHook(); - } - }; - - static void CrashClient(); - - static void InitLoadLibHook(); - - static void ReadIntegrityCheck(); - static void ScanIntegrityCheck(); - static void FlagIntegrityCheck(); - - static unsigned long ProtectProcess(); - - static void PatchVirtualProtect(void* vp, void* vpex); - static void PatchThreadCreation(); - - static void VerifyThreadIntegrity(); - - static void QuickCodeScanner1(); - static void QuickCodeScanner2(); - - static void UninstallLibHook(); - static void InstallLibHook(); - - static void CheckStartupTime(); - static void SystemTimeDiff(LPSYSTEMTIME stA, LPSYSTEMTIME stB, LPSYSTEMTIME stC); - - private: - enum IntergrityFlag - { - NO_FLAG = (0), - INITIALIZATION = (1 << 0), - MEMORY_SCAN = (1 << 1), - SCAN_INTEGRITY_CHECK = (1 << 2), - -#ifdef PROCTECT_PROCESS - READ_INTEGRITY_CHECK = (1 << 3), -#endif - - MAX_FLAG, - }; - - static Utils::Time::Interval LastCheck; - static unsigned long Flags; - - static void PerformScan(); - static void PatchWinAPI(); - - static void NullSub(); - - static bool IsPageChangeAllowed(void* callee, void* addr, size_t len); - static void AssertCalleeModule(void* callee); - -#ifdef DEBUG_LOAD_LIBRARY - static HANDLE LoadLibary(std::wstring library, HANDLE file, DWORD flags, void* callee); - static HANDLE WINAPI LoadLibaryAStub(const char* library); - static HANDLE WINAPI LoadLibaryWStub(const wchar_t* library); - static HANDLE WINAPI LoadLibaryExAStub(const char* library, HANDLE file, DWORD flags); - static HANDLE WINAPI LoadLibaryExWStub(const wchar_t* library, HANDLE file, DWORD flags); -#endif - - static BOOL WINAPI VirtualProtectStub(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect); - static BOOL WINAPI VirtualProtectExStub(HANDLE hProcess, LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect); - - static void LostD3DStub(); - static void CinematicStub(); - static void SoundInitStub(int a1, int a2, int a3); - static void SoundInitDriverStub(); - - static void DObjGetWorldTagPosStub(); - static void AimTargetGetTagPosStub(); - - static void AcquireDebugPrivilege(HANDLE hToken); - - static NTSTATUS NTAPI NtCreateThreadExStub(PHANDLE hThread, ACCESS_MASK desiredAccess, LPVOID objectAttributes, HANDLE processHandle, LPTHREAD_START_ROUTINE startAddress, LPVOID parameter, BOOL createSuspended, DWORD stackZeroBits, DWORD sizeOfStackCommit, DWORD sizeOfStackReserve, LPVOID bytesBuffer); - static int ValidateThreadTermination(void* addr); - static void ThreadEntryPointStub(); - - static std::mutex ThreadMutex; - static std::vector OwnThreadIds; - static std::map> ThreadHookMap; - - static Utils::Hook CreateThreadHook; - static Utils::Hook LoadLibHook[6]; - static Utils::Hook VirtualProtectHook[2]; - }; -} - diff --git a/src/Components/Modules/Bots.cpp b/src/Components/Modules/Bots.cpp index 492ff427..edccc68b 100644 --- a/src/Components/Modules/Bots.cpp +++ b/src/Components/Modules/Bots.cpp @@ -149,7 +149,7 @@ namespace Components g_botai[entref.entnum] = {0}; g_botai[entref.entnum].weapon = 1; - g_botai[entref.entnum].active = false; + g_botai[entref.entnum].active = true; }); Script::AddMethod("BotWeapon", [](Game::scr_entref_t entref) // Usage: BotWeapon(); @@ -313,6 +313,18 @@ namespace Components } } + /* + * Should be called when a client drops from the server + * but not "between levels" (Quake-III-Arena) + */ + void Bots::ClientDisconnect_Hk(int clientNum) + { + g_botai[clientNum].active = false; + + // Call original function + Utils::Hook::Call(0x4AA430)(clientNum); + } + Bots::Bots() { // Replace connect string @@ -326,6 +338,9 @@ namespace Components Utils::Hook(0x441B80, Bots::G_SelectWeaponIndex_Hk, HOOK_JUMP).install()->quick(); + // Reset BotMovementInfo.active when client is dropped + Utils::Hook(0x625235, Bots::ClientDisconnect_Hk, HOOK_CALL).install()->quick(); + // Zero the bot command array for (auto i = 0u; i < std::extent_v; i++) { diff --git a/src/Components/Modules/Bots.hpp b/src/Components/Modules/Bots.hpp index 94b25c9a..910bacfa 100644 --- a/src/Components/Modules/Bots.hpp +++ b/src/Components/Modules/Bots.hpp @@ -21,5 +21,7 @@ namespace Components static void G_SelectWeaponIndex(int clientNum, int iWeaponIndex); static void G_SelectWeaponIndex_Hk(); + + static void ClientDisconnect_Hk(int clientNum); }; } diff --git a/src/Components/Modules/CardTitles.cpp b/src/Components/Modules/CardTitles.cpp index 396eebf5..e3ac4a5c 100644 --- a/src/Components/Modules/CardTitles.cpp +++ b/src/Components/Modules/CardTitles.cpp @@ -218,8 +218,5 @@ namespace Components // This is placed here in case the anticheat has been disabled! // This checks specifically for launching the process suspended to inject a dll -#if !defined(DISABLE_ANTICHEAT) - AntiCheat::CheckStartupTime(); -#endif } } diff --git a/src/Components/Modules/Changelog.cpp b/src/Components/Modules/Changelog.cpp index a6da0d83..93b5974d 100644 --- a/src/Components/Modules/Changelog.cpp +++ b/src/Components/Modules/Changelog.cpp @@ -55,9 +55,5 @@ namespace Components // Changelog UIFeeder::Add(62.0f, Changelog::GetChangelogCount, Changelog::GetChangelogText, Changelog::SelectChangelog); - -#ifndef DISABLE_ANTICHEAT - Scheduler::OnFrameAsync(AntiCheat::QuickCodeScanner1); -#endif } } diff --git a/src/Components/Modules/ClientCommand.cpp b/src/Components/Modules/ClientCommand.cpp index 8cc40d1c..060fb81f 100644 --- a/src/Components/Modules/ClientCommand.cpp +++ b/src/Components/Modules/ClientCommand.cpp @@ -180,7 +180,7 @@ namespace Components ClientCommand::Add("entitycount", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params) { - Logger::Print("Entity count = %i\n", *Game::level_num_entities); + Logger::Print("Entity count = %i\n", Game::level->num_entities); }); // Also known as: "vis" @@ -243,7 +243,7 @@ namespace Components ClientCommand::Add("g_testCmd", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params) { assert(ent != nullptr); - ent->client->ps.stunTime = 1000 + *Game::level_time; // 1000 is the default test stun time + ent->client->ps.stunTime = 1000 + Game::level->time; // 1000 is the default test stun time }); } diff --git a/src/Components/Modules/Discovery.cpp b/src/Components/Modules/Discovery.cpp index 93dbf96f..c71ca293 100644 --- a/src/Components/Modules/Discovery.cpp +++ b/src/Components/Modules/Discovery.cpp @@ -83,16 +83,6 @@ namespace Components ServerList::InsertRequest(address); } }); - - // This is placed here in case the anticheat has been disabled! - // Make sure this is called after the memory scan! -#ifndef DISABLE_ANTICHEAT - Utils::Hook(0x5ACB9E, []() // Somewhere in the renderer, past the scan check - { - AntiCheat::ScanIntegrityCheck(); - return Utils::Hook::Call(0x4AA720)(); - }, HOOK_CALL).install()->quick(); -#endif } void Discovery::preDestroy() diff --git a/src/Components/Modules/Exception.cpp b/src/Components/Modules/Exception.cpp index 3700587b..572bfdfd 100644 --- a/src/Components/Modules/Exception.cpp +++ b/src/Components/Modules/Exception.cpp @@ -137,10 +137,6 @@ namespace Components Utils::IO::CreateDir("minidumps"); PathCombineA(filename, "minidumps\\", Utils::String::VA("%s-" VERSION "-%s.dmp", exeFileName, filenameFriendlyTime)); -#ifndef DISABLE_ANTICHEAT - AntiCheat::UninstallLibHook(); -#endif - DWORD fileShare = FILE_SHARE_READ | FILE_SHARE_WRITE; HANDLE hFile = CreateFileA(filename, GENERIC_WRITE | GENERIC_READ, fileShare, nullptr, (fileShare & FILE_SHARE_WRITE) > 0 ? OPEN_ALWAYS : OPEN_EXISTING, NULL, nullptr); MINIDUMP_EXCEPTION_INFORMATION ex = { GetCurrentThreadId(), ExceptionInfo, FALSE }; @@ -157,10 +153,6 @@ namespace Components TerminateProcess(GetCurrentProcess(), ExceptionInfo->ExceptionRecord->ExceptionCode); } -#ifndef DISABLE_ANTICHEAT - AntiCheat::InstallLibHook(); -#endif - return EXCEPTION_CONTINUE_SEARCH; } diff --git a/src/Components/Modules/Gamepad.cpp b/src/Components/Modules/Gamepad.cpp index f6fc1aef..0d102b23 100644 --- a/src/Components/Modules/Gamepad.cpp +++ b/src/Components/Modules/Gamepad.cpp @@ -1763,7 +1763,7 @@ namespace Components void Gamepad::CG_RegisterDvars_Hk() { - // Call original method + // Call original function Utils::Hook::Call(0x4F8DC0)(); InitDvars(); diff --git a/src/Components/Modules/Gametypes.cpp b/src/Components/Modules/Gametypes.cpp index c155cf99..d7f567f4 100644 --- a/src/Components/Modules/Gametypes.cpp +++ b/src/Components/Modules/Gametypes.cpp @@ -98,15 +98,5 @@ namespace Components // Dynamically grab gametypes Utils::Hook(0x5FA46C, Gametypes::BuildGametypeList, HOOK_CALL).install()->quick(); // Scr_UpdateGameTypeList Utils::Hook(0x632155, Gametypes::BuildGametypeList, HOOK_CALL).install()->quick(); // UI_UpdateGameTypesList - - // This is placed here in case the anticheat has been disabled! - // Make sure this is called after every onther anticheat check! -#ifndef DISABLE_ANTICHEAT - Utils::Hook(0x5ACBA3, []() // Somewhere in the renderer, past other renderer hooks! - { - AntiCheat::FlagIntegrityCheck(); - return Utils::Hook::Call(0x50AB20)(); - }, HOOK_CALL).install()->quick(); -#endif } } diff --git a/src/Components/Modules/Localization.cpp b/src/Components/Modules/Localization.cpp index 3b385506..641f1b0f 100644 --- a/src/Components/Modules/Localization.cpp +++ b/src/Components/Modules/Localization.cpp @@ -297,13 +297,6 @@ namespace Components } } }); - -// #ifndef DISABLE_ANTICHEAT -// if (!Dedicated::IsEnabled() && !ZoneBuilder::IsEnabled() && !Utils::IsWineEnvironment() && !Loader::IsPerformingUnitTests()) -// { -// AntiCheat::PatchVirtualProtect(VirtualProtect, VirtualProtectEx); -// } -// #endif } Localization::~Localization() diff --git a/src/Components/Modules/Menus.cpp b/src/Components/Modules/Menus.cpp index f29b4499..2d316acf 100644 --- a/src/Components/Modules/Menus.cpp +++ b/src/Components/Modules/Menus.cpp @@ -70,7 +70,7 @@ namespace Components script->next = nullptr; - Game::source_t* source = allocator->allocate(); + auto* source = allocator->allocate(); if (!source) { Game::FreeMemory(script); @@ -116,7 +116,7 @@ namespace Components { Utils::Memory::Allocator* allocator = Utils::Memory::GetAllocator(); - Game::menuDef_t* menu = allocator->allocate(); + auto* menu = allocator->allocate(); if (!menu) return nullptr; menu->items = allocator->allocateArray(512); @@ -284,17 +284,7 @@ namespace Components if (menus.empty()) { - // // Try loading the original menu, if we can't load our custom one - // Game::menuDef_t* originalMenu = AssetHandler::FindOriginalAsset(Game::XAssetType::ASSET_TYPE_MENU, menudef->window.name).menu; - // - // if (originalMenu) - // { - // menus.push_back({ false, originalMenu }); - // } - // else - // { menus.push_back({ false, menudef }); // Native menu -// } } return menus; @@ -308,7 +298,7 @@ namespace Components if (menus.empty()) return nullptr; // Allocate new menu list - Game::MenuList* newList = allocator->allocate(); + auto* newList = allocator->allocate(); if (!newList) return nullptr; newList->menus = allocator->allocateArray(menus.size()); @@ -319,7 +309,7 @@ namespace Components } newList->name = allocator->duplicateString(menu); - newList->menuCount = menus.size(); + newList->menuCount = static_cast(menus.size()); // Copy new menus for (unsigned int i = 0; i < menus.size(); ++i) @@ -759,50 +749,14 @@ namespace Components void Menus::RegisterCustomMenusHook() { - Game::UiContext* uiInfoArray = (Game::UiContext*)0x62E2858; - // Game::MenuList list; - Utils::Hook::Call(0x401700)(); // call original load functions - //Game::XAssetHeader header = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_MENULIST, "ui_mp/iw4x.txt"); - //if (header.data && !(header.menuList->menuCount == 1 && !_stricmp("default_menu", header.menuList->menus[0]->window.name))) - //{ - // // Utils::Hook::Call(0x401700)(uiInfoArray, header.menuList, 1); // add loaded menus - // std::memcpy(&list, header.data, sizeof(Game::MenuList)); - // for (int i = 0; i < uiInfoArray->menuCount; i++) - // { - // for (int j = 0; j < list.menuCount; j++) - // { - // if (!list.menus[j]) continue; // skip already used entries - // if (!stricmp(list.menus[j]->window.name, uiInfoArray->Menus[i]->window.name)) - // { - // uiInfoArray->Menus[i] = list.menus[j]; // overwrite UiContext pointer - // list.menus[j] = nullptr; // clear entries that already exist so we don't add them later - // } - // } - // } - - // for (int i = 0; i < list.menuCount; i++) - // { - // if (list.menus[i]) - // { - // uiInfoArray->Menus[uiInfoArray->menuCount++] = list.menus[i]; - // } - // } - //} - - for (int i = 0; i < uiInfoArray->menuCount; i++) +#ifdef _DEBUG + for (int i = 0; i < Game::uiContext->menuCount; i++) { - OutputDebugStringA(Utils::String::VA("%s\n", uiInfoArray->Menus[i]->window.name)); + OutputDebugStringA(Utils::String::VA("%s\n", Game::uiContext->Menus[i]->window.name)); } - - /* - header = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_MENULIST, "ui_mp/mod.txt"); - if (header.data && !(header.menuList->menuCount == 1 && !_stricmp("default_menu", header.menuList->menus[0]->window.name))) - { - Utils::Hook::Call(0x401700)(uiInfoArray, header.menuList, 1); // add loaded menus - } - */ +#endif } Menus::Menus() @@ -824,12 +778,12 @@ namespace Components // Use the connect menu open call to update server motds Utils::Hook(0x428E48, []() + { + if (!Party::GetMotd().empty() && Party::Target() == *Game::connectedHost) { - if (!Party::GetMotd().empty() && Party::Target() == *Game::connectedHost) - { - Dvar::Var("didyouknow").set(Party::GetMotd()); - } - }, HOOK_CALL).install()->quick(); + Dvar::Var("didyouknow").set(Party::GetMotd()); + } + }, HOOK_CALL).install()->quick(); // Intercept menu painting Utils::Hook(0x4FFBDF, Menus::IsMenuVisible, HOOK_CALL).install()->quick(); @@ -844,53 +798,49 @@ namespace Components Utils::Hook::SetString(0x6FC790, "main_text"); Command::Add("openmenu", [](Command::Params* params) + { + if (params->size() != 2) { - if (params->size() != 2) - { - Logger::Print("USAGE: openmenu \n"); - return; - } + Logger::Print("USAGE: openmenu \n"); + return; + } - // Not quite sure if we want to do this if we're not ingame, but it's only needed for ingame menus. - if (Dvar::Var("cl_ingame").get()) - { - Game::Key_SetCatcher(0, 16); - } + // Not quite sure if we want to do this if we're not ingame, but it's only needed for ingame menus. + if (Dvar::Var("cl_ingame").get()) + { + Game::Key_SetCatcher(0, 16); + } - Game::Menus_OpenByName(Game::uiContext, params->get(1)); - }); + Game::Menus_OpenByName(Game::uiContext, params->get(1)); + }); Command::Add("reloadmenus", [](Command::Params*) + { + // Close all menus + Game::Menus_CloseAll(Game::uiContext); + + // Free custom menus + Menus::FreeEverything(); + + // Only disconnect if in-game, context is updated automatically! + if (Game::CL_IsCgameInitialized()) { - // Close all menus - Game::Menus_CloseAll(Game::uiContext); + Game::Cbuf_AddText(0, "disconnect\n"); + } + else + { + // Reinitialize ui context + Utils::Hook::Call(0x401700)(); - // Free custom menus - Menus::FreeEverything(); - - // Only disconnect if in-game, context is updated automatically! - if (Game::CL_IsCgameInitialized()) - { - Game::Cbuf_AddText(0, "disconnect\n"); - } - else - { - // Reinitialize ui context - Utils::Hook::Call(0x401700)(); - - // Reopen main menu - Game::Menus_OpenByName(Game::uiContext, "main_text"); - } - }); - -#ifndef DISABLE_ANTICHEAT - Scheduler::OnFrameAsync(AntiCheat::QuickCodeScanner2); -#endif + // Reopen main menu + Game::Menus_OpenByName(Game::uiContext, "main_text"); + } + }); Command::Add("mp_QuickMessage", [](Command::Params*) - { - Command::Execute("openmenu quickmessage"); - }); + { + Command::Execute("openmenu quickmessage"); + }); // Define custom menus here Menus::Add("ui_mp/changelog.menu"); diff --git a/src/Components/Modules/Script.cpp b/src/Components/Modules/Script.cpp index bbe73384..798a2541 100644 --- a/src/Components/Modules/Script.cpp +++ b/src/Components/Modules/Script.cpp @@ -607,7 +607,7 @@ namespace Components return; } - Logger::Print(*Game::level_scriptPrintChannel, "%s", str); + Logger::Print(Game::level->scriptPrintChannel, "%s", str); } }); diff --git a/src/Components/Modules/ServerList.cpp b/src/Components/Modules/ServerList.cpp index c2351a23..586c61e6 100644 --- a/src/Components/Modules/ServerList.cpp +++ b/src/Components/Modules/ServerList.cpp @@ -890,11 +890,6 @@ namespace Components // Add frame callback Scheduler::OnFrame(ServerList::Frame); - - // This is placed here in case the anticheat has been disabled! -#if !defined(DISABLE_ANTICHEAT) && defined(PROCTECT_PROCESS) - Scheduler::OnFrame(AntiCheat::ReadIntegrityCheck, true); -#endif } ServerList::~ServerList() diff --git a/src/Game/Functions.cpp b/src/Game/Functions.cpp index 75d37d74..d3043806 100644 --- a/src/Game/Functions.cpp +++ b/src/Game/Functions.cpp @@ -537,6 +537,8 @@ namespace Game ScreenPlacement* scrPlaceFullUnsafe = reinterpret_cast(0x1084460); + level_locals_t* level = reinterpret_cast(0x1A831A8); + void Sys_LockRead(FastCriticalSection* critSect) { InterlockedIncrement(&critSect->readCount); diff --git a/src/Game/Structs.hpp b/src/Game/Structs.hpp index e1561338..e4cee00b 100644 --- a/src/Game/Structs.hpp +++ b/src/Game/Structs.hpp @@ -7282,6 +7282,139 @@ namespace Game TempPriority tempPriority; }; + struct trigger_info_t + { + unsigned __int16 entnum; + unsigned __int16 otherEntnum; + int useCount; + int otherUseCount; + }; + + struct com_parse_mark_t + { + int lines; + const char* text; + int ungetToken; + int backup_lines; + const char* backup_text; + }; + + struct cached_tag_mat_t + { + int time; + int entnum; + unsigned __int16 name; + float tagMat[4][3]; + }; + + struct Turret + { + bool inuse; + int flags; + int fireTime; + float arcmin[2]; + float arcmax[2]; + float dropPitch; + int stance; + int prevStance; + int fireSndDelay; + float userOrigin[3]; + float playerSpread; + int state; + EntHandle target; + float targetOffset[3]; + EntHandle manualTarget; + float manualTargetOffset[3]; + int targetTime; + int stateChangeTime; + int modeChangeTime; + float maxRangeSquared; + int prevTargetIndex; + team_t eTeam; + int convergenceTime[2]; + float targetPos[3]; + float missOffsetNormalized[3]; + float scanSpeed; + float scanDecelYaw; + int scanPauseTime; + bool triggerDown; + float heatLevel; + int heatPenaltyEndTime; + float barrelRollRate; + int autoRotationStopDelay; + int lastAutoRotationRequestTime; + unsigned __int8 fireSnd; + unsigned __int8 fireSndPlayer; + unsigned __int8 stopSnd; + unsigned __int8 stopSndPlayer; + unsigned __int8 scanSnd; + }; + + static_assert(sizeof(Turret) == 0xC4); + + struct level_locals_t + { + gclient_s* clients; + gentity_s* gentities; + int num_entities; + gentity_s* firstFreeEnt; + gentity_s* lastFreeEnt; + Turret* turrets; + void* logFile; + int initializing; + int clientIsSpawning; + objective_t objectives[32]; + int maxclients; + int framenum; + int time; + int previousTime; + int frametime; + int startTime; + int teamScores[4]; + int lastTeammateHealthTime; + int bUpdateScoresForIntermission; + bool teamHasRadar[4]; + bool teamRadarBlocked[4]; + int manualNameChange; + int numConnectedClients; + int sortedClients[18]; + char voteString[1024]; + char voteDisplayString[1024]; + int voteTime; + int voteExecuteTime; + int voteYes; + int voteNo; + int numVotingClients; + SpawnVar spawnVar; + int savepersist; + EntHandle droppedWeaponCue[32]; + float fFogOpaqueDist; + float fFogOpaqueDistSqrd; + int currentPlayerClone; + trigger_info_t pendingTriggerList[256]; + trigger_info_t currentTriggerList[256]; + int pendingTriggerListSize; + int currentTriggerListSize; + int finished; + int bPlayerIgnoreRadiusDamage; + int bPlayerIgnoreRadiusDamageLatched; + int registerWeapons; + int bRegisterItems; + int currentEntityThink; + void* openScriptIOFileHandles[1]; + char* openScriptIOFileBuffers[1]; + com_parse_mark_t currentScriptIOLineMark[1]; + cached_tag_mat_t cachedTagMat; + int scriptPrintChannel; + float compassMapUpperLeft[2]; + float compassMapWorldSize[2]; + float compassNorth[2]; + void* vehicles; + int hudElemLastAssignedSoundID; + }; + + static_assert(sizeof(level_locals_t) == 0x2F78); + #pragma endregion #ifndef IDA diff --git a/src/Main.cpp b/src/Main.cpp index 0e78434b..db9c14ad 100644 --- a/src/Main.cpp +++ b/src/Main.cpp @@ -55,7 +55,7 @@ BOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD ul_reason_for_call, LPVOID /*l Steam::Proxy::RunMod(); // Ensure we're working with our desired binary - char* _module = reinterpret_cast(0x400000); + auto* _module = reinterpret_cast(0x400000); auto hash1 = Utils::Cryptography::JenkinsOneAtATime::Compute(_module + 0x1000, 0x2D531F); // .text auto hash2 = Utils::Cryptography::JenkinsOneAtATime::Compute(_module + 0x2D75FC, 0xBDA04); // .rdata if ((hash1 != 0x54684DBE @@ -67,17 +67,6 @@ BOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD ul_reason_for_call, LPVOID /*l return FALSE; } -#ifndef DISABLE_ANTICHEAT - []() - { - if (!Components::Dedicated::IsEnabled() && !Components::Loader::IsPerformingUnitTests()) - { - Components::AntiCheat::ProtectProcess(); - Components::AntiCheat::PatchThreadCreation(); - } - }(); -#endif - DWORD oldProtect; VirtualProtect(_module + 0x1000, 0x2D6000, PAGE_EXECUTE_READ, &oldProtect); // Protect the .text segment @@ -88,18 +77,6 @@ BOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD ul_reason_for_call, LPVOID /*l { Main::Uninitialize(); } - else if (ul_reason_for_call == DLL_THREAD_ATTACH) - { -#ifndef DISABLE_ANTICHEAT - []() - { - if (!Components::Dedicated::IsEnabled() && !Components::Loader::IsPerformingUnitTests()) - { - Components::AntiCheat::VerifyThreadIntegrity(); - } - }(); -#endif - } return TRUE; } diff --git a/src/STDInclude.hpp b/src/STDInclude.hpp index bdec22d1..4480f216 100644 --- a/src/STDInclude.hpp +++ b/src/STDInclude.hpp @@ -90,17 +90,6 @@ #undef min #endif -// VMProtect -// #define USE_VMP -#ifdef USE_VMP -#include -#define __VMProtectBeginUltra VMProtectBeginUltra -#define __VMProtectEnd VMProtectEnd() -#else -#define __VMProtectBeginUltra -#define __VMProtectEnd -#endif - // Protobuf #include "proto/session.pb.h" #include "proto/party.pb.h" diff --git a/src/Utils/Utils.cpp b/src/Utils/Utils.cpp index e17480bf..5166b84f 100644 --- a/src/Utils/Utils.cpp +++ b/src/Utils/Utils.cpp @@ -123,10 +123,6 @@ namespace Utils void SafeShellExecute(HWND hwnd, LPCSTR lpOperation, LPCSTR lpFile, LPCSTR lpParameters, LPCSTR lpDirectory, INT nShowCmd) { -#ifndef DISABLE_ANTICHEAT - Components::AntiCheat::LibUnlocker _; -#endif - [=]() { __try