Merge branch 'develop' into branding

This commit is contained in:
Edo 2022-04-15 14:50:58 +02:00 committed by GitHub
commit 653760952c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 207 additions and 1386 deletions

View File

@ -39,7 +39,7 @@ jobs:
uses: microsoft/setup-msbuild@v1.1 uses: microsoft/setup-msbuild@v1.1
- name: Generate project files - name: Generate project files
run: tools/premake5 vs2022 --ac-disable run: tools/premake5 vs2022
- name: Set up problem matching - name: Set up problem matching
uses: ammaraskar/msvc-problem-matcher@master uses: ammaraskar/msvc-problem-matcher@master

View File

@ -19,9 +19,6 @@
|:----------------------------|:-----------------------------------------------| |:----------------------------|:-----------------------------------------------|
| `--copy-to=PATH` | Optional, copy the DLL to a custom folder after build, define the path here if wanted. | | `--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. | | `--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-unit-tests` | Always compile unit tests. |
| `--force-exception-handler` | Install custom unhandled exception handler even for Debug builds. | | `--force-exception-handler` | Install custom unhandled exception handler even for Debug builds. |
| `--force-minidump-upload` | Upload minidumps even for Debug builds. | | `--force-minidump-upload` | Upload minidumps even for Debug builds. |

View File

@ -1,4 +1,4 @@
@echo off @echo off
echo Updating submodules... echo Updating submodules...
call git submodule update --init --recursive call git submodule update --init --recursive
call tools\premake5 %* vs2022 --ac-disable call tools\premake5 %* vs2022

Binary file not shown.

Binary file not shown.

View File

@ -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

View File

@ -71,21 +71,6 @@ newoption {
description = "Copy debug information for binaries as well to the path given via --copy-to." 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 { newoption {
trigger = "force-unit-tests", trigger = "force-unit-tests",
description = "Always compile unit tests." description = "Always compile unit tests."
@ -269,23 +254,11 @@ workspace "iw4x"
"./src", "./src",
"./lib/include", "./lib/include",
} }
syslibdirs {
"./lib/bin",
}
resincludedirs { resincludedirs {
"$(ProjectDir)src" -- fix for VS IDE "$(ProjectDir)src" -- fix for VS IDE
} }
-- Debug flags -- 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 if _OPTIONS["force-unit-tests"] then
defines {"FORCE_UNIT_TESTS"} defines {"FORCE_UNIT_TESTS"}
end end

View File

@ -1,944 +0,0 @@
#include <STDInclude.hpp>
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<DWORD> AntiCheat::OwnThreadIds;
std::map<DWORD, std::shared_ptr<Utils::Hook>> 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<BYTE>(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<char*>(callee), &hModuleTarget);
GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, reinterpret_cast<char*>(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<uint32_t>(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<char*>(kernel32Str), sizeof kernel32Str), -1).data());
if (kernel32)
{
FARPROC loadLibA = GetProcAddress(kernel32, Utils::String::XOR(std::string(reinterpret_cast<char*>(loadLibAStr), sizeof loadLibAStr), -1).data());
FARPROC loadLibW = GetProcAddress(kernel32, Utils::String::XOR(std::string(reinterpret_cast<char*>(loadLibWStr), sizeof loadLibWStr), -1).data());
std::string libExA = Utils::String::XOR(std::string(reinterpret_cast<char*>(loadLibAStr), sizeof loadLibAStr), -1);
std::string libExW = Utils::String::XOR(std::string(reinterpret_cast<char*>(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<char*>(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<unsigned int> 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<char*>(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<unsigned int> 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<char*>(0x400FFF);
unsigned int hash = Utils::Cryptography::JenkinsOneAtATime::Compute(textBase + 1, textSize + 1);
if (hashVal.has_value() && hash != hashVal.value())
{
Utils::Hook::Set<BYTE>(0x42A667, 0x90); // Crash
}
hashVal.emplace(hash);
__VMProtectEnd;
}
void AntiCheat::QuickCodeScanner2()
{
__VMProtectBeginUltra("");
static Utils::Time::Interval interval;
static std::optional<unsigned int> hashVal;
if (!interval.elapsed(42s)) return;
interval.update();
// Hash .text segment
unsigned int hash = Utils::Cryptography::JenkinsOneAtATime::Compute(reinterpret_cast<char*>(0x401000), 0x2D6000);
if (hashVal.has_value() && hash != hashVal.value())
{
Utils::Hook::Set<BYTE>(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<char*>(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<uint32_t>(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<void()>(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<void()>(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<char*>(callee), &hModuleTarget);
GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, reinterpret_cast<char*>(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<PSID>(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<TOKEN_USER*>(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<PACL>(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<SECURITY_DESCRIPTOR>();
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<std::mutex> _(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<char*>(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<std::mutex> _(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<std::mutex> _(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<std::mutex> _(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<std::mutex> _(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<char*>(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<Utils::Hook> hook = std::make_shared<Utils::Hook>();
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(&current);
GetProcessTimes(GetCurrentProcess(), &creation, &exit, &kernel, &user);
FileTimeToSystemTime(&creation, &creationSt);
AntiCheat::SystemTimeDiff(&current, &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);
}
}
}

View File

@ -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<DWORD> OwnThreadIds;
static std::map<DWORD, std::shared_ptr<Utils::Hook>> ThreadHookMap;
static Utils::Hook CreateThreadHook;
static Utils::Hook LoadLibHook[6];
static Utils::Hook VirtualProtectHook[2];
};
}

View File

@ -149,7 +149,7 @@ namespace Components
g_botai[entref.entnum] = {0}; g_botai[entref.entnum] = {0};
g_botai[entref.entnum].weapon = 1; 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: <bot> BotWeapon(<str>); Script::AddMethod("BotWeapon", [](Game::scr_entref_t entref) // Usage: <bot> BotWeapon(<str>);
@ -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<void(int)>(0x4AA430)(clientNum);
}
Bots::Bots() Bots::Bots()
{ {
// Replace connect string // Replace connect string
@ -326,6 +338,9 @@ namespace Components
Utils::Hook(0x441B80, Bots::G_SelectWeaponIndex_Hk, HOOK_JUMP).install()->quick(); 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 // Zero the bot command array
for (auto i = 0u; i < std::extent_v<decltype(g_botai)>; i++) for (auto i = 0u; i < std::extent_v<decltype(g_botai)>; i++)
{ {

View File

@ -21,5 +21,7 @@ namespace Components
static void G_SelectWeaponIndex(int clientNum, int iWeaponIndex); static void G_SelectWeaponIndex(int clientNum, int iWeaponIndex);
static void G_SelectWeaponIndex_Hk(); static void G_SelectWeaponIndex_Hk();
static void ClientDisconnect_Hk(int clientNum);
}; };
} }

View File

@ -218,8 +218,5 @@ namespace Components
// This is placed here in case the anticheat has been disabled! // This is placed here in case the anticheat has been disabled!
// This checks specifically for launching the process suspended to inject a dll // This checks specifically for launching the process suspended to inject a dll
#if !defined(DISABLE_ANTICHEAT)
AntiCheat::CheckStartupTime();
#endif
} }
} }

View File

@ -55,9 +55,5 @@ namespace Components
// Changelog // Changelog
UIFeeder::Add(62.0f, Changelog::GetChangelogCount, Changelog::GetChangelogText, Changelog::SelectChangelog); UIFeeder::Add(62.0f, Changelog::GetChangelogCount, Changelog::GetChangelogText, Changelog::SelectChangelog);
#ifndef DISABLE_ANTICHEAT
Scheduler::OnFrameAsync(AntiCheat::QuickCodeScanner1);
#endif
} }
} }

View File

@ -180,7 +180,7 @@ namespace Components
ClientCommand::Add("entitycount", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params) 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" // 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) ClientCommand::Add("g_testCmd", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params)
{ {
assert(ent != nullptr); 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
}); });
} }

View File

@ -83,16 +83,6 @@ namespace Components
ServerList::InsertRequest(address); 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<void()>(0x4AA720)();
}, HOOK_CALL).install()->quick();
#endif
} }
void Discovery::preDestroy() void Discovery::preDestroy()

View File

@ -137,10 +137,6 @@ namespace Components
Utils::IO::CreateDir("minidumps"); Utils::IO::CreateDir("minidumps");
PathCombineA(filename, "minidumps\\", Utils::String::VA("%s-" VERSION "-%s.dmp", exeFileName, filenameFriendlyTime)); 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; 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); 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 }; MINIDUMP_EXCEPTION_INFORMATION ex = { GetCurrentThreadId(), ExceptionInfo, FALSE };
@ -157,10 +153,6 @@ namespace Components
TerminateProcess(GetCurrentProcess(), ExceptionInfo->ExceptionRecord->ExceptionCode); TerminateProcess(GetCurrentProcess(), ExceptionInfo->ExceptionRecord->ExceptionCode);
} }
#ifndef DISABLE_ANTICHEAT
AntiCheat::InstallLibHook();
#endif
return EXCEPTION_CONTINUE_SEARCH; return EXCEPTION_CONTINUE_SEARCH;
} }

View File

@ -1763,7 +1763,7 @@ namespace Components
void Gamepad::CG_RegisterDvars_Hk() void Gamepad::CG_RegisterDvars_Hk()
{ {
// Call original method // Call original function
Utils::Hook::Call<void()>(0x4F8DC0)(); Utils::Hook::Call<void()>(0x4F8DC0)();
InitDvars(); InitDvars();

View File

@ -98,15 +98,5 @@ namespace Components
// Dynamically grab gametypes // Dynamically grab gametypes
Utils::Hook(0x5FA46C, Gametypes::BuildGametypeList, HOOK_CALL).install()->quick(); // Scr_UpdateGameTypeList Utils::Hook(0x5FA46C, Gametypes::BuildGametypeList, HOOK_CALL).install()->quick(); // Scr_UpdateGameTypeList
Utils::Hook(0x632155, Gametypes::BuildGametypeList, HOOK_CALL).install()->quick(); // UI_UpdateGameTypesList 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<void()>(0x50AB20)();
}, HOOK_CALL).install()->quick();
#endif
} }
} }

View File

@ -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() Localization::~Localization()

View File

@ -70,7 +70,7 @@ namespace Components
script->next = nullptr; script->next = nullptr;
Game::source_t* source = allocator->allocate<Game::source_t>(); auto* source = allocator->allocate<Game::source_t>();
if (!source) if (!source)
{ {
Game::FreeMemory(script); Game::FreeMemory(script);
@ -116,7 +116,7 @@ namespace Components
{ {
Utils::Memory::Allocator* allocator = Utils::Memory::GetAllocator(); Utils::Memory::Allocator* allocator = Utils::Memory::GetAllocator();
Game::menuDef_t* menu = allocator->allocate<Game::menuDef_t>(); auto* menu = allocator->allocate<Game::menuDef_t>();
if (!menu) return nullptr; if (!menu) return nullptr;
menu->items = allocator->allocateArray<Game::itemDef_s*>(512); menu->items = allocator->allocateArray<Game::itemDef_s*>(512);
@ -284,17 +284,7 @@ namespace Components
if (menus.empty()) 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 menus.push_back({ false, menudef }); // Native menu
// }
} }
return menus; return menus;
@ -308,7 +298,7 @@ namespace Components
if (menus.empty()) return nullptr; if (menus.empty()) return nullptr;
// Allocate new menu list // Allocate new menu list
Game::MenuList* newList = allocator->allocate<Game::MenuList>(); auto* newList = allocator->allocate<Game::MenuList>();
if (!newList) return nullptr; if (!newList) return nullptr;
newList->menus = allocator->allocateArray<Game::menuDef_t*>(menus.size()); newList->menus = allocator->allocateArray<Game::menuDef_t*>(menus.size());
@ -319,7 +309,7 @@ namespace Components
} }
newList->name = allocator->duplicateString(menu); newList->name = allocator->duplicateString(menu);
newList->menuCount = menus.size(); newList->menuCount = static_cast<int>(menus.size());
// Copy new menus // Copy new menus
for (unsigned int i = 0; i < menus.size(); ++i) for (unsigned int i = 0; i < menus.size(); ++i)
@ -759,50 +749,14 @@ namespace Components
void Menus::RegisterCustomMenusHook() void Menus::RegisterCustomMenusHook()
{ {
Game::UiContext* uiInfoArray = (Game::UiContext*)0x62E2858;
// Game::MenuList list;
Utils::Hook::Call<void()>(0x401700)(); // call original load functions Utils::Hook::Call<void()>(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<void(void*, Game::MenuList*, int)>(0x401700)(uiInfoArray, header.menuList, 1); // add loaded menus
// std::memcpy(&list, header.data, sizeof(Game::MenuList));
// for (int i = 0; i < uiInfoArray->menuCount; i++) #ifdef _DEBUG
// { for (int i = 0; i < Game::uiContext->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++)
{ {
OutputDebugStringA(Utils::String::VA("%s\n", uiInfoArray->Menus[i]->window.name)); OutputDebugStringA(Utils::String::VA("%s\n", Game::uiContext->Menus[i]->window.name));
} }
#endif
/*
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<void(void*, Game::MenuList*, int)>(0x401700)(uiInfoArray, header.menuList, 1); // add loaded menus
}
*/
} }
Menus::Menus() Menus::Menus()
@ -883,10 +837,6 @@ namespace Components
} }
}); });
#ifndef DISABLE_ANTICHEAT
Scheduler::OnFrameAsync(AntiCheat::QuickCodeScanner2);
#endif
Command::Add("mp_QuickMessage", [](Command::Params*) Command::Add("mp_QuickMessage", [](Command::Params*)
{ {
Command::Execute("openmenu quickmessage"); Command::Execute("openmenu quickmessage");

View File

@ -607,7 +607,7 @@ namespace Components
return; return;
} }
Logger::Print(*Game::level_scriptPrintChannel, "%s", str); Logger::Print(Game::level->scriptPrintChannel, "%s", str);
} }
}); });

View File

@ -890,11 +890,6 @@ namespace Components
// Add frame callback // Add frame callback
Scheduler::OnFrame(ServerList::Frame); 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() ServerList::~ServerList()

View File

@ -537,6 +537,8 @@ namespace Game
ScreenPlacement* scrPlaceFullUnsafe = reinterpret_cast<ScreenPlacement*>(0x1084460); ScreenPlacement* scrPlaceFullUnsafe = reinterpret_cast<ScreenPlacement*>(0x1084460);
level_locals_t* level = reinterpret_cast<level_locals_t*>(0x1A831A8);
void Sys_LockRead(FastCriticalSection* critSect) void Sys_LockRead(FastCriticalSection* critSect)
{ {
InterlockedIncrement(&critSect->readCount); InterlockedIncrement(&critSect->readCount);

View File

@ -7282,6 +7282,139 @@ namespace Game
TempPriority tempPriority; 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 #pragma endregion
#ifndef IDA #ifndef IDA

View File

@ -55,7 +55,7 @@ BOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD ul_reason_for_call, LPVOID /*l
Steam::Proxy::RunMod(); Steam::Proxy::RunMod();
// Ensure we're working with our desired binary // Ensure we're working with our desired binary
char* _module = reinterpret_cast<char*>(0x400000); auto* _module = reinterpret_cast<char*>(0x400000);
auto hash1 = Utils::Cryptography::JenkinsOneAtATime::Compute(_module + 0x1000, 0x2D531F); // .text auto hash1 = Utils::Cryptography::JenkinsOneAtATime::Compute(_module + 0x1000, 0x2D531F); // .text
auto hash2 = Utils::Cryptography::JenkinsOneAtATime::Compute(_module + 0x2D75FC, 0xBDA04); // .rdata auto hash2 = Utils::Cryptography::JenkinsOneAtATime::Compute(_module + 0x2D75FC, 0xBDA04); // .rdata
if ((hash1 != 0x54684DBE if ((hash1 != 0x54684DBE
@ -67,17 +67,6 @@ BOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD ul_reason_for_call, LPVOID /*l
return FALSE; return FALSE;
} }
#ifndef DISABLE_ANTICHEAT
[]()
{
if (!Components::Dedicated::IsEnabled() && !Components::Loader::IsPerformingUnitTests())
{
Components::AntiCheat::ProtectProcess();
Components::AntiCheat::PatchThreadCreation();
}
}();
#endif
DWORD oldProtect; DWORD oldProtect;
VirtualProtect(_module + 0x1000, 0x2D6000, PAGE_EXECUTE_READ, &oldProtect); // Protect the .text segment 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(); 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; return TRUE;
} }

View File

@ -90,17 +90,6 @@
#undef min #undef min
#endif #endif
// VMProtect
// #define USE_VMP
#ifdef USE_VMP
#include <VMProtect/VMProtectSDK.h>
#define __VMProtectBeginUltra VMProtectBeginUltra
#define __VMProtectEnd VMProtectEnd()
#else
#define __VMProtectBeginUltra
#define __VMProtectEnd
#endif
// Protobuf // Protobuf
#include "proto/session.pb.h" #include "proto/session.pb.h"
#include "proto/party.pb.h" #include "proto/party.pb.h"

View File

@ -123,10 +123,6 @@ namespace Utils
void SafeShellExecute(HWND hwnd, LPCSTR lpOperation, LPCSTR lpFile, LPCSTR lpParameters, LPCSTR lpDirectory, INT nShowCmd) void SafeShellExecute(HWND hwnd, LPCSTR lpOperation, LPCSTR lpFile, LPCSTR lpParameters, LPCSTR lpDirectory, INT nShowCmd)
{ {
#ifndef DISABLE_ANTICHEAT
Components::AntiCheat::LibUnlocker _;
#endif
[=]() [=]()
{ {
__try __try