如何在我的方法的指针中替换指向重写(虚拟)方法的指针?(x64和x86版本)

本文关键字:指针 方法 版本 虚拟 x64 x86 我的 替换 重写 | 更新日期: 2023-09-27 18:00:15

有问题动态替换C#方法的内容?我发现@Logman的回复很好。我没有资格在评论中提出这个问题。

using System;
using System.Reflection;
using System.Runtime.CompilerServices;
namespace ReplaceHandles
{
    class Program
    {
        static void Main(string[] args)
        {
            Injection.replace();
            Target target = new Target();
            target.test();
            Console.Read();
        }
    }
    public class Injection
    {
        public static void replace()
        {
            MethodInfo methodToReplace = typeof(Target).GetMethod("test", BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
            MethodInfo methodToInject = typeof(Target2).GetMethod("test", BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
            RuntimeHelpers.PrepareMethod(methodToReplace.MethodHandle);
            RuntimeHelpers.PrepareMethod(methodToInject.MethodHandle);
            ReplaceInner(methodToReplace, methodToInject);
        }
        static void ReplaceInner(MethodInfo methodToReplace, MethodInfo methodToInject)
        {
            unsafe
            {
                if (IntPtr.Size == 4)
                {
                    int* inj = (int*)methodToInject.MethodHandle.Value.ToPointer() + 2;
                    int* tar = (int*)methodToReplace.MethodHandle.Value.ToPointer() + 2;
                    *tar = *inj;
                }
                else
                {
                    ulong* inj = (ulong*)methodToInject.MethodHandle.Value.ToPointer() + 1;
                    ulong* tar = (ulong*)methodToReplace.MethodHandle.Value.ToPointer() + 1;
                    *tar = *inj;
                }
            }
        }
    }

    public class Base
    {
        public virtual void test()
        {
        }
    }
    public class Target : Base
    {
        public override void test()
        {
            Console.WriteLine("Target.test()");
        }
        public void test3()
        {
            Console.WriteLine("Target.test3()");
        }
    }
    public class Target2
    {
        public void test()
        {
            Console.WriteLine("Target.test2()");
        }
    }
}

一切都有效,但不适用于替换重写的方法。

如何在我的方法的指针中替换指向重写(虚拟)方法的指针?(x64和x86版本)

更新的答案

首先,请记住

方法地址=方法虚拟地址+声明此成员的类的基地址。。

如果要替换的方法是虚拟重写的方法,请使用以下方法。

if (methodToReplace.IsVirtual)
{
    ReplaceVirtualInner(methodToReplace, methodToInject);
} else {
    ReplaceInner(methodToReplace, methodToInject);
}

使用平台目标x86x64进行测试:它工作!!!。

static void ReplaceVirtualInner(MethodInfo methodToReplace, MethodInfo methodToInject)
{
    unsafe
    {
        UInt64* methodDesc = (UInt64*)(methodToReplace.MethodHandle.Value.ToPointer());
        int index = (int)(((*methodDesc) >> 32) & 0xFF);
        if (IntPtr.Size == 4)
        {
            uint* classStart = (uint*)methodToReplace.DeclaringType.TypeHandle.Value.ToPointer();
            classStart += 10;
            classStart = (uint*)*classStart;
            uint* tar = classStart + index;
            uint* inj = (uint*)methodToInject.MethodHandle.Value.ToPointer() + 2;
            //int* tar = (int*)methodToReplace.MethodHandle.Value.ToPointer() + 2;
            *tar = *inj;
        }
        else
        {
            ulong* classStart = (ulong*)methodToReplace.DeclaringType.TypeHandle.Value.ToPointer();
            classStart += 8;
            classStart = (ulong*)*classStart;
            ulong* tar = classStart + index;
            ulong* inj = (ulong*)methodToInject.MethodHandle.Value.ToPointer() + 1;
            //ulong* tar = (ulong*)methodToReplace.MethodHandle.Value.ToPointer() + 1;
            *tar = *inj;
        }
    }
}

原始答案

您必须运行(从cmd销售)以Release模式而不是以Debug模式编译的exe

我已经试过了,我确认在这种情况下不会抛出异常。

C:'dev'Calc>C:'dev'Calc'bin'Release'Calc.exe
Target.targetMethod1()
Target.targetMethod2()
Not injected 2
Target.targetMethod3(Test)
Target.targetMethod4()
Version x64 Release

Version x64 Release

Version x64 Release

Version x64 Release
Injection.injectionMethod1
Injection.injectionMethod2
Injected 2
Injection.injectionMethod3 Test

正如您所看到的,上面的运行没有以下异常

C:'dev'Calc>C:'dev'Calc'bin'Debug'Calc.exe
Target.targetMethod1()
Target.targetMethod2()
Not injected 2
Target.targetMethod3(Test)
Target.targetMethod4()
Version x64 Debug

Version x64 Debug

Version x64 Debug

Version x64 Debug

Unhandled Exception: System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
   at InjectionTest.Target.targetMethod1() in C:'dev'Calc'Program.cs:line 38
   at InjectionTest.Target.test() in C:'dev'Calc'Program.cs:line 31
   at InjectionTest.Program.Main(String[] args) in C:'dev'Calc'Program.cs:line 21

原因在本评论中进行了解释

在调试编译器中添加一些中间人代码并注入您的方法你需要重新计算你的方法的地址

问题编辑后

查看修改后的问题,我确认如果Base方法声明为virtual,则会出现问题。我正在想办法。

解决方法1

我的第一个想法是替换new keyworkd而不是override(所以当Base方法不是virtual时)。这使得它工作,所以我想(当我们有一个虚拟方法时)注入应该在基类级别发生。。。不同的行为必须与使用CCD_ 10和CCD_