MelonLoader Sold Out?!

Right now, there is a flurry of accusations being thrown around of MelonLoader’s staff giving logs and IPs to VRChat.  Many of these accusations are backed up with screenshots (low-resolution pls fix, 144px is a thumbnail not a screenshot) and logs, so they look pretty authentic, but what’s actually going on here?  Let’s throw all the walls of text, accusations, responses, and drama aside, and be objective, scientific, and reasonable.  This is a reverse engineering blog, so let’s use our existing skills as game modders to look under the hood of our tools themselves.  I’ll also talk about how to trust your tools. 

Disclaimer: I should note that while I’m a modder of games in general, I have no affiliation with either side of this argument.  Using the tools below to reverse-engineer VRChat is a violation of the VRChat Terms of Service and will get you banned if you’re stupid about it.  I am using them here as an analysis of a tool injected into VRChat (which is the subject of this discussion), and will not be going into the processes of VRC itself unless necessary to describe how MelonLoader works. I also must note that I am not affiliated with VRChat, VRChat Inc, and that I’m not authorized to use their trademark, nor look at their works. The VRChat registered trademark is being used here in a referential capacity, which is fair use under US trademark law.

IMPORTANT: You should not use this general process on viruses, actual malware, or even suspect executables/DLLs without taking proper precautions, such as using a sandbox or (preferably) a designated reversing PC with a disk image.  I have already determined that I can trust these applications to not trash my PC, which is why I am so fast and loose with the following analysis, and I have experience in forensics and reverse-engineering, not to mention a piece of paper in a frame somewhere saying I’ve passed a bunch of tests in Uni regarding this sort of thing.  I have also backed up everything to an external NAS as a part of my standard backup policy.

So, with that out of the way, let’s start with the basics:  What is MelonLoader?  What does it do?

Backstory

Unity3D is a popular game engine used for lots and lots of games, ranging from the laziest possible asset swap, to highly popular titles made by AAA studios.  Under the hood, Unity uses a modified version of Mono, an open-source version of the .NET engine, to enable simple and efficient creation of game behaviors using UnityScript, C#, F#, Visual Basic, or any number of other languages that compile to the bytecode it uses (called Common Interface Language, or CIL).

For a while, it was very easy to add a mod to the game, as you had full access to the “managed” Mono DLLs and could decompile them (using ILSpy or dnSpy) or modify them directly using one of many tools such as Harmony or dnLib.  For many studios that dislike modding for one reason or another, however, this represented undesired behavior, and many different strategies were tried to eliminate modding, ranging from obfuscation to complicated integrity checks and DRM. 

Recently, Unity produced a new feature called IL2CPP, which converted the CIL bytecode into C++, and then compiled it into native code (which still interacted with Mono).  While this was presented as an optimization, many studios lept onto it as a method of protecting their code from modding and decompilation.

MelonLoader was born as a way to continue using CIL mods with Unity games.  It’s largely an ease-of-use wrapper around several other lower-level tools, chiefly IL2CPPDumper and AssemblyUnhollower, and it provides a modloader.

So, let’s see how it works, running on the assumption that we don’t trust the GitHub code, for some reason.

The Setup

We’re going to be using tools from a company called Sysinternals (which was absorbed by Microsoft a while ago), collectively called the Sysinternals Suite.  You can get them from Microsoft.  They’re designed to give you a huge window into the inner workings of Windows, and are particularly useful for reverse-engineering, debugging, and malware analysis. Procmon is going to be our tool of choice for this adventure. I’ll be using a filter to screen out everything except VRChat.exe.

I’m also using an open-source tool called Process Hacker to give real-time data on what a process is doing, what it’s connected to, which file handles it has open, etc.  It’s particularly useful for visualizing process trees.  You can get it here.  Just a note: It frequently pisses off antivirus, since it uses hooks and detours to get what it needs.  You’ll want to run it as admin.

Finally, for our networking, we’ll be using the free commercial tool Fiddler to see encrypted HTTP TLS traffic.  You’ll need to install the root certificate to see TLS traffic.  Wireshark will also work, but Unity is a bit finicky regarding root certs, and Fiddler works around this somehow. Also, Fiddler’s UI is specifically designed for this sort of workflow. (I’m not sponsored by Telerik, but it’s very useful in my field.) I think Burp Suite will also work, but it’s Java so ew.

Order of Operations

When you install MelonLoader, you are directed to install it by extracting it into the base of the game install folder, along with a DLL called “version.dll”. You’ll note that there are no executables for patching files, and no overwrites.

MelonLoader takes advantage of a weakness in Unity where it searches for version.dll, expecting the OS to tell it it’s in System32. Unfortunately for Unity, this leaves them open to a form of attack called DLL hijacking, where you can just plop your own (patched or proxied) version of a system DLL in the same folder as the game executable, and it’ll get loaded instead, since the OS checks there first. It’s a pretty common problem plaguing Windows, particularly installers, since it can be used as a privilege escalation attack when the installer gets waved past UAC. It’s not a big problem here, since games run without escalated privileges. Let’s see this attack in action using procmon:

Filter for VRChat.exe and Path containing version.dll
Procmon filter settings
VRChat looking for version.dll, immediately finds what it’s looking for… In the wrong place.

As you can see, VRChat QueryOpens VRChat\version.dll first. (QueryOpen is basically a try at opening the file, AFAIK; It’s not very well documented, since it’s some internal Windows thing.) Finding it, it then locks the file and uses it. The DLL loads up the real version.dll from System32 (so it can pass OS calls back to it), and the process continues along its merry way, without noticing that ML was injected.

What happens if we remove version.dll? In the next run, I renamed ML’s version.dll to version.dll.bak. I used the same filters as the last run.

NAME NOT FOUND
Unity working normally.

As you’ll note, it still checks for VERSION.DLL in the same directory as the executable (remember, Windows is case-insensitive), but failing to do so, it continues up the list and checks System32. It finds System32\version.dll and loads it. The game runs normally, without MelonLoader being injected.

So what does MelonLoader do once it’s injected?

Well, let’s restore version.dll, clear procmon, do a clean re-install of ML, and change the filters thusly:

Process and file activity, yay.

Start capture, and run:

There’s a lot here to unpack, so let’s take it step-by-step.

  1. First, MelonLoader opens a console window (represented by conhost.exe running, which is the internal console emulator for Windows).
  2. Next, we see a bunch of EXEs getting written to disk. This is actually the last part of the stage where MelonLoader downloads and extracts a few ZIP files, chiefly IL2CPPDumper and IL2CPPAssemblyUnhollower, not to mention the deobfuscation table and Unity dependencies. More on that in a bit.
  3. Next, IL2CPPDumper is run. This reads and dumps the metadata from IL2CPP to disk for the next step.
  4. AssemblyUnhollower is executed, and it makes a bunch of DLLs that modders can reference. They don’t contain useful data, but reference the native code in an easy way.
  5. VRChat continues, as seen by it reading youtube-dl to make some sort of hash or size check.

Next, let’s look at the networking side of things using Fiddler.

Networking

Close Discord and anything else producing traffic to minimize the shit you have to sift through. Next, open Fiddler, enable Streaming, enable Decoding, and install your root cert. Finally, make sure you’re capturing, and fire up your game.

Oh God

So, here we see a bunch of calls to xbox.com (built-in MS crap), Unity configuration and metrics that ML failed to axe, MelonLoader API calls, and github traffic. Let’s ignore Unity and Xbox traffic and focus on MelonLoader stuff.

  1. First, MelonLoader sends a HTTP GET /api/v1/game/vrchat with no parameters or body.
  2. The server returns a JSON object containing game name and ID, as well as obfuscation mapping file URI and IL2CPPDumper/Unhollower versions.
  3. It does this again, for some reason. (Bug?) [1]
  4. It fetches the Unity Runtime Libraries ZIP for 2018.4.20 from GitHub, which contains stuff mods need to run.
  5. It grabs IL2CPPDumper from GitHub
  6. It downloads IL2CPPUnhollower from GitHub.
  7. Finally, it retrieves the deobfuscation libraries from GitHub.
  8. Control is passed back to Unity and we get more Unity API crap.

What about Obfuscated Mods?

One of MelonLoader’s features is that it can detect control flow obfuscation in the mods it loads, and will refuse to load them. This is a safety feature to prevent users from loading mods they can’t inspect on their own. Unfortunately, one of the claims making the rounds is that this detection is triggering bans of users of obfuscated mods like Notorious.

Let’s try it out.

I’ve made a simple universal mod that pops up a messagebox when the game loads.

Now let’s obfuscate it using ConfuserEx 1.5.0 with Normal + control flow obfuscation.

Nice.

And now let’s try to load it.

Very nice.

While this happened, I left Fiddler open and noticed no untoward network traffic.

Trusting your Tools

Modding is extremely complex these days and involves lots of moving parts, bustling communities, and frequently these clash, resulting in miscommunication and confusion. Often, the best way to place trust in these tools is to pull them apart yourself to see how they tick, and determine on your own whether you can trust these programs and tools. Here, I’ve tried to give you a few more tools to use.

Conclusions

From the above, using the version of MelonLoader published on ML’s GitHub as of 11:02PM PST on April 10th, 2021, I can see no malicious traffic, data collection, or tupper worms. With the information currently at hand, I can conclude with confidence that MelonLoader is not selling data, logs, tokens, etc to VRChat. I have also completely decompiled both the loader and loaded DLLs of emmVRC and concluded that it is also not malicious or obfuscated, as of 1.1.0/2.8.4 (although they can change that very easily by swapping out the file served by BakaUpdate.php, which is why I hate the self-updater).

I would be happy to perform a similar analysis of mods/clients you use to see if they meet the same standards.

Updates and Corrections

[1] – April 25th, 2021: @HerpDerpinstine contacted me on Twitter to let me know that this was a hasty attempt at failover implemented after the OVH datacenter fire. MelonLoader’s team has plans for a better system.