pedram@smash:~/blog$ cat hacking-the-pirates-of-the-caribbean-online-mmorpg.md

# Hacking the Pirates of the Caribbean Online MMORPG · 2008-06-23

My DVLabs teammates recently presented on Reverse Engineering Dynamic Languages, specifically Python, at RECON 2008. As a case study, we demonstrated hacking cheats into Disney's Pirates of the Caribbean Online game. The game has received some pretty solid reviews and boasts over 10,000 paid online players. They also have a free download available for those of you who would like to follow along. Let's take a look at some of the hilarious effects of our case study and then cover some simple technical details so that readers can reproduce to join in on the fun.

To be clear, the cheats shown below are the results of client-side modifications. No malicious actions were performed against Disney's servers.

To start things off, here are the results of a simple jump hack which allows the avatar to leap over 15 times higher than what he otherwise should:


This next video demonstrates a similar cheat, modifying our character's speed. Towards the end of the video you'll see us stop and wait for another user to run by to give you a sense of what is "normal":



Here is what the ridiculous jump height looks like from the perspective of another user:


Some side effects of the jump hack include the ability to... fly? Apparently if a character is able to leave the "sphere" of influence of their ship, the physics engine sets the avatar's Z coordinate to the height at which they moved away from the ship:



Some more flying... we can actually fly along the water faster than any ship in the game can move:



Except of course, our ship, which can travel faster than enemy's cannonballs. This is the default ship given to you in the game. We have modified the ship's speed, acceleration, ramming power, maximum crew, maximum cannons, and other fun traits:



If you're interested in the technical aspects behind these modifications, read on...

There are three files we need to worry about modifying, all located within the install directory:
  • Phase1.pyd
  • phase_1.mf
  • Launcher1.exe
Phase1.pyd is a PE file containing frozen python objects that are deserialized and executed by a C stub function. This is where most of the values we will want to modify are located.

phase_1.mf is an archive containing DLLs needed during runtime, as well as a copy of Phase1.pyd. When the game is run, this file's contents are extracted and the internal Phase1.pyd overwrites the external one.

Launcher1.exe is a UPX-packed executable that contains MD5 checks to ensure integrity of the .mf and .pyd files. UPX is an executable packer commonly used by malware. However, it is quite efficient and is also used for non-malicious executables like this one. Here's an example unpacking session:

$ upx -d Launcher1.exe
        File size         Ratio      Format      Name
   --------------------   ------   -----------   -----------
   1925120 <-   1512960   78.59%    win32/pe     Launcher1.exe
Unpacked 1 file.

Once we've unpacked the file, we can make the 2-byte NOP patch to bypass the MD5 check, allowing us to arbitrarily modify the Python code within the other files. Here's the before:
Launcher1.exe (base address 0x400000)
    00422188 E8 88 8F FE+ call std::basic_string....
    0042218D 85 C0        test eax, eax
    0042218F 75 49        jnz short loc_4221DA
    00422191 8D 75 94     lea esi, [ebp+var_6C]

And here's the after:

Launcher1.exe (base address 0x400000)
    00422188 E8 88 8F FE+ call std::basic_string....
    0042218D 85 C0        test eax, eax
    0042218F 90           nop
    00422190 90           nop
    00422191 8D 75 94     lea esi, [ebp+var_6C]
The above code is where the Launcher does a string comparison between the calculated MD5 on disk and the MD5 value sent from the server. By overwriting the conditional jump that was there with NOP instructions, we force the check to always pass.

Now that the Launcher process ignores the integrity of the frozen Python code, we can go ahead and begin to modify the Python objects in Phase1.pyd.

The code objects within the PYD file are serialized, and while we could modify values in a hex editor for a quick and dirty cheat, there is a much more surgically precise and flexible method that we demonstrated in our presentation at REcon. The process involves deserializing and disassembling the Python code objects. You can then modify constant values, change byte code instruction logic, and re-inject the code back into the PYD. If you're interested in this method, you can peruse our slides. For the rest of you, we will continue and walk through some simple byte swap cheats that require nothing more than a hex editor.
 
If we load Phase1.pyd into a hex editor, or IDA, we can see string references to Python namespaces. Most of the interesting ones are suffixed with Globals. For example, pirates.reputation.ReputationGlobals, which governs experience needed to level up, pirates.economy.EconomyGlobals defines the cost of any item in the game, pirates.piratebase.PirateGlobals controls the physics of your character, including speed, acceleration, and jump height. The format of the object table within the PYD is: name, pointer, length. So, if you find a reference to a possibly interesting namespace, you can follow the pointer value to locate the serialized object. That would be where the values to change would reside.

I will cover one simple jump hack and leave the rest as an exercise for the reader. The default value for a character's speed and jump force in Pirates is 24.0. I discovered this using our AntiFreeze tool (see slides mentioned previously). For the purposes of continuing this walkthrough, here is what the disassembly of the Python code looks like that initializes the in-game character's physics attributes:
    load_const  161  # '24.0'
    store_name  196  # "'ToonForwardFastSpeed'"     
    load_const  161  # '24.0'
    store_name  197  # "'ToonJumpFastForce'"
    load_const  162  # '8.0'
    store_name  198  # "'ToonReverseFastSpeed'"
    load_const  163  # '120.0'
    store_name  199  # "'ToonRotateFastSpeed'"
    load_const  161  # '24.0'
    store_name  200  # "'ToonForwardSpeed'"
    load_const  161  # '24.0'
    store_name  201  # "'ToonJumpForce'"
The code above is from a disassembled python code object stored within the PYD. The float values themselves can be found with a simple byte search. However, because of how Python code objects are structured, these data values are not necessarily next to each other within the file. Also, if you are not rebuilding the PYD's object table, any changes must not affect the size of the file. Otherwise, the game will bail while trying to deserialize the code objects.

When a float value is serialized in Python, it is stored in a string representation. So, 24.0 is stored in file as "24.0f", the "f" indicating it's a float. For the purposes of a demonstration everyone can easily follow along with, simply search Phase1.pyd for all occurrences of "24.0f". Now, if we simply replace the correct 24.0 value to, say, a 99.0 value, our character will be able to leap buildings.

After we do that, however, we also have to consider the phase_1.mf archive file. This file contains a copy of Phase1.pyd, and it overwrites our changes upon game execution. Here is a quick Python script that will copy the contents of your Phase1.pyd file into the phase_1.mf so that the changes are persistent:
    #!c:\python\python.exe
    #
    # injects Phase1.pyd into phase_1.mf in parts because
    # the file size is too large for python to manipulate
    # in memory as a string.   

    pyd = file('Phase1.pyd', 'rb').read()     old_mf = file('phase_1.mf', 'rb')         foo = old_mf.read(1770) # index of PYD within MF     foo += pyd
    old_mf.seek(1770+len(pyd))     bar = old_mf.read()         result = foo + bar         old_mf.close()         new_mf = file('phase_1.mf', 'wb')     new_mf.write(result)
Once this has been completed, we are able to run the game and test out the injected cheat. If all went well, our character should be able to jump as depicted in the videos above. The cheats involving modification of the game economy and ship characteristics become more complicated--too complicated to attempt using simply a hex editor--but our slides give the information needed to start down that path.
 
Hopefully you've got a basic understanding of how to mess around with this game. The demonstrated method above is a quick solution, so check out the slides if you'd like to see the "correct" way to do it. Also, keep in mind these techniques can be applied to any game written in a dynamic language due to the inherent type information requirements. For those who would like to extend the research, take a look at EVE Online, which reportedly also utilizes Python.

Originally published 2008-06-23 on the TippingPoint DVLabs blog.

pedram@smash:~$