Skip to content

Instantly share code, notes, and snippets.

@em92
Last active April 14, 2020 09:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save em92/c6a6cd66aa354311265d10c19515dd06 to your computer and use it in GitHub Desktop.
Save em92/c6a6cd66aa354311265d10c19515dd06 to your computer and use it in GitHub Desktop.
#define BG_CANITEMBEGRABED 0x538F0
#define PMOVE 0x57EC0
#define CLIENTTHINK_REAL 0x59DF0
#define CA_ROUNDSTATETRANSITION 0x5FB00
#define CMD_CALLVOTE_F 0x6B160
#define TOSSCLIENTITEMS 0x6FC00
#define PLAYER_DIE 0x70310
#define G_DAMAGE 0x72010
#define TOUCH_ITEM 0x787C0
#define LAUNCHITEM 0x79AE0
#define DROP_ITEM 0x79D10
#define TELEPORTPLAYER 0x84EF0
#define G_EXPLODEMISSILE 0x859E0
#define G_RUNMISSILE 0x86350
// #define G_MISSILEIMPACT 0x86350
#define FIRE_ROCKET 0x87650
#define PICKUP_TEAM 0x96970
#define G_USETARGETS 0x97B50
#define G_SPAWN 0x97DB0
#define G_TEMPENTITY 0x980A0
#define KAMIKAZEDAMAGE 0x98C20
#define G_STARTKAMIKAZE 0x9BA30
@agkr234
Copy link

agkr234 commented Feb 7, 2020

Maybe "0x86350" is not the address of G_MissileImpact but the address of G_RunMissile.
I don't have enough knowledge to explain it, but I will write down my thoughts as much as I can.

1. The address of "0x86350" is called when I shoot a missile.

I added the following code into hook.c to check the condition where "0x86350" is called.

void __cdecl My_G_MissileImpact(gentity_t* ent, trace_t* trace) {
	G_MissileImpact(ent, trace);
	Com_Printf("G_MissileImpact\n");
}

Then, I shot a missile and then got the message specified in Com_Printf every frame.
A picture is worth a thousand words. Please watch this video.

https://www.youtube.com/watch?v=TvBobA-bhCI

2. "0x86350" is called from G_RunFrame.

This is a part of the code of G_RunFrame (from wolfcamql12.2).

		if ( ent->s.eType == ET_MISSILE ) {
			G_RunMissile( ent );
			continue;
		}

		if ( ent->s.eType == ET_ITEM || ent->physicsObject ) {
			G_RunItem( ent );
			continue;
		}

		if ( ent->s.eType == ET_MOVER ) {
			G_RunMover( ent );
			continue;
		}

and these are two parts of the assembly code partly corresponding to the right-above code.

.text:0000000000083294 loc_83294:                              ; CODE XREF: sub_82EF0+392↑j
.text:0000000000083294                 mov     eax, [rbx+4]
.text:0000000000083297                 mov     rdi, rbx
.text:000000000008329A                 cmp     eax, 3
.text:000000000008329D                 jz      loc_83B4A
.text:00000000000832A3                 cmp     eax, 0Bh
.text:00000000000832A6                 jz      loc_83B55
.text:00000000000832AC                 cmp     eax, 2
.text:00000000000832AF                 jz      loc_83500
.text:00000000000832B5                 mov     r12d, [rbx+288h]
.text:00000000000832BC                 test    r12d, r12d
.text:00000000000832BF                 jnz     loc_83500
.text:00000000000832C5                 cmp     eax, 4
.text:00000000000832C8                 jz      loc_83EA3
.text:00000000000832CE                 cmp     ebp, 3Fh ; '?'
.text:00000000000832D1                 jg      loc_8394F
.text:00000000000832D7                 call    sub_5B6B0
.text:00000000000832DC                 nop     dword ptr [rax+00h]
.text:00000000000832E0                 jmp     loc_831F5

.text:0000000000083B4A loc_83B4A:                              ; CODE XREF: sub_82EF0+3AD↑j
.text:0000000000083B4A                 call    sub_86350
.text:0000000000083B4F                 nop
.text:0000000000083B50                 jmp     loc_831F5

You can see a repeat of "cmp" and "jz" instructions in the first assembly code. I think this is the repetation of conditional jump, so it corresponds to the part of G_RunFrame code. Also, "3" in the forth line corresponds to ET_MISSILE.

If "ent->s.eType" is 3(ET_MISSILE), it will jump to "loc_83B4A" in the second assembly code. In this assembly code, you can see "sub_86350" and it corresponds to the address I hooked the "My_G_MissileImpact" function to.

Btw, thank you for replying my question about hooking in minqlx's issue :) maybe I will comment your reply later.

@agkr234
Copy link

agkr234 commented Feb 7, 2020

Furthermore, I think the code of G_MissileImpact is included in G_RunMissile.

This is a part of the code in G_MissileImpact (from wolfcamql12.2)

	// check for bounce
	if ( !other->takedamage &&
		( ent->s.eFlags & ( EF_BOUNCE | EF_BOUNCE_HALF ) ) ) {
		G_BounceMissile( ent, trace );
		G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 );
		return;
	}

This is a part of the assembly code in "0x86350"(G_RunMissile)

.text:00000000000866F5 loc_866F5:                              ; CODE XREF: sub_86350+390↑j
.text:00000000000866F5                 mov     rsi, r12
.text:00000000000866F8                 mov     rdi, r14
.text:00000000000866FB                 call    sub_86140
.text:0000000000086700                 xor     edx, edx
.text:0000000000086702                 mov     esi, 29h ; ')'
.text:0000000000086707                 mov     rdi, r14
.text:000000000008670A                 call    sub_982F0
.text:000000000008670F                 add     dword ptr [r14+3FCh], 1
.text:0000000000086717                 jmp     short loc_866C0

There is "EV_GRENADE_BOUNCE" in G_AddEvent's arguments and this corresponds to "29h" in the assembly code ("mov esi, 29h"in the sixth line) Also, "sub_982F0" is the address of G_AddEvent. ("call sub_982F0" in the eighth line)

I think it is still lack of reasons, but there is a possibility that G_MissileImpact is included in G_RunMissile.

@em92
Copy link
Author

em92 commented Feb 9, 2020

I think the code of G_MissileImpact is included in G_RunMissile.

I think you are right.

I have an idea how to hook something inside the function: make CALL opcode (current Hooks are using JMP opcodes). But:

  1. not sure, it is possible
  2. don't know how to implement it via minqlx

@agkr234
Copy link

agkr234 commented Feb 13, 2020

Thank you for suggesting the idea! I still don't have enough experience to modify assembly codes, so I made a roundabout code in a hook on G_MissileImpact. Come to think of it, G_RunMissile triggers G_MissileImpact only when a projectile collides with something, so I made lines of a code doing collision detection with BG_EvaluateTrajectory and SV_Trace, like G_RunMissile does. It may be technically not a hook since It just detects whether G_MissileImpact triggers, but I'm currently fine with it because I wanted a trigger detection of G_MissileImpact.

I'm done with G_MissileImpact for now, and I tried to implement and call G_RadiusDamage, but there's two issues.

1. An argument "float damage" causes segfault.

A hook on G_RadiusDamage works fine when I tried it out the following code.

qboolean __cdecl My_G_RadiusDamage(vec3_t origin, gentity_t* attacker, float damage, float radius, gentity_t* ignore, int mod) {
	qboolean a;
	a = G_RadiusDamage(origin, attacker, -1.23456789, radius, ignore, mod);
	return a;
}

But it causes segfault when "float damage" argument is given to "a = G_RadiusDamage(...)". I debugged it by outputting what value the argument had.

qboolean __cdecl My_G_RadiusDamage(vec3_t origin, gentity_t* attacker, float damage, float radius, gentity_t* ignore, int mod) {
	qboolean a;
	a = G_RadiusDamage(origin, attacker, -1.23456789, radius, ignore, mod);
	Com_Printf("%f %f %f \n", origin[0], origin[1], origin[2]);
	Com_Printf("%d \n", attacker->s.number);
	Com_Printf("%f\n", damage);
	Com_Printf("%f\n", radius);
	Com_Printf("%d \n", ignore->s.number);
	Com_Printf("%d\n", mod);
	return a;
}
-3810.000000 -2164.000000 1.000000
425
-0.425325
-0.425325
425 
0

It had negative value, so I put negative value "-1.23456789" into the place where "float damage" should be, as the above code shows, and then shot a rocket, but it didn't cause any segfault. I wonder what's wrong with the value of "float damage".

Also I wonder why "float radius" doesn't cause any segfault even though it has the same value as "float damage".

2. It causes segfault when G_RadiusDamage is called after Com_Printf.

I found that it caused segfault when I was debugging the arguments with Com_Printf, but it didn't cause any segfault G_RadiusDamage was called before Com_Printf.

@em92
Copy link
Author

em92 commented Feb 17, 2020

I tried to implement and call G_RadiusDamage, but there's two issues.

What is address of G_RadiusDamage?

It had negative value, so I put negative value "-1.23456789" into the place where "float damage" should be, as the above code shows, and then shot a rocket

I think stack is spoiled there, so Com_Printf can return invalid values up there and even segfault. Another way to get values

  1. add breakpoing via raise(2)
qboolean __cdecl My_G_RadiusDamage(vec3_t origin, gentity_t* attacker, float damage, float radius, gentity_t* ignore, int mod) {
	raise(2);
        // ...
  1. make debug
  2. run qlds using gdb. For example using this script:
#!/bin/bash
cd "$(dirname "$0")"
export LD_PRELOAD=$LD_PRELOAD:./minqlx/bin/minqlx.x64.so
LD_LIBRARY_PATH="./linux64:$LD_LIBRARY_PATH" exec gdb ./qzeroded.x64 "$@"

After this type "r" to run qlds, then try to execute G_RadiusDamage. Game will freeze, so you can return to gdb console, type these to get values:

p origin
p damage
p radius

After this, you can continue executing using "c" command.

@agkr234
Copy link

agkr234 commented Feb 19, 2020

What is address of G_RadiusDamage?

I'm sorry. The address of G_RadiusDamage may be 0x739B0.

I attempted to execute the original G_RadiusDamage by calling both of My_G_RadiusDamage code I wrote, but it caused segfault this time.
I added the code to the original minqlx code and returned server.cfg to default, but it still caused segfault.
Maybe there's a possibility that the address of G_RadiusDamage is wrong.

@em92
Copy link
Author

em92 commented Feb 24, 2020

Sorry, I tried to figure out, what is going on 0x739B0, but couldn't

@em92
Copy link
Author

em92 commented Apr 10, 2020

Hey @agkr234!
If still you are interested, we have recently found out old address table
https://tjone270.org/games/quakelive/dev/qagamex64.so.symbols
In most cases, add +0x30 to match current ones.

@agkr234
Copy link

agkr234 commented Apr 11, 2020

Wow! This is awesome!
This must save me a lot of time. thanks!
It may be too much to ask, but does it happen to have old address table of qzeroded.x64?

@em92
Copy link
Author

em92 commented Apr 12, 2020

No info about qzeroded unfortunately.

@agkr234
Copy link

agkr234 commented Apr 14, 2020

Oh that's unfortunate, but the address table of qagamex64 is really really helpful :)
Btw, I made a mod like Quake1 Midair and I'm testing it on the server named "Quake1-based Midair Mod"
Please take a look if you are interested. (I'm sorry but you may have high ping since it's on Japan.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment