How I Broke Graal in 2 Days
For those of you who don’t know, Graal is an old popular MMO from the early 2000s on the PC that lets players create servers, design levels and otherwise create mmo experiences much like BYOND or Furcadia. The game has 4 main servers that people can play on each with a different genre of gameplay. One is fantasy (Zodiac), one is mafia/gunshooting (Era) and the other 2 aren’t super relevant since they have < 5 players. The most popular gamemode is Era which averages ~50-100 players for the last 15 years. Graal itself is just an engine based off of Zelda, and there are tons of developer tools for adding stuff that loads at runtime to create an actual game.
Ultimately I was able to break the game and accomplish feats like alter my player’s stats, get infinite ammo, make my punches instantly kill and buy items from stores for free (items that take weeks if not months to grind for). I would like to say that I had no malicious intent in doing this and didn’t abuse this power at all. It was merely out of curiosity. I did notify the administrators of all the vulnerabilities I found.
How I broke it
Graal is actually not that easy to hack on if you want to do anything complicated. It’s packed with Themida and makes it basically impossible to do static analysis. If you try to attach a debugger with CheatEngine the game will automatically close. I actually unsuccessfully tried to get anything better than a teleport hack out of this game for 4 years where every year I’d make some attempt at hacking it but then fail because of how difficult it was to get a debugger attached (the TP hack is EXTREMELY easy to do). The game purposefully makes it very difficult to cheat in and does take a lot of precautions to dissuade hacking. However, there is no real anti-cheat except preventing the game from being easily reversed.
The way I got around both these security protections is by just by using VEH debugging and never really doing much static analysis. But still, this is only the beginning of an attempt at breaking this game. We need to understand how exactly the game works because otherwise it’s going to be very difficult to attack.
Graal contains many layers of abstraction than in your traditional game. That is to say, the core aspects of a server you’re connected to aren’t actually contained in the binary when you install the game but are loaded once the game is started up and sit on top of the actual engine. In Graal there’s not a lot of hard coded stuff in the binary outside of accomplishing this. All the game logic can be found in clientside scripts written in something called gscript which interact with the game server.
I believe the files are .code
files in the weblevels/
directory, but I have no idea how exactly the clientside script .code
file format works because I can’t find the routines that handle this in the binary–nor am I exactly certain that there is a way to access this information. Give me a few more days and I bet I could do it though.
This lead me to explore attacking Graal by getting into all the data that the client/server send back and forth to each other. Most of the testing was done on Era but I also found just as many bugs on Zodiac.
Attacking zlib
The big weakness of Graal is that it relies on zlib
for compressing a lot of its data. I figured the game was doing some compression/decompression of data using it, so I attached breakpoints on Graal.inflate
and Graal.deflate
.
I was able to figure out that this was zlib
inflate because when I looked at the first argument in eax
and compared it to the first argument of zlib.inflate
I saw that it was basically a form of zstream*
except with int
s instead of long
s for some of the data types. So, all the data that gets sent to the server is in deflate
(compress) and otherwise in inflate
if it’s coming to us.
I was able to find data like this in the inflate
output buffer, so I knew I was on the right track
which surprises me because it appears the game sends its data in plaintext more or less!
Writing a hook
To attack the game’s netcode I had to write hooks for inflate
and deflate
. Basically used the technique described in this article except I had to use cdecl__ which took me 3 hours to figure out. The only difference between the hooking technique I used here and my previous hooks is that it involves writing a jmp
to your hook, writing back the original bytes, executing the original function, writing back the jump and returning the original function output. Before I was trying to some nightmare mode thing where I was writing raw assembly but then I couldn’t call functions that threw exceptions, had to manually allocate stack space for variables and though it worked it was too messy. To this day I can’t figure out how to build detours on Windows, so this would do.
In the end the code wasn’t super interesting, but it’s crucial for being able to interact with the input/output bytes of the buffers during compression.
int _cdecl HookedDeflate(z_stream* stream, int flush)
{
memcpy(origDeflate, oldBytesDeflate, SIZE);
std::string str;
// Too big of messages are just blobs of large data and not packet related
if (stream->avail_in > 0 && stream->avail_in < 1048) {
int j = 0;
while (j < stream->avail_in && stream->avail_in > 0) {
char c = *(char*)(stream->next_in + j);
str += c;
j++;
}
if (str.find("HitNoPk,Punch") != std::string::npos) {
// ...
}
else if (str.find("removeweapon,1") != std::string::npos) {
int idx = str.find("removeweapon,1");
stream->next_in[idx + 13] = '0';
}
..
In the above excerpt preventing the removal of an item from the player’s inventory in some cases is as simple as changing a 1
to a 0
.
If we look at the messages we send to the server it’s pretty interesting
The game uses a weird string-like format that isn’t exactly string like because there are some binary parts, but most of the message/RPC-like protocols just involve sending data like this as strings. Eventually I stumbled upon the way items are bought, for instance it’s typically comma delimited and contains store name, action, quantity, cost
and just set the price to 0
(someone didn’t do bounds checks).
I noticed that each shop seems to use a different store script, so I would guess that some shops are vulnerable while others aren’t with different scripts. It was pretty cool being able to spend 0 spar tickets on a gun instead of 3500, and quite the dopamine rush! I couldn’t believe that worked.
The other thing I tried doing was making my punches instantly kill, which was as easy as changing the damage parameter. I also tried getting infinite money by sending floating points in, but it just rounded up the values. At no point could I overflow any data though or send negative values (some scripts did do bounds checking). I’m sure there are TONS more vulnerable scripts that I didn’t get time to test, but I could only inspect a small amount of messages.
On Zodiac (the above image) I was able to make my fireballs do MAX damage very easily since the damage of a spell is controlled by the player for some weird reason. Normally they only do 251 or so at level 1.
Conclusion
That about wraps it up. I left out some of the stuff about what the client receives since I didn’t think it was super interesting, but you can’t really prevent yourself from taking damage in the game or anything. All in all I had an insane amount of fun doing this!
I believe the game tried security through obscurity since they didn’t think anyone could actually change the data that gets sent to the server.
Future work would include figuring out the .code
file format and being able to not just packet edit but construct packets.