PDA

View Full Version : Pro tips for hooking up the function



Whiskas
2nd May 2016, 23:11
I have an issue with seg faults. I would like to know more about the address where "magic" happens. The only way I know to learn something about my address is hooking it up in libcod and add some printfs to see what variables are there and how long function works until its death. To learn something about hooking up I checked few examples from libcod like these:

http://pastebin.com/PNzExehb was hooked up as http://pastebin.com/4qbJNiYZ

I was impressed that anyone could determine what address is "gentityaddress_to_num()". As I am newb at rewritting functions, I would like to ask you for any tips I should know before I start. Also I would be thankful if anyone could share the list (or database) of known functions (addresses).

Thank you in advance

kung foo man
3rd May 2016, 00:22
You don't need to let it crash, you can unhook a hooked function in itself, call itself, save the return value, rehook it and just return the saved value, so for the engine it's like nothing happened.

Example from



static int size_all = 0;
static int i = 0;
cHook *hook_MSG_WriteBigString;
void MSG_WriteBigString(int *MSG, char *s)
{

int len;

len = strlen(s);
printf("i=%d size_all=%d len=%d MSG=%p %s\n", i, size_all, len, MSG, s);

size_all += len;
i++;

hook_MSG_WriteBigString->unhook();

void (*sig)(int *MSG, char *s);
*(int *)&sig = 0x0806825E;
sig(MSG, s);

hook_MSG_WriteBigString->hook();

}


And instead of writing the hardcoded address (*(int *)&sig = 0x0806825E;), you can take the value of hook_MSG_WriteBigString->from.

For finding stuff you want: e.g. at the start of libcod I wanted to find a script function ("closer" because it was easy to reimplement and I actually never used it) and I had no clue about anything, since cracking was totally new to me. I started with some C knowledge and WinHex, like this:

At first I searched the plain string in WinHex: lets say it was at file offset 0x12345

Then you go to that file address in IDA (some option in menu) and then IDA shows you the actual memory address of that file offset, lets say 0xaabbccdd is "closer". But the address wasn't linked anywhere, but it had to be linked somewhere I thought, so I searched the hex numbers of it in WinHex with no success, till I reversered them (0xddccbbaa), since x86 is using little-endianness (https://en.wikipedia.org/wiki/Endianness). That way I found the actual table which hold the string->function address relations and I could slowly decompile all kind of glued C function to the script engine e.g.

On the other hand you could just search an error string from anything in WinHex too and enter the file offset in IDA again. Since IDA has analyzed a lot from beginning, it can show all found function which point to the error message then.

And don't forget all the GPL engines, RTCW, Quake 3 etc., which all have a lot of code in common.

voron00
4th May 2016, 06:42
I didn't fully understand the magic stuff and database thing but apparently you want to find what causes the segfault.

When a program crashes it should generate a core dump file. For more or less modern systems it should be in /var/lib/systemd/coredump core.cod2_lnxded.1000.etc.

Move this core dump to your home dir, if its compressed (lz4), unlz4 it (unlz4 filename.lz4), move the uncompressed core dump to your cod2_lnxded dir and start GDB like:
gdb cod2_lnxded corefilename

You should see the crash now (if not, type 'bt'). The adress where it's crashed should be the toppest one. Search this adress in ida and it should point you the spot of the crash (proably will be inside of some sub).

If you can't understand what exactly this function does you proably want to hook it.

Hooking is quite easy, there are multiple ways to do it. Basicly you redirect this function, do some stuff e.g read it's params and call the original function. You can hook a function, function call or use a cHook class.

For hooking a function, use the way kung posted above, for a function call there is alot of references in libcod.cpp just search cracking_hook_call but basicly it works like:



cracking_hook_call(0x0812A3EC, (int)hook_test_func);


void hook_test_func(int a1, int a2) {

printf("Test: %i, %i", a1, a2);

int (*Orig)(int a1, int a2);
*(int *)&Orig = 0x0812A004; // The real adress of a function you are trying to hook as a call

Orig(a1, a2);

}



1. Hook the call
2. Get stuff and do stuff
3. Call the original

You can find where the function e.g sub_812A004 is called by just searching it as text in IDA and you will find something like:


.text:0812A3EC call sub_812A004

Where 0812A3EC is the hook call adress.

You don't have to call the original function if you are rewriting it as a reverse, for this you can also use cracking_hook_function but do NOT use craacking_hook_function if you are calling the original inside a hooked function. Reversing though can be a lot of pain in the arse. As kung said above you can look at q3 source to see some useful references.

BTW: I had a segfaults with playFxOnTag() function, which was related to models without that tag in some way, also feel free to upload your coredump file, or share the code where that crash is so we can look for it aswell.

kung foo man
4th May 2016, 07:14
Hooking is quite easy, there are multiple ways to do it. Basicly you redirect this function, do some stuff e.g read it's params and call the original function. You can hook a function, function call or use a cHook class (which can be more handy than usual cracking_hook_function, but does the same thing atm).



Ye, but remember the "Original" function is still redirected, so when you call it without unhooking it, you are catched in a loop. Hence this little helper class:



cHook::cHook(int from, int to) {
this->from = from;
this->to = to;
}
void cHook::hook() {
memcpy((void *)oldCode, (void *)from, 5);
cracking_hook_function(from, to);
}
void cHook::unhook() {
memcpy((void *)from, (void *)oldCode, 5);
}


It saves the first 5 bytes just to restore them later to call the original function.

Whiskas
4th May 2016, 10:51
My core dump looks like this:


Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x080e983d in ?? ()
(gdb) bt
#0 0x080e983d in ?? ()
#1 0x0808f697 in ?? ()
#2 0x0808fa6f in ?? ()
#3 0x080943e1 in ?? ()
#4 0x08061779 in ?? ()
#5 0x08062451 in ?? ()
#6 0x0806251d in ?? ()
#7 0x080d2b22 in ?? ()
#8 0xb73c5a83 in __libc_start_main (main=0x80d2990, argc=12, argv=0xbfc54ac4,
init=0x8049b90, fini=0x8139b50, rtld_fini=0xb7760180 <_dl_fini>,
stack_end=0xbfc54abc) at libc-start.c:287
#9 0x0804a4d1 in ?? ()


Address 0x080e983d belongs to this function:


int __cdecl sub_80E97F0(int a1, int a2)
{
int v3; // [sp+Ch] [bp-Ch]@5
char v4; // [sp+17h] [bp-1h]@1

v4 = 1;
if ( !(unsigned __int8)sub_80E9758(a2) )
v4 = 0;
if ( !(unsigned __int8)sub_80D9E84(a1 + 1348, a2) )
v4 = 0;
v3 = sub_80E9270(a2);
if ( !*(_DWORD *)(v3 + 132)
&& *(_BYTE *)(a1 + 1365) != a2
&& *(_BYTE *)(a1 + 1366) != a2
&& *(_DWORD *)(v3 + 876) != a2 )
{
v4 = 0;
}
return (unsigned __int8)v4;
}

The problem seems to be in an IF statement as it's right after calling the sub_80E9270 function. Who knows, maybe this function doesn't return anything to v3 variable.


.text:080E9832 call sub_80E9270
.text:080E9837 mov [ebp+var_C], eax
.text:080E983A mov eax, [ebp+var_C]
.text:080E983D cmp dword ptr [eax+84h], 0 //HERE
.text:080E9844 jnz short loc_80E987C
.text:080E9846 mov eax, [ebp+var_C]
.text:080E9849 mov eax, [eax+36Ch]

If I want to hook up int __cdecl sub_80E97F0(int a1, int a2) function I should write in libcod address where the function begins?

.text:080E97F0 sub_80E97F0 proc near ; CODE XREF: sub_808F510+182p

Like this?:


cracking_hook_call(0x080E97F0, (int)hook_my_Problem);

Then I just would like to know what params contain and v3 variable..



int hook_my_Problem(int a1, int a2)
{
printf("Param a1 == %i Param a2 == %i \n", a1, a2);
hook_my_Problem->unhook();
}

How can I hook right after the call of sub_80E9270 to just print v3 variable?

Something like this?


int hook_my_Problem(int a1, int a2)
{
printf("Param a1 == %i Param a2 == %i \n", a1, a2);
hook_my_Problem->unhook();
int v3;
void (*sig)(int a1, itn a2);
*(int *)&sig = 0x080E9832; //the sub_80E9270 call
sig(a1, a2);

hook_my_Problem->hook();
v3 = sub_80E9270(a2);
printf("Param V3 == %i \n", v3);

hook_my_Problem->unhook();
}

Btw. Should this function be void as it returns nothing? If so should I change aslo cracking_hook_call(0x080E97F0, (int)hook_my_Problem); to (viod)hook_my_Problem?

Thanks for your help!

voron00
4th May 2016, 11:46
Ok so the crash is in the BG_IsWeaponValid, which is (i guess ) related to weapons. You proably need to check them out. And this function is small so why not just revere it, going to be a piece of cake, here we go:



cracking_hook_function(0x080E97F0, (int)hook_BG_IsWeaponValid);



int hook_BG_IsWeaponValid(int a1, int a2) {

int v3;
char v4;

signed int (*sub_80E9758)(int a1);
*(int *)&sub_80E9758 = 0x80E9758;

int (*sub_80D9E84)(int a1, signed int a2);
*(int *)&sub_80D9E84 = 0x80D9E84;

int (*sub_80E9270)(int a1);
*(int *)&sub_80E9270 = 0x80E9270;

v4 = 1;
if ( !(unsigned int8_t)sub_80E9758(a2) )
v4 = 0;
if ( !(unsigned int8_t)sub_80D9E84(a1 + 1348, a2) )
v4 = 0;
v3 = sub_80E9270(a2);
if ( !*(long *)(v3 + 132) && *(char *)(a1 + 1365) != a2 && *(char *)(a1 + 1366) != a2 && *(long *)(v3 + 876) != a2 )
v4 = 0;

// printf("Testcall\n");

return (unsigned int8_t)v4;
}


I have no idea why it segfaults though, and it's called from a SV_UserMove (possibly every player's frame). Try playing with this code, it could be that v3 e.g weapon + its somewhat parameter is broken or something.

voron00
4th May 2016, 12:35
Also maybe put:

printf("Last weapon used: %s\n", *(char**)(v3 + 0));

To see which weapon has crashed the server (this would be the last message before the crash then). Have you tried reproducing it somehow? How does it happens anyway?

Whiskas
4th May 2016, 13:14
int hook_BG_IsWeaponValid(int a1, int a2) {

int v3;
char v4;

signed int (*sub_80E9758)(int a1);
*(int *)&sub_80E9758 = 0x80E9758;

int (*sub_80D9E84)(int a1, signed int a2);
*(int *)&sub_80D9E84 = 0x80D9E84;

int (*sub_80E9270)(int a1);
*(int *)&sub_80E9270 = 0x80E9270;

v4 = 1;
if ( !(unsigned int8_t)sub_80E9758(a2) )
v4 = 0;
if ( !(unsigned int8_t)sub_80D9E84(a1 + 1348, a2) )
v4 = 0;
v3 = sub_80E9270(a2);
if ( !*(long *)(v3 + 132) && *(char *)(a1 + 1365) != a2 && *(char *)(a1 + 1366) != a2 && *(long *)(v3 + 876) != a2 )
v4 = 0;

// printf("Testcall\n");

return (unsigned int8_t)v4;
}



Sooo for the short functions we can only change types of variables and it will work out anyway? Whoa. :eek:

But where did it came from?


signed int (*sub_80E9758)(int a1);
*(int *)&sub_80E9758 = 0x80E9758;

int (*sub_80D9E84)(int a1, signed int a2);
*(int *)&sub_80D9E84 = 0x80D9E84;

int (*sub_80E9270)(int a1);
*(int *)&sub_80E9270 = 0x80E9270;

My _weapons.gsc file is nothing special. I've only added function deleting machine guns and added precaching every rifle for every team. http://pastebin.com/yKg1RfBb

I've added some prints to your hook function. It compiled properly (Thanks!). Just waiting for the server to become empty so I can implement new libcod.

Whiskas
4th May 2016, 13:23
Also maybe put:

printf("Last weapon used: %s\n", *(char**)(v3 + 0));

To see which weapon has crashed the server (this would be the last message before the crash then). Have you tried reproducing it somehow? How does it happens anyway?

Sorry as I was writting my last post there wasn't your second post.


int hook_BG_IsWeaponValid(int a1, int a2) {

printf("hook_BG_IsWeaponValid -- Starting \n");
printf("hook_BG_IsWeaponValid -- a1 == %i a2 == %i \n", a1, a2);

int v3;
char v4;

signed int (*sub_80E9758)(int a1);
*(int *)&sub_80E9758 = 0x80E9758;

int (*sub_80D9E84)(int a1, signed int a2);
*(int *)&sub_80D9E84 = 0x80D9E84;

int (*sub_80E9270)(int a1);
*(int *)&sub_80E9270 = 0x80E9270;

v4 = 1;
if ( !(unsigned int8_t)sub_80E9758(a2) )
v4 = 0;
if ( !(unsigned int8_t)sub_80D9E84(a1 + 1348, a2) )
v4 = 0;
v3 = sub_80E9270(a2);
printf("hook_BG_IsWeaponValid -- sub_80E9270(a2) = v3 == %i \n", v3);
printf("hook_BG_IsWeaponValid -- Checking fi anything changed a1 == %i a2 == %i \n", a1, a2);
if ( !*(long *)(v3 + 132) && *(char *)(a1 + 1365) != a2 && *(char *)(a1 + 1366) != a2 && *(long *)(v3 + 876) != a2 )
v4 = 0;

printf("Finished hook_BG_IsWeaponValid \n");

return (unsigned int8_t)v4;
}

Should I place your printf("Last weapon used: %s\n", *(char**)(v3 + 0)); right after v3 = sub_80E9270(a2); ? Why is it even a char? :eek: Mine prints are bad then?

I wass trying to reproduce it by disconnecting the server with opened weapon menu, but no success. Seg fault is quite rare, about one per day, but I had none since 2 days now.

There is nothing special when it happens.


^9(GAME_AXIS)Lazlow B^7: QUICKMESSAGE_SORRY
^9(GAME_AXIS)Lazlow B^7: QUICKMESSAGE_SORRY
Client 50 connecting with 100 challenge ping from 41.227.135.167:11431
Going from CS_FREE to CS_CONNECTED for (num 0 guid 0)
^9(GAME_AXIS)Lazlow B^7: QUICKMESSAGE_SORRY
^9(GAME_DEAD)Ndrecke24^7: SORRY JB
^9(GAME_AXIS)Lazlow B^7: QUICKMESSAGE_SORRY
Segmentation fault (core dumped)



Client 11 connecting with 150 challenge ping from 39.32.79.134:28960
Going from CS_FREE to CS_CONNECTED for (num 2 guid 0)
0: EXE_DISCONNECTED
clientDownload: 2 : begining "whiskys.lair2/zzz_rifle_only.iwd"
clientDownload: 2 : file "whiskys.lair2/zzz_rifle_only.iwd" completed
clientDownload: 2 : begining "whiskys.lair2/zzz_modz.iwd"
clientDownload: 2 : file "whiskys.lair2/zzz_modz.iwd" completed
Client 12 connecting with 150 challenge ping from 41.111.108.187:15369
Going from CS_FREE to CS_CONNECTED for (num 0 guid 0)
Segmentation fault (core dumped)

voron00
4th May 2016, 13:40
The prints are ok but you wont get any useful info from that as it will return the same integer its char becasue that will return the name of weapon e.g m1garand_mp, and ye it must be right after v3 = sub_80E9270(a2);

Whiskas
4th May 2016, 14:00
For the research purposes:

What this 0 ( printf("Last weapon used: %s\n", *(char**)(v3 + 0)); ) stands for ?

Can I printf other ints (a1, a2) this way?

I was never good at pointers, what does *(char**) do? Googling it provides only info about char itself..

IzNoGoD
4th May 2016, 14:45
*(char**)foo casts foo to a char-pointer-pointer. char-pointers are basically arrays of characters, more commonly referred to as strings. So a char-pointer-pointer is a string-pointer. The * in front of it dereferences the pointer, so it dereferences a char** into a char*.

The +0 is probably a leftover from trying different addresses near it.

Whiskas
6th May 2016, 00:10
Finally(?) got seg fault with your libcod's function:


[45709.850790] cod2_lnxded_1_0[1722]: segfault at 0 ip b7725b42 sp bf9a7330 error 4 in libcod2_1_0_hookLessSpam.so[b7711000+20000]
[45711.750596] UDP: bad checksum. From 197.1.146.216:28960 to 89.36.219.214:28960 ulen 45
[45715.700044] UDP: bad checksum. From 197.1.146.216:28960 to 89.36.219.214:28960 ulen 54
[45735.651877] UDP: bad checksum. From 197.1.146.216:28960 to 89.36.219.214:28960 ulen 77
[45739.513702] UDP: bad checksum. From 197.1.146.216:28960 to 89.36.219.214:28960 ulen 122


And the screen's log:


hook_BG_IsWeaponValid -- Last weapon used: enfield_scope_mp
hook_BG_IsWeaponValid -- Last weapon used: enfield_scope_mp
hook_BG_IsWeaponValid -- Last weapon used: enfield_scope_mp
hook_BG_IsWeaponValid -- Last weapon used: enfield_scope_mp
hook_BG_IsWeaponValid -- Last weapon used: none
hook_BG_IsWeaponValid -- Last weapon used: enfield_scope_mp
hook_BG_IsWeaponValid -- Last weapon used: none
hook_BG_IsWeaponValid -- Last weapon used: enfield_scope_mp
hook_BG_IsWeaponValid -- Last weapon used: none
hook_BG_IsWeaponValid -- Last weapon used: enfield_scope_mp
hook_BG_IsWeaponValid -- Last weapon used: none
hook_BG_IsWeaponValid -- Last weapon used: kar98k_mp
hook_BG_IsWeaponValid -- Last weapon used: none
hook_BG_IsWeaponValid -- Last weapon used: none
hook_BG_IsWeaponValid -- Last weapon used: none
hook_BG_IsWeaponValid -- Last weapon used: none
hook_BG_IsWeaponValid -- Last weapon used: none
hook_BG_IsWeaponValid -- Last weapon used: none
hook_BG_IsWeaponValid -- Last weapon used: none
hook_BG_IsWeaponValid -- Last weapon used: none
hook_BG_IsWeaponValid -- Last weapon used: none
hook_BG_IsWeaponValid -- Last weapon used: none
hook_BG_IsWeaponValid -- Last weapon used: none
hook_BG_IsWeaponValid -- Last weapon used: kar98k_sniper_mp
hook_BG_IsWeaponValid -- Last weapon used: webley_mp
Segmentation fault (core dumped)
==854==
==854== HEAP SUMMARY:
==854== in use at exit: 532 bytes in 27 blocks
==854== total heap usage: 33 allocs, 6 frees, 1,137 bytes allocated
==854==
==854== 16 bytes in 1 blocks are still reachable in loss record 1 of 5
==854== at 0x482A17C: malloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==854== by 0x1140CA: ??? (in /bin/dash)
==854== by 0x11B2FD: ??? (in /bin/dash)
==854== by 0x11BD98: ??? (in /bin/dash)
==854== by 0x10AD6F: main (in /bin/dash)
==854==
==854== 16 bytes in 1 blocks are still reachable in loss record 2 of 5
==854== at 0x482A17C: malloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==854== by 0x48BC9C7: strdup (strdup.c:42)
==854== by 0x11414A: ??? (in /bin/dash)
==854== by 0x10C179: ??? (in /bin/dash)
==854== by 0x11BDC3: ??? (in /bin/dash)
==854== by 0x10AD6F: main (in /bin/dash)
==854==
==854== 20 bytes in 1 blocks are still reachable in loss record 3 of 5
==854== at 0x482A17C: malloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==854== by 0x1140CA: ??? (in /bin/dash)
==854== by 0x11B3E6: ??? (in /bin/dash)
==854== by 0x10C1C0: ??? (in /bin/dash)
==854== by 0x11BDC3: ??? (in /bin/dash)
==854== by 0x10AD6F: main (in /bin/dash)
==854==
==854== 112 bytes in 1 blocks are still reachable in loss record 4 of 5
==854== at 0x482A17C: malloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==854== by 0x482C3AF: realloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==854== by 0x114112: ??? (in /bin/dash)
==854== by 0x1135D9: ??? (in /bin/dash)
==854== by 0x10DCD0: ??? (in /bin/dash)
==854== by 0x10CA06: ??? (in /bin/dash)
==854== by 0x113E37: ??? (in /bin/dash)
==854== by 0x10ADEF: main (in /bin/dash)
==854==
==854== 368 bytes in 23 blocks are still reachable in loss record 5 of 5
==854== at 0x482A17C: malloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==854== by 0x1140CA: ??? (in /bin/dash)
==854== by 0x11B2FD: ??? (in /bin/dash)
==854== by 0x11BD53: ??? (in /bin/dash)
==854== by 0x10AD6F: main (in /bin/dash)
==854==
==854== LEAK SUMMARY:
==854== definitely lost: 0 bytes in 0 blocks
==854== indirectly lost: 0 bytes in 0 blocks
==854== possibly lost: 0 bytes in 0 blocks
==854== still reachable: 532 bytes in 27 blocks
==854== suppressed: 0 bytes in 0 blocks
==854==
==854== For counts of detected and suppressed errors, rerun with: -v
==854== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
==12416== Memcheck, a memory error detector
==12416== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==12416== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==12416== Command: ./start
==12416==
> [INFO] Compiled for: CoD2 1.0
Compiled: May 4 2016 17:05:07 using GCC 4.8.4
> [PLUGIN LOADED]
CoD2 MP 1.0 build linux-i386 Oct 24 2005
----- FS_Startup -----
Current search path:

I don't really think that webley is causing the problem :|. And yes it was used before without segfault.

kung foo man
6th May 2016, 07:57
Based on vorons reversed function, lets just eliminate all possible segfaults (four dereferences of pointers in the if):



int hook_BG_IsWeaponValid(int a1, int a2) {

int v3;
char v4;

signed int (*sub_80E9758)(int a1);
*(int *)&sub_80E9758 = 0x80E9758;

int (*sub_80D9E84)(int a1, signed int a2);
*(int *)&sub_80D9E84 = 0x80D9E84;

int (*sub_80E9270)(int a1);
*(int *)&sub_80E9270 = 0x80E9270;

v4 = 1;
if ( !(unsigned int8_t)sub_80E9758(a2) )
v4 = 0;
if ( !(unsigned int8_t)sub_80D9E84(a1 + 1348, a2) )
v4 = 0;
v3 = sub_80E9270(a2);

if ((long *)(v3 + 132) == NULL)
{
printf("this would later crash 1\n");
return 0;
}
if ((char *)(a1 + 1365) == NULL)
{
printf("this would later crash 2\n");
return 0;
}
if ((char *)(a1 + 1366) == NULL)
{
printf("this would later crash 3\n");
return 0;
}
if ((long *)(v3 + 876) == NULL)
{
printf("this would later crash 4\n");
return 0;
}

if ( !*(long *)(v3 + 132) && *(char *)(a1 + 1365) != a2 && *(char *)(a1 + 1366) != a2 && *(long *)(v3 + 876) != a2 )
v4 = 0;

// printf("Testcall\n");

return (unsigned int8_t)v4;
}


Edit: and if nothing helps, you might even just ignore the segfault: http://stackoverflow.com/questions/8456085/why-cant-i-ignore-sigsegv-signal

Whiskas
6th May 2016, 11:13
Based on vorons reversed function, lets just eliminate all possible segfaults (four dereferences of pointers in the if):



int hook_BG_IsWeaponValid(int a1, int a2) {

int v3;
char v4;

signed int (*sub_80E9758)(int a1);
*(int *)&sub_80E9758 = 0x80E9758;

int (*sub_80D9E84)(int a1, signed int a2);
*(int *)&sub_80D9E84 = 0x80D9E84;

int (*sub_80E9270)(int a1);
*(int *)&sub_80E9270 = 0x80E9270;

v4 = 1;
if ( !(unsigned int8_t)sub_80E9758(a2) )
v4 = 0;
if ( !(unsigned int8_t)sub_80D9E84(a1 + 1348, a2) )
v4 = 0;
v3 = sub_80E9270(a2);

if ((long *)(v3 + 132) == NULL)
{
printf("this would later crash 1\n");
return 0;
}
if ((char *)(a1 + 1365) == NULL)
{
printf("this would later crash 2\n");
return 0;
}
if ((char *)(a1 + 1366) == NULL)
{
printf("this would later crash 3\n");
return 0;
}
if ((long *)(v3 + 876) == NULL)
{
printf("this would later crash 4\n");
return 0;
}

if ( !*(long *)(v3 + 132) && *(char *)(a1 + 1365) != a2 && *(char *)(a1 + 1366) != a2 && *(long *)(v3 + 876) != a2 )
v4 = 0;

// printf("Testcall\n");

return (unsigned int8_t)v4;
}



Whoa, thanks. Added and waiting for potential segfault.

Let's imagine that some of these pointers will turn null and function will return 0. What happens next? What does engine do? It pretends like nothing really happened? :D



Edit: and if nothing helps, you might even just ignore the segfault: http://stackoverflow.com/questions/8456085/why-cant-i-ignore-sigsegv-signal

In case your function doesn't solve the problem, does libcod has it's own signal() function? Or shall I add signal.h to the libcod's repository? :)

voron00
8th May 2016, 13:05
Ookay..I just had the same segfaults today. And i almost know how it happens: 2 guys, with the same ip(!), GUID 0(!). 1 of them crashes with the EXE_LOSTRELIABLECOMMANDS (i didn't really get this part). Then he joins a server again with another name(!) and server crashes (lol). Need to somehow reproduce this.
EDIT: Arrghh this is getting outta hand, and they somehow managing to do it when im not on server...
1105

Whiskas
8th May 2016, 16:00
Maybe this could help? :)

https://killtube.org/showthread.php?1767-Q3-FIX-Q3-fake-clients-fix&p=11941&viewfull=1#post11941

voron00
8th May 2016, 16:15
Nah, i don't want to lose a playersbase. There are alot of people with the same ip's at this time. Will try kung's code later.

Whiskas
8th May 2016, 16:45
Nah, i don't want to lose a playersbase. There are alot of people with the same ip's at this time. Will try kung's code later.

I've already had 3 segfaults with Kungs code. Strange thing that none of these has printed "this would later crash 1\n". Going to add more prints to find out when does it crash..

kung foo man
8th May 2016, 17:36
Do you have a new core dump? The crash should be on another address now, since the old function is overwritten (just to check if it was really hooked).

Whiskas
8th May 2016, 17:40
Do you have a new core dump? The crash should be on another address now, since the old function is overwritten (just to check if it was really hooked).

Yes I do:



Core was generated by `./cod2_lnxded_1_0 +set net_port 28960 +set dedicated 2 +set fs_game whiskys.lai'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0xb76d9b42 in ?? ()
(gdb) bt
#0 0xb76d9b42 in ?? ()
#1 0x00000057 in ?? ()
#2 0x00000057 in ?? ()
#3 0xbff2a668 in ?? ()
#4 0x0808f697 in ?? ()
#5 0x0808fa6f in ?? ()
#6 0x080943e1 in ?? ()
#7 0x08061779 in ?? ()
#8 0x08062451 in ?? ()
#9 0x0806251d in ?? ()
#10 0x080d2b22 in ?? ()
#11 0xb73e7a83 in __libc_start_main (main=0x80d2990, argc=12, argv=0xbff2ecc4,
init=0x8049b90, fini=0x8139b50, rtld_fini=0xb7782180 <_dl_fini>,
stack_end=0xbff2ecbc) at libc-start.c:287
#12 0x0804a4d1 in ?? ()


Not sure how to find such address in libcod's file

Edit: Hmm, core dump says its made before your code was implemented (It was voron's version without your if statements). Not sure if core file is overwritten at every crash.

kung foo man
8th May 2016, 18:16
Hm, I wonder if #0 is really the last executed opcode address (program counter, aka PC), since the backtrace should probably be a bit more similiar. Can you post the output of layout asm in gdb?


Should look something like this:



┌───────────────────────────────────────────────── ──────────────────────────┐
│0x7ffff740d756 <__libc_start_main+214> mov 0x39670b(%rip),%rax #│
│0x7ffff740d75d <__libc_start_main+221> mov 0x8(%rsp),%rsi │
│0x7ffff740d762 <__libc_start_main+226> mov 0x14(%rsp),%edi │
│0x7ffff740d766 <__libc_start_main+230> mov (%rax),%rdx │
│0x7ffff740d769 <__libc_start_main+233> callq *0x18(%rsp) │
>│0x7ffff740d76d <__libc_start_main+237> mov %eax,%edi │
│0x7ffff740d76f <__libc_start_main+239> callq 0x7ffff7427970 <exit> │
│0x7ffff740d774 <__libc_start_main+244> xor %edx,%edx │
│0x7ffff740d776 <__libc_start_main+246> jmpq 0x7ffff740d6b9 <__libc_start│
│0x7ffff740d77b <__libc_start_main+251> mov 0x39ca2e(%rip),%rax #│
│0x7ffff740d782 <__libc_start_main+258> ror $0x11,%rax │
│0x7ffff740d786 <__libc_start_main+262> xor %fs:0x30,%rax │
│0x7ffff740d78f <__libc_start_main+271> callq *%rax │
└───────────────────────────────────────────────── ──────────────────────────┘
multi-thre process 3718 In: __libc_start_main Line: ?? PC: 0x7ffff740d76d
#3 0x00007ffff7466eb5 in _IO_do_write () from /lib/x86_64-linux-gnu/libc.so.6
#4 0x00007ffff74671ff in _IO_file_overflow ()
from /lib/x86_64-linux-gnu/libc.so.6
#5 0x0000000000408756 in ?? ()
#6 0x0000000000403980 in ?? ()
#7 0x00007ffff740d76d in __libc_start_main ()
from /lib/x86_64-linux-gnu/libc.so.6
(gdb)

Whiskas
8th May 2016, 18:27
lqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq qqqqqqqqqqqqqqqqqqqqqqqqqqqqqk
x x
x x
x x
x x
x x
x x
x [ No Assembly Available ] x
x x
x x
x x
x x
x x
x x
mqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq qqqqqqqqqqqqqqqqqqqqqqqqqqqqqj
multi-thre Thread 0xb7099 In: Line: ?? PC: 0xb76d9b42
Cannot access memory at address 0xb76d9b42
(gdb)

voron00
8th May 2016, 19:12
1106
Original crash, without overwriting in libcod.

Whiskas
9th May 2016, 23:58
Got some news about the source of the segfault.

Kungs code didn't worked, cause if statement is not causing server to crash..

I've added few more prints:


int hook_BG_IsWeaponValid(int a1, int a2) {

printf("hook_BG_IsWeaponValid -- Begin\n");
printf("hook_BG_IsWeaponValid -- 1 a1 : %i, a2 : %i \n", a1, a2);

int v3;
char v4;

signed int (*sub_80E9758)(int a1);
*(int *)&sub_80E9758 = 0x80E9758;

printf("hook_BG_IsWeaponValid -- 2 a1 : %i \n", a1);


int (*sub_80D9E84)(int a1, signed int a2);
*(int *)&sub_80D9E84 = 0x80D9E84;

printf("hook_BG_IsWeaponValid -- 3 a1 : %i, a2 : %i \n", a1, a2);

int (*sub_80E9270)(int a1);
*(int *)&sub_80E9270 = 0x80E9270;
printf("hook_BG_IsWeaponValid -- 4 a1 : %i \n", a1);

v4 = 1;
if ( !(unsigned int8_t)sub_80E9758(a2) )
v4 = 0;
printf("hook_BG_IsWeaponValid -- 5 a2 : %i \n", a2);
if ( !(unsigned int8_t)sub_80D9E84(a1 + 1348, a2) )
v4 = 0;
printf("hook_BG_IsWeaponValid -- 6 a2 : %i \n", a2);
v3 = sub_80E9270(a2);
printf("hook_BG_IsWeaponValid -- Last weapon used: %s\n", *(char**)(v3 + 0));
if ((long *)(v3 + 132) == NULL)
{
printf("this would later crash 1\n");
return 0;
}
if ((char *)(a1 + 1365) == NULL)
{
printf("this would later crash 2\n");
return 0;
}
if ((char *)(a1 + 1366) == NULL)
{
printf("this would later crash 3\n");
return 0;
}
if ((long *)(v3 + 876) == NULL)
{
printf("this would later crash 4\n");
return 0;
}
printf("hook_BG_IsWeaponValid -- After Kungs IF statements \n");
if ( !*(long *)(v3 + 132) && *(char *)(a1 + 1365) != a2 && *(char *)(a1 + 1366) != a2 && *(long *)(v3 + 876) != a2 )
v4 = 0;
printf("hook_BG_IsWeaponValid -- After IF statement \n");
// printf("Testcall\n");

return (unsigned int8_t)v4;
}

Log's of two last segfaults:


hook_BG_IsWeaponValid -- 6 a2 : 0
hook_BG_IsWeaponValid -- Last weapon used: none
hook_BG_IsWeaponValid -- After Kungs IF statements
hook_BG_IsWeaponValid -- After IF statement
hook_BG_IsWeaponValid -- Begin
hook_BG_IsWeaponValid -- 1 a1 : 141507876, a2 : 111
hook_BG_IsWeaponValid -- 2 a1 : 141507876
hook_BG_IsWeaponValid -- 3 a1 : 141507876, a2 : 111
hook_BG_IsWeaponValid -- 4 a1 : 141507876
hook_BG_IsWeaponValid -- 5 a2 : 111
hook_BG_IsWeaponValid -- 6 a2 : 111
Segmentation fault (core dumped)


hook_BG_IsWeaponValid -- After Kungs IF statements
hook_BG_IsWeaponValid -- After IF statement
hook_BG_IsWeaponValid -- Begin
hook_BG_IsWeaponValid -- 1 a1 : 141559896, a2 : 211
hook_BG_IsWeaponValid -- 2 a1 : 141559896
hook_BG_IsWeaponValid -- 3 a1 : 141559896, a2 : 211
hook_BG_IsWeaponValid -- 4 a1 : 141559896
hook_BG_IsWeaponValid -- 5 a2 : 211
hook_BG_IsWeaponValid -- 6 a2 : 211
Segmentation fault (core dumped)


As we can see it ends at v3 = sub_80E9270(a2); which is:


int __cdecl sub_80E9270(int a1)
{
return dword_8576160[a1];
}

According to this thread https://killtube.org/showthread.php?2067-Server-Crash&p=11792&viewfull=1#post11792 I'll have to pay more attention to weapon functions. Let you know if I found anything.

kung foo man
10th May 2016, 02:06
Ok, watching a bit longer the pointer logic going on here, I just realized my if's were total useless, they actually can't check anything. They basically add a number to 0 or greater and test if that number is still 0.

Lets name that function getWeaponStruct_80E9270 (based on Mitch's comment in the Server Crash thread).

Lets dissect:



int __cdecl getWeaponStruct_80E9270(int a1)
{
return dword_8576160[a1];
}


The [] is basically syntax sugar for:



int __cdecl getWeaponStruct_80E9270(int a1)
{
return *((int *)0x8576160 + a1);
}


Same game here, this function just cannot fail. It will always point to a valid array element address, implying your debug-printed a1 values of 111 and 211. It could only fail with very large numbers (either positive or negative).

The only reason you don't see the printf after that line, is because you dereference v3 in the next printf:



printf("hook_BG_IsWeaponValid -- Last weapon used: %s\n", *(char**)(v3 + 0));


And I guess that's the whole problem here, getWeaponStruct() probably returns 0.

So the only thing you need to check for in hook_BG_IsWeaponValid() should be:


if ( ! v3)
{
printf("Warning: getWeaponStruct(%d) returned 0!\n", a2);
return 0;
}

Whiskas
12th May 2016, 13:18
hook_BG_IsWeaponValid -- 5 a2 : 218
hook_BG_IsWeaponValid -- 6 a2 : 218
hook_BG_IsWeaponValid -- 7 After v3 = 80E9270(a2)
Warning: getWeaponStruct(218) returned 0!
hook_BG_IsWeaponValid -- 5 a2 : 218
hook_BG_IsWeaponValid -- 6 a2 : 218
hook_BG_IsWeaponValid -- 7 After v3 = 80E9270(a2)
Warning: getWeaponStruct(218) returned 0!
hook_BG_IsWeaponValid -- 5 a2 : 213
hook_BG_IsWeaponValid -- 6 a2 : 213
hook_BG_IsWeaponValid -- 7 After v3 = 80E9270(a2)
Warning: getWeaponStruct(213) returned 0!
hook_BG_IsWeaponValid -- 5 a2 : 213
hook_BG_IsWeaponValid -- 6 a2 : 213
hook_BG_IsWeaponValid -- 7 After v3 = 80E9270(a2)
Warning: getWeaponStruct(213) returned 0!

This totally solved my problem, thank you Kung! You are wise man.


P.S.
IMO, it should be added to libcod's repository :)

voron00
12th May 2016, 13:49
Great, i will add it soon, just need to find offsets for 1.2 and 1.3 too

Edit: Ok here it is: https://github.com/voron00/libcod/commit/88ee15a056b71540387e3a0b7c0a0ec3a4958125

Didn't test on 1.2 but offsets seems correct so