domenica 20 ottobre 2013

.NET code protection, your are doing it wrong

In the past years I have audited various .NET programs in order to verify if it was possible to bypass the license registration mechanism. The most used technique is to obfuscate the MSIL in order to avoid decompiling the registration routine and writing a valid keygen.

The problem of this approach is that even if you use a strong code obfuscator (and there are plenty of them available out there) the code can be easly hacked if not designed in a secure way.

A typical solution is composed of a DLL that implements the license check routine which validates if the product is correctly registered. If this check succeeds then a License object is created and returned. In the "most advanced" case the check is repeated every tot seconds.

The problem in this case is how you have designed this mechanism. If you have followed good OO design then I have bad news for you. Consider the following piece of code:

using System;

namespace CodeProtection
{
    public class License
    {
        public License(String name)
        {
            this.Name = name;
        }

        public String Name { get; private set; }
    }

    public interface ILicenseChecker
    {
        License GetRegisteredLicense();
    }

    public sealed class DefaultLicenseChecker : ILicenseChecker
    {
        public License GetRegisteredLicense()
        {
            return GetRegisteredLicenseFromSecureStore();
        }

        private License GetRegisteredLicenseFromSecureStore()
        {
            // Check if the license is valid, return null on fail. 
            // This code is heavily  obfuscated and difficult to reverse.
            return null;
        }
    }

    public static class LicenseManager
    {
        internal static ILicenseChecker Checker = new DefaultLicenseChecker();

        public static License GetLicense()
        {
            return Checker.GetRegisteredLicense();
        }
    }

    public class Program
    {
        static void Main(string[] args)
        {
            var license = LicenseManager.GetLicense();
            if (license != null)
            {
                CodeToProtect();
            }
            else
            {
                Console.WriteLine("Product not licensed");
            }
        }

        private static void CodeToProtect()
        {
            Console.WriteLine("Great stuff done here!");
        }
    }
}

We want to be sure that the method CodeToProtect will be executed only if the license is valid. To do this the code follows a good OO design, it uses an interface in order to decouple the contract from the effective implementation. This allows the developer to use a moked version of  ILicenseChecker during the development and to switch to the final implementation when needed.

If you release that code it is higly probable that it will be hacked in a very easy way. To hack that code it is not necessary to modify the MSIL or to use more complex stuff like the CLR Memory Diagnostics. It is possible to write a bypass by using only plain reflection, like this one: 

using System.Reflection;
using CodeProtection;

namespace CodeProtectionBypass
{
    class Program
    {
        static void Main(string[] args)
        {
            var licenseManagerType = typeof(LicenseManager);
            var checkerProperty = licenseManagerType.GetField("Checker", 
                BindingFlags.NonPublic | BindingFlags.Static);

            checkerProperty.SetValue(null, new HackedLicenseChecker());

            // run the program
            CodeProtection.Program.Main(args);
        }
    }

    public sealed class HackedLicenseChecker : ILicenseChecker
    {
        public License GetRegisteredLicense()
        {
            return new License("Hacked");
        }
    } 
}

TL;DR
When you need to design the code that will check the validity of your license, follow these simple advices:
  • Use sealed classes to avoid someone else extending your class in a malicious way
  • Don't use Interface or Abstract class as field or property in the license check code, that will prevents others to replace your objects via reflection
  • Insert the validation code in the same assembly where it is used, and declare the methods internal or private. This allow the obfuscator to properly obfuscate the code and the routine names, this isn't possible if the method is public

Nessun commento:

Posta un commento