How to hack Flash SWF games for fun

My roommate and I wanted a good and silly online game to disconnect from work. We came across to this good looking 8 ball pool game:

Hacking a Flash SWF game

It has a few simple yet addicting mechanics. On the right we can see there's plenty of people and a public chatroom and on the left the game itself.

People challenge each other to play for points and rank on the site. My roommate and I are pretty even, but I want to give my luck a little push. So I had to break this game. But how can I cheat here?

If you right click, you'll quickly see it's a Flash app. Or, if you're a bit paranoid like I am, you'll have it blocked automatically in Chrome's privacy settings. I recommend you do it as well.[1]

First look

Let's go back to cheating my roommate! We can see what is exactly being loaded to play the game easily with Chrome Developer Tools (similar to Firefox and other major browsers):

Chrome Dev Tools: Network tab

I wasn't expecting two *.swf files to play one game! But here they are. Now, because this website offers multiple games, and from further research not worth mentioning, it is clear that motor.swf loads first (the people, the chatroom and everything else) and then it loads the game requested on the left, our 8ball.swf.

What is a *.swf file? Why do they use it?

Way before HTML5 and CSS3 were around there was an easy way to make animations, games, programs, ads and so on, in browser: Adobe Flash (formerly known as Macromedia Flash). Remember when youtube didn't have an HTML5 option? That's right.

Flash programs can be "compiled" (I'll explain the quotes in a moment) into a *.swf file and run in any major browser.
There's plenty of bad stuff about it, but it's still a solution to the eyes of lots of developers and managers. Of course, it's also a big deal to port an already made flash gaming platform to a modern solution.

Most important of all: a lot of people still think that their code is safe when they "compile" their flamboyant game into a comfortable *.swf as if it were an *.exe. But it is not the same. The source code is inside the *.swf, essentially in plain view.

From the Wikipedia:

A compiler is a computer program (or a set of programs) that
transforms source code written in a programming language (the source
language) into another computer language (the target language), with
the latter often having a binary form known as object code. The
most common reason for converting source code is to create an
executable program.

But SWF files are not compiled into a machine-like level, nor they run directly on your operating system. You can open one up and get your hands inside that app or game, and change the code to your liking. In a platform where there are lots of people playing, for hard earned rankings and tournaments... it's a big deal.

Let's get down to it >:)

Downloading and decompiling the source code

From Chrome's dev tools I can easily copy the *.swf's URLs. I'll use cURL to download both files (you can use wget or any other similar tool in all platforms):

     > curl -O http://XXX.com/8ball.swf
      % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                     Dload  Upload   Total   Spent    Left  Speed
    100  561k  100  561k    0     0  1748k      0 --:--:-- --:--:-- --:--:-- 1750k
    
    
    > curl -O http://XXX.com/motor.swf?etc...
      % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                     Dload  Upload   Total   Spent    Left  Speed
    100  626k  100  626k    0     0   745k      0 --:--:-- --:--:-- --:--:--  745k

That was easy! Now it's time to open up their guts.

There's a real diversity of the so called SWF decompilers; I'll use JPEXS Free Flash Decompiler. Let's start with motor.swf:

JPEXS Free Flash Decompiler

As you can see, you can browse through (and tamper with) images, texts, sprites and so on. We are interested in the scripts. That's where the code is. Lots of code.

Most of the files in the scripts folder are libraries needed to make the game run or add some functionality.
The main script file has about 4k lines of code. Seems a lot but in 5 minutes you can skim through and get a rough idea what you can play with. For example:

    if(param1 == "win" || param1 == "lose" || param1 == "draw")
                {
                   _loc4_ = this.miUser.jugando.split(".");
                   if(_loc4_.length == 3)
                   {
                      [...]
                      _loc5_ = new URLRequest(this.direccionWeb + "xt-darPuntosPartida.php");
                      _loc5_.method = URLRequestMethod.POST;
                      _loc6_ = new URLVariables();
                      _loc6_.haz = "normal";
                      _loc6_.j = this.juego;
                      _loc6_.uNMio = this.miUser.userName;

We could modify param1 at any time and become instant winners through a chat command for example. My roommate would smell something fishy though...
And if we cared about points, we could tamper the requests to xt-darPuntosPartida.php which is where points are tracked and logged to impress the ladies... ;)

Since this motor.swf is what loads the game, controls the chat system, the users, etc. we could just eliminate the protections our client has to prevent flooding, uppercase talking, swear words... It's all here (more on that later).

Even more fun: there's an "internal" command/messaging system (provided by the SmartFoxServer engine this game relies on where clients send their internal messages to the rest of clients as notifications or orders, for example when they leave the room or start a game. But there are more messages that can be sent... We could directly kick people out. Or be "sneaky" and send an internal message to someone's client indicating that they have lost the match (even if they haven't...).

There are no checks, no ciphers, nothing.

Let's give a bit of an advantage to ourselves, shall we? That's why we're here after all.

Making winning a lot easier

Let's now take a look at the other file, the game's file: 8ball.swf:

JPEXS 8ball.swf code hacking

Here, inside the ochoball folder there are several files. One file controls each aspect of the game: ball physics, table physics, forces, precision, etc.

This game gives you a little hint of where the ball is going to go by displaying a small line. What if we were to make that line larger? That'd be an improvement! I could aim like a rockstar.

Turns out, the developers thought of naming the tip of your stick "flecha" (arrow, in Spanish). That "flecha" is responsible for drawing trajectory lines. I won't bore you with the details, but the game uses simple math to figure out those trajectory lines. We're interested in these two shapes:

try {
  if (this.linea_dir_bb != null) {
    removeChild(this.linea_dir_bb);
  }
  this.linea_dir_bb = null;
  this.linea_dir_bb = new Shape();
  this.linea_dir_bb.name = "linea2";
  this.linea_dir_bb.graphics.lineStyle(2, 15858334, 0.5);
  this.linea_dir_bb.graphics.moveTo(pos_x, pos_y);
  this.linea_dir_bb.graphics.lineTo(pos_x + this.bola_direc.velx, pos_y + this.bola_direc.vely);
  addChild(this.linea_dir_bb);
  if (this.linea_dir_bgolp != null) {
    removeChild(this.linea_dir_bgolp);
  }
  this.linea_dir_bgolp = null;
  this.linea_dir_bgolp = new Shape();
  this.linea_dir_bgolp.name = "linea3";
  this.linea_dir_bgolp.graphics.lineStyle(2, 15858334, 0.5);
  this.linea_dir_bgolp.graphics.moveTo(this.vector_bolas[j].pos_x, this.vector_bolas[j].pos_y);
  this.linea_dir_bgolp.graphics.lineTo(this.vector_bolas[j].pos_x + this.vector_bolas[j].velx, this.vector_bolas[j].pos_y + this.vector_bolas[j].vely);
  addChild(this.linea_dir_bgolp);
}

One of those lines tells where the white ball goes after impact, and the other one tells where the ball you impacted is going to go. It sets a style (lineStyle()), sets its initial point (moveTo()) and then it draws to where it should go (lineTo()).

We don't want to change its appearance or starting point, but we do want to make it larger, so the endpoint has to go further. So let's add a multiplier to simulate greater force on the velx and vely properties to make the line is larger. Something like this.vector_bolas[j].pos_x + this.vector_bolas[j].velx * 6 for both properties.

JPEXS Decompiler allows us to edit and save code changes, but when you try to do it, you'll get this message:

Playerglobal.swc

Nothing to worry about, it's just a free file you can download from Adobe Debug Downloads, down the page. Download it, save it wherever and tell JPEXS's advanced settings, Path tab, where to find it.

You can now Edit the code and then Save it. And that's it. You have your own cheated SWF file.

Loading the modified SWF in the browser

Now that we have our cheating version of the game, how do we actually play it online? If we navigate to the game's website, it's going to load the default version and that's no good. We need to trick the browser into using our own file instead of the server's.

There are several ways to do that: using particular browsers, modifying the cache and tampering its info state, replacing the file through proxies, using a sniffer...

I'll use Charles Proxy's map-local feature, they have a free trial. You can also use Fiddler.

HTTP Proxy simple diagram

Our computer is on the left, Charles Proxy center and the game's website on the right. We just need to configure the proxy so that when the server gives us the game's SWF file, we discard it and replace it with our own.

In Charles Proxy is as easy as this:

Charles Proxy

And then you just need to make your browser point at the proxy (I use a Chrome extension called SwitchyOmega, but anything will do) and reload your browser. You may need to empty your cache or else it'll load the old cached SWF already existing in your computer which isn't modified.

And voilá!

Isn't this way easier to aim?

Now I'm definitely winning every time with those massive trajectory lines.

Let's remove some chat restrictions too while we're at it, like these:

if(this.miUser.userName.substr(0,6) == "Guest_")
         {
            this.SystemChat(this._("Guests cannot talk on the chat. Please, register to have full access to the site."));
            return;
         }
         if(this.Experiencia == 0)
         {
            this.SystemChat(this._("For safety reasons, new users are not allowed to use the public chat. Enjoy the game, very soon you will be able to chat with other users."));
            return;
         }
         if(st.length > 230)
         {
            this.SystemChat(this._("Message too long (the limit is 230 characters per message)."));
            return;
         }
         var ContadorMayusculas:int = 0;
         var ContadorMinusculas:int = 0;
         var VecMayusculas:Array = new Array("A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z");
         i = 0;
         while(i < st.length)
         {
            if(VecMayusculas.indexOf(st.charAt(i)) >= 0)
            {
               ContadorMayusculas++;
            }
            else
            {
               ContadorMinusculas++;
            }
            i++;
         }
         if(ContadorMayusculas > 10 && ContadorMayusculas > ContadorMinusculas)
         {
            this.SystemChat(this._("Please, don\'t use so many capital letters."));
            return;
         }

... and many many more. Just commenting them out works fine in this case.

Possibilities and conclusion

What else can we do here? In this particular game we could add functions in the code to add more trajectory lines (i.e. calculating band bouncing). Or grab admin privileges and use the AS3 and SmartFoxServer's API to kick users at will, get all points, automate an always-win bot, flood the chat, figure out a payload to send to a real admin and escalate privileges... basically cause any mayhem we want with virtually no detection. We could create all sorts of problems.

These SWF clients and the server trust messages from any source that pretends to be an admin because developers never thought there would be forgery. They thought, as most do, that SWF is some kind of unmodifiable, safe *.exe file, but it's not. At all.

Never trust the client


  1. If you don't want unwanted, potentially bugged and resource consuming plugins taking over your precious web-surfing peace, check your settings. ↩︎