Inject method in C# assembly with Mono.Cecil - c#

I'm developing some .Net application and I need to inject in any assembly new method with my own code. I'm using Mono.Cecil to get body of assembly and I found some samples, but they're old enough. Unfortunately, there's no info in migraton section on github wiki.
So, I have this code:
using System;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace CustomFieldsInjection
{
public partial class Injector
{
public static void MethodInjection(string assemblyFilename, string typeName, string methodName)
{
AssemblyDefinition assembly = AssemblyFactory.GetAssembly(assemblyFilename);
TypeReference returnTypeReference = assembly.MainModule.Import(typeof(void));
MethodDefinition methodDefinition = new MethodDefinition(methodName, MethodAttributes.Public | MethodAttributes.Static, returnTypeReference);
Instruction instruction1 = methodDefinition.Body.CilWorker.Create(OpCodes.Nop);
Instruction instruction2 = methodDefinition.Body.CilWorker.Create(OpCodes.Ldstr, methodName);
MethodReference writeline = assembly.MainModule.Import(typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }));
methodDefinition.Body.CilWorker.Append(instruction1);
methodDefinition.Body.CilWorker.Append(instruction2);
methodDefinition.Body.CilWorker.InsertAfter(instruction2, methodDefinition.Body.CilWorker.Create (OpCodes.Call, writeline));
methodDefinition.Body.CilWorker.Append (methodDefinition.Body.CilWorker.Create(OpCodes.Ret))
assembly.MainModule.Inject(methodDefinition, assembly.MainModule.Types[typeName]);
MethodReference methodReference = null;
foreach (MethodDefinition method in assembly.MainModule.Types[typeName].Methods)
{
if (method.Name == methodName)
{
methodReference = assembly.MainModule.Import(method);
break;
}
}
Instruction callTest = methodDefinition.Body.CilWorker.Create(OpCodes.Call, methodReference);
if (assembly.EntryPoint != null)
{
assembly.EntryPoint.Body.CilWorker.InsertBefore(assembly.EntryPoint.Body.Instructions[0], callTest);
}
AssemblyFactory.SaveAssembly(assembly, assemblyFilename);
}
}
}
It's old sample. Most features are up to date. I'm interesting in this construction:
assembly.MainModule.Inject(methodDefinition, assembly.MainModule.Types[typeName]);
I could not find a new analogues of this design. Someone can tell me what it can be replaced?

I'm not familiar with the construct you are referring to, but adding a MethodDefinition to an existing type is quite easy
using (var assemblyDefinition = AssemblyDefinition.ReadAssembly("assemblyPath")) {
var module = AssemblyDefinition.MainModule;
//Select the type you need to open for addition
var typeDef = module.Types.First(td => td.Name == "footer");
//Add your MethodDefinition
typeDef.Methods.Add(your_method_definition);
//Write the assembly back
assemblyDefinition.Write();
}
NOTE: If you don't use yet cecil 0.10.0.0 you'll use slightly different ReadAssembly() and Write() variants (without the using, and passing the assemblyPath to Write, mainly...)

Related

Is there a way to make debugger work with modified assembly

I am trying to modify assembly before using it.
Main file:
using IlGenTestTarget;
using Lokad.ILPack;
using System.Reflection;
using Mono.Cecil;
using IlGenTest;
Assembly inAssembly = Assembly.GetAssembly(typeof(Class1));
AssemblyGenerator assemblyGenerator = new AssemblyGenerator();
byte[] b = assemblyGenerator.GenerateAssemblyBytes(inAssembly);
AssemblyDefinition assemblyDefinition = AssemblyDefinition.ReadAssembly(new MemoryStream(b));
foreach (ModuleDefinition module in assemblyDefinition.Modules) {
IlGenTestUtils.RemoveRemovedElements(module.Types);
foreach (TypeDefinition type in module.Types) {
IlGenTestUtils.RemoveRemovedElements(type.Methods);
IlGenTestUtils.RemoveRemovedElements(type.Fields);
}
}
MemoryStream ms = new MemoryStream();
assemblyDefinition.Write(ms);
byte[] res = ms.ToArray();
Assembly resAssembly = Assembly.Load(res);
Module resModule = resAssembly.GetModules()[0];
Type resType = resModule.GetType("IlGenTestTarget.Class1");
MethodInfo resMethod = resType.GetMethod("Method1");
resMethod.Invoke(null, null);
IlGenTestUtils:
using Mono.Cecil;
using Mono.Collections.Generic;
namespace IlGenTest
{
public class IlGenTestUtils
{
public static List<T> GetRemovedElements<T>(Collection<T> collection) where T : ICustomAttributeProvider
{
return collection
.Where(t => t.CustomAttributes.Any(attr => attr.AttributeType.Name == "RemovedAttribute"))
.ToList();
}
public static void RemoveRemovedElements<T>(Collection<T> collection) where T : ICustomAttributeProvider
{
foreach (T t in GetRemovedElements<T>(collection))
{
collection.Remove(t);
}
}
}
}
When I put breakpoint on Method1, everything works fine, but progam is not paused on it. When I invoke Method1 directly, without creating new assembly, program is paused on breakpoint as expected. Is there a way to make breakpoints work with dynamic assembly?
The link between "source line at which a breakpoint is placed" and "assembly file contents" is defined by the .pdb file for the assembly. After modifying the assembly, I would actually be surprised if the link between them would still work.
You would need to also rebuild the .pdb file, which seems hard if not impossible.

How to Apply XmlIncludeAttribute to TypeBuilder?

I am developing a library in C# that generates runtime types using System.Reflection.Emit.TypeBuilder class and i want to generate the following class hierarchy:
[XmlInclude(typeof(Derived))]
public class Base
{
}
public class Derived : Base
{
}
I use the TypeBuilder class in the following way:
class Program
{
public static void Main(string[] args)
{
var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("Test"), AssemblyBuilderAccess.Run);
var moduleBuilder = assembly.DefineDynamicModule("Test");
var baseTypeBuilder = moduleBuilder.DefineType("Base", TypeAttributes.Public, typeof(Object));
var derivedTypeBuilder = moduleBuilder.DefineType("Derived", TypeAttributes.Public);
derivedTypeBuilder.SetParent(baseTypeBuilder);
baseTypeBuilder.SetCustomAttribute(new CustomAttributeBuilder(typeof(XmlIncludeAttribute).GetConstructor(new[] { typeof(Type) }), new[] { derivedTypeBuilder }));
var baseType = baseTypeBuilder.CreateType();
var derivedType = derivedTypeBuilder.CreateType();
var attribute = baseType.GetCustomAttribute<XmlIncludeAttribute>();
}
}
The call:
var attribute = baseType.GetCustomAttribute<XmlIncludeAttribute>();
I receive the following error:
Could not load file or assembly 'Test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.
Any ideas are well-appreciated: how can i apply a custom attribute on a TypeBuilder for base class that refers to a TypeBuilder for a derived class?
P.S: I'm using Visual Studio 2017 (v15.7.5) and a C# Class Library (.NET Framework project template) NOT .NET Core or .NET Standard
Cannot provide much reason but a solution that will work without additional load/unload or whatever from and to disk:
Adding a separate field containing the actual Assembly, one can just subscribe to AppDomain.CurrentDomain.AssemblyResolve and check if the dynamic assembly was requested.
If it was, you just need to return your field and it works perfectly fine.
Example:
class Program
{
static Assembly ass;
public static void Main(string[] args)
{
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("Test"), AssemblyBuilderAccess.Run);
var moduleBuilder = assembly.DefineDynamicModule("Test");
var baseTypeBuilder = moduleBuilder.DefineType("Base", TypeAttributes.Public, typeof(Object));
var derivedTypeBuilder = moduleBuilder.DefineType("Derived", TypeAttributes.Public);
derivedTypeBuilder.SetParent(baseTypeBuilder);
baseTypeBuilder.SetCustomAttribute(new CustomAttributeBuilder(typeof(XmlIncludeAttribute).GetConstructor(new[] { typeof(Type) }), new[] { derivedTypeBuilder }));
var baseType = baseTypeBuilder.CreateType();
var derivedType = derivedTypeBuilder.CreateType();
ass = baseType.Assembly;
var attribute = baseType.GetCustomAttribute<XmlIncludeAttribute>();
Console.WriteLine(attribute.Type.FullName);
}
private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
return ass;
}
}
I've reproduced your exception. It's looks like the .NET Framework want to read a module from disk.
So simplest workaround would be to save your assembly to the disk:
var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(
new AssemblyName("Test"),
AssemblyBuilderAccess.RunAndSave); // allow run & save
var moduleBuilder = assembly.DefineDynamicModule("Test",
"Test.dll"); // specify a file name where module will be stored
...
var baseType = baseTypeBuilder.CreateType();
var derivedType = derivedTypeBuilder.CreateType();
assembly.Save("Test.dll");
Now I was able to get attribute without exception:
var attribute = baseType.GetCustomAttribute<XmlIncludeAttribute>();
Well, I can tell you why the fix #X39 posted works. Stepping through the forest of framework code (perhaps fallbacks for different cultures) for the baseType.GetCustomAttribute(); call, when finally reaching appdomain.cs I see:
At this point, _AssemblyResolve is null (which is the private backing field for AppDomain.CurrentDomain.AssemblyResolve) and after stepping through to return null;, the code crashes with the error you posted.
More info: https://learn.microsoft.com/en-us/dotnet/api/system.appdomain.assemblyresolve?view=netframework-4.7.2
#X39 feel free to merge my answer and let me know so that I can remove mine, I did not want to edit your answer directly.

Adding support for dynamic file loaders in c#

If I have a Console App in C# that reads in files of a certain format and converts them to business objects, I design this by having an IReader interface so that I can support different formats, eg XML, CSV, pipe delimited etc, and have different concrete classes for each file format.
If the requirement is to be able to load new file readers (new formats) in dynamically without having to recompile, is there a way I can accomplish that?
The only way I can think of is somehow using XSD or reg expressions but it seems to me there should be a better solution
This sounds like you want a plugin mechanism for loading your IReaders dynamically. There are plenty of examples out there.
Simple plugin mechanism sample
SO discussion
You could use reflection. Each implementation of IReader could go in a distinct DLL. You would also create an Attribute to tag each implementation of IReader that states which file format it handles.
public sealed class InputFormatAttribute : Attribute
{
private string _format;
public string Format
{
get { return format; }
}
public InputFormatAttribute(string format)
{
_format = format;
}
}
[InputFormat("CSV")]
public class CSVReader : IReader
{
// your CSV parsing code here
public BusinessObject Parse(string file)
{}
}
BusinessObject LoadFile(string fileName)
{
BusinessObject result = null;
DirectoryInfo dirInfo = new DirectoryInfo(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location));
FileInfo[] pluginList = dirInfo.GetFiles("*.DLL");
foreach (FileInfo plugin in pluginList)
{
System.Reflection.Assembly assem = System.Reflection.Assembly.LoadFile(fileInfo.FullName);
Type[] types = assem.GetTypes();
Type type = types.First(t => t.BaseType == "IReader");
object[] custAttrib = type.GetCustomAttributes(typeof(InputFormatAttribute), false);
InputFormatAttribute at = (InputFormatAttribute)custAttrib[0];
if (at.Format.Equals(Path.GetExtension(fileName).Substring(1), StringComparison.CurrentCultureIgnoreCase))
{
IReader reader = (IReader)assem.CreateInstance(type.FullName);
return reader.Parse(fileName);
}
}
// got here because not matching plugin found
return null;
}
Depends on the complicity of Readers, you may decide to use CodeDom to let someone to write the code directly. Short example:
// create compiler
CodeDomProvider provider = CSharpCodeProvider.CreateProvider("C#");
CompilerParameters options = new CompilerParameters();
// add more references if needed
options.ReferencedAssemblies.Add("system.dll");
options.GenerateExecutable = false;
options.GenerateInMemory = true;
// compile the code
string source = "using System;namespace Bla {public class Blabla { public static bool Test { return false; }}}";
CompilerResults result = provider.CompileAssemblyFromSource(options, source);
if (!result.Errors.HasErrors)
{
Assembly assembly = result.CompiledAssembly;
// instance can be saved and then reused whenever you need to run the code
var instance = assembly.CreateInstance("Bla.Blabla");
// running some method
MethodInfo method = instance.GetType().GetMethod("Test"));
var result = (bool)method.Invoke(_formulas, new object[] {});
}
But probably you'll have to provide sort of editor or accomplish the task partially (so only necessary code have to be written).

c# create an instance of an object from string

I have a string variable contain:
string classCode = "public class Person { public string Name{get;set;} }";
How can I create an instance of an object from the classCode ?
like
object obj = CreateAnInstanceAnObject(classCode);
You'll need to use CodeDom to compile an in-memory assembly, and then use reflection to create the type.
Here's a sample article on MSDN that walks through the process of code generation.
Once you've compiled the code, you can use Activator.CreateInstance to create an instance of it.
Building on the answers from above, here is a working demo to generate, compile and instantiate a class from an in-memory assembly:
namespace DynamicCompilation
{
using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Reflection;
using Microsoft.CSharp;
internal static class Program
{
private static void Main()
{
var ccu = new CodeCompileUnit();
var cns = new CodeNamespace("Aesop.Demo");
cns.Imports.Add(new CodeNamespaceImport("System"));
var ctd = new CodeTypeDeclaration("Test")
{
TypeAttributes = TypeAttributes.Public
};
var ctre = new CodeTypeReferenceExpression("Console");
var cmie = new CodeMethodInvokeExpression(ctre, "WriteLine", new CodePrimitiveExpression("Hello World!"));
var cmm = new CodeMemberMethod
{
Name = "Hello",
Attributes = MemberAttributes.Public
};
cmm.Statements.Add(cmie);
ctd.Members.Add(cmm);
cns.Types.Add(ctd);
ccu.Namespaces.Add(cns);
var provider = new CSharpCodeProvider();
var parameters = new CompilerParameters
{
CompilerOptions = "/target:library /optimize",
GenerateExecutable = false,
GenerateInMemory = true
};
////parameters.ReferencedAssemblies.Add("System.dll");
var results = provider.CompileAssemblyFromDom(parameters, ccu);
if (results.Errors.Count == 0)
{
var t = results.CompiledAssembly.GetType("Aesop.Demo.Test");
var inst = results.CompiledAssembly.CreateInstance("Aesop.Demo.Test");
t.InvokeMember("Hello", BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod, null, inst, null);
}
Console.ReadLine();
}
}
}
Simple put you cannot do this in one line as you are attempting. It is possible to create an instance of an existing class via it's name and one of the overloads of Activator.CreateInstance.
What you are trying to achieve here though is quite different. You are attempting to both 1) define a new class type and 2) create an instance of it. Defining new metadata in the running process dynamically is very difficult to achieve with static languages like C#. It requires a significant amount of work that can't easily be put into a StackOverflow answer.
The following project should guide you in what your trying to accomplish:
RunTime Code Compilation
However, if you are attempting to write code at runtime, you may want to rethink your architecture. You may be creating more of a headache for yourself than you need to be.
What are you trying to accomplish by creating this object?

How to load Assembly at runtime and create class instance?

I have a assembly. In this assembly I have a class and interface. I need to load this assembly at runtime and want to create an object of the class and also want to use the interface.
Assembly MyDALL = Assembly.Load("DALL"); // DALL is name of my dll
Type MyLoadClass = MyDALL.GetType("DALL.LoadClass"); // LoadClass is my class
object obj = Activator.CreateInstance(MyLoadClass);
This is my code. How could it be improved?
If your assembly is in GAC or bin use the assembly name at the end of type name instead of Assembly.Load().
object obj = Activator.CreateInstance(Type.GetType("DALL.LoadClass, DALL", true));
You should Use Dynamic Method with for Improving. its faster than reflection..
Here is a sample code for creating Object using Dynamic Method..
public class ObjectCreateMethod
{
delegate object MethodInvoker();
MethodInvoker methodHandler = null;
public ObjectCreateMethod(Type type)
{
CreateMethod(type.GetConstructor(Type.EmptyTypes));
}
public ObjectCreateMethod(ConstructorInfo target)
{
CreateMethod(target);
}
void CreateMethod(ConstructorInfo target)
{
DynamicMethod dynamic = new DynamicMethod(string.Empty,
typeof(object),
new Type[0],
target.DeclaringType);
ILGenerator il = dynamic.GetILGenerator();
il.DeclareLocal(target.DeclaringType);
il.Emit(OpCodes.Newobj, target);
il.Emit(OpCodes.Stloc_0);
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Ret);
methodHandler = (MethodInvoker)dynamic.CreateDelegate(typeof(MethodInvoker));
}
public object CreateInstance()
{
return methodHandler();
}
}
//Use Above class for Object Creation.
ObjectCreateMethod inv = new ObjectCreateMethod(type); //Specify Type
Object obj= inv.CreateInstance();
This method takes only 1/10th time needed by Activator.
Check out http://www.ozcandegirmenci.com/post/2008/02/Create-object-instances-Faster-than-Reflection.aspx
Check out http://www.youtube.com/watch?v=x-KK7bmo1AM
To modify his code to load multiple assemblies use
static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
string assemblyName = args.Name.Split(',').First();
using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("YourNamespace." + assemblyName + ".dll"))
{
byte[] assemblyData = new byte[stream.Length];
stream.Read(assemblyData, 0, assemblyData.Length);
return Assembly.Load(assemblyData);
}
}
In your main method put
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
Be sure to add your assemblies to your project and change the build action property to "Embedded Resource".

Categories