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
This commit is contained in:
Matt Woodrow 2008-07-01 09:04:00 +00:00
parent b01dd0798c
commit d530d0c726
12 changed files with 1013 additions and 486 deletions

View File

@ -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 <sys/mman.h>
@ -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_

View File

@ -0,0 +1,341 @@
#include "detours.h"
#include <asm/asm.h>
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; i<detour_restore.bytes; i++)
{
detour_restore.patch[i] = ((unsigned char *)detour_address)[i];
}
//detour_callback = spengine->ExecAlloc(100);
JitWriter wr;
JitWriter *jit = &wr;
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; i<paramsize; i++)
{
#if defined PLATFORM_WINDOWS
IA32_Push_Rm_Disp8_ESP(jit, (paramsize*4));
#elif defined PLATFORM_LINUX
IA32_Push_Rm_Disp8_ESP(jit, 4 +(paramsize*4));
#endif
}
/* Push thisptr onto the stack */
#if defined PLATFORM_WINDOWS
IA32_Push_Reg(jit, REG_ECX);
#elif defined PLATFORM_LINUX
IA32_Push_Rm_Disp8_ESP(jit, 4 + (paramsize*4));
#endif
jitoffs_t call = IA32_Call_Imm32(jit, 0);
IA32_Write_Jump32_Abs(jit, call, callbackfunction);
/* Pop thisptr */
#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
/* Pop params from the stack */
for (size_t i=0; i<paramsize; i++)
{
IA32_Add_Rm_Imm8(jit, REG_ESP, 4, MOD_REG);
}
//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 */
if (wr.outbase != NULL)
{
copy_bytes((unsigned char *)detour_address, (unsigned char*)wr.outptr, detour_restore.bytes);
}
wr.outptr += detour_restore.bytes;
/* Return to the original function */
call = IA32_Jump_Imm32(jit, 0);
IA32_Write_Jump32_Abs(jit, call, (unsigned char *)detour_address + detour_restore.bytes);
//_skip:
//mov eax, [g_returnvalue]
//ret
IA32_Send_Jump8_Here(jit, jmp);
IA32_Mov_Eax_Mem(jit, (jit_int32_t)&CDetourManager::returnValue);
IA32_Return(jit);
if (wr.outbase == NULL)
{
CodeSize = wr.get_outputpos();
wr.outbase = (jitcode_t)spengine->AllocatePageMemory(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; i<block_restore.bytes; i++)
{
block_restore.patch[i] = ((unsigned char *)block_address)[i];
}
JitWriter wr;
JitWriter *jit = &wr;
wr.outbase = (jitcode_t)block_address;
wr.outptr = wr.outbase;
if (isVoid)
{
IA32_Return(jit);
}
else
{
IA32_Mov_Reg_Imm32(jit, REG_EAX, returnValue);
IA32_Return(jit);
}
isEnabled = true;
}
void CBlocker::DisableBlock()
{
if (!isValid || !isEnabled)
{
return;
}
/* First, save restore bits */
for (size_t i=0; i<block_restore.bytes; i++)
{
((unsigned char *)block_address)[i] = block_restore.patch[i];
}
isEnabled = false;
}
CBlocker::~CBlocker()
{
if (!isValid || !isEnabled)
{
return;
}
DisableBlock();
}
bool CBlocker::Init( ISourcePawnEngine *spengine, IGameConfig *gameconf )
{
this->spengine = 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;
}

View File

@ -0,0 +1,166 @@
#ifndef _INCLUDE_SOURCEMOD_DETOURS_H_
#define _INCLUDE_SOURCEMOD_DETOURS_H_
#include "extension.h"
#include <jit/jit_helpers.h>
#include <jit/x86/x86_macros.h>
#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_

View File

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

329
extensions/tf2/asm/asm.c Normal file
View File

@ -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;
}
}
*/

38
extensions/tf2/asm/asm.h Normal file
View File

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

View File

@ -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; i<critical_restore.bytes; i++)
if (calcIsAttackCriticalKnifeDetour != NULL)
{
critical_restore.patch[i] = ((unsigned char *)critical_address)[i];
calcIsAttackCriticalKnifeDetour->EnableDetour();
HookCreated = true;
}
critical_callback = spengine->ExecAlloc(100);
JitWriter wr;
JitWriter *jit = &wr;
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; i<critical_restore.bytes; i++)
if (!HookCreated)
{
jit->write_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; i<melee_restore.bytes; i++)
{
melee_restore.patch[i] = ((unsigned char *)melee_address)[i];
}
melee_callback = spengine->ExecAlloc(100);
JitWriter wr;
JitWriter *jit = &wr;
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; i<melee_restore.bytes; i++)
{
if ((int)i != callbyte)
{
jit->write_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; i<knife_restore.bytes; i++)
{
knife_restore.patch[i] = ((unsigned char *)knife_address)[i];
}
knife_callback = spengine->ExecAlloc(100);
JitWriter wr;
JitWriter *jit = &wr;
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; i<knife_restore.bytes; i++)
{
jit->write_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);
if (result)
{
RETURN_DETOUR_VALUE(DETOUR_RESULT_OVERRIDE, returnValue);
}
else
{
RETURN_DETOUR_VALUE(DETOUR_RESULT_IGNORED, returnValue);
}
return !!result;
}
void RemoveDetours()
{
CDetourManager::DeleteDetour(calcIsAttackCriticalDetour);
CDetourManager::DeleteDetour(calcIsAttackCriticalMeleeDetour);
CDetourManager::DeleteDetour(calcIsAttackCriticalKnifeDetour);
}

View File

@ -29,114 +29,21 @@
* Version: $Id$
*/
#ifndef _INCLUDE_SOURCEMOD_CRITICALS_H_
#define _INCLUDE_SOURCEMOD_CRITICALS_H_
#include "extension.h"
#include <jit/jit_helpers.h>
#include <jit/x86/x86_macros.h>
#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_

View File

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

View File

@ -215,10 +215,6 @@
RelativePath="..\criticals.h"
>
</File>
<File
RelativePath="..\detours.h"
>
</File>
<File
RelativePath="..\extension.h"
>
@ -259,6 +255,34 @@
>
</File>
</Filter>
<Filter
Name="CDetour"
>
<File
RelativePath="..\..\..\public\CBaseServer and friends\CDetour\detourhelpers.h"
>
</File>
<File
RelativePath="..\..\..\public\CBaseServer and friends\CDetour\detours.cpp"
>
</File>
<File
RelativePath="..\..\..\public\CBaseServer and friends\CDetour\detours.h"
>
</File>
</Filter>
<Filter
Name="asm"
>
<File
RelativePath="..\asm\asm.c"
>
</File>
<File
RelativePath="..\asm\asm.h"
>
</File>
</Filter>
</Files>
<Globals>
</Globals>

View File

@ -214,10 +214,6 @@
RelativePath="..\criticals.h"
>
</File>
<File
RelativePath="..\detours.h"
>
</File>
<File
RelativePath="..\extension.h"
>
@ -258,6 +254,34 @@
>
</File>
</Filter>
<Filter
Name="CDetour"
>
<File
RelativePath="..\..\..\public\CBaseServer and friends\CDetour\detourhelpers.h"
>
</File>
<File
RelativePath="..\..\..\public\CBaseServer and friends\CDetour\detours.cpp"
>
</File>
<File
RelativePath="..\..\..\public\CBaseServer and friends\CDetour\detours.h"
>
</File>
</Filter>
<Filter
Name="asm"
>
<File
RelativePath="..\asm\asm.c"
>
</File>
<File
RelativePath="..\asm\asm.h"
>
</File>
</Filter>
</Files>
<Globals>
</Globals>

View File

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