【发布时间】:2018-01-08 23:47:50
【问题描述】:
我目前正在尝试通过用一些 IL 替换属性获取实现来交换它。 我用这个问题作为参考:How to replace a pointer to a pointer to a method in a class of my method inherited from the system class?
我唯一的区别是我的方法是通过 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);
这会为生成的类型添加一个新函数BeforeGet{PropertyName}
生成新类型后,我将其实例化以确保内存地址存在:
dynamic fakeType = Activator.CreateInstance(type);
我从现有类中检索propertyInfo GetMethod,以及新创建的BeforeGet{PropertyName} fakeType 类Type。
在此函数中使用了两个 MethodInfo:
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
}
}
运行此代码后,我将在我的程序中执行以下代码:
LoadedTag.Item.ItemID; 其中 LoadedTag 是应该获得 Item Getter 的新实现的类,但我得到一个空引用异常,因为该函数尚未被替换。
但是,如果我在即时窗口中执行此代码,则确实设置了 ItemID 并调用了拦截函数。
我认为问题是由于垃圾收集器删除了 fakeType ,它保存了在方法交换期间使用的函数的实际指针。 如果是这样我应该如何解决这个问题?
提前谢谢你!
如果需要请咨询完整代码,我会上传到 Github。
【问题讨论】:
-
看github.com/tonerdo/pose。有代码可以做到这一点
-
@zaitsman 谢谢!明天我看看能不能实现!
-
尝试分析代码,但我认为它没有达到我想要达到的效果。 :(
-
确实值得发布完整的代码,因为在运行时交换方法 IL 等有意义的任务中,很多事情都可能出错
-
既然您提到它在测试环境中工作...您确定在您的生产程序中它由于时间问题而无法正常工作...您是否在组装负载上调用替换事件? msdn.microsoft.com/en-us/library/…