2005-04-14 14:54:13 +02:00
|
|
|
/* ======== SourceHook ========
|
|
|
|
* By PM
|
|
|
|
* No warranties of any kind
|
|
|
|
* ============================
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @file sourcehook.cpp
|
|
|
|
* @brief Contains the implementation of the SourceHook API
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
#include "sourcehook_impl.h"
|
|
|
|
|
|
|
|
#if SH_SYS == SH_SYS_WIN32
|
|
|
|
# include <windows.h>
|
|
|
|
#elif SH_SYS == SH_SYS_LINUX
|
|
|
|
# include <sys/mman.h>
|
|
|
|
# include <sys/limits.h>
|
|
|
|
# include <unistd.h>
|
|
|
|
# ifndef PAGESIZE
|
|
|
|
# define PAGESIZE 4096
|
|
|
|
# endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if SH_RUNTIME_CODEGEN == 1
|
|
|
|
# if SH_COMP == SH_COMP_GCC
|
|
|
|
# define SH_CCC_CODESIZE 15
|
|
|
|
// :TODO:
|
|
|
|
//void SH_CCC_MakeGate(void *origthisptr, unsigned char* &ptr, void *origvtable, int i)
|
|
|
|
//{
|
|
|
|
//}
|
|
|
|
# elif SH_COMP == SH_COMP_MSVC
|
|
|
|
# define SH_CCC_CODESIZE 19
|
|
|
|
void SH_CCC_MakeGate(void *origthisptr, void *ccthisptr, unsigned char* ptr, void *origfunc)
|
|
|
|
{
|
|
|
|
// --Subtract old this pointer to get the offset
|
|
|
|
// 00417EDF 81 E9 A1 7B 33 01 sub ecx,1337BA1h
|
|
|
|
*ptr++ = 0x81;
|
|
|
|
*ptr++ = 0xE9;
|
|
|
|
*reinterpret_cast<void**>(ptr) = ccthisptr;
|
|
|
|
ptr += sizeof(void*);
|
|
|
|
// --Add it to the new this pointer
|
|
|
|
// 00417EE5 81 C1 37 13 37 13 add ecx,13371337h
|
|
|
|
*ptr++ = 0x81;
|
|
|
|
*ptr++ = 0xC1;
|
|
|
|
*reinterpret_cast<void**>(ptr) = origthisptr;
|
|
|
|
ptr += sizeof(void*);
|
|
|
|
// --Prepare address
|
|
|
|
// 00417EEB B8 EF BE AD DE mov eax,0DEADBEEFh
|
|
|
|
*ptr++ = 0xB8;
|
|
|
|
*reinterpret_cast<void**>(ptr) = origfunc;
|
|
|
|
ptr += sizeof(void*);
|
|
|
|
// -- Do actual jump
|
|
|
|
// 00417EF0 FF E0 jmp eax
|
|
|
|
*ptr++ = 0xFF;
|
|
|
|
*ptr++ = 0xE0;
|
|
|
|
}
|
|
|
|
# endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
namespace SourceHook
|
|
|
|
{
|
|
|
|
CSourceHookImpl::CSourceHookImpl()
|
|
|
|
{
|
|
|
|
// Get page size
|
|
|
|
#if SH_SYS == SH_SYS_WIN32
|
|
|
|
SYSTEM_INFO sysinfo;
|
|
|
|
GetSystemInfo(&sysinfo);
|
|
|
|
m_PageSize = sysinfo.dwPageSize;
|
|
|
|
#elif SH_SYS == SH_SYS_LINUX
|
|
|
|
m_PageSize = PAGESIZE;
|
|
|
|
#else
|
|
|
|
# error Unsupported system
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
CSourceHookImpl::~CSourceHookImpl()
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CSourceHookImpl::IsPluginInUse(Plugin plug)
|
|
|
|
{
|
2005-04-16 18:55:53 +02:00
|
|
|
// Iterate through all hook managers which are in this plugin
|
2005-04-14 14:54:13 +02:00
|
|
|
// Iterate through their hooks
|
|
|
|
// If a hook from an other plugin is found, return true
|
|
|
|
// Return false otherwise
|
|
|
|
|
2005-04-16 18:55:53 +02:00
|
|
|
for (HookManInfoList::iterator iter = m_HookMans.begin(); iter != m_HookMans.end(); ++iter)
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
|
|
|
if (iter->plug == plug && !iter->ifaces.empty())
|
|
|
|
{
|
2005-04-16 18:55:53 +02:00
|
|
|
for (std::list<HookManagerInfo::Iface>::iterator iter2 = iter->ifaces.begin(); iter2 != iter->ifaces.end(); ++iter2)
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-04-16 18:55:53 +02:00
|
|
|
std::list<HookManagerInfo::Iface::Hook>::iterator iter3;
|
2005-04-14 14:54:13 +02:00
|
|
|
for (iter3 = iter2->hooks_pre.begin(); iter3 != iter2->hooks_pre.end(); ++iter3)
|
|
|
|
if (iter3->plug == plug)
|
|
|
|
return true;
|
|
|
|
for (iter3 = iter2->hooks_post.begin(); iter3 != iter2->hooks_post.end(); ++iter3)
|
|
|
|
if (iter3->plug == plug)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSourceHookImpl::UnloadPlugin(Plugin plug)
|
|
|
|
{
|
2005-04-16 18:55:53 +02:00
|
|
|
// 1) Manually remove all hooks by this plugin
|
|
|
|
std::list<RemoveHookInfo> hookstoremove;
|
2005-04-14 14:54:13 +02:00
|
|
|
|
2005-04-16 18:55:53 +02:00
|
|
|
for (HookManInfoList::iterator iter = m_HookMans.begin(); iter != m_HookMans.end(); ++iter)
|
|
|
|
{
|
|
|
|
for (std::list<HookManagerInfo::Iface>::iterator iter2 = iter->ifaces.begin(); iter2 != iter->ifaces.end();
|
|
|
|
++iter2)
|
|
|
|
{
|
|
|
|
for (std::list<HookManagerInfo::Iface::Hook>::iterator iter3 = iter2->hooks_pre.begin(); iter3 != iter2->hooks_pre.end(); ++iter3)
|
|
|
|
if (iter3->plug == plug)
|
|
|
|
hookstoremove.push_back(RemoveHookInfo(iter3->plug, iter2->ptr, iter->func, iter3->handler, false));
|
|
|
|
|
|
|
|
for (std::list<HookManagerInfo::Iface::Hook>::iterator iter3 = iter2->hooks_post.begin(); iter3 != iter2->hooks_post.end(); ++iter3)
|
|
|
|
if (iter3->plug == plug)
|
|
|
|
hookstoremove.push_back(RemoveHookInfo(iter3->plug, iter2->ptr, iter->func, iter3->handler, true));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (std::list<RemoveHookInfo>::iterator rmiter = hookstoremove.begin(); rmiter != hookstoremove.end(); ++rmiter)
|
|
|
|
RemoveHook(rmiter->plug, rmiter->iface, rmiter->hpf, rmiter->handler, rmiter->post);
|
|
|
|
|
|
|
|
// 2) Other plugins may use hook managers in this plugin.
|
|
|
|
// Get a list of hook managers that are in this plugin and are used by other plugins
|
|
|
|
// Delete all hook managers that are in this plugin
|
|
|
|
|
|
|
|
HookManInfoList tmphookmans;
|
2005-04-14 14:54:13 +02:00
|
|
|
bool erase = false;
|
2005-04-16 18:55:53 +02:00
|
|
|
for (HookManInfoList::iterator iter = m_HookMans.begin(); iter != m_HookMans.end();
|
|
|
|
erase ? iter=m_HookMans.erase(iter) : ++iter)
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-04-16 18:55:53 +02:00
|
|
|
if (iter->plug == plug)
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-04-16 18:55:53 +02:00
|
|
|
if (!iter->ifaces.empty())
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-04-16 18:55:53 +02:00
|
|
|
// All hooks by this plugin are already removed
|
|
|
|
// So if there is an iface, it has to be used by an other plugin
|
|
|
|
tmphookmans.push_back(*iter);
|
2005-04-14 14:54:13 +02:00
|
|
|
}
|
|
|
|
erase = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
erase = false;
|
|
|
|
}
|
|
|
|
|
2005-04-16 18:55:53 +02:00
|
|
|
// For each hook manager:
|
|
|
|
for (HookManInfoList::iterator iter = tmphookmans.begin(); iter != tmphookmans.end(); ++iter)
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-04-16 18:55:53 +02:00
|
|
|
// Find a suitable hook manager in an other plugin
|
|
|
|
HookManInfoList::iterator newHookMan = FindHookMan(m_HookMans.begin(), m_HookMans.end(),
|
2005-04-14 14:54:13 +02:00
|
|
|
iter->proto, iter->vtbl_offs, iter->vtbl_idx, iter->thisptr_offs);
|
2005-04-16 18:55:53 +02:00
|
|
|
if (newHookMan == m_HookMans.end())
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
|
|
|
// This should _never_ happen.
|
2005-04-16 18:55:53 +02:00
|
|
|
// If there is a hook from an other plugin, the plugin must have provided a hook manager as well.
|
2005-04-14 14:54:13 +02:00
|
|
|
SH_ASSERT(0);
|
|
|
|
}
|
|
|
|
|
2005-04-16 18:55:53 +02:00
|
|
|
// AddHook should make sure that every plugin only has _one_ hook manager for _one_ proto/vi/vo/thisptroffs
|
|
|
|
SH_ASSERT(newHookMan->plug != plug);
|
2005-04-14 14:54:13 +02:00
|
|
|
|
2005-04-16 18:55:53 +02:00
|
|
|
// The first hooker should be always used - so the new hook manager has to be empty
|
|
|
|
SH_ASSERT(newHookMan->ifaces.empty());
|
|
|
|
|
|
|
|
// Move the ifaces from the old hook manager to the new one
|
|
|
|
newHookMan->ifaces = iter->ifaces;
|
|
|
|
|
|
|
|
// Unregister the old one, register the new one
|
|
|
|
iter->func(HA_Unregister, NULL);
|
|
|
|
newHookMan->func(HA_Register, &(*newHookMan));
|
2005-04-14 14:54:13 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSourceHookImpl::CompleteShutdown()
|
|
|
|
{
|
2005-04-16 18:55:53 +02:00
|
|
|
std::list<RemoveHookInfo> hookstoremove;
|
|
|
|
|
|
|
|
for (HookManInfoList::iterator iter = m_HookMans.begin(); iter != m_HookMans.end(); ++iter)
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-04-16 18:55:53 +02:00
|
|
|
for (std::list<HookManagerInfo::Iface>::iterator iter2 = iter->ifaces.begin(); iter2 != iter->ifaces.end();
|
|
|
|
++iter2)
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-04-16 18:55:53 +02:00
|
|
|
for (std::list<HookManagerInfo::Iface::Hook>::iterator iter3 = iter2->hooks_pre.begin(); iter3 != iter2->hooks_pre.end(); ++iter3)
|
|
|
|
hookstoremove.push_back(RemoveHookInfo(iter3->plug, iter2->ptr, iter->func, iter3->handler, false));
|
|
|
|
|
|
|
|
for (std::list<HookManagerInfo::Iface::Hook>::iterator iter3 = iter2->hooks_post.begin(); iter3 != iter2->hooks_post.end(); ++iter3)
|
|
|
|
hookstoremove.push_back(RemoveHookInfo(iter3->plug, iter2->ptr, iter->func, iter3->handler, true));
|
2005-04-14 14:54:13 +02:00
|
|
|
}
|
|
|
|
}
|
2005-04-16 18:55:53 +02:00
|
|
|
|
|
|
|
for (std::list<RemoveHookInfo>::iterator rmiter = hookstoremove.begin(); rmiter != hookstoremove.end(); ++rmiter)
|
|
|
|
RemoveHook(rmiter->plug, rmiter->iface, rmiter->hpf, rmiter->handler, rmiter->post);
|
|
|
|
|
|
|
|
m_HookMans.clear();
|
2005-04-14 14:54:13 +02:00
|
|
|
}
|
|
|
|
|
2005-04-16 18:55:53 +02:00
|
|
|
bool CSourceHookImpl::AddHook(Plugin plug, void *iface, int ifacesize, HookManagerPubFunc myHookMan, ISHDelegate *handler, bool post)
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-04-16 18:55:53 +02:00
|
|
|
// 1) Get info about the hook manager
|
|
|
|
HookManagerInfo tmp;
|
|
|
|
if (myHookMan(HA_GetInfo, &tmp) != 0)
|
2005-04-14 14:54:13 +02:00
|
|
|
return false;
|
|
|
|
|
2005-04-16 18:55:53 +02:00
|
|
|
// Add the proposed hook manager to the _end_ of the list if the plugin doesn't have a hook manager with this proto/vo/vi registered
|
|
|
|
HookManInfoList::iterator hookmaniter;
|
|
|
|
for (hookmaniter = m_HookMans.begin(); hookmaniter != m_HookMans.end(); ++hookmaniter)
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-04-16 18:55:53 +02:00
|
|
|
if (hookmaniter->plug == plug && strcmp(hookmaniter->proto, tmp.proto) == 0 &&
|
|
|
|
hookmaniter->vtbl_offs == tmp.vtbl_offs && hookmaniter->vtbl_idx == tmp.vtbl_idx &&
|
|
|
|
hookmaniter->thisptr_offs == tmp.thisptr_offs)
|
2005-04-14 14:54:13 +02:00
|
|
|
break;
|
|
|
|
}
|
2005-04-16 18:55:53 +02:00
|
|
|
if (hookmaniter == m_HookMans.end())
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-04-16 18:55:53 +02:00
|
|
|
// No such hook manager from this plugin yet, add it!
|
|
|
|
tmp.func = myHookMan;
|
2005-04-14 14:54:13 +02:00
|
|
|
tmp.plug = plug;
|
2005-04-16 18:55:53 +02:00
|
|
|
m_HookMans.push_back(tmp);
|
2005-04-14 14:54:13 +02:00
|
|
|
}
|
|
|
|
|
2005-04-16 18:55:53 +02:00
|
|
|
// Then, search for a suitable hook manager (from the beginning)
|
|
|
|
HookManInfoList::iterator hookman = FindHookMan(m_HookMans.begin(), m_HookMans.end(), tmp.proto, tmp.vtbl_offs, tmp.vtbl_idx, tmp.thisptr_offs);
|
|
|
|
SH_ASSERT(hookman != m_HookMans.end());
|
2005-04-14 14:54:13 +02:00
|
|
|
|
|
|
|
// Tell it to store the pointer if it's not already active
|
2005-04-16 18:55:53 +02:00
|
|
|
if (hookman->ifaces.empty())
|
|
|
|
hookman->func(HA_Register, &(*hookman));
|
2005-04-14 14:54:13 +02:00
|
|
|
|
2005-04-16 18:55:53 +02:00
|
|
|
std::list<HookManagerInfo::Iface>::iterator ifsiter = std::find(hookman->ifaces.begin(), hookman->ifaces.end(), iface);
|
2005-04-14 14:54:13 +02:00
|
|
|
|
2005-04-16 18:55:53 +02:00
|
|
|
if (ifsiter == hookman->ifaces.end())
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-04-16 18:55:53 +02:00
|
|
|
HookManagerInfo::Iface ifs;
|
2005-04-14 14:54:13 +02:00
|
|
|
ifs.ptr = iface;
|
|
|
|
ifs.callclass = GetCallClass(iface, ifacesize);
|
2005-04-16 18:55:53 +02:00
|
|
|
void *vtableptr = *reinterpret_cast<void**>(reinterpret_cast<char*>(iface) + hookman->vtbl_offs);
|
|
|
|
SetMemAccess(vtableptr, sizeof(void*) * hookman->vtbl_idx, SH_MEM_READ | SH_MEM_WRITE);
|
|
|
|
ifs.orig_entry = (*reinterpret_cast<void***>(reinterpret_cast<char*>(iface) + hookman->vtbl_offs))[hookman->vtbl_idx];
|
|
|
|
(*reinterpret_cast<void***>(reinterpret_cast<char*>(iface) + hookman->vtbl_offs))[hookman->vtbl_idx] =
|
|
|
|
(*reinterpret_cast<void***>(reinterpret_cast<char*>(hookman->hookfunc_inst) + hookman->hookfunc_vtbl_offs))[hookman->hookfunc_vtbl_idx];
|
|
|
|
hookman->ifaces.push_back(ifs);
|
|
|
|
ifsiter = hookman->ifaces.end();
|
2005-04-14 14:54:13 +02:00
|
|
|
--ifsiter;
|
|
|
|
}
|
2005-04-16 18:55:53 +02:00
|
|
|
HookManagerInfo::Iface::Hook hookinfo;
|
2005-04-14 14:54:13 +02:00
|
|
|
hookinfo.handler = handler;
|
|
|
|
hookinfo.plug = plug;
|
|
|
|
if (post)
|
|
|
|
ifsiter->hooks_post.push_back(hookinfo);
|
|
|
|
else
|
|
|
|
ifsiter->hooks_pre.push_back(hookinfo);
|
|
|
|
|
|
|
|
|
|
|
|
// Now that it is done, check whether we have to update any callclasses
|
|
|
|
for (Impl_CallClassList::iterator cciter = m_CallClasses.begin(); cciter != m_CallClasses.end(); ++cciter)
|
|
|
|
{
|
|
|
|
if (cciter->iface == iface)
|
|
|
|
{
|
|
|
|
ApplyCallClassPatch(*cciter, tmp.vtbl_offs, tmp.vtbl_idx, ifsiter->orig_entry);
|
|
|
|
|
|
|
|
// We can assume that there is no more callclass with the same iface
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2005-04-16 18:55:53 +02:00
|
|
|
bool CSourceHookImpl::RemoveHook(Plugin plug, void *iface, HookManagerPubFunc myHookMan, ISHDelegate *handler, bool post)
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-04-16 18:55:53 +02:00
|
|
|
HookManagerInfo tmp;
|
|
|
|
if (myHookMan(HA_GetInfo, &tmp) != 0)
|
2005-04-14 14:54:13 +02:00
|
|
|
return false;
|
|
|
|
|
2005-04-16 18:55:53 +02:00
|
|
|
// Find the hook manager and the hook
|
|
|
|
HookManInfoList::iterator hookman = FindHookMan(m_HookMans.begin(), m_HookMans.end(),
|
2005-04-14 14:54:13 +02:00
|
|
|
tmp.proto, tmp.vtbl_offs, tmp.vtbl_idx, tmp.thisptr_offs);
|
2005-04-16 18:55:53 +02:00
|
|
|
if (hookman == m_HookMans.end())
|
2005-04-14 14:54:13 +02:00
|
|
|
return false;
|
|
|
|
|
2005-04-16 18:55:53 +02:00
|
|
|
for (std::list<HookManagerInfo::Iface>::iterator ifaceiter = hookman->ifaces.begin(); ifaceiter != hookman->ifaces.end(); ++ifaceiter)
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
|
|
|
if (ifaceiter->ptr == iface)
|
|
|
|
{
|
2005-04-16 18:55:53 +02:00
|
|
|
std::list<HookManagerInfo::Iface::Hook> &hooks = post ? ifaceiter->hooks_post : ifaceiter->hooks_pre;
|
2005-04-14 14:54:13 +02:00
|
|
|
bool erase;
|
2005-04-16 18:55:53 +02:00
|
|
|
for (std::list<HookManagerInfo::Iface::Hook>::iterator hookiter = hooks.begin();
|
2005-04-14 14:54:13 +02:00
|
|
|
hookiter != hooks.end(); erase ? hookiter = hooks.erase(hookiter) : ++hookiter)
|
|
|
|
{
|
|
|
|
erase = hookiter->plug == plug && hookiter->handler->IsEqual(handler);
|
|
|
|
if (erase)
|
|
|
|
hookiter->handler->DeleteThis(); // Make the _plugin_ delete the handler object
|
|
|
|
}
|
|
|
|
if (ifaceiter->hooks_post.empty() && ifaceiter->hooks_pre.empty())
|
|
|
|
{
|
|
|
|
// Deactivate the hook
|
|
|
|
(*reinterpret_cast<void***>(reinterpret_cast<char*>(ifaceiter->ptr) +
|
2005-04-16 18:55:53 +02:00
|
|
|
hookman->vtbl_offs))[hookman->vtbl_idx]= ifaceiter->orig_entry;
|
2005-04-14 14:54:13 +02:00
|
|
|
|
|
|
|
// Release the callclass
|
|
|
|
ReleaseCallClass(ifaceiter->callclass);
|
|
|
|
|
|
|
|
// Remove the iface info
|
2005-04-16 18:55:53 +02:00
|
|
|
hookman->ifaces.erase(ifaceiter);
|
2005-04-14 14:54:13 +02:00
|
|
|
|
2005-04-16 18:55:53 +02:00
|
|
|
if (hookman->ifaces.empty())
|
|
|
|
hookman->func(HA_Unregister, NULL);
|
2005-04-14 14:54:13 +02:00
|
|
|
}
|
|
|
|
// :TODO: Better return value? Or none?
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void *CSourceHookImpl::GetCallClass(void *iface, size_t size)
|
|
|
|
{
|
|
|
|
for (Impl_CallClassList::iterator cciter = m_CallClasses.begin(); cciter != m_CallClasses.end(); ++cciter)
|
|
|
|
{
|
|
|
|
if (cciter->iface == iface)
|
|
|
|
{
|
|
|
|
++cciter->refcounter;
|
|
|
|
return cciter->ptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// The layout of callclasses is:
|
|
|
|
// Copy of the class; vtable entries pointing to gates
|
|
|
|
// Pointer to the corresponding CallClass object
|
|
|
|
// Copy of the class; vtable entries pointing to original functions
|
|
|
|
|
|
|
|
CallClass tmp;
|
|
|
|
char *ptr = new char[size * 2 + sizeof(void*)];
|
|
|
|
tmp.ptr = reinterpret_cast<void*>(ptr);
|
|
|
|
memcpy(reinterpret_cast<void*>(ptr), iface, size);
|
|
|
|
memcpy(reinterpret_cast<void*>(ptr + size + sizeof(void*)), iface, size);
|
|
|
|
tmp.iface = iface;
|
|
|
|
tmp.size = size;
|
|
|
|
tmp.refcounter = 1;
|
|
|
|
|
|
|
|
// Go through _all_ hooks and apply any needed patches
|
2005-04-16 18:55:53 +02:00
|
|
|
for (HookManInfoList::iterator hookman = m_HookMans.begin(); hookman != m_HookMans.end(); ++hookman)
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-04-16 18:55:53 +02:00
|
|
|
for (std::list<HookManagerInfo::Iface>::iterator ifaceiter = hookman->ifaces.begin(); ifaceiter != hookman->ifaces.end(); ++ifaceiter)
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
|
|
|
if (ifaceiter->ptr == iface)
|
|
|
|
{
|
2005-04-16 18:55:53 +02:00
|
|
|
if (!ApplyCallClassPatch(tmp, hookman->vtbl_offs, hookman->vtbl_idx, ifaceiter->orig_entry))
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
|
|
|
FreeCallClass(tmp);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_CallClasses.push_back(tmp);
|
|
|
|
// The object has to be followed by the pointer to the vtable info object
|
|
|
|
*reinterpret_cast<void**>(ptr + size) = &m_CallClasses.back();
|
|
|
|
return tmp.ptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSourceHookImpl::ReleaseCallClass(void *ptr)
|
|
|
|
{
|
|
|
|
Impl_CallClassList::iterator iter = std::find(m_CallClasses.begin(), m_CallClasses.end(), ptr);
|
|
|
|
if (iter == m_CallClasses.end())
|
|
|
|
return;
|
|
|
|
--iter->refcounter;
|
|
|
|
if (iter->refcounter < 1)
|
|
|
|
{
|
|
|
|
FreeCallClass(*iter);
|
|
|
|
m_CallClasses.erase(iter);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSourceHookImpl::FreeCallClass(CallClass &cc)
|
|
|
|
{
|
|
|
|
for (CallClass::VTableList::iterator vtbliter = cc.vtables.begin(); vtbliter != cc.vtables.end(); ++vtbliter)
|
|
|
|
{
|
|
|
|
#if SH_RUNTIME_CODEGEN == 1
|
|
|
|
// Delete generated callgates
|
|
|
|
for (std::list<int>::iterator cgiter = vtbliter->patches.begin(); cgiter != vtbliter->patches.end(); ++cgiter)
|
|
|
|
{
|
|
|
|
char *cgptr = reinterpret_cast<char**>(vtbliter->ptr)[*cgiter];
|
|
|
|
delete [] cgptr;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
delete [] reinterpret_cast<char*>(vtbliter->ptr);
|
|
|
|
}
|
|
|
|
delete [] reinterpret_cast<char*>(cc.ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CSourceHookImpl::ApplyCallClassPatch(CallClass &cc, int vtbl_offs, int vtbl_idx, void *orig_entry)
|
|
|
|
{
|
|
|
|
char *ptr = reinterpret_cast<char*>(cc.ptr);
|
|
|
|
void *vtable = *reinterpret_cast<void**>(ptr + vtbl_offs);
|
|
|
|
|
|
|
|
// Check whether we already know that vtable
|
|
|
|
CallClass::VTableList::iterator vtbliter = std::find(cc.vtables.begin(),
|
|
|
|
cc.vtables.end(), reinterpret_cast<void*>(vtable));
|
|
|
|
int actvtablesize=MAX_VTABLE_LEN;
|
|
|
|
if (vtbliter == cc.vtables.end())
|
|
|
|
{
|
|
|
|
CallClass::VTable newvtableinfo;
|
|
|
|
int pagesnum = (MAX_VTABLE_LEN % m_PageSize == 0) ? MAX_VTABLE_LEN / m_PageSize : MAX_VTABLE_LEN / m_PageSize + 1;
|
|
|
|
// Set all pages in the range to readwrite
|
|
|
|
// :TODO: Fix this!
|
|
|
|
char *pagebegin = reinterpret_cast<char*>(vtable) - (reinterpret_cast<int>(vtable) % m_PageSize);
|
|
|
|
for (int ipage = 0; ipage < pagesnum; ++ipage)
|
|
|
|
{
|
|
|
|
if (!SetMemAccess(reinterpret_cast<void*>(pagebegin + ipage * m_PageSize), 1,
|
|
|
|
SH_MEM_READ | SH_MEM_WRITE))
|
|
|
|
{
|
|
|
|
// We can't go here anymore
|
|
|
|
actvtablesize = static_cast<int>((pagebegin + ipage * m_PageSize) - reinterpret_cast<char*>(vtable));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (actvtablesize < 1)
|
|
|
|
{
|
|
|
|
// We can't access the vtable -> Quit
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// Create a new vtable
|
|
|
|
newvtableinfo.ptr = reinterpret_cast<void*>(new char[actvtablesize]);
|
|
|
|
// Fill it with the information from the old vtable
|
|
|
|
memcpy(newvtableinfo.ptr, vtable, actvtablesize);
|
|
|
|
newvtableinfo.actsize = actvtablesize;
|
|
|
|
// Set the pointer in the object and add it to the list of already known vtables
|
|
|
|
*reinterpret_cast<void**>(ptr + vtbl_offs) = newvtableinfo.ptr;
|
|
|
|
cc.vtables.push_back(newvtableinfo);
|
|
|
|
|
|
|
|
vtbliter = cc.vtables.end();
|
|
|
|
--vtbliter;
|
|
|
|
// :TODO: When not codegen, patch the other vtable?
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check whether we already have this patch
|
|
|
|
if (std::find(vtbliter->patches.begin(), vtbliter->patches.end(), vtbl_idx) == vtbliter->patches.end())
|
|
|
|
{
|
|
|
|
// No -> apply it
|
|
|
|
|
|
|
|
// Get a call gate
|
|
|
|
void *callgate = NULL;
|
|
|
|
#if SH_RUNTIME_CODEGEN == 1
|
|
|
|
unsigned char *cggen = new unsigned char[SH_CCC_CODESIZE];
|
|
|
|
SH_CCC_MakeGate(cc.iface, cc.ptr, cggen, orig_entry);
|
|
|
|
callgate = (void*)cggen;
|
|
|
|
SetMemAccess(callgate, SH_CCC_CODESIZE, SH_MEM_READ | SH_MEM_WRITE | SH_MEM_EXEC);
|
|
|
|
#else
|
|
|
|
// :TODO:
|
|
|
|
#error Not supported yet!
|
|
|
|
#endif
|
|
|
|
vtbliter->patches.push_back(vtbl_idx);
|
|
|
|
reinterpret_cast<void**>(vtbliter->ptr)[vtbl_idx] = callgate;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-04-16 18:55:53 +02:00
|
|
|
CSourceHookImpl::HookManInfoList::iterator CSourceHookImpl::FindHookMan(HookManInfoList::iterator begin,
|
|
|
|
HookManInfoList::iterator end, const char *proto, int vtblofs, int vtblidx, int thisptrofs)
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-04-16 18:55:53 +02:00
|
|
|
for (HookManInfoList::iterator hookmaniter = m_HookMans.begin(); hookmaniter != m_HookMans.end(); ++hookmaniter)
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-04-16 18:55:53 +02:00
|
|
|
if (strcmp(hookmaniter->proto, proto) == 0 && hookmaniter->vtbl_offs == vtblofs && hookmaniter->vtbl_idx == vtblidx &&
|
|
|
|
hookmaniter->thisptr_offs == thisptrofs)
|
2005-04-14 14:54:13 +02:00
|
|
|
break;
|
|
|
|
}
|
2005-04-16 18:55:53 +02:00
|
|
|
return hookmaniter;
|
2005-04-14 14:54:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void CSourceHookImpl::SetRes(META_RES res)
|
|
|
|
{
|
|
|
|
m_CurRes = res;
|
|
|
|
}
|
|
|
|
|
|
|
|
META_RES CSourceHookImpl::GetPrevRes()
|
|
|
|
{
|
|
|
|
return m_PrevRes;
|
|
|
|
}
|
|
|
|
|
|
|
|
META_RES CSourceHookImpl::GetStatus()
|
|
|
|
{
|
|
|
|
return m_Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
const void *CSourceHookImpl::GetOrigRet()
|
|
|
|
{
|
|
|
|
return m_OrigRet;
|
|
|
|
}
|
|
|
|
|
|
|
|
const void *CSourceHookImpl::GetOverrideRet()
|
|
|
|
{
|
|
|
|
return m_OverrideRet;
|
|
|
|
}
|
|
|
|
|
|
|
|
META_RES &CSourceHookImpl::GetCurResRef()
|
|
|
|
{
|
|
|
|
return m_CurRes;
|
|
|
|
}
|
|
|
|
|
|
|
|
META_RES &CSourceHookImpl::GetPrevResRef()
|
|
|
|
{
|
|
|
|
return m_PrevRes;
|
|
|
|
}
|
|
|
|
|
|
|
|
META_RES &CSourceHookImpl::GetStatusRef()
|
|
|
|
{
|
|
|
|
return m_Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSourceHookImpl::SetOrigRet(const void *ptr)
|
|
|
|
{
|
|
|
|
m_OrigRet = ptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSourceHookImpl::SetOverrideRet(const void *ptr)
|
|
|
|
{
|
|
|
|
m_OverrideRet = ptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSourceHookImpl::SetIfacePtr(void *ptr)
|
|
|
|
{
|
|
|
|
m_IfacePtr = ptr;
|
|
|
|
}
|
|
|
|
void *CSourceHookImpl::GetIfacePtr()
|
|
|
|
{
|
|
|
|
return m_IfacePtr;
|
|
|
|
}
|
|
|
|
}
|