Counter-Strike 1.6 hacking
After trying and failing to hack this game on and off for almost 15 years (I started playing young), I finally had enough background in C, assembly, and operating systems to pull it off. I did some reading and then wrote an internal hack with some help from forum posts on unknowncheats.me.
This is what final result looks like:
It’s only a no-flashbang hack, but all of the dirty work is done. It wouldn’t be too hard to extend the hack into something like a wallhack or an aimbot.
Here’s how it works and how to extend it.
Background
An internal hack is made of two parts:
- The injector
- The DLL file (.so on linux)
The injector is a program that allocates space in the target process (the game) and then calls the win32 api CreateRemoteThread
function to start execution of the DLL file. The DLL then has access to all the game’s memory and can do whatever you want it to do granted you know how to do it.
Luckily, Valve released the source code (called the sdk) to Half-Life which CS is a mod of. So we can take a look around there for hints.
In the source code there’s a function called Initialize
that takes in a pointer to cl_enginefunc_t
.
int CL_DLLEXPORT Initialize( cl_enginefunc_t *pEnginefuncs, int iVersion ) |
Great–what is cl_enginefunc_t
?
Doing a quick search in the sdk comes up with:
// Pointers to the exported engine functions themselves |
This is the game’s engine function table.
If we can find where the Initialize
function is in memory with a debugger, we can actually use this struct in our dll by reading the function’s parameters as they’re passed, and then creating our own cl_enginefunc_t
pointer and setting it to the address we find.
This is super important because it means we have a way to call these game engine functions–functions like Con_Printf
and pfnSetScreenFade
which I used to make the no flash hack–inside the dll which we inject.
Finding the address of the engine function table
Doing the debugging to actually find the engine function table is the real legwork. I use x64dbg instead of OllyDbg because it’s just a tiny bit more colorful and has a decompiler option to convert assembly instructions to C code. I also like the “Time wasted debugging” feature that counts how long you’ve spent debugging the program.
After reading some forum posts on unknowncheats.me, I found a guide that spells everything out.
So, what needs to be done, in order, is:
Search for the string “Initialize”
Use the references tab and search for Initialize in hw.dll
Dissect what’s going on
- The address of
GetProcAddress
is moved into esi and then called
GetProcAddress
takes in a module and a string which is the name of a function that you want it to return the address of. In this case, hw.dll wants the Initialize
function that’s exported from client.dll.
test eax, eax
If it can’t find it, it prints “could not link client.dll function Initialize”. If it found it, it moves the result from eax
(Remember: return values are put into eax) into a place in memory.
Awesome, that’s the address of Initialize
. Now we need to find where Initialize
is used so we can see its parameters being passed and therefore find the engine function table. Luckily, with its address doing this is easy.
Find references to the address of Initialize
Right clicking the address and hitting “search for references to selected address” gives a few results, but luckily the first one is the one we need. Double-clicking on it gives us this:
By default, arguments are passed via the stack in reverse order. The highlighted section shows the version (7
) first being pushed, then the address to the engine function table being pushed next (hw.4C56A98
).
THIS is the money right here. Address 0x4C56A98 is the relative address of the engine function table.
All that needs to be done is to find a way to reference this address in the dll we write. In other words, we have to find an absolute address.
Relative vs. absolute addresses
An absolute address is an address that won’t change the next time we run the game. The above address will change with each run because it really takes the form of
hw.dll+engfunc_table_offset.
When the game runs, the operating system first loads the executable and then loads each .dll file that the game requires. The spot in memory where they get put isn’t the same every time–it’s up to the OS. The catch is that all the code and data structures inside of each dll will stay the same distance away from the assigned base in memory. So the “offset” won’t change unless the game developers recompile the game with changes to the code.
The solution is to get the base address of hw.dll through x64dbg. Then we can calculate the offset.
Calculating the offset
We can find the base address of hw.dll by looking at the symbols tab in x64dbg:
The base of hw.dll is 0x04AF0000.
The offset to the game engine function table from hw.dll (the module where it lives) is:
0x4C56A98 (address we found) - 0x04AF0000 (base of hw.dll) = 0x00166A98
and voila! All we need to do then is use GetModuleHandle
to find the base of hw.dll
when the game runs and then add this number to it and then we’ll have a working pointer to the game engine function table.
Code for the DLL
|
How to run the code
The full code is on my Github here.
I used a non-steam version of CS 1.6. Shameful, I know. I just don’t want any hacking tools anywhere near my 15 year old steam account. I do NOT want to get vac banned (oh yeah, this hack is definitely detected).
You can download a copy here.
I also used MinGW and Git SCM for the C++ compiler and linux command-line on Windows, respectively.
The steps are:
- Download MinGW, run the setup and get all of the basic stuff
- Download Git SCM for the command line
- Add your msys mingw folder to your path on the command line
- Clone the full code:
git clone https://github.com/nsarka/cs-internal-hack.git
- Run
cd cs-internal-hack
- Run
make
- Run the game, then go into the injector folder and run the injector as administrator
If it worked, you should see “Hack Loaded” print into the console.
Extending the hack
At this point it shouldn’t be too bad. You can take a look at the actual cl_enginefunc_t
struct to see what engine functions are available to you, and if that isn’t enough you can also use this same method to find some other function table structures. There is the export table, the studio and the interface. Read about how to find them here.
Conclusion
I can’t say that was all my original work–far from it. The people at unknowncheats.me have been doing this for years so I have to thank them for posting their findings. Without it this would’ve been a months long ordeal since reverse engineering is the most time consuming part.
It was pretty fun to do this type of research and even more fun to actually get things working. I probably crashed the game 20-30 times trying to get the offsets right.
If anybody is interested or needs help running this my email is nsarka00@gmail.com. I’ll reply within a day.