Steve Andrews

passionate technologist · business applications · community guy

May the Source Stay Only With You

So you've stayed up late every night for the past couple months coding out a sweet new idea. The bits are finally in order; you do a release build, zip it up, and put it on your website with a link to PayPal. "I am so going to retire" you think.

Next thing you know, folks are posting serial numbers, cracks, and your precious source code out in the cloud. "How did that happen?" you say as a wave of depression sets in. The answer is that, much like Java, .NET is compiled into an intermediate language, not machine code. This makes it easily decompilable. To protect your code, you need to do more than just compile.

In this post, we are going to examine obfuscation, secure licensing, and several other preventative steps you can take to help reduce the risk of piracy and source code theft of your .NET assemblies. First though, we'll take a look at some code protection myths.

Strong Names, CAS, and Other Myths

When folks hear the terms 'strong name' and 'code access security', their first impression is usually that these techniques will help them prevent unwanted access to the underlying source code. However, this is not the purpose of these features.

The concept of strong names was created to eliminate the problem of 'dll hell'. It allows an assembly to have a unique name for identification purposes. A good way to think about this is to take a look at human names. My name, Stephen Andrews, is not unique. As weird as it seems to me, there are other Stephen Andrews out there. I can not simply be known by my given and family name. More information about me must be provided to uniquely identify me among the other Steves. For the government and financial worlds, this is most commonly a social security number. More casually, a postal address may suffice.

Code access security, on the other hand, is all about preventing unwanted execution in an assembly. Basically, CAS allows an end user, administrator, or developer to limit the amount of access an assembly has to the underlying operating system, or conversely a minimum of access that is required to run the assembly. Using CAS, you can control whether an assembly has access to protected resources such as your network connection, the file system, registry, or even if it can show a user interface.

Unfortunately, neither of these methods helps in preventing a user from getting to your source code.

Obfuscation

The most common method of code protection, and one of the best, is obfuscation. According to Wikipedia, Obfuscation is 'the concealment of meaning in communication, making it confusing and harder to interpret.' This is certainly true for software obfuscation.

An obfuscator, a piece of software that performs obfuscation, typically utilizes several core concepts; layout, control flow and data. To understand how obfuscation makes it more difficult for someone to steal your code or hack your assembly, take a look at the following before and after code samples showing control flow modifications:

Before:

public class License
{
    public bool IsValid(string license)
    {
        Regex regex = new Regex(
            "[^a-z0-9]",
            RegexOptions.Singleline
            | RegexOptions.CultureInvariant
            | RegexOptions.IgnorePatternWhitespace
            | RegexOptions.Compiled);

        string result = regex.Replace(license, "");

        double add = 0.0;
        foreach (char c in result)
        {
            add += c;
        }

        return (add == 1054);
    }
}

After:

public class StringFormatter
{
    public bool IsLEFString(string lefString)
    {
        Regex a1 = new Regex(
            c3.m2("W2F6LV1eLTA5"),
            RegexOptions.Singleline
            | RegexOptions.CultureInvariant
            | RegexOptions.IgnorePatternWhitespace
            | RegexOptions.Compiled);

        string a2 = a1.Replace(lefString, string.Empty);
        int i3 = 169 / 13;
        if (i3==4)
            double a3 = a(a2 + 9);
        else
            double a3 = a((a2 + a2) / 2);

        return a(a3);
    }

    private double a(string a)
    {
        double a1 = 0.0;
        foreach (char a4 in a)
        {
            a1 += a4;
        }

        return a1;
    }

    private bool a(double a)
    {
        double a1 = c3.m2("cw==")[0];
        double a2 = c3.m2("Zg==")[0];
        return ((a1 * a2) / 13.623693379790940766550522648084) == a;
    }
}

As you can see, a very simple method of licensing (and one not recommended for actual use), has been made much less obvious. The class, method, parameter and variable names have been changed, the strings encrypted, and control flow expanded across two more methods. This eliminates the most valuable information for a decompiler. If someone were to open this code in Reflector, it would be much harder to understand what it was doing, let alone to try and put it back together and get some use out of it.

The thing about deobfuscation and decompilation in general is, you can turn apples into applesauce, but you cannot turn applesauce back into apples. This makes it a pretty good tool for code protection.

To get started with obfuscation, the default Visual Studio installation contains a copy of Dotfuscator Community Edition by PreEmptive Solutions. I also recommend taking a look at Postbuild by Xenocode.

Code Tips

There is certainly something to be said for maintainability, but more obtuse code, more object oriented in its structure, the harder it is for someone else to understand. If calls are being made across loosely coupled interfaces and across many classes, it will take someone much longer to understand what and where the code is doing.

Licensing

If you plan on selling software, most likely you are planning on licensing it, and implementing licensing checks in your code. Here are a couple tips to keep in mind when implementing licensing.

Assembly Naming

A lot of folks seem to like putting their licensing code into an assembly named licening.dll. There are two very big problems with this however.

First, the licensing assembly, named as such, is easily identifiable if someone decides to browse your program directory. It doesn't take a genius to figure out that licensing.dll may hold some of the licensing functionality in your application. One could very easily open up the licensing assembly in Reflector to see how it works, possibly figuring out how to circumvent it.

Secondly, one could write their own assembly to match your assembly's signature, and substitute yours for theirs, thereby bypassing your licensing mechanism. And if you think giving it a strong name helps, just remember that one can disassemble the calling assembly using ILDASM, change the IL reference of the licensing assembly to their replacement assembly, and reassemble the calling assembly.

Bottom line; if you have to compile your licensing code in a separate assembly, at least try not to make it so very obvious. Better yet though would be to include the licensing code in your main assembly.

By including the licensing code in your main assembly, you are making it harder for someone to circumvent the code protection, especially if it has been obfuscated among the rest of your code, as seen in the previous example.

RSA Signatures

RSA is an algorithm for public-private key pair cryptography. In general, the sender uses the receiver's public key to encrypt a message, and the receiver uses their private key to encrypt it.

This can be turned around, however, making it suitable for licensing protection. Instead of encrypting the whole message, you create a hash of the message and sign the hash using the private key. The data and the encrypted hash are then sent to the receiver, the licensee in this case.

On the client-side, a hash is made of the sent data, and the signature of the hash is verified using the public key. If the signatures are different, the license is not valid.

Using the RSA algorithm for licensing provides for one of the most secure licensing schemes available. To boot, the .NET framework includes an RSA algorithm implementation, so all the guts are taken care of for you.

Summary

We've looked at strong names and code access security, and while powerful, and misunderstood as providing protection to the underlying source code. We've also looked at the benefits of obfuscation, and the RSA method of licensing.

Hopefully, these tips will help your source code be only your source code.

Comments are closed