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