如何在我的方法的指针中替换指向重写的(虚拟)方法的指针? (发布x64和x86) [英] How to replace the pointer to the overridden (virtual) method in the pointer of my method? (Release x64 and x86)
问题描述
有问题的
动态替换C#的内容方法?
我从@ Logman找到了很好的回应。我没有资格在评论中提问。
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()");
}
}
}
一切正常,但无效
推荐答案
更新的答案
第一首先,请记住
Updated Answer
First of all, keep in mind that
方法地址=方法虚拟地址+该类的基地址声明该成员。
如果替换方法是虚拟替代方法,请使用以下内容。
If the method to replace is a virtual overridden method, please use the following.
if (methodToReplace.IsVirtual)
{
ReplaceVirtualInner(methodToReplace, methodToInject);
} else {
ReplaceInner(methodToReplace, methodToInject);
}
已通过两者平台目标 x86进行了测试和 x64 :它有效 !!!
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;
}
}
}
原始答案
您必须运行(从 cmd
出售)编译后的 exe
在发布
模式下,而不在 Debug
下。
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
和原因在此注释
添加一些中间人代码并注入方法
,您需要重新计算方法的地址
in debug compiler adds some middle man code and to inject your method you need to recalculate address of your method
问题编辑后
看着修订后的问题,我确认如果 Base
方法声明为<$ c,则存在问题$ c>虚拟。我正在尝试找到一种解决方法。
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.
我的第一个想法是替换 new
而不是 override
(因此,当Base方法不是 virtual
)。
这使得它变得有效,所以我想(当我们有一个虚拟方法时)注入应该发生在基类级别上……并且不同的行为必须与使用 callvirt
与 call
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
这篇关于如何在我的方法的指针中替换指向重写的(虚拟)方法的指针? (发布x64和x86)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!