.Net Managed Assembly Removal menu

User Tag List

Results 1 to 12 of 12
  1. #1
    Harland's Avatar Member
    Reputation
    8
    Join Date
    Oct 2007
    Posts
    50
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    .Net Managed Assembly Removal

    I've been looking into methods of removing a .Net Managed Assembly from a host process, but have run into some boundaries. I've currently been using the stock standard method of starting the CLR in the host process and loading my managed assembly into the default application domain. Whenever I made changes to my managed assembly I would just restart the host process and reinject.

    Has anyone managed to create their own App Domain from the bootstrapper and then execute a function from their managed assembly using ICLRRuntimeHost::ExecuteInAppDomain. That way I am hoping my managed assembly will be loaded into anything other than the default app domain, and thus can call AppDomain.Unload.

    I was wondering if anyone has some insight into whether this will drop the handle referencing my Managed Assembly without having to shutdown the process.
    Last edited by Harland; 07-10-2010 at 10:02 PM.

    .Net Managed Assembly Removal
  2. #2
    XTZGZoReX's Avatar Active Member
    Reputation
    32
    Join Date
    Apr 2008
    Posts
    173
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I'm fairly sure that if you return from the main method in the assembly you're hosting, the app domain will be unloaded, so you could just kick up another app domain right away.

    ... I may be wrong...

  3. #3
    Harland's Avatar Member
    Reputation
    8
    Join Date
    Oct 2007
    Posts
    50
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    If only that was true

    I have been experimenting with some alternative's. I get my C++ Bootstrap to load a C# Bootstrap. This C# Bootstrap then creates a new App Domain and loads a new Assembly. Since it's not the default App Domain it can be unloaded. Unfortunately I am having issues calling the method AppDomain::CreateInstanceAndUnwrap which makes use of an Interface to access my new Assembly methods.

    All code below has error handling removed and all Absolute referencing removed.

    C++ Bootstrapper:
    Code:
    HRESULT hr = CorBindToRuntimeEx(
    	NULL, 
    	L"wks",
    	0,
    	CLSID_CLRRuntimeHost,
    	IID_ICLRRuntimeHost, 
    	(PVOID*)&pClrHost);
    
    hr = pClrHost->Start();
    
    //szLibFile points to my C# Bootstrap
    DWORD dwRet = 0;
    hr = pClrHost->ExecuteInDefaultAppDomain(
    	szLibFile,
    	L"Bootstrapper.Entry", 
    	L"Start", 
    	L"TestParam", 
    	&dwRet);
    C# Bootstrapper:
    Code:
    namespace Bootstrapper
    {
        public class Entry
        {
    	public static int Start(String MyParameter)
            {
                AppDomainSetup ads = new AppDomainSetup();
    
                ads.ApplicationBase = MyParameter;
                ads.DisallowBindingRedirects = false;
                ads.DisallowCodeDownload = true;
                ads.ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
    
                AppDomain MyAppDomain = AppDomain.CreateDomain(string.Format("MyDomain"), null, ads);
               
                IEntry entryInstance = (IEntry)MyAppDomain .CreateInstanceAndUnwrap("BawtLib", "BawtLib.Entry");
    
                entryInstance.Start();
    
                return 0;
            } 
        }
    }
    C# Bawt Library
    Code:
    namespace BawtLib
    {
        [Serializable]
        public class Entry : MarshalByRefObject, IEntry
        {
            public void Start()
            {
                MessageBox.Show("Bootstrap Loaded Assembly Fine");
            }
        }
    }
    C# Shared Interface
    Code:
    namespace SharedInterface
    {
        public interface IEntry
        {
            void Start();
        }
    }
    So my method is:
    1. C++ Bootstrapper Loads the CLR into the host process. Then Executes the Entry::Start() Method in the C# Bootstrapper.
    2. The C# Boostrapper is suppose to create a new App Domain and create the object located in C# Bawt Library inside that domain. The SharedInterface is an interface to this method.

    Unfortunately the line below fails and I am unsure why.
    Code:
    Entry entryInstance = (IEntry)MyAppDomain .CreateInstanceAndUnwrap("BawtLib", "BawtLib.Entry");

  4. #4
    Kryso's Avatar Active Member
    Reputation
    40
    Join Date
    Jul 2009
    Posts
    97
    Thanks G/R
    0/3
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    AppDomain.Unload( AppDomain.CurrentDomain ) works for me like a charm
    Tea and cake or death?!

  5. #5
    XTZGZoReX's Avatar Active Member
    Reputation
    32
    Join Date
    Apr 2008
    Posts
    173
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Also, you're using the old pre-4.0 .NET hosting APIs; see this: http://www.mmowned.com/forums/world-...ml#post1907925

    Also, you just mentioned it yourself; why not just create the new AppDomains inside your C# bootstrapping DLL? (or am I misunderstanding you; is that what is failing?)

  6. #6
    Harland's Avatar Member
    Reputation
    8
    Join Date
    Oct 2007
    Posts
    50
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Yeah unfortunately it is failing at the point where I want to create an instance of my BawtLib.Entry class. Specifically the line below:

    Code:
    IEntry entryInstance = (IEntry)MyAppDomain .CreateInstanceAndUnwrap("BawtLib", "BawtLib.Entry");
    An exception is raised which is as follows:

    "Unable to cast transparent proxy to type"

    The solutions I have found on the net seem to confuse me, in particular this one, which suggests "subscribe to the AssemblyResolve Event for the first AppDomain".

    One thing I should note is I have set my new App Domain's BasePath to be where my Injector lies (along with all other assemblies). I tried throwing all my assemblies into the same folder as my host process and let the App Domain resolve it's own path, but that resulted in the same issue.

    Originally Posted by Kryso View Post
    AppDomain.Unload( AppDomain.CurrentDomain ) works for me like a charm
    I am guessing you managed to create a new App Domain then and load your assembly into that, because the default app domain cannot be unloaded as easy as that one liner.

    If anyone has any further suggestions, I will attempt to download VS2010 and get the .Net 4.0 Framework up and running. I have still yet to make that changeover.

  7. #7
    swayenvoy's Avatar Member
    Reputation
    1
    Join Date
    Jul 2008
    Posts
    19
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    You can try to use a Callback that will be executed in you AppDomain, this only works when you don't need a ref in the DefaultAppDomain.

    Some of my code for that problem:

    Code:
    AppDomain injectedDomain = AppDomain.CreateDomain("injectedDomain", null, domSetup);
                
                injectedDomain.DoCallBack(() =>
                {
                    Thread guiThread = new Thread(() =>
                    {
                        Assembly guiAsm = Assembly.LoadFrom(String.Format(@"{0}\TequilaSunrise.Wow.ObjectBrowser.dll", AppDomain.CurrentDomain.SetupInformation.ApplicationBase));
                        Type guiType = guiAsm.GetType("TequilaSunrise.Wow.ObjectBrowser.Starter");
                        MethodInfo guiMain = guiType.GetMethod("Main", BindingFlags.Static | BindingFlags.Public);
                        guiMain.Invoke(null, null);
                    });
                    guiThread.SetApartmentState(ApartmentState.STA);
                    guiThread.Start();
                });

  8. #8
    Apoc's Avatar Angry Penguin
    Reputation
    1387
    Join Date
    Jan 2008
    Posts
    2,750
    Thanks G/R
    0/12
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I'm only posting this once, this is more or less what we use in Onyx to handle reloading the assembly, without having to shut down the host each time.

    It allows you to press F11 to 'restart' the bot (only if you closed it out already).

    Code:
    using System;
    using System.IO;
    using System.Reflection;
    using System.Runtime.InteropServices;
    using System.Threading;
    using System.Windows.Forms;
    
    namespace Onyx.DomainManager
    {
        public interface IAssemblyLoader
        {
            void LoadAndRun(string file);
        }
    
        public class EntryPoint
        {
            [DllImport("User32.dll")]
            private static extern short GetAsyncKeyState(Keys vKey);
    
            [STAThread]
            public static int Main(string args)
            {
                bool firstLoaded = false;
                while (true)
                {
                    if (!firstLoaded)
                    {
                        firstLoaded = true;
                        new OnyxDomain(args);
                    }
    
                    if ((GetAsyncKeyState(Keys.F11) & 1) == 1)
                    {
                        new OnyxDomain(args);
                    }
    
                    Thread.Sleep(10);
                }
                return 0;
            }
        }
    
        public static class DomainManager
        {
            public static AppDomain CurrentDomain { get; set; }
            public static OnyxAssemblyLoader CurrentAssemblyLoader { get; set; }
        }
    
        public class OnyxAssemblyLoader : MarshalByRefObject, IAssemblyLoader
        {
            public OnyxAssemblyLoader()
            {
                AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
            }
    
            #region IAssemblyLoader Members
    
            public void LoadAndRun(string file)
            {
                Assembly asm = Assembly.Load(file);
                MethodInfo entry = asm.EntryPoint;
                //object o = asm.CreateInstance(entry.Name);
                entry.Invoke(null, null);
            }
    
            #endregion
    
            private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
            {
                if (args.Name == Assembly.GetExecutingAssembly().FullName)
                    return Assembly.GetExecutingAssembly();
    
                string appDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
                string shortAsmName = Path.GetFileName(args.Name);
                string fileName = Path.Combine(appDir, shortAsmName);
    
                if (File.Exists(fileName))
                {
                    return Assembly.LoadFrom(fileName);
                }
                return Assembly.GetExecutingAssembly().FullName == args.Name ? Assembly.GetExecutingAssembly() : null;
            }
        }
    
        /// <summary> 
        /// The actual domain object we'll be using to load and run the Onyx binaries.
        /// </summary>
        public class OnyxDomain
        {
            private readonly Random _rand = new Random();
            public OnyxDomain(string assemblyName)
            {
                try
                {
                    string appBase = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
                    var ads = new AppDomainSetup { ApplicationBase = appBase, PrivateBinPath = appBase };
                    DomainManager.CurrentDomain = AppDomain.CreateDomain("OnyxDomain_Internal_" + _rand.Next(0, 100000),
                                                                         null, ads);
                    AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
                    DomainManager.CurrentAssemblyLoader =
                        (OnyxAssemblyLoader)
                        DomainManager.CurrentDomain.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName,
                                                                   typeof(OnyxAssemblyLoader).FullName);
    
                    DomainManager.CurrentAssemblyLoader.LoadAndRun(
                        Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), assemblyName));
                }
                catch (Exception e)
                {
                    MessageBox.Show(e.ToString());
                }
                finally
                {
                    DomainManager.CurrentAssemblyLoader = null;
                    AppDomain.Unload(DomainManager.CurrentDomain);
                }
            }
    
            Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
            {
                try
                {
                    Assembly assembly = System.Reflection.Assembly.Load(args.Name);
                    if (assembly != null)
                        return assembly;
                }
                catch
                {
                    // ignore load error 
                }
    
                // *** Try to load by filename - split out the filename of the full assembly name
                // *** and append the base path of the original assembly (ie. look in the same dir)
                // *** NOTE: this doesn't account for special search paths but then that never
                //           worked before either.
                string[] Parts = args.Name.Split(',');
                string File = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\" + Parts[0].Trim() + ".dll";
    
                return System.Reflection.Assembly.LoadFrom(File);
            }
        }
    }
    Just use the following in your bootstrap DLL:

    Code:
    hr = clrHost->ExecuteInDefaultAppDomain(dllLocation, L"Onyx.DomainManager.EntryPoint", L"Main", L"NAME OF YOUR ASSEMBLY INCLUDING EXTENSION", &dwRet);
    dllLocation is the location of the DLL being injected. (We keep all of our files in the same directory, to make updates easier) Use GetModuleFileName to get the path of your injected bootstrap DLL.

    For the name of the assembly, you'd pass something like "Onyx.exe" for example.

    The assembly you're loading MUST have an entry point defined, or it will fail miserably.

  9. #9
    Harland's Avatar Member
    Reputation
    8
    Join Date
    Oct 2007
    Posts
    50
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Nice thanks Apoc.

    The idea of subscribing to the AssemblyResolve event really had me stumped.

    I can now happily go along coding without the hassle of restarting the host process. Yay!

  10. #10
    Threk's Avatar Member
    Reputation
    1
    Join Date
    Oct 2010
    Posts
    23
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by Apoc View Post
    I'm only posting this once, this is more or less what we use in Onyx to handle reloading the assembly, without having to shut down the host each time.

    It allows you to press F11 to 'restart' the bot (only if you closed it out already).

    Code:
    using System;
    using System.IO;
    using System.Reflection;
    using System.Runtime.InteropServices;
    using System.Threading;
    using System.Windows.Forms;
    
    namespace Onyx.DomainManager
    {
        public interface IAssemblyLoader
        {
            void LoadAndRun(string file);
        }
    
        public class EntryPoint
        {
            [DllImport("User32.dll")]
            private static extern short GetAsyncKeyState(Keys vKey);
    
            [STAThread]
            public static int Main(string args)
            {
                bool firstLoaded = false;
                while (true)
                {
                    if (!firstLoaded)
                    {
                        firstLoaded = true;
                        new OnyxDomain(args);
                    }
    
                    if ((GetAsyncKeyState(Keys.F11) & 1) == 1)
                    {
                        new OnyxDomain(args);
                    }
    
                    Thread.Sleep(10);
                }
                return 0;
            }
        }
    
        public static class DomainManager
        {
            public static AppDomain CurrentDomain { get; set; }
            public static OnyxAssemblyLoader CurrentAssemblyLoader { get; set; }
        }
    
        public class OnyxAssemblyLoader : MarshalByRefObject, IAssemblyLoader
        {
            public OnyxAssemblyLoader()
            {
                AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
            }
    
            #region IAssemblyLoader Members
    
            public void LoadAndRun(string file)
            {
                Assembly asm = Assembly.Load(file);
                MethodInfo entry = asm.EntryPoint;
                //object o = asm.CreateInstance(entry.Name);
                entry.Invoke(null, null);
            }
    
            #endregion
    
            private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
            {
                if (args.Name == Assembly.GetExecutingAssembly().FullName)
                    return Assembly.GetExecutingAssembly();
    
                string appDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
                string shortAsmName = Path.GetFileName(args.Name);
                string fileName = Path.Combine(appDir, shortAsmName);
    
                if (File.Exists(fileName))
                {
                    return Assembly.LoadFrom(fileName);
                }
                return Assembly.GetExecutingAssembly().FullName == args.Name ? Assembly.GetExecutingAssembly() : null;
            }
        }
    
        /// <summary> 
        /// The actual domain object we'll be using to load and run the Onyx binaries.
        /// </summary>
        public class OnyxDomain
        {
            private readonly Random _rand = new Random();
            public OnyxDomain(string assemblyName)
            {
                try
                {
                    string appBase = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
                    var ads = new AppDomainSetup { ApplicationBase = appBase, PrivateBinPath = appBase };
                    DomainManager.CurrentDomain = AppDomain.CreateDomain("OnyxDomain_Internal_" + _rand.Next(0, 100000),
                                                                         null, ads);
                    AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
                    DomainManager.CurrentAssemblyLoader =
                        (OnyxAssemblyLoader)
                        DomainManager.CurrentDomain.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName,
                                                                   typeof(OnyxAssemblyLoader).FullName);
    
                    DomainManager.CurrentAssemblyLoader.LoadAndRun(
                        Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), assemblyName));
                }
                catch (Exception e)
                {
                    MessageBox.Show(e.ToString());
                }
                finally
                {
                    DomainManager.CurrentAssemblyLoader = null;
                    AppDomain.Unload(DomainManager.CurrentDomain);
                }
            }
    
            Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
            {
                try
                {
                    Assembly assembly = System.Reflection.Assembly.Load(args.Name);
                    if (assembly != null)
                        return assembly;
                }
                catch
                {
                    // ignore load error 
                }
    
                // *** Try to load by filename - split out the filename of the full assembly name
                // *** and append the base path of the original assembly (ie. look in the same dir)
                // *** NOTE: this doesn't account for special search paths but then that never
                //           worked before either.
                string[] Parts = args.Name.Split(',');
                string File = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\" + Parts[0].Trim() + ".dll";
    
                return System.Reflection.Assembly.LoadFrom(File);
            }
        }
    }
    Just use the following in your bootstrap DLL:

    Code:
    hr = clrHost->ExecuteInDefaultAppDomain(dllLocation, L"Onyx.DomainManager.EntryPoint", L"Main", L"NAME OF YOUR ASSEMBLY INCLUDING EXTENSION", &dwRet);
    dllLocation is the location of the DLL being injected. (We keep all of our files in the same directory, to make updates easier) Use GetModuleFileName to get the path of your injected bootstrap DLL.

    For the name of the assembly, you'd pass something like "Onyx.exe" for example.

    The assembly you're loading MUST have an entry point defined, or it will fail miserably.
    Sorry, to push this thread, but I have a problem.
    Every time I use your code wow crashes.
    It's an SystemError.

    It says:
    "Eine neue Schutzseite für den Stapel kann nicht erstellt werden."
    ( in English: "A new guard page for the stack can not be created.")
    I'm using .Net 4.0 and Windows 7 64bit.

    Without your DomainManager works all fine, so my bootstrapper is working with .net 4.0

  11. #11
    streppel's Avatar Active Member
    Reputation
    77
    Join Date
    Mar 2007
    Posts
    196
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    on which line does it crash?
    debug it inprocess and oop and then we might be able to help you

  12. #12
    Bananenbrot's Avatar Contributor
    Reputation
    153
    Join Date
    Nov 2009
    Posts
    384
    Thanks G/R
    1/3
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    ... would you mind telling us exactly which statement throws? Obviously this sounds like a stack overflow...
    Did you wrap your injected code in try/catch block? Because that message doesn't sound like in StackOverflowException Class (System).

    I had the same problem, caused by the AssemblyResolve handler trying to resolve some dll...

    iirc, you can strip out this handler
    Code:
    Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
            {
                try
                {
                    Assembly assembly = System.Reflection.Assembly.Load(args.Name);
                    if (assembly != null)
                        return assembly;
                }
                catch
                {
                    // ignore load error 
                }
    
                // *** Try to load by filename - split out the filename of the full assembly name
                // *** and append the base path of the original assembly (ie. look in the same dir)
                // *** NOTE: this doesn't account for special search paths but then that never
                //           worked before either.
                string[] Parts = args.Name.Split(',');
                string File = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\" + Parts[0].Trim() + ".dll";
    
                return System.Reflection.Assembly.LoadFrom(File);
            }
    There were some awkward missing assembly references caused by that. System.Reflection.Assembly.LoadFrom(File); recurses and is responsible for the stack overflow.

Similar Threads

  1. [VB.NET]WoW PS Manager
    By Trle94 in forum Programming
    Replies: 1
    Last Post: 05-21-2010, 05:00 PM
  2. .NET 4 and Mixed Mode Assembly loading
    By adaephon in forum WoW Memory Editing
    Replies: 9
    Last Post: 04-16-2010, 04:18 PM
  3. Destructor's Tutorial: Managed .NET DLL Injection
    By ugkbunb in forum Programming
    Replies: 1
    Last Post: 07-30-2009, 05:15 PM
  4. [Release] Inject Managed .Net Code!
    By bigtimt in forum WoW Memory Editing
    Replies: 6
    Last Post: 10-12-2008, 03:52 PM
All times are GMT -5. The time now is 03:05 PM. Powered by vBulletin® Version 4.2.3
Copyright © 2024 vBulletin Solutions, Inc. All rights reserved. User Alert System provided by Advanced User Tagging (Pro) - vBulletin Mods & Addons Copyright © 2024 DragonByte Technologies Ltd.
Digital Point modules: Sphinx-based search