https://github.com/Thorioum/golf-it-internal
"Golf It!" is a small 3D golf game that sees upwards of 1000 players active at one time. I first got into the game years ago just playing random multiplayer maps with friends, and while the base game of competing was fun enough, there was a part of me that just wanted an advantage.
My ego couldn't take running out of time and having 100 strokes added to my score ruining any chance I had of success. While the me from 5 years ago didnt have the ability to do anything, the me of a few years later set on a quest for knowledge, and success.
Research Begins
Fueled with a desire to learn about memory manipulation and exploit creation I began my journey. At this point in starting the only experience I had was giving myself infinite money using Cheat Engine in a game Bloons TD 6.
While I could have easily just found some other software to use to make my dumb little golf ball fly around the map, I hated the idea of just using someone elses software they made with no understanding of how it functions and little effort put it. Where's the payoff in that?
I began my advanced research through youtube guides on memory manipulation in windows, though what I was trying to achieve was niche enough that youtube didnt really cover all the bases.
After enough research I found out very simply how to read and write memory (the ReadMemory and WriteMemory of the windows api respectively), but was missing one key piece of data that still hinders progress in anything I try to mod today.
how the hell do i find the memory addresses of what I want to read and write to, and consistently?
Attempt 0: Barely even an option
The most simple thing one could do in order to find and manipulate a variable in the games memory is scan for it manually.
This would involve launching your game and using a software like cheat engine to find your target variable you want to manipulate
Consider this very simple scenario of finding the address of you ammo:
You start with 30 ammo, so you use cheat engine to scan the entire games loaded memory for variables with the value of 30.
You shoot 3 bullets and now have 27 ammo.
You then scan all the variables in that first scanned list, for any that have changed to a value of 27.
This is the process of filtering variables in memory to eventually finding yours. Using predictable changes in the target and scanning/filtering for them until eventually after repeated scanning on the same list, until you are left with one memory address, which is that of your ammo.
Now that you have the memory address of the ammo, you can then change the value the address of the ammo has. VoilĂ you hacked the game. (Video linked on this topic at the bottom of post)
BUT the adress you just found will always be different every time you restart your game, and can even change as your game continues so this is a suboptimal solution.
Bringing us back to the second part of my question: how do I find these memory addresses consistently?
Attempt 1: Pointer Maps
The first thing I tried, and had learned about, was that of pointer maps. Essentially finding a list of pointers the point one after another to each other with the ending pointer pointing to the target value, staying consistent after each game startup. Before, I had been using the very simple technique of rescanning the game over and over with cheat engine and finding the new location of my position coordinates every time the game started up
This technique of pointmap scanning involved, after finding the variable in memory (for example the golf balls y position variable), using cheat engine to generate a list of pointer paths that led directly to the position of the ball.
For example you would have a pointer1->pointer2->pointer3->pos then add an offset for the y value. If we were to decompile the game and see what these pointers actually were, they could be something like Game.World*->World.Ball*->Ball.BallPos*->BallPos
(I later found out that this game that this game was unreal engine and there are some other strategies you can apply to those games but I didn't know it at the time)
Now the pointermaps had a few key issues.
1->After using cheat engine to generate a map of pointers that led to your variable, you would be given a list of hundereds of pointer paths that led to your variable with them everchanging and most stopping to work
2->Even if you did find a pointer path that worked there was a chance that if you restarted your game or tried it on a different device, it would just cease to work. due to the offset of variables being loaded from classes being inconsistent
3->The most glaring issue: even if you found one that worked, consistently, across devices, if the game ever updated you would need to redo the entire scanning for pointer paths process as all the ones you have became invalid
Even so, at the time I couldn't think of an easy way around it and painstakingly created my first version of the hack using pointer paths to the golf balls position variable and velocity variable. Afterwards, it was updated to use the camera's rotation to fly to where you were facing.
namespace mem { //"GolfIt-Win64-Shipping.exe"+0x05948FE8 std::vector <unsigned int> xoffsets = { 0x8,0x0,0x58,0x0,0x0,0x88, 0x10 }; std::vector <unsigned int> zoffsets = { 0x8,0x0,0x58,0x0,0x0,0x88, 0x14 }; std::vector <unsigned int> yoffsets = { 0x8,0x0,0x58,0x0,0x0,0x88, 0x18 }; std::vector <unsigned int> xveloffsets = { 0x8,0x0,0x58,0x0,0x0,0x88, 0x40 }; std::vector <unsigned int> zveloffsets = { 0x8,0x0,0x58,0x0,0x0,0x88, 0x44 }; std::vector <unsigned int> yveloffsets = { 0x8,0x0,0x58,0x0,0x0,0x88, 0x48 }; //"GolfIt-Win64-Shipping.exe"+057DD8C8 std::vector <unsigned int> cameraQuatXOffsets = { 0xE8,0x128,0x1A0,0xA0,0x28,0x130, 0x0 }; std::vector <unsigned int> cameraQuatZOffsets = { 0xE8,0x128,0x1A0,0xA0,0x28,0x130, 0x4 }; std::vector <unsigned int> cameraQuatYOffsets = { 0xE8,0x128,0x1A0,0xA0,0x28,0x130, 0x8 }; std::vector <unsigned int> cameraQuatWOffsets = { 0xE8,0x128,0x1A0,0xA0,0x28,0x130, 0xC }; std::vector <unsigned int> cameraXOffsets = { 0xE8,0x128,0x1A0,0xA0,0x28,0x130, 0x10 }; std::vector <unsigned int> cameraZOffsets = { 0xE8,0x128,0x1A0,0xA0,0x28,0x130, 0x14 }; std::vector <unsigned int> cameraYOffsets = { 0xE8,0x128,0x1A0,0xA0,0x28,0x130, 0x18 }; }
the comment is the base pointer, usually the module name and then the starting pointer offset for the chain
The pointer paths I ended up using were ones that seemed to stay after crossrefrencing different pointer paths and finding the best one. More info on how pointer maps function can be found in a nicely organized format at the end of this post
Attempt 2: Function Hooking
Now function hooking, while in my opinion less difficult conceptually, is harder to pull off in code.
Function hooking in my context involves, finding the offset of where a function was loaded in the games memory, and in simple terms, adding my own code to the function in order to collect data from the stack memory and variables passed into the parameters.
This section will cover hooking the head, the top of a function, not anywhere in the middle or end
When you run a game for the first time, on the surface level there are two main parts that are in your memory. The hex data of your games code (which is in normal circumstances forever unchanging), and then extra data containing variables, pointers, and objects the game has created while running.
Now pointer scanning involved on being able to reliably find a piece of data in the section of the games memory that contained variables and objects, and finding and manipulating the variables we needed straight from there.
This technique will involve finding a specific function loaded into the games memory and manipulating the functions that use the variables, pointers and objects, to then find said variables, pointers, and objects.
How do we do this?
Unlike pointer scanning, this one I could not find as much cohessive information on and took lots of trial and error to figure out. For that reason I will try to explain in as understandable terms the steps I took to finally end up at my end goal.
What we are going to do is find a function that writes to our target variable. Hook the function, and use the functions parameters the same way the function itself does in order to locate the address of our variable. The function is the same every game startup so we can consistently hook the function and everytime the function is called we can basically record the address of our target variable back to our program, and then do whatever we want with it.
Step 1: Finding the functions offset
This part will be written for using cheat engine
For function hooking to work, we need the offset, the location of the function from the base of the module.
Why am I calling it module and not program?
The function you are targeting may not actually be in the loaded code of the game itself, and instead, code that the game uses that is actually stored somewhere else. Usually a loaded dll.
We can already get the adress of any module the game loads pretty easily, so then we need to find what module it is and the offset of the function we are looking for.
First things first, you need to find the variable that you want to manipulate (duh). So you launch the game, scan for your variable, and once you locate your variable you will want to right click it and see what opcodes write to your selected memory adress. Sometimes it may be multiple functions, sometimes it may at first seem like one. But you will want to find a function/opcode that consistently writes to your target adress when you need it to. Finally, you need to click "Show Dissasembly" so you can view the assembly instruction that is writing to your address, but most importantly, that offset of the function writing to your address.
Here is a breakdown of finding my golf balls function and offset
You can see my golf balls position ended up being controlled by a PhysX_64.dll, which is a common NVIDIA physics engine. Now that I have the exact offset of the code that writes to my variable, I need to find the containing function.\
Step 2: Finding the function and dissasembling it
The offset for the code I found was
PhysX_64.dll+CBA12
which i will continue to use in this segment
For this segment, a dissasembler will be needed. Your going to want to find your module and load it into your dissasembler. if it was fortnite.exe find fortnite.exe on your disk and load it in. if it was PhysX_64.dll like it was for me then find that dll put it into the dissasembler.
Now you will want to locate the position of the code you found. In IDA you can easily do this by pressing "G" for jump to address, and typing in "HEADER+CBA12"(my example)
From here start scrolling up until you find the start of the function. It would be good to rename the function to something memorable so you can come back to it for later use.
From here, record the function offset, this will be used later
Please note, that you may need to rebase ida to 000000 in order for the number/offset on the left side to be of any use. Edit->Segment->Rebase Program
Now in this scenario im not comparing my assembly to the actual source code (which I would guess is the same case for you), so you have to inspect and give it your best guess on what is happening
I have IDA pro, if you dont have IDA pro, you will need to find something that dissasembles into psuedocode.
Pressing F5 in the function in IDA will bring you do a psuedocode dissassembly of your function.
Now you want to find the exact line of psuedocode that you target variable if modified at and work your way back from there all the way until you identify all the variables that go into locating your target, as well as most importantly the starting point, which is the parameter(s) of the function
you can see i've renamed some variables that seemed important in the calculation.
Step 3: Putting it all together -- Putting it into code
This is the only part of this whole thing I used an external library. The actual hooking and coding a hook of a function is an artform in itself which I did not spend my time struggling with. Instead I used a library that automatically modifies the memory of the loaded function and does all the magic for me called MinHook. Its github will be linked at the bottom
What you can do with the psuedocode (and what I did) is copy the psuedocode bar for bar back into your function hook in order to get the address of the variable you want
typedef __int64 (__fastcall* tickBall)(__int64 ball); tickBall pFuncBall = nullptr; __int64 __fastcall detourTick(__int64 ball) { //copied psuedocode __int64 lastOrentation = *(QWORD*)(*(QWORD*)(ball + 0x38) + 8 * 0); DWORD* pOrientation = *(DWORD**)(lastOrentation + 0x28); pPosition = (uintptr_t*)pOrientation; // return pFuncBall(ball); }
If any of this code especially at the top is confusing, it is all apart of setting up the hook for the function using minhook, i've linked a guide to using MinHook down below
And there you have it, now I have a pointer ("pPosition" as shown in the code above) that points directly to the position of the golf ball. This pointer is updated every time this function writes to it.
Now everytime you launch your hack you can consistently hook and gain access to the address of the variables most important to you.
Step 4: (Optional but Recommended) Pattern Scanning
Pattern scanning is the technique of instead of finding the function offset and using that over and over again statically, every time we load the program finding a certain pattern of bytes that only our function has loaded and using those to find the offset of our function.
Why would we want to do this?
Well when a game updates, new code is added or old code is removed and everything is shifted over. That means 9 times out of 10 the function offset you found and have been using wont work anymore
Pattern scanning is a way around this
We scan all of the games loaded code in memory and find a unique pattern of bytes thats only generated at the start of our target function
A pattern of bytes that you scan for may look like this (the one I use in my program)
4C 8B DC 55 57 41 57 49 8D 6B ? 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? 48 33 C4 48 89 45 ? 48 8B 01
In order to automatically create the pattern you need, there are plugins you can get for IDA like "sigmakerex" that allows you to select a function and automatically find a selection of bytes thats unique to the function and will work for your byte pattern
Okay well if were scanning for a set of bytes, what the question marks and how do we scan for those?
The question marks are whats called wildcards and they are placeholder bytes that we are fine with being any byte after a scan
the byte could be 05 or it could be 8D but it doesnt matter if a wildcard is there, it will just match it and be fine with it. Basically a placeholder.
So first we save all of the bytes of a games code that loaded into memory into our own buffer. Then we use an algorithm to go through byte by byte and try to find an address that fits our pattern mold. As soon as we find something that fits our mold we return that address. This is how even after Golf It! updates, I can still find the function offset I need and hook the correct function so my hack continues to work.
ScanData signature = ScanData("4C 8B DC 55 57 41 57 49 8D 6B ? 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? 48 33 C4 48 89 45 ? 48 8B 01"); size_t size = Memory::getModuleSize(procId, "GolfIt-Win64-Shipping.exe"); ScanData data = ScanData(size); uintptr_t moduleBaseAddr = Memory::getModuleBaseAddr(procId, "GolfIt-Win64-Shipping.exe"); HANDLE golfItHandle = GetCurrentProcess(); Memory::read(golfItHandle, moduleBaseAddr, data.data, data.size); uintptr_t cameraAddr = Scanner::scan(signature, data); std::cout << "Found Camera Function At: " << cameraAddr << std::endl;
code i wrote that does exactly as i described
Finally
Hopefully what I covered gave into insight to my journey creating my fly hack and how you can do something similar for any game you want
Code for pattern scanning has an implemenation in my github repo "Asgard" if you are interested.
Code for the entire hack itself can be viewed on my github as well.
https://github.com/Thorioum/golf-it-internal https://github.com/Thorioum/Asgard
Resources
--Pointer Scanning https://www.youtube.com/watch?v=nQ2F2iW80Fk
--Pattern Scanning https://gamehacking.academy/pages/7/02/
--MinHook https://github.com/TsudaKageyu/minhook
--MinHook Usage https://www.youtube.com/watch?v=qEbPCIFtyOs