vendredi 12 août 2022

How to access running program from dynamically loaded dll

So I'm trying to access a running program from which I've injected an external DLL at runtime. I have two projects: console app and class library.

Console app:

using System;
using System.Reflection;

namespace SharpConsole
{
    public class GameEvent
    {
        private bool _cancelled;

        public bool Cancelled { get => _cancelled; }

        public void SetCancelled(bool cancelled)
        {
            _cancelled = cancelled;
        }

        public override string ToString()
        {
            return $"Event (Cancelled={Cancelled})";
        }
    }

    public class API
    {
        public static void Print(string text)
        {
            Console.WriteLine($"[API] {text}");
        }
    }

    public class Program
    {
        public static string TestValue = "some initial test value";

        static void Main(string[] args)
        {
            var evt = new GameEvent();

            Console.WriteLine("[Console] " + evt.ToString());
            Console.WriteLine("[Console] Loading dll...");
            // var asm = Assembly.LoadFile(@"C:\Users\tinytengu\Desktop\SharpTest\SharpExternal\bin\Release\netcoreapp3.1\SharpExternal.dll");
            var asm = Assembly.LoadFile(@"C:\Users\tinytengu\SharpExternal\SharpExternal.dll");

            var classType = asm.GetType("SharpExternal.Class1");                                    
            Console.WriteLine("[Console] Invoking SharpExternal.Class1.Main method...");
            var methodInfo = classType.GetMethod("Main", BindingFlags.Static | BindingFlags.Public);
            methodInfo.Invoke(null, new object[] { evt });

            Console.WriteLine("[Console] After changes: " + evt.ToString());
            Console.WriteLine();

            methodInfo = classType.GetMethod("ShowTestValue", BindingFlags.Static | BindingFlags.Public);
            methodInfo.Invoke(null, null);

            Console.ReadLine();
        }
    }
}

And a class library:

using System;

namespace SharpExternal
{
    public class Class1
    {
        public static void Main(ref SharpConsole.GameEvent evt)
        {
            Console.WriteLine("[DLL] " + evt.ToString());
            Console.WriteLine("[DLL] Cancelling an event");
            evt.SetCancelled(true);   
        }

        public static void ShowTestValue()
        {
            SharpConsole.API.Print(SharpConsole.Program.TestValue);
        }
    }
}

What is going on:

  1. The console app creates GameEvent class instance
  2. It injects the ExternalSharp.dll
  3. ExternalSharp.Class1.Main gets called passing previously created GameEvent instance reference to it.
  4. ExternalSharp changes GameEvent's Cancelled state and SharpConsole outputs an instance of a GameEvent that has been successfully modified from ExternalSharp
  5. ExternalSharp's ShowTestValue gets called so it outputs a Console's TestValue field value.

The thing is, I can compile SharpExternal (which uses SharpConsole project as a dependency so it can use its classes. I can also compile SharpConsole and use its DLL file as a dependency, the result is the same) once and move its DLL file on the Desktop so it can't access any files, be recompiled and etc., i.e. in a completely empty folder. BUT I can change Console's TestValue at any moment, even from Console.ReadLine in runtime, then recompile only the Console app and the SharpExternal will output new value when the ShowTestValue method is called. I can add more static properties, methods, etc. before and after TestValue, change the file as I want, but unchanged from the first time SharpExternal.dll file which is still located on the Desktop manages to find TestValue (and other static fields, methods, ...) every time and output the correct value.

I'd like to know how this whole thing works, how ExternalSharp finds the correct TestValue address every time. I suppose it's because of the static modifier, but I haven't found any information beyond the fact that it allows type members to refer to the type itself, and not just to its instance, which I already knew.





Aucun commentaire:

Enregistrer un commentaire