Loading byte array assembly into new AppDomain thr

2019-08-12 06:56发布

问题:

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

回答1:

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;
    }            
}