extensions/tf2/CDetour/detours.cpp
author Matt Woodrow <pred@alliedmods.net>
Thu Feb 26 12:42:13 2009 +1300 (2009-02-26)
changeset 2600 f78e5656cc6f
parent 2184 d6b9d57ed4cd
permissions -rw-r--r--
Updated CDetour code in TF2 extension to prevent crashes with spies. Updated windows sig.
     1 /**
     2 * vim: set ts=4 :
     3 * =============================================================================
     4 * SourceMod
     5 * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
     6 * =============================================================================
     7 *
     8 * This program is free software; you can redistribute it and/or modify it under
     9 * the terms of the GNU General Public License, version 3.0, as published by the
    10 * Free Software Foundation.
    11 * 
    12 * This program is distributed in the hope that it will be useful, but WITHOUT
    13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    14 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    15 * details.
    16 *
    17 * You should have received a copy of the GNU General Public License along with
    18 * this program.  If not, see <http://www.gnu.org/licenses/>.
    19 *
    20 * As a special exception, AlliedModders LLC gives you permission to link the
    21 * code of this program (as well as its derivative works) to "Half-Life 2," the
    22 * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    23 * by the Valve Corporation.  You must obey the GNU General Public License in
    24 * all respects for all other code used.  Additionally, AlliedModders LLC grants
    25 * this exception to all derivative works.  AlliedModders LLC defines further
    26 * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    27 * or <http://www.sourcemod.net/license.php>.
    28 *
    29 * Version: $Id: detours.cpp 248 2008-08-27 00:56:22Z pred $
    30 */
    31 
    32 #include "detours.h"
    33 #include <asm/asm.h>
    34 
    35 ISourcePawnEngine *CDetourManager::spengine = NULL;
    36 IGameConfig *CDetourManager::gameconf = NULL;
    37 
    38 void CDetourManager::Init(ISourcePawnEngine *spengine, IGameConfig *gameconf)
    39 {
    40 	CDetourManager::spengine = spengine;
    41 	CDetourManager::gameconf = gameconf;
    42 }
    43 
    44 CDetour *CDetourManager::CreateDetour(void *callbackfunction, void **trampoline, const char *signame)
    45 {
    46 	CDetour *detour = new CDetour(callbackfunction, trampoline, signame);
    47 	if (detour)
    48 	{
    49 		if (!detour->Init(spengine, gameconf))
    50 		{
    51 			delete detour;
    52 			return NULL;
    53 		}
    54 
    55 		return detour;
    56 	}
    57 
    58 	return NULL;
    59 }
    60 
    61 CDetour::CDetour(void *callbackfunction, void **trampoline, const char *signame)
    62 {
    63 	enabled = false;
    64 	detoured = false;
    65 	detour_address = NULL;
    66 	detour_trampoline = NULL;
    67 	this->signame = signame;
    68 	this->detour_callback = callbackfunction;
    69 	spengine = NULL;
    70 	gameconf = NULL;
    71 	this->trampoline = trampoline;
    72 }
    73 
    74 bool CDetour::Init(ISourcePawnEngine *spengine, IGameConfig *gameconf)
    75 {
    76 	this->spengine = spengine;
    77 	this->gameconf = gameconf;
    78 
    79 	if (!CreateDetour())
    80 	{
    81 		enabled = false;
    82 		return enabled;
    83 	}
    84 
    85 	enabled = true;
    86 
    87 	return enabled;
    88 }
    89 
    90 void CDetour::Destroy()
    91 {
    92 	DeleteDetour();
    93 	delete this;
    94 }
    95 
    96 bool CDetour::IsEnabled()
    97 {
    98 	return enabled;
    99 }
   100 
   101 bool CDetour::CreateDetour()
   102 {
   103 	if (!gameconf->GetMemSig(signame, &detour_address))
   104 	{
   105 		g_pSM->LogError(myself, "Could not locate %s - Disabling detour", signame);
   106 		return false;
   107 	}
   108 
   109 	if (!detour_address)
   110 	{
   111 		g_pSM->LogError(myself, "Sigscan for %s failed - Disabling detour to prevent crashes", signame);
   112 		return false;
   113 	}
   114 
   115 	detour_restore.bytes = copy_bytes((unsigned char *)detour_address, NULL, OP_JMP_SIZE+1);
   116 
   117 	/* First, save restore bits */
   118 	for (size_t i=0; i<detour_restore.bytes; i++)
   119 	{
   120 		detour_restore.patch[i] = ((unsigned char *)detour_address)[i];
   121 	}
   122 
   123 	JitWriter wr;
   124 	JitWriter *jit = &wr;
   125 	jit_uint32_t CodeSize = 0;
   126 	
   127 	wr.outbase = NULL;
   128 	wr.outptr = NULL;
   129 
   130 jit_rewind:
   131 
   132 	/* Patch old bytes in */
   133 	if (wr.outbase != NULL)
   134 	{
   135 		copy_bytes((unsigned char *)detour_address, (unsigned char*)wr.outptr, detour_restore.bytes);
   136 	}
   137 	wr.outptr += detour_restore.bytes;
   138 
   139 	/* Return to the original function */
   140 	jitoffs_t call = IA32_Jump_Imm32(jit, 0);
   141 	IA32_Write_Jump32_Abs(jit, call, (unsigned char *)detour_address + detour_restore.bytes);
   142 
   143 	if (wr.outbase == NULL)
   144 	{
   145 		CodeSize = wr.get_outputpos();
   146 		wr.outbase = (jitcode_t)spengine->AllocatePageMemory(CodeSize);
   147 		spengine->SetReadWrite(wr.outbase);
   148 		wr.outptr = wr.outbase;
   149 		detour_trampoline = wr.outbase;
   150 		goto jit_rewind;
   151 	}
   152 
   153 	spengine->SetReadExecute(wr.outbase);
   154 
   155 	*trampoline = detour_trampoline;
   156 
   157 	return true;
   158 }
   159 
   160 void CDetour::DeleteDetour()
   161 {
   162 	if (detoured)
   163 	{
   164 		DisableDetour();
   165 	}
   166 
   167 	if (detour_trampoline)
   168 	{
   169 		/* Free the allocated trampoline memory */
   170 		spengine->FreePageMemory(detour_trampoline);
   171 		detour_trampoline = NULL;
   172 	}
   173 }
   174 
   175 void CDetour::EnableDetour()
   176 {
   177 	if (!detoured)
   178 	{
   179 		DoGatePatch((unsigned char *)detour_address, &detour_callback);
   180 		detoured = true;
   181 	}
   182 }
   183 
   184 void CDetour::DisableDetour()
   185 {
   186 	if (detoured)
   187 	{
   188 		/* Remove the patch */
   189 		ApplyPatch(detour_address, 0, &detour_restore, NULL);
   190 		detoured = false;
   191 	}
   192 }