C# 从VTable调用结构中的非托管函数

C# 从VTable调用结构中的非托管函数,c#,.net,unmanaged,vtable,C#,.net,Unmanaged,Vtable,是否可以从C#调用函数到结构中的非托管函数(通过VTable) 例如,我正在进程中挂接一个应用程序,并且正在为(应用程序的)每个类重新创建结构 然后,我通常会: var * data = (SomeStruct*)(Address); 我希望通过以下任一方式从结构的VTable调用函数 Invoke<delegate>(data->VTable[0x3C])(delegateArguments) Invoke(数据->VTable[0x3C])(delegateArgume

是否可以从C#调用函数到结构中的非托管函数(通过VTable)

例如,我正在进程中挂接一个应用程序,并且正在为(应用程序的)每个类重新创建结构

然后,我通常会:

var * data = (SomeStruct*)(Address);
我希望通过以下任一方式从结构的VTable调用函数

Invoke<delegate>(data->VTable[0x3C])(delegateArguments)
Invoke(数据->VTable[0x3C])(delegateArguments)

var eax=InvokeVTable[0x3C])(arg1,arg2)
此外,这能有效地完成吗(因为这些vtable函数可以被多次调用)

也许通过反射发射


据我所知,编组必须在每次调用
Invoke
func.

时创建委托函数,因为虚拟方法表包含指向函数的指针,假设您知道偏移量(看起来您知道),您可以通过在上调用来获取中的指针值,如下所示:

IntPtr ptr = Marshal.ReadIntPtr(data.VTable, 0x3C);
// Assuming a signature of f(int, int) returning int
Func<int, int, int> func = (Func<int, int, int>)
    Marshal.GetDelegateForFunctionPointer(ptr, typeof(Func<int, int, int>));
然后,您可以在上调用以获取适当类型的委托,如下所示:

IntPtr ptr = Marshal.ReadIntPtr(data.VTable, 0x3C);
// Assuming a signature of f(int, int) returning int
Func<int, int, int> func = (Func<int, int, int>)
    Marshal.GetDelegateForFunctionPointer(ptr, typeof(Func<int, int, int>));
//假设f(int,int)的签名返回int
Func Func=(Func)
GetDelegateForFunctionPointer(ptr,typeof(Func));

然后,您可以根据需要调用代理。

好吧,我找到了一个可能的解决方案:

我创建了一个通用的Invoke方法,该方法创建并缓存所有委托以供将来使用 也

公共作废选择(uint目标)
{
已修复(无效*p此=&此)
{
Generic.Invoke(this.VTable[0xC0],CallingConvention.ThisCall)
((uint)pThis,target);
}
}
[字段偏移量(0x00)]
公共uint*VTable;
缓存:

  public static T Invoke<T>(uint addr, CallingConvention conv) where T : class
        {
          var type = typeof(T);
            if (!cache.Contains(type))
                cache.Set<T>(type, NativeHelper.GetDelegateForFunctionPointer<T>(addr, conv));

            return cache.Get<T>(type);
        }
publicstatict调用(uint-addr,CallingConvention-conv),其中T:class
{
var类型=类型(T);
如果(!cache.Contains(类型))
Set(type,NativeHelper.GetDelegateForFunctionPointer(addr,conv));
返回cache.Get(类型);
}
以及创建函数的函数(并适用于泛型函数/操作)

public static T GetDelegateForFunctionPointer(uint ptr,CallingConvention conv)
T:在哪里上课
{
var delegateType=typeof(T);
var method=delegateType.GetMethod(“调用”);
var returnType=方法.returnType;
变量参数类型=
方法
.GetParameters()
.选择((x)=>x.ParameterType)
.ToArray();
var invoke=new DynamicMethod(“invoke”,returnType,paramTypes,typeof(Delegate));
var il=invoke.GetILGenerator();
for(int i=0;i
指向
VTable
字段的指针是否正确?这是指向指针的指针,在本例中,它似乎是多余的。@casperOne我认为如果您想将其视为指针,那么可以使用
data.VTable[0x3C]
而不是
ReadIntPtr()
正如您所建议的那样。@svick只是想确保它被正确地带过来。是的,我似乎得出了相同的结论(但我缓存了函数,并且使用了通用的GetDelegateForFunctionPointer)。这样,调用func将不会有太大的性能loss@Dan没有理由不缓存它,在加载它的进程的整个生命周期中,该地址根本不会移动。是的,但是casperOne,因为我实际上没有存储对函数本身的引用,我需要一种缓存的方法,这样我就不会一直调用GetDelegateForFunctionPointer。看看我对“Select”函数的回答,我将其作为虚拟Select的包装器function@Dan我不太明白。缓存策略与这个问题几乎没有关系;一旦获得代理,如何缓存它(如果确实需要)取决于您。最重要的部分是首先从vtable获取函数。
  public static T Invoke<T>(uint addr, CallingConvention conv) where T : class
        {
          var type = typeof(T);
            if (!cache.Contains(type))
                cache.Set<T>(type, NativeHelper.GetDelegateForFunctionPointer<T>(addr, conv));

            return cache.Get<T>(type);
        }
public static T GetDelegateForFunctionPointer<T>(uint ptr, CallingConvention conv)
        where T : class
        {

            var delegateType = typeof(T);
            var method = delegateType.GetMethod("Invoke");
            var returnType = method.ReturnType;
            var paramTypes =
                method
                .GetParameters()
                .Select((x) => x.ParameterType)
                .ToArray();
            var invoke = new DynamicMethod("Invoke", returnType, paramTypes, typeof(Delegate));
            var il = invoke.GetILGenerator();
            for (int i = 0; i < paramTypes.Length; i++)
                il.Emit(OpCodes.Ldarg, i);
                il.Emit(OpCodes.Ldc_I4, ptr);

            il.EmitCalli(OpCodes.Calli, conv, returnType, paramTypes);
            il.Emit(OpCodes.Ret);
            return invoke.CreateDelegate(delegateType) as T;
        }