FusionFall Retro: Electron Client

424 views web nodejs electron reverse-engineering

by CPunch

[NOTE: If you're from the FFU dev team first of all I have major respect for you guys. Recreating a childhood classic of mine means so much to me and shows you guys are extremely talented. Second of all, instead of banning me, I would be more than happy to help you guys! Please contact me at [email protected] for more info.]

Before we get started I wanted to say that this isn't one of my extremely technical posts, Electron by it's nature is one of if not the easiest thing to reverse engineer to date. Even calling it "reverse engineering" seems like an overstatement. Seriously use common sense when creating Electron apps and EXPECT everyone and their grandmother to be looking at your source.

Electron Clients are becoming more and more common. If you're not familiar with Electron, it's a chromium-based solution to write desktop applications using JavaScript, making it easy for Web Developers to adapt their application to desktop, with access to your filesystem, and tons of npm packages to do anything from inter-process communication to using raw sockets. Recently however, I've been seeing more and more people use it for *other* uses. Some are quite harmless but others :eyes: can have XSS vulns in applications in charge of updating firmware on embedded devices! Today I'm not going to be looking at that extreme end of exploitation, but I will explain how you can easily reverse engineer any Electron-Based application.

I'll start by explaining how Electron works. Electron, as I've said before, lets you convert web applications to desktop applications easily, under the hood it's powered by NodeJS & and a custom chromium client. It's an extremely powerful tool, with amazing applications being developed with it, however like all things web-based looking at the source of these applications are extremely easy. ASAR packages are usually used to pack the source contents of the web app, which is an NPM package we can install like so:

sudo npm install -g asar

FusionFall Retro is a revival of one of my favorite games from my childhood, they use an Electron Client paired with a Unity WebClient for their client. It's a strange solution, although it works. I installed it through Wine on my machine and it works great, you can do these same steps on a windows machine as long as you have nodejs & npm installed and change a few things.

I started by looking at the client in ~/.wine32/drive_c/users/cpunch/Application\ Data/FusionFall\ Universe/Games/Retro/ and if we look under the resources folder, we can see our ASAR packages.

You can easily unpack these into a folder called 'dump' with

asar extract app.asar dump

There will be a file called main.js, this is what will be ran when the client starts, basically the entry-point. Since it's javascript it's extremely easy to read and understand.

Later in the file it loads the BrowserWindow and tells it to go to the URL passed by the launcher. There's also a IPC library it uses to pass arguments provided by the launcher to the Unity Client. They also verify the URL argument by checking if "fusionfallretro.com" was *somewhere* in the string. Because of this, I can make the electron app go to any url as long as it includes 'fusionfallretro.com', even as a param like ?q=fusionfallretro.com e.g. "Retro.exe -url https://google.com/search?q=fusionfallretro.com" will successfully go to google.com. This could be prevented if they either just embedded the URL into the client (since it doesn't seem to change), or just by doing some key pinning and comparing it.

[Yes, I was at a starbucks while doing this lmao]

So, now let's actually start modifying the Electron client. First, i'm going to change the main.js file to open the dev console on the window after they load the URL. The launcher won't launch if the hash of app.asar is wrong (based on what it gets from it's web api, but I'll get into that in the next post.) However, we can still tell the client to load the webpage (https://playclient.fusionfallretro.com) without the launcher! Knowing this and applying my changes to main.js, I'll repackage it.

And then use asar to repackage the modified files into app.asar.

asar pack dump app.asar

Alright! We can launch Retro.exe from the command line, and it'll at least open the webpage which is all we need!

Retro.exe -url https://playclient.fusionfallretro.com

And then we can just right-click html, ctrl+a to select all and paste it in our favorite TextEditor.

Now, let's check what is actually running in the electron browser. If we look at the html source, there's a obfuscated javascript script.

Yeah, yeah I know it looks scary but don't worry. The original script is quite small and you'll find it's actually quite simple to reverse if you get creative. First, lets clean up that javascript real quick, I used beautifier.io, with no options checked. If you paste the script in there and press ctrl+enter, it'll output something a *little* more human readable.

You should end up with something like this:

Again, not the most readable thing in the world, but you'll see some things look a little bit more familiar. Now, we still aren't sure what the _0x4a77 functions return, but we can infer it's strings to help set up the unityobject. We can actually get all of those if we run everything above 'var tUnityObject' in a javascript console. Then we can call each function one by one and get their true results! So, open a new tab and paste everything above 'var tUnityObject' into the console. Now, it'll output 'undefined' if everything runs fine, now we have the environment setup, lets dump the arguments!

I tried out '_0x4a77[2]' which translated too "addParam." I continued to do this and eventually I dumped all of the arguments.

All this script does is load the .unity3d file and sets up our webplayer. If you notice the first argument to the UnityObject constructor, it looks like a base64 string, but don't be fooled. For a while I was stuck here, not knowing how the source URL was decoded from that. Then I decided to go back to the developer console in the Electron client. I eventually found that they had changed the implementation of UnityObject and hidden it obfuscated at the end of jquery.js!

If you look at the beginning of that, it's the same sort of obfuscation that was used in the last script! Now we can copy this and go back to beautifier.io, they have an option called "Detect packers and obfuscators," and it basically does what we did manually for the last script! Now that we have it a little more human readable, let's look at how they decode the source.

In the constructor of the UnityObject class, the first argument is passed to the setAttribute method with 'src'.

If we take a look at the method, we can see if the first argument is a string of a length of 3 AKA "src", it will decode the 2nd argument!

First thing it does is decode the first 2 hexadecimals into a base16 integer, which is the amount of times it was encoded in base64! it then decodes it that many times until the source URL is finally decoded!! So, lets rewrite this and decode our string! So, we can literally just copy and paste that and assign _0x9891x5 to our dumped string from the html, and get our URL!

[The script I rewrote with named vars so you know how they encode the source.]

And after running that, you should be left with the original, decoded URL of the .unity3d file!

Nice!! We successfully dumped the unity3d file! Next post I'll be talking about the launcher and how I debugged that to create my own custom launcher! See you next post!

Sep 30, 2019 by CPunch