为什么SafeHandle.DangerousGetHandle()"危险"?

Ian*_*Ian 6 c# pinvoke handles .net-3.5

这是我第一次使用它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);
Run Code Online (Sandbox Code Playgroud)

这个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;
            }
}
Run Code Online (Sandbox Code Playgroud)

问题:

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

Dan*_*ose 5

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

private static IntPtr GetRegistryKeyHandle(RegistryKey rKey)
{
    return rKey.Handle.DangerousGetHandle();
}
Run Code Online (Sandbox Code Playgroud)

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

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


Han*_*ant 4

你所做的事情实际上是危险的。当您使用 IntPtr 时,您使用的RegistryKey 对象可能会被垃圾收集并最终确定。这使得句柄值无效,从而使您的代码随机失败。好吧,随机故障并不完全危险,但如果您实际上长时间握住手柄,它确实为手柄回收攻击打开了大门。随机故障模式应该足以激励您采取措施。

让你的 pinvoke 声明看起来像这样:

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

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