13

I'm currently trying to swap a property get implementation by replacing it with a bit of IL. I was using this question as reference: How to replace a pointer to a pointer to a method in a class of my method inherited from the system class?

The only difference I have is that my method is declared via a MethodBuilder:

MethodBuilder propertyGetBuilder = builder.DefineMethod
(
    dynamicFunctionName,
    MethodAttributes.Public,
    propertyInfo.PropertyType,
    Type.EmptyTypes
);

ILGenerator propertyGetIlGenerator = propertyGetBuilder.GetILGenerator();

propertyGetIlGenerator.Emit(OpCodes.Ldarg_0);
propertyGetIlGenerator.Emit(OpCodes.Ldstr, propertyInfo.Name);
propertyGetIlGenerator.Emit(OpCodes.Ldstr, relationKeyField.Name);
propertyGetIlGenerator.Emit(OpCodes.Ldstr, relationAttribute.RelationColumn);
propertyGetIlGenerator.Emit(OpCodes.Call, loadRelationMethod);

propertyGetIlGenerator.Emit(OpCodes.Ret);

This adds a new function to a generated type called BeforeGet{PropertyName}

After generating the new type I instantiate it to make sure the memory address exists: dynamic fakeType = Activator.CreateInstance(type);

I retrieve the propertyInfo GetMethod from the existing class, and the newly created BeforeGet{PropertyName} fakeType class Type.

After that both MethodInfo's are used in this function:

RuntimeHelpers.PrepareMethod(methodA.MethodHandle);
RuntimeHelpers.PrepareMethod(methodB.MethodHandle);

unsafe
{
    if (IntPtr.Size == 4)
    {
        int* inj = (int*)methodA.MethodHandle.Value.ToPointer() + 2;
        int* tar = (int*)methodB.MethodHandle.Value.ToPointer() + 2;
#if DEBUG
        Console.WriteLine("\nVersion x86 Debug?\n");

        byte* injInst = (byte*)*inj;
        byte* tarInst = (byte*)*tar;

        int* injSrc = (int*)(injInst + 1);
        int* tarSrc = (int*)(tarInst + 1);

        *tarSrc = (((int)injInst + 5) + *injSrc) - ((int)tarInst + 5);
#else
        *tar = *inj;
#endif
    }
    else
    {
        long* inj = (long*)methodA.MethodHandle.Value.ToPointer() + 1;
        long* tar = (long*)methodB.MethodHandle.Value.ToPointer() + 1;
#if DEBUG
        Console.WriteLine("\nVersion x64 Debug\n");
        byte* injInst = (byte*)*inj;
        byte* tarInst = (byte*)*tar;

        int* injSrc = (int*)(injInst + 1);
        int* tarSrc = (int*)(tarInst + 1);

        *tarSrc = (((int)injInst + 5) + *injSrc) - ((int)tarInst + 5);
#else
        *tar = *inj;
#endif
    }
}

After running this code I'm executing the following code in my Program: LoadedTag.Item.ItemID; Where LoadedTag is the class which should have gotten a new implementation of the Item Getter but instead I get a null reference exception because the function hasn't been replaced.

However if I execute this code in the immediate window the ItemID is indeed set and the intercepting function is called.

I think the issue is due to the garbage collector removing the fakeType which holds the actual pointers to the functions used during the method swapping. If so how should I resolve this?

Thank you in advance!

If required please inquire about the full code and I'll upload it to Github.

CSDev
  • 3,177
  • 6
  • 19
  • 37
Stijn Bernards
  • 1,091
  • 11
  • 29
  • 2
    Look at https://github.com/tonerdo/pose. There is code that does that there – zaitsman Jan 08 '18 at 23:56
  • @zaitsman Thanks! I'll take a look tomorrow to see if I can implement it! – Stijn Bernards Jan 08 '18 at 23:56
  • Tried analyzing the code, but I don't think it does what I'm trying to achieve. :( – Stijn Bernards Jan 09 '18 at 19:07
  • Worth posting full code indeed, because many things can go wrong in such sensetive task as swapping method IL at runtime – Evk Jan 11 '18 at 14:53
  • since you mentioned it works in a test environment...are you sure in your production program that its not working due to something straightforward as a timing issue...are you calling the replacement on the assemblyload event? https://msdn.microsoft.com/en-us/library/system.appdomain.assemblyload(v=vs.110).aspx – Ctznkane525 Jan 12 '18 at 09:43
  • This is exactly what I ran into today, thanks for posting this question @StijnBernards – Henry van Megen Nov 01 '18 at 13:54

1 Answers1

-1

it is not clear from your code but: is the "fakeType" in the same scope with where you exchange the pointers?

If you changed the scope, and the fakeType is not available indeed the garbage collector might invalidate the memory space.

So I would try to move the "swapping code" immediately after you generate the faketype to test if that is the problem.

Cheers, hope it helps