web nodejs electron reverse-engineering
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.
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
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.
You should end up with something like this:
I tried out '_0x4a77' 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!