首页 > 解决方案 > 从 MethodInfo 获取具有私有(不可访问)类型的 Func<>

问题描述

考虑以下代码:

private class ThirdPartyClass {
    private class InternalPrivateClass { }
    private static InternalPrivateClass Init() { 
        return new InternalPrivateClass(); 
    }
    private static int DoSomething(InternalPrivateClass t1) { 
        return 0; 
    }
}

假设我无法控制ThirdPartyClass并以任何方式对其进行逆向工程是成本高昂的。我希望能够在DoSomething没有反射性能开销的情况下快速调用。所以我到目前为止:

Type t = typeof(ThirdPartyClass);
object context = t.GetMethod("Init", BindingFlags.NonPublic | BindingFlags.Static).Invoke(null, null);
MethodInfo mi = t.GetMethod("DoSomething", BindingFlags.NonPublic | BindingFlags.Static);
// ...now what?

解决了!(https://stackoverflow.com/a/52652398/2692950

ᅟ</p>

为什么我需要这个的例子:

看看这个 MD4 哈希实现:https ://stackoverflow.com/a/46821287/2692950 (缩短版:https ://stackoverflow.com/a/52640221/2692950 )

这很好用,除了每个散列操作都通过反射调用方法!

在这个例子中,我们通过反射调用一个不可访问的私有函数System.Security.Cryptography.Utils.HashEnd(SafeProvHandle h),将 aSafeHandle作为参数传递。这是有效的,因为SafeProvHandle继承自SafeHandle. SafeProvHandle不能直接引用,因为它是私有的,因此似乎无法直接调用此函数。

(我最感兴趣的是问题顶部的一般情况是否存在解决方案,但如果有人知道更好的方法来实现直接通过 获取加密服务提供商ALG_ID,我会全神贯注 :)

标签: c#.netreflectiondelegatesfunc

解决方案


这有点棘手,但可以使用 System.Reflection.Emit 命名空间中的 DynamicMethod 来完成。它允许我们在运行时发出调用这些方法的 IL,而无需在我们的代码中引用有效的、可见的标识符。此类能够使用的技巧之一是跳过我们通过构造函数中的参数设置的各种安全性和可见性检查。从示例中,我们需要替换Utils类。这是使用 DynamicMethod 创建委托的重写:

internal static class DelegateUtils
{
    private static readonly Type UtilsType = Type.GetType("System.Security.Cryptography.Utils");
    private static readonly Func<int, SafeHandle> CreateHashDel;
    private static readonly Action<SafeHandle, byte[], int, int> HashDataDel;
    private static readonly Func<SafeHandle, byte[]> EndHashDel;

    static DelegateUtils()
    {
        CreateHashDel = CreateCreateHashDelegate();
        HashDataDel = CreateHashDataDelegate();
        EndHashDel = CreateEndHashDelegate();
    }

    internal static SafeHandle CreateHash(int algid)
    {
        return CreateHashDel(algid);
    }

    internal static void HashData(SafeHandle h, byte[] data, int ibStart, int cbSize)
    {
        HashDataDel(h, data, ibStart, cbSize);
    }

    internal static byte[] EndHash(SafeHandle h)
    {
        return EndHashDel(h);
    }

    private static Func<int, SafeHandle> CreateCreateHashDelegate()
    {
        var prop = UtilsType.GetProperty("StaticProvHandle", BindingFlags.NonPublic | BindingFlags.Static);

        var createHashMethod = UtilsType.GetMethods(BindingFlags.NonPublic | BindingFlags.Static)
            .FirstOrDefault(mi => mi.Name == "CreateHash" && mi.GetParameters().Length == 2);

        var createHashDyn = new DynamicMethod("CreateHashDyn", typeof(SafeHandle), new[] { typeof(int) }, typeof(object), true);
        var ilGen = createHashDyn.GetILGenerator();
        ilGen.Emit(OpCodes.Call, prop.GetGetMethod(true));
        ilGen.Emit(OpCodes.Ldarg_0);
        ilGen.Emit(OpCodes.Call, createHashMethod);
        ilGen.Emit(OpCodes.Ret);

        var del = (Func<int, SafeHandle>)createHashDyn.CreateDelegate(typeof(Func<int, SafeHandle>));
        return del;
    }

    private static Action<SafeHandle, byte[], int, int> CreateHashDataDelegate()
    {
        var hashDataMethod = UtilsType.GetMethods(BindingFlags.NonPublic | BindingFlags.Static)
            .FirstOrDefault(mi => mi.Name == "HashData" && mi.GetParameters().Length == 4);
        var hashDataDyn = new DynamicMethod("HashDataDyn", typeof(void), new[] { typeof(SafeHandle), typeof(byte[]), typeof(int), typeof(int) }, typeof(object), true);
        var ilGen = hashDataDyn.GetILGenerator();
        ilGen.Emit(OpCodes.Ldarg_0);
        ilGen.Emit(OpCodes.Ldarg_1);
        ilGen.Emit(OpCodes.Ldarg_2);
        ilGen.Emit(OpCodes.Ldarg_3);
        ilGen.Emit(OpCodes.Call, hashDataMethod);
        ilGen.Emit(OpCodes.Ret);

        var del = (Action<SafeHandle, byte[], int, int>)hashDataDyn.CreateDelegate(typeof(Action<SafeHandle, byte[], int, int>));
        return del;
    }

    private static Func<SafeHandle, byte[]> CreateEndHashDelegate()
    {
        var endHashMethod = UtilsType.GetMethods(BindingFlags.NonPublic | BindingFlags.Static)
            .FirstOrDefault(mi => mi.Name == "EndHash" && mi.GetParameters().Length == 1);
        var endHashDyn = new DynamicMethod("EndHashDyn", typeof(byte[]), new[] { typeof(SafeHandle) }, typeof(object), true);
        var ilGen = endHashDyn.GetILGenerator();
        ilGen.Emit(OpCodes.Ldarg_0);
        ilGen.Emit(OpCodes.Call, endHashMethod);
        ilGen.Emit(OpCodes.Ret);

        var del = (Func<SafeHandle, byte[]>)endHashDyn.CreateDelegate(typeof(Func<SafeHandle, byte[]>));
        return del;
    }
}

接下来的问题是这能带来多大的速度优势。根据您要散列的数据的大小,它会给您带来 2-4 倍的增长。越小越能提高速度,这可能是因为我们在那里进行计算的时间更少,而在方法调用之间花费的时间更多。以下是快速基准测试的结果:

BenchmarkDotNet=v0.11.1, OS=Windows 10.0.17134.286 (1803/April2018Update/Redstone4)
Intel Core i5-4200U CPU 1.60GHz (Haswell), 1 CPU, 4 logic and 2 physical cores
频率=2240904 Hz, Resolution=446.2485 ns, Timer=TSC
[主机]:.NET Framework 4.7.2 (CLR 4.0.30319.42000),32 位 LegacyJIT-v4.7.3163.0
DefaultJob:.NET Framework 4.7.2 (CLR 4.0.30319.42000),32 位 LegacyJIT-v4.7.3163。 0

方法 | N | 平均值 | 错误 | 标准差 |
----------- |------ |----------:|----------:|------- ---:|
反射 | 1000 | 16.239 我们 | 0.1252 我们 | 0.1046 我们 |
代表 | 1000 | 4.329 我们 | 0.0245 我们 | 0.0230 我们 |
反射 | 10000 | 31.832 我们 | 0.1599 我们 | 0.1335 我们 |
代表 | 10000 | 19.703 我们 | 0.1005 我们 | 0.0940 我们 |

请注意,N 是被散列的字节数。这是使用 OPs 链接中提供的所有代码来创建 MD4 实现,然后在其上调用 ComputeHash。

基准代码:

public class MD4DelegateVsReflection
{
    private MD4 md4 = MD4.Create();
    private byte[] data;

    [Params(1000, 10000)]
    public int N;

    public void SetupData()
    {
        data = new byte[N];
        new Random(42).NextBytes(data);
    }

    [GlobalSetup(Target = nameof(Reflection))]
    public void ReflectionSetup()
    {
        MD4.SetReflectionUtils();
        SetupData();
    }

    [GlobalSetup(Target = nameof(Delegate))]
    public void DelegateSetup()
    {
        MD4.SetDelegateUtils();
        SetupData();
    }

    [Benchmark]
    public byte[] Reflection() => md4.ComputeHash(data);

    [Benchmark]
    public byte[] Delegate() => md4.ComputeHash(data);
}

推荐阅读