31 Oct 2018

Unity3D, Mono and invalid PE files, part 2

In the first part of the series I explained how some cheat authors try to protect their work against other cheaters. It was a quick introduction to Unity3D and bugs in Mono that cheat authors exploit.

Last week someone emailed me another example of a game cheat. My tool from the previous article failed to fix invalid metadata, so I decided to look at it again.

Cheats by BlackMod.net

The cheat I received was made by Mod4U from BlackMod.net team. It appears that Mod4U is one of the most active members of the team, judging by number of the releases. His/her cheats use invalid PE file tricks and are encrypted, as you'll see later in the article.

After looking at different mods from other team members, I've confirmed that Rito, Aurora and Legend also are using invalid PE files for hiding their work. But none of their cheats encrypt Assembly-CSharp.dll.

So, let's look at the cheats and see what new tricks BlackMod team members have found!

Just like in my previous article, I'll be using Mono sources from commit 74be9b6, so that they would more or less match the code used by Unity3D.

Unchecked CLI header values

Mono ignores pretty much every field from CLI header in mono_image_load_cli_header:

.NET runtime validates ch_size, ch_runtime_major, ch_runtime_minor fields. Other reversing tools validate some of the fields.

Unchecked MetaDataRoot values

Mono completely ignores md_version_major and other values of the MetaDataRoot in load_metadata_ptrs:

.NET runtime and tools based on older dnLib validate md_version_major and md_version_minor fields.

How long is the version string?

ECMA-335 specification paragraph II.24.2.1 specifically says

Call the length of the string (including the terminator) m (we require m <= 255); the length x is m rounded up to a multiple of four.

Mono throws specification out the window and does it in another, completely broken way:

Mono accepts version_string_len that is not rounded to multiple of four and rounds it up automatically. It also accepts string length larger than necessary.

This is a very interesting bug, as it affects most of the commonly used .NET reversing tools.

  • .NET 2.0 runtime doesn't actually follow ECMA-335 specification. It doesn't require string length to be aligned and doesn't align it. But it does require that metadata streams start at dword boundary.
  • dnLib/dnSpy will accept unaligned strings but will not align them. It will accept extra long strings. dnLib doesn't require streams to start at dword boundary.
  • CFF Explorer will happily read file with unaligned string length and will align it automatically. It will also accept file with extra long strings.

Invalid HeapSizes/MDStreamFlags value

Mono processes only the lowest 3 bits of HeapSizes in load_tables:

and completely ignores flag value 0x40 which is quite crucial.

See the dnLib sources:

When ExtraData flag is set, there is one more field present in .NET metadata::

.NET runtime and tools based on dnLib respect the flag but Mono completely ignores it. D'oh!

Invalid size of #~ stream

Mono doesn't check size of #~ stream at all. Really. It accesses all the #~ fields with no validation whatsoever:

So, you can set size of #~ stream to a random number and Mono will be happy. CFF will be happy as well. But any other tool will complain.

Invalid size of #Blob stream

Mono correctly reads offset and size for #Blob stream:

but doesn't check values for correctness. So, it will also accept insanely large values for heap_blob.size.

The only checks are done when reading from #Blob stream and only ensure that index is smaller than the declared #Blob size:

Assembly-CSharp.dll encryption

As I already mentioned, Mod4U is the only Blackmod team member who uses this type of protection. To learn how exactly the protection works, please read my previous post.

Today I'd like analyze different encryption methods used by Mod4U. Data show that Mod4U keeps stealing the protection mechanisms from others!

Not operation

On 24-Jun-2018, Mod4U released Dragon Spear v1.25 MOD. APK contains Assembly-CSharp.dll encrypted using simple NOT operation. It also contains libmono.so for armeabi-v7a architecture which is protected by some protector that I can't recognize.

Thanks to VirusTotal, we can track this libmono.so back to a Chinese game from year 2016, called Lance Town. Stolen! smile

Xor 0x71

On 12-Jul-2018, Mod4U uses different encryption in his/her Noblesse M Global v1.2.0 release. APK contains libmono.so for armeabi-v7a architecture and it's not protected at all. This is the exact same encryption that was used by AndroidThaiMod and described in my previous post.

Additional VirusTotal search shows that this protection has been used by different teams since August 2017. Stolen! smile

Sub 0x0D

Starting from 24-Jul-2018 release of Boxing Star v1.1.2, Mod4U uses yet another version of libmono.so. Now there are 2 libmono.so versions - one for armeabi-v7a and one for x86 architecture.

This version of libmono.so seems to be stolen from a different Chinese game! smile How else can you explain presence of the exact same code described in Pediy post about Chinese game and reference to ThreeKindom.dll? And the function name rvlt_modify_data_with_name - a reference to RevolutionTeam who released the game.

VirusTotal search confirms the theory. Stolen again! bigsmile

Cheats by platinmods.com

Platinmods is another Android modding team using pretty much the same tricks.

I took just a quick look at their releases (eg. BLEACH Brave Souls v7.3.1) and found that they employ another encryption method (Xor 0xFB).

According to VirusTotal, that particular version of libmono.so has been used by Vietnamese modding team W4VN.NET since April-2017.

Update for my tool

My original intention was to make a SIMPLE tool that helps you to fix the broken PE file and metadata. My tool does exactly that.

Most of the tricks that I described are trivial to fix. Check the value, if it's incorrect, overwrite it with correct value. Repeat until done. Something like this:

However, "Invalid #~/#Blob size" tricks are particularly difficult to fix. My tool cannot magically support all possible cases, not without writing full .NET metadata reader/writer. And I don't want to do that, sorry. wink

So, if you really, really need to fix invalid stream sizes, try running de4dot on the decrypted Assembly-CSharp-fixed.dll. de4dot versions from years 2016-2017 work best, versions from 2018 most likely will complain about "invalid parameter" or something like that. Same advice applies for dnSpy - versions from 2017 will open most (or all) decrypted files, versions from year 2018 will not.

Download here:


As predicted in my previous article, cheat authors are willing to exploit any and all bugs in Mono to hide their work from others. Some of the bugs they've discovered are really surprising and affect more than just one tool. I expect cheat authors to continue finding new and innovative ways to hide their code in the future as well.

Next time I'll look into... well, I have no idea! bigsmile

P.S. If you still can't make some DLL work, please send me an email (or leave a comment below). I'll look at it and try to help you.

12 thoughts on “Unity3D, Mono and invalid PE files, part 2

  1. help me fix error:
    Unhandled Exception: System.IndexOutOfRangeException: Index was outside the bounds of the array.
    at FixUnity3D.Program.Main(String[] args)

    • I will look at that protection someday when I get some free time, but I cannot promise you anything.

  2. I wish Admin Kao will add xigncode3 for apk protection bypasser? this protection add in every games but dont know how other bypass it.

Comments are closed.