Thread: help with okapi
View Single Post
HamletEagle
AMX Mod X Plugin Approver
Join Date: Sep 2013
Location: Romania
Old 06-20-2017 , 05:44   Re: help with okapi
Reply With Quote #8

Please note that okapi has a memory leak, somewhere. I noticed it only when hooking virtual functions under linux. Until Arkshine decides to fix the module, you can only hope that you don't do something that will cause the crash.
Also treemap will be removed, I remember Arkshine saying that they are not as reliable as we thought at the beginning.

Now, about your question:
To find the function you need it's symbol for linux and a signature of bytes for windows for most functions. But, BuyTouch function is exported, this means that in windows it has a name, it's not called sub_*****.
So, for this kind of functions a signature is not needed, you can still use a symbol, as for linux.

1.On linux function is easy to find. Open cs.so with IDA and search for it's name.
The right symbol is:
Code:
_ZN8CBuyZone8BuyTouchEP11CBaseEntity
2.For exported functions, we do the same step as for linux on windows. Load mp.dll in IDA.
You will find this symbol:
Code:
?BuyTouch@CBuyZone@@QAEXPAVCBaseEntity@@@Z
If the function was not exported, you could not use the symbol and the signature of bytes was needed.
Just for the sake of it, I'll show you how to make a signature for this function. I don't know from where you got that signature, but it's wrong.

Load mp.dll in IDA(if you didn't already), go to Option -> general and in the right panel, at "Number of opcode bytes" put 10. Go back to IDA View - A, you should see something like this:
Code:
.text:100C0AC0 56                                            push    esi
.text:100C0AC1 8B 74 24 08                                   mov     esi, [esp+4+arg_0]
.text:100C0AC5 57                                            push    edi
.text:100C0AC6 8B F9                                         mov     edi, ecx
.text:100C0AC8 8B 06                                         mov     eax, [esi]
.text:100C0ACA 8B CE                                         mov     ecx, esi
.text:100C0ACC FF 90 A0 00 00 00                             call    dword ptr [eax+0A0h]
.text:100C0AD2 85 C0                                         test    eax, eax
.text:100C0AD4 74 23                                         jz      short loc_100C0AF9
.text:100C0AD6 8B 4F 04                                      mov     ecx, [edi+4]
.text:100C0AD9 8B 81 AC 01 00 00                             mov     eax, [ecx+1ACh]
.text:100C0ADF 85 C0                                         test    eax, eax
.text:100C0AE1 74 08                                         jz      short loc_100C0AEB
.text:100C0AE3 3B 86 C8 01 00 00                             cmp     eax, [esi+1C8h]
.text:100C0AE9 75 0E                                         jnz     short loc_100C0AF9
What we need are the numbers after text:something. In order to make a signature as reliable as possible, you need to keep only first byte from each line and replace the others with ?

So, for example: 8B 74 24 08 become 8B ? ? ?
Let's do this:
PHP Code:
56 8B ? ? ? 57 8B 8B 8B 
(the lines that have only one byte remains unchanged - for example the first one). I went for a random number of bytes. Let's check if the signature is unique, i.e it points to only one function.
In IDA go to search -> sequence of bytes, paste and search. You'll get something like:
Code:
Address        Function                                              Instruction
-------        --------                                              -----------
.text:10025970 sub_10025970                                          push    esi
.text:10033BF0 sub_10033BF0                                          push    esi
.text:100AAEF2 sub_100AAEF0                                          push    esi
.text:100C0AC0 ?BuyTouch@CBuyZone@@QAEXPAVCBaseEntity@@@Z            push    esi
.text:100C0C10 ?BombTargetTouch@CBombTarget@@QAEXPAVCBaseEntity@@@Z  push    esi
.text:100C1670 ?GravityTouch@CTriggerGravity@@QAEXPAVCBaseEntity@@@Z push    esi
.text:100CDD10 ?DefaultTouch@CBasePlayerItem@@QAEXPAVCBaseEntity@@@Z push    esi
.text:100D0892                                                       push    esi
.text:100D0A00 ?ArmouryTouch@CArmoury@@QAEXPAVCBaseEntity@@@Z        push    esi
This means that the signature is not good enough, it points to more functions. We need to add more bytes.
PHP Code:
56 8B ? ? ? 57 8B 8B 8B  FF ? ? ? ? ? 85 74 8B ? ? 8B ? ? ? ? ? 85 74 
Basically, start with some bytes, then keep adding until the search returns only your function. Now, we need to convert this signature so we can use it with okapi:
Code:
0x56,0x8B,0xDFF,0xDFF,0xDFF,0x57,0x8B,0xDFF,0x8B,0xDFF,0x8B,,0xDFF,0xFF,0xDFF,0xDFF,0xDFF,0xDFF,0xDFF,0x85,0xDFF,0x74,0xDFF,0x8B,0xDFF,0xDFF,0x8B,0xDFF,0xDFF,0xDFF,0xDFF,0xDFF,0x85,0xDFF,0x74
Add 0x in front of each byte and replace any ? with 0xDFF. Add , before each of them.

Now, we found the function, let's create a plugin for hooking it.
PHP Code:
#include <amxmodx>
#include <okapi>

public plugin_init()
{
    new const 
BuyTouchLinuxSymbol  [] = "?BuyTouch@CBuyZone@@QAEXPAVCBaseEntity@@@Z"
    
new const BuyTouchWindowsSymbol[] = "_ZN8CBuyZone8BuyTouchEP11CBaseEntity"
    
    
new HandleFuncBuyTouch
    
if
    ( 
        (
HandleFuncBuyTouch okapi_mod_get_symbol_ptr(BuyTouchLinuxSymbol)) || 
        (
HandleFuncBuyTouch okapi_mod_get_symbol_ptr(BuyTouchWindowsSymbol))
    ) 
    { 
        
okapi_add_hook(okapi_build_method(HandleFuncBuyToucharg_voidarg_cbasearg_cbase), "OnBuyTouch", .post 1
    } 
}

public 
OnBuyTouch(const entity, const id)
{
    
client_print(0print_chat"Debug = %i %i"entityid)

I'll explain again why I did it like that.
In linux function have names, so we can identify them by a symbol. In windows, very few functions have names, the others are called sub_****. In this case, the BuyTouch is part of the few function, so a signature is not needed, we can do it easily with a symbol, same as for linux.

Now, next thing that you'll see is that I used okapi_build_method instead of okapi_build_function. okapi is a bit harder to use than orpheu due to that. okapi_build_function should be used for function with no class(i.e no CSomething::FunctionName, they are called only FunctionName, for example InstallGameRules).
okapi_build_method is used for functions that are part of a class.
Let's look here: https://github.com/s1lentq/ReGameDLL...gers.cpp#L1748

Code:
void CBuyZone::BuyTouch(CBaseEntity *pOther)
You see that function is part of CBuyZone class, so we need to use okapi_build_method for it.

The first argument is the address, which we retrieved from the symbol. The second argument is the return value of the function. Since it's "void", we use arg_void. The third argument is the type of the class, since it's CBuyZone and we know a buyzone is an entity, we can safely say it's the same as CBaseEntity, which in okapi is represented by an arg_cbase.
The next parameters for okapi_build_method are the parameters of the function that you need to hook. Look again at the link, the param is CBaseEntity *pOther, so again arg_cbase.

I said that okapi is harder to use because orpheu would have added internally for you the class param, based on the file that you provide. So the first arg_cbase is not needed while working with orpheu, it does that automatically.

But, again, for the sake of it, let's assume that this function would not have a name, so we have to hook it from a signature.

PHP Code:
#include <amxmodx>
#include <okapi>

public plugin_init()
{
    new const 
BuyTouchLinuxSymbol     [] = "?BuyTouch@CBuyZone@@QAEXPAVCBaseEntity@@@Z"
    
new const BuyTouchWindowsSignature[] = {0x560x8B0xDFF0xDFF,0xDFF,0x57,0x8B,0xDFF,0x8B,0xDFF,0x8B,0xDFF,0xFF,0xDFF,0xDFF,0xDFF,0xDFF,0xDFF,0x85,0xDFF,0x74,0xDFF,0x8B,0xDFF,0xDFF,0x8B,0xDFF,0xDFF,0xDFF,0xDFF,0xDFF,0x85,0xDFF,0x74 }
    
    new 
HandleFuncBuyTouch
    
if
    ( 
        (
HandleFuncBuyTouch okapi_mod_get_symbol_ptr(BuyTouchLinuxSymbol)) || 
        (
HandleFuncBuyTouch okapi_mod_find_sig(BuyTouchWindowsSignaturesizeof BuyTouchWindowsSignature))
    ) 
    { 
        
okapi_add_hook(okapi_build_method(HandleFuncBuyToucharg_voidarg_cbasearg_cbase), "OnBuyTouch", .post 1
    } 
}

public 
OnBuyTouch(const entity, const id)
{
    
client_print(0print_chat"Debug = %i %i"entityid)

Output is the same, but why making our life harder by creating the signature?

Ask anything that's not clear.
__________________

Last edited by HamletEagle; 06-20-2017 at 06:40.
HamletEagle is offline