Cameron Hotchkies

Categories

  • Coding

Tags

  • C#
  • code
  • decompilation
  • obfuscation
  • reverse-engineering

Let’s talk about code obfuscation for a minute. I’m not talking about the interesting puzzle obfuscation of competitions, but the kind done at compile time to protect intellectual property.

The most common languages for this kind of obfuscation tend to be Java and C#. Part of the reasoning for this is the fact that they both support reflection making decompilation trivial.

// original code
public void DoMagic() {
    TrivialExample("Abcd", 1234);
}

private void TrivialExample(string FileName, int Limit) {
    int counter = 0;
    for (counter = 0; counter < Limit; counter++) {
        FileStream fs = new FileStream(FileName +
            "." +
            Limit +
            ".log", FileMode.OpenOrCreate);
        StreamWriter sw = new StreamWriter(fs);
        sw.WriteLine("Lorem awesome"); fs.Close();
    }
}

Run through dotPeek, the same function is almost identical:

// decompilation
public void DoMagic() {
    this.TrivialExample("Abcd", 1234);
}

private void TrivialExample(string FileName, int Limit) {
    for (int index = 0; index < Limit; ++index) {
        FileStream fileStream = new FileStream(
            string.Concat(new object[4] {
                (object) FileName,
                (object) ".",
                (object) Limit,
                (object) ".log"
            }),
            FileMode.OpenOrCreate);
        new StreamWriter((Stream) fileStream).WriteLine( "Lorem awesome");
        fileStream.Close();
    }
}

The same code run through dotfuscator, then decompiled:

// reflected decompilation of obfuscation
public void DoMagic() {
    this.a("Abcd", 1234);
}

private void a(string A_0, int A_1) {
    for (int index = 0; index < A_1; ++index) {
        FileStream fileStream = new FileStream(
            string.Concat(new object[4] {
                (object) A_0,
                (object) ".",
                (object) A_1,
                (object) ".log"
            }),
            FileMode.OpenOrCreate);
        new StreamWriter((Stream) fileStream).WriteLine( "Lorem awesome");
        fileStream.Close();
    }
}

In this small example you see the private methods have been renamed along with their arguments. As can be seen by an excerpt from Absinthe, non-trivial examples become substantially more confusing for human readability.

public void a(string A_0, int A_1, bool A_2) {
    if (this.g == null)
        this.g = new Hashtable();
    else if (!this.a.Equals(this.d) && this.h == null)
        this.h = new Hashtable();

    if (A_2)
        this.h.Add((object) A_0, (object) A_1);
    else
        this.g.Add((object) A_0, (object) A_1);

    this.k = true;
}

At this stage, I’d like to point out that although this does make it more difficult to read, it doesn’t make it impossible. In fact, because public methods, framework calls and text strings are not altered it’s very possible. Just slower.

There are three likely groups of people that would want to decompile your code:

  1. People who want to subvert your copy protection (product keys, DRM)
  2. People who want to steal your intellectual property for competitive purposes
  3. Developers who are trying to actually use your code, understand your library or file a bug report.

Let’s face it, if someone really wants wants to steal your software, or content, they will find a way. This goes double for industrial espionage, as ridiculous as it sounds. Part of the techniques for de-obfuscating code include re-implementing the code in chunks inside of an IDE. For example 

F2 in Visual Studio will rename all instances of a variable in an entire solution, rinse, repeat. So in the case of cloning software, by the time it’s understandable, the reverse engineer has a working project over half finished. Add to that the fact that they’re now annoyed you made them do more work than was originally necessary.

What about the developer that wants to try to help you? It’s not as contrived as it might initially seem. What if you didn’t have time to write concise documentation? Or you did three versions ago but never updated it, so it is totally misleading in the current revision. There’s also the possibility that a developer is using your code and it didn’t work and they want to help you fix it, giving you as much information as they have access to, because they understand what you do, how they can help you and they want to use your product. These are your consumers, and they want to help you..

Detailed user submitted bug reports are made of fucking gold.

Quote me on that. Make a quaint cross-stitch wall hanging and put it above your desk. Make it your desktop background. Live it. Now matter how awesome your QA department is, some things slip by them. They slipped by you first too. Some bugs are just like that. Maybe it’s a custom kernel the user built from scratch on a rooted android phone. Maybe their collection of malware earned after years of surfing for porn creates a perfect storm of instability. Maybe solar flares reflect off their neighbor’s El Camino in a very distinct way on Thursdays.. Whatever it is, if you’re standing in the way of more details you’re hurting yourself more than them. Despite your insane workload, you’ve decided that you’d rather protect your code than save days tracking down a bug based on “It crashes when I try to use it”.

If your business model is so dependent on people not being able to mimic your product, you’re already dead. Relationships can’t be cloned. Innovation can’t be cloned, innovations can but you should always be moving forward anyways. And if all else fails, hide the important logic behind a web service, that way any exceptions can be emailed directly to your dev team & bug tracker.