在.NET 4中捕获AccessViolation异常,好还是不好?

4
文件中有一种类型为SECURITY_IDENTIFIER结构的对象。我需要从这个结构中获取所有者SID。为了做到这一点,我调用了GetSecurityDescriptorOwner WinAPI函数,并创建了System.Security.Principal.SecurityIdentifier(它有一个重载,以IntPtr作为参数)。
问题在于,文件中的这个结构有时会损坏,因此我从GetSecurityDescriptorOwner获得的指针是无效的。它不是IntPtr.Zero,而是无效的,因此当我创建SecurityIdentifier类型的对象时,我会得到AccessViolationException异常,这是无法用.NET 4的简单try-catch捕获的。
我知道有一个属性可以捕获这样的异常,所以我暂时使用了它,但我不喜欢这个解决方案。不建议捕获Corrupted State Exceptions(CSE),但我没有看到其他解决方案。这个WinAPI函数给我返回了无效的指针,我看不到检查它是否有效的方法。有什么想法吗?
更新
WinAPI
BOOL WINAPI GetSecurityDescriptorOwner(
  _In_   PSECURITY_DESCRIPTOR pSecurityDescriptor,
  _Out_  PSID *pOwner,
  _Out_  LPBOOL lpbOwnerDefaulted
);

外部定义

[DllImport("Advapi32.dll")]
static extern bool GetSecurityDescriptorOwner(
   IntPtr pSecurityDescriptor,
   out IntPtr owner,
   out bool defaulted);

更新

private static SecurityIdentifier GetSecurityIdentifier()
{
    // Allocate managed buffer for invalid security descriptor structure (20 bytes)
    int[] b = new int[5] {1, 1, 1, 1, 1};

    // Allocate unmanaged memory for security descriptor 
    IntPtr descriptorPtr = Marshal.AllocHGlobal(b.Length);

    // Copy invalid security descriptor structure to the unmanaged buffer
    Marshal.Copy(b, 0, descriptorPtr, b.Length);

    IntPtr ownerSid;
    bool defaulted;

    if (GetSecurityDescriptorGroup(descriptorPtr, out ownerSid, out defaulted))
    {
        // GetSecurityDescriptorGroup returns true, but `ownerSid` is `1`
        // Marshal.GetLastWin32Error returns 0 here
        return new SecurityIdentifier(ownerSid);
    }

    return null;
}

这段代码有时会从SecurityIdentifier构造函数中抛出损坏状态异常。有什么解决方法吗?


你能给我那个API方法的extern定义吗?也许你应该使用SafeHandle而不是IntPtr。SafeHandle有IsInvalid属性。 - SmartK8
这是一种自定义文件格式,其中存储了安全描述符吗?如果是这样,为什么要花时间处理这个损坏的描述符,而不是首先修复导致其损坏的问题呢? - Damien_The_Unbeliever
这不是自定义的,而是HIVE文件。我正在从SK记录中提取SECURITY_IDENTIFIER。我无法修复它,也不需要这样做。我真的不知道为什么会出现损坏,但在处理大量数据时有时会发生这种情况。 - axe
2个回答

1

你尝试过调用 IsValidSecurityDescriptor 吗?

[DllImport("Advapi32.dll")]
static extern bool IsValidSecurityDescriptor(IntPtr pSecurityDescriptor);


if (IsValidSecurityDescriptor(descriptorPtr) && 
    GetSecurityDescriptorOwner(descriptorPtr, out ownerSid, out defaulted))
{
     return new SecurityIdentifier(ownerSid);
}

不是很确定。我只找到了SeValidSecurityDescriptor,它说“调用SeValidSecurityDescriptor的调用者不能假设返回的TRUE意味着给定的安全描述符一定具有有效内容”。这个看起来更好。谢谢。我会尝试并发布反馈。 - axe
谢谢。看起来这个可以用。我一定会添加这个函数调用,但是我能否删除 [HandleProcessCorruptedStateExceptions] 属性并确保它不会对损坏的安全描述符结构返回 true,以便我不会再次遇到相同的问题?据我从 MSDN 上看到的,它说 function checks the validity of the components that are present in the security descriptor, It does not verify whether certain components are present. 我真的不明白这是什么意思。 - axe
@axe - 很高兴能帮到你。我对内部机制了解不够,无法确定你是否会再次遇到相同的问题。只有试一试才知道... - Joe
谢谢。我认为这是正确的解决方案。我添加了try-catch块以防万一,但根据我执行的测试,IsValidSecurityDescriptor有效。再次感谢。 - axe

0

更新:

试试这个:

    [DllImport("Advapi32.dll")]
    static extern bool GetSecurityDescriptorOwner(
       [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U4)]
       Int32[] securityDescriptor,
       [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1)] 
       out Byte[] owner,
       out Boolean defaulted);


    [DllImport("Advapi32.dll")]
    static extern bool IsValidSecurityDescriptor(
        [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U4)] 
        Int32[] securityDescriptor);

    private static SecurityIdentifier GetSecurityIdentifier()
    {
        // Allocate managed buffer for invalid security descriptor structure (20 bytes)
        Int32[] b = new[] { 1, 1, 1, 1, 1 };

        Byte[] ownerSid;
        bool defaulted;

        if (IsValidSecurityDescriptor(b) &&
            GetSecurityDescriptorOwner(b, out ownerSid, out defaulted))
        {
            return new SecurityIdentifier(ownerSid, 0);
        }

        return null;
    }

    static void Main()
    {
        for (Int32 index = 0; index < 1000; index++)
        {
            SecurityIdentifier identifier = GetSecurityIdentifier();
            String text = identifier == null ? "(none)" : identifier.Value;
            Console.WriteLine(text);
        }

        Console.ReadKey();
    }

我不关心错误类型。那些文件很可能已经损坏了,我只需要获取可能的信息。如果出现错误,我只需要写入日志并忽略它。问题在于GetSecurityDescriptorOwner有时会返回true,但给出无效指针。我能够在本地重现这个问题,请参见更新后的示例。 - axe
你可以尝试使用SafeFileHandle代替SafeHandle,我没有意识到它是输出参数。当然,除非IsValidSecurityDescriptor的解决方案不起作用。 :) - SmartK8
[MarshalAs(UnmanagedType.LPArray)] out Byte[] 并没有太大帮助,因为在这种情况下问题仍然存在,但现在SCE是从GetSecurityDescriptorOwner函数中抛出的。 - axe
好的,我终于有时间在现实生活中研究它了。我想到了这个变体。它完全跳过了指针。 - SmartK8
你试过那个更新的解决方案吗?它完全摒弃了使用IntPtr,因为在我尝试使用IsValidSecurityDescriptor(同时使用IntPtr)时仍然会引发一些异常。 - SmartK8
显示剩余12条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接