Okay, first up for those who perhaps are not so interested in spending many hours trawling through pages and pages of assembler code, I'll skip straight to the good bits and give you a run down of the sneaky CHEAT_MODE I found hidden in the pinball game included with windows XP.
Load up the game and type the words hidden test. Looks pretty normal? Well, as your ball is flyin' 'round, click on the pinball machine. Drag your mouse around. The ball follows your every command - blatantly ignoring the laws of gravity we have come to expect it to follow!
There's more too. The "hidden test" mode has a bunch of functions put in there to help the developers out during the game's creation. Here's ones I found, or can see in the code:
h: Shows the high-score table, with an entry of 1,000,000,000 for you to put your name next to.
m: Shows the amount of system memory
r: Increases your "rank" in the game
y: Shows the game frame rate in the title
These ones I can see are trapped in the code, but I can't see what they do:
b, F11, F12, F15 (how do you do that? Key code 0x7E)
There also seems to be some way to turn it off, but I can't figure it out. And also I keep making the graphix do wacky things, as if I haven't pushed the cartridge in to the Megadrive properly or something.
I had a quick google around for cheats for this game - I found all the other cheats in the game: 1max = free ball, gmax = the gravity thing etc... but no sites listed the "hidden test" cheat. So I'm assuming that no one bothered to pull the key-handling code to bits. I did, and here's how you can do it too....
How'd you find that cheat?
Right. That's it for games - now I'll explain how I did it and show you how to do some basic reverse engineering and cracking ya self. It's not really really difficult but it is really really tedious. And potentially spirit-crushing. So, it that's your thing then read on, otherwise - get back to pinball!
Here's the idea behind cracking and reverse engineering: A program is a set of zillions of instructions that the computer runs to do stuff for us. The computer executes these instructions one at a time. Using a debugger we can step through and look at each instruction to see what's going on. Out of the zillions of single instructions, there will only be a few (well, a bunch) that we care about - like, say, the ones that say "If this registration number is incorrect, then exit the program". We then just need to change it to say "If this registration number is NOT incorrect, then exit the program". Pretty easy hey?
The catch is that machine level instructions are presented as assembly code - a very low level programming language which is bloody hard to understand. The more assembler you learn, the easier it is to figure out what's going on. I'm told. You at least need to know the basics if you want to hack around - otherwise it's like looking at random squiggles and dots.
Have a search for "asm tutorials" or "assembler tutorials" and you'll find some good'ens. However, the best resource I found is an old DOS .exe called the Ketman x86 Tutor or something. It's seriously great for learning, but was made for DOS so some bits (like file access) don't work. It's also the "demo" version - but for picking up the basics it's awesome++. Another way to figure out assembler is to write some very basic programs in C, then run those in the debugger. Oh. The debugger...
Let's get Hackin'
First up, you have to get WinDbg, the windows debugger from Microsoft. Then configure it to get the symbols from the Microsoft Symbols server. Or just search for "WinDbg Symbols" and you'll find some good set up info.
Once you've got that going, open Pinball and open the debugger. From the debugger select "Attach to Process" and select the pinball.exe process. The debugger springs to life! Have a look at the pinball game now. You can't mess with it. It's just sitting there waiting to execute the next instruction.
Now, in the command window type
x pinball!*. If you have your symbol path set up correctly, it will go and grab the symbols (whatever they are) from Microsoft. Then it will display a bunch of information about pinball.exe - including all the function names, and some variables!
Look through the list - there are heaps of interesting things. And you can set breakpoints on ANY of them. Unfortunately most programs in the wild don't come with "symbol" information like this. It certainly makes life easier for us beginners though.
After checking out the list I ended up setting a break point on what looked to be a "key down" function, as I reasoned that this would be where it checked for cheats. I typed:
bp PINBALL!pb_keydown - bp means breakpoint. When the program is running, and hits a breakpoint, it will stop executing instructions, and give control back to the debugger. Next hit F5 to continue running the program. The debugger says "the debuggee is running...." and Pinball starts playing again.
Now, back in the game press the any key. Pinball freezes again. That's good - the debugger has stopped at our breakpoint.
You can then step through each of Pinball's instructions with F10 (to jump over function calls) and F11 (to step in to the function calls). Each time you step over an instruction it executes it and goes to the next instruction. By continually pressing F10, or F11 you are now actually running the program very very very slowly... Look at each instruction as it passes - after a handful of instructions you will see the code:
call PINBALL!pbctrl_bdoor_controller. "call" is the instruction to start a function or procedure and "pbctrl_bdoor_controller" is the function name. Hmm... "bdoor_controller"? Could that mean... back-door controller? (spoiler: yep, it does!)
Well, who cares about "key down" functions when we've got "back door" function! Remove the breakpoint we set (press F9 in the command window to get a dialog box of breakpoints) and the set a new breakpoint with
bp PINBALL!pbctrl_bdoor_controller. Now when we run pinball it will break at the start of the back-door code!
Next, have a look at the back-door controller function in the disassembly (View->Disassembly). The function is a few pages long. This is pretty good - we no longer have to worry about the zillions of other instructions anymore, we just need to figure out what these ones do. It's a good idea to step through the function a few times just to see if anything obvious looks useful. Once you get to the "ret" instruction (ret = return) the function is finished - so hit F5 again to run the program, and press another key in Pinball. You'll be back at the start of the function again.
It can be hard to get an idea of what's going on. So I copied-pasted the function into notepad and had a lil' study. Buried in the middle I noticed this assignment:
mov [PINBALL!cheat_mode (01024ff8)],eax. Oooh! A variable called "cheat mode"! That instruction says that the variable "cheat_mode" is stored in memory location 01024ff8. So open up the memory window (View->Memory) and type that number into the location bar. The first byte you see is 00. We all know that 0 means off and 1 means on, so edit the first byte to be 01. Now, disable your breakpoint and press F5. Pinball is running in cheat mode!!!! Woooo!!!
That was pretty easy eh? But that's just half the work. We don't want to have to edit memory or write a patch to get in to cheat mode if we don't have to. Time to figure out how the bdoor_controller function really works...
The back door function
My guess was that the program would need to get the key code of the key you pressed, so I started looking in memory for where that might happen. I noticed this code about 9 instructions in to the bdoor_controller function:
0100e1c6 mov eax,[ebp+0x8]. I read somewhere that the "ebp" register is where arguments are stored for function calls, so I guessed that this would be reading an argument passed in to the bdoor_controller function.
I opened up the "memory" window in the debugger and typed in "eax" - this shows the memory that is pointed at by the eax register. As it passed the above code the value in that area of memory changes! I then ran the program, and pressed another key - it changed again. Yep - this is where the key you pressed is stored. The number you see in that memory location is the key code in hexadecimal.
I then spent the good part of an afternoon stepping through the back door controller function figuring out how it worked. Basically, If the key you press is the start of a cheat word it assigns a number to a counter. If the next key you press is the next letter of cheat, the counter is incremented. This continues until you press the last letter of the cheat, with the correct number in the counter. Then the cheat executes.
There were two ways I found the cheats - one was to find the counter value that was required to execute the cheat instruction and then work backward by finding which letters incremented the counter to that value. The other way was to find code that initially sets the counter value and worked forwards from there, writing down the letters that incremented the counter each time.
Here's an example of how I found the extra ball cheat, using the "working backwards" method:
Find the code that runs the cheat:
0100e477 call PINBALL!table_add_extra_ball (0100c2f3)
Follow the instuctions upwards looking to see what would need to happen for this code to get executed. A few instructions up there is this compare, followed by a conditional jump:
0100e463 mov eax,[edge_man+0x14 (01025050)] ; Get Counter
0100e468 cmp eax,0x3f
0100e46b jnz back_door+0x2ce (0100e47e) ; Jmp if counter not 63 (x3f)
As the instuction before 100e463 is a jump statement, then execution must get to here from somewhere else. So in my copy-pasted function I searched for "100e463" to see where it gets called. There is only 1 occurance and it is here:
0100e453 jz back_door+0x2b3 (0100e463) ; Jmp if its "X" (x58)
Ta da! The last letter of the cheat is "X"! But for the cheat to fire, the key needs to be "X" and the counter needs to be 63 (0x3f). So now we need to find where the counter is compared to 62. I searched the function for 62 (0x3e) and found the place where the counter is compared:
0100e25a cmp eax,0x3e then followed the code backwards from there. As before, there is a jump statement a few instructions up, so this bit of code must get called from somewhere else. The instruction after the jump is 100e24c, so I searched for this and found:
0100e1fc jz back_door+0x9c (0100e24c) ; Jmp if its "A" (x41)
The second last letter is "A"! Now simply repeat this until you find the area where the counter is initialised and you've got the whole cheat! Tedious, but strangely rewarding.
There are some cheats I think that must have been removed before the game was released, or were put in there as red-herrings. I found the word "QUOTES" in the routine, as well as the word "CINEMATRONICS." - both which appear to do nothing in the end.
That's not too tricky hey. Picking up some assembler makes the process a lot easier, but as long as you think about how the program would have to work in a higher-level language you'll figure it out.
As a bonus I've included an annotated text document of the routine in case you get stuck. Let me know if you find anything else in there!