How to replace the pointer to the overridden (virtual) method in the pointer of my method? (Release x64 and x86)
有问题
动态替换C#方法的内容?
我从@ Logman那里得到了很好的回应。 我没有资格在评论中提出这个问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | 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()"); } } } |
一切正常,但不能替代重写方法。
更新的答案
首先,请记住这一点
Method Address = Method Virtual Address + base address of class that declares this member..
如果要替换的方法是虚拟重写方法,请使用以下方法。
1 2 3 4 5 6 | if (methodToReplace.IsVirtual) { ReplaceVirtualInner(methodToReplace, methodToInject); } else { ReplaceInner(methodToReplace, methodToInject); } |
使用平台目标x86和x64进行测试:它可以工作!!!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | 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; } } } |
原始答案
您必须运行(从
我已经尝试过,并且我确认在这种情况下不会抛出异常。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | C:\dev\Calc>C:\dev\Calc\bin elease\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 |
正如您所看到的,上面的运行没有以下异常
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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 |
这个评论中解释了原因
in debug compiler adds some middle man code and to inject your method
you need to recalculate address of your method
问题编辑后
查看修订后的问题,我确认如果将
解决方法1
我的第一个想法是替换
这使它工作,所以我猜(当我们有一个虚拟方法)注入应该发生在基类级别可能......并且不同的行为必须与使用