I think many people think that it is impossible to create a hook from a plugin without extension but in fact this is not the case and in this topic I will show you how to hook a normal function without using extension
Notes
This method is simply as information and is not as an alternative
It is very difficult to make such a hook and you need to understand ASM very well
If the hook will crash it is very difficult to understand what exactly is causing the crash which is also a minus
This topic uses Source Scramble but this hook can also be done without it
There is also a great replacement for Source Scramble that will save you from the first stage
Stages
You need to get the base addresses of server.dll and sourcemod.logic.dll
Get the plugin's callback address
Create a hook
Create an original function (optional)
Getting base addresses
There is already a topic on the forum about how to get a PEB so we will skip this part.
PEB stores modules in a ladder order, so we need to go through all the steps to find the necessary modules.
This is C++ code and we need to do the same via sourcemod.
// Count user modules : iterate through the entire list pEntry = pHeadEntry->Flink;
while (pEntry != pHeadEntry) { Count++; pEntry = pEntry->Flink; }
And we should get something like this (This is just an example and you can rewrite it as you want).
Spoiler
PHP Code:
Address EnumerateModules (Address pProcessEnviromentBlock) { /* Why 0x0C and 0x1C? */ /* This is an offset that can be calculated through the structures I showed above in the C++ code */
Address pStart = ADDRESS(LoadFromAddress(InInitilizationOrderModuleList, NumberType_Int32)); /* Getting first module */ Address pModule = ADDRESS(LoadFromAddress(pStart, NumberType_Int32)); /* Getting next module */
while (LoadFromAddress(pModule, NumberType_Int32) != INT(pStart)) /* Iteration until we reach first module */ { pModule = ADDRESS(LoadFromAddress(pModule, NumberType_Int32)); }
return Address_Null; }
Now we need to get the module name and its address but the problem is that the module names are stored in Unicode format so I created a function that allows reading Unicode but it's not perfect
Spoiler
Code
PHP Code:
while (LoadFromAddress(pModule, NumberType_Int32) != INT(pStart)) /* Iteration until we reach first module */ { char szName[36]; ReadUnicode(ADDRESS(LoadFromAddress(pModule + ADDRESS(0x20), NumberType_Int32)), szName, sizeof szName);
It remains to add StrEqual and save the found modules so we go to the next stage
Getting callback address
Now We need to create a callback through which you can get another callback that will already be set to Hook
You can use any sourcemod function to do this
I chose SortFloats
The offset of this function is 33C0
Spoiler
To pass an argument to callback you need to write sending the argument and calling the function manually using ASM
I have already written a ready made ASM that should work on any sourcemod function
/* Restore original bytes of SortFloats function*/
for (int i; i < sizeof g_iContext; i++) StoreToAddress(pFunc + ADDRESS(i), iSaved[i], NumberType_Int8); }
void CreateHandleCallback (Address IPluginContext) { /* Here we got the context of our plugin as an argument */ /* Now You need to create an SDKCall using a context */
StartPrepSDKCall(SDKCall_Raw);
/* 136 is offset to GetFunctionById which is located in virtual method table */ /* https://www.sourcemod.net/sp-dox/class_source_pawn_1_1_i_plugin_context.html */
I'll add that I'm using the UTIL_SetModel function as an example
Creating hook
Now the callback has already been found it remains to create a Hook
Our hook is that the UTIL_SetModel function will create a call to our plugin's function and pass parameters to it
Spoiler
Code
PHP Code:
public void UTIL_SetModel (Address CBaseEntity, Address name) { /* just output something to make sure the hook works */ PrintToServer("CBaseEntity %X name %X", CBaseEntity, name); }
Address pServerBase = ADDRESS(LoadFromAddress(g_pServer + ADDRESS(8), NumberType_Int32)); /* Getting server base address */ Address pFunc = pServerBase + ADDRESS(0x207490); /* adding base address to offset of UTIL_SetModel function */
/* Patch the function so that it calls OUR Callback and passes parameters to it */
for (int i; i < sizeof iCall; i++) StoreToAddress(pFunc + ADDRESS(i), iCall[i], NumberType_Int8);
As you can see our hook works but the original code is not called which means the UTIL_SetModel function just outputs a message so you need to create an SDK that will call the original code
In General there are two ways to solve this problem the first is to create the SDK and the second is to rewrite the meaning of the original function in our hook
I will follow the path of creating the SDK
Creating an original code call via the SDK is not universal and is not suitable for every function
The best way for me is to create a jump through a relative address
MemoryBlock pMemory = new MemoryBlock (sizeof iCall + 4 + size); /* size of OUR patch + JMP + size of original bytes */ Address pAddr = pMemory.Address;
Address pServerBase = ADDRESS(LoadFromAddress(g_pServer + ADDRESS(8), NumberType_Int32)); /* Getting server base address */ Address pFunc = pServerBase + ADDRESS(0x207490); /* adding base address to offset of UTIL_SetModel function */
/* just nopping all the place where OUR jump will be located */
for (int i; i < size; i++) { bytes[i] = LoadFromAddress(pFunc + ADDRESS(i), NumberType_Int8); StoreToAddress(pFunc + ADDRESS(i), 0x90, NumberType_Int8); }
/* Creating a function that will call our callback */
for (int i; i < sizeof iCall; i++) StoreToAddress(pAddr + ADDRESS(i), iCall[i], NumberType_Int8);
/* Inserting the jump that jumps from UTIL_SetModel into the function that will call OUR callback */ StoreToAddress(pFunc, 0xE9, NumberType_Int8); StoreToAddress(pFunc + ADDRESS(1), INT(relative), NumberType_Int32);
/* Inserting the jump that jumps from the function that will call OUR callback into (UTIL_SetModel + 8) */ for (int i; i < size; i++) StoreToAddress(pAddr + ADDRESS(sizeof iCall + i), bytes[i], NumberType_Int8);
/* Creating OUR SDK which will call original code */ StartPrepSDKCall(SDKCall_Static); PrepSDKCall_SetAddress(pAddr + ADDRESS(sizeof iCall)); PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain); PrepSDKCall_AddParameter(SDKType_String, SDKPass_Pointer); g_hOriginal = EndPrepSDKCall(); }
Result
Ending...
I repeat that this is not an alternative
All this can be done without Source Scramble but without it you will have to use gamedata and search for dummy/unused functions/memory