Monday, March 16, 2020

Dynamic Assembly Loading (C# Reflection)

We will achieve following task with dynamic assembly loading in C#.

using System.Net.Http;
using System;
using System.Linq;

var client = new HttpClient();
client.BaseAddress = new Uri("http://api.open-notify.org/astros.json");
string result = client.GetStringAsync("").Result;


Don't add System.Net.Http dll to your reference list. It will be automatically loaded from GAC.

using System;
using System.Linq;
using System.Reflection;

//We don't have "using System.Net.Http" 

//Load System.Net.Http
string name1 = "System.Net.Http,Version=4.0.0.0," + "Culture=neutral,PublicKeyToken=b03f5f7f11d50a3a";
Assembly a1 = Assembly.Load(name1);

//Create HttpClient instance
Type clientType = a1.GetTypes().Where(t => t.Name.Equals("HttpClient")).Single();
object client = Activator.CreateInstance(clientType);

//Set BaseAddress on client
PropertyInfo propertyBaseAddress = clientType.GetProperties().Where(p => p.Name == "BaseAddress").Single();
Uri url = new Uri("http://api.open-notify.org/astros.json");
propertyBaseAddress.SetValue(client, url);

//Test property value
var BaseAddressValue = propertyBaseAddress.GetValue(client);

//Get HttpResponseMessage Task
var methodGetStringAsync = clientType.GetMethod("GetStringAsync", new Type[] { typeof(Uri) });
var responseTask = methodGetStringAsync.Invoke(client, new string[] { null });

//Get HttpResponseMessage result from Task
var responseType = responseTask.GetType();
PropertyInfo propResponse = responseType.GetProperties().Where(p => p.Name == "Result").Single();

var responseResult = propResponse.GetValue(responseTask);  //This is the string result


Bonus: List your loaded assemblies

Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();

foreach (Assembly a in assemblies)
{
     Console.WriteLine(a.GetName());
}


Output
mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Net.Http, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
System.Net.Http is displayed here even not referenced in Visual Studio.

Friday, March 13, 2020

Strong Name Signing in .NET

Using a strong name protects your assembly against manipulation.

For example your software using a class named Class1 in ClassLibrary1 namespace.

If you compile a new dll contains ClassLibrary1.Class1 then you can use this dll instead of original one.

This is very basic example but it can be applicable in real life.

To be sure that your software is using your assembly version then you can sign your project.

Microsoft description is:
When a strong-named assembly is created, it contains the simple text name of the assembly, the version number, optional culture information, a digital signature, and the public key that corresponds to the private key used for signing.
Example here shows signing process in Visual Studio. You can use also command line tool (sn.exe).

Coding

Our solution has 2 projects, executable and a class library. Main program is using ClassLibrary1.Class1

using ClassLibrary1;
using System;

namespace strongname
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("strongname loaded");

            Class1 c = new Class1();

            Console.ReadKey();
        }
    }
}


using System;

namespace ClassLibrary1
{
    public class Class1
    {
        public Class1()
        {
            Console.WriteLine("Hello from Class1");
        }
    }
}

Sign

Right click to project in VS and select Properties. In Signing tab check "Sign the assembly" and select "New" in listbox. Enter password for your private key.


This will create a pfx (Personal Information Exchange Format) file in your project folder.

Run

strongname loaded
Hello from Class1

Recompile

If you create a new project with the same namespace, class and functions then you can use the new dll with current executable.

For test change the code and recompile dll in outside of project folder.

csc.exe /t:library /out:ClassLibrary1.dll Class1.cs
Then replace the old ClassLibrary1.dll with the new file.

Result

When you try to run your executable an error will be shown because the new dll has not signed with correct key.

C:\strongname\bin\Debug\strongname.exe

Unhandled Exception: System.IO.FileLoadException: Could not load file or assembly 'ClassLibrary1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=77d705841860f240' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)
at strongname.Program.Main(String[] args)

If you completely remove signing from your projects then your manipulated dll will work.