Hmmm, ok.
Well I thought I'd show everyone else what I did, so If you want to make sigs yourself, you can. Here's the code for CBasePlayer::CommitSuicide() as it is in the .dll (NOTE: This is for WINDOWS, not LINUX...I hear this is not necesary at all for linux):
First I'll talk about my mask: it's just a string of characters that tells my sig scanner whether to just skip that byte, or make sure it matches. I use '?' to say don't worry about that byte, and anything else to say, it needs to match, like say 'x'.
Code:
221F9860 CBasePlayer_CommitSuicide:
221F9860 83EC44 sub esp,00000044h
221F9863 56 push esi
221F9864 8BF1 mov esi,ecx
221F9866 83BE8C00000000 cmp dword ptr [esi+0000008Ch],00000000h
221F986D 756D jnz RETURN_CBasePlayer_CommitSuicide
221F986F 8B0D40CC4822 mov ecx,[2248CC40] ; ecx = gpGlobals
221F9875 D986380A0000 fld dword ptr [esi+00000A38h]
221F987B D8590C fcomp dword ptr [ecx+0Ch]
221F987E DFE0 fnstsw ax
221F9880 F6C441 test ah,41h
221F9883 7457 jz RETURN_CBasePlayer_CommitSuicide
221F9885 D9410C fld dword ptr [ecx+0Ch]
221F9888 D80544DF3522 fadd dword ptr [2235DF44]
221F988E D99E380A0000 fstp dword ptr [esi+00000A38h]
221F9894 83BE9C00000000 cmp dword ptr [esi+0000009Ch],00000000h
221F989B 7414 jz SUB_CBasePlayer_CommitSuicide_1
221F989D 8BCE mov ecx,esi
221F989F 8B01 mov eax,[ecx]
221F98A1 FF909C010000 call [eax+0000019Ch]
221F98A7 C7869C000000000000+ mov dword ptr [esi+0000009Ch],00000000h
221F98B1 SUB_CBasePlayer_CommitSuicide_1:
221F98B1 57 push edi
221F98B2 8B3E mov edi,[esi]
221F98B4 6A00 push 00000000h
221F98B6 6800100000 push 00001000h
221F98BB 6A00 push 00000000h
221F98BD 56 push esi
221F98BE 56 push esi
221F98BF 8D4C241C lea ecx,[esp+1Ch]
221F98C3 E868020600 call SUB_L22259B30
221F98C8 50 push eax
221F98C9 8BCE mov ecx,esi
221F98CB FF97E0000000 call [edi+000000E0h]
221F98D1 8B16 mov edx,[esi]
221F98D3 8BCE mov ecx,esi
221F98D5 FF9284030000 call [edx+00000384h]
221F98DB 5F pop edi
221F98DC RETURN_CBasePlayer_CommitSuicide:
221F98DC 5E pop esi
221F98DD 83C444 add esp,00000044h
221F98E0 C3 retn
Ok, now let's work through it:
Code:
221F9860 83EC44 sub esp,00000044h
221F9863 56 push esi
221F9864 8BF1 mov esi,ecx
These most likely won't ever change, the first line is just making 0x44 bytes of space reserved for this function on the stack, then saving the esi register, then storing ecx (wich contains the this pointer) into esi. So so far:
Code:
sig = "\x83\xEC\x44\x56\x8C\xF1";
mask = "xxxxxx";
Now just go throught the rest of the function:
Code:
221F9866 83BE8C00000000 cmp dword ptr [esi+0000008Ch],00000000h
This line is comparing the value at an offset of 0x8C fromthe this pointer to 0. That gives us this->IsAlive( ). This is where I would use a mask. If valve decides to change the class a little, the offset for this might change, so the new code for this function could be different next time:
Code:
sig = "\x83\xEC\x44\x56\x8C\xF1\x83\xBE\x8C\x00\x00\x00\x00";
mask = "xxxxxxxx????x";
Keep going...
Code:
221F986D 756D jnz RETURN_CBasePlayer_CommitSuicide
This is just a relative jump, so most likely this wont change. It would only change if the lenght of this function changed, and if valve changed that, it would mean a major rearrangment of the sig any way
Code:
sig = "\x75\x6D";
mask = "xx";
Moving on...
Code:
221F986F 8B0D40CC4822 mov ecx,[2248CC40]
This is interesting. It loads ecx with the value at 2248CC40. That would be the .dll's gpGlobals pointer. This could definately change around. All it is is the location of the global pointer. So:
Code:
sig = "\x8B\x0D\x40\xCC\x48\x22";
mask = "xx????";
Moving on:
Code:
221F9875 D986380A0000 fld dword ptr [esi+00000A38h]
221F987B D8590C fcomp dword ptr [ecx+0Ch]
Ok this loads the variable at offset 0xA38 from gpGlobals for floating point computation. That would be gpGlobals->curtime; Again, if they change the class, then this offset could change. Then it does a floating point comparison to the variable at offset 0x0C from the this pointer. That would be m_fNextSuicideTime. Once again, this offset could also change.
Code:
sig = "D9\x86\x38\x0A\x00\x00\xD8\x59\x0C";
mask = "xx????xx?";
Moving on:
Code:
221F987E DFE0 fnstsw ax
221F9880 F6C441 test ah,41h
221F9883 7457 jz RETURN_CBasePlayer_CommitSuicide
Ok this is where we find the resuld of the comparison. the first line loads the result from the FPU into ax, then the next line compares that result to 0x41 (0x41 mean the first value is less than the second..aka m_gNextSuicideTime > gpGlobals->curtime). then the next line says that if it is less than, jump to the end of this function...aka "return".
Code:
sig = "\xDF\xE0\xF6\xC4\x41\x74\x57";
mask = "xxxxxxx";
Moving on:
Code:
221F9885 D9410C fld dword ptr [ecx+0Ch]
221F9888 D80544DF3522 fadd dword ptr [2235DF44]
221F988E D99E380A0000 fstp dword ptr [esi+00000A38h]
Ok this loads the FPU with the variable at offset 0x0C from gpGlobals (gpGlobals->curtime). Then it adds the value at 2235DF44 to it (that value happens to be 5) Then it stores the result in offset 0xA38 to the this pointer (m_fNextSuicideTime) So:
Code:
sig = "\xD9\x41\x0C\xD8\x05\x44\xDF\x35\x22\xD9\x9E\x38\x0A\x00\x00";
mask = "xx?XX????xx????";
Moving on:
Code:
221F9894 83BE9C00000000 cmp dword ptr [esi+0000009Ch],00000000h
221F989B 7414 jz SUB_CBasePlayer_CommitSuicide_1
Ok, this compares the variable at offset 0x9C of the this pointer (m_iHealth) to 0, then if it doesn't equal 0, jumps to "SUB_CBa......". Basically, if m_iHealth equals 0 already, it will skip the part of the code that notifies all the clients that the player's health changed, because changing it from 0 to 0 ISN't changing it.
Code:
sig = "\x83\xBE\x9C\x00\x00\x00\x00\x74\x14";
mask = "xx????xxx";
Moving on:
Code:
221F989D 8BCE mov ecx,esi
221F989F 8B01 mov eax,[ecx]
221F98A1 FF909C010000 call [eax+0000019Ch]
221F98A7 C7869C000000000000+ mov dword ptr [esi+0000009Ch],00000000h
Ok, this loads the this pointer (esi) back into ecx, then loads the value at offset 0x00 from the this pointer into eax. The value at offset 0x00 is the address of the VTable for this class. Then it calls the function pointed to by offset 0x19C in the VTable. This is the whole reason why we're using sigs in the first place. Because Valve's updates are changing the VTable, but we aren't getting updated code, so our code tries to use the old offset here, when that offset is changed. As of the time I'm writing this, offset 0x19C point to the function CBasePlayer::NetworkStateChanged_m_iHealth(). This notifies all the clients that the player's health has changed. After that call, sets variable at offset 0x9C from this pointer (m_iHealth) to 0. So:
Code:
sig = "\x8B\xCE\x8B\x01\xFF\x90\x9C\x01\x00\x00\xC7\x86\x9C\x00\x00\x00\x00\x00\x00\x00\x00";
mask = "xxxxxx????xx????xxxx";
Moving on:
Code:
221F98B1 SUB_CBasePlayer_CommitSuicide_1:
221F98B1 57 push edi
221F98B2 8B3E mov edi,[esi]
221F98B4 6A00 push 00000000h
221F98B6 6800100000 push 001000000h
221F98BB 6A00 push 00000000h
221F98BD 56 push esi
221F98BE 56 push esi
221F98BF 8D4C241C lea ecx,[esp+1Ch]
221F98C3 E868020600 call 22259B30
Ok, here we go: This puhes edi on the stack (to save it), then loads the value at offset 0x00 from ecx (aka, address of the vtable again) into edi. Then it pushes 0x00000000, 0x00001000, 0x00000000, esi(this), esi onto the stack. then it then it loads offset 0x1C from the stack pointer into ecx. We're about to do a __thiscall: we're going to call CTakeDamageInfo( this, this, 0, DMG_NEVERGIB ), thats what all these pushes are for, passing the arguments. The [eps+1Ch] is using some of that space we allocated on the stack origonally back in the beginning to hold a CTakeDamageInfo object.
Code:
sig = "\x57\x8B\x3E\x6A\x00\x68\x00\x01\x00\x00\x00x6A\x00\x56\x56\x8D\x4C\x24\x1C\xE8\x68\x02\x06\x00";
mask = "xxxxxxxxxxxxxxxxx?x????";
Moving on:
Code:
221F98C8 50 push eax
221F98C9 8BCE mov ecx,esi
221F98CB FF97E0000000 call [edi+000000E0h]
221F98D1 8B16 mov edx,[esi]
221F98D3 8BCE mov ecx,esi
221F98D5 FF9284030000 call [edx+00000384h]
221F98DB 5F pop edi
Ok, this pushes eax, the return value from our previous call to the CTakeDamageInfo constructor, and puts that on the stack as a arguement for our next function call, which is offset 0xE0 from edi, aka offset 0xE0 on the CBasePlayer VTable, aka CBasePlayer::Event_Killed() Then we load edx with the value pointed to by offset 0 of esi (the address to the vtable again), load ecx with esi again (ecx = this again) and do another __thiscall: this time to offset 0x384 of the vtable, aka Event_Dying() then it restores edi, which was saved earlier.
Code:
sig = "\x50\x8B\xCE\xFF\x97\xE0\x00\x00\x00\x8B\x16\x8B\xCE\xFF\x92\x84\x03\x00\x00\x5F";
mask = "xxxxx????xxxxxx????x";
And finally:
Code:
221F98DC RETURN_CBasePlayer_CommitSuicide:
221F98DC 5E pop esi
221F98DD 83C444 add esp,00000044h
221F98E0 C3 retn
This restores esi, which was saved on the stack WAY back at the beginning, adds 0x44 back to the stack pointer, which frees up the space we allocated in it WAY back in the beginning, then returns.
Code:
sig = "\x5E\x83\xC4\x44\xC3";
mask = "xxxxx";
Now if you put all that together you get:
Code:
sig = "\x83\xEC\x44\x56\x8B\xF1\x83\xBE\x8C\x00\x00\x00\x00\x75
\x6D\x8B\x0D\x40\xCC\x48\x22\xD9\x86\x38\x0A\x00\x00\xD8
\x59\x0C\xDF\xE0\xF6\xC4\x41\x74\x57\xD9\x41\x0C\xD8\x05
\x44\xDF\x35\x22\xD9\x9E\x38\x0A\x00\x00\x83\xBE\x9C\x00
\x00\x00\x00\x74\x14\x8B\xCE\x8B\x01\xFF\x90\x9C\x01\x00
\x00\xC7\x86\x9C\x00\x00\x00\x00\x00\x00\x00\x57\x8B\x3E
\x6A\x00\x68\x00\x10\x00\x00\x6A\x00\x56\x56\x8D\x4C\x24
\x1C\xE8\x68\x02\x06\x00\x50\x8B\xCE\xFF\x97\xE0\x00\x00
\x00\x8B\x16\x8B\xCE\xFF\x92\x84\x03\x00\x00\x5F\x5E\x83
\xC4\x44\xC3";
mask = "xxxxxxxx????xxxxx????xx????xx?xxxxxxxxx?xx????xx????xx????
xxxxxxxxx????xx????xxxxxxxxxxxxxxxxxxxxx?x????xxxxx????xx
xxxx????xxxxxx";
and the length of the sig as 129
Ok that's a really long sig. So what I did was find the minimum length i could use, using the wildcards, and still locate only one function matching that sig. I need at least 14 bytes for that. Then just to be safe, I'm using 25 bytes. I include the whole sig in my plugin, and my sigscanner automatically uses a longer length if it finds multiple matches, or automatically uses a shorted length if it finds no matches (like if an update changed it, and part of the function changed, so use a less specific sig)