Raised This Month: $ Target: $400


Post New Thread Reply   
Thread Tools Display Modes
Author Message
Join Date: Nov 2005
Reply With Quote #1

Hello, and welcome to today's episode of Function Signature Scanning (sigscanning for short). This guide is oriented for those of you who don’t know what the hell is going on and can’t cope anymore. I will be covering the finer details of the vague art for extracting function signatures from memory and then re-scanning for them at program runtime. But first, let’s go into some background information.

What is Sigscanning and how is it useful?
Sigscanning is a method of looking for the “signature” of a function at program runtime. A signature for a function is a set of bytes which are the instructions a machine uses to operate. These set of bytes are what make up functions (or subroutines as they are called in Assembly). When we have enough of these bytes, we can then obviously match them again to the function in memory. Why is this useful for plugins? Well each time Valve releases an update for their mods, they may change classes such as CBaseEntity used by many plugin coders to change a player’s entity. Due to the fact that we do not have the code for each of these changes, we may be using virtual functions that now rely at different addresses. This would cause a crash at runtime. To avoid this, our only solution is to use (a) offsets, (b) dumby virtual function insertion, or (c) sigscanning. Each have their pros and cons, but it turns out that although sigscanning is the most tedious out of the three, it is the most reliable.

So what is this Assembly machine code and runtime memory crap?
To understand all of this, one must first understand how the code of a C++ program is compiled into a binary file. When you hit “Build Solution” on your IDE or run gcc, your compiler does a multitude of operations. First it takes the code and runs it through a Preprocessor. The Preprocessor takes all the #directives (e.g. #define, #include, etc.) and substitutes them with normal C++ code. It also takes the code of all the inline functions and replaces them with the code that called them (substitution again). After the Preprocessor has done its job, the code is passed on to the Parser. In simplified terms, the Parser basically takes C++ code and turns it into Assembly (asm) code. After the Parser has parsed the code into Assembly code, the Assembler “assembles” the code into mostly machine (byte) code which is put into Object files that carry the .obj extension. Then the Linker gathers all the Object files where it looks for function calls and sets the “links” to them (static linking) as well as external calls to functions outside the scope of the assembled code (dynamic linking) and finally maps any runtime calls (runtime linking) and finally outputs the finished product (a binary file, i.e. an executable or library). So when a server loads your plugin, it uses runtime calls to call functions from within your plugin, and likewise your plugin calls functions from the server.

Now where this all comes into play in sigscanning is where you will be taking the server module (.dll or .so file) from memory and scanning it in its machine-code form for those functions that you need. This is done by first disassembling the server module back into Assembly form and finding the instructions that make up that function you want.

Assembly is the “lowest-level” language in programming terms. It is easy to flip-flop between Assembly and machine code because they are basically the same, except Assembly uses something called “Mnemonics” before every instruction. These are the keywords “push, pop, mov, call,” etc. that define what the instruction does. As C++ code is translated into Assembly, you can imagine the very structured syntax of C++ flattened out into primitive simple Assembly instructions. I won’t go into further detail on Assembly due to its complex nature. The best way to learn more about it (which is recommended) is to read one of the links provided at the bottom of this writing.

Before we go any further
Read BAILOPAN’s three DevLogs on sigscanning to gain an understanding of the proceeding sections (i.e., how I obtained the code used for the sigscanner).
Oh, you’ve not hacked yet?http://www.sourcemod.net/devlog/?p=55
Finding Functions, Part 2http://www.sourcemod.net/devlog/?p=56
Finding Functions, Part 3http://www.sourcemod.net/devlog/?p=57

How do I scan for signatures at runtime?
Since BAILOPAN explained the functions used here in his DevLogs, I won’t go into much detail about them. This is the complete code for a basic sigscanner. All functions and variables are described with comments.

#ifndef SIGSCAN_H
#define SIGSCAN_H

class CSigScan {
	/* Private Variables */
	/* Base Address of the server module in memory */
	static unsigned char *base_addr;
	/* The length to the module's ending address */
	static size_t base_len;

    /* The signature to scan for */
	unsigned char *sig_str;
	/* A mask to ignore certain bytes in the signature such as addresses
	   The mask should be as long as all the bytes in the signature string
	   Use '?' to ignore a byte and 'x' to check it
	   Example: "xxx????xx" - The first 3 bytes are checked, then the next 4 are
	   ignored, then the last 2 are checked */
	char *sig_mask;
	/* The length of sig_str and sig_mask (not including a terminating null for sig_mask) */
	size_t sig_len;

	/* Private Functions */
	void* FindSignature(void);

	/* Public Variables */
	/* sigscan_dllfunc should be set to the function "gameServerFactory" in Load() */
	static void *(*sigscan_dllfunc)(const char *pName, int *pReturnCode);
	/* If the scan was successful or not */
	char is_set;
	/* Starting address of the found function */
	void *sig_addr;

	/* Public Functions */
	CSigScan(void): sig_str(NULL), sig_mask(NULL), sig_len(0), sig_addr(NULL) {}

	static bool GetDllMemInfo(void);
	void Init(unsigned char *sig, char *mask, size_t len);

void InitSigs(void);

/* Sig Functions */
class CBaseAnimating;
void CBaseAnimating_Ignite(CBaseAnimating *cba, float flFlameLifetime);

#include <stdio.h>

#ifdef WIN32
	#define WIN32_LEAN_AND_MEAN
	#include <windows.h>
	#include <dlfcn.h>
	#include <sys/types.h>
	#include <sys/stat.h> 

#include "sigscan.h"

/* There is no ANSI ustrncpy */
unsigned char* ustrncpy(unsigned char *dest, const unsigned char *src, int len) {
		dest[len] = src[len];

	return dest;

/* //////////////////////////////////////
    CSigScan Class
    ////////////////////////////////////// */
unsigned char* CSigScan::base_addr;
size_t CSigScan::base_len;
void *(*CSigScan::sigscan_dllfunc)(const char *pName, int *pReturnCode);

/* Initialize the Signature Object */
void CSigScan::Init(unsigned char *sig, char *mask, size_t len) {
	is_set = 0;

	sig_len = len;
	sig_str = new unsigned char[sig_len];
	ustrncpy(sig_str, sig, sig_len);

	sig_mask = new char[sig_len+1];
	strncpy(sig_mask, mask, sig_len);
	sig_mask[sig_len+1] = 0;

		return ; // GetDllMemInfo() Failed

	if((sig_addr = FindSignature()) == NULL)
		return ; // FindSignature() Failed

	is_set = 1;
	// SigScan Successful!

/* Destructor frees sig-string allocated memory */
CSigScan::~CSigScan(void) {
	delete[] sig_str;
	delete[] sig_mask;

/* Get base address of the server module (base_addr) and get its ending offset (base_len) */
bool CSigScan::GetDllMemInfo(void) {
	void *pAddr = (void*)sigscan_dllfunc;
	base_addr = 0;
	base_len = 0;

	#ifdef WIN32

		return false; // GetDllMemInfo failed: !pAddr

	if(!VirtualQuery(pAddr, &mem, sizeof(mem)))
		return false;

	base_addr = (unsigned char*)mem.AllocationBase;

	IMAGE_DOS_HEADER *dos = (IMAGE_DOS_HEADER*)mem.AllocationBase;
	IMAGE_NT_HEADERS *pe = (IMAGE_NT_HEADERS*)((unsigned long)dos+(unsigned long)dos->e_lfanew);

	if(pe->Signature != IMAGE_NT_SIGNATURE) {
		base_addr = 0;
		return false; // GetDllMemInfo failed: pe points to a bad location

	base_len = (size_t)pe->OptionalHeader.SizeOfImage;


	Dl_info info;
	struct stat buf;
	if(!dladdr(pAddr, &info))
		return false;
	if(!info.dli_fbase || !info.dli_fname)
		return false;
	if(stat(info.dli_fname, &buf) != 0)
		return false;
	base_addr = (unsigned char*)info.dli_fbase;
	base_len = buf.st_size;

	return true;

/* Scan for the signature in memory then return the starting position's address */
void* CSigScan::FindSignature(void) {
	unsigned char *pBasePtr = base_addr;
	unsigned char *pEndPtr = base_addr+base_len;
	size_t i;

	while(pBasePtr < pEndPtr) {
		for(i = 0;i < sig_len;i++) {
			if((sig_mask[i] != '?') && (sig_str[i] != pBasePtr[i]))

		// If 'i' reached the end, we know we have a match!
		if(i == sig_len)
			return (void*)pBasePtr;


	return NULL;

/* Signature Objects */
CSigScan CBaseAnimating_Ignite_Sig;

/* Set the static base_addr and base_len variables then initialize all Signature Objects */
void InitSigs(void) {

	/* void CBaseAnimating::Ignite(float flFlameLifetime, bool bNPCOnly, float flSize, bool bCalledByLevelDesigner);
	Last Address: 0x220BC7A0
	Signature: 56 8B F1 8B? 86? BC? 00? 00? 00? C1? E8? 1B? A8? 01? 0F? 85?
9A? 00? 00? 00? 8B 16 FF 92? F0? 00? 00? 00? 80? 7C? 24? 0C? 00? 74? 08? 84 C0 0F?
84? 83? 00? 00? 00? 3C 01 75? 20? 80 7C 24 14 00 75? 19? 8B CE E8 83? 1A? 01? 00?
85? C0? 74? 0E? 8B 10 8B C8 FF 92? 08? 05? 00? 00? 84 C0 74? 5F? 57 6A 01 56 E8
48? EA? 07? 00? 8B F8 83 C4 08 85 FF 74? 3D? 8B 44 24 0C 50 8B CF E8 83? E5? 07?
00? 68 00 00 00 08 8B CE */
	CBaseAnimating_Ignite_Sig.Init((unsigned char*)"\x56\x8B\xF1\x8B\x86\xBC\x00\x00\x00\xC1\xE8\x1B\xA8\x01\x0F\x85\x9A

	return ;

/* Example of a sig-scanned method function */
void CBaseAnimating_Ignite(CBaseAnimating *cba, float flFlameLifetime) {
	int bNPCOnly = false, bCalledByLevelDesigner = false;
	float flSize = 0.0f;

		return ; // sigscan failed

	#ifdef WIN32
	/* The first parameter takes a pointer to the object,
	   the second is just a (required) dummy parameter */
	typedef void (__fastcall *func)(CBaseAnimating*, void*, float, bool, float, bool); 
	func thisfunc = (func)CBaseAnimating_Ignite_Sig.sig_addr; // Pointer to the function scanned from memory
	/* Pass "this" for the first parameter, and 0 for the second parameter */
	thisfunc(cba, 0, flFlameLifetime, (bool)bNPCOnly, flSize, (bool)bCalledByLevelDesigner);
	/* NOTICE: This is the code you must use IF you are using a 
	#  variadac class method (e.g. va_method(argc, ...)).
	#  typedef void (*func)(float, bool, float, bool);
	#  func thisfunc = (func)CBaseAnimating_Ignite_Sig.sig_addr;
	#  The CBaseAnimating object must be pushed onto the stack manually
	#  for the real method to use (and thus its arguments as well).
	#__asm {
	#	mov ecx, cba;
	#	push flFlameLifetime;
	#	push bNPCOnly;
	#	push flSize;
	#	push bCalledByLevelDesigner;
	#	call thisfunc;
	#}; */
	typedef void (*func)(CBaseAnimating*, float, bool, float, bool);
	func thisfunc = (func)CBaseAnimating_Ignite_Sig.sig_addr; // Pointer to the function scanned from memory
	// GCC pushes the CBaseAnimating object for us automaticly
	thisfunc(cba, flFlameLifetime, (bool)bNPCOnly, flSize, (bool)bCalledByLevelDesigner);

	return ;
This will find the signature of a function at runtime, however it’s is useless unless you already have or know how to find signatures for which it can scan.

How do I find the signature of a function?
To find a signature for a function, we need a Disassembler to disassemble the server module. Here are links to free Disassemblers that seem to work well:Linux:
Showdax pointed out a very helpful way that makes finding signatures under Linux much easier than under Windows. The GNU tool “objdump” can be used against the server module to disassemble the binary into AT&T Assembly code with each subroutine (and variable?) given its name from the source code. Here, as an example from showdax’s post:
 ~/usr/srcds/dod/bin$ objdump -d server_i486.so > server_i486.so.objdump
This dumps the disassembled code into the file “server_i486.so.objdump”. You can then simply open up that file and search for the function name that corresponds to its subroutine then extract its bytes to create a signature (discussed later in How do I create a searchable signature?).

Under Windows, finding a signature becomes much more complicated as PE-format binaries do not include symbol names for each subroutine. I suggest using the IDA Pro Freeware Disassembler since I will be using a method I have found to find signatures that I only know how to do in IDA Pro (although you may be able to find a way to do my method in the other disassemblers).

The rest of this section will assume you are running Windows, have Microsoft Visual Studio C++ and using the IDA Pro Freeware Disassembler. If you don’t have one of these, then there’s always bittorrent ;). First what you need to do is compile the HL2M server module. So navigate your way to the “hl2src/dlls” directory and open up “hl_sdk.vcproj”. Once open inside MSVC++, go to the Build menu and select Configuration Manager. Set the configuration to “Release” and press OK. Go to the Build menu again and select Build. This may take a bit as everything is compiled together. Once finished, you should have a new directory named “Release_hl2.” Inside, there will be the compiled server.dll file and a server.pdb file. The PDB file is key to finding the subroutines in Assembly for the function you want to use. Now open IDA Pro and go to the File menu and open the server.dll file you just compiled. When you open it, a dialog will pop up. Just press OK and everything will start to load. Eventually IDA Pro will say “There is a PDB file located in the same directory for this binary” or something to that effect. Click Yes and it will use that PDB file to name the subroutines which will make our life much easier. As things load, IDA Pro will go through the DLL, disassembling it and naming everything to their corresponding function or variable name. This may take a while and may even appear that IDA has frozen, but it has not so don’t close it or you will have to start over. After… maybe an hour, everything may have finished and we can continue on with finding signatures. BUT FIRST we have to open up yet another IDA Pro again (don’t close the other IDA Pro) and load the server.dll for the mod we will be sigscanning (in this case, Counter-Strike: Source). This file is located in “x/cstrike/bin” where is x is srcds or wherever you keep your server files. Again, do the same procedure, except this time we won’t have a PDB file for it. This should take a shorter time than last time since there is no PDB file so again wait for it to finish disassembling. After both IDA Pro processes have finished disassembling, we are ready to start finding signatures.

To start looking for a signature, switch over to the HL2M server module IDA Pro window. Let’s try to find the signature for Teleporting an entity. This is the prototype for its method from the source code:
virtual void CBaseEntity::Teleport(const Vector *newPosition, const QAngle *newAngles, const Vector *newVelocity);
Now in IDA Pro look for “CBaseEntity::Teleport” in the “Names” list then double click it:
Its name is represented as “CBaseEntity_Teleport” for the subroutine. As you can see, IDA Pro lists all the local and parameter variables near the top, and then the actual code is listed below with its corresponding opcode bytes (machine code). (Tip: I had to actually set IDA Pro to list the opcode bytes from the Options menu, in “General” then under the tab “Disassembly”). Now you might be thinking, “gee this is easy, I’ll just use the bytes here for the signature.” WRONG! To make life harder, Valve decided to change almost all the CBase* methods (I’ve seen) for Counter-Strike: Source from the Half-Life 2: Deathmatch code. Due to this fact, we will have to use the HL2M disassembly as clues for finding the reciprocal function/method in the CS:S disassembly. To find our Teleport function, we will have to find a unique set of instructions that wouldn’t be repeated in another subroutine. I had to do some trial and error with this function since Valve had changed or moved various pieces of code. If you try looking over the subroutine for something unique, you may see a group of repeated “mov” instructions:
.text:220C96CF 89 44 24 18          mov   [esp+30h+var_18], eax
.text:220C96D3 89 74 24 1C          mov   [esp+30h+var_14], esi
.text:220C96D7 89 74 24 20          mov   [esp+30h+var_10], esi
.text:220C96DB 89 74 24 24          mov   [esp+30h+var_C], esi
.text:220C96DF 89 74 24 28          mov   [esp+30h+var_8], esi
.text:220C96E3 89 74 24 2C          mov   [esp+30h+var_4], esi
Try searching for part of this in the CS:S disassembly:
And it should show up (I started from the top and searched down, I also “searched for the next item” to make sure there wasn’t another set of instructions alike). Then to verify this is indeed the subroutine for the function Teleport(), compare the two subroutines to see if they have similar instructions in the same places. If they do then it is and now you can use some of the instructions from your find in machine-code form to create a signature, which is discussed in the next section.

How do I create a searchable signature?
Once we have found the subroutine for the function we want, we can start extracting the bytes for use in a signature. Here is part of the Assembly for CBaseEntity::Teleport():
220D9940 CBaseEntity_Teleport proc near ; CODE XREF: sub_220B6330+14 p
220D9940 ; sub_22143900+A p ...
220D9940 83 EC 18 sub esp, 18h
220D9943 53 push ebx
220D9944 56 push esi
220D9945 8B D9 mov ebx, ecx
220D9947 8B 0D 78 B2 46 22 mov ecx, dword_2246B278
220D994D 33 F6 xor esi, esi
220D994F 33 C0 xor eax, eax
220D9951 3B CE cmp ecx, esi
220D9953 7E 21 jle short loc_220D9976
220D9955 8B 15 6C B2 46 22 mov edx, dword_2246B26C
220D995B EB 03 jmp short loc_220D9960
220D995B ; ---------------------------------------------------------------------------
220D995D 8D 49 00 align 10h
220D9960 loc_220D9960: ; CODE XREF: CBaseEntity_Teleport+1B j
220D9960 ; CBaseEntity_Teleport+2A j
220D9960 39 1C 82 cmp [edx+eax*4], ebx
220D9963 74 09 jz short loc_220D996E
220D9965 83 C0 01 add eax, 1
220D9968 3B C1 cmp eax, ecx
220D996A 7C F4 jl short loc_220D9960
220D996C EB 08 jmp short loc_220D9976
220D996E ; ---------------------------------------------------------------------------
220D996E loc_220D996E: ; CODE XREF: CBaseEntity_Teleport+23 j
220D996E 3B C6 cmp eax, esi
220D9970 0F 8D 17 01 00 00 jge loc_220D9A8D
220D9976 loc_220D9976: ; CODE XREF: CBaseEntity_Teleport+13 j
220D9976 ; CBaseEntity_Teleport+2C j
220D9976 55 push ebp
220D9977 57 push edi
220D9978 8D 44 24 10 lea eax, [esp+28h+var_18]
220D997C 50 push eax
220D997D 51 push ecx
220D997E B9 6C B2 46 22 mov ecx, offset dword_2246B26C
220D9983 89 5C 24 18 mov [esp+30h+var_18], ebx
220D9987 E8 B4 88 F9 FF call sub_22072240
220D998C 8D 4C 24 14 lea ecx, [esp+28h+var_14]
220D9990 51 push ecx
220D9991 53 push ebx
220D9992 89 44 24 18 mov [esp+30h+var_18], eax
220D9996 89 74 24 1C mov [esp+30h+var_14], esi
220D999A 89 74 24 20 mov [esp+30h+var_10], esi
I have written a small C program for which I use to create a signature string and signature mask from. It is not necessary to use, but it can help to reduce the amount of tedious work you have to do to create a signature.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>

int main() {
    int i, len, out_i = 0, mask_i = 0;
    char *sig, *out, *mask;
    sig = (char*)calloc(1024, sizeof(char));
    fgets(sig, 1022, stdin);
    len = strlen(sig);
    out = (char*)calloc(len*2, sizeof(char));
    mask = (char*)calloc(len*2, sizeof(char));
    for(i = 0;i < len;i++) {
        if(isalnum(sig[i]) && isalnum(sig[i+1])) {
            out[out_i++] = '\\';
            out[out_i++] = 'x';
            out[out_i++] = sig[i];
            out[out_i++] = sig[i+1];
            if(sig[i+2] == '?') {
                mask[mask_i++] = '?';
                i += 2;
            else {
                mask[mask_i++] = 'x';
    printf("\n\nSize: %d\n\nSig:\n%s\n\nMask:\n%s", strlen(mask), out, mask);
    return 0;
It takes an input string of hexadecimal characters just as they appear as bold in the Assembly above. For each byte that should be ignored, a question mark after a hexadecimal byte will tell the program where a “?” should be in the signature mask. I will give an example of what the input and output should look like near the end of this section.

When creating a signature, you must also create a signature mask as well. A signature mask tells the sigscanner which bytes to ignore, such as addresses which may change at runtime. The signature mask is the same length as the signature. A question mark (“?”) in the mask signifies the position of the byte in the signature string to be ignored. Any other character corresponding to a byte in the signature string will be checked (usually we use “x” to represent these).

For the Teleport() function we found in the last section, we will build our signature from the bold hexadecimal characters in the Assembly above. I will be using the format of my “Sig Creator” for developing a signature string and mask for the sigscanner. The first three instructions in the subroutine are called the “function prologue.” These instructions will most likely never change as long as the variable declarations are not changed. So to start off, input “83 EC 18 53 56” into the sigcreator. Next is a mov instruction that probably won’t change: “8B D9”. After that is another mov instruction, but this mov instruction contains an address in it, so add this to the input: “8B? 0D? 78? B2? 46? 22?”. This will appear as question marks in the signature mask. The next set of xor instructions don’t reference an address so they’re safe to add normally to the input: “33 F6 33 C0”. From here on, continue to input each segment of bytes into the sigcreator, where any segment referencing an address should have question marks follow each hexadecimal character pairs (like I did for the mov instruction). Eventually you’ll end at those last mov instructions. This isn’t the end of the subroutine, but it’s enough to develop a worthy signature. So press enter in the sigcreator, and it should output something like this:
?83 EC 18 53 56 8B D9 8B? 0D? 78? B2? 46? 22? 33 F6 33 C0 3B CE 7E? 21? 8B? 15?
6C? B2? 46? 22? EB? 03? 8D? 49? 00? 39 1C 82 74? 09? 83? C0? 01? 3B C1 7C? F4?
EB? 08? 3B C6 0F? 8D? 17? 01? 00? 00? 55 57? 8D? 44? 24? 10? 50 51 B9? 6C? B2?
46? 22? 89 5C 24 18 E8? B4? 88? F9? FF? 8D? 4C? 24? 14? 51 53 89 44 24 18 89
74 24 1C 89 74 24 20 89 74 24 24 89 74 24 28 89 74 24 2C

Size: 106


Now you have a nice signature and mask you can use for your sigscanner and it also gives you the length of the signature as well. Cheers!

Thank you to those who contributed to this document directly and/or indirectly:
Lance Vorgin – Forefather of sigscanners, posted his sigscanner on the sourcemod forums somewhere
BAILOPAN – Created his DevLogs on how to create a sigscanner, also gave tips as well as other useful information
Showdax – Suggested the objdump command for disassembly on Linux
CyberMind – Examples of how to use a different method of calling sig functions
Jacob Lojo – Posted a thread on how to develop a signature from disassembly (I was having trouble finding sigs with my scanner before his post). Also contributed a link to another disassembler

Oh, you’ve not hacked yet?http://www.sourcemod.net/devlog/?p=55
Finding Functions, Part 2http://www.sourcemod.net/devlog/?p=56
Finding Functions, Part 3http://www.sourcemod.net/devlog/?p=57
Introduction to Reverse Engineering Software - http://www.acm.uiuc.edu/sigmil/RevEng/index.html
The Art of Assembly Language Programming - http://maven.smith.edu/~thiebaut/Art.../artofasm.html

If you find something that is vague or incorrect, call it out and I will be happy to change it. This is a work-in-progress and any tips would be greatly appreciated.
SeLfkiLL is offline
Send a message via AIM to SeLfkiLL
Senior Member
Join Date: Dec 2004
Old 01-04-2006 , 11:55  
Reply With Quote #2

I prefer using objdump for disassembling files under Linux. It's pretty easy to use:
~/usr/srcds/dod/bin$ objdump -d server_i486.so > server_i486.so.objdump
Then just search for your function:
00860670 <_ZN10CDODPlayer10DODRespawnEv>:
  860670:       56                      push   %esi
  860671:       ba 01 00 00 00          mov    $0x1,%edx
  860676:       53                      push   %ebx
  860677:       83 ec 14                sub    $0x14,%esp
  86067a:       8b 5c 24 20             mov    0x20(%esp,1),%ebx
  86067e:       8b 0b                   mov    (%ebx),%ecx
  860680:       89 54 24 04             mov    %edx,0x4(%esp,1)
  860684:       89 1c 24                mov    %ebx,(%esp,1)
  860687:       ff 91 18 04 00 00       call   *0x418(%ecx)
  86068d:       8b 03                   mov    (%ebx),%eax
  86068f:       89 1c 24                mov    %ebx,(%esp,1)
  860692:       ff 90 60 04 00 00       call   *0x460(%eax)
  860698:       8b 93 84 0e 00 00       mov    0xe84(%ebx),%edx
  86069e:       85 d2                   test   %edx,%edx
  8606a0:       74 2e                   je     8606d0 <_ZN10CDODPlayer10DODRespawnEv+0x60>
showdax is offline
Send a message via MSN to showdax
Senior Member
Join Date: Apr 2005
Location: /dev/null
Old 01-04-2006 , 14:39  
Reply With Quote #3

Thanks a lot SeLfkiLL!

how about a how to include cbase.h without 200 errors?

so mabey complete working example attached so I can steal your cbase.h
Ph34r teh zombies!!!
find a better way to play CSS, connect to...
awuh0 is offline
Send a message via ICQ to awuh0 Send a message via AIM to awuh0 Send a message via MSN to awuh0
Join Date: Jan 2004
Old 01-04-2006 , 19:14  
Reply With Quote #4

Nice write-up :]

In the future I'd like to write about a more advanced signature scanning -- that is pattern matching, so you can easily streamline retrieving of EIPs, relative call addresses, and inlined addresses.
BAILOPAN is offline
Senior Member
Join Date: Oct 2004
Reply With Quote #5

Originally Posted by SeLfkiLL
	#ifdef WIN32
	typedef void (*func)(float, bool, float, bool);
	func thisfunc = (func)CBaseAnimating_Ignite_Sig.sig_addr; // Pointer to the function scanned from memory
	/* The CBaseAnimating object must be pushed onto the stack manually
	   for the real method to use (and thus its arguments as well). */
	__asm {
		mov ecx, cba;
		push flFlameLifetime;
		push bNPCOnly;
		push flSize;
		push bCalledByLevelDesigner;
		call thisfunc;
There's a better* way to call a member function in Win32 than all that asm crap. BAILOPAN actually mentioned it a few weeks ago in #sourcemod, and I just tested to see if it works about an hour ago, ironically. Keep in mind this is only for member functions that do not have variable arguments. To handle those, do it like GCC and pass this as the first parameter. Here is the method BAILOPAN suggested:
	#ifdef WIN32
	/* the first parameter is the this pointer, the second is just a dummy, just pass 0 */
	typedef void (__fastcall *func)(CBaseAnimating*, void*, float, bool, float, bool);
	func thisfunc = (func)CBaseAnimating_Ignite_Sig.sig_addr; // Pointer to the function scanned from memory

	thisfunc(cba, 0, flFlameLifetime, (bool)bNPCOnly, flSize, (bool)bCalledByLevelDesigner); 

Basically, the fastcall calling convention passes the first 2 parameters (as long as they fit in a register, 32 or 64bits long) in ECX and EDX. As you already know, MSVC handles thiscall by passing this in ECX. By simply passing the CBA pointer and a dummy value in ECX and EDX via fastcall, we are putting the this pointer right where it needs to be, and the dummy in EDX is ignored. Luckily both thiscall and fastcall have the callee clean the stack (which is why variable argument functions need to be handled normally like cdecl).

*I'm sure 'better' is a point of contention here. Personally, I just never liked using possibly nonportable inline asm to do things that could be done without it. Granted the 6 ops you used are hardly nonportable, but it's the principle.
cybermind is offline
Join Date: Nov 2005
Old 01-05-2006 , 01:28  
Reply With Quote #6

showdax - thanks for the tip, I'll add that to the list

awuh0 - http://www.hl2coding.com/forums/viewtopic.php?t=1056 someone posted a template for a plugin that uses cbase.h

BAILOPAN - thanks and yeah the sigscanning process could definitely be improved in terms of scanning for signatures (maybe using * for more than one byte matching) and the functionality as you proposed. I look forward to your next article.

CyberMind - yeah that's a much better way to call member functions. I'll have that changed in the next update for finding signatures.

This article would probably be better posted on a wiki somewhere, maybe something that can be thought about for sourcemod/sourcemm? ;)

Thanks for all your replies. I will improve upon the guide and add a section for contributers so nobody goes unnoticed.
SeLfkiLL is offline
Send a message via AIM to SeLfkiLL
Senior Member
Join Date: Oct 2004
Old 01-05-2006 , 01:51  
Reply With Quote #7

Despite there being "wikis, wikis everywhere!", one specifically for S:MM plugins would be good. There's lot to be learned by people starting fresh that could be put in a single place. I doubt Valve would enjoy having a section on their official HL2 wiki for S:MM plugins .
cybermind is offline
Veteran Member
Join Date: May 2006
Old 01-06-2006 , 00:03  
Reply With Quote #8

EDIT: Nevermind.
API is offline
Send a message via AIM to API
Senior Member
Join Date: Oct 2004
Old 01-06-2006 , 00:15  
Reply With Quote #9

Once you run InitSigs(), the address of the function is in CBase_RoundRespawn_Sig.sig_addr.
cybermind is offline
Senior Member
Join Date: Dec 2004
Location: san frandisco
Old 01-08-2006 , 14:00  
Reply With Quote #10

No love for me? Well thanks a hell of a lot.
Avoid like the plague.
vancelorgin is offline

Thread Tools
Display Modes

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 18:03.

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