【问题标题】:Why is SafeHandle.DangerousGetHandle() "Dangerous"?为什么 SafeHandle.DangerousGetHandle() “危险”?
【发布时间】:2012-01-13 21:05:15
【问题描述】:

这是我第一次使用SafeHandle

我需要调用这个需要 UIntPtr 的 P/Invoke 方法。

    [DllImport("advapi32.dll", CharSet = CharSet.Auto)]
    public static extern int RegOpenKeyEx(
      UIntPtr hKey,
      string subKey,
      int ulOptions,
      int samDesired,
      out UIntPtr hkResult);

此 UIntPtr 将派生自 .NET 的 RegistryKey 类。我将使用上面的方法将 RegistryKey 类转换为 IntPtr,这样我就可以使用上面的 P/Invoke:

        private static IntPtr GetRegistryKeyHandle(RegistryKey rKey)
        {
            //Get the type of the RegistryKey
            Type registryKeyType = typeof(RegistryKey);

            //Get the FieldInfo of the 'hkey' member of RegistryKey
            System.Reflection.FieldInfo fieldInfo =
                registryKeyType.GetField("hkey", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);

            //Get the handle held by hkey
            if (fieldInfo != null)
            {
                SafeHandle handle = (SafeHandle)fieldInfo.GetValue(rKey);

                //Get the unsafe handle
                IntPtr dangerousHandle = handle.DangerousGetHandle();                
                return dangerousHandle;
            }
}

问题:

  1. 有没有更好的方法来编写这个而不使用“不安全”句柄?
  2. 为什么不安全的手柄很危险?

【问题讨论】:

    标签: c# .net-3.5 pinvoke handles


    【解决方案1】:

    RegistryKey 有一个句柄属性。所以你可以使用

    private static IntPtr GetRegistryKeyHandle(RegistryKey rKey)
    {
        return rKey.Handle.DangerousGetHandle();
    }
    

    这有潜在的危险,因为当你使用它时,你得到的指针可能不再有效。引用MSDN

    使用 DangerousGetHandle 方法可能会带来安全风险,因为如果句柄已被 SetHandleAsInvalid 标记为无效,DangerousGetHandle 仍会返回原始的、可能过时的句柄值。返回的句柄也可以在任何时候回收。充其量,这意味着手柄可能会突然停止工作。在最坏的情况下,如果句柄或句柄所代表的资源暴露给不受信任的代码,这可能会导致对重用或返回的句柄的回收安全攻击。例如,不受信任的调用者可以查询刚刚返回的句柄上的数据并接收完全不相关资源的信息。有关安全使用 DangerousGetHandle 方法的更多信息,请参阅 DangerousAddRef 和 DangerousRelease 方法。

    【讨论】:

    • 我忘了提到我的代码的目的是模仿 NETFX4 的 64 位注册表支持。我们只使用 NETFX 3.5,所以 RegistryKey 类中没有 Handle 成员。
    【解决方案2】:

    你所做的实际上是危险的。您使用的 RegistryKey 对象可以在您使用 IntPtr 时进行垃圾收集和最终确定。这使句柄值无效,从而使您的代码随机失败。好吧,好吧,随机故障并不是很危险,但如果您实际上长时间握住手柄,它确实打开了手柄回收攻击的大门。随机故障模式应该足以激发您对此采取行动。

    让您的 pinvoke 声明如下所示:

    [DllImport("advapi32.dll", CharSet=CharSet.Auto)]
    internal static extern int RegOpenKeyEx(SafeRegistryHandle key, string subkey, 
        int options, int sam, out SafeRegistryHandle result);
    

    因此您可以始终如一地使用安全句柄包装类。相应地调整反射代码。

    【讨论】:

    • 我忘了提到我的代码的目的是模仿 NETFX4 的 64 位注册表支持。我们只使用 NETFX 3.5,因此没有可用的 SafeRegistryHandle。
    • 只需将其设为 SafeHandleZeroOrMinusOneIsInvalid,即 SafeRegistryHandle 的基类。或者,如果您讨厌输入名称(谁不会),则只需简单的 SafeHandle。
    猜你喜欢
    • 2015-03-08
    • 1970-01-01
    • 1970-01-01
    • 2021-01-01
    • 2012-08-30
    • 2011-11-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多