Sie sind auf Seite 1von 7

Quake2 wallhack tutorial - cppdude

This fairly advanced tutorial is going to teach you how to make a wallhack for Q
uake2. To use it you will need TSearch and W32Dasm. We are going to be hacking t
he final version of Quake2 (3.20); and we are going to be using OpenGL apis. Don
t worry if you dont know opengl, ill explain what you need to know. You will als
o need a basic knowledge of asm. Read sheeps tuts if you dont(www.gamehacking.co
m). Knowing C is also helpful but not required.
Ok lets go. Look in you Quake2 dir. Youll find a ref_gl.dll dll file. This file
is used by Quake2 to use opengl, and its this file we are gonna hack. So disasem
ble it with dasm. If you can, save it to a text file and open it with notepad, c
os we are gonna be doing a lot of copy pastin.
Our wallhack will have 2 modes. The first will be a wireframe mode, where we wil
l force quake2 to draw all walls in wireframe. To do this we must first find the
function that draws the world, and replace it with a call to our code. By world
i mean not things like guns, monsters etc. Here is the pseudocode of our code s
ection:
DrawMode(Lines)
Drawworld()
DrawMode(Fill)
return //to game code
Pretty easy that. DrawMode will force all drawwing after that call to be done wi
th lines, then we drawworld in lines, then we restore DrawMode to fill to make s
ure everything else is drawn properly; cos we dont want guns, players etc to be
drawn in lines.
Ok now you understand the theory of our first wallhack mode, lets put it into pr
actice. We will first need to find out the Quake2 function to drawworld. In your
asm view of Quake2 that you just made do a search for r_drawworld. r_drawworld
is a Quake2 cvar that determines wether Quake2 will draw the world. Without thi
s it would be very difficult to find the function that draws the world, and we w
ould have to draw everything in wireframe mode (not nice).
You will find a string ref to r_drawworld:
* Possible StringData Ref from Data Obj ->"r_drawworld"
|
:1000914F 68BCE40210
push 1002E4BC
The address of the string ref is being pushed as a parameter to a function. What
does this function do? It takes a string pointer as one of its parameters("r_d
rawworld" in this case) and adds a cvar to the console, so that you can use that
cvar in the console for example by typing:
r_drawworld 0
The function then returns a pointer to a variable. Functions return values by sa
ving to the eax register, so the r_drawworld varibale is saved like this:
mov dword ptr [10056908], eax
So now at location 10056908, we have a pointer to the r_drawworld variable. We n
ow need to do a search for that address and find out where that pointer is deref
erenced. So do a search for 10056908 and you should find this:
* Referenced by a CALL at Address:

|:10008EC6
|
:1000C080 A108690510
:1000C085 83EC48
:1000C088 D94014
:1000C08B D81D74730210
:1000C091 DFE0
:1000C093 F6C440
:1000C096 0F8543010000
:1000C09C F6050467051002
:1000C0A3 0F8536010000
...

mov eax, dword ptr [10056908]


sub esp, 00000048
fld dword ptr [eax+14]
fcomp dword ptr [10027374]
fstsw ax
test ah, 40
jne 1000C1DF
test byte ptr [10056704], 02
jne 1000C1DF

Great. Youve now found the function that draws the world. You can see that the p
ointer is being dereferenced (mov eax, dword ptr [10056908]). W32Dasm tells you
that the function is called at 10008EC6. Go there by doing a search for 10008EC6
. Youll come to a code section like this:
:10008EB2
:10008EB7
:10008EBC
:10008EC1
:10008EC6
:10008ECB
:10008ED0
:10008ED5
:10008EDA
:10008EDF

E889F9FFFF
E874F8FFFF
E80FFCFFFF
E82A330000
E8B5310000
E8A0F2FFFF
E83BAFFFFF
E8D6F5FFFF
E811250000
E82CFFFFFF

call
call
call
call
call
call
call
call
call
call

10008840
10008730
10008AD0
1000C1F0
1000C080
10008170
10003E10
100084B0
1000B3F0
10008E10

//call to drawworld

We have now found out where the drawworld function is called. Hooray! We can now
replace that call with a call to our code section. At the end of files there is
usually a large area of unused memory that you can use for a code section. So g
o to the end of the asm of the file. Youll find a large area of 0x00s after 5 0
x90s. We can start our code section at the 0x90s; at 1002644B.
This is where we start using TSearch; to make asm scripts. Read the TSearch help
file if you dont know how to make TSearch asm scripts. Load up TSearch and open
up the easywrite interpreter. In a new script type:
offset 1002644B

;define the start of our code section

We now need to make a call to the DrawMode function. The OpenGL api we will use
for this is glPolygonMode. In C if you wanted to do make following polygons draw
n with lines you would do:
glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
So now we need to make a call to the function glPolygonMode, and we need to find
its the address of the pointer to it. In W32Dasm do a search for glPolygonMode.
Youll find a string ref address being pushed to a function like this:
* Possible StringData Ref from Data Obj ->"glPolygonMode"
|
:10011108 6830000310
push 10030030
:1001110D 50
push eax
:1001110E FFD6
call esi
tProcAddress
:10011110 8B0DD4600510
mov ecx, dword ptr [100560D4]
* Possible StringData Ref from Data Obj ->"glPolygonOffset"

;call Ge

:10011116 6820000310
:1001111B 51
:1001111C A334510510
onMode address is saved
:10011121 A3C45C0510
...

|
push 10030020
push ecx
mov dword ptr [10055134], eax

;glPolyg

mov dword ptr [10055CC4], eax

Here the address of the string ref "glPolygonMode" is being pushed as a function
parameter and the called is then called(call esi). In C this function is GetPro
cAddress. It is used to get a pointer to a function imported from a module such
as a dll file. GetProcAddress returns a pointer to the function(glPolygonMode in
this case). So now we need to look for where the address of glPolygonMode is sa
ved. Like all functions, the address is stored in the eax register so we need to
look after call esi to where the eax register is saved to. Ive commented above
where that is in case you cant figure it out.
The address is saved in two places. Do a search for one of these addresses and y
oull find that the api is called like this:
call dword ptr [10055CC4]
You now know how to call glPolygonMode. Soon you can add it to your TSearch asm
script. But first you need to push the parameters to the function. In asm we pus
h parameters in reverse order becuase pushing puts the parameters onto a stack w
hich is a first in last out data structure. So we first push GL_LINE. This is wh
ere the gl.h header file comes in. It will tell what GL_LINE is defined as. If y
ou dont already have the gl.h header file, download it from www.opengl.org. Ill
tell you what value to use anyway, its 0x1B01. So in your asm script in TSearch
add a push for that value. Your script now looks like this;
//define the start of our code section
offset 1002644B
//push GL_LINE
push 1B01
Now we need to push GL_FRONT_AND_BACK which is defined as 0x0408. Then you call
glPolygonMode. So now your asm script looks like this:
//define the start of our code section
offset 1002644B
//push GL_LINE
push 1B01
//push GL_FRONT_AND_BACK
push 408
//call glPolygonMode
call dword ptr [10055CC4]
Now that all following polygons are drawn in line mode, we can call drawworld. W
e can then restore drawing to GL_FILL and then return to the game code. So your
asm script now looks like this:
//define the start of our code section
offset 1002644B
//push GL_LINE
push 1B01
//push GL_FRONT_AND_BACK
push 408
//call glPolygonMode
call dword ptr [10055CC4]

//call drawworld
call 1000C080
//push GL_FILL
push 1B02
//push GL_FRONT_AND_BACK
push 408
//call glPolygonMode
call dword ptr [10055CC4]
//return to game code
ret
Great. We now have our code section defined. We now need to make a call to it, b
y replacing the existing call to the drawworld function. We already found that t
he existing call to drawworld was at 10008EC6. So we add a call to our code sect
ion in our asm script so that it now looks like this:
//define the start of our code section
offset 1002644B
//push GL_LINE
push 1B01
//push GL_FRONT_AND_BACK
push 408
//call glPolygonMode
call dword ptr [10055CC4]
//call drawworld
call 1000C080
//push GL_FILL
push 1B02
//push GL_FRONT_AND_BACK
push 408
//call glPolygonMode
call dword ptr [10055CC4]
//return to game code
ret
//make call to our code
offset 10008EC6
//by replacing existing call to drawworld function
call 1002644b
Save this script and run it on quake2.
Disaster!!! Quake2 now looks a mess. Every frame that Quake2 draws is being draw
n on the previous frame. When the world was drawn in fill mode this wasnt a prob
lem but now that we are drawing the world in lines we need to clear the screen e
verytime Quake2 draws a new frame. We could do this by using an opengl api, like
this:
glClear(GL_COLOR_BUFFER_BIT);
but Quake2 already has a cvar to do this so i would rather hack that. The Quake2
cvar to do this is gl_clear. Do a search for gl_clear in W32Dasm and youll find
, like r_drawworld, its the address of the string being pushed to the add cvar f
unction, and a pointer returned. Search for where that pointer is being referenc
ed like you did before and youll come to a code section like this:
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:10008D43(C)
|
:10008DA6 8B1574620510
mov edx, dword ptr [10056274]

:10008DAC
:10008DAF
:10008DB5
:10008DB7
:10008DBA
:10008DBC
:10008DC1

D94214
D81DD0720210
DFE0
F6C440
7507
6800410000
EB05

fld dword ptr [edx+14]


fcomp dword ptr [100272D0]
fstsw ax
test ah, 40
jne 10008DC3
push 00004100
jmp 10008DC8

* Referenced by a (U)nconditional or (C)onditional Jump at Address:


|:10008DBA(C)
|
:10008DC3 6800010000
push 00000100
You can see that 0x4100 is being pushed. If this is used as the paramater to glC
lear(the opengl api, not to be confused with gl_clear, the Quake2 cvar), then th
e screen buffer and the depth buffer(not important) will be cleared. But there i
s a jump above that push that will cause only 0x100 to be pushed to glClear. Th
is will only clear the depth buffer, and that mess our wallhack was making will
still be there. So all we need to do is(you guessed it) nop the jne.
So now your TSearch asm script will now look like this:
//define the start of our code section
offset 1002644B
//push GL_LINE
push 1B01
//push GL_FRONT_AND_BACK
push 408
//call glPolygonMode
call dword ptr [10055CC4]
//call drawworld
call 1000C080
//push GL_FILL
push 1B02
//push GL_FRONT_AND_BACK
push 408
//call glPolygonMode
call dword ptr [10055CC4]
//return to game code
ret
//make call to our code
offset 10008EC6
//by replacing existing call to drawworld function
call 1002644b
//nop jne to make sure GL_COLOR_BUFFER_BIT is also passed to glClear
offset 10008DBA
hex 9090
If you run this you will have a fully working wallhack for Quake2. If you want t
o toggle it off you use the bottom half of the easywrite interpreter. In this yo
u can just replace the call to our code section with a call to the original draw
world function. The rest of the code section you can leave intact. Note: If you
are implementing this wallhack method on other games you may have to nop any ori
ginal calls to glPolygonMode.
You are probably thinking: how can i remove that gay pink as the clear color? Jo
hn Carmack seems to like this color because its exactly the same for Quake3. You
simply call glClearColor like this:

glClearColor(0.0f, 0.5f, 0.75f, 1.0f);


for a nice calm blue. The first 3 params are RGB, and the 4th is an alpha value
which you dont need to know unless you decide to get into the wonderful world of
OpenGL programming. Use the TSearch converter to get the hex values from the fl
oating point values i just specified. Remember params are pushed in reverse orde
r. Very important that. Stick a call to glClearColor in your code section before
the drawworld call. I know its silly and a waste of processor power to call tha
t api everytime but it works and youve gained lots of power by only drawing line
s anyway.
I cant be bothered to explain exactly how i do the second wallhack mode, because
the methods are pretty much the same. The second wallhack mode will be a depth
test hack. By this i mean that we will disable depth testing while drawing entit
ies so that they are always drawn onto the screen nomatter if they are behind a
wall. Depth testing is OpenGL jargon for testing wether an object to be drawn on
the screen is behind another object, and if it is then it is not drawn. Here is
the C code for our code section for this method:
glDisable(GL_DEPTH_TEST);
drawentities();
glEnable(GL_DEPTH_TEST);
return;
GL_DEPTH_TEST is defined as 0x0B71. One other thing you should know, you must dr
awentities last if you intend them to be always on screen. We found that code se
ction that originally calls drawworld:
:10008EB2
:10008EB7
:10008EBC
:10008EC1
:10008EC6
:10008ECB
:10008ED0
:10008ED5
:10008EDA
:10008EDF

E889F9FFFF
E874F8FFFF
E80FFCFFFF
E82A330000
E8B5310000
E8A0F2FFFF
E83BAFFFFF
E8D6F5FFFF
E811250000
E82CFFFFFF

call
call
call
call
call
call
call
call
call
call

10008840
10008730
10008AD0
1000C1F0
1000C080//drawworld
10008170//drawentities
10003E10
100084B0
1000B3F0
10008E10

Drawentities is also called there. When i made my depth test hack I made that dr
awenities call last in that list of calls. Works perfectly.
That sums up my Quake2 wallhack tutorial. The methods here can be applied to any
OpenGL game, but if you cant find the specific function that draws the world, t
hen youll just have to make everything wireframe mode, which is bad cos you cant
see your ammo health etc.
You may be wondering why i made
ll i couldnt for the life of me
ties. I found out the functions
ng code isnt actually there. If
st it on www.gamehacking.com.

this tutorial using Quake2 instead of Quake3. We


find the block of code that draws the world/enti
that dereference the cvar pointer, but the drawi
you figure it out please write a tutorial and po

Also, i like to get the bytes generated by TSearch and put them into a C++ progr
am so that I have a nice small executable to use while playing Quake. I made a C
++ class to do this. If you are going to do this remember that you should set th
e process's class priority to idle while patching, else the asm code you inject
can easily get executed while it is still being injected(not nice).

Have fun and go rage some servers =)


cppdude

Das könnte Ihnen auch gefallen