Click here to Skip to main content
15,867,308 members
Articles / Security / Encryption

Encrypted code compiled at runtime

Rate me:
Please Sign up or sign in to vote.
4.83/5 (29 votes)
28 Jan 2013GPL314 min read 90.9K   3.9K   90   30
Encrypt your C# classes and compile them during run-time (hiding their code in the exe file).

Introduction

This article first describes the code to create a small bare bones utility (written with .NET 2010 in c# 4.0) that combines the power of run-time compilation together with the encryption of data to create encrypted classes, starting from normal c# classes, and then it explains how to use these encrypted classes in a .NET c# project.

When I said 'bare bones, I meant that the utility works well when the input is correct, with all the data provided without errors and compliant to the requirements. Due to my laziness (since this is the work of free time in rainy/snowy days), I have implemented only the basic error check routines, exception trapping and the like.

The goal of this article is mainly to provide means to hide the code from the human eye reading the exe file with some disassembler like ILdasm. It doesn't prevent someone to see the decrypted code in memory while running, and, as a CodeProject user skilled on the matter pointed out, while decrypting, it creates files in the temp folder where the clear code is already decrypted; so, if your intention is to use this solution as an alternative to code obfuscation, beware that the code is hidden only in the exe file (or dll), not in memory or in temp files.

Background

Two concepts could be useful to understand this small application:

  1. Run time compilation,
  2. The Rijndael encryption algorithm (which is the one used for AES [advanced encryption standard] in it's 128bit variant ).

If you want to dig a bit in the run-time compilation concepts, I found the following articles useful :

On the other hand, the Rijndael encryption algorithm provided here is the one I wrote some time ago in C#, and, if needed, it can be changed with little effort into a different algorithm, to suit particular needs.
Knowing the Rijndael encryption algorithm is NOT necessary to understand the present program.

Using the code (basic usage)

If we want to use an encrypted class in our code using the present solution, then we should follow some simple steps:

  1. First: in a new or existing project, write a class (or a bunch of classes), from here on named "original class", that will later be encrypted, and, just for ease of use (but it's not required to), save it to a file (it is possible to include many classes and many namespaces in the same file).
  2. Second: encrypt that "original class" and create a new class containing the encrypted code and some methods to do the dirty work for us (this class will be called "management class"): this will be easily done with the small utility "class encryptor" available for download at the top of this article.
  3. Third: include the management class just created into our project, in place of the original class, and use the management class methods to instantiate the original class or to call the methods inside the original class.

First

I suppose that everyone reading this article is quite capable of writing a small test class on the fly, but, for the laziest lovers of copy/paste, we can use this "original class":

C#
// this is the "original class"
using System;
namespace MyMath {
    public class BasicMath {
        public int add(int a, int b)            {return a + b;}
        public int sub(int a, int b)            {return a - b;}
}}

Second

Now we run the 'Class Encryptor' program :

Image 1

In the top white textbox we should paste the code of the original class, and then we should provide an encryption key (password) in the following slim white textbox. This is enough to run the program and to receive a ready-to-use management class.

The input class is encrypted using the Rijndael algorithm and the provided encryption key to generate the management class that contains the encrypted code (in the form of a byte array) and some methods to make it easier to use (details on these methods will come after, in the third section).

In the optionals group we have two textboxes and three checkboxes:

TextBox 1 (Namespace): the namespace in the generated code that contains the management class (it can be left empty, in which case a class with no namespace will be created).

TextBox 2 (Class name): the name of the management class to be created. If left empty, then the class will be named, by default, as "MyManagementClass".

CheckBox 1 (Create standalone class): if left unchecked, the resulting code will contain "using MyRuntime;", where MyRuntime is a class (provided in the sources zip file, and described with more detail in the appropriate section far below in this article) that contains the encryption/decryption routines, among with methods to compile the encrypted code and to create the encrypted code; this is useful when we use many encryption management classes (so the encryption managing code will be only in one place). If, on the other hand, this checkbox is checked, then all the needed decryption routines are included in the output file; this is useful when we use only one encryption management class, so we don't have to use a second file to manage encryption etc.

CheckBox 2 (Create easy methods): if left unchecked, only two methods will be provided in the management class: one to instantiate a class (which name will have to be passed as parameter) and one to execute a method in an encrypted class; this is useful to create small management classes and to avoid leaving some hint of the encrypted class names inside the management class. The class names included in the encrypted code are added in a line of comment, for your reference. You can always delete this line before deploying your solution. If, on the other hand this checkbox is checked, then, for every class included in the encrypted code, two methods will be created (one to instantiate the class and one to execute methods in that class).

CheckBox 3 (Use secure code): if checked, then the byte array containing the encryption key (password) will not be included in the generated code, and every method will contain a parameter more: the encryption key.

After pressing the 'Encrypt' button, the output grey textbox at the bottom will be filled with the management class code. Just click once on it to select it all, then copy/paste in a new file class of your project.

Third

The generated code

Now we'll examine the code of the generated management class.

If we copy/paste all the 'MyMath' class code presented hereinabove as the original class, and we use as password "asd" (yes, yes it's too short, but I'm lazy ... you should know by now). In the 'Optionals' we could add, as namespace, "myspace". Leave the checkboxes unchecked, so the generated code will be the easiest to understand.

Press the "Encrypt" button, and we'll get the following code:

C#
using System;
using MyRuntime;
namespace myspace 
{
    public class MyManagementClass 
    {
        private readonly byte[] ecode = new byte[] { 71, 15, 247, 187, 16, 123, 74, 190, 123, 19, 
          215, 31, 108, 14, 79, 190, 138, 174, 233, 55, 25, 168, 78, 187, 86, 45, 66, 190, 
          137, 187, 49, 6, 184, 194, 126, 206, 175, 175, 123, 36, 247, 230, 218, 133, 177, 
          203, 63, 22, 147, 73, 33, 12, 128, 177, 146, 17, 150, 108, 209, 83, 105, 52, 141, 
          95, 209, 32, 45, 24, 69, 131, 51, 189, 63, 244, 42, 189, 30, 169, 116, 30, 251, 
          21, 126, 84, 62, 40, 136, 20, 59, 177, 95, 88, 149, 150, 117, 85, 236, 41, 133, 
          212, 160, 173, 160, 231, 0, 74, 7, 53, 102, 38, 106, 2, 56, 133, 31, 17, 194, 25, 
          233, 75, 213, 40, 64, 128, 61, 143, 110, 218, 247, 213, 237, 242, 212, 200, 196, 
          184, 63, 129, 102, 91, 27, 145, 160, 174, 196, 91, 224, 204, 40, 69, 149, 180, 
          247, 219, 122, 171, 212, 144, 93, 119 };
        private readonly byte[] key = new byte[] { 97, 115, 100 };
 
        public object NewClass(string classname)
        {
            System.Reflection.Assembly asm = RuntimeCodeCompiler.CompileEncryptedCode(ecode, key);
            return asm.CreateInstance(classname);
        }
        public object CallMethod(string classname, string methodname, params object[] methodparameters)
        {
            System.Reflection.Assembly asm = RuntimeCodeCompiler.CompileEncryptedCode(ecode, key);
            return RuntimeCodeCompiler.CallMethod(asm, classname, methodname, methodparameters);
        }
 
        // Available classes:  MyMath.BasicMath; 
    }
}

It's quite simple: at the beginning of the class there are two byte arrays: ecode and key, followed by two methods and a comment.

byte[] ecode can be very long and contains the encrypted code of the original class.

byte[] key contains the encryption key (converted from string to byte array). Wait! Before complaining about security etc, please have a gentle look herein above, where the "CheckBox 3 (Use secure code)" is described.

object NewClass(string classname) is the method to be called to instantiate an original class that is inside the encrypted code (ecode). I found this useful also when encrypting the code of a form: put both Form1.cs and Form1.Designer.cs in the same file and then encrypt them creating a management class containing only them; then, in the Program.cs replace 'Application.Run(new Form1());' with 'Application.Run(new MyManagementClass().NewClass("Form1"));'. Doing this I was able to run a simple program where the only visible code was that of the management class and that of the short 'program.cs' file with the Main class.

object CallMethod(string classname, string methodname, params object[] methodparameters) should be used to call a method inside an encrypted class. The parameters should be self explicative: the classname, the methodname and a list (comma separated) of all the parameters accepted by the method.

// Available classes: is a comment (wow!) that shows the names of all the classes contained inside the encrypted code (ecode). I put it there only for reference, to have the class names ready under my nose when I wanted to test it. If you don't like it, just delete it. (This comment is not shown when the checkbox "Use secure code" is checked).

More details

Let's examine now, with little more detail, how the two methods work.

NewClass method:

C#
private object NewClass(string classname)
{
    System.Reflection.Assembly asm = RuntimeEncryptedCodeCompiler.CompileEncryptedCode(ecode, key);
    return asm.CreateInstance(classname);  
}

Quite simple: create an Assembly using the method CompileEncryptedCode and create an instance from it.

C#
public static System.Reflection.Assembly CompileEncryptedCode(byte[] ecode, byte[] key)
{
    byte[] dcode = ecode.RijndaelEncryption(key, false);
    string code = System.Text.Encoding.UTF8.GetString(dcode);
    return CompileCode(code);
}

CompileEncryptedCode is a static method of class RuntimeEncryptedCodeCompiler found in the 'MyRuntimeCompiler.cs' file (included in the source zip file): it decrypts the key using the Rijndael encryption algorithm, then transforms the byte array to a string (containing the original code), and then compiles the code. Both the encryption routines and the run-time code compilation routines can be found in the private part of RuntimeEncryptedCodeCompiler. I coded the encryption algorithm from scratch in c# using the specifications (why did I do this? because I love writing algorithms and because it is beautiful, of course), while for the run-time compilation I searched the internet and copied/modified/adapted some parts of code from the StackOverflow site (see the first link to that site, mentioned in the Background section, above).

CallMethod method:

C#
private object CallMethod(string classname, string methodname, params object[] methodparameters)
{
    System.Reflection.Assembly asm = RuntimeEncryptedCodeCompiler.CompileEncryptedCode(ecode, key);
    return RuntimeEncryptedCodeCompiler.CallMethod(asm, classname, methodname, methodparameters);
}

As with the previous method, first create an Assembly object, then call a static method from the same RuntimeEncryptedCodeCompiler class mentioned above, to create an instance of the desired original class and then call, using reflection, the desired method (using Type -> InvokeMember), as seen in the following code:

C#
public static object CallMethod(Assembly ass, string classname, string methodname, object[] methodparameters)
{
    object obj = ass.CreateInstance(classname);
    object result = obj.GetType().InvokeMember(methodname, 
      System.Reflection.BindingFlags.InvokeMethod, null, obj, methodparameters);
    return result; 
}
Use the generated code

Once the previously generated "management class" is safely saved into a .cs file inside your project, it is easy to call a method of the "original class", now encrypted inside the "management class":

C#
myspace.MyManagementClass tc = new myspace.MyManagementClass();
object bm = tc.CallMethod("MyMath.BasicMath", "add", 4, 7);

In the first line we create a new instance of the "management class", here called MyManagementClass.

In the second line of code, we call a method of the encrypted "original class". We do this by calling the "CallMethod" method of the "management class", passing as parameters: the class name (with namespace, if any was present in the "original class"), the method name, and a list of the parameters as needed by this method. We then get the return object from this method (object bm), and we can also (optionally) convert or cast it to a more specific type that suits our needs.

Using the code (optional usage)

The case described above was the simplest one and it contained all the base features for compiling encrypted code during run-time; checking the optional features on the Form adds some useful code to the ouput, but does not change the base concepts just described.

Create easy methods

If we check the "Create easy methods" checkbox, some more methods will be created: for every class included in the "original class" text there will be one creator and one method to call the encrypted methods of every said classes. In our case (the "MyMath" class), one creator and one caller method will be created:

C#
public object NewMyMath_BasicMath() // the creator (no need to pass class name)
public object CallMyMath_BasicMathMethod (string methodname, params object[] methodparameters) // the method caller (no need to pass class name) 

These methods act as the previously mentioned NewClass and CallMethod methods, that, in this case, are not created.

C#
public object NewMyMath_BasicMath()
{
    System.Reflection.Assembly asm = RuntimeEncryptedCodeCompiler.CompileEncryptedCode(ecode, key);
    return asm.CreateInstance("MyMath.BasicMath");
}
 
public object CallMyMath_BasicMathMethod(string methodname, params object[] methodparameters)
{
    System.Reflection.Assembly asm = RuntimeEncryptedCodeCompiler.CompileEncryptedCode(ecode, key);
    return RuntimeEncryptedCodeCompiler.CallMethod(asm, "MyMath.BasicMath", methodname, methodparameters);
}
Create standalone class

If we check the "Create standalone class" checkbox, then the output "management class" will no longer be dependant on the "RuntimeEncryptedCodeCompiler" class: this is accomplished by including all the decryption routines inside the "management class". These routines include the previously mentioned "CompileEncryptedCode" method together with the Rijndael encryption/decryption methods.

"CompileEncryptedCode" is a modified version (with added encryption capabilities) of the code I found on the StackOverflow site (see the first link to that site, mentioned in the Background section, above). It mainly decrypts the code passed as parameter and convert it into an UTF8 string, then, with the help of "CSharpCodeProvider", it returns a System.Reflection.Assembly.

Rijndael encryption will not be explained here, since it is not the main topic of this article, and there are tons of document regarding it, through the internet.

If you want to take a look to the code of "CompileEncryptedCode" method or to examine the encryption routines, please find them in the "RuntimeEncryptedCodeCompiler" class, included in the sources (downloadable zip file at the top); you can also (of course) find them in the output "management class" of the 'Class Encryptor program'.

Use secure code

If we check the "Use secure code" checkbox, then the 'private readonly byte[] key' in the "management class" will be commented out, and all methods will have a 'byte[] key' as first parameter. In this scenario, we need to provide the password every time we call a method from the "management class".

public object NewClass(byte[] key, string classname)
{
    System.Reflection.Assembly asm = RuntimeEncryptedCodeCompiler.CompileEncryptedCode(ecode, key);
    return asm.CreateInstance(classname);
}
public object CallMethod(byte[] key, string classname, string methodname, params object[] methodparameters)
{
    System.Reflection.Assembly asm = RuntimeEncryptedCodeCompiler.CompileEncryptedCode(ecode, key);
    return RuntimeEncryptedCodeCompiler.CallMethod(asm, classname, methodname, methodparameters);
} 

As you can see in the code, the two basic methods are almost identical to the same methods generated without the "Use secure code" option. In the previous case the key was a global readonly variable: easier to use, but it stores the password inside the code (so, someone skilled enough could find it, and use it to decrypt your code); in this 'secure' case, instead, the key is not stored within the class code (so it should be more secure, that is why I gave the name "Use secure code" to the current option...), but every time you call a method, you must provide the key (which, for the lazy programmer, could be a nuisance).

There could be many other solutions, like using a base password that permutes/changes/decrypts somehow during the execution, so to hide it in the code (and I'll think about something like that during my 'meditation' time, but at the moment I do not consider this matter as strictly bound to the main idea of this article; but if you happen to have a great idea, then feel free to share in the comments below, and if people like it, then I'll implement it).

Using the code (RuntimeEncryptedCodeCompiler)

In the "MyRuntimeCompiler.cs" file there is a namespace called MyRuntime; and in that namespace there is the class RuntimeEncryptedCodeCompiler. This class is the 'core' of the run-time compilation of encrypted code and also the core of the creation of the "management class" (which, in case someone forgot, contains the encrypted code and the methods to use it).

This class contains three methods:

  1. "
    public static string CreateEncryptedCodeManagingClass(string
     code, byte[] key, string namespacename, string classname, bool 
    createStandAloneClass = false, bool createEasyMethods = false, bool
    securecode = false)
    ", which basically is called by the 'Class Encryptor' program when we press on the button 'Encrypt' (the only button in the form), to generate the code for the "management class".
    The code of this method is long, but obvious, since it consist in creating a big string, gradually appending smaller strings of code.  
  2. "public static System.Reflection.Assembly CompileEncryptedCode(byte[] ecode, byte[] key)", which decrypts the encrypted code, compiles and run it.
    public static System.Reflection.Assembly CompileEncryptedCode(byte[] ecode, byte[] key)
    {
        byte[] dcode = ecode.RijndaelEncryption(key, false);
        string code = System.Text.Encoding.UTF8.GetString(dcode);
        return CompileCode(code);
    }  
    first line decrypts the code into a byte array (using the Rijndael algorithm); second line transforms this decrypted byte array into a UTF8 string; third line compiles the code (using the private method "CompileCode", mentioned below).
  3. "public static object CallMethod(Assembly ass, string classname, string methodname, object[] methodparameters)", which, given an assembly, creates the respective object and uses Reflection to call a method inside it (usually we don't need to call this method directly, but it is called by the "management class" when created without the option "Create standalone class").

The other private methods contain:

  1. the very important "private static Assembly CompileCode(string code)", which, given as argument a string containing working C# code, compiles it (using the class 'Microsoft.CSharp.CSharpCodeProvider') and returns an Assembly object (with which we are able to instantiate a new object and also (through reflection) to call methods inside that object.
  2. a bunch o simple text editing routines, which are not worth explaining
  3. the encryption routines, which implement the Rijndael algorithm, which, in turn, is too long to be explained here (as I've already said before: if you are interested, they wrote many books and articles on this subject).

Points of Interest

With the routines explained below, some uses that come into my mind are:

  • hide code in your executables
  • hide code that will work only after providing a password
  • create a 'for your eyes' only executable to show info (text, images etc)
  • with little changes it is possible to get the encrypted code from outside the program, thus allowing that code to be stored in an external file, or to send the same code via internet (mail, website etc). Maybe, the next snowy week-end I'll implement some small changes to get the encrypted code from the outside.

I'm open to new ideas about other possible uses and also to suggestions about improving this program and related sources.

History

v1.00 was ready when I started writing the article... then, while writing, some ideas popped up and I implemented them, thus reaching the current v1.03 (which is the first published version).

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)


Written By
Software Developer (Senior)
Switzerland Switzerland
C#, SQL (and in the past also: C++, C, VBA)

Comments and Discussions

 
Questionhow to use after encrypt? Pin
Member 1316401631-May-19 3:32
Member 1316401631-May-19 3:32 
QuestionCan it used in WPF project? Pin
ly_he29-Jan-18 16:20
ly_he29-Jan-18 16:20 
Questionuse Ecrypted Class Pin
Yashar_smart28-Jul-15 1:57
Yashar_smart28-Jul-15 1:57 
GeneralMy vote of 4 Pin
ravithejag29-Jan-13 0:20
ravithejag29-Jan-13 0:20 
GeneralRe: My vote of 4 Pin
ravithejag29-Jan-13 0:22
ravithejag29-Jan-13 0:22 
GeneralRe: My vote of 4 Pin
Bruno Tabbia29-Jan-13 5:12
Bruno Tabbia29-Jan-13 5:12 
SuggestionDecryption Method on run time Pin
Vanlalruata_Hnamte23-Jan-13 1:02
professionalVanlalruata_Hnamte23-Jan-13 1:02 
GeneralRe: Decryption Method on run time Pin
Bruno Tabbia23-Jan-13 2:07
Bruno Tabbia23-Jan-13 2:07 
QuestionHow to decrypt the class...??? Pin
Kurniawan Prasetyo22-Jan-13 14:16
Kurniawan Prasetyo22-Jan-13 14:16 
AnswerRe: How to decrypt the class...??? Pin
Bruno Tabbia22-Jan-13 20:29
Bruno Tabbia22-Jan-13 20:29 
GeneralMy vote of 5 Pin
Fred Flams22-Jan-13 2:19
Fred Flams22-Jan-13 2:19 
GeneralMy vote of 4 Pin
wvd_vegt21-Jan-13 22:50
professionalwvd_vegt21-Jan-13 22:50 
GeneralRe: My vote of 4 Pin
Bruno Tabbia21-Jan-13 23:26
Bruno Tabbia21-Jan-13 23:26 
QuestionNice try, but... Pin
Dewey21-Jan-13 11:58
Dewey21-Jan-13 11:58 
AnswerRe: Nice try, but... Pin
Bruno Tabbia21-Jan-13 20:51
Bruno Tabbia21-Jan-13 20:51 
GeneralMy vote of 1 Pin
robert081521-Jan-13 7:50
robert081521-Jan-13 7:50 
GeneralRe: My vote of 1 Pin
Bruno Tabbia21-Jan-13 9:34
Bruno Tabbia21-Jan-13 9:34 
GeneralRe: My vote of 1 Pin
robert081521-Jan-13 10:12
robert081521-Jan-13 10:12 
GeneralRe: My vote of 1 Pin
Bruno Tabbia21-Jan-13 20:47
Bruno Tabbia21-Jan-13 20:47 
GeneralRe: My vote of 1 Pin
robert081521-Jan-13 21:17
robert081521-Jan-13 21:17 
GeneralRe: My vote of 1 Pin
Bruno Tabbia21-Jan-13 23:08
Bruno Tabbia21-Jan-13 23:08 
GeneralRe: My vote of 1 Pin
Fred Flams22-Jan-13 2:14
Fred Flams22-Jan-13 2:14 
GeneralRe: My vote of 1 Pin
robert081522-Jan-13 2:29
robert081522-Jan-13 2:29 
GeneralRe: My vote of 1 Pin
Bruno Tabbia22-Jan-13 5:09
Bruno Tabbia22-Jan-13 5:09 
GeneralMy vote of 5 Pin
adriancs21-Jan-13 7:25
mvaadriancs21-Jan-13 7:25 

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.