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");
AssemblyDefinition sourceAssembly
= AssemblyFactory.GetAssembly("DummyApp.exe");
CustomAttribute ca =
new CustomAttribute(sourceAssembly.MainModule.Import(
typeof(AssemblyExtendedInfo).GetConstructor(
new Type[] { typeof(string) })));
ca.ConstructorParameters.Add("This is some extended information!");
sourceAssembly.CustomAttributes.Add(ca);
AssemblyFactory.SaveAssembly(sourceAssembly, "newassembly.exe");
Console.WriteLine("Signing Complete!");
Console.WriteLine("Verifying...");
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
{
[AttributeUsage(AttributeTargets.Assembly)]
public sealed class AssemblyExtendedInfo : Attribute
{
private string extendedInfo = string.Empty;
public string ExtenededInfo
{
get
{
return extendedInfo;
}
}
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