1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/extensions/tf2/CDetour/detours.cpp Tue Jul 01 09:04:00 2008 +0000
1.3 @@ -0,0 +1,341 @@
1.4 +#include "detours.h"
1.5 +#include <asm/asm.h>
1.6 +
1.7 +ISourcePawnEngine *CDetourManager::spengine = NULL;
1.8 +IGameConfig *CDetourManager::gameconf = NULL;
1.9 +int CDetourManager::returnValue = 0;
1.10 +
1.11 +void CDetourManager::Init(ISourcePawnEngine *spengine, IGameConfig *gameconf)
1.12 +{
1.13 + CDetourManager::spengine = spengine;
1.14 + CDetourManager::gameconf = gameconf;
1.15 +}
1.16 +
1.17 +CDetour *CDetourManager::CreateDetour(void *callbackfunction, size_t paramsize, const char *signame)
1.18 +{
1.19 + CDetour *detour = new CDetour(callbackfunction, paramsize, signame);
1.20 + if (detour)
1.21 + {
1.22 + if (!detour->Init(spengine, gameconf))
1.23 + {
1.24 + delete detour;
1.25 + return NULL;
1.26 + }
1.27 +
1.28 + return detour;
1.29 + }
1.30 +
1.31 + return NULL;
1.32 +}
1.33 +
1.34 +void CDetourManager::DeleteDetour(CDetour *detour)
1.35 +{
1.36 + delete detour;
1.37 +}
1.38 +
1.39 +CBlocker * CDetourManager::CreateFunctionBlock( const char *signame, bool isVoid )
1.40 +{
1.41 + CBlocker *block = new CBlocker(signame, isVoid);
1.42 +
1.43 + if (block)
1.44 + {
1.45 + if (!block->Init(spengine, gameconf))
1.46 + {
1.47 + delete block;
1.48 + return NULL;
1.49 + }
1.50 +
1.51 + return block;
1.52 + }
1.53 +
1.54 + return NULL;
1.55 +}
1.56 +
1.57 +void CDetourManager::DeleteFunctionBlock(CBlocker *block)
1.58 +{
1.59 + delete block;
1.60 +}
1.61 +
1.62 +CDetour::CDetour(void *callbackfunction, size_t paramsize, const char *signame)
1.63 +{
1.64 + enabled = false;
1.65 + detoured = false;
1.66 + detour_address = NULL;
1.67 + detour_callback = NULL;
1.68 + this->signame = signame;
1.69 + this->callbackfunction = callbackfunction;
1.70 + spengine = NULL;
1.71 + gameconf = NULL;
1.72 + this->paramsize = paramsize;
1.73 +}
1.74 +
1.75 +CDetour::~CDetour()
1.76 +{
1.77 + DeleteDetour();
1.78 +}
1.79 +
1.80 +bool CDetour::Init(ISourcePawnEngine *spengine, IGameConfig *gameconf)
1.81 +{
1.82 + this->spengine = spengine;
1.83 + this->gameconf = gameconf;
1.84 +
1.85 + if (!CreateDetour())
1.86 + {
1.87 + enabled = false;
1.88 + return enabled;
1.89 + }
1.90 +
1.91 + enabled = true;
1.92 +
1.93 + return enabled;
1.94 +}
1.95 +
1.96 +bool CDetour::IsEnabled()
1.97 +{
1.98 + return enabled;
1.99 +}
1.100 +
1.101 +bool CDetour::CreateDetour()
1.102 +{
1.103 + if (!gameconf->GetMemSig(signame, &detour_address))
1.104 + {
1.105 + g_pSM->LogError(myself, "Could not locate %s - Disabling detour", signame);
1.106 + return false;
1.107 + }
1.108 +
1.109 + if (!detour_address)
1.110 + {
1.111 + g_pSM->LogError(myself, "Sigscan for %s failed - Disabling detour", signame);
1.112 + return false;
1.113 + }
1.114 +
1.115 + detour_restore.bytes = copy_bytes((unsigned char *)detour_address, NULL, OP_JMP_SIZE);
1.116 +
1.117 + /* First, save restore bits */
1.118 + for (size_t i=0; i<detour_restore.bytes; i++)
1.119 + {
1.120 + detour_restore.patch[i] = ((unsigned char *)detour_address)[i];
1.121 + }
1.122 +
1.123 + //detour_callback = spengine->ExecAlloc(100);
1.124 + JitWriter wr;
1.125 + JitWriter *jit = ≀
1.126 + jit_uint32_t CodeSize = 0;
1.127 +
1.128 + wr.outbase = NULL;
1.129 + wr.outptr = NULL;
1.130 +
1.131 +jit_rewind:
1.132 +
1.133 + /* Push all our params onto the stack */
1.134 + for (size_t i=0; i<paramsize; i++)
1.135 + {
1.136 +#if defined PLATFORM_WINDOWS
1.137 + IA32_Push_Rm_Disp8_ESP(jit, (paramsize*4));
1.138 +#elif defined PLATFORM_LINUX
1.139 + IA32_Push_Rm_Disp8_ESP(jit, 4 +(paramsize*4));
1.140 +#endif
1.141 + }
1.142 +
1.143 + /* Push thisptr onto the stack */
1.144 +#if defined PLATFORM_WINDOWS
1.145 + IA32_Push_Reg(jit, REG_ECX);
1.146 +#elif defined PLATFORM_LINUX
1.147 + IA32_Push_Rm_Disp8_ESP(jit, 4 + (paramsize*4));
1.148 +#endif
1.149 +
1.150 + jitoffs_t call = IA32_Call_Imm32(jit, 0);
1.151 + IA32_Write_Jump32_Abs(jit, call, callbackfunction);
1.152 +
1.153 + /* Pop thisptr */
1.154 +#if defined PLATFORM_LINUX
1.155 + IA32_Add_Rm_Imm8(jit, REG_ESP, 4, MOD_REG); //add esp, 4
1.156 +#elif defined PLATFORM_WINDOWS
1.157 + IA32_Pop_Reg(jit, REG_ECX);
1.158 +#endif
1.159 +
1.160 + /* Pop params from the stack */
1.161 + for (size_t i=0; i<paramsize; i++)
1.162 + {
1.163 + IA32_Add_Rm_Imm8(jit, REG_ESP, 4, MOD_REG);
1.164 + }
1.165 +
1.166 + //If TempDetour returns non-zero we want to load something into eax and return this value
1.167 +
1.168 + //test eax, eax
1.169 + IA32_Test_Rm_Reg(jit, REG_EAX, REG_EAX, MOD_REG);
1.170 +
1.171 + //jnz _skip
1.172 + jitoffs_t jmp = IA32_Jump_Cond_Imm8(jit, CC_NZ, 0);
1.173 +
1.174 + /* Patch old bytes in */
1.175 + if (wr.outbase != NULL)
1.176 + {
1.177 + copy_bytes((unsigned char *)detour_address, (unsigned char*)wr.outptr, detour_restore.bytes);
1.178 + }
1.179 + wr.outptr += detour_restore.bytes;
1.180 +
1.181 + /* Return to the original function */
1.182 + call = IA32_Jump_Imm32(jit, 0);
1.183 + IA32_Write_Jump32_Abs(jit, call, (unsigned char *)detour_address + detour_restore.bytes);
1.184 +
1.185 + //_skip:
1.186 + //mov eax, [g_returnvalue]
1.187 + //ret
1.188 + IA32_Send_Jump8_Here(jit, jmp);
1.189 + IA32_Mov_Eax_Mem(jit, (jit_int32_t)&CDetourManager::returnValue);
1.190 + IA32_Return(jit);
1.191 +
1.192 + if (wr.outbase == NULL)
1.193 + {
1.194 + CodeSize = wr.get_outputpos();
1.195 + wr.outbase = (jitcode_t)spengine->AllocatePageMemory(CodeSize);
1.196 + spengine->SetReadWrite(wr.outbase);
1.197 + wr.outptr = wr.outbase;
1.198 + detour_callback = wr.outbase;
1.199 + goto jit_rewind;
1.200 + }
1.201 +
1.202 + spengine->SetReadExecute(wr.outbase);
1.203 +
1.204 + return true;
1.205 +}
1.206 +
1.207 +void CDetour::EnableDetour()
1.208 +{
1.209 + if (!detoured)
1.210 + {
1.211 + DoGatePatch((unsigned char *)detour_address, &detour_callback);
1.212 + detoured = true;
1.213 + }
1.214 +}
1.215 +
1.216 +void CDetour::DeleteDetour()
1.217 +{
1.218 + if (detoured)
1.219 + {
1.220 + DisableDetour();
1.221 + }
1.222 +
1.223 + if (detour_callback)
1.224 + {
1.225 + /* Free the gate */
1.226 + spengine->ExecFree(detour_callback);
1.227 + detour_callback = NULL;
1.228 + }
1.229 +}
1.230 +
1.231 +void CDetour::DisableDetour()
1.232 +{
1.233 + if (detoured)
1.234 + {
1.235 + /* Remove the patch */
1.236 + /* This may screw up */
1.237 + ApplyPatch(detour_address, 0, &detour_restore, NULL);
1.238 + detoured = false;
1.239 + }
1.240 +}
1.241 +
1.242 +CBlocker::CBlocker( const char *signame, bool isVoid )
1.243 +{
1.244 + this->isVoid = isVoid;
1.245 + isEnabled = false;
1.246 + isValid = false;
1.247 +
1.248 + spengine = NULL;
1.249 + gameconf = NULL;
1.250 + block_address = NULL;
1.251 + block_sig = signame;
1.252 +
1.253 + if (isVoid)
1.254 + {
1.255 + /* Void functions we only patch in a 'ret' (1 byte) */
1.256 + block_restore.bytes = 1;
1.257 + }
1.258 + else
1.259 + {
1.260 + /* Normal functions need an mov eax, value */
1.261 + block_restore.bytes = 6;
1.262 + }
1.263 +}
1.264 +
1.265 +void CBlocker::EnableBlock( int returnValue )
1.266 +{
1.267 + if (!isValid || isEnabled)
1.268 + {
1.269 + return;
1.270 + }
1.271 +
1.272 + /* First, save restore bits */
1.273 + for (size_t i=0; i<block_restore.bytes; i++)
1.274 + {
1.275 + block_restore.patch[i] = ((unsigned char *)block_address)[i];
1.276 + }
1.277 +
1.278 + JitWriter wr;
1.279 + JitWriter *jit = ≀
1.280 + wr.outbase = (jitcode_t)block_address;
1.281 + wr.outptr = wr.outbase;
1.282 +
1.283 + if (isVoid)
1.284 + {
1.285 + IA32_Return(jit);
1.286 + }
1.287 + else
1.288 + {
1.289 + IA32_Mov_Reg_Imm32(jit, REG_EAX, returnValue);
1.290 + IA32_Return(jit);
1.291 + }
1.292 +
1.293 + isEnabled = true;
1.294 +}
1.295 +
1.296 +void CBlocker::DisableBlock()
1.297 +{
1.298 + if (!isValid || !isEnabled)
1.299 + {
1.300 + return;
1.301 + }
1.302 +
1.303 + /* First, save restore bits */
1.304 + for (size_t i=0; i<block_restore.bytes; i++)
1.305 + {
1.306 + ((unsigned char *)block_address)[i] = block_restore.patch[i];
1.307 + }
1.308 +
1.309 + isEnabled = false;
1.310 +}
1.311 +
1.312 +CBlocker::~CBlocker()
1.313 +{
1.314 + if (!isValid || !isEnabled)
1.315 + {
1.316 + return;
1.317 + }
1.318 +
1.319 + DisableBlock();
1.320 +}
1.321 +
1.322 +bool CBlocker::Init( ISourcePawnEngine *spengine, IGameConfig *gameconf )
1.323 +{
1.324 + this->spengine = spengine;
1.325 + this->gameconf = gameconf;
1.326 +
1.327 + if (!gameconf->GetMemSig(block_sig, &block_address))
1.328 + {
1.329 + g_pSM->LogError(myself, "Could not locate %s - Disabling blocker", block_sig);
1.330 + isValid = false;
1.331 + return false;
1.332 + }
1.333 +
1.334 + if (!block_address)
1.335 + {
1.336 + g_pSM->LogError(myself, "Sigscan for %s failed - Disabling blocker", block_sig);
1.337 + isValid = false;
1.338 + return false;
1.339 + }
1.340 +
1.341 + isValid = true;
1.342 +
1.343 + return true;
1.344 +}