Reverse-engineering a proprietary game server with Erlang Erlang, the fear of game developers... Loïc Hoguin - @lhoguin Erlang Cowboy and Nine Nines Founder

Background

Why reverse-engineer? Curiosity

Research the game

Build your own server Because you want a challenge Because the official server is discontinued



Frowned upon Few game companies understand

Data mining is cheating

Reverse-engineering is cheating

Even if you don't take advantage of your knowledge

Anticheat technology Online games feature anticheat technology

Often instead of real security

You have to bypass it, undetected

It's OK, anticheat systems are full of flaws

Windows Online games usually only run on Windows

Wine doesn't work because of anticheat programs

A few steps require a Windows box

If you're doing any gaming you probably have one

Phantasy Star Universe (PSU) SEGA game released in 2006

US version shutdown in early 2010

JP version still running

Protected by GameGuard (check files, memory, cheat tools...)

TCP for patch server, SSL for login/game servers

My example for this talk

Packet logging

Undetected logging Methods available can vary depending on the game

Common methods: Snooping Hooking a function on packet receive/send Man in the middle



Breaking through PSU's SSL Find the client SSL certificate

Try connecting to the server from Erlang

Use hosts file to redirect the client to localhost

Make the client connect to Erlang and redirect the packets to the server

You just built a proxy for the game

Packets going through the proxy are readable

Tee A command that redirects input to both standard output and file

Make the proxy save to a file at the same time as redirecting

Proxy hint You can always send the server whatever you want now

You can read, modify, filter packets

You can send any packet anytime

You get more control than you would through the client

Though you need to know the protocol first

Packet analysis

Protocol First we need to figure out the general framing by hand

Open the log file with a hex editor and find packet boundaries

We got a packet size followed by a command number and the packet

The protocol is 32bit

Spreadsheet help Parse the file and aggregate the info to CSV files

Figure out the field boundaries

We got 8bit, 16bit, 32bit data, 32bit floats, ASCII and UCS2 strings

Always take notes of what you are doing

Aggregate the field values Get a clear view of what values a field can take

Sometimes the value never changes

Knowing the values allow you to guess the field purpose

Example: player ID, player level...

It's actually not that hard to figure out most of them

Game mechanics A few key values hide a lot of secrets

Player ID identifies a player account

Quest, zone, map and entry IDs identify the map you play

Target ID identifies the player object in the zone

Client and file analysis helps figure some of these out

Extracting files

What files? Quest and zone files

Quest files identify the mission played

Zone files define the scripts and objects in a set of area

If we are to write a server, we must understand those

File extraction The files aren't always named or identified

Extract and tag them as properly as possible

Ignore duplicates

Get a good SSD for this, it can represent GBs of data

Files analysis

Client files too Analyze both client and server files

They share file formats

PSU has an offline mode where more missions can be found

Sometimes file formats can be found on Google, often not

Start with a debugger First, remove the anticheat technology

Load the game up to the game title

Break at all file loadings (find them with a dissassembler)

Press Enter, loading the login screen

You now have the ASM for loading the file

Step by step Tediously advance step by step to find the interesting functions

The main archive format was encrypted using a blowfish variant

It was also compressed using a custom LZE algorithm

We got through their "security", let's extract the files

Concurrent extraction Use Erlang to concurrently extract all the files you have

This can take some time, but less than if you had to do it on 1 core

Chances are your extraction code is wrong and doesn't handle edge cases

Continue with an hex editor Hex editing allow you to isolate values and group of values

Problem: some files are just structs and arrays with pointers

Pointers get converted to real memory addresses on load

We still need to use a debugger to figure these out

File parser We now have enough info to write a parser for all files

We should make sure the parser gets values in the right range

Pattern matching allow us to crash on unexpected values

Also crash on values that don't seem to change

Concurrently check our assumptions Parse all files concurrently with range checking

If all files pass, then all our assumptions are verified

Bonus: convert the files to readable formats

Prototype server

Validating protocol assumptions Using the proxy would be too limited

We need a valid implementation checked against the client

PSU's protocols Patch, login and game servers

Patch is a very simple TCP protocol

Login and game are the same SSL protocols

Login just redirects to the game server on successful auth

First implementation Make use of the previously logged packets

Take one log and just send all the packets unmodified

Reach in-game and stop there

Figure out the packet order

Try modifying values and check that nothing went wrong

Trial and error Figuring out values and testing them is a trial and error process

We're developers, we're used to do this

It gets easier when we properly reach in-game

Trial and error

In-game Open the menus, move the character, enter rooms

In other words: make the client send packets!

Note what packets are sent when you do something

Find in the logs what is replied when it happens

Responses Same as before, start sending a logged packet

Then figure out the values and test things out

Write a function that does it for you for next times

Shell testing You don't have to wait for client actions

Use the shell to send packets directly from the server

Make your character warp around!

Test things out thoroughly

Warping is good Make sure to write a quick command to warp around

Changing areas allow you to unstuck yourself

The client doesn't do everything asynchronously

Feedback loop

Lengthy process We need early feedback

We must not make the client disconnect

Reconnecting makes us lose at least 1 minute!

Reloading Code reloading allows us to test fixes right away

Data files can be reloaded too

Client can be forced to reload an area through warping

Don't crash in the network layer We must not kill the socket

When something bad happens, print the error in the console!

If a packet can't be parsed, print its hex representation!

Tips: also print when something is parsed properly

You can always crash after you finished working on the server

Still trial and error Someone can help by figuring out values in the client

But this is still mostly trial and error

Although it's much better thanks to Erlang

Demo