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

后端 未结 1 1251
余生分开走
余生分开走 2020-12-15 14:04

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 S         


        
相关标签:
1条回答
  • 2020-12-15 14:53

    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:\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
    

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

    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
    

    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

    0 讨论(0)
提交回复
热议问题