Saturday, November 28, 2015

Adding Keyboard Support to a Crazy Taxi Clone

Note

I've wanted to do a blog/tutorial on how to add keyboard support to arcade games for a while - this game in particular was extremely easy to write a library for which is why it was selected - global names are not stripped, it's modular, runs on a modern OS - we'll focus more on strategy and technique rather than having to wade through obfuscation first. In practice, this is the same method I use for much more difficult games. 

Background

There are a vast assortment of arcade games - some well known, others super obscure. Although security is one component of getting a PC-based arcade game to run natively on a modern PC, what good is it if you can't actually play it?

A number of these games had a limited release, ran on dated hardware, and will eventually be lost deeper into obscurity - let's do something about that!

That said, let's dig up a strange one and give adding keyboard support to it a shot.

Frenzy Express


Ok, now we're in strange territory - this game came out in 2001 as a blatant ripoff of crazy taxi: the player races around town avoiding obstacles; collecting time and power-ups while stopping at pickup points and dropping them off across town. Given that this is a scooter and not a taxi, packages such as flowers, toys, and important documents (whatever those are) are carted around while the player tries to make 15,000$ to go on vacation.


First Time Run

The game itself runs fine without any security-related modifications (even on Windows 10). Of course, no input works besides the common exit keys (ESC or F12 - in this case, it's both).


It starts with a simple calibration screen that errors, but doesn't stop the game from proceeding. Eventually, we'll get this working perfectly as well.


We then hit the attract mode loop and everything seems to be working fine - albeit lacking input.

Ok - let's dig into this!

The Files

The files I have are from a Frenzy Express 2.0 Hard Disk - if you can find someone to get a copy from, follow along :)

There are two disks - G and H:

H has a rather small copy of windows (looks like Windows 95 or some other 9x):


G has two items in its root - a directory named 'FRENZY' (where we'll be spending all of our time) and WIN386.SWP (a swap file). The FRENZY directory can be taken out of this structure and placed anywhere - all paths are relative to the game.


The Frenzy directory contains a gl folder with textures, models, etc. The 'show' directory contains MP3s for music (I wonder if they have a real MP3 license...), The history folder has two files which are replay data for the attract mode and how to play, the sound folder contains all the effects, speech, etc., and the rest of the files are either dlls for various components of the game (sound, video, IO), the game executable itself (xwin.exe), or an x.cfg file which is probably game data. I'm guessing that boot.exe does something system-related, although I never looked into it.


Well... next step!

IDA

First, we should dive into the xwin.exe file - after all, this is the entry point for the game. However, a file named xio.dll might be interesting as well - perhaps the developers made the input modular. Jumping to the import table, it looks to be that way:


We have an XIO object that contains various functions for interacting with the IO (getting and setting along with some reset and initialization logic), it looks like we also have some globals as well (wow, could they have made this any easier?!).

Jumping into the XIO dll, we see that methods names are left intact (could we be any luckier).

Putting setVibration into hexrays, we see a few common elements:

A number of functions are simply void returned calls to do something to the internal state of XIO and/or talk to the hardware (we'll jump into this in a second).


This looks like it's checking some value and if it's 0, it sets the value back to a default of 3. After all of this , it calls that sub at the bottom with a 6XX number and the value we're passing.

This looks like some kind of write function - we have the 9x direct hardware access __outbyte here on a conditional. It looks like if the dword at the top is set, it will run another function but if not, it will push the value to hardware directly. At this point, we can assume that the dword at the top is a flag if the OS supports direct hardware access (any Windows before NT).


A quick look at  that upper sub confirms - this route uses DeviceIoControl to make the hardware call indirectly (think ioctl for Windows). Just to be sure - let's xref that dword in the previous function to see where it's set:

Makes sense - the GetVersion() line where it checks >= 0x80000000  is an old trick to determine if a Windows version is NT or later - they don't support direct IO access due to a number of issues unless it's from a driver.

Ok, so we have the general idea of how it writes - xref'ing DeviceIoControl again we find the read function:


Given that we have the general idea - we can now determine that the IO setup of this game works in a pretty straightforward fashion (game <---> xio.dll <---> hardware).

For clarity, we may want to build a struct in IDA to list the properties that we see (using the logic that a function like setSteeringBar will talk to the struct members that have to do with steering data, lamps with lamp data, etc).

We get something like this:


And when we run in hex-rays, things look a lot cleaner:


From this point, we should start first by trying to get to the operator menu or inserting a coin - any of the basic non movement-oriented inputs... just to get a feel for what we'll be doing the rest of the time.

Coin and Operator Menu

The game makes several calls to xio::update() - several due to the fact that it's responsible for updating all the states:


Right off the cuff - we notice that g_bKeyExec & 8 (or the 4th bit of that value) is a conditional to update the coin counter - looks like we found our coin button.

Looking deeper into the update input state, we can see that various bits are set to control buttons:


Looks like this byte controls buttons (more than likely the operator menu, coin drop, start, and the arrow keys on the machine).

To test this theory, let's use CheatEngine to mess with the values and see what we can have happen. First, however, let's force this thing to Windowed mode so we can more easily change values and debug on a single screen.

Hacking in Windowed Mode

First of all, the game contains a call to ChangeDisplaySettingsA to force 640x480:

We can easily nop out this function with no real issues (although the screen test on startup might show that it failed - whatever, doesn't matter).

The REAL meat and potatoes comes from CreateWindowExA: 


This is where it pulls the current screen resolution (1920x1080 for me) out and sets the window to that size. Unfortunately for us, the game at this point will render a big black window with the game sitting in the lower corner - not exactly what we want. Let's force this to statically render a window at 640x480!

The trick here is we need some bytes to play with - push eax for height and width are only one byte - the function call won't give us enough bytes to push a two byte value like 640 into it like that as those are at least 4 bytes:

However, we don't need GetSystemMetrics for width or its parameter index - so we have 5 bytes extra... still not enough.

Provided registers are not reused, we can use the GetSystemMetrics call above the other set values (the one for height) and keep it in the proper buffer with a bit of rewriting and move everything up the few extra bytes we'll have - provided the stack stays in good shape, we should be fine doing this:

The result:



There we go - it should be in windowed mode now; although the top will be cut off a bit because I changed the windows style to have a border so we could move it - no matter.


Fuzzing with CheatEngine

I love this thing - hats off to the team that made it. It's more than just for trainers, it can scan for codecaves, nop out variables being set, scan memory and update in realtime. It's a very useful tool for Windows debugging and fuzzing.

The first thing we'll want to do is find out where in the game the g_KeyExc variable is stored.

From this point, we can fire up CheatEngine and add it as a variable to watch:

While adding an address manually, we tick the pointer checkbox, set the value to byte, and put the address in below:


We fire up the game and attach it to cheatEngine and we see that the value is just sitting there - makes sense.
Now, we could try to modify the value, but remember that this is an IO set value - it's gonna be firing like every X milliseconds so it will quickly get overwritten - we need to first shut the IO library up so we can set it ourselves.

If we right click on the value, we can select 'Find out what writes to this address'.


The top one looks like it writes a lot - we can select Add to the Codelist to keep track of it and give it a fun name. In addition, if we right click on the line in the Code List, we can select 'Replace with Code that Does Nothing' and it will NOP this value being set for us - it can be restored later as well.

Now, if we set the value to 8, a coin should be dropped:



Success!

If we wanted to get even more fancy with testing, we could hook up some rudimentary input support via CheatEngine's LUA scripts, add keyboard handling to set the bits, etc, but I'll leave that up to the reader (I actually did this but I lost the lua script file, haha).

Writing the DLL

Ok, now that we know the base layout of all the dll calls the game makes, we can construct a dll based on a similar XIO object and replace the calls to hardware with scanning the keyboard. However, doing this would be (and was) an unnecessary task

*** IMPORTANT LESSON ***

One of the biggest challenges a lot of reversers face in the beginning is going too granular - sometimes it's necessary, yes, but in this case, replacing all the DeviceIoControl calls in unnecessary given the context of the game - the game itself reads from the globals mostly and sends some information back to the hardware, but we don't really need that - we just need to tell the game which values we want which is why CheatEngine fuzzing is a valuable step - it will let us see what values per the globals affect the game. In fact, I added all the globals to a CheatEngine table:


With that said, I did originally map out the device calls in the original dll (found on my github page: https://github.com/batteryshark/feXIOEmulator), but due to the convoluted way values are passed around - weird stuff happened: the steering wasn't working properly, the brake made the handlebars jam to the right when you let go, and acceleration was unplayable as the scooter went careening at warp speed toward the nearest objects.



Looking at the whole thing again, and making some modifications, the game really only needs a few exports:

It needs the various globals such as brake velocity, button state, acceleration, etc.
It needs to be able to open and close the interface (basically left blank for us because there's no hardware to make a device file for), it needs to update all the values, and get / set certain values... That's pretty much it.

The current I/O library as of this writing can be found here: https://github.com/batteryshark/feXIOEmulator

The dllmain is a simple dll entry point - nothing special.

The real meat is in the XIO.cpp and XIO.h files - the header like usual lays out the groundwork for the XIO object, and the  cpp file contains most of the logic (including the keyboard logic):



It's always important (especially if you're putting something up on github) to comment the living shit out of a library, especially when reversing. This way, you can catch logic errors or even data type errors.

If we were to, say, give lampState an unsigned char value (even though it shouldn't be an int ever) in XIO::selectLamp:

The game would refuse to link with the mangled name because it would be:
?selectLamp@XIO@@QAEXE@Z
instead of
?selectLamp@XIO@@QAEXH@Z

It's important to check the mangled names the game wants for hints as to the data types that go into a function if the names aren't stripped - in this case, they weren't. It's also important to name the functions the same unless you're using IAT_Patcher or something of the like to change the name in the binary itself. In this case, we don't need to.

Keyboard Input

All this time and no talk about Keyboard stuff yet - what am I doing? That's because the keyboard part itself is easy (besides maybe some hardware-side logic like analog stuff that we'd have to emulate in code) - figuring out what to hook it all up to is the challenge most of the time:



For this game, I used GetAsyncKeyState, I made a couple of defines myself, and set the bits I needed to set for each member function to do its thing. Remember, the original hardware could only send 1 byte back at a time and it only called once - so values are very small (some between 0-64 or 0-255).

This is where iterations upon iterations of tests come in. Set a value, launch the game, go into the operator menu / gameplay and see if it acts how you want it to act - rinse and repeat.

After a while, you'll get something like this:



Conclusion

Input writing for arcade games can be a fun battle back-and-forth with granularity, original intent, and other factors when you don't have the original hardware or have never even seen the machine itself, but the steps to figuring out what a game does from an unknown data source are all pretty much the same. If all else fails - debug, fuzz, fuck around with shit - see what happens. In the end, you might just get it working :)

Cheers!