On GameMaker game decompilation

On GameMaker: Studio game decompilation

It seems that questions about whether (and if so, to what extent) GameMaker: Studio and GameMaker Studio 2 games can be decompiled are being asked at a constant pace, and yet there are still no resources to clear up these questions. So I've decided to make a small post on the matter.

(originally published in May 2015, updated in 2019)

How it was in GM≤8.1

Older versions of GameMaker stored game data in a way that remains common to Lua game engines these days - that is, all game data was compressed and encrypted, but, in the end, stored without substantial post-processing.

And thus, eventually someone had the persistence to reverse-engineer the runtime's binaries, and wrote a tool that would reassemble the data from an executable to a largely-valid source file. Needless to say, such a thing was valuable if you were trying to recover lost work, and a bit of a disaster if you were working on a game with any online features or didn't anticipate a small chance of your work being stolen outright.

To protect against that, people then made obfuscators (to one of which I even contributed some methodology) and tools anti-decompilers (tools specifically subverting the expected executable structure), and things were kind of okay... so long as you were aware of possibilities.

How it was in GM:S

Among the key changes in GameMaker: Studio was the rewrite of how code was being compiled - now, instead of storing it in text-y format, it was compiled to bytecode, which made for smaller file sizes, higher performance, and not unnecessary information like comments or names of constants.

When targeting HTML5, GameMaker would compile the abstract syntax tree to JavaScript, combine that with runtime code, and run it all through a minifier-obfuscator to reduce file size and strip out any unnecessary information.

All this at a slight downside of it no longer being possible to execute arbitrary code from a string, but that's entirely fixable as of me updating this post.

Later on, YYC (YoYoCompiler) was introduced. YYC compiles GML to equivalent C++ code and then through an appropriate C++ compiler (most often, Clang), allowing to take advantage of C++ specific optimizations and to have the game code interface with runtime directly, all for great effect on performance and with some pleasant security implications (more on this later).

So, let's further look into what risks each of three output types (non-YYC, YYC, HTML5) has.

Non-YYC

Can it be decompiled?: To an extent.

It's no news that bytecode is by definition more decompile-able than native code, moreso with common stack-based virtual machines - as the code unrolls into instructions somewhat predictably, the process is also reversible - so long as someone is willing to sit through and write matchers for all possible instruction patterns.

GameMaker's reliance on numeric constants means that

draw_sprite_ext(spr_some,1, 100,100, 1,1, 45, c_white, 1.0);

is compiled as if it was

draw_sprite_ext(4, 1, 100, 100, 1, 1, 45, 16777215, 1);

(replacing all constants by their values in process)

Overall, the data that can be scrapped includes general code structure to the point of variable names (as they are shown in errors), but will not include resource names when used in code, constants, macros (which are expanded), or enums.

Additionally, modern versions of GM compiler will cull branches of if-expressions if the condition can be evaluated compile-time. For example,

#macro is_debug true
#macro Release:is_debug false
if (is_debug) {
    ver = "DEBUG";
    // ... some debug code
} else ver = "RELEASE";

would compile to just

ver = "RELEASE";

on Release configuration.

To counter this, tools could be developed to obfuscate most names (except ones referencing built-in functions and variables) and/or manipulate bytecode and file format to require manual cleanup.

While the later is a complicated subject, obfuscation is pretty simple. In fact, you could even do it by hand if you wanted:

Obfuscating variable names in a GameMaker: Studio game by hand
(mouseover/click to play GIF)

At this time, the only tool that offers anything resembling adequate VM decompilation is UndertaleModTool. While it isn't going to make you a project file, it is perfectly suitable for recovering lost scripts (though also see Recovering Lost Work) or making mods for games.

While you might seek for more tempting offers, remember to be careful, for else, as it goes with these delicate topics, you may find yourself downloading (or even having paid for) a prank at best, or malware/spyware at worst.

YYC

Can it be decompiled?: Unlikely.

As if C/C++ decompilation wasn't a complicated enough topic as-is, further decompiling that to non-C++ code is a really strange topic - to the point that I cannot recall any examples off-hand.

While it is still possible to scrap a list of script/object names, or, with substantial effort, detour a script for modding purposes, the nature of YYC makes it incredibly difficult to tell what was anything was supposed to do.

JS/HTML5

Can it be decompiled?: Perhaps, but why

On one hand, JS code generated from GML is the closest-looking thing to the actual GML, and minified JS is still JS, not machine code.

But then some GML-specific bits of syntax (like with-loops) compile more than one JS structure, and everything is minified/obfuscated, so you face somewhat similar problems to YYC.

So, with enough effort, you could figure out which 3-letter function is supposed to do what, and non-code assets are easier extracted than with other platforms (again, for browser-based nature), but it's hard to think of a valid reason of doing this to yourself.

Resources

Can graphics/audio be extracted?: Absolutely

People seem surprised but I'm not sure what they expect - if something is being drawn on screen or played through audio device, it can be intercepted and extracted at a variety of points on the way there. This had been the case for a very long time now.

Various measures can be taken about this but are generally not worth the hassle.

Shaders

As one exception from above rules, we have shaders.

GM:S/GMS2 will compile shaders on game start, and, as result, will store them as-is.

Therefore it is not a good idea to, for some reason, store sensitive information inside your GLSL/HLSL code.

Some people argue (and loudly so) that GM should be encrypting the code, but we know exactly how much that helps - see: GM≤8.1, or pretty much any case with anti-tamper/anti-piracy measures - once a single person cracked the algorithm, it is instantly worthless, and also all existing works can now be extracted.

In conclusion

As time goes on, tools continue to evolve.

While, ultimately, anything that runs on end user's hardware can be reverse-engineered at some point, having baseline adequate security (through use of YYC) is a good thing for games where this matters.

Even so, for games with online features (like multiplayer or leaderboards), you should always have server-side validation to prevent packet/request tamper.

And, never forget - the ultimate goal in making games/software is usually to finish the thing, not come up with the most sophisticated counter-measures.

Have fun!

Related posts:

7 thoughts on “On GameMaker game decompilation

  1. I just wanted to say that your answer was the first thing Google found on this subject… and that I love pretty much everything you’ve posted on this ‘site; a lot of GML gems and tips here.

    I originally found your posts because because of your work on NTT (which I think is a pretty amazing piece of reverse-engineering).

    I certainly hope that you’re right and GMS 2.0+ is largely creating code that’s inherently obfuscated.

    I wouldn’t want people to realize that I pretty much made my entire game’s framework out of code assets from the Marketplace that I’ve customized to fit my game’s peculiar needs, lol. That takes Real Work, but it’s nothing like building an engine from scratch (and thank goodness; I’ve saved roughly a man-year thus far, not reinventing wheels).

  2. Pingback: GameMaker: Recovering lost work

    • That is correct, although shaders are always easily obtained through numerous tools for debugging GPU state.

      Code could be minified on compile, but I’m not sure if that would have any substantial effect, considering that inputs/outputs will always have to be named accordingly, and structure is best not messed with to not loose performance.

  3. “Non-YYC: Can it be decompiled?: Maybe, at some point of future.”

    I think this part is a bit outdated…

    • I don’t believe he’s wrong about this. GM:S 1 had a few decompilers that sorta worked. GM:S 2 has yet to have anything surface as far as I’m aware.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.