Как заменить указатель на указатель на метод в классе моего метода, унаследованного от системного класса?

Уже задал этот вопрос. Как заменить указатель на переопределенный (виртуальный) метод на указатель моего метода? (Release x64 и x86) Спасибо @Machine Learning, решила проблему. Но возникла новая проблема. Если система унаследовала от класса, например "Systems.Windows.Forms", то это изменение не работает. Пример:

using System;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Windows.Forms;

namespace ReplaceHandles
{
    public class Target1 : UserControl
    {
        public void test()
        {
            Console.WriteLine("Target1.test()");
        }
    }

    public class Target2
    {
        public void test()
        {
            Console.WriteLine("Target2.test()");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Injection.Replace();
            var target = new Target1();
            target.test();
            Console.Read();
        }
    }
}

Класс, который заменяет указатели

    public class Injection
    {
        public static void Replace()
        {
            var methodToReplace = typeof(Target1).GetMethod("test", BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
            var methodToInject = typeof(Target2).GetMethod("test", BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
            RuntimeHelpers.PrepareMethod(methodToReplace.MethodHandle);
            RuntimeHelpers.PrepareMethod(methodToInject.MethodHandle);
            if (methodToReplace.IsVirtual) ReplaceVirtualInner(methodToReplace, methodToInject);
            else ReplaceInner(methodToReplace, methodToInject);
        }

Замена виртуальных методов

        static void ReplaceVirtualInner(MethodInfo methodToReplace, MethodInfo methodToInject)
        {
            unsafe
            {
                var methodDesc = (UInt64*)(methodToReplace.MethodHandle.Value.ToPointer());
                var index = (int)(((*methodDesc) >> 32) & 0xFF);
                if (IntPtr.Size == 4)
                {
                    if (methodToReplace.DeclaringType != null)
                    {
                        var classStart = (uint*)methodToReplace.DeclaringType.TypeHandle.Value.ToPointer();
                        classStart += 10;
                        classStart = (uint*)*classStart;
                        var tar = classStart + index;

                        var inj = (uint*)methodToInject.MethodHandle.Value.ToPointer() + 2;
#if DEBUG

                        var injInst = (byte*)*inj;
                        var tarInst = (byte*)*tar;
                        var injSrc = (int*)(injInst + 1);
                        var tarSrc = (int*)(tarInst + 1);
                        *tarSrc = (((int)injInst + 5) + *injSrc) - ((int)tarInst + 5);
#else
                        *tar = *inj;
#endif
                    }
                }
                else
                {
                    if (methodToReplace.DeclaringType != null)
                    {
                        var classStart = (ulong*)methodToReplace.DeclaringType.TypeHandle.Value.ToPointer();
                        classStart += 8;
                        classStart = (ulong*)*classStart;
                        var tar = classStart + index;

                        var inj = (ulong*)methodToInject.MethodHandle.Value.ToPointer() + 1;
#if DEBUG
                        var injInst = (byte*)*inj;
                        var tarInst = (byte*)*tar;
                        var injSrc = (int*)(injInst + 1);
                        var tarSrc = (int*)(tarInst + 1);
                        *tarSrc = (((int)injInst + 5) + *injSrc) - ((int)tarInst + 5);
#else
                        *tar = *inj;
#endif
                    }
                }
            }
        }

и замена не виртуальных методов

        static void ReplaceInner(MethodInfo methodToReplace, MethodInfo methodToInject)
        {
            unsafe
            {
                if (IntPtr.Size == 4)
                {
                    var inj = (int*)methodToInject.MethodHandle.Value.ToPointer() + 2;
                    var tar = (int*)methodToReplace.MethodHandle.Value.ToPointer() + 2;
#if DEBUG
                    var injInst = (byte*)*inj;
                    var tarInst = (byte*)*tar;
                    var injSrc = (int*)(injInst + 1);
                    var tarSrc = (int*)(tarInst + 1);

                    *tarSrc = (((int)injInst + 5) + *injSrc) - ((int)tarInst + 5);
#else
                    *tar = *inj;
#endif
                }
                else
                {
                    ulong* inj = (ulong*)methodToInject.MethodHandle.Value.ToPointer() + 1;
                    ulong* tar = (ulong*)methodToReplace.MethodHandle.Value.ToPointer() + 1;
#if DEBUG
                    var injInst = (byte*)*inj;
                    var tarInst = (byte*)*tar;
                    var injSrc = (int*)(injInst + 1);
                    var tarSrc = (int*)(tarInst + 1);

                    *tarSrc = (((int)injInst + 5) + *injSrc) - ((int)tarInst + 5);
#else
                    *tar = *inj;
#endif
                }
            }
        }
    }

Ответ 1

Когда целевой класс получен из MarshalByRefObject, тогда ReplaceInner (для обычных методов) перестает работать, но ReplaceVirtualInner (для методов overridden) в порядке.

MarshalByRefObject - это базовый класс для объектов, которые обмениваются данными через границы домена приложения путем обмена сообщениями с использованием прокси. Объекты, которые не наследуются от MarshalByRefObject, являются неявно маршалировать по значению. Когда удаленное приложение ссылается на маршал по значению объекта, копия объекта передается через границы домена приложения.

Это может быть частично устранено путем маркировки как virtual метода для замены.

Но когда целевой класс получен из Content, также перестает работать метод ReplaceVirtualInner (для overridden).

К сожалению, Windows.Forms получены из обоих из них, поэтому я не вижу простой работы.

Различные подходы и альтернативы

Возможно, вы захотите рассмотреть другой подход: базовый пример трассировки с PostSharp и аспектно-ориентированным программированием, CodeProject article и doc о трассировке.

Кроме того, другая альтернатива (не знаю, если это возможно для вас) заключается в использовании UserControl WPF вместо форм, и в этом случае замена нормального метода будет работать нормально (после вас 'импортировали необходимые сборки и сделали Main [STAThread])

Окончательное решение с обратной технологией

Хорошо, если вы действительно хотите заставить его работать любой ценой, позвольте продолжить реверсирование цели.

Откройте скомпилированный .exe с CFF Explorer.

Найдите таблицы в .NET Directory > MetaData Streams и разгруппируйте Method Tables. Вы найдете 2 метода с тем же именем и разными RVA, соответствующими 2 классам (TypeDef). Вам просто нужно овервердить цель RVA с помощью метода инъекции RVA и сохранить обратный exe с новым именем.