Skip to content

Obfuscation and Me

July 15, 2010

Those of you who program in .NET languages hopefully know about something called “Reflector”. It’s made by a company called Red-Gate and despite having one of the most irritating automatic update requirements, is possibly my favourite tool ever. Reflector is a program that can load any .NET assembly containing MSIL instructions. So far so good, but the magic comes from the disassembler, which converts MSIL (which is, in itself, fairly readable), into totally passable C# code. Or VB.NET code, or “Oxygene” (whatever that is). It can be a little shocking to write code and find that it can be completely disassembled into a high level language, but if you think about it, it’s obviously possible – especially given the enormous amount of metadata embedded in .NET assemblies. Frankly, if you can’t deal with people stealing your precious ideas, but you don’t know how to protect them (e.g., host MSIL in your own runtime, write CLI/C++ code, whatever), your ideas are probably not worth stealing anyway.

Mind you, this little “loophole” has resulted in several companies developing “obfuscators” for .NET (they exist in Java too, but Java is a bit rubbish so who really cares). In my day to day activity, I open up many .NET assemblies, (usually to work out how to work around third party bugs through some extreme programming), but today, it was a little different – I opened up Reflector on an evaluation DLL (again, I wanted to see how fixable an issue was without the source code), and was greeted with this:

protected override void OnKeyDown(KeyEventArgs e)
    // This item is obfuscated and can not be translated.

This was a little surprising, especially as I had never seen Reflector get so upset before. I’ve seen obfuscated .NET code before, Dotfuscator, which has a cut down copy distributed with Microsoft Visual Studio, is used semi-regularly by paranoid people, and not to particularly great effect (it replaces methods, variables, and the entire private interface with unhelpful names like “a” and “b” and “aa”). The MSIL could still always get decompiled, you just lost the context of seeing what things were actually called. In this case, it was a little different. The field names and private interface was helpfully replaced with unicode characters that all looked like the “you opened a gif in notepad” block symbol, but I can deal with that. The method logic, however, was apparently impossible to decode. Naturally, the MSIL is all there and correct – you would hope so as it is required for the assembly to actually be useful, and, to be honest, MSIL is not significantly harder to read than old versions of BASIC; it’s even got line numbers for you, but nevertheless, the code in this function was poisoning Reflector’s decompiler.

.method family hidebysig virtual instance void OnKeyDown(class [System.Windows.Forms]System.Windows.Forms.KeyEventArgs e) cil managed
    .maxstack 2
    .locals init (
        [0] int32 num)
    L_0000: ldc.i4 2
    L_0005: stloc num
    L_0009: br.s L_000d
    L_000b: br.s L_0022
    L_000d: ldloc num
    L_0011: switch (L_004b, L_0037, L_000b) // <-- This is here to annoy us
    L_0022: ldarg.0
    L_0023: call instance int32 [System.Windows.Forms]System.Windows.Forms.Control::get_Width()
    L_0028: ldc.i4.s 15
    L_002a: bge.s L_0055
    L_002c: ldc.i4 1
    L_0031: stloc num
    L_0035: br.s L_000d
    L_0037: br.s L_0039
    L_0039: ldarg.1
    L_003a: ldc.i4.1
    L_003b: callvirt instance void [System.Windows.Forms]System.Windows.Forms.KeyEventArgs::set_Handled(bool)
    L_0040: ldc.i4 0
    L_0045: stloc num
    L_0049: br.s L_000d
    L_004b: ldc.i4.1
    L_004c: br.s L_0051
    L_004e: ldc.i4.0      // <-- This is the ACTUAL problem
    L_004f: br.s L_0051
    L_0051: brfalse.s L_0053
    L_0053: br.s L_0055
    L_0055: ldarg.0
    L_0056: ldarg.1
    L_0057: call instance void [System.Windows.Forms]System.Windows.Forms.Control::OnKeyDown(class [System.Windows.Forms]System.Windows.Forms.KeyEventArgs)
    L_005c: ret

I don’t expect everyone to be able to read the MSIL above, but bear with me. The obfuscator has inserted a mysterious switch statement at the start of the function, which it initially jumps right over. Then, at random points within the function, it sets a “magic” variable, jumps back to the switch, and uses the variable to jump all the way back to where it just came from. I’d like to say it was ingenious but it’s actually depressingly trivial. More interesting is that this is very close to legal C#, with one major issue – line L_004e (with cannot be reached during normal program flow) performs an operation that cannot be done in a high level language, pushing a second arbitrary value onto the virtual machine’s local stack (usually used for branching against a constant). In fact, if you open use the wonderful Reflexil plugin for reflector, you can remove the single offending instruction, and Reflector will dutifully decompile the result:

protected override void OnKeyDown(KeyEventArgs e)
    int num = 2;
    switch (num)
        case 0:
            if (1 != 0)

        case 1:
            e.Handled = true;
            num = 0;
            goto Label_000D;

            if (base.Width < 15)
                num = 1;
                goto Label_000D;

I find it interesting to see how these obfuscators work. In my opinion, they’re based on a flawed premise – much like DRM for movies: there is only so much they can do if they want their product to still work. I even question whether it’s enough to deter a layman from nicking bits of their code – I mean, you are simply annoying the people who actually use Reflector in the first place, people who (I hope) have a vague functional knowledge of MSIL. Still, it looks like this assembly was protected by a tool written by the same people who maintain Reflector itself. I wonder if perhaps it gave up a little easily for that exact reason…

From → C#, Programming

One Comment

Trackbacks & Pingbacks

  1. Mono.Cecil vs Obfuscation: FIGHT « Eat/Play/Hate

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: