Do your own SA:MP command's.

Many of you probably C+P'ed the "addClientCommand" out of mod_sa, it's easy to use and has one annoying thing to it.. It is the internal SA:MP limit of commands, you can actually bypass this pretty easy and do your own "command handler" which will also classify you as !337 l33t hax0r.

First you need to know how SA:MP handles commands, basically if the input text of the editcontrol on the chatgui contains a "/" it scans through the internal list of commands and if it can't find one it'll send the request to the server.
This is where we come in, it's actually pretty easy to go between the call and do your own magic.

Today I will present you how, you should have a little experience tho I won't bother explaining everything here.

Step one - acquiring offsets)


First we need to locate the call we want to interfere SA:MP makes it easy as there's an "I don't know this command" text-string in SA:MP.

Now is the time where you should open OllyDBG (in my case) or any other program you're familiar with which is able to search for string references and can jump to the reference.
I analyzed the samp module, and jumped right to the text references window.

I searched the text, and jumped right to the first reference:
[img=375x276]http://i.xyin.ws/i/9019A5.png[/img]

Now we got the location where SA:MP outputs the message if it can't find a command (normally you see this only on DEBUG MODE).
But we're interested in the place where the server receives the command, and it's not far away believe me it's right above! It's this call:

2715B1.png


We can add a breakpoint there and type any command to confirm this.
When the breakpoint triggers, you should look at the register set and UHH?? what we got there the command we typed:

1F2C44.png

If we investigate further we can take some assumptions on the first 3 instructions:

D9BF98.png


The first one pushes the parameter on the stack, line two is.. well let's simply say it's our class pointer, and line 3 invokes our function.
If we take the address of line 3 minus the base address we have the relative address of the function and now have our first offset it is: 0x65DF8.
Going further in the function at 046E5C60(relative: 0x65C60) we talked about (we name it SendCommandToServer now) allows us to find out which function type it actually is (even if the instructions imply it's a class function).
/* add explanation  part here. */


Things we know now:
We got a call at (+0x65DF8) to patch so it calls our own function.
We know our functions "SendCommandToServer" location it is at: 0x65C60.

Step two - writing the code)


Now we got the information we need, nice. At this point I assume you know how to get the Module Address of the samp.dll and know basic C++.

First we need to construct a function body that gives the same conditions as the original "SendCommandToServer" so we can just redirect the call to our function.

Our investigations before lead us to the point that it is a class function, has a return of int and uses the default stdcall calling convention (callee must clean the stack and the return value is stored in eax who had guess that?).
As stdcall is a default Win32 API convention we can just proceed by defining our function body as our first and only parameter is a char* it's simple.

The function will look like this (the parameter is 'const' as we don't want to mess with it):
[shcode=cpp]
int __stdcall SendCommandToServer(const char *szCmd)
{
    return 1;
}
[/shcode]

Now, with the function alone we can't add our command what to-do now?
As I told you already we need to redirect the call to our own address, so we it calls our function and not the SA:MP one this is relatively easy for anyone who is familiar with memory editing but not for people w/o experience. On this part I'll provide more C+P in the hope you'll understand this.

The first thing I do every time is: helper functions HELPER FUNCTIONS! you'll love them.
We need to copy a bit of memory, so we need to mess with protecting memory areas ain't nobody got time for that.

C+P for you:
[shcode=cpp]
void SetData(void* pAddress, void* pData, size_t size)
{
    DWORD dwOldProtect; // to keep the old perms
    VirtualProtect(pAddress, size, PAGE_EXECUTE_READWRITE, &dwOldProtect); // make it read/write and copy old protections to our temporary var
    memcpy(pAddress, pData, size); // set
    VirtualProtect(pAddress, size, dwOldProtect, NULL); // reset protections
}
[/shcode]

Now if we know SA:MP is ready and got the SA:MP base (let's say we store the SA:MP base address in a DWORD named dwSAMP).
We can continue to patch the locations we want, from step numbrê uno we know the call we need to redirect is located at: 0x65DF8

Now calculate the address in memory by simply adding and it'll look like this:
[shcode=cpp]
DWORD dwPatchLoc = dwSAMP + 0x65DF8; // ;call SendCommandToServer (this is our original call location)
[/shcode]

Now we need to somehow put our address in the call instruction, for this I'll show you a hex dump of the call instruction:
"E8 63 FE FF FF"

The blue marked "E8" is the opcode for "CALL" on x86, therefore we know the rest is the relative function address.
We want to keep the E8, so we should not write directly at dwPatchLoc, therefore we move 1 byte further we'll do this on our SetData function.
Good know, we know this. What about the RELATIVE ADDRESS? well, we can get the address of the function in our code easily now we basically need to subtract the location of dwPatchLoc+5 why +5? because we want the end and the instruction is 5 bytes long and I'm sick of explanations okay?

We calculate and patch the address with the following code:
[shcode=cpp]
DWORD dwRelativeAddr = ((DWORD)SendCommandToServer) - (dwPatchLoc + 5); // calculate the relative address
SetData((void*)(dwPatchLoc + 1), &dwRelativeAddr, 4); // set our relative address (remember +1 there to skip the call opcode!)
[/shcode]

aaand... we use the size of 4? Guess why we need to patch 4 bytes. As we use memcpy and copy from one place to another we need our dwRelativeAddr buffer.

The full code of this procedure should look like that:
[shcode=cpp]
DWORD dwPatchLoc = dwSAMP + 0x65DF8; // ;call SendCommandToServer (this is our original call location)
DWORD dwRelativeAddr = ((DWORD)SendCommandToServer) - (dwPatchLoc + 5); // calculate the relative address
SetData((void*)(dwPatchLoc + 1), &dwRelativeAddr, 4); // set our relative address (remember +1 there to skip the call opcode!)
[/shcode]

Step three - Are we done now?)


We're done, ok no we're not you should remember: Even if we are now able to catch commands, and make our own handler we still do not call the original function this means: The server will receive no command none, nada, finito. How to solve that? Well we fetched the offset of the original before we just need to invoke it if we want.

Or if you want to be isolated from the server then you're done. Yes you're done just leave.
If you decide to finish your work amazing, you probably understood a bit!

We proceed now with "how to call the original function".
First.. We need to store the address of the original function in a global accessible buffer, sounds hard but just declare a DWORD type variable at the global scope.
Now we set this variable (I call it "origSendCommandToServer") to the function address by simply doing:
[shcode=cpp]origSendCommandToServer = dwSAMP + 0x65C60[/shcode] before your patching code.

Now we have the address of the original function in our buffer, as we do not care about the return of the function as SA:MP doesn't even use it internally we can easily invoke the call with inline assembly and return 1; on our function as we did before.

I won't explain the inline assembly here, but it's basically replicating what you seen before ECX is still the class pointer so we don't need to put that back but we "fetched" from the stack therefore we need to put EBX back and call the function.
If we add these 2 instructions, then we call the original function again our shiny new SendCommandToServer function on our end looks now like this:
[shcode=cpp]
int __stdcall SendCommandToServer(const char *szCmd)
{
    /* push the parameter back to the stack and call original function  */
    __asm push ebx
    __asm call [origSendCommandToServer]
    return 1;
}
[/shcode]
now it also sends the command to the server now we should be proud of our self especially if you understood what I wrote.



Step four - Final thoughts)


Now, you probably already did a simple comparison of the szCmd buffer and found out the server receives your super secret commands!
If you handled the command you can just return out of it before we re-call the original function with inline-assembly.

This means, if we want to add the command "myawesomecommand" it should look like this:
[shcode=cpp]
int __stdcall SendCommandToServer(const char *szCmd)
{
    // now we can be happy *yeahy*
    // you should probably now do your own command handler.
    // allowing you to add function addresses so you can do it as simple as the internal addChatCommand.

    if (!strcmp(szCmd, "/myawesomecommand"))
    {
        MessageBox(NULL, "Yeah you typed my command!", "Hello!", MB_OK);
        return 1; // return 1 here so we cancel the server call (to the original function).
    }

    /* push the parameter back to the stack and call original function  */
    __asm push ebx
    __asm call [origSendCommandToServer]
    return 1;
}
[/shcode]

In my opinion this way is much better, as you don't need to obey the clients limits.
There are also other places to hook or replace allowing you to fetch normal chat input also, but this one looked the simplest for you guys but it does the job just fine.

Here's also the full code dump if you got confused:
[shcode=cpp]
void SetData(void* pAddress, void* pData, size_t size)
{
    DWORD dwOldProtect;
    VirtualProtect(pAddress, size, PAGE_EXECUTE_READWRITE, &dwOldProtect);
    memcpy(pAddress, pData, size);
    VirtualProtect(pAddress, size, dwOldProtect, NULL);
}

DWORD origSendCommandToServer = NULL;
int __stdcall SendCommandToServer(const char *szCmd)
{
    // now we can be happy *yeahy*
    // you should probably now do your own command handler.
    // allowing you to add function addresses so you can do it as simple as the internal addChatCommand.

    if (!strcmp(szCmd, "/myawesomecommand"))
    {
        MessageBox(NULL, "Yeah you typed my command!", "Hello!", MB_OK);
        return 1; // return 1 here so we cancel the server call (to the original function).
    }

    /* push the parameter back to the stack and call original function  */
    __asm push ebx
    __asm call [origSendCommandToServer]
    return 1;
}

void LoopOrSomething()
{
    DWORD dwSAMP = (DWORD)GetModuleHandle("samp.dll");
    if (dwSAMP && !origSendCommandToServer) //
    {
        origSendCommandToServer = dwSAMP + 0x65C60; // this is the address of the original function
        
        DWORD dwPatchLoc = dwSAMP + 0x65DF8; // ;call SendCommandToServer (this is our original call location)
        DWORD dwRelativeAddr = ((DWORD)SendCommandToServer) - (dwPatchLoc + 5); // calculate the relative address
        SetData((void*)(dwPatchLoc + 1), &dwRelativeAddr, 4); // set our relative address (remember +1 there to skip the call opcode!)
    }
}
[/shcode]

I hope this little "HOW TO" helped you a bit, even If I literally suck at explaining things.
Greets 0x_.
 

0x_

Wtf I'm not new....
Administrator
Joined
Feb 18, 2013
Messages
1,116
Reaction score
167
RE: [HOW TO] Do your own SA:MP command's.

I'll do a shameless bump.
/e: There's a minor fail in one of my explanations.
 

Opcode.eXe

Expert
Joined
Feb 18, 2013
Messages
1,486
Reaction score
227
Location
( ͡° ͜ʖ ͡°)
I press my ass and no work  :facepalm:.

/e:
That moment when you know that this isn't copypasted like all the other people do.
 

NarutoUA

Member
Joined
May 26, 2013
Messages
20
Reaction score
1
You can improve it by registering commands like:

Code:
std::map <string, std::function<string>> g_CmdMap;
void registerCommand(std::string strCmd, std::function<string> funcCmd)
{
g_CmdMap.insert(std::pair<std::string,std::function<std::string>>(strCmd, funcCmd) );
}

int __stdcall SendCommandToServer(const char *szCmd)
{

// PROCESS MAP HERE
//
//

   /* push the parameter back to the stack and call original function  */
   __asm push ebx
   __asm call [origSendCommandToServer]
   return 1;
}
 

0x_

Wtf I'm not new....
Administrator
Joined
Feb 18, 2013
Messages
1,116
Reaction score
167
btw, I forgot to mention you can also easily redirect the call of samp.dll+0x6492A to receive every message and then do the normal recall stuff this will allow you to make your own command specifier or stuff.
 
Top