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