This site is a testing version, but all data is shared with the live forum.


Raised This Month: $ Target: $400
 0% 

[TUT] Most Efficient Function Hooking Method


Post New Thread Reply   
 
Thread Tools Display Modes
Author Message
jim_yang
Veteran Member
Join Date: Aug 2006
Old 09-04-2010 , 04:35   [TUT] Most Efficient Function Hooking Method
Reply With Quote #1

Introduction:
This artical is for advanced coders who know the C++ and some basic asm knowledge, and want to make the most efficient plugins ever based on metamod plugin or amxx module plugin.
Basiclly, memory hack again.

Warning:
This method need some asm/disasm knowledge, try it at your own risk. May crash game lots of times if you don't know what you are doing !

Background:
So let's begin with triditional function hooking method, example below:
Code:
void fooA()
{
    printf("Hello world!");
}
 
void fooB()
{
    fooA();
}
 
void fooC()
{
    fooA();
}
In function fooB, fooA() is called, so how to hook fooA(), the method is:
Code:
void PreHook()
{
    do some operation to identify this call is from fooB(), not fooC()
    do else
    return Handled or Ignored
}
 
void PostHook()
{
    do some operation to identify this call is from fooB(), not fooC()
    do else
}
 
void fooA_hook()
{
    if( PreHook() == Handled )
        return;
    fooA();
    PostHook();
}
Well, above is the triditional function hooking procedure, the PreHook is at the beginning of the function, if its return value is "Handled", exit the function. Then call the original fooA(), then call PostHook()
This hooking method is not efficient, because you need to filter other situations to make sure which function called your hooked function, especially the function passed some parameters. There are a lot of actual examples, like:
Hook log event, Round_Start, your way (actually amxx) is to hook
Code:
void AlertMessage(ALERT_TYPE atype, char *szFmt, ...)
And parse the string, check it if contains "Round_Start".
But there are a lot of functions which use this function "UTIL_LogPrintf" to log something. It means that, once you hooked the function, it will do the check operation when those functions call it.
The "UTIL_LogPrintf" is not called very often, it's ok. But what about hooking functions like Emit_Sound, Set_Model, they are called very often even within a second. And you need to compare lots of stirngs.

New Method: Redirect a function from where it called
Continue with the code sample above:
fooB() and fooC() both called fooA(), and we only need to hook fooA() when fooB() called, so we can redirect the function call in fooB()
Code:
void MyHookFunction()
{
}
 
void fooB()
{
    MyHookFunction(); //change fooA() to MyHookFunction()
}
You will see that only fooA() in fooB() is redirected, not affect fooC()

How To: (asm part begins)
I will take an actual example to show how to do the hook, so let's hook the
Code:
UTIL_LogPrintf("World triggered \"Round_Start\"\n");
this function is in CCSGameRules::Think()
Search string "Round_Start", you will find this part
Code:
.text:10097E47                 push    offset aWorldTrigger_2 ; "World triggered \"Round_Start\"\n"
.text:10097E4C                 call    sub_100CFCD0
All you need to do is just change
Code:
call    sub_100CFCD0
to
Code:
call    sub_MyHookFunction
So how to change ?
Code:
binary code of UTIL_LogPrintf("World triggered \"Round_Start\"\n");
.text:10097E47  68 DC FE 11 10 -> push 1011FEDC
.text:10097E4C  E8 7F 7E 03 00 -> call 00037E7F
You will see that the "call 00037E7F" is not the actuall function addr "call sub_100CFCD0", why ?
Because "call" instruction uses relative address in this situation.
Code:
"call 00037E7F" is at address 10097E4C
so the target function' address = 10097E4C + 00037E7F + 5 = sub_100CFCD0
5 is the size of "E8 7F 7E 03 00", 5 bytes.
So the formula is:
Code:
MyHookFunction_Call_Offset = MyHookFunction's addr - Here - sizeof(DWORD);
"Here" is the address where you want to redirect the call
Code:
.text:10097E4C                 call    sub_100CFCD0
                                       "Here" = 10097E4C + 1; //call inc take up 1 byte
Final:
That's all, really simple, right ? Part of C++ code below:
Code:
template <typename P> static void patch_mem_data(const DWORD realaddr, P value)
{
    void *addr = (void *)realaddr;
    DWORD oldflags = NULL;
    if(VirtualProtect(addr, sizeof(P), PAGE_EXECUTE_READWRITE, &oldflags))
    {
        *(P *)addr = value;
        DWORD tmpflags;
        if(oldflags == PAGE_EXECUTE_READWRITE || VirtualProtect(addr, sizeof(P), oldflags, &tmpflags))
        {
            UTIL_SERVER_PRINT("[CSDM] Mem Patch Succeed At: 0x%08X\n", realaddr);
            return;
        }
    }
    LOG_ERROR(PLID, "[CSDM] patch_mem::VirtualProtect failed. Address: 0x%08X\n", realaddr);
    return;
}
 
static void write_func(void *func, DWORD addr)
{
    patch_mem_data<DWORD>(addr, (DWORD)func - addr - sizeof(DWORD));
}
 
void CSDM_RoundStart(const char *szMsg)
{
    SERVER_PRINT("our round start hooked ! \n");
}
 
void Meta_Attach()
{
    //hook Round_Start
    write_func(CSDM_RoundStart, GETREALADDR(0x97E4D));
}
More:
There is also a direct address call format in "call" instruction, it open appreas when engine function is called, such as:
Code:
call    dword_101624A8   FF 15 A8 24 16 10
You can change "FF 15" to "E8", and use relative address then.

Final after Final:
Most important part is shown above, you can implement this to what you like.
Actually with this hooking method, you don't even need to use some system from amxx, like:
event system,
log event system,
message system,
...
And your module will become the most efficient one you ever made !
__________________
Project : CSDM all in one - 99%
<team balancer#no round end#entity remover#quake sounds#fake full#maps management menu#players punishment menu#no team flash#colored flashbang#grenade trails#HE effect#spawn protection#weapon arena#weapon upgrade#auto join#no weapon drop#one name>
jim_yang is offline
jim_yang
Veteran Member
Join Date: Aug 2006
Old 09-04-2010 , 04:36   Re: [TUT] Most Efficient Function Hooking Method
Reply With Quote #2

A example that using this method to hook when a player timed out or dropped while he's trying to connect to server
console print like this:
Code:
PlayerA timed out
PlayerB dropped
here is the function in swds.dll
sub_1D4EFF0("%s timed out\n", _ESI + 19720);
Code:
.text:01D9B036                 push    eax             ; Args
.text:01D9B037                 push    offset aSTimedOut ; "%s timed out\n"
.text:01D9B03C                 call    sub_1D4EFF0
the imagebase of swds.dll is
Code:
.text:01D01000 ; Imagebase   : 1D00000
so offset where need to patch is
Code:
01D9B03C + 1 - 1D00000 = 9B03D
so final code
Code:
void C_Client_TimedOut(const char *szMsg, const char *szName)
{
    here player timed out hooked
}
 
void C_Client_Dropped(const char *szMsg, const char *szName)
{
    here player dropped hooked
}
 
void Meta_Attach()
{
    //hook Client timed out
    write_func(C_Client_TimedOut, GETREALADDR(0x9B03D));
    //hook Client dropped
    write_func(C_Client_Dropped, GETREALADDR(0x9FF94));
}
Hint:
GETREALADDR is a macro I used,
gamemod = mpbase + offset
serverdll = swdsbase + offset
or you can do signature scan to get the real addr
"write_func" can found above
__________________
Project : CSDM all in one - 99%
<team balancer#no round end#entity remover#quake sounds#fake full#maps management menu#players punishment menu#no team flash#colored flashbang#grenade trails#HE effect#spawn protection#weapon arena#weapon upgrade#auto join#no weapon drop#one name>

Last edited by jim_yang; 09-08-2010 at 09:27.
jim_yang is offline
joaquimandrade
Veteran Member
Join Date: Dec 2008
Location: Portugal
Old 09-04-2010 , 05:12   Re: [TUT] Most Efficient Function Hooking Method
Reply With Quote #3

Let me just add a note:

Normally, what is done to hook a function is to hook the function itself. What jim is talking about here is to hook calls to that function. The differences are:

Hooking function:
You hook all the calls to that function.
It's harder and less efficient because you have to replace code that doesn't have always the same size.

Hooking function calls.
You hook one call only (by one call I mean one place in the code where the function is called).
It's easier and more efficient because you have to replace a call with other call (that have the same size)

About this of replacing code, I'm talking about assembly code.
About the efficiency, is not something really big but there is a difference.

Image example:

[IMG]http://img830.**************/img830/8785/jimf.png[/IMG]
__________________
joaquimandrade is offline
Seta00
The Seta00 user has crashed.
Join Date: Jan 2010
Location: Berlin
Old 09-04-2010 , 18:21   Re: [TUT] Most Efficient Function Hooking Method
Reply With Quote #4

what's the dysentery id?
Seta00 is offline
Javivi
AlliedModders Donor
Join Date: Dec 2008
Old 09-06-2010 , 17:48   Re: [TUT] Most Efficient Function Hooking Method
Reply With Quote #5

Nice, jim'.
__________________
Javivi is offline
Arkshine
AMX Mod X Plugin Approver
Join Date: Oct 2005
Old 09-06-2010 , 17:55   Re: [TUT] Most Efficient Function Hooking Method
Reply With Quote #6

Interesting stuff, I like when you post such things, jim_yang !
__________________
Arkshine is offline
ot_207
Veteran Member
Join Date: Jan 2008
Location: Romania The Love Country
Old 09-06-2010 , 18:05   Re: [TUT] Most Efficient Function Hooking Method
Reply With Quote #7

Quote:
Originally Posted by Arkshine View Post
Interesting stuff, I like when you post such things, jim_yang !
Agreed.
__________________
My approved plug-ins | Good for newbies! | Problems?

Back, will come around when I have time.
ot_207 is offline
jim_yang
Veteran Member
Join Date: Aug 2006
Old 09-08-2010 , 09:15   Re: [TUT] Most Efficient Function Hooking Method
Reply With Quote #8

thank you guys !
joaq: nice picture
__________________
Project : CSDM all in one - 99%
<team balancer#no round end#entity remover#quake sounds#fake full#maps management menu#players punishment menu#no team flash#colored flashbang#grenade trails#HE effect#spawn protection#weapon arena#weapon upgrade#auto join#no weapon drop#one name>
jim_yang is offline
jim_yang
Veteran Member
Join Date: Aug 2006
Old 09-08-2010 , 09:41   Re: [TUT] Most Efficient Function Hooking Method
Reply With Quote #9

A example that hooking
"cvar" changed to "value"
dllbase is swds's entry
Code:
void C_Server_CvarChanged(const char *szMsg, const char *szCvarName, const char *szValue)
{
}
 
void Meta_Attach()
{
    //hook server cvar changed
    write_func(C_Server_CvarChanged, GETREALADDR(0x3A336));
}
all you need is to find the address that "call" is at and plus 1
__________________
Project : CSDM all in one - 99%
<team balancer#no round end#entity remover#quake sounds#fake full#maps management menu#players punishment menu#no team flash#colored flashbang#grenade trails#HE effect#spawn protection#weapon arena#weapon upgrade#auto join#no weapon drop#one name>

Last edited by jim_yang; 09-17-2010 at 05:19.
jim_yang is offline
Immortal_BLG
Member
Join Date: Feb 2010
Location: RUSSIA
Old 09-08-2010 , 20:19   Re: [TUT] Most Efficient Function Hooking Method
Reply With Quote #10

Also need to get the original function address before patching. For calling from the redirected function when needed - for normal process work.
Code:
static void write_func(void *func, DWORD addr, DWORD &orgFn)
{
    // First: Save the original function address....
    orgFn = *reinterpret_cast <DWORD *> (addr) + addr + sizeof (void *);

    // Second: Patch this address....
    patch_mem_data<DWORD>(addr, (DWORD)func - addr - sizeof(DWORD));
}

Last edited by Immortal_BLG; 09-08-2010 at 20:21.
Immortal_BLG is offline
Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -4. The time now is 20:13.


Powered by vBulletin®
Copyright ©2000 - 2024, vBulletin Solutions, Inc.
Theme made by Freecode