Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
245 views
in Technique[技术] by (71.8m points)

c# - How to replace the pointer to the overridden (virtual) method in the pointer of my method? (Release x64 and x86)

In question Dynamically replace the contents of a C# method? I found a good response from @ Logman. I do not have standing to ask it in the comments.

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()");
        }
    }
}

Everything works, but does not work the replacement of overridden methods.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Updated Answer

First of all, keep in mind that

Method Address = Method Virtual Address + base address of class that declares this member..

If the method to replace is a virtual overridden method, please use the following.

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

Tested with both platform target x86 and x64: it works!!!.

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

Original Answer

You have to run (from a cmd sell) the exe compiled in Release mode, not in Debug.

I've tried and I confirm it does not throw exceptions in that case.

C:devCalc>C:devCalcinReleaseCalc.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

as you can see, the above runs whithout the following exception

C:devCalc>C:devCalcinDebugCalc.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:devCalcProgram.cs:line 38
   at InjectionTest.Target.test() in C:devCalcProgram.cs:line 31
   at InjectionTest.Program.Main(String[] args) in C:devCalcProgram.cs:line 21

and the reason is explained in this comment

in debug compiler adds some middle man code and to inject your method you need to recalculate address of your method

After the question's edit

Looking at the revised question, I confirm that there is an issue if the Base method is declared as virtual. I'm trying to find a workaround.

workaround 1

My first idea was to replace the new keyworkd instead of the override (so when the Base method is not virtual). That makes it work, so I guess that (when we have a virtual method) the injection shoud happen at the base class level maybe... and the different behaviour must have to do with using callvirt vs call


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...