From d530d0c72690f9beb0e5b044d33c641d6af9713c Mon Sep 17 00:00:00 2001 From: Matt Woodrow Date: Tue, 1 Jul 2008 09:04:00 +0000 Subject: [PATCH] Big critical hits cleanup. Cleaner, easier to maintain, uses less memory and should be much less likely to cause crashes when valve change things on me. --HG-- extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%402317 --- .../{detours.h => CDetour/detourhelpers.h} | 10 +- extensions/tf2/CDetour/detours.cpp | 341 +++++++++++++++ extensions/tf2/CDetour/detours.h | 166 ++++++++ extensions/tf2/Makefile | 6 +- extensions/tf2/asm/asm.c | 329 +++++++++++++++ extensions/tf2/asm/asm.h | 38 ++ extensions/tf2/criticals.cpp | 388 +++--------------- extensions/tf2/criticals.h | 113 +---- extensions/tf2/extension.cpp | 11 +- extensions/tf2/msvc8/tf2.vcproj | 32 +- extensions/tf2/msvc9/tf2.vcproj | 32 +- gamedata/sm-tf2.games.txt | 33 +- 12 files changed, 1013 insertions(+), 486 deletions(-) rename extensions/tf2/{detours.h => CDetour/detourhelpers.h} (89%) create mode 100644 extensions/tf2/CDetour/detours.cpp create mode 100644 extensions/tf2/CDetour/detours.h create mode 100644 extensions/tf2/asm/asm.c create mode 100644 extensions/tf2/asm/asm.h diff --git a/extensions/tf2/detours.h b/extensions/tf2/CDetour/detourhelpers.h similarity index 89% rename from extensions/tf2/detours.h rename to extensions/tf2/CDetour/detourhelpers.h index 6ed0cc01..9d701b4e 100644 --- a/extensions/tf2/detours.h +++ b/extensions/tf2/CDetour/detourhelpers.h @@ -1,8 +1,8 @@ /** * vim: set ts=4 : * ============================================================================= - * SourceMod Team Fortress 2 Extension - * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. + * SourceMod SDKTools Extension + * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved. * ============================================================================= * * This program is free software; you can redistribute it and/or modify it under @@ -29,8 +29,8 @@ * Version: $Id$ */ -#ifndef _INCLUDE_SOURCEMOD_DETOURS_H_ -#define _INCLUDE_SOURCEMOD_DETOURS_H_ +#ifndef _INCLUDE_SOURCEMOD_DETOURHELPERS_H_ +#define _INCLUDE_SOURCEMOD_DETOURHELPERS_H_ #if defined PLATFORM_LINUX #include @@ -95,4 +95,4 @@ inline void ApplyPatch(void *address, int offset, const patch_t *patch, patch_t } } -#endif //_INCLUDE_SOURCEMOD_DETOURS_H_ +#endif //_INCLUDE_SOURCEMOD_DETOURHELPERS_H_ diff --git a/extensions/tf2/CDetour/detours.cpp b/extensions/tf2/CDetour/detours.cpp new file mode 100644 index 00000000..f3ccd14e --- /dev/null +++ b/extensions/tf2/CDetour/detours.cpp @@ -0,0 +1,341 @@ +#include "detours.h" +#include + +ISourcePawnEngine *CDetourManager::spengine = NULL; +IGameConfig *CDetourManager::gameconf = NULL; +int CDetourManager::returnValue = 0; + +void CDetourManager::Init(ISourcePawnEngine *spengine, IGameConfig *gameconf) +{ + CDetourManager::spengine = spengine; + CDetourManager::gameconf = gameconf; +} + +CDetour *CDetourManager::CreateDetour(void *callbackfunction, size_t paramsize, const char *signame) +{ + CDetour *detour = new CDetour(callbackfunction, paramsize, signame); + if (detour) + { + if (!detour->Init(spengine, gameconf)) + { + delete detour; + return NULL; + } + + return detour; + } + + return NULL; +} + +void CDetourManager::DeleteDetour(CDetour *detour) +{ + delete detour; +} + +CBlocker * CDetourManager::CreateFunctionBlock( const char *signame, bool isVoid ) +{ + CBlocker *block = new CBlocker(signame, isVoid); + + if (block) + { + if (!block->Init(spengine, gameconf)) + { + delete block; + return NULL; + } + + return block; + } + + return NULL; +} + +void CDetourManager::DeleteFunctionBlock(CBlocker *block) +{ + delete block; +} + +CDetour::CDetour(void *callbackfunction, size_t paramsize, const char *signame) +{ + enabled = false; + detoured = false; + detour_address = NULL; + detour_callback = NULL; + this->signame = signame; + this->callbackfunction = callbackfunction; + spengine = NULL; + gameconf = NULL; + this->paramsize = paramsize; +} + +CDetour::~CDetour() +{ + DeleteDetour(); +} + +bool CDetour::Init(ISourcePawnEngine *spengine, IGameConfig *gameconf) +{ + this->spengine = spengine; + this->gameconf = gameconf; + + if (!CreateDetour()) + { + enabled = false; + return enabled; + } + + enabled = true; + + return enabled; +} + +bool CDetour::IsEnabled() +{ + return enabled; +} + +bool CDetour::CreateDetour() +{ + if (!gameconf->GetMemSig(signame, &detour_address)) + { + g_pSM->LogError(myself, "Could not locate %s - Disabling detour", signame); + return false; + } + + if (!detour_address) + { + g_pSM->LogError(myself, "Sigscan for %s failed - Disabling detour", signame); + return false; + } + + detour_restore.bytes = copy_bytes((unsigned char *)detour_address, NULL, OP_JMP_SIZE); + + /* First, save restore bits */ + for (size_t i=0; iExecAlloc(100); + JitWriter wr; + JitWriter *jit = ≀ + jit_uint32_t CodeSize = 0; + + wr.outbase = NULL; + wr.outptr = NULL; + +jit_rewind: + + /* Push all our params onto the stack */ + for (size_t i=0; iAllocatePageMemory(CodeSize); + spengine->SetReadWrite(wr.outbase); + wr.outptr = wr.outbase; + detour_callback = wr.outbase; + goto jit_rewind; + } + + spengine->SetReadExecute(wr.outbase); + + return true; +} + +void CDetour::EnableDetour() +{ + if (!detoured) + { + DoGatePatch((unsigned char *)detour_address, &detour_callback); + detoured = true; + } +} + +void CDetour::DeleteDetour() +{ + if (detoured) + { + DisableDetour(); + } + + if (detour_callback) + { + /* Free the gate */ + spengine->ExecFree(detour_callback); + detour_callback = NULL; + } +} + +void CDetour::DisableDetour() +{ + if (detoured) + { + /* Remove the patch */ + /* This may screw up */ + ApplyPatch(detour_address, 0, &detour_restore, NULL); + detoured = false; + } +} + +CBlocker::CBlocker( const char *signame, bool isVoid ) +{ + this->isVoid = isVoid; + isEnabled = false; + isValid = false; + + spengine = NULL; + gameconf = NULL; + block_address = NULL; + block_sig = signame; + + if (isVoid) + { + /* Void functions we only patch in a 'ret' (1 byte) */ + block_restore.bytes = 1; + } + else + { + /* Normal functions need an mov eax, value */ + block_restore.bytes = 6; + } +} + +void CBlocker::EnableBlock( int returnValue ) +{ + if (!isValid || isEnabled) + { + return; + } + + /* First, save restore bits */ + for (size_t i=0; ispengine = spengine; + this->gameconf = gameconf; + + if (!gameconf->GetMemSig(block_sig, &block_address)) + { + g_pSM->LogError(myself, "Could not locate %s - Disabling blocker", block_sig); + isValid = false; + return false; + } + + if (!block_address) + { + g_pSM->LogError(myself, "Sigscan for %s failed - Disabling blocker", block_sig); + isValid = false; + return false; + } + + isValid = true; + + return true; +} diff --git a/extensions/tf2/CDetour/detours.h b/extensions/tf2/CDetour/detours.h new file mode 100644 index 00000000..ca995320 --- /dev/null +++ b/extensions/tf2/CDetour/detours.h @@ -0,0 +1,166 @@ + +#ifndef _INCLUDE_SOURCEMOD_DETOURS_H_ +#define _INCLUDE_SOURCEMOD_DETOURS_H_ + +#include "extension.h" +#include +#include +#include "detourhelpers.h" + +/** + * CDetours class for SourceMod Extensions by pRED* + * detourhelpers.h entirely stolen from CSS:DM and were written by BAILOPAN (I assume). + * asm.h/c from devmaster.net (thanks cybermind) + */ + +class CDetourManager; + +class CDetour +{ +public: + + bool IsEnabled(); + + /** + * These would be somewhat self-explanatory I hope + */ + void EnableDetour(); + void DisableDetour(); + + friend class CDetourManager; + +protected: + CDetour(void *callbackfunction, size_t paramsize, const char *signame); + ~CDetour(); + + bool Init(ISourcePawnEngine *spengine, IGameConfig *gameconf); +private: + + /* These create/delete the allocated memory */ + bool CreateDetour(); + void DeleteDetour(); + + bool enabled; + bool detoured; + + patch_t detour_restore; + void *detour_address; + void *detour_callback; + + const char *signame; + + void *callbackfunction; + + size_t paramsize; + + ISourcePawnEngine *spengine; + IGameConfig *gameconf; +}; + +class CBlocker +{ +public: + void EnableBlock(int returnValue = 0); + void DisableBlock(); + + friend class CDetourManager; + +protected: + CBlocker(const char *signame, bool isVoid); + ~CBlocker(); + + bool Init(ISourcePawnEngine *spengine, IGameConfig *gameconf); + +private: + bool isValid; + bool isEnabled; + bool isVoid; + patch_t block_restore; + void *block_address; + + const char *block_sig; + + ISourcePawnEngine *spengine; + IGameConfig *gameconf; +}; + +class CDetourManager +{ +public: + + /** + * Return Types for Detours + */ + enum DetourReturn + { + DetourReturn_Ignored = 0, /** Ignore our result and let the original function run */ + DetourReturn_Override = 1, /** Block the original function from running and use our return value */ + }; + + static void Init(ISourcePawnEngine *spengine, IGameConfig *gameconf); + + /** + * Creates a new detour + * @param callbackfunction Void pointer to your detour callback function. This should be a static function. + * It should have pointer to the thisptr as the first param and then the same params + * as the original function. Use void * for unknown types. + * @param paramsize This is usually the number of params the function has (not including thisptr). If the function + * passes complex types by value you need to add the sizeof() the type (aligned to 4 bytes). + * Ie: passing something of size 8 would count as 2 in the param count. + * @param signame Section name containing a signature to fetch from the gamedata file. + * @return A new CDetour pointer to control your detour. + * + * Example: + * + * CBaseServer::ConnectClient(netadr_s &, int, int, int, char const*, char const*, char const*, int) + * + * Callback: + * DetourReturn ConnectClientDetour(void *CBaseServer, void *netaddr_s, int something, int something2, int something3, char const* name, char const* pass, const char* steamcert, int len); + * + * Creation: + * CDetourManager::CreateDetour((void *)&ConnectClientDetour, 8, "ConnectClient"); + */ + static CDetour *CreateDetour(void *callbackfunction, size_t paramsize, const char *signame); + + /** + * Deletes a detour + */ + static void DeleteDetour(CDetour *detour); + + /** + * Creates a function blocker. This is slightly faster than a detour because it avoids a call. + * + * @param signame Section name containing a signature to fetch from the gamedata file. + * @param isVoid Specifies if the function can return void. + */ + static CBlocker *CreateFunctionBlock(const char *signame, bool isVoid); + + /** + * Delete a function blocker. + */ + static void DeleteFunctionBlock(CBlocker *block); + + + /** + * Global DetourReturn value to use for the current hook + */ + static int returnValue; + + friend class CBlocker; + friend class CDetour; + +private: + static ISourcePawnEngine *spengine; + static IGameConfig *gameconf; +}; + +typedef bool DetourReturn; + +#define DETOUR_RESULT_IGNORED false +#define DETOUR_RESULT_OVERRIDE true + +#define SET_DETOUR_RETURN_VALUE(value) CDetourManager::returnValue=(int)value +#define RETURN_DETOUR(result) return result +#define RETURN_DETOUR_VALUE(result,value) do { SET_DETOUR_RETURN_VALUE(value); return (result); } while(0) + +#endif // _INCLUDE_SOURCEMOD_DETOURS_H_ diff --git a/extensions/tf2/Makefile b/extensions/tf2/Makefile index f98b48c5..d8b772a8 100644 --- a/extensions/tf2/Makefile +++ b/extensions/tf2/Makefile @@ -18,7 +18,7 @@ PROJECT = game.tf2 USEMETA = true OBJECTS = sdk/smsdk_ext.cpp extension.cpp natives.cpp RegNatives.cpp criticals.cpp \ - util.cpp + util.cpp CDetour/detours.cpp asm/asm.c ############################################## ### CONFIGURE ANY OTHER FLAGS/OPTIONS HERE ### @@ -43,7 +43,7 @@ endif ifeq "$(ENGINE)" "orangebox" HL2SDK = $(HL2SDK_OB) HL2PUB = $(HL2SDK_OB)/public - HL2LIB = $(HL2SDK_OB)/linux_sdk + HL2LIB = $(HL2SDK_OB)/lib/linux CFLAGS += -DORANGEBOX_BUILD METAMOD = $(SOURCEMM16) INCLUDE += -I$(HL2SDK)/public/game/server @@ -101,6 +101,8 @@ $(BIN_DIR)/%.o: %.cpp all: check mkdir -p $(BIN_DIR)/sdk + mkdir -p $(BIN_DIR)/CDetour + mkdir -p $(BIN_DIR)/asm if [ "$(USEMETA)" == "true" ]; then \ ln -sf $(SRCDS)/bin/tier0_i486.so tier0_i486.so; \ fi diff --git a/extensions/tf2/asm/asm.c b/extensions/tf2/asm/asm.c new file mode 100644 index 00000000..ce3a7dff --- /dev/null +++ b/extensions/tf2/asm/asm.c @@ -0,0 +1,329 @@ +#include "asm.h" + +//if dest is NULL, returns minimum number of bytes needed to be copied +//if dest is not NULL, it will copy the bytes to dest as well as fix CALLs and JMPs +//http://www.devmaster.net/forums/showthread.php?t=2311 +int copy_bytes(unsigned char *func, unsigned char* dest, int required_len) { + int bytecount = 0; + + while(bytecount < required_len && *func != 0xCC) + { + // prefixes F0h, F2h, F3h, 66h, 67h, D8h-DFh, 2Eh, 36h, 3Eh, 26h, 64h and 65h + int operandSize = 4; + int FPU = 0; + int twoByte = 0; + unsigned char opcode = 0x90; + unsigned char modRM = 0xFF; + while(*func == 0xF0 || + *func == 0xF2 || + *func == 0xF3 || + (*func & 0xFC) == 0x64 || + (*func & 0xF8) == 0xD8 || + (*func & 0x7E) == 0x62) + { + if(*func == 0x66) + { + operandSize = 2; + } + else if((*func & 0xF8) == 0xD8) + { + FPU = *func; + if (dest) + *dest++ = *func++; + else + func++; + bytecount++; + break; + } + + if (dest) + *dest++ = *func++; + else + func++; + bytecount++; + } + + // two-byte opcode byte + if(*func == 0x0F) + { + twoByte = 1; + if (dest) + *dest++ = *func++; + else + func++; + bytecount++; + } + + // opcode byte + opcode = *func++; + if (dest) *dest++ = opcode; + bytecount++; + + // mod R/M byte + modRM = 0xFF; + if(FPU) + { + if((opcode & 0xC0) != 0xC0) + { + modRM = opcode; + } + } + else if(!twoByte) + { + if((opcode & 0xC4) == 0x00 || + (opcode & 0xF4) == 0x60 && ((opcode & 0x0A) == 0x02 || (opcode & 0x09) == 0x09) || + (opcode & 0xF0) == 0x80 || + (opcode & 0xF8) == 0xC0 && (opcode & 0x0E) != 0x02 || + (opcode & 0xFC) == 0xD0 || + (opcode & 0xF6) == 0xF6) + { + modRM = *func++; + if (dest) *dest++ = modRM; + bytecount++; + } + } + else + { + if((opcode & 0xF0) == 0x00 && (opcode & 0x0F) >= 0x04 && (opcode & 0x0D) != 0x0D || + (opcode & 0xF0) == 0x30 || + opcode == 0x77 || + (opcode & 0xF0) == 0x80 || + (opcode & 0xF0) == 0xA0 && (opcode & 0x07) <= 0x02 || + (opcode & 0xF8) == 0xC8) + { + // No mod R/M byte + } + else + { + modRM = *func++; + if (dest) *dest++ = modRM; + bytecount++; + } + } + + // SIB + if((modRM & 0x07) == 0x04 && + (modRM & 0xC0) != 0xC0) + { + if (dest) + *dest++ = *func++; //SIB + else + func++; + bytecount++; + } + + // mod R/M displacement + + // Dword displacement, no base + if((modRM & 0xC5) == 0x05) { + if (dest) { + *(unsigned int*)dest = *(unsigned int*)func; + dest += 4; + } + func += 4; + bytecount += 4; + } + + // Byte displacement + if((modRM & 0xC0) == 0x40) { + if (dest) + *dest++ = *func++; + else + func++; + bytecount++; + } + + // Dword displacement + if((modRM & 0xC0) == 0x80) { + if (dest) { + *(unsigned int*)dest = *(unsigned int*)func; + dest += 4; + } + func += 4; + bytecount += 4; + } + + // immediate + if(FPU) + { + // Can't have immediate operand + } + else if(!twoByte) + { + if((opcode & 0xC7) == 0x04 || + (opcode & 0xFE) == 0x6A || // PUSH/POP/IMUL + (opcode & 0xF0) == 0x70 || // Jcc + opcode == 0x80 || + opcode == 0x83 || + (opcode & 0xFD) == 0xA0 || // MOV + opcode == 0xA8 || // TEST + (opcode & 0xF8) == 0xB0 || // MOV + (opcode & 0xFE) == 0xC0 || // RCL + opcode == 0xC6 || // MOV + opcode == 0xCD || // INT + (opcode & 0xFE) == 0xD4 || // AAD/AAM + (opcode & 0xF8) == 0xE0 || // LOOP/JCXZ + opcode == 0xEB || + opcode == 0xF6 && (modRM & 0x30) == 0x00) // TEST + { + if (dest) + *dest++ = *func++; + else + func++; + bytecount++; + } + else if((opcode & 0xF7) == 0xC2) // RET + { + if (dest) { + *(unsigned short*)dest = *(unsigned short*)func; + dest += 2; + } + func += 2; + bytecount += 2; + } + else if((opcode & 0xFC) == 0x80 || + (opcode & 0xC7) == 0x05 || + (opcode & 0xF8) == 0xB8 || + (opcode & 0xFE) == 0xE8 || // CALL/Jcc + (opcode & 0xFE) == 0x68 || + (opcode & 0xFC) == 0xA0 || + (opcode & 0xEE) == 0xA8 || + opcode == 0xC7 || + opcode == 0xF7 && (modRM & 0x30) == 0x00) + { + if (dest) { + //Fix CALL/JMP offset + if ((opcode & 0xFE) == 0xE8) { + if (operandSize == 4) + *(long*)dest = ((func + *(long*)func) - dest); + else + *(short*)dest = ((func + *(short*)func) - dest); + } else { + if (operandSize == 4) + *(unsigned long*)dest = *(unsigned long*)func; + else + *(unsigned short*)dest = *(unsigned short*)func; + } + dest += operandSize; + } + func += operandSize; + bytecount += operandSize; + + } + } + else + { + if(opcode == 0xBA || // BT + opcode == 0x0F || // 3DNow! + (opcode & 0xFC) == 0x70 || // PSLLW + (opcode & 0xF7) == 0xA4 || // SHLD + opcode == 0xC2 || + opcode == 0xC4 || + opcode == 0xC5 || + opcode == 0xC6) + { + if (dest) + *dest++ = *func++; + else + func++; + } + else if((opcode & 0xF0) == 0x80) // Jcc -i + { + if (dest) { + if (operandSize == 4) + *(unsigned long*)dest = *(unsigned long*)func; + else + *(unsigned short*)dest = *(unsigned short*)func; + + dest += operandSize; + } + func += operandSize; + bytecount += operandSize; + } + } + } + + return bytecount; +} + +//insert a specific JMP instruction at the given location +void inject_jmp(void* src, void* dest) { + *(unsigned char*)src = OP_JMP; + *(long*)((unsigned char*)src+1) = (long)((unsigned char*)dest - ((unsigned char*)src + OP_JMP_SIZE)); +} + +//fill a given block with NOPs +void fill_nop(void* src, unsigned int len) { + unsigned char* src2 = (unsigned char*)src; + while (len) { + *src2++ = OP_NOP; + --len; + } +} + +void* eval_jump(void* src) { + unsigned char* addr = (unsigned char*)src; + + if (!addr) return 0; + + //import table jump + if (addr[0] == OP_PREFIX && addr[1] == OP_JMP_SEG) { + addr += 2; + addr = *(unsigned char**)addr; + //TODO: if addr points into the IAT + return *(void**)addr; + } + + //8bit offset + else if (addr[0] == OP_JMP_BYTE) { + addr = &addr[OP_JMP_BYTE_SIZE] + *(char*)&addr[1]; + //mangled 32bit jump? + if (addr[0] = OP_JMP) { + addr = addr + *(int*)&addr[1]; + } + return addr; + } + /* + //32bit offset + else if (addr[0] == OP_JMP) { + addr = &addr[OP_JMP_SIZE] + *(int*)&addr[1]; + } + */ + + return addr; +} +/* +from ms detours package +static bool detour_is_imported(PBYTE pbCode, PBYTE pbAddress) +{ + MEMORY_BASIC_INFORMATION mbi; + VirtualQuery((PVOID)pbCode, &mbi, sizeof(mbi)); + __try { + PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)mbi.AllocationBase; + if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) { + return false; + } + + PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader + + pDosHeader->e_lfanew); + if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) { + return false; + } + + if (pbAddress >= ((PBYTE)pDosHeader + + pNtHeader->OptionalHeader + .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress) && + pbAddress < ((PBYTE)pDosHeader + + pNtHeader->OptionalHeader + .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress + + pNtHeader->OptionalHeader + .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size)) { + return true; + } + return false; + } + __except(EXCEPTION_EXECUTE_HANDLER) { + return false; + } +} +*/ diff --git a/extensions/tf2/asm/asm.h b/extensions/tf2/asm/asm.h new file mode 100644 index 00000000..23b774ed --- /dev/null +++ b/extensions/tf2/asm/asm.h @@ -0,0 +1,38 @@ +#ifndef __ASM_H__ +#define __ASM_H__ + +#define OP_JMP 0xE9 +#define OP_JMP_SIZE 5 + +#define OP_NOP 0x90 +#define OP_NOP_SIZE 1 + +#define OP_PREFIX 0xFF +#define OP_JMP_SEG 0x25 + +#define OP_JMP_BYTE 0xEB +#define OP_JMP_BYTE_SIZE 2 + +#ifdef __cplusplus +extern "C" { +#endif + +//if dest is NULL, returns minimum number of bytes needed to be copied +//if dest is not NULL, it will copy the bytes to dest as well as fix CALLs and JMPs +//http://www.devmaster.net/forums/showthread.php?t=2311 +int copy_bytes(unsigned char *func, unsigned char* dest, int required_len); + +//insert a specific JMP instruction at the given location +void inject_jmp(void* src, void* dest); + +//fill a given block with NOPs +void fill_nop(void* src, unsigned int len); + +//evaluate a JMP at the target +void* eval_jump(void* src); + +#ifdef __cplusplus +} +#endif + +#endif //__ASM_H__ diff --git a/extensions/tf2/criticals.cpp b/extensions/tf2/criticals.cpp index c6ce26f8..30006072 100644 --- a/extensions/tf2/criticals.cpp +++ b/extensions/tf2/criticals.cpp @@ -31,353 +31,46 @@ #include "criticals.h" -ISourcePawnEngine *spengine = NULL; -CriticalHitManager g_CriticalHitManager; IServerGameEnts *gameents = NULL; -int g_returnvalue; +CDetour *calcIsAttackCriticalDetour = NULL; +CDetour *calcIsAttackCriticalMeleeDetour = NULL; +CDetour *calcIsAttackCriticalKnifeDetour = NULL; -bool CriticalHitManager::CreateCriticalDetour() +IForward *g_critForward = NULL; + +void InitialiseDetours() { - if (!g_pGameConf->GetMemSig("CalcCritical", &critical_address) || !critical_address) + calcIsAttackCriticalDetour = CDetourManager::CreateDetour((void *)&TempDetour, 0, "CalcCritical"); + calcIsAttackCriticalMeleeDetour = CDetourManager::CreateDetour((void *)&TempDetour, 0, "CalcCriticalMelee"); + calcIsAttackCriticalKnifeDetour = CDetourManager::CreateDetour((void *)&TempDetour, 0, "CalcCriticalKnife"); + + bool HookCreated = false; + + if (calcIsAttackCriticalDetour != NULL) { - g_pSM->LogError(myself, "Could not locate CalcCritical - Disabling Critical Hit forward"); - return false; + calcIsAttackCriticalDetour->EnableDetour(); + HookCreated = true; } - if (!g_pGameConf->GetOffset("CalcCriticalBackup", (int *)&(critical_restore.bytes))) + if (calcIsAttackCriticalMeleeDetour != NULL) { - g_pSM->LogError(myself, "Could not locate CalcCriticalBackup - Disabling Critical Hit forward"); - return false; + calcIsAttackCriticalMeleeDetour->EnableDetour(); + HookCreated = true; } - /* First, save restore bits */ - for (size_t i=0; iEnableDetour(); + HookCreated = true; } - critical_callback = spengine->ExecAlloc(100); - JitWriter wr; - JitWriter *jit = ≀ - wr.outbase = (jitcode_t)critical_callback; - wr.outptr = wr.outbase; - - /* Function we are detouring into is - * - * void CriticalDetour(CTFWeaponBase(void *) *pWeapon) - * - * push pWeapon [ecx] - */ - -#if defined PLATFORM_WINDOWS - IA32_Push_Reg(jit, REG_ECX); -#elif defined PLATFORM_LINUX - IA32_Push_Rm_Disp8_ESP(jit, 4); -#endif - - jitoffs_t call = IA32_Call_Imm32(jit, 0); - IA32_Write_Jump32_Abs(jit, call, (void *)TempDetour); - - -#if defined PLATFORM_LINUX - IA32_Add_Rm_Imm8(jit, REG_ESP, 4, MOD_REG); //add esp, 4 -#elif defined PLATFORM_WINDOWS - IA32_Pop_Reg(jit, REG_ECX); -#endif - - //If TempDetour returns non-zero we want to load something into eax and return this value - - //test eax, eax - IA32_Test_Rm_Reg(jit, REG_EAX, REG_EAX, MOD_REG); - - //jnz _skip - jitoffs_t jmp = IA32_Jump_Cond_Imm8(jit, CC_NZ, 0); - - /* Patch old bytes in */ - for (size_t i=0; iwrite_ubyte(critical_restore.patch[i]); + g_pSM->LogError(myself, "No critical hit forwards could be initialized - Disabled critical hit hooks"); + return; } - /* Return to the original function */ - call = IA32_Jump_Imm32(jit, 0); - IA32_Write_Jump32_Abs(jit, call, (unsigned char *)critical_address + critical_restore.bytes); - - //_skip: - //mov eax, [g_returnvalue] - //ret - IA32_Send_Jump8_Here(jit, jmp); - IA32_Mov_Eax_Mem(jit, (jit_int32_t)&g_returnvalue); - IA32_Return(jit); - - return true; -} - -bool CriticalHitManager::CreateCriticalMeleeDetour() -{ - if (!g_pGameConf->GetMemSig("CalcCriticalMelee", &melee_address) || !melee_address) - { - g_pSM->LogError(myself, "Could not locate CalcCriticalMelee - Disabling Critical Hit forward"); - return false; - } - - if (!g_pGameConf->GetOffset("CalcCriticalMeleeBackup", (int *)&(melee_restore.bytes))) - { - g_pSM->LogError(myself, "Could not locate CalcCriticalMeleeBackup - Disabling Critical Hit forward"); - return false; - } - - /* First, save restore bits */ - for (size_t i=0; iExecAlloc(100); - JitWriter wr; - JitWriter *jit = ≀ - wr.outbase = (jitcode_t)melee_callback; - wr.outptr = wr.outbase; - - /* Function we are detouring into is - * - * void CriticalDetour(CTFWeaponBase(void *) *pWeapon) - * - * push pWeapon [ecx] - */ - -#if defined PLATFORM_WINDOWS - IA32_Push_Reg(jit, REG_ECX); -#elif defined PLATFORM_LINUX - IA32_Push_Rm_Disp8_ESP(jit, 4); -#endif - - jitoffs_t call = IA32_Call_Imm32(jit, 0); - IA32_Write_Jump32_Abs(jit, call, (void *)TempDetour); - - -#if defined PLATFORM_LINUX - IA32_Add_Rm_Imm8(jit, REG_ESP, 4, MOD_REG); //add esp, 4 -#elif defined PLATFORM_WINDOWS - IA32_Pop_Reg(jit, REG_ECX); -#endif - - //If TempDetour returns non-zero we want to load something into eax and return this value - - //test eax, eax - IA32_Test_Rm_Reg(jit, REG_EAX, REG_EAX, MOD_REG); - - //jnz _skip - jitoffs_t jmp = IA32_Jump_Cond_Imm8(jit, CC_NZ, 0); - - int callbyte = -1; - /* The callbyte should return the nth byte (starting from 1) in the backup bytes - Should be an 0xE8 (call) */ - g_pGameConf->GetOffset("CalcCriticalMeleeCallByte", &callbyte); - - callbyte--; - void *function = NULL; - - if (callbyte > -1) - { - /* Check if the 'callbyte' is actually a call */ - if (melee_restore.patch[callbyte] != 0xE8) - { - g_pSM->LogError(myself, "Invalid callbyte - Melee detour may work incorrectly"); - } - else - { - /* Find the absolute address of the function it calls */ - void *offsetaddr = (void *)((unsigned char *)melee_address + callbyte + 1); - int offset = (int)*(unsigned char *)offsetaddr; - function = (unsigned char *)offsetaddr + offset + 4; - } - } - - /* Patch old bytes in */ - for (size_t i=0; iwrite_ubyte(melee_restore.patch[i]); - continue; - } - - /* Write in the adjusted call instead */ - jitoffs_t call = IA32_Call_Imm32(jit, 0); - IA32_Write_Jump32_Abs(jit, call, function); - - i += 4; - } - - /* Return to the original function */ - call = IA32_Jump_Imm32(jit, 0); - IA32_Write_Jump32_Abs(jit, call, (unsigned char *)melee_address + melee_restore.bytes); - - //_skip: - //mov eax, [g_returnvalue] - //ret - IA32_Send_Jump8_Here(jit, jmp); - IA32_Mov_Eax_Mem(jit, (jit_int32_t)&g_returnvalue); - IA32_Return(jit); - - return true; -} - -bool CriticalHitManager::CreateCriticalKnifeDetour() -{ - if (!g_pGameConf->GetMemSig("CalcCriticalKnife", &knife_address) || !knife_address) - { - g_pSM->LogError(myself, "Could not locate CalcCriticalKnife - Disabling Critical Hit forward"); - return false; - } - - if (!g_pGameConf->GetOffset("CalcCriticalKnifeBackup", (int *)&(knife_restore.bytes))) - { - g_pSM->LogError(myself, "Could not locate CalcCriticalKnifeBackup - Disabling Critical Hit forward"); - return false; - } - - /* First, save restore bits */ - for (size_t i=0; iExecAlloc(100); - JitWriter wr; - JitWriter *jit = ≀ - wr.outbase = (jitcode_t)knife_callback; - wr.outptr = wr.outbase; - - /* Function we are detouring into is - * - * void CriticalDetour(CTFWeaponBase(void *) *pWeapon) - * - * push pWeapon [ecx] - */ - -#if defined PLATFORM_WINDOWS - IA32_Push_Reg(jit, REG_ECX); -#elif defined PLATFORM_LINUX - IA32_Push_Rm_Disp8_ESP(jit, 4); -#endif - - jitoffs_t call = IA32_Call_Imm32(jit, 0); - IA32_Write_Jump32_Abs(jit, call, (void *)TempDetour); - - -#if defined PLATFORM_LINUX - IA32_Add_Rm_Imm8(jit, REG_ESP, 4, MOD_REG); //add esp, 4 -#elif defined PLATFORM_WINDOWS - IA32_Pop_Reg(jit, REG_ECX); -#endif - - //If TempDetour returns non-zero we want to load something into eax and return this value - - //test eax, eax - IA32_Test_Rm_Reg(jit, REG_EAX, REG_EAX, MOD_REG); - - //jnz _skip - jitoffs_t jmp = IA32_Jump_Cond_Imm8(jit, CC_NZ, 0); - - /* Patch old bytes in */ - for (size_t i=0; iwrite_ubyte(knife_restore.patch[i]); - } - - /* Return to the original function */ - call = IA32_Jump_Imm32(jit, 0); - IA32_Write_Jump32_Abs(jit, call, (unsigned char *)knife_address + knife_restore.bytes); - - //_skip: - //mov eax, [g_returnvalue] - //ret - IA32_Send_Jump8_Here(jit, jmp); - IA32_Mov_Eax_Mem(jit, (jit_int32_t)&g_returnvalue); - IA32_Return(jit); - - return true; -} - -void CriticalHitManager::EnableCriticalDetour() -{ - if (!detoured) - { - if (normalcreated) - { - DoGatePatch((unsigned char *)critical_address, &critical_callback); - } - if (meleecreated) - { - DoGatePatch((unsigned char *)melee_address, &melee_callback); - } - if (knifecreated) - { - DoGatePatch((unsigned char *)knife_address, &knife_callback); - } - - detoured = true; - } -} - -void CriticalHitManager::DeleteCriticalDetour() -{ - if (detoured) - { - DisableCriticalDetour(); - } - - if (critical_callback) - { - /* Free the gate */ - spengine->ExecFree(critical_callback); - critical_callback = NULL; - } - - if (melee_callback) - { - /* Free the gate */ - spengine->ExecFree(melee_callback); - melee_callback = NULL; - } - - if (knife_callback) - { - /* Free the gate */ - spengine->ExecFree(knife_callback); - knife_callback = NULL; - } -} - -bool TempDetour(void *pWeapon) -{ - return g_CriticalHitManager.CriticalDetour(pWeapon); -} - -void CriticalHitManager::DisableCriticalDetour() -{ - if (critical_callback) - { - /* Remove the patch */ - ApplyPatch(critical_address, 0, &critical_restore, NULL); - detoured = false; - } - - if (melee_callback) - { - /* Remove the patch */ - ApplyPatch(melee_address, 0, &melee_restore, NULL); - detoured = false; - } - - if (knife_callback) - { - /* Remove the patch */ - ApplyPatch(knife_address, 0, &knife_restore, NULL); - detoured = false; - } } int CheckBaseHandle(CBaseHandle &hndl) @@ -413,7 +106,7 @@ int CheckBaseHandle(CBaseHandle &hndl) return index; } -bool CriticalHitManager::CriticalDetour(void *pWeapon) +DetourReturn TempDetour(void *pWeapon) { edict_t *pEdict = gameents->BaseEntityToEdict((CBaseEntity *)pWeapon); @@ -431,23 +124,40 @@ bool CriticalHitManager::CriticalDetour(void *pWeapon) return false; } - if (!forward) + if (!g_critForward) { g_pSM->LogMessage(myself, "Invalid Forward"); return false; } + + int returnValue=0; CBaseHandle &hndl = *(CBaseHandle *)((uint8_t *)pWeapon + info.actual_offset); int index = CheckBaseHandle(hndl); - forward->PushCell(index); //Client index - forward->PushCell(engine->IndexOfEdict(pEdict)); // Weapon index - forward->PushString(pEdict->GetClassName()); //Weapon classname - forward->PushCellByRef(&g_returnvalue); //return value + g_critForward->PushCell(index); //Client index + g_critForward->PushCell(engine->IndexOfEdict(pEdict)); // Weapon index + g_critForward->PushString(pEdict->GetClassName()); //Weapon classname + g_critForward->PushCellByRef(&returnValue); //return value cell_t result = 0; - forward->Execute(&result); + g_critForward->Execute(&result); - return !!result; + if (result) + { + RETURN_DETOUR_VALUE(DETOUR_RESULT_OVERRIDE, returnValue); + } + else + { + RETURN_DETOUR_VALUE(DETOUR_RESULT_IGNORED, returnValue); + } + +} + +void RemoveDetours() +{ + CDetourManager::DeleteDetour(calcIsAttackCriticalDetour); + CDetourManager::DeleteDetour(calcIsAttackCriticalMeleeDetour); + CDetourManager::DeleteDetour(calcIsAttackCriticalKnifeDetour); } diff --git a/extensions/tf2/criticals.h b/extensions/tf2/criticals.h index b9749f43..199090c9 100644 --- a/extensions/tf2/criticals.h +++ b/extensions/tf2/criticals.h @@ -29,114 +29,21 @@ * Version: $Id$ */ +#ifndef _INCLUDE_SOURCEMOD_CRITICALS_H_ +#define _INCLUDE_SOURCEMOD_CRITICALS_H_ + #include "extension.h" #include #include -#include "detours.h" +#include "CDetour/detours.h" -class CriticalHitManager -{ -public: - CriticalHitManager() - { - enabled = false; - detoured = false; - critical_address = NULL; - critical_callback = NULL; - - melee_address = NULL; - melee_callback = NULL; - - knife_address = NULL; - knife_callback = NULL; - - forward = NULL; - - normalcreated = false; - meleecreated = false; - knifecreated = false; - } - - ~CriticalHitManager() - { - if (forward != NULL) - { - forwards->ReleaseForward(forward); - } - - DeleteCriticalDetour(); - } - - void Init() - { - normalcreated = CreateCriticalDetour(); - meleecreated = CreateCriticalMeleeDetour(); - knifecreated = CreateCriticalKnifeDetour(); - - if (!normalcreated && !meleecreated && !knifecreated) - { - enabled = false; - g_pSM->LogError(myself, "No critical hit forwards could be initialised - Disabled critical hit hooks"); - return; - } - - forward = forwards->CreateForward("TF2_CalcIsAttackCritical", ET_Hook, 4, NULL, Param_Cell, Param_Cell, Param_String, Param_CellByRef); - - if (!forward) - { - g_pSM->LogError(myself, "Failed to create forward - Disabling critical hit hook"); - enabled = false; - return; - } - - /* TODO: Only enable this once forwards exist. Requires IForwardListener functionality */ - EnableCriticalDetour(); - - enabled = true; - } - - bool IsEnabled() - { - return enabled; - } - - bool CriticalDetour(void *pWeapon); - -private: - IForward *forward; - - /* These create/delete the allocated memory */ - bool CreateCriticalDetour(); - bool CreateCriticalMeleeDetour(); - bool CreateCriticalKnifeDetour(); - void DeleteCriticalDetour(); - - bool normalcreated; - bool meleecreated; - bool knifecreated; - - /* These patch/unpatch the server.dll */ - void EnableCriticalDetour(); - void DisableCriticalDetour(); - - bool enabled; - bool detoured; - - patch_t critical_restore; - void *critical_address; - void *critical_callback; - - patch_t melee_restore; - void *melee_address; - void *melee_callback; - - patch_t knife_restore; - void *knife_address; - void *knife_callback; -}; +void InitialiseDetours(); +void RemoveDetours(); bool TempDetour(void *pWeapon); -extern ISourcePawnEngine *spengine; +extern IForward *g_critForward; + extern IServerGameEnts *gameents; -extern CriticalHitManager g_CriticalHitManager; + +#endif //_INCLUDE_SOURCEMOD_CRITICALS_H_ diff --git a/extensions/tf2/extension.cpp b/extensions/tf2/extension.cpp index a22edb43..0971c479 100644 --- a/extensions/tf2/extension.cpp +++ b/extensions/tf2/extension.cpp @@ -35,6 +35,7 @@ #include "iplayerinfo.h" #include "sm_trie_tpl.h" #include "criticals.h" +#include "CDetour/detours.h" /** * @file extension.cpp @@ -96,12 +97,14 @@ bool TF2Tools::SDK_OnLoad(char *error, size_t maxlength, bool late) return false; } + CDetourManager::Init(g_pSM->GetScriptingEngine(), g_pGameConf); + sharesys->AddNatives(myself, g_TFNatives); sharesys->RegisterLibrary(myself, "tf2"); playerhelpers->RegisterCommandTargetProcessor(this); - spengine = g_pSM->GetScriptingEngine(); + g_critForward = forwards->CreateForward("TF2_CalcIsAttackCritical", ET_Hook, 4, NULL, Param_Cell, Param_Cell, Param_String, Param_CellByRef); g_pCVar = icvar; @@ -133,13 +136,17 @@ void TF2Tools::SDK_OnUnload() g_RegNatives.UnregisterAll(); gameconfs->CloseGameConfigFile(g_pGameConf); playerhelpers->UnregisterCommandTargetProcessor(this); + + forwards->ReleaseForward(g_critForward); + + RemoveDetours(); } void TF2Tools::SDK_OnAllLoaded() { SM_GET_LATE_IFACE(BINTOOLS, g_pBinTools); - g_CriticalHitManager.Init(); + InitialiseDetours(); } bool TF2Tools::RegisterConCommandBase(ConCommandBase *pVar) diff --git a/extensions/tf2/msvc8/tf2.vcproj b/extensions/tf2/msvc8/tf2.vcproj index 8b02c34a..980cacc4 100644 --- a/extensions/tf2/msvc8/tf2.vcproj +++ b/extensions/tf2/msvc8/tf2.vcproj @@ -215,10 +215,6 @@ RelativePath="..\criticals.h" > - - @@ -259,6 +255,34 @@ > + + + + + + + + + + + + + + diff --git a/extensions/tf2/msvc9/tf2.vcproj b/extensions/tf2/msvc9/tf2.vcproj index 96ba1de9..c57b0c10 100644 --- a/extensions/tf2/msvc9/tf2.vcproj +++ b/extensions/tf2/msvc9/tf2.vcproj @@ -214,10 +214,6 @@ RelativePath="..\criticals.h" > - - @@ -258,6 +254,34 @@ > + + + + + + + + + + + + + + diff --git a/gamedata/sm-tf2.games.txt b/gamedata/sm-tf2.games.txt index 1072a2f9..f809e1b2 100644 --- a/gamedata/sm-tf2.games.txt +++ b/gamedata/sm-tf2.games.txt @@ -6,37 +6,37 @@ { "Burn" { - "library" "server" + "library" "server" "windows" "\x56\x8B\xF1\x8B\x8E\x2A\x2A\x00\x00\x8B\x01\x8B\x90\x2A\x2A\x00\x00\xFF\xD2\x84\xC0" "linux" "@_ZN15CTFPlayerShared4BurnEP9CTFPlayerP13CTFWeaponBase" } "RemoveDisguise" { - "library" "server" + "library" "server" "windows" "\x51\x56\x8B\xF1\x8B\x46\x2A\x57\x8D\x7E\x2A\x8D\x4C\x24\x08\x83\xE0\xF7" "linux" "@_ZN15CTFPlayerShared14RemoveDisguiseEv" } "Disguise" { - "library" "server" + "library" "server" "windows" "\x56\x8B\xF1\x8B\x8E\x2A\x2A\x00\x00\xE8\x2A\x2A\x2A\x2A\x8B\x8E\x2A\x2A\x00\x00\x8B\x89\x2A\x2A\x00\x00" "linux" "@_ZN15CTFPlayerShared8DisguiseEii" } "CalcCritical" { - "library" "server" + "library" "server" "linux" "@_ZN13CTFWeaponBase26CalcIsAttackCriticalHelperEv" "windows" "\x83\xEC\x08\x53\x56\x6A\x00" } "CalcCriticalMelee" { - "library" "server" + "library" "server" "linux" "@_ZN18CTFWeaponBaseMelee26CalcIsAttackCriticalHelperEv" "windows" "\x83\xEC\x08\x53\x57\x8B\xF9\xE8\x2A\x2A\x2A\x2A\x8B\xD8" } "CalcCriticalKnife" { - "library" "server" + "library" "server" "linux" "@_ZN8CTFKnife26CalcIsAttackCriticalHelperEv" "windows" "\x33\xC0\x83\xB9\x08\x13\x00\x00\x01\x0F\x94\xC0\xC3" } @@ -49,27 +49,6 @@ "windows" "281" "linux" "282" } - "CalcCriticalBackup" - { - "linux" "7" - "windows" "7" - } - "CalcCriticalMeleeBackup" - { - "linux" "7" - "windows" "7" - } - /* The byte number (starting from 1) of the call (0xE8) byte or -1 if none */ - "CalcCriticalMeleeCallByte" - { - "linux" "-1" - "windows" "-1" - } - "CalcCriticalKnifeBackup" - { - "linux" "11" - "windows" "9" - } } } } \ No newline at end of file