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!
Read More

Unity3D, Mono and invalid PE files


Some time ago, Reoto asked a very nice question on Black Storm forum:

Can someone fix the .dll (.net) pe header to MS DOS?
How can I do that?
If you know about protecting .net files for Android, please help me.
I have another question.
Can I fix dnspy to resolve .dll pe header isn't .net?

Obviously, English is not author's first language but it seemed like an interesting problem, so I decided to look into it.

Here is one of the files in question: https://mega.nz/#!0g4VHaIR!KmpQirte4_3lv8MSxyjETiufjFGb-CITpFGrXwxSgGY

TL;DR: Mono loader used by Unity3D accepts invalid PE files. It can be used to break most .NET decompilers. dnlib and tools based on dnlib (dnSpy, de4dot) were updated on 20-Apr-2018 but the rest of the tools still can't handle such files.
Read More

Since you asked.. How to inject byte array using dnlib


Quite often I receive random questions about dnlib from my friends. To be honest, I have no idea why they think I know the answers to life the universe and everything else. 🙂 So, in this series of posts I'll attempt to solve their problems - and hope that the solution helps someone else too.

So, today's question is:

We're trying to add a byte array to an assembly using dnlib. We wrote some code* but dnlib throws exception when saving modified assembly:
An unhandled exception of type 'dnlib.DotNet.Writer.ModuleWriterException' occurred in dnlib.dll
Additional information: Field System.Byte[] ::2026170854 (04000000) initial value size != size of field type

I gave the friend the standard answer - make a sample app, see how it looks and then implement it with dnlib. Seriously, how hard can it be? 🙂

Well, array initialization in .NET is anything but simple.

How arrays are initialized in C#

Note - the following explanation is shamelessly copied from "Maximizing .NET Performance" by Nick Wienholt. It's a very nice book but getting little outdated. You can Google for "Apress.Maximizing.Dot.NET.Performance.eBook-LiB", if interested.

Value type array initialization in C# can be achieved in two distinct ways—inline with the array variable declaration, and through set operations on each individual array element, as shown in the following snippet:

int[] arrInline = new int[]{0,1,2};

//set operation per element
int[] arrPerElement = new int[3];
arrPerElement[0] = 0;
arrPerElement[1] = 1;
arrPerElement[2] = 2;

For a value type array that is initialized inline and has more than three elements, the C# compiler in both .NET 1.0 and .NET 1.1 generates a type named <PrivateImplementationDetails> that is added to the assembly at the root namespace level. This type contains nested value types that reference the binary data needed to initialize the array, which is stored in a .data section of the PE file. At runtime, the System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray method is called to perform a memory copy of the data referenced by the <PrivateImplementationDetails> nested structure into the array's memory location. The direct memory copy is roughly twice as fast for the initialization of a 20-by-20 element array of 64-bit integers, and array initialization syntax is generally cleaner for the inline initialization case.

Say what? You can read the text 3 times and still be no wiser. So, let's make a small sample application and disassemble it.

How array initialization looks in MSIL

Let's start with sample app that does nothing.

using System;
class Program
    static byte[] bla = new byte[] {1,2,3,4,5};
    static void Main()

Compile without optimizations, and disassemble using ildasm. And even after removing all extra stuff, there's still a lot of code & metadata for such a simple thing. 🙂

.assembly hello {}

.class private auto ansi beforefieldinit Program extends [mscorlib]System.Object
  .field public static uint8[] bla

  .method private hidebysig specialname rtspecialname static void  .cctor() cil managed
    newarr     [mscorlib]System.Byte
    ldtoken    field valuetype '<PrivateImplementationDetails>{E21EC13E-4669-42C8-B7A5-2EE7FBD85904}'/'__StaticArrayInitTypeSize=5' '<PrivateImplementationDetails>{E21EC13E-4669-42C8-B7A5-2EE7FBD85904}'::'$$method0x6000003-1'
    call       void [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array, valuetype [mscorlib]System.RuntimeFieldHandle)
    stsfld     uint8[] Program::bla

.data cil I_00002098 = bytearray (01 02 03 04 05) 

.class private auto ansi '<PrivateImplementationDetails>{E21EC13E-4669-42C8-B7A5-2EE7FBD85904}' extends [mscorlib]System.Object
  .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) 
  .class explicit ansi sealed nested private '__StaticArrayInitTypeSize=5' extends [mscorlib]System.ValueType
    .pack 1
    .size 5

  .field static assembly valuetype '<PrivateImplementationDetails>{E21EC13E-4669-42C8-B7A5-2EE7FBD85904}'/'__StaticArrayInitTypeSize=5' '$$method0x6000003-1' at I_00002098

For one byte array that we declared, compiler created .data directive, 2 static fields, one class and one nested class. And it added a global static constructor. Yikes!

Implementing it in dnlib

Now that we know all the stuff that's required for an array, we can make a tool that will add byte array to an assembly of our choice. To make things simpler, I decided not to create a holder class (named <PrivateImplementationDetails>{E21EC13E-4669-42C8-B7A5-2EE7FBD85904} in the example) and put everything in global module instead.

Note - Since I'm not a .NET/dnlib wizard, I always do it one step at a time, make sure it works and then continue. So, my workflow looks like this: write a code that does X → compile and run it → disassemble the result → verify that result X matches the expected → fix the bugs and repeat. Only after I've tested one thing, I move to the next one.

It also helps to make small test program first. Once you know that your code works as intended, you can use it in a larger project. Debugging the entire ConfuserEx project just to find a small bug in modifications made by someone - it's not fun! So, step-by-step...

First, we need to add the class with layout. It's called '__StaticArrayInitTypeSize=5' in the example above. That's quite simple to do in dnlib:

ModuleDefMD mod = ModuleDefMD.Load(args[0]);
Importer importer = new Importer(mod);
ITypeDefOrRef valueTypeRef = importer.Import(typeof(System.ValueType));
TypeDef classWithLayout = new TypeDefUser("'__StaticArrayInitTypeSize=5'", valueTypeRef);
classWithLayout.Attributes |= TypeAttributes.Sealed | TypeAttributes.ExplicitLayout;
classWithLayout.ClassLayout = new ClassLayoutUser(1, 5);

Now we need to add the static field with data, called '$$method0x6000003-1'.

FieldDef fieldWithRVA = new FieldDefUser("'$$method0x6000003-1'", new FieldSig(classWithLayout.ToTypeSig()), FieldAttributes.Static | FieldAttributes.Assembly | FieldAttributes.HasFieldRVA);
fieldWithRVA.InitialValue = new byte[] {1,2,3,4,5};

Once that is done, we can add our byte array field, called bla in the example.

ITypeDefOrRef byteArrayRef = importer.Import(typeof(System.Byte[]));
FieldDef fieldInjectedArray = new FieldDefUser("bla", new FieldSig(byteArrayRef.ToTypeSig()), FieldAttributes.Static | FieldAttributes.Public);

That's it, we have all the fields. Now we need to add code to global .cctor to initialize the array properly.

ITypeDefOrRef systemByte = importer.Import(typeof(System.Byte));
ITypeDefOrRef runtimeHelpers = importer.Import(typeof(System.Runtime.CompilerServices.RuntimeHelpers));
IMethod initArray = importer.Import(typeof(System.Runtime.CompilerServices.RuntimeHelpers).GetMethod("InitializeArray", new Type[] { typeof(System.Array), typeof(System.RuntimeFieldHandle) }));

MethodDef cctor = mod.GlobalType.FindOrCreateStaticConstructor();
IList instrs = cctor.Body.Instructions;
instrs.Insert(0, new Instruction(OpCodes.Ldc_I4, 5));
instrs.Insert(1, new Instruction(OpCodes.Newarr, systemByte));
instrs.Insert(2, new Instruction(OpCodes.Dup));
instrs.Insert(3, new Instruction(OpCodes.Ldtoken, fieldWithRVA));
instrs.Insert(4, new Instruction(OpCodes.Call, initArray));
instrs.Insert(5, new Instruction(OpCodes.Stsfld, fieldInjectedArray));

And that's it! Simples!

Further reading

Commented demo code at Pastebin
Longer explanation how array initialization works in C#


Just to clarify - this is a sample code. It works for me but if it blows up in your project, it's your problem. And there always are some things that can be improved.

• Sometimes I'm overcomplicating things.. You don't need to explicitly import System.Byte, you can use mod.CorLibTypes.Byte for that.

instrs.Insert(1, new Instruction(OpCodes.Newarr, mod.CorLibTypes.Byte.ToTypeDefOrRef()));

SZArraySig is a cleaner but less obvious way to refer to any array. If you need to reference complex arrays, this is better:

FieldDef fieldInjectedArray = new FieldDefUser("bla", new FieldSig(new SZArraySig(mod.CorLibTypes.Byte)), FieldAttributes.Static | FieldAttributes.Public);