Raised This Month: $51 Target: $400
 12% 

Sig lengths


Post New Thread Reply   
 
Thread Tools Display Modes
Author Message
[email protected]
Member
Join Date: Dec 2005
Old 12-29-2005 , 21:28   Sig lengths
Reply With Quote #1

Just curious what you guys think about the length you should use for sigs when using a sigscanner. It's easy enough to find the functions if you know what you're doing, and to mask out the bytes that could change when valve puts out an update. For example, I could have a sig for CBasePlayer::CommitSuicide( ) that is up to 129 bytes long. But that would be silly. Now, I could also have it as little as 14 bytes (any less and i get multiple matches, using the mask I have). Now it would make sense to go at least a little bit (no pun intended) past the least ammount, in case the dll gets changed and some other functions match more closely. But you don't want to make it too long, because then it's more likely that a change in that function will change the sig, and then you no longer can find it with a scan.

Right now I'm just going an extra 10 bytes past the minimum to identify the function, but I just thought I'd see what anyone else thought was a good idea.
Lojo.jacob@gmail.com is offline
c0ldfyr3
AlliedModders Donor
Join Date: Aug 2005
Location: Ireland
Old 12-30-2005 , 02:24  
Reply With Quote #2

IMO: Most Valve updates require at least one sig change anyway, so just use what you think and change whatever is needed each update.
__________________
c0ldfyr3 is offline
Send a message via MSN to c0ldfyr3 Send a message via Yahoo to c0ldfyr3
[email protected]
Member
Join Date: Dec 2005
Old 12-30-2005 , 16:04  
Reply With Quote #3

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)
Lojo.jacob@gmail.com is offline
c0ldfyr3
AlliedModders Donor
Join Date: Aug 2005
Location: Ireland
Old 12-30-2005 , 18:58  
Reply With Quote #4

Most of my sigs are just 16 and the all work fine, for now. But if you look at Fyshs examples in the nemod source, he uses your method yet now none work.

So, as I said, it wont matter how ingenuitive you are, it will still probably require a recompile for some reason or another on each update anyway, and it isnt very hard to find a few sigs again.
But thats just me
__________________
c0ldfyr3 is offline
Send a message via MSN to c0ldfyr3 Send a message via Yahoo to c0ldfyr3
SeLfkiLL
Member
Join Date: Nov 2005
Old 01-01-2006 , 00:52  
Reply With Quote #5

Thanks lojo.jacob, that's very helpful! And for anybody who doesn't understand what's going on, I found an excellent tutorial on Reverse Engineering which should clear up some things. Here's also a comprehensive guide on Assembly Language as well.
SeLfkiLL is offline
Send a message via AIM to SeLfkiLL
Reply



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 08:15.


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