AlliedModders

AlliedModders (https://forums.alliedmods.net/index.php)
-   Module Coding (https://forums.alliedmods.net/forumdisplay.php?f=9)
-   -   The art of signature scanning (https://forums.alliedmods.net/showthread.php?t=184165)

TheAvenger 05-01-2012 16:30

The art of signature scanning
 
Hi!

I just joined this community, and even though it might not seem as the most
appropriate one (I'm only doing metamod extensions, which aren't related to amxx),
but I won't deny that it is flourishing (considering GoldSrc's age of 14 years)
and this seemed as the best place to find help.

I've noticed that signature scanning is highly appreciated since it is the most efficient
and dynamic way to get full access of the modifications that GoldSrc has to offer.

In my specific case, I'm trying to learn signature scanning to be used in my
Counter-Strike extension. Right now I do not have any specific goal nor does
my extension have a purpose, but I do want to master (or at least learn) the
art of signature scanning (I hate explicit offsets :nono:).

Right now I'm testing signature scanning with the "StartDeathCam__11CBasePlayer" call,
which obviously is: void CBasePlayer::StartDeathCam(void) from player.h@Ln:1349.

I'm using the well spread method that is documented on the alliedmodders wiki.

The problem I am experiencing is simple, yet I have not been able to solve it. I do
my signature call (Note: I'm on linux so there's no interest for Windows atm) to find
the address of the "StartDeathCam" method.

After I've found out the position I create a pointer to the address (void (*StartDeathCam) (void*),
where as void* is a CBasePlayer pointer) which I then call in-game by user command (i.e 'say .test').

As mentioned here, I am calling this method by simple specifying a pvPrivateData
pointer as the void* (CBasePlayer*) since the 'this' pointer is the first one to be
pushed onto the stack under GCC.

This casues an instant crash (note: I've tried to use the command under several circumstances,
such as: dead, alive, round end, freezetime etc...) with no recovery, and this is where I am stuck.

This is the code that I am using (sigscanner.h):
PHP Code:

#ifndef SIGSCANNER_H
#define SIGSCANNER_H

// Include files
#include <extdll.h>
#include "global.h" // Just contains type defines such-as unsigned char = byte etc...

class SigScanner
{
public:
    
// Constructor
    
SigScanner(void);
    
    
// Public functions
    
bool Init(void pAddr);                        // Initialize signature scanner
    
void FindSignature(byte pSignaturesize_t sigLength);    // Finds a specified signature
    
private:
    
// Private variables
    
byte m_baseAddr;        // Base address to memory
    
size_t m_baseLength;        // Our base address length
};

#endif 

And this is the sigscanner implementation:
PHP Code:

// Header files
#include <dlfcn.h>
#include <sys/types.h>
#include <sys/stat.h>

// Our class header
#include "sigscanner.h"

SigScanner::SigScanner(void)
{
    
// Just reset variables
    
m_baseAddr null;
    
m_baseLength 0;
}

bool SigScanner::Init(void pAddr)
{
    
// Setup variables
    
Dl_info info;
    
struct stat buffer;
    
    
// Load our address
    
if(!dladdr(pAddr, &info))
        return 
false;
    
    if(!
info.dli_fbase || !info.dli_fname)
        return 
false;
    
    if(
stat(info.dli_fname, &buffer) != 0)
        return 
false;
    
    
// Success, setup variables
    
m_baseAddr = (byte*) info.dli_fbase;
    
m_baseLength buffer.st_size;
    
    
// Return...
    
return true;
}

void SigScanner::FindSignature(byte pSignaturesize_t sigLength)
{
    
// Setup our memory pointers
    
byte pBasePtr m_baseAddr;
    
byte pEndPtr m_baseAddr m_baseLength /* 0x100000 <-- Should this be used instead? */;
    
    
size_t i;
    
    
// Start searching memory
    
while(pBasePtr pEndPtr)
    {
        for(
0sigLengthi++)
        {
            if(
pSignature[i] != 0x2A && pSignature[i] != pBasePtr[i])
                    break;
        }
        
        
// Check for a match
        
if(== sigLength/* + 1 */)
        {
            
// Return our match
            
return (void*) pBasePtr;
        }
        
        
// Search memory in an aligned manner
        
pBasePtr += sizeof(byte*);
    }
    
    
// No success...
    
return (void*) null;


And this is how I use the sig-scanner implementation (read comments):
PHP Code:

// Global function pointer and type define
typedef void (*StartDeathCam)(void *);
StartDeathCam gRS null;

void Game::EventServerActivatePost(edict_t pEntListint /*edictCount*/int clientMax)
{
    
// Initializing some stuff first
    // ...

    // Here, I initialize my signature scanner (and specifies AddToFullPack as start address)
    
if(m_sigScanner.Init((void*) MDLL_AddToFullPack))
    {
        
// This is the Linux signature for StartDeathCam method
        
char signature[] = "StartDeathCam__11CBasePlayer";

        
// And this is where I am fetching the function pointer (note: it is valid, i.e the logs outputs success)
        
StartDeathCam gRS = (StartDeathCamm_sigScanner.FindSignature((byte*) signaturestrlen(signature));
        
        if(
gRS != null)
        {
            
m_debug->Log("We have found our signature!");
        }
        
        else 
m_debug->Log("Failed to find signature.");
    } else 
m_debug->Log("Failed to initialize SigScanner.");
}

void Game::EventClientCommand(edict_t pEnt)
{
    
// Sorting out all invalid objects and verifying commands
    // ....

    
if(strcmp(command".test") != 0)
        
Meta::Return(Meta::Ignored);

    
// This is where I am calling the function,
    // but it results in a server crash...
    
(gRS)(pEnt->pvPrivateData);

    
// Return as ignored...
    
Meta::Return(Meta::Ignored);


I'm sorry for the wall of code, but C++ code easily gets extensive.

I am also aware of the popular Orpheu module which uses signature
scanning, but it consists of _so_ much code that it is hardly extinguishable
from the rest (of the incurious code).

I do not know if I am to be expecting a reply since I presented everything
kinda sloppy, but I would appreciate any help/assistance that is offered!

EDIT: In case of interest I'm using g++ 4.6.3 @ Ubuntu 12.04 i386

joaquimandrade 05-02-2012 19:03

Re: The art of signature scanning
 
Fellow, when you got a symbolic name like StartDeathCam__11CBasePlayer you don't want to find it in memory like you do for signature scanning. Signature scanning is to find bytes that represent processor instructions. The symbolic names are "mapped" to the addresses they represent with GetProcAddress in windows and dlsym in unix or linux or posix or whatever. Check the file library.cpp that you can find in the sources for the Rage module.

Edit: one thing you should get used to do is to play around with dlls and so's in IDA so you can see more of how a library is made on the inside. In this case, what you should have done to understand you were doing it wrong would be:

In IDA checking the offset of CBasePlayer::StartDeathCam which is straightforward just a matter of browsing the code.
In your code doing something like

Code:

long offset = (long) gRS - (long) sigScanner.m_baseAddr;
and printing it and compare with the value you got from IDA.

TheAvenger 05-03-2012 07:30

Re: The art of signature scanning
 
Aaa, yes! I noticed my mistake. I can't believe I was so stupid. But in that case,
I must ask, when do you want to use signature scanning? Is it only of interest when
using windows? Since all executables/libraries have the symbols provided with them
in Linux (which in turn means that dlsym will always find them)?

I must say though, that this could not be a more interesting subject for me right now.
I've just implemented a detour, and have caught InstallGameRules event (with pre-post hook),
now I'm just trying to figure out how to use this class pointer it returns:)

EDIT: I must also ask about the class pointer CBasePlayer. For instance; if I want to
call a function which takes a CBasePlayer pointer as an argument, should I provide
an edict_t pointer or the pvPrivateData pointer?


Haha, just hooked RoundRespawn and tried to call it with pvPrivateData. Couldn't have gone smoother!

joaquimandrade 05-03-2012 10:01

Re: The art of signature scanning
 
Quote:

Originally Posted by TheAvenger (Post 1701029)
Aaa, yes! I noticed my mistake. I can't believe I was so stupid. But in that case,
I must ask, when do you want to use signature scanning? Is it only of interest when
using windows? Since all executables/libraries have the symbols provided with them
in Linux (which in turn means that dlsym will always find them)?

I must say though, that this could not be a more interesting subject for me right now.
I've just implemented a detour, and have caught InstallGameRules event (with pre-post hook),
now I'm just trying to figure out how to use this class pointer it returns:)

EDIT: I must also ask about the class pointer CBasePlayer. For instance; if I want to
call a function which takes a CBasePlayer pointer as an argument, should I provide
an edict_t pointer or the pvPrivateData pointer?

Yes its mostly of interest when using windows.

InstallGameRules creates the global CHalfLifeMultiplay object. With it you can:
call and hook its virtual functions with simple direct code just with their offsets in the virtual table
call and hook its non virtual functions with signature scanning and more complicated hooking.
Change its data. For example with this i'm modifing the object field named m_iUnBalancedRounds to make round balancing happen every round.

About the last question you should provide the pointer pvPrivateData since that is a CBasePlayer* in the case of a player and is you are expected to pass it when you call a class member function.

One thing you must be aware is that to call class member functions (classes that are non static and belong to a class) the convention call you must use is thiscall that is much different from windows to linux and therefore will force you to create different code to handle it with some ifdefs.

TheAvenger 05-03-2012 11:33

Re: The art of signature scanning
 
Quote:

Originally Posted by joaquimandrade (Post 1701138)
Yes its mostly of interest when using windows.

InstallGameRules creates the global CHalfLifeMultiplay object. With it you can:
call and hook its virtual functions with simple direct code just with their offsets in the virtual table
call and hook its non virtual functions with signature scanning and more complicated hooking.
Change its data. For example with this i'm modifing the object field named m_iUnBalancedRounds to make round balancing happen every round.

About the last question you should provide the pointer pvPrivateData since that is a CBasePlayer* in the case of a player and is you are expected to pass it when you call a class member function.

One thing you must be aware is that to call class member functions (classes that are non static and belong to a class) the convention call you must use is thiscall that is much different from windows to linux and therefore will force you to create different code to handle it with some ifdefs.

Interesting! Thanks for all the details about InstallGameRules. Right now I'm trying to document
the CGameRules class by using 'fake' structure pointers which works perfect!

Right now it looks something like this:
PHP Code:

struct CGameRules
{
    
byte fill1[40];
    
long m_fTeamCount;            // Offset: 40
    
byte fill2[4];
    
long m_fRoundCount;            // Offset: 48
    
byte fill3[4];
    
int m_iRoundTimeSecs;            // Offset: 56
    
byte fill4[8];
    
int m_iAccountTerrorist;        // Offset: 68
    
int m_iAccountCT;            // Offset: 72
    
byte fill5[8];
    
int m_iNumSpawnableTerrorist;    // Offset: 84
    
int m_iNumSpawnableCT;        // Offset: 88
    
byte fill6[8];
    
int m_iHostagesRescued;        // Offset: 100
    
byte fill7[4];
    
int m_iRoundWinStatus;        // Offset: 108
    
short m_iNumCTWins;            // Offset: 112
    
short m_iNumTerroristWins;        // Offset: 114
    
bool m_bTargetBombed;        // Offset: 116
    
bool m_bBombDefused;        // Offset: 117
    
bool m_bMapHasBombTarget;    // Offset: 118
    
bool m_bMapHasBombZone;        // Offset: 119
    
byte fill8[1];
    
bool m_bMapHasRescueZone;    // Offset: 121
    
bool m_bMapHasEscapeZone;        // Offset: 122
    
byte fill9[1];
    
byte m_iMapHasVIPSafetyZone;    // Offset: 124
    
byte fill10[36];
    
bool m_bRoundTerminating;        // Offset: 161
    
byte fill11[2];
    
float m_flRequiredEscapeRatio;    // Offset: 164
}; 

There is one question I'd like to ask you though, how do you find these offsets, and especially the name
of the offets (and what they do)? I take it for granted that you haven't come up with these names by yourself?

Currently I'm designing this structure from the file 'CGameOffsets' supplied in Arkshine's plugin "Round Terminator".
This is because I have no idea myself, how to find these offets and _especially_ what they do (the name often describes that).

About the _thiscall convention, it's a breeze for me. Since I'm only programming for linux, it's the GCC calling convention,
which means no push to the ecx registry or anything, just use it as a first argument! Awesome :)

Perhaps I should tell Arkshine this myself, but you also seem to have a lot of knowledge on the subject. In his CGameOffset
file (supplied in the zip file here), a LOT of different offsets are specified, although I believe one of the offsets is incorrect (or around ~4 of them).

According to the file, the variable (remember I'm talking linux offets here) m_flRequiredEscapeRatio is at offset 164 (and it's
a long, or well a float, but it's still 4 bytes). This means that it takes up space for offsets 164-168 (4 bytes), but the adjacent offset
specified, m_iNumEscapers, is at offset 166, which makes no sense, unless there is some low/upper bit computations behind
the scene (which still makes no sense since its a float), it should be invalid. Which also (might) mean that all the offsets 'below'
this one, is also invalid (i.e m_iHaveEscaped and m_pVIP).

However I would appreciate it a lot if you could briefly explain how to find these member offsets (and their types, and names).

EDIT: After further analyzing, it's pretty obvious m_iNumEscapers is at offset 168, not 166...

EDIT 2: Another question I have is about CBasePlayer*. Let's take CGameRules as an example. It contains the member m_pVIP, which
is a CBasePlayer pointer. If I want to, for example, find out the players name or fetch his entity (edict_t) how would I proceed?

Normally one would just do STRING(m_pVIP->v.netname), but I have no insight/knowledge on how the CBasePlayer structure is?
One way would be too loop all players entites, and check if their pvPrivateData matches the CBasePlayer address, but that isn't efficient...

If I look here at the partial source code (player.cpp) it seems as if pev is a member structure (which obviously is an
edict_t pointer). If that is true, I only need to find the offset of this structure (in CBasePlayer*), but how does one do that?

Arkshine 05-03-2012 15:04

Re: The art of signature scanning
 
Quote:

how to find these offets
By using a disassembler/decompiler like IDA. You load a CS binary and simply find your function and check the code.

Quote:

what they do
By checking the references of the offset, then you guess the name and what it does. About these offsets specifically, some are from the HL2SDK, some files for CSS has been leaked or has been created, and the game rules for CS/CSS is basically the same. Except that, all others offsets, you simply guess for all.

Quote:

I believe one of the offsets is incorrect
Probably a typo. Not like I've tested all, just seen the offset on IDA. Yes, just check m_iNumEscapers is 168. The others are fine. Just a typo. I did not probably not tested under linux.

Quote:

If I want to, for example, find out the players name or fetch his entity (edict_t) how would I proceed?
You can find a lot of answers by reviewing the HLSDK. Even if in CS it a bit different, it's still based on it and most of stuffs are the same. In CBaseEntity, you would see it contains entvars_t *pev, so from that, you can get easily the name.

TheAvenger 05-03-2012 15:14

Re: The art of signature scanning
 
Quote:

Originally Posted by Arkshine (Post 1701348)
You can find a lot of answers by reviewing the HLSDK. Even if in CS it a bit different, most of things are the same. In CBaseEntity, you would see it contains entvars_t *pev, so from that, you can get easily the name.

Yes, I just looked at your excellent code of the CSSDK port. Just seconds ago I tried the 'pev' variable
on a CBasePlayer entity, although I had no success. Since it is the 'first' variable, it should be at offset
zero, right? Or perhaps some differentiation ~4 bytes. I had no success though.

Trying to find out the offset, I used this code (it's rough I know, but its fast)
PHP Code:

for(int i 0100i++)
{
    
entvars_t pev = (entvars_t*)(((byte*)pEnt->pvPrivateData) + i);
    
m_debug->Log(Global::Format("Your name is: %s"String(pev->netname)));


This code crashes after 5 loops (segmentation fault). So there could be two reasons; the area this variable
is stored in, is protected (which would require a call to mprotect), or the offset is completely wrong. Since
you've written/found out about _tons_ of offsets, do you perhaps know the offset of the 'pev' member variable?

NOTE: For the first 5 values, the fields were all empty (the netname returned an empty string).
EDIT: Ops, the CBaseEntity is obviously not a pod structure, which means offset 0 is probably invalid. I'll try to find it using IDA I guess.

EDIT2: I'd also like to ask, how exact do you estimate your CS-SDK is compared to the original?

For example; it would be awesome if one could just cast the pvPrivateData to CBasePlayer (which you've both
defined and declared in the CS-SDK). Obviously the implementations might be wrong, but the declarations
in the header files (variables, function headers etc.)?

joaquimandrade 05-03-2012 19:09

Re: The art of signature scanning
 
PHP Code:

long get_pev_offset()
{
        
edict_tworld INDEXENT(0);
        
charworldPrivateData = (char*) world->pvPrivateData;
        
entvars_tworldEntvars = &world->v;
 
        for(
long i=0;;i++)
        {
                
voidvalue = *((void**)(worldPrivateData i));
 
                if(
value == (void*)worldEntvars)
                {
                        return 
i;
                }
        }
 
        return 
0;
}

edict_tPrivateToEdict(const void *pdata)
{
    if (
pdata==NULL)
    {
        return 
NULL;
    }
    
char *ptr=(char*)pdata;

    static 
long pev_offset = -1;
    
    if(
pev_offset == -1)
    {
        
pev_offset get_pev_offset();
    }
    
    
ptr += pev_offset;
    
    
entvars_t *pev=*(entvars_t **)ptr;

    if (
pev==NULL)
    {
        return 
NULL;
    }
    
    return 
pev->pContainingEntity;
}; 

Try something like this. It has code from me and code from hamsandwich.

TheAvenger 05-03-2012 21:31

Re: The art of signature scanning
 
Quote:

Originally Posted by joaquimandrade (Post 1701507)
PHP Code:

long get_pev_offset()
{
        
edict_tworld INDEXENT(0);
        
charworldPrivateData = (char*) world->pvPrivateData;
        
entvars_tworldEntvars = &world->v;
 
        for(
long i=0;;i++)
        {
                
voidvalue = *((void**)(worldPrivateData i));
 
                if(
value == (void*)worldEntvars)
                {
                        return 
i;
                }
        }
 
        return 
0;
}

edict_tPrivateToEdict(const void *pdata)
{
    if (
pdata==NULL)
    {
        return 
NULL;
    }
    
char *ptr=(char*)pdata;

    static 
long pev_offset = -1;
    
    if(
pev_offset == -1)
    {
        
pev_offset get_pev_offset();
    }
    
    
ptr += pev_offset;
    
    
entvars_t *pev=*(entvars_t **)ptr;

    if (
pev==NULL)
    {
        return 
NULL;
    }
    
    return 
pev->pContainingEntity;
}; 

Try something like this. It has code from me and code from hamsandwich.

It's really late here so I'll just give a quick reply (I have an additional question but that one
requires more thorough explanation); is that really necessary? I didn't know about the edict_t
pContainaingEntity member variable, but now that I do, I have no problems what so ever!

I've just defined a simple CBasePlayer structure (which I convert pvPrivateData to)
and access the entvars directly. For example:
Code:

((CBasePlayer*)pEnt->pvPrivateData)->pev->pContainaingEntity
With that code, its work excellent without any segmentations faults! But since your code is much
more extensive, is there a reason behind it? Or why haven't you just called entvars directly to
access pContainingEntity?

Thanks so much for all help btw!

NOTE: Judging by the code, is the pev offset dynamic? If that's the case, it doesn't
make any sense? A structure can't be dynamic, entvars_t will always be at offset zero
in the CBasePlayer structure, it's the nature of C(++)...

EDIT: Perhaps it's mod specific?

joaquimandrade 05-03-2012 21:36

Re: The art of signature scanning
 
Quote:

Originally Posted by TheAvenger (Post 1701584)
It's really late here so I'll just give a quick reply (I have an additional question but that one
requires more thorough explanation); is that really necessary? I didn't know about the edict_t
pContainaingEntity member variable, but now that I do, I have no problems what so ever!

I've just defined a simple CBasePlayer structure (which I convert pvPrivateData to)
and access the entvars directly. For example:
Code:

((CBasePlayer*)pEnt->pvPrivateData)->pev->pContainaingEntity
With that code, its work excellent without any segmentations faults! But since your code is much
more extensive, is there a reason behind it? Or why haven't you just called entvars directly to
access pContainingEntity?

Thanks so much for all help btw!

The thing is: the offset of the pev member in classes is compilation dependent and maybe mod dependent. If you check hamdata.ini in an amxmodx installation you can see:

Code:

@section cstrike linux
    pev 0
@section cstrike windows
    pev 4

So, if you plan on doing stuff only for cstrike linux is ok to assume 0.


All times are GMT -4. The time now is 14:38.

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