When someone has too much time on their hands (and usually also has a lot of work to do), their productivity sometimes slips... and by "slips", I mean it completely collapses into itself and implodes.
ANYWAY, back to the focus of this thread, I decided to have some fun with my newly installed Windows XP OS on my VM, and by fun, I mean screwing around with this wonderful game which brings me back so many memories: 3D Pinball - Space Cadet. I've been getting more into reverse engineering these few days and I thought I'd give my skills a shot at a real application and see how far I could go.
This write up is motivational material for other reverse engineers out there and just lulz for everybody else who is interested. Enjoy!
Hacking 3D Pinball - Space Cadet
Hacking the In-game Score
What we're going to do first is exploring how one could go about and change their game score. Well, it's actually pretty easy if you have the right tool and the relevent skills (obviously). The tool we are going to use is the classic Cheat Engine (I'll be using version 6.6), infamous (especially around kids) for being used to hack game scores on their favourite games. For those who aren't familiar with Cheat Engine, it's basically a memory-hacking tool which accesses another process's memory space for disassembling and debugging... and causing mayhem by changing values everywhere. For those who do know and have used Cheat Engine before (as a kid), the first couple of steps might be familiar.
First, we'll open up the game and Cheat Engine, then proceed to load the
pinball.exe process like so:
In the value box, we need to do a
First Scan to find the value we want in memory, i.e. we want to scan for our score so we can edit its value in memory.
Since our score is currently
0 and there are more than likely a billion
0s in memory, we will start off the game and get a number which is less likely to already exist in memory. Like so:
After typing in
6000 as our value and clicking
First Scan, CE instantly finds a corresponding value and its address. Now, we can try editing this value to see what happens to our score.
We'll continue playing the game and...
Oops? Well that didn't work as we hoped?
Okay, it's not as easy in this scenario because it probably works differently than we expected, so let's see what's actually happening in the code.
Right here, we can see that the score value that we captured from the scan was not what we wanted. In the bottom right window, we see the address of
eax which is the same one from the scan but
eax doesn't actually keep track of the value. Instead, the score being passed to
ecx is moved to the location to where
eax points. So how do we get to a score of
696969 then? Well, let's try injecting some code to constantly set
ecx to our desired value in Tools -> Auto Assemble. We'll then need a
Code Injection template in the new Auto Assemble window in Template -> Code Injection, then the address to inject is the blue-highlighted line.
Enter in our assembly code to change
ecx and then press execute.
Easy! But what's this? Our ball number has also changed to exactly the same value? Okay, because I'm lazy and I can't be bothered taking too many screenshots, basically, what happens if we scan for the address of the ball (like we did for our score), we end up at the same piece of code but with
eax holding the value of the ball number (instead of the score). How we can patch this is by only setting
eax is equal to the score's address. We'll restart the entire game and then use this code in the injection instead:
We press Execute again, run the game, and...
Done! Simple, right? The issue here is that it's now only a static value, but we can essentially do whatever we want to the score with the right code. Now we just enter in our name on the high score...
Hmm... high score...
Hacking the Scoreboard
Okay, this is probably a lot more difficult compared to what we did above and probably longer as well, so I'll try to keep it brief. Since the high scores should be a persistent thing, the data needs to be cached somewhere, most likely on disk. If we were to find where the high scores are located, we could try looking around for a data file in its own directory, but that's not always a guaranteed method. So let's try something else. Fire up your debuggers (I'll be explaining with Ollydbg) and let's get to it.
The first thing we want to do is to reach the high score window, then pause the debugger there while the game waits for us to provide input.
At this point, we'd want to navigate the call stack to find out what happened on the journey to the score board. To do this in Ollydbg, open up the call stack (
Alt+K) and then start at the very bottom (the bottom is the first traced call and the top is the most recent call) like so:
From there, we'll navigate through all the instructions, including jumping into function calls and see if we find anything interesting... So, to skip the boring and unnecessary parts, here is something that I found after a few calls:
That looks like a registry subkey but we are lacking the key from which it stems. If we scroll down a bit, we will see that one possible key is
So let's dive into the registry and follow it.
Looks like we hit the jackpot! The scores from the previous section is there but what are these
-999 values? Well, they're just default values to fill in the spaces. Back to the game, let's register our score on the scoreboard and see if it pops up in the registry. Hit the run key (
F9) to continue the game. And if you're actually doing this (I highly doubt it), you'll realise that the registry doesn't change until you exit the game.
So let's try screwing over the scoreboard and then launch the game to see our ever-so genius plan to take over the world of pinball gaming! Heheheh...
Hey, what gives? Everything's gone! Okay, so Billy Gates wasn't as stupid as we thought and he has foiled our plans... Drats! That conniving... little... devil... Anyway! There was something that we missed. Did you find it? Let me show you:
There it is. This is our previous record. Let's take a look at our new (empty) scoreboard after exiting the game.
Okay, that is very different. Seems like this is some sort of hashing method. If we take the value
-4995 and divide it by
-999, we get
5, which is the number of players on the scoreboard. Aha! That must be the algorithm! Simply add up all the scores! Right?! Well, no. If we take a look back at the previous, legitimate record, we get
696969 + 30000 + 2500 - 999 - 999 = 727471 != 727926. Well, we were close, actually. We're almost there. If we try to do a strings analysis, we can actually find that the program access the string
Verification two times: once during start up and once after exiting. You can probably guess that the start up access reads the scores from the registry while the last access writes the scores back. What we want to know is how the
Verification value is generated, so I'll skip all the gory reversing details and jump straight to where the algorithm is located. Here is the entire algorithm:
I added in a few comments at the last arrow there. Basically, what this algorithm does is it retrieves the name of the player and the score. Each letter of the player is summed together and then added with their score (yes, this also includes the filler players with
-999 scores as we've concluded from before). This is repeated until all of the players and their scores have been added together. So, back to our previous legitimate example, let's try and calculate the
(100 (d) + 116 (t) + 109 (m) + 696969) + (65 (A) + 30000) + (65 (A) + 2500) + (0 - 999) + (0 - 999) = 727926 == 727926
Hey, it worked! NOW LET'S UNLEASH OUR POWER!
So yeah, good stuff. Had some fun. If anyone would like to know any specifics (whatever I cut out to keep this short), feel free to leave a comment or throw a message into my inbox.
Using Ollydbg be like: