Click here to Skip to main content
Click here to Skip to main content

Mono.Cecil, The Most Powerful Tool You've Ever Heard Of

By , 19 Feb 2007
 

Introduction

At my current employer, the need for being able to "Sign" some additional information to an assembly came about. As an option, I could have always just created a CustomAttribute for the assembly, and recompiled each and every time I needed to, or actually injected some data after the fact.

I happened to come across a project that had very little documentation called Cecil (Mono.Cecil). Cecil is an IL modification library that is very powerful. Using Cecil, one can open up any existing .NET assembly, modify IL, bind events, remove functionality, add new functionality, etc., and then proceed to write the new assembly out to disk, or a byte[].

Basically when it comes down to it, in an effort to find a solution to a simple problem, I came across a tool that was way more powerful than I had ever heard of.

I intend to start out with some very basic examples and eventually dive into some more complex examples, so stay tuned!

The Custom "Signing" Example

For this example, I'll be signing a simple console application with some CustomAttributes, AFTER compile time.

I'm no expert with the post compilation tasks, but basically you'll want to compile dummyapp first, and copy the resulting EXE into the bin path of the AppSigner project (Program.cs).

using System;
using System.Collections.Generic;
using System.Text;
using Mono.Cecil;

namespace AppSigner
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Signing DummyApp.exe");
            // create an Assembly definition from target assembly... 
            // in this case I'll pass in full path to DummyApp.exe 
            AssemblyDefinition sourceAssembly 
                = AssemblyFactory.GetAssembly("DummyApp.exe");
            //Next, we'll need to "Import" a reference of the new 
            //CustomAttribute (which happens to exist in my signing 
            //app for the moment.)
            //typeof(string) denotes what the constructor parameter 
            //type is. If your attribute was of Guid you could use 
            //typeof(Guid) etc.
            CustomAttribute ca =
                new CustomAttribute(sourceAssembly.MainModule.Import(
            typeof(AssemblyExtendedInfo).GetConstructor(
                new Type[] { typeof(string) })));
            //Assign the parameters value of specified type
            ca.ConstructorParameters.Add("This is some extended information!");
            //go ahead and now add this customattribute instance 
            // back to the target assembly's CustomAttributes collection.
            sourceAssembly.CustomAttributes.Add(ca);
            //finally go ahead and persist back to disk. In this case 
            //for clarity, I went ahead and just wrote a new file out.
            AssemblyFactory.SaveAssembly(sourceAssembly, "newassembly.exe");
            Console.WriteLine("Signing Complete!");
            Console.WriteLine("Verifying...");
            //now just to verify, we'll open the new assembly back up, 
            //and loop through custom attributes...
            AssemblyDefinition targetAssembly
                = AssemblyFactory.GetAssembly("newassembly.exe");
    
            foreach (CustomAttribute eca in targetAssembly.CustomAttributes)
            {
                if (eca.Constructor.DeclaringType.Name == 
                    "AssemblyExtendedInfo")
                {
                    Console.WriteLine(
                    "newassembly.exe's ApplicationExtendedInformation attribute:");
                    Console.WriteLine(eca.ConstructorParameters[0].ToString());
                }
            }
            Console.ReadLine();
        }
    }
}

You also need to define the AssemblyExtendedInfo class which will contain your assemblies extra information (AssemblyExtendedInfo.cs).

using System;
using System.Collections.Generic;
using System.Text;

namespace AppSigner
{
    //mark this attribute as applicable to Assemblies
    [AttributeUsage(AttributeTargets.Assembly)]
    public sealed class AssemblyExtendedInfo : Attribute
    {
        private string extendedInfo = string.Empty;

        public string ExtenededInfo
        {
            get
            {
                return extendedInfo;
            }
        }

        //this will be the constructor we'll be referring to later
        public AssemblyExtendedInfo(string extendedInfo)
        {
            this.extendedInfo = extendedInfo;
        }

        public override string ToString()
        {
            return extendedInfo;
        }
    }
}

Points of Interest

Now many of you will wonder what the point is to this exercise considering that the new Attribute won't be visible within the assembly information in Windows Explorer etc. Well, you can easily use Simple Reflection to determine this value, or go ahead and reopen with the Mono.Cecil library. I hoped that this would give you a bit of insight into how powerful this library is, and what kind of modification you can make to an existing assembly without having to decompile or reflect out a bunch of source.

What's To Come?

I intend to follow up with at least two further articles explaining the capabilities of Mono.Cecil as well as further possible uses, etc. In the articles to come, I plan on going into rewriting functionality, as well as injecting tracing information into existing assemblies. I am somewhat new to Mono.Cecil so bear with me, I'll try to answer any questions I can!

Article History

  • 19th February, 2007 - Initial post

License

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

About the Author

ronnyek
United States United States
Member
I've been involved with software development since teaching Basic to teachers in 6th grade. Since then I've been involved with every aspect of computers.
 
Lately, I've involved myself very much in building Entperise Java and .Net applications.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 1memberSalvodif17 Jan '13 - 0:53 
few details another article will be appreciate
GeneralNew GUIDmembersabrown10026 Nov '07 - 12:19 
Thanks. Is there any way I can assign a new GUID to the patched assembley?
GeneralDebug mode...memberpablodg1 Nov '07 - 10:39 
Hi... Nice article!
 
By the way, did you get any workarround for instructing cecil not to remove debug stuff from an assembly? (I though maybe the problem is that visual studio realizes that the assembly changes after the .pdb was created... any clue?)
 
Regards, PabloDG
GeneralNice library !memberJuergen Posny19 Feb '07 - 23:19 
Nice library, I haven't got the time to have a deeper look in Cecil, but it looks very interesting!
 
I have only one question:
I had a look in the debug folder and I saw
the original DummyApp.exe is about 16kb,
the signed newassembly.exe is about 3kb.
Whatelse then adding a custom attribute is Cecil doing with the assembly?
 
Greetings, Juergen
GeneralRe: Nice library !memberronnyek20 Feb '07 - 3:30 
Truthfully I dont know... only thing I could guess is diff between building an assembly as debug mode and release. I suspect cecil is stripping all debugging hooks etc... but this is only speculation.
GeneralAbstract ILmemberInsincere Dave19 Feb '07 - 10:10 
This library looks great, I had a little look at Abstract IL but as it was designed for F# I didn't investigate it much. Thanks for the article.
GeneralUnsealing sealed classesmvpNishant Sivakumar19 Feb '07 - 8:22 
Many useful classes (from an inheritance perspective) in the BCL are sealed. This tool'd be good to unseal them easily. Though alternatively, you could ildasm, modify, and then ilasm to get the same thing.
 
Regards,
Nish
Nish’s thoughts on MFC, C++/CLI and .NET (my blog)
Currently working on C++/CLI in Action for Manning Publications. (*Sample chapter available online*)

GeneralRe: Unsealing sealed classesmemberRamon Smits19 Feb '07 - 10:20 
But! They wont have the same strong name thus this would be another copy. Better to disassemble only the required classes and put them in your own namespace to extend it with your required functionality.
GeneralRe: Unsealing sealed classesmemberPhilip Laureano20 Sep '07 - 16:48 
Here's how you do it, Nish:
 
string targetPath = @"...";
string targetFile = Path.Combine(targetPath, "YourFile.exe");
string outFile = Path.Combine(targetPath, "output.exe");
AssemblyDefinition assembly = AssemblyFactory.GetAssembly(targetFile);
ModuleDefinition mainModule = assembly.MainModule;
foreach(TypeDefinition typeDef in mainModule.Types)
{
   typeDef.IsSealed = false;
}
AssemblyFactory.SaveAssembly(assembly, outFile);
 
Enjoy! Smile | :)
GeneralGreat article!memberAlexey A. Popov19 Feb '07 - 7:03 
At last someone has touched the Cecil! I tired it once but wasn't sure at the time if it was a good framework.. Well, the project has been scrapped anyway Smile | :)
GeneralRe: Great article!memberronnyek19 Feb '07 - 7:58 
Thanks, keep an eye out... whats to come is where it gets REALLY interesting.
GeneralSigning for preventing unauthorized copiesُmemberMehran Taheri Mood19 Feb '07 - 6:03 
Hi and thanks for this good article.Smile | :)
Can I use this method to prevent others to make unauthorized copies of my software.
I am preparing a package and want to find a good way to protect it.

 
Regards,Mehran
GeneralRe: Signing for preventing unauthorized copiesُmemberronnyek19 Feb '07 - 6:11 
Absolutely!
As to whether this would be the ideal way to do it, I cant say yes there. In my articles, I wrote "Signing" in quotes as I didnt want it to be confused with Assembly signing with strong key etc.
 
If you were looking at a method of injecting customer information as well as perhaps a license, yes. You absolutely could do that very easily.
 
Create a custom attribute with a paramter type of data you want to store... say a "License", and write the extra information the same manner.
 
There is a small amount of overhead in "signing" an assembly, but if it fits your software model, it would be better than simply compiling a copy of the assembly for each and every customer, with a license as an embedded resource... (however with Cecil, I am pretty sure you could inject the license as a resource after the fact.)
 
I dont know if any of this was any use to you, but if you have any further questions, do ask them!
GeneralRe: Signing for preventing unauthorized copiesُmemberChristian Klauser19 Feb '07 - 6:34 
Wouldn't it be as easy for the cracker to change that customer information with this very same tool? Just thinking...
GeneralRe: Signing for preventing unauthorized copiesُmemberronnyek19 Feb '07 - 6:40 
Well thats why I said thats perhaps not the best method of doing this... While it would be possible, I assume if you were going to license software in such a manner, you'd have to do some License protection etc.(obviously plaintext isnt a real smart idea).
 
I would just go out on a limb here and say... one could use this method to handle licensing, and make it relatively secure... Being that wasn't the goal of this article, I didn't go into that at all.
 
I've got a serious difference in opinion regarding software licensing schemes than most developers and so this situation would be just solved completely different in my case =)
GeneralRe: Signing for preventing unauthorized copiesُmember[echo]26 Feb '07 - 22:22 
What's your opinion on licensing schemes then? (just curious)
 
//Henke

GeneralRe: Signing for preventing unauthorized copiesُmemberronnyek27 Feb '07 - 3:29 
My opinion is we're dealing with IL here. Personal opinion is your assembly protection is only going to stop from making it a free-for-all with your assemblies source. Anyone who's got the drive and resources to get your app for free, WILL crack your app.
 
Thats a fact I am able to live with, and focus on other parts of my software. Why invest thousands to protect your binaries, if its so easy. I generally tend to go with a bit of obfuscation, and then provide incentives for people to want to go legit.
 
Calling home is another route, however just about every implementation I've seen of this was more or less a joke, and pretty intrusive at that. A pop/imap library I use (should remain nameless =)) calls home and when it fails, it goes into "unlicensed mode". I cant tell you how frequently our app domains have to be restarted corporate servers because their licensing is horked. Alienating customers, and ultimately breaking live applications is not something I am going to be a part of... so in general my rule of thumbs are...
 
1) Dont overdue it, it will be cracked if there is enough desire for the app.
 
2) Price application at a point where cracking is simply not worth it
 
3) Ensure application fails gracefully, and doesn't really have the potential to break live applications.
 
4) Licensing should effectively reduce attack surface, and prevent your apps from being a complete free for all. Discourage the unseasoned enthusiast from using reflector to just rip out the bits that handle protection.
GeneralRe: Signing for preventing unauthorized copiesُmembersabrown10027 Oct '07 - 9:37 
My advice: With .Net - rely on your legal team for security. Get your app to log failed hacking attempts and send them to your server. Then sue them. But use some protection to avoid casual hacking.

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

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130516.1 | Last Updated 19 Feb 2007
Article Copyright 2007 by ronnyek
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid