11 Feb 2019

Unity3D protection in Moonton games, part 2

I wrote about Moonton game protection in November 2018. It was a pretty boring protection, so I quickly forgot about that. In January 2019 Moonton devs decided to change their protection. I'm not sure if it's a coincidence or not - but here's the update anyway.

This analysis covers:

specifically versions from 1.3.37 upto 1.3.47 (latest at the time of writing). All other games that I mentioned in my previous post haven't been updated, or are still using the old protection mechanism.

Simple ELF cryptor

I am planning write a separate blog post about ELF cryptors in general, so he're just a quick summary - 11 methods are encrypted:

  • sub_1D1308
  • sub_1D1309
  • sub_1D1320
  • sub_1D1321
  • loc_1D0FEC
  • loc_1D0FED
  • DecodeInt
  • DecodeHeaderFlag
  • mono_method_get_header_summary
  • mono_metadata_parse_mh_full
  • load_metadata_ptrs

These names might look a little bit weird but those are the actual names of methods, as contained in ELF symbol table. Cryptor code obtains method addresses and sizes on runtime by parsing ELF file headers, decryption is simple RC4 using a hardcoded key:

Fun fact - this list also tells you where exactly to look for the good stuff. No more boring comparison of original libmono.so and a modified one. Thanks guys! smile

Changed DOS signature

Just like in previous version MSDOS signature is changed from "MZ" to "ER". However, the check is now implemented in the verify_msdos_header:

and the check from mono_image_load_pe_data is removed altogether.

Encrypted PE header

This is a big change from the previous version. If you open some DLLs in hex editor, you'll see that PE header is encrypted:

The decryption is done do_mono_image_load:

If you look into sub_1D1308, you'll see that it decrypts first 0x1000 bytes of .NET assembly. Decryption algorithm is slightly modified AES-128 in ECB mode with another hardcoded key. Very secure! bigsmile

As for AES implementation, we can find a very similar opensource code, for example, here. Comparing decompiled code with the original, you can see that aes_decrypt has been modified in a way that it includes additional XOR encryption with another hardcoded key:

Now we can decrypt the assembly and take a look at .NET metadata. smile

Obfuscated .NET metadata streams

Opening decrypted assembly in CFF Explorer, we can see that stream headers look like a mess.

Apparently, stream names were changed to a more random strings in load_metadata_ptrs. Also some fake streams have been added (Mono happily ignores them):

Previously stream offsets and sizes were obscured using simple "not" operation:

Now size and data fields are swapped around and it's using a special method DecodeInt to decode stream offset/size:

The new method DecodeInt looks like this:

That decompiled code looks scary, doesn't it? smile

Here's a more readable version which replaces "binary NOT" and bitshifting with XOR operations:

We can now fix stream headers and look at the methods and IL code.

Modified method headers

This is another change from previous version.

The magic happens in mono_metadata_parse_mh_full. There are 3 small changes in the code:


First change is related to method header. Method's header flag is encoded and there's a new function DecodeHeaderFlag. Decompiled code of DecodeHeaderFlag looks like crap:

When rewritten using XOR operation it looks much nicer:

Tiny method header

Second change is related to tiny method headers. Instead of one line in original code

we now have 2 lines

To help you understand that weird calculation, here's a picture:

In .NET framework, tiny method header is immediately followed by IL code. In the Moonton version, there are some junk bytes inserted between tiny method header and IL code. To fix that, you need to process method header, find IL code size, find number of junk bytes and move IL code to the proper place. It's not hard, just... annoying.

Fat method header

Third and final change is in the fat header processing. Size of IL code gets decrypted using the same DecodeInt function we saw earlier. It's a very simple thing to fix, much easier than the tiny header.

Fix all that and observe the decompiled code:


Moonton protection has been significantly improved since I looked at it last time. However, there are plenty of MODs out there, even for the games protected with this latest protection which indicates that protection has been bypassed by some MOD teams already.

As explained earlier, I will no more provide a ready-made executable that can remove the protection. But here you have all the information you need to make your own tool.

Have fun! smile

Thanks to Sonny for bringing my attention to the updated version of Moonton.

15 thoughts on “Unity3D protection in Moonton games, part 2

  1. Thanks for the amazing tutorial, I just took your old source of fixer anf implemented all that you mentioned and It is working you are awesome.

  2. Hmm... Where can I get atlas battleground.unity3d? Bcoz I can't fine it anywhere can u tell me plzz

  3. Really nice article. I had just figured out the AES module cryption before i stumbled on this, and thank god I did, figuring out the internal .NET fuckery would've taken me ages! I took your old application and modified it to fix up the changes you mentioned (parsing the metadata tables upto the method headers was a bit of a pain in the ass!). However, certain TypeDef and Field entries seem to be messed up, and references to locals in .ctor() functions are null'd out (FAT header messed up?). I'm failing to find any changes in their libmono.so to suggest they're doing more than what you've discovered. Do you think I've just made an error in my fixup code? I promise I've stared at it for hours before making this post.

      • I realized another mistake I was making was fixing up method bodies multiple times, as I didn't realize different method definitions could have the same body rva.

    • After getting passed the .NET protection it might interest you to know that this game also employs a modified Lua VM for their Lua scripts (which seems to be the chunk of game functionality) that's "hiding" in the library at comlibs/arch/libmoba.bytes. The compiled lua scripts have a basic aes & gzip applied, and if you follow luaU_undump in libmoba you'll find that some opcodes are modified as well. Interesting stuff!

      • It was just some opcode swapping thankfully. Here's a funny snippet where they check some hardcoded package names :)
        {hidden link}

  4. They are now using il2cpp. Their global-metadata.dat is invalid. I bet its a placeholder and they fill it with bytes when the game loads. I will try investigating more!

  5. thanks mr. kao you still alive, can you share more link or ebook about protecting moonton games
    xia xia

    • Hello Koclok, if I learn more about Moonton games, I'll share it here. But at the moment I don't have anything new.

      • liblogic.so it's the true libil2cpp.so of the game, remember they create a process called UnityKillsMe, its true?

Leave a Reply

  • Be nice to me and everyone else.
  • If you are reporting a problem in my tool, please upload the file which causes the problem.
    I can`t help you without seeing the file.
  • Links in comments are visible only to me. Other visitors cannot see them.

Your email address will not be published.

six  −  3  =