Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Loading byte array assembly into new AppDomain throws a FileNotFound exception

I'm trying to do the following:

  • Download a byte array that contains an assembly that I need to execute.
  • Load an object from this assembly in a new app domain and execute method on the object

Here is my code that attempts to load the assembly into new app domain:

    public object Execute(byte[] agentCode)
    {
        var app = AppDomain.CreateDomain("MonitoringProxy", AppDomain.CurrentDomain.Evidence, new AppDomainSetup {ApplicationBase = AppDomain.CurrentDomain.BaseDirectory}, new PermissionSet(PermissionState.Unrestricted));
        app.AssemblyResolve += AppOnAssemblyResolve;
        var assembly = app.Load(agentCode);

The codebase dies on the last line with the following message:

Additional information: Could not load file or assembly 'Alertera.AgentProxy, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.

No code ever hits the AppOnAssemblyResolve function. What's interesting is that it has read the name of the assembly properly. Furthermore, Alertera.AgentProxy assembly does not have any external dependencies, except on System and Newtonsoft.Json. However, Newtsoft.Json has been imbedded into it as a resource, so it does not need to be loaded separately.

Any pointers? Using .NET 2 for maxmimum compatibility

like image 795
Igorek Avatar asked Feb 01 '26 06:02

Igorek


1 Answers

Maybe using the callback on the app domain to switch to the context of the newly created app domain will allow you to load successfully? Something like this...

    public object Execute(byte[] assemblyBytes)
    {
        AppDomain domainWithAsm = AsmLoad.Execute(assemblyBytes);
        ....
    }

    [Serializable]
    public class AsmLoad
    {
        public byte[] AsmData;

        public void LoadAsm() 
        {
            Assembly.Load(AsmData);
            Console.WriteLine("Loaded into: " + AppDomain.CurrentDomain.FriendlyName);
        }

        public static AppDomain Execute(byte[] assemblyBytes)
        {
            AsmLoad asmLoad = new AsmLoad() { AsmData = assemblyBytes };
            var app = AppDomain.CreateDomain("MonitoringProxy", AppDomain.CurrentDomain.Evidence, new AppDomainSetup { ApplicationBase = AppDomain.CurrentDomain.BaseDirectory }, new PermissionSet(PermissionState.Unrestricted));
            app.DoCallBack(new CrossAppDomainDelegate(asmLoad.LoadAsm));
            return app;
        }
    }

EDIT:

Here is a more complete example, which shows how to load an assembly and pass information back to the calling app domain, and also unloads the app domain created to load the assembly.

class Program
{
    static void Main(string[] args)
    {
        var assemblyBytes = File.ReadAllBytes(@"C:\dev\Newtonsoft.Json.dll");

        // load an unload the same assembly 5 times
        for (int i = 0; i < 5; i++)
        {
            var assemblyContainer = AssemblyContainer.LoadAssembly(assemblyBytes, true);
            var assemblyName = assemblyContainer.AssemblyName;

            assemblyContainer.Unload();
        }

        Console.ReadKey();
    }
}    

[Serializable]
public class AssemblyContainer
{
    public byte[] AssemblyData { get; set; }
    public bool ReflectionOnly { get; set; }
    private AppDomain Container { get; set; }
    public AssemblyName AssemblyName { get; set; }

    /// <summary>
    /// Unload the domain containing the assembly
    /// </summary>
    public void Unload()
    {
        AppDomain.Unload(Container);
    }

    /// <summary>
    /// Load the assembly
    /// </summary>
    /// <remarks>This will be executed</remarks>
    public void LoadAssembly()
    {                
        var assembly = ReflectionOnly ? Assembly.ReflectionOnlyLoad(AssemblyData) : Assembly.Load(AssemblyData);
        AssemblyName = assembly.GetName();

        // set data to pick up from the main app domain
        Container.SetData("AssemblyData", AssemblyName);
    }

    /// <summary>
    /// Load the assembly into another domain
    /// </summary>
    /// <param name="assemblyBytes"></param>
    /// <param name="reflectionOnly"></param>
    /// <returns></returns>
    public static AssemblyContainer LoadAssembly(byte[] assemblyBytes, bool reflectionOnly = false)
    {
        var containerAppDomain = AppDomain.CreateDomain(
            "AssemblyContainer",
            AppDomain.CurrentDomain.Evidence,
            new AppDomainSetup
            {
                ApplicationBase = AppDomain.CurrentDomain.BaseDirectory
            },
            new PermissionSet(PermissionState.Unrestricted));

        AssemblyContainer assemblyContainer = new AssemblyContainer()
        {
            AssemblyData = assemblyBytes,
            ReflectionOnly = reflectionOnly,
            Container = containerAppDomain
        };

        containerAppDomain.DoCallBack(new CrossAppDomainDelegate(assemblyContainer.LoadAssembly));

        // collect data from the other app domain
        assemblyContainer.AssemblyName = (AssemblyName)containerAppDomain.GetData("AssemblyData");
        return assemblyContainer;
    }            
}    
like image 188
steve16351 Avatar answered Feb 02 '26 18:02

steve16351



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!