Add support to define function signatures in gamedata
A "Functions" section is parsed in gamedata files that allow you to define the signature of functions including metadata like the calling convention and specifying the register an argument is passed in. A new native DHookCreateFromConf can be used to setup a hook or detour from one of that function sections in the "Functions" section.
This commit is contained in:
parent
e2d71bae03
commit
aa3018a27d
@ -255,6 +255,7 @@ program.sources += [
|
||||
'extension.cpp',
|
||||
'listeners.cpp',
|
||||
'natives.cpp',
|
||||
'signatures.cpp',
|
||||
'vhook.cpp',
|
||||
'util.cpp',
|
||||
'dynhooks_sourcepawn.cpp',
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "extension.h"
|
||||
#include "listeners.h"
|
||||
#include "dynhooks_sourcepawn.h"
|
||||
#include "signatures.h"
|
||||
|
||||
DHooks g_DHooksIface; /**< Global singleton for extension's main interface */
|
||||
SMEXT_LINK(&g_DHooksIface);
|
||||
@ -57,6 +58,7 @@ bool DHooks::SDK_OnLoad(char *error, size_t maxlength, bool late)
|
||||
sharesys->AddNatives(myself, g_Natives);
|
||||
|
||||
g_pEntityListener = new DHooksEntityListener();
|
||||
g_pSignatures = new SignatureGameConfig();
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -84,6 +86,7 @@ void DHooks::SDK_OnAllLoaded()
|
||||
SM_GET_LATE_IFACE(SDKHOOKS, g_pSDKHooks);
|
||||
|
||||
g_pSDKHooks->AddEntityListener(g_pEntityListener);
|
||||
gameconfs->AddUserConfigHook("Functions", g_pSignatures);
|
||||
}
|
||||
|
||||
void DHooks::SDK_OnUnload()
|
||||
@ -101,6 +104,8 @@ void DHooks::SDK_OnUnload()
|
||||
handlesys->RemoveType(g_HookSetupHandle, myself->GetIdentity());
|
||||
handlesys->RemoveType(g_HookParamsHandle, myself->GetIdentity());
|
||||
handlesys->RemoveType(g_HookReturnHandle, myself->GetIdentity());
|
||||
|
||||
gameconfs->RemoveUserConfigHook("Functions", g_pSignatures);
|
||||
}
|
||||
|
||||
bool DHooks::SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlength, bool late)
|
||||
|
@ -121,6 +121,7 @@
|
||||
<ClCompile Include="..\extension.cpp" />
|
||||
<ClCompile Include="..\listeners.cpp" />
|
||||
<ClCompile Include="..\natives.cpp" />
|
||||
<ClCompile Include="..\signatures.cpp" />
|
||||
<ClCompile Include="..\util.cpp" />
|
||||
<ClCompile Include="..\vhook.cpp" />
|
||||
</ItemGroup>
|
||||
|
@ -37,6 +37,7 @@
|
||||
<ClCompile Include="..\DynamicHooks\conventions\x86MsThiscall.cpp">
|
||||
<Filter>DynamicHooks\conventions</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\signatures.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\extension.h">
|
||||
|
88
natives.cpp
88
natives.cpp
@ -1,6 +1,7 @@
|
||||
#include "natives.h"
|
||||
#include "util.h"
|
||||
#include "dynhooks_sourcepawn.h"
|
||||
#include "signatures.h"
|
||||
|
||||
// Must match same enum in sdktools.inc
|
||||
enum SDKFuncConfSource
|
||||
@ -48,7 +49,7 @@ cell_t Native_CreateHook(IPluginContext *pContext, const cell_t *params)
|
||||
return hndl;
|
||||
}
|
||||
|
||||
//native Handle:DHookCreateDetour(Address:funcaddr, CallingConvention callConv, ReturnType:returntype, ThisPointerType:thistype);
|
||||
//native Handle:DHookCreateDetour(Address:funcaddr, CallingConvention:callConv, ReturnType:returntype, ThisPointerType:thistype);
|
||||
cell_t Native_CreateDetour(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
HookSetup *setup = new HookSetup((ReturnType)params[3], PASSFLAG_BYVAL, (CallingConvention)params[2], (ThisPointerType)params[4], (void *)params[1]);
|
||||
@ -64,6 +65,86 @@ cell_t Native_CreateDetour(IPluginContext *pContext, const cell_t *params)
|
||||
return hndl;
|
||||
}
|
||||
|
||||
// native Handle:DHookCreateFromConf(Handle:gameconf, const String:function[], DHookCallback:callback=INVALID_FUNCTION);
|
||||
cell_t Native_DHookCreateFromConf(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
IGameConfig *conf;
|
||||
HandleError err;
|
||||
if ((conf = gameconfs->ReadHandle(params[1], pContext->GetIdentity(), &err)) == nullptr)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid Handle %x (error %d)", params[1], err);
|
||||
}
|
||||
|
||||
char *function;
|
||||
pContext->LocalToString(params[2], &function);
|
||||
|
||||
SignatureWrapper *sig = g_pSignatures->GetFunctionSignature(function);
|
||||
if (!sig)
|
||||
{
|
||||
return pContext->ThrowNativeError("Function signature \"%s\" was not found.", function);
|
||||
}
|
||||
|
||||
IPluginFunction *callback = nullptr;
|
||||
if (params[3] != -1)
|
||||
{
|
||||
callback = pContext->GetFunctionById(params[3]);
|
||||
if (!callback)
|
||||
return pContext->ThrowNativeError("Failed to find callback function.");
|
||||
}
|
||||
|
||||
HookSetup *setup = nullptr;
|
||||
// This is a virtual hook.
|
||||
if (sig->offset.length() > 0)
|
||||
{
|
||||
int offset;
|
||||
if (!conf->GetOffset(sig->offset.chars(), &offset))
|
||||
{
|
||||
return BAD_HANDLE;
|
||||
}
|
||||
|
||||
setup = new HookSetup(sig->retType, PASSFLAG_BYVAL, sig->hookType, sig->thisType, offset, callback);
|
||||
}
|
||||
// This is a detour.
|
||||
else
|
||||
{
|
||||
void *addr = nullptr;;
|
||||
if (sig->signature.length() > 0)
|
||||
{
|
||||
if (!conf->GetMemSig(sig->signature.chars(), &addr) || !addr)
|
||||
{
|
||||
return BAD_HANDLE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!conf->GetAddress(sig->address.chars(), &addr) || !addr)
|
||||
{
|
||||
return BAD_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
setup = new HookSetup(sig->retType, PASSFLAG_BYVAL, sig->callConv, sig->thisType, addr);
|
||||
}
|
||||
|
||||
// Push all the arguments.
|
||||
auto args = sig->args.iter();
|
||||
while (!args.empty())
|
||||
{
|
||||
ParamInfo info = args->value;
|
||||
setup->params.push_back(info);
|
||||
args.next();
|
||||
}
|
||||
|
||||
// Create the handle to hold this setup.
|
||||
Handle_t hndl = handlesys->CreateHandle(g_HookSetupHandle, setup, pContext->GetIdentity(), myself->GetIdentity(), NULL);
|
||||
if (!hndl)
|
||||
{
|
||||
delete setup;
|
||||
return pContext->ThrowNativeError("Failed to create hook");
|
||||
}
|
||||
|
||||
return hndl;
|
||||
}
|
||||
|
||||
//native bool:DHookSetFromConf(Handle:setup, Handle:gameconf, SDKFuncConfSource:source, const String:name[]);
|
||||
cell_t Native_SetFromConf(IPluginContext *pContext, const cell_t *params)
|
||||
@ -1232,10 +1313,11 @@ sp_nativeinfo_t g_Natives[] =
|
||||
{
|
||||
{"DHookCreate", Native_CreateHook},
|
||||
{"DHookCreateDetour", Native_CreateDetour},
|
||||
{"DHookSetFromConf", Native_SetFromConf },
|
||||
{"DHookCreateFromConf", Native_DHookCreateFromConf},
|
||||
{"DHookSetFromConf", Native_SetFromConf},
|
||||
{"DHookAddParam", Native_AddParam},
|
||||
{"DHookEnableDetour", Native_EnableDetour},
|
||||
//{"DHookDisableDetour", Native_DisableDetour},
|
||||
{"DHookDisableDetour", Native_DisableDetour},
|
||||
{"DHookEntity", Native_HookEntity},
|
||||
{"DHookGamerules", Native_HookGamerules},
|
||||
{"DHookRaw", Native_HookRaw},
|
||||
|
562
signatures.cpp
Normal file
562
signatures.cpp
Normal file
@ -0,0 +1,562 @@
|
||||
#include <signatures.h>
|
||||
|
||||
SignatureGameConfig *g_pSignatures;
|
||||
|
||||
enum ParseState
|
||||
{
|
||||
PState_None,
|
||||
PState_Root,
|
||||
PState_Function,
|
||||
PState_Arguments,
|
||||
PState_Argument
|
||||
};
|
||||
|
||||
ParseState g_ParseState;
|
||||
unsigned int g_IgnoreLevel;
|
||||
// The parent section type of a platform specific "windows" or "linux" section.
|
||||
ParseState g_PlatformOnlyState;
|
||||
|
||||
SignatureWrapper *g_CurrentSignature;
|
||||
ke::AString g_CurrentFunctionName;
|
||||
ParamInfo g_CurrentArgumentInfo;
|
||||
ke::AString g_CurrentArgumentName;
|
||||
|
||||
SignatureWrapper *SignatureGameConfig::GetFunctionSignature(const char *function)
|
||||
{
|
||||
auto sig = signatures_.find(function);
|
||||
if (!sig.found())
|
||||
return nullptr;
|
||||
|
||||
return sig->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Game config "Functions" section parsing.
|
||||
*/
|
||||
SMCResult SignatureGameConfig::ReadSMC_NewSection(const SMCStates *states, const char *name)
|
||||
{
|
||||
// We're ignoring the parent section. Ignore all child sections as well.
|
||||
if (g_IgnoreLevel > 0)
|
||||
{
|
||||
g_IgnoreLevel++;
|
||||
return SMCResult_Continue;
|
||||
}
|
||||
|
||||
// Handle platform specific sections first.
|
||||
#if defined WIN32
|
||||
if (!strcmp(name, "windows"))
|
||||
#elif defined _LINUX
|
||||
if (!strcmp(name, "linux"))
|
||||
#elif defined _OSX
|
||||
if (!strcmp(name, "mac"))
|
||||
#endif
|
||||
{
|
||||
// We're already in a section for a different OS that we're ignoring. Can't have a section for our OS in here.
|
||||
if (g_IgnoreLevel > 0)
|
||||
{
|
||||
smutils->LogError(myself, "Unreachable platform specific section in \"%s\" Function: line: %i col: %i", g_CurrentFunctionName, states->line, states->col);
|
||||
return SMCResult_HaltFail;
|
||||
}
|
||||
|
||||
// We don't support nested (useless) sections of the same OS like "windows" { "windows" { "foo" "bar" } }
|
||||
if (g_PlatformOnlyState != PState_None)
|
||||
{
|
||||
smutils->LogError(myself, "Duplicate platform specific section for \"%s\". Already parsing only for that OS: line: %i col: %i", name, states->line, states->col);
|
||||
return SMCResult_HaltFail;
|
||||
}
|
||||
|
||||
// This is a specific block for us.
|
||||
g_PlatformOnlyState = g_ParseState;
|
||||
return SMCResult_Continue;
|
||||
}
|
||||
#if defined WIN32
|
||||
else if (!strcmp(name, "linux") || !strcmp(name, "mac"))
|
||||
#elif defined _LINUX
|
||||
else if (!strcmp(name, "windows") || !strcmp(name, "mac"))
|
||||
#elif defined _OSX
|
||||
else if (!strcmp(name, "windows") || !strcmp(name, "linux"))
|
||||
#endif
|
||||
{
|
||||
if (g_PlatformOnlyState != PState_None)
|
||||
{
|
||||
smutils->LogError(myself, "Unreachable platform specific section in \"%s\" Function: line: %i col: %i", g_CurrentFunctionName, states->line, states->col);
|
||||
return SMCResult_HaltFail;
|
||||
}
|
||||
|
||||
// A specific block for a different platform.
|
||||
g_IgnoreLevel++;
|
||||
return SMCResult_Continue;
|
||||
}
|
||||
|
||||
switch (g_ParseState)
|
||||
{
|
||||
case PState_Root:
|
||||
{
|
||||
auto sig = signatures_.find(name);
|
||||
if (sig.found())
|
||||
g_CurrentSignature = sig->value;
|
||||
else
|
||||
g_CurrentSignature = new SignatureWrapper();
|
||||
g_CurrentFunctionName = name;
|
||||
g_ParseState = PState_Function;
|
||||
break;
|
||||
}
|
||||
|
||||
case PState_Function:
|
||||
{
|
||||
if (!strcmp(name, "arguments"))
|
||||
{
|
||||
g_ParseState = PState_Arguments;
|
||||
}
|
||||
else
|
||||
{
|
||||
smutils->LogError(myself, "Unknown subsection \"%s\" (expected \"arguments\"): line: %i col: %i", name, states->line, states->col);
|
||||
return SMCResult_HaltFail;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PState_Arguments:
|
||||
{
|
||||
g_ParseState = PState_Argument;
|
||||
g_CurrentArgumentName = name;
|
||||
|
||||
auto arg = g_CurrentSignature->args.find(name);
|
||||
// Continue changing that argument now.
|
||||
if (arg.found())
|
||||
{
|
||||
g_CurrentArgumentInfo = arg->value;
|
||||
}
|
||||
else
|
||||
{
|
||||
ParamInfo info;
|
||||
memset(&info, 0, sizeof(info));
|
||||
info.flags = PASSFLAG_BYVAL;
|
||||
g_CurrentArgumentInfo = info;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
smutils->LogError(myself, "Unknown subsection \"%s\": line: %i col: %i", name, states->line, states->col);
|
||||
return SMCResult_HaltFail;
|
||||
}
|
||||
|
||||
return SMCResult_Continue;
|
||||
}
|
||||
|
||||
SMCResult SignatureGameConfig::ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
|
||||
{
|
||||
// We don't care for anything in this section or subsections.
|
||||
if (g_IgnoreLevel > 0)
|
||||
return SMCResult_Continue;
|
||||
|
||||
switch (g_ParseState)
|
||||
{
|
||||
case PState_Function:
|
||||
|
||||
if (!strcmp(key, "signature"))
|
||||
{
|
||||
if (g_CurrentSignature->address.length() > 0 || g_CurrentSignature->offset.length() > 0)
|
||||
{
|
||||
smutils->LogError(myself, "Cannot have \"signature\", \"address\" or \"offset\" keys at the same time in one function: line: %i col: %i", states->line, states->col);
|
||||
return SMCResult_HaltFail;
|
||||
}
|
||||
g_CurrentSignature->signature = value;
|
||||
}
|
||||
else if (!strcmp(key, "address"))
|
||||
{
|
||||
if (g_CurrentSignature->signature.length() > 0 || g_CurrentSignature->offset.length() > 0)
|
||||
{
|
||||
smutils->LogError(myself, "Cannot have \"signature\", \"address\" or \"offset\" keys at the same time in one function: line: %i col: %i", states->line, states->col);
|
||||
return SMCResult_HaltFail;
|
||||
}
|
||||
g_CurrentSignature->address = value;
|
||||
}
|
||||
else if (!strcmp(key, "offset"))
|
||||
{
|
||||
if (g_CurrentSignature->address.length() > 0 || g_CurrentSignature->signature.length() > 0)
|
||||
{
|
||||
smutils->LogError(myself, "Cannot have \"signature\", \"address\" or \"offset\" keys at the same time in one function: line: %i col: %i", states->line, states->col);
|
||||
return SMCResult_HaltFail;
|
||||
}
|
||||
g_CurrentSignature->offset = value;
|
||||
}
|
||||
else if (!strcmp(key, "callconv"))
|
||||
{
|
||||
CallingConvention callConv;
|
||||
|
||||
if (!strcmp(value, "cdecl"))
|
||||
callConv = CallConv_CDECL;
|
||||
else if (!strcmp(value, "thiscall"))
|
||||
callConv = CallConv_THISCALL;
|
||||
else if (!strcmp(value, "stdcall"))
|
||||
callConv = CallConv_STDCALL;
|
||||
else
|
||||
{
|
||||
smutils->LogError(myself, "Invalid calling convention \"%s\": line: %i col: %i", value, states->line, states->col);
|
||||
return SMCResult_HaltFail;
|
||||
}
|
||||
|
||||
g_CurrentSignature->callConv = callConv;
|
||||
}
|
||||
else if (!strcmp(key, "hooktype"))
|
||||
{
|
||||
HookType hookType;
|
||||
|
||||
if (!strcmp(value, "entity"))
|
||||
hookType = HookType_Entity;
|
||||
else if (!strcmp(value, "gamerules"))
|
||||
hookType = HookType_GameRules;
|
||||
else if (!strcmp(value, "raw"))
|
||||
hookType = HookType_Raw;
|
||||
else
|
||||
{
|
||||
smutils->LogError(myself, "Invalid hook type \"%s\": line: %i col: %i", value, states->line, states->col);
|
||||
return SMCResult_HaltFail;
|
||||
}
|
||||
|
||||
g_CurrentSignature->hookType = hookType;
|
||||
}
|
||||
else if (!strcmp(key, "return"))
|
||||
{
|
||||
g_CurrentSignature->retType = GetReturnTypeFromString(value);
|
||||
|
||||
if (g_CurrentSignature->retType == ReturnType_Unknown)
|
||||
{
|
||||
smutils->LogError(myself, "Invalid return type \"%s\": line: %i col: %i", value, states->line, states->col);
|
||||
return SMCResult_HaltFail;
|
||||
}
|
||||
}
|
||||
else if (!strcmp(key, "this"))
|
||||
{
|
||||
if (!strcmp(value, "ignore"))
|
||||
g_CurrentSignature->thisType = ThisPointer_Ignore;
|
||||
else if (!strcmp(value, "entity"))
|
||||
g_CurrentSignature->thisType = ThisPointer_CBaseEntity;
|
||||
else if (!strcmp(value, "address"))
|
||||
g_CurrentSignature->thisType = ThisPointer_Address;
|
||||
else
|
||||
{
|
||||
smutils->LogError(myself, "Invalid this type \"%s\": line: %i col: %i", value, states->line, states->col);
|
||||
return SMCResult_HaltFail;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
smutils->LogError(myself, "Unknown key in Functions section \"%s\": line: %i col: %i", key, states->line, states->col);
|
||||
return SMCResult_HaltFail;
|
||||
}
|
||||
break;
|
||||
|
||||
case PState_Argument:
|
||||
|
||||
if (!strcmp(key, "type"))
|
||||
{
|
||||
g_CurrentArgumentInfo.type = GetHookParamTypeFromString(value);
|
||||
if (g_CurrentArgumentInfo.type == HookParamType_Unknown)
|
||||
{
|
||||
smutils->LogError(myself, "Invalid argument type \"%s\" for argument \"%s\": line: %i col: %i", value, g_CurrentArgumentName.chars(), states->line, states->col);
|
||||
return SMCResult_HaltFail;
|
||||
}
|
||||
}
|
||||
else if (!strcmp(key, "size"))
|
||||
{
|
||||
g_CurrentArgumentInfo.size = atoi(value);
|
||||
|
||||
if (g_CurrentArgumentInfo.size < 1)
|
||||
{
|
||||
smutils->LogError(myself, "Invalid argument size \"%s\" for argument \"%s\": line: %i col: %i", value, g_CurrentArgumentName.chars(), states->line, states->col);
|
||||
return SMCResult_HaltFail;
|
||||
}
|
||||
}
|
||||
else if (!strcmp(key, "flags"))
|
||||
{
|
||||
size_t flags;
|
||||
if (strstr(value, "byval"))
|
||||
flags |= PASSFLAG_BYVAL;
|
||||
else if (strstr(value, "byref"))
|
||||
flags |= PASSFLAG_BYREF;
|
||||
else if (strstr(value, "byref"))
|
||||
flags |= PASSFLAG_ODTOR;
|
||||
else if (strstr(value, "octor"))
|
||||
flags |= PASSFLAG_OCTOR;
|
||||
else if (strstr(value, "oassignop"))
|
||||
flags |= PASSFLAG_OASSIGNOP;
|
||||
else if (strstr(value, "ocopyctor"))
|
||||
flags |= PASSFLAG_OCOPYCTOR;
|
||||
else if (strstr(value, "ounalign"))
|
||||
flags |= PASSFLAG_OUNALIGN;
|
||||
|
||||
g_CurrentArgumentInfo.flags = flags;
|
||||
}
|
||||
else if (!strcmp(key, "register"))
|
||||
{
|
||||
g_CurrentArgumentInfo.custom_register = GetCustomRegisterFromString(value);
|
||||
|
||||
if (g_CurrentArgumentInfo.custom_register == Register_t::None)
|
||||
{
|
||||
smutils->LogError(myself, "Invalid register \"%s\": line: %i col: %i", value, states->line, states->col);
|
||||
return SMCResult_HaltFail;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
smutils->LogError(myself, "Unknown key in Functions section \"%s\": line: %i col: %i", key, states->line, states->col);
|
||||
return SMCResult_HaltFail;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
smutils->LogError(myself, "Unknown key in Functions section \"%s\": line: %i col: %i", key, states->line, states->col);
|
||||
return SMCResult_HaltFail;
|
||||
}
|
||||
return SMCResult_Continue;
|
||||
}
|
||||
|
||||
SMCResult SignatureGameConfig::ReadSMC_LeavingSection(const SMCStates *states)
|
||||
{
|
||||
// We were ignoring this section.
|
||||
if (g_IgnoreLevel > 0)
|
||||
{
|
||||
g_IgnoreLevel--;
|
||||
|
||||
// We were in a subsection of an ignored section. Keep ignoring.
|
||||
if (g_IgnoreLevel > 0)
|
||||
return SMCResult_Continue;
|
||||
}
|
||||
|
||||
// We were in a section only for our OS.
|
||||
if (g_PlatformOnlyState == g_ParseState)
|
||||
{
|
||||
g_PlatformOnlyState = PState_None;
|
||||
return SMCResult_Continue;
|
||||
}
|
||||
|
||||
switch (g_ParseState)
|
||||
{
|
||||
case PState_Function:
|
||||
g_ParseState = PState_Root;
|
||||
|
||||
if (!g_CurrentSignature->address.length() && !g_CurrentSignature->signature.length())
|
||||
{
|
||||
smutils->LogError(myself, "Function \"%s\" doesn't have a \"signature\" nor \"address\" set: line: %i col: %i", g_CurrentFunctionName, states->line, states->col);
|
||||
return SMCResult_HaltFail;
|
||||
}
|
||||
|
||||
// Save this function signature in our cache.
|
||||
signatures_.insert(g_CurrentFunctionName.chars(), g_CurrentSignature);
|
||||
g_CurrentFunctionName = nullptr;
|
||||
g_CurrentSignature = nullptr;
|
||||
break;
|
||||
case PState_Arguments:
|
||||
g_ParseState = PState_Function;
|
||||
break;
|
||||
case PState_Argument:
|
||||
g_ParseState = PState_Arguments;
|
||||
|
||||
if (g_CurrentArgumentInfo.type == HookParamType_Unknown)
|
||||
{
|
||||
smutils->LogError(myself, "Missing argument type for argument \"%s\": line: %i col: %i", g_CurrentArgumentName.chars(), states->line, states->col);
|
||||
return SMCResult_HaltFail;
|
||||
}
|
||||
|
||||
// The size wasn't set in the config. See if that's fine and we can guess it from the type.
|
||||
if (!g_CurrentArgumentInfo.size)
|
||||
{
|
||||
if (g_CurrentArgumentInfo.type == HookParamType_Object)
|
||||
{
|
||||
smutils->LogError(myself, "Object param \"%s\" being set with no size: line: %i col: %i", g_CurrentArgumentName.chars(), states->line, states->col);
|
||||
return SMCResult_HaltFail;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_CurrentArgumentInfo.size = GetParamTypeSize(g_CurrentArgumentInfo.type);
|
||||
}
|
||||
}
|
||||
|
||||
if (g_CurrentArgumentInfo.pass_type == SourceHook::PassInfo::PassType::PassType_Unknown)
|
||||
g_CurrentArgumentInfo.pass_type = GetParamTypePassType(g_CurrentArgumentInfo.type);
|
||||
g_CurrentSignature->args.insert(g_CurrentArgumentName.chars(), g_CurrentArgumentInfo);
|
||||
|
||||
g_CurrentArgumentName = nullptr;
|
||||
break;
|
||||
}
|
||||
|
||||
return SMCResult_Continue;
|
||||
}
|
||||
|
||||
void SignatureGameConfig::ReadSMC_ParseStart()
|
||||
{
|
||||
g_ParseState = PState_Root;
|
||||
g_IgnoreLevel = 0;
|
||||
g_PlatformOnlyState = PState_None;
|
||||
g_CurrentSignature = nullptr;
|
||||
g_CurrentFunctionName = nullptr;
|
||||
g_CurrentArgumentName = nullptr;
|
||||
}
|
||||
|
||||
ReturnType SignatureGameConfig::GetReturnTypeFromString(const char *str)
|
||||
{
|
||||
if (!strcmp(str, "void"))
|
||||
return ReturnType_Void;
|
||||
else if (!strcmp(str, "int"))
|
||||
return ReturnType_Int;
|
||||
else if (!strcmp(str, "bool"))
|
||||
return ReturnType_Bool;
|
||||
else if (!strcmp(str, "float"))
|
||||
return ReturnType_Float;
|
||||
else if (!strcmp(str, "string"))
|
||||
return ReturnType_String;
|
||||
else if (!strcmp(str, "stringptr"))
|
||||
return ReturnType_StringPtr;
|
||||
else if (!strcmp(str, "charptr"))
|
||||
return ReturnType_CharPtr;
|
||||
else if (!strcmp(str, "vector"))
|
||||
return ReturnType_Vector;
|
||||
else if (!strcmp(str, "vectorptr"))
|
||||
return ReturnType_VectorPtr;
|
||||
else if (!strcmp(str, "entity"))
|
||||
return ReturnType_CBaseEntity;
|
||||
else if (!strcmp(str, "edict"))
|
||||
return ReturnType_Edict;
|
||||
|
||||
return ReturnType_Unknown;
|
||||
}
|
||||
|
||||
HookParamType SignatureGameConfig::GetHookParamTypeFromString(const char *str)
|
||||
{
|
||||
if (!strcmp(str, "int"))
|
||||
return HookParamType_Int;
|
||||
else if (!strcmp(str, "bool"))
|
||||
return HookParamType_Bool;
|
||||
else if (!strcmp(str, "float"))
|
||||
return HookParamType_Float;
|
||||
else if (!strcmp(str, "string"))
|
||||
return HookParamType_String;
|
||||
else if (!strcmp(str, "stringptr"))
|
||||
return HookParamType_StringPtr;
|
||||
else if (!strcmp(str, "charptr"))
|
||||
return HookParamType_CharPtr;
|
||||
else if (!strcmp(str, "vectorptr"))
|
||||
return HookParamType_VectorPtr;
|
||||
else if (!strcmp(str, "entity"))
|
||||
return HookParamType_CBaseEntity;
|
||||
else if (!strcmp(str, "objectptr"))
|
||||
return HookParamType_ObjectPtr;
|
||||
else if (!strcmp(str, "edict"))
|
||||
return HookParamType_Edict;
|
||||
else if (!strcmp(str, "object"))
|
||||
return HookParamType_Object;
|
||||
|
||||
return HookParamType_Unknown;
|
||||
}
|
||||
|
||||
Register_t SignatureGameConfig::GetCustomRegisterFromString(const char *str)
|
||||
{
|
||||
if (!strcmp(str, "al"))
|
||||
return AL;
|
||||
else if (!strcmp(str, "cl"))
|
||||
return CL;
|
||||
else if (!strcmp(str, "dl"))
|
||||
return DL;
|
||||
else if (!strcmp(str, "Bl"))
|
||||
return BL;
|
||||
else if (!strcmp(str, "ah"))
|
||||
return AH;
|
||||
else if (!strcmp(str, "ch"))
|
||||
return CH;
|
||||
else if (!strcmp(str, "dh"))
|
||||
return DH;
|
||||
else if (!strcmp(str, "bh"))
|
||||
return BH;
|
||||
|
||||
else if (!strcmp(str, "ax"))
|
||||
return AX;
|
||||
else if (!strcmp(str, "cx"))
|
||||
return CX;
|
||||
else if (!strcmp(str, "dx"))
|
||||
return DX;
|
||||
else if (!strcmp(str, "bx"))
|
||||
return BX;
|
||||
else if (!strcmp(str, "sp"))
|
||||
return SP;
|
||||
else if (!strcmp(str, "bp"))
|
||||
return BP;
|
||||
else if (!strcmp(str, "si"))
|
||||
return SI;
|
||||
else if (!strcmp(str, "di"))
|
||||
return DI;
|
||||
|
||||
else if (!strcmp(str, "eax"))
|
||||
return EAX;
|
||||
else if (!strcmp(str, "ECX"))
|
||||
return ECX;
|
||||
else if (!strcmp(str, "EDX"))
|
||||
return EDX;
|
||||
else if (!strcmp(str, "EBX"))
|
||||
return EBX;
|
||||
else if (!strcmp(str, "esp"))
|
||||
return ESP;
|
||||
else if (!strcmp(str, "ebp"))
|
||||
return EBP;
|
||||
else if (!strcmp(str, "esi"))
|
||||
return ESI;
|
||||
else if (!strcmp(str, "edi"))
|
||||
return EDI;
|
||||
|
||||
else if (!strcmp(str, "mm0"))
|
||||
return MM0;
|
||||
else if (!strcmp(str, "mm1"))
|
||||
return MM1;
|
||||
else if (!strcmp(str, "mm2"))
|
||||
return MM2;
|
||||
else if (!strcmp(str, "mm3"))
|
||||
return MM3;
|
||||
else if (!strcmp(str, "mm4"))
|
||||
return MM4;
|
||||
else if (!strcmp(str, "mm5"))
|
||||
return MM5;
|
||||
else if (!strcmp(str, "mm6"))
|
||||
return MM6;
|
||||
else if (!strcmp(str, "mm7"))
|
||||
return MM7;
|
||||
|
||||
else if (!strcmp(str, "xmm0"))
|
||||
return XMM0;
|
||||
else if (!strcmp(str, "xmm1"))
|
||||
return XMM1;
|
||||
else if (!strcmp(str, "xmm2"))
|
||||
return XMM2;
|
||||
else if (!strcmp(str, "xmm3"))
|
||||
return XMM3;
|
||||
else if (!strcmp(str, "xmm4"))
|
||||
return XMM4;
|
||||
else if (!strcmp(str, "xmm5"))
|
||||
return XMM5;
|
||||
else if (!strcmp(str, "xmm6"))
|
||||
return XMM6;
|
||||
else if (!strcmp(str, "xmm7"))
|
||||
return XMM7;
|
||||
|
||||
else if (!strcmp(str, "cs"))
|
||||
return CS;
|
||||
else if (!strcmp(str, "ss"))
|
||||
return SS;
|
||||
else if (!strcmp(str, "ds"))
|
||||
return DS;
|
||||
else if (!strcmp(str, "es"))
|
||||
return ES;
|
||||
else if (!strcmp(str, "fs"))
|
||||
return FS;
|
||||
else if (!strcmp(str, "gs"))
|
||||
return GS;
|
||||
else if (!strcmp(str, "si"))
|
||||
return SI;
|
||||
else if (!strcmp(str, "di"))
|
||||
return DI;
|
||||
|
||||
|
||||
else if (!strcmp(str, "st0"))
|
||||
return ST0;
|
||||
|
||||
return Register_t::None;
|
||||
}
|
41
signatures.h
Normal file
41
signatures.h
Normal file
@ -0,0 +1,41 @@
|
||||
#ifndef _INCLUDE_SIGNATURES_H_
|
||||
#define _INCLUDE_SIGNATURES_H_
|
||||
|
||||
#include "extension.h"
|
||||
#include "util.h"
|
||||
#include <am-string.h>
|
||||
#include <sm_stringhashmap.h>
|
||||
|
||||
class SignatureWrapper {
|
||||
public:
|
||||
ke::AString signature;
|
||||
ke::AString address;
|
||||
ke::AString offset;
|
||||
StringHashMap<ParamInfo> args;
|
||||
CallingConvention callConv;
|
||||
HookType hookType;
|
||||
ReturnType retType;
|
||||
ThisPointerType thisType;
|
||||
};
|
||||
|
||||
class SignatureGameConfig : public ITextListener_SMC {
|
||||
public:
|
||||
SignatureWrapper *GetFunctionSignature(const char *function);
|
||||
public:
|
||||
//ITextListener_SMC
|
||||
SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name);
|
||||
SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value);
|
||||
SMCResult ReadSMC_LeavingSection(const SMCStates *states);
|
||||
void ReadSMC_ParseStart();
|
||||
|
||||
private:
|
||||
ReturnType GetReturnTypeFromString(const char *str);
|
||||
HookParamType GetHookParamTypeFromString(const char *str);
|
||||
Register_t GetCustomRegisterFromString(const char *str);
|
||||
|
||||
private:
|
||||
StringHashMap<SignatureWrapper *> signatures_;
|
||||
};
|
||||
|
||||
extern SignatureGameConfig *g_pSignatures;
|
||||
#endif
|
77
sourcemod/gamedata/fbtools.games.txt
Normal file
77
sourcemod/gamedata/fbtools.games.txt
Normal file
@ -0,0 +1,77 @@
|
||||
"Games"
|
||||
{
|
||||
"cstrike"
|
||||
{
|
||||
"Signatures"
|
||||
{
|
||||
"Deafen"
|
||||
{
|
||||
"library" "server"
|
||||
"windows" "\x55\x8B\xEC\x83\xEC\x2A\x89\x4D\x2A\x8B\x45\x2A\x8B\x10\x8B\x4D\x2A\x8B\x82\x2A\x2A\x2A\x2A\xFF\xD0\x85\xC0\x74"
|
||||
"linux" "@_ZN9CCSPlayer6DeafenEf"
|
||||
}
|
||||
"CFlashbangProjectile::Detonate"
|
||||
{
|
||||
"library" "server"
|
||||
"windows" "\x53\x56\x8B\xF1\x57\x8B\x86\x2A\x2A\x2A\x2A\xC1\xE8\x2A\xA8\x2A\x74\x2A\xE8\x2A\x2A\x2A\x2A\xD9\x05\x2A\x2A\x2A\x2A\x6A\x40\x6A\x00\x51\x8B\xCE"
|
||||
"linux" "@_ZN20CFlashbangProjectile8DetonateEv"
|
||||
}
|
||||
"PercentageOfFlashForPlayer"
|
||||
{
|
||||
"library" "server"
|
||||
"windows" "\x53\x8B\xDC\x83\xEC\x08\x83\xE4\x2A\x83\xC4\x2A\x55\x8B\x6B\x2A\x89\x6C\x2A\x2A\x8B\xEC\x81\xEC\x2A\x2A\x2A\x2A\x56\x8B\x73\x08\x8D\x4D\xAC"
|
||||
"linux" "@_Z26PercentageOfFlashForPlayerP11CBaseEntity6VectorS0_"
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
"csgo"
|
||||
{
|
||||
"Signatures"
|
||||
{
|
||||
// "tr_player_flashbanged" is in RadiusFlash which calls CCSPlayer::Deafen and PercentageOfFlashForPlayer
|
||||
"Deafen"
|
||||
{
|
||||
"library" "server"
|
||||
"windows" "\x55\x8B\xEC\x83\xEC\x28\x56\x57\x8B\xF9\xF3\x0F\x11\x4D\xFC"
|
||||
"linux" "\x55\x89\xE5\x83\xEC\x48\x89\x7D\xFC\x8B\x7D\x08\x89\x5D\xF4\x89\x75\xF8\x8B\x07"
|
||||
}
|
||||
// "flashbang_detonate" and "Scorch"
|
||||
"CFlashbangProjectile::Detonate"
|
||||
{
|
||||
"library" "server"
|
||||
"windows" "\x55\x8B\xEC\x83\xEC\x6C\x53\x56\x57\x8B\xF1"
|
||||
"linux" "\x55\x89\xE5\x57\x56\x53\x81\xEC\x0C\x01\x00\x00\x8B\x5D\x08\x89\x1C\x24\xE8\x2A\x2A\x2A\x2A\x85\xC0\x89\xC6"
|
||||
}
|
||||
"PercentageOfFlashForPlayer"
|
||||
{
|
||||
"library" "server"
|
||||
"windows" "\x53\x8B\xDC\x83\xEC\x08\x83\xE4\xF0\x83\xC4\x04\x55\x8B\x6B\x04\x89\x6C\x24\x04\x8B\xEC\x81\xEC\x08\x01\x00\x00\x56\x8B\xF1"
|
||||
"linux" "\x55\x89\xE5\x57\x56\x53\x81\xEC\x9C\x01\x00\x00\x8B\x5D\x08\x8B\x03"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
"Functions"
|
||||
{
|
||||
"Deafen"
|
||||
{
|
||||
"signature" "Deafen"
|
||||
"callconv" "thiscall"
|
||||
"return" "void"
|
||||
"this" "entity"
|
||||
"arguments"
|
||||
{
|
||||
"distance"
|
||||
{
|
||||
"type" "float"
|
||||
"windows"
|
||||
{
|
||||
"register" "xmm1"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -33,14 +33,17 @@ public void OnPluginStart()
|
||||
PrintToServer("CFlashbangProjectile::Detonate detoured!");
|
||||
|
||||
|
||||
hFlashbangDeafen = DHookCreateDetour(Address_Null, CallConv_THISCALL, ReturnType_Void, ThisPointer_CBaseEntity);
|
||||
hFlashbangDeafen = DHookCreateFromConf(temp, "Deafen");
|
||||
if (!hFlashbangDeafen)
|
||||
SetFailState("Failed to setup detour for Deafen");
|
||||
/*hFlashbangDeafen = DHookCreateDetour(Address_Null, CallConv_THISCALL, ReturnType_Void, ThisPointer_CBaseEntity);
|
||||
if (!hFlashbangDeafen)
|
||||
SetFailState("Failed to setup detour for Deafen");
|
||||
|
||||
if (!DHookSetFromConf(hFlashbangDeafen, temp, SDKConf_Signature, "Deafen"))
|
||||
SetFailState("Failed to load Deafen signature from gamedata");
|
||||
|
||||
DHookAddParam(hFlashbangDeafen, HookParamType_Float, .custom_register=DHookRegister_XMM1);
|
||||
DHookAddParam(hFlashbangDeafen, HookParamType_Float, .custom_register=DHookRegister_XMM1);*/
|
||||
|
||||
if (!DHookEnableDetour(hFlashbangDeafen, false, Detour_OnDeafen))
|
||||
SetFailState("Failed to detour Deafen.");
|
||||
|
@ -219,7 +219,7 @@ native bool DHookRemoveEntityListener(ListenType type, ListenCB callback);
|
||||
* @param callback Callback function
|
||||
*
|
||||
* @return Returns setup handle for the hook.
|
||||
* @error Failed to create hook setup handle.
|
||||
* @error Failed to create hook setup handle or invalid callback function.
|
||||
*/
|
||||
native Handle DHookCreate(int offset, HookType hooktype, ReturnType returntype, ThisPointerType thistype, DHookCallback callback);
|
||||
|
||||
@ -237,6 +237,18 @@ native Handle DHookCreate(int offset, HookType hooktype, ReturnType returntype,
|
||||
*/
|
||||
native Handle DHookCreateDetour(Address funcaddr, CallingConvention callConv, ReturnType returntype, ThisPointerType thisType);
|
||||
|
||||
/**
|
||||
* Setup a detour or hook for a function as described in a "Functions" section in gamedata.
|
||||
*
|
||||
* @param gameconf GameConfig handle
|
||||
* @param name Name of the function in the gamedata to load.
|
||||
* @param callback Callback function (only required for virtual hooks - ignored for detours).
|
||||
*
|
||||
* @return Setup handle for the detour or INVALID_HANDLE if offset/signature/address wasn't found.
|
||||
* @error Failed to create detour setup handle, invalid gamedata handle, invalid callback function or failed to find function in gamedata.
|
||||
*/
|
||||
native Handle DHookCreateFromConf(Handle gameconf, const char[] name, DHookCallback callback=INVALID_FUNCTION);
|
||||
|
||||
/**
|
||||
* Load details for a vhook or detour from a gamedata file.
|
||||
*
|
||||
@ -246,7 +258,7 @@ native Handle DHookCreateDetour(Address funcaddr, CallingConvention callConv, Re
|
||||
* @param name Name of the property to find.
|
||||
*
|
||||
* @return True on success, false if nothing was found.
|
||||
* @error Invalid setup handle.
|
||||
* @error Invalid setup or gamedata handle.
|
||||
*/
|
||||
native bool DHookSetFromConf(Handle setup, Handle gameconf, SDKFuncConfSource source, const char[] name);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user