Click here to Skip to main content
15,894,095 members
Articles / Programming Languages / C#
Tip/Trick

Running encrypted .NET assemblies

Rate me:
Please Sign up or sign in to vote.
4.81/5 (18 votes)
4 Jan 2017CPOL3 min read 18.9K   372   16   5
.NET running encryped assemblies

Introduction

In the past, we were using obfuscation to make it harder to reverse our .NET assemblies. Though this worked well, it was still only harder to read what the code does. So, we came up with the idea to simply encrypt our assemblies and only decrypt them when they are needed/executed. Here's the very first prototype - maybe this is useful.

Using the code

To demonstrate the whole thing, I created a Visual Studio solution which contains following four projects. I tried to keep everything as simple as possible so that everyone can understand what's happening.

Cryptor

is a command line executable which is used to encrypt an assembly. To make things as easy as possible, I added a call to the tool as post build step of each assembly which shall be encrypted. The call to the tool is as simple as Cryptor <inFile> <outFile> <password>. So, during the build, an additional file is created where an "e" is added to the extension (Assembly.dll -> Assembly.dlle). The password used is "test", what else :-)

TheApplication

This is my test application - simple as could be. The user can enter 2 numbers which are then added using the brilliant logic contained in "TheClassLib" and the result is shown.

TheClassLib

Even simpler - this lib is used by TheAppication - and required, as TheApplication does not know how to add two intergers. Sorry, just kidding - I created this lib to show how (and that) dependencies to other encrypted assemblies are not a problem.

SecStarter

The SecStarter is a tiny Windows Forms Application which asks the user for the password before the application is started. It also contains the magic which needs to happen to execute TheApplication, which has a dependency to TheClassLib. First of all the SecureLoader fetches all of the encrypted assemblies it can find in the current working directory. It assumes that there's only one encrypted exe (.exee) - if you have more, you'll have to add some code here.

 

C++
internal void LoadAssembies(string password)
{
    string[] fileList = Directory.GetFiles(Environment.CurrentDirectory);

    foreach (string fileName in fileList)
    {
        if (fileName.EndsWith(".exee"))
        {
            this.executableBinary = LoadAndDecrypt(fileName, password);
        }

        if (fileName.EndsWith(".dlle"))
        {
            int startIndex = fileName.LastIndexOf("\\") + 1;
            string nameOnly = fileName.Substring(startIndex, fileName.Length - (startIndex + 5));
            this.dllList.Add(nameOnly, LoadAndDecrypt(fileName, password));
        }
    }
}

The call to

C++
LoadAndDecrypt(fileName, password)

does the decryption work as such.

Now, the trick to start and run the app is to create my own AppDomain and ask it to execute TheApplication. 

C++
Evidence adevidence = AppDomain.CurrentDomain.Evidence;
AppDomain domain = AppDomain.CreateDomain("MyAppDomain", adevidence);

domain.AssemblyResolve += Domain_AssemblyResolve;

domain.ExecuteAssemblyByName("TheAppication.exe");

As the exe does not sit in the working directory, the loading of the assembly will fail and we end up in an exception. Luckily, we can register for an event which is fired when the domain tries to load the assembly and we can add our own code:

C++
private Assembly Domain_AssemblyResolve(object sender, ResolveEventArgs args)
{
    if (args.Name.EndsWith(".exe"))
    {
        Assembly assembly = Assembly.Load(this.executableBinary);
        return assembly;
    }
    else
    {
        string[] assemblyData = args.Name.Split(',');
        Assembly assembly = Assembly.Load(dllList[assemblyData[0]]);
        return assembly;
    }
}

Depending on what the domain is asking for, we load the Assembly into memory and return it to the domain so that it can be executed.

Important to note is, that the creation of the AppDomain as well as the execution of the exe has to be done in a seperate Thread - otherwise we end up with exceptions.

C++
internal void StartTheApp()
{
    Thread theThread = new Thread(MainWorkerThread);
    theThread.Start();
}

public void MainWorkerThread()
{
    Evidence adevidence = AppDomain.CurrentDomain.Evidence;
    AppDomain domain = AppDomain.CreateDomain("MyAppDomain", adevidence);

    domain.AssemblyResolve += Domain_AssemblyResolve;

    domain.ExecuteAssemblyByName("TheAppication.exe");
}

There might be some code missing, e.g. cleaning up the AppDomain and the whole thing could be more professional (where are the unit tests?) - well, I wanted to share the idea and I'm looking forward to comments (and I hereby apologize for reading/answering them late as I am pretty busy at the moment)

 

Points of Interest

Still, as the user needs the password to start the app, the content of the assembly can be unwrapped - how to decrypt is visible in the (plain) starter code. However, encrypting the assemblies raises the bar and at least an attacker who does not know the password has it harder.

One could also combine the approach with some certificate e.g. in the cert store to avoid the password.

History

1st Version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Germany Germany
Teamlead and Developer with 20yrs of experience. Lifelong learner, curious and enthusiastic.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Gun Gun Febrianza9-Jan-17 7:55
Gun Gun Febrianza9-Jan-17 7:55 
Bookmarks & Follows this article.!
GeneralMy vote of 5 Pin
Franc Morales4-Jan-17 13:09
Franc Morales4-Jan-17 13:09 
QuestionLicensing Pin
Nathan Minier4-Jan-17 5:29
professionalNathan Minier4-Jan-17 5:29 
QuestionWhere store password Pin
GrzesiekD4-Jan-17 2:00
GrzesiekD4-Jan-17 2:00 
AnswerRe: Where store password Pin
Guenter Liehl4-Jan-17 2:45
Guenter Liehl4-Jan-17 2:45 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.