1

I have a .NET assembly that is built by me but would like to be able rewrite the .DLL with some minor but arbitrary attribute change file at runtime. Specifically I would like to be able to change a property of an attribute of a class so that I can customize the binary depending on the situation.

To illustrate, I want to achieve the effect of editing the assembly being generated from the code

[SomeAttribute("Name")]
public class MyClass{
    ...

such that the new assembly is functionally the same as

[SomeAttribute("Custom Name")]
public class MyClass{
    ...

And this "Custom Name" could be anything (determined at runtime). Is this possible to do at runtime?

The reason why the actual .DLL needs to be modified is because it will get loaded up by a seperate process which cannot determine the runtime information (I do not control this process).

Experimentation so far has shown that it seems to work if the new "Custom Name" is the same length as the original, but not otherwise (even if you edit the preceding byte that specifies the length; presumably there are offsets stored in the file somewhere).

EDIT: Forgot to mention, solution needs to be under the .NET 2 framework as well.

11
  • 1
    I would recommend an alternative approach in which you can create a new assembly and create a new subclass of MyClass in that assembly along with changes in the attribute name. Your current approach may not be allowed and antivirus may detect it as harmful behavior. Commented Jul 24, 2018 at 7:05
  • @AkashKava Won't I have to rebuild the assembly manually then with the subclass inside? I need the new name to be arbitrary; it can't be pre-made. I am not currently so worried about antivirus as I have full control over the original assembly (it's not a system assembly or anything) Commented Jul 24, 2018 at 7:20
  • 1
    I can't remember the exact details, but if you're using signed assemblies - which in a production environment you should be - then you can't even change one bit without invalidating the assembly. Commented Jul 24, 2018 at 8:09
  • @Enigmativity yes that's correct, that's not a concern right now though Commented Jul 24, 2018 at 8:13
  • 1
    To modify it, Fody and Mono.Cecil are probably your instruments. In general what you are trying to do is a bad idea. And probably an XY problem Commented Jul 24, 2018 at 8:16

3 Answers 3

2

Unclear what you really want to do (XY problem?)

Still, if you want to modify an assembly, you normally use Mono.Cecil that self-describes as: you can load existing managed assemblies, browse all the contained types, modify them on the fly and save back to the disk the modified assembly. .

Note that an attribute can contain extra data on top of the data that is passed as a parameter:

public class MyAttribute : Attribute
{
    public MyAttribute(string str)
    {
        argument = str;
    }

    private string argument;

    public string Argument { get; }

    public string AssemblyName
    {
        get
        {
            return Assembly.GetEntryAssembly().FullName;
        }
    }
}


[MyAttribute("Hello")]
class Program
{
    static void Main(string[] args)
    {
        var attr = typeof(Program).GetCustomAttribute<MyAttribute>();
        Console.WriteLine(attr.Argument);
        Console.WriteLine(attr.AssemblyName);
    }
}
Sign up to request clarification or add additional context in comments.

3 Comments

Great suggestion with the Attribute, unfortunately the attribute in question is a sealed class and moreover the value I want to change isn't virtual.
Sorry I forgot to mention this before, but solution needs to be run on .NET 2, will update the question
I will have a look in the archive for older versions of Mono.Cecil, it seems they used to support .NET 2.
1

Using the extremely helpful suggestion from @xanatos I have made this solution:

Under .NET 2, you can install package Mono.Cecil 0.9.6.1.

The code then is as follows:

AssemblyDefinition assbDef = AssemblyDefinition.ReadAssembly("x.dll");
TypeDefinition type = assbDef.MainModule.GetType("NameSpace.MyClass").Resolve();
foreach (CustomAttribute attr in type.CustomAttributes)
{
    TypeReference argTypeRef = null;
    int? index = null;
    for (int i = 0; i < attr.ConstructorArguments.Count; i++)
    {
        CustomAttributeArgument arg = attr.ConstructorArguments[i];

        string stringValue = arg.Value as string;
        if (stringValue == "Name")
        {
            argTypeRef = arg.Type;
            index = i;
        }
    }

    if (index != null)
    {
        attr.ConstructorArguments[(int)index] = new CustomAttributeArgument(argTypeRef, newName);
    }
}    
assbDef.Write("y.dll");

Which will search an assembly for any attribute arguments with value "Name" and replace their value with newName.

Comments

0

Rather than modifying the DLL, you can add attributes at run-time using the TypeDescriptor class; e.g.

TypeDescriptor.AddAttributes(typeof(MyClass), new SomeAttribute("Custom Name"));

The only caveat with this approach is that code which relies purely on reflection will not be able to read the added attribute(s) - you must use TypeDescriptor.GetAttributes(). However, most of the built-in .NET Framework methods that operate on attributes are aware of metadata added at run-time.

https://msdn.microsoft.com/en-us/library/system.componentmodel.typedescriptor_methods(v=vs.110).aspx

1 Comment

Unfortunately that's not an option, the dll could be invoked somewhere else (by a different process). It needs to updated for all future possible uses of the dll, not just for me loading it in memory. I'll clarify the question a little.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.