如何安全地为 32 位和 64 位从 GetTokenInformation() 调用可变长度的结构数组?C#.

本文关键字:结构 数组 调用 位从 安全 何安全 位和 GetTokenInformation | 更新日期: 2023-09-27 17:57:07

我遵循此处提供的 pinvoke 代码,但对将可变长度数组编组为 size=1 然后通过计算偏移量而不是索引到数组中来逐步执行它感到有点害怕。难道没有更好的方法吗?如果没有,我应该如何做到这一点才能使其对 32 位和 64 位安全?

    [StructLayout(LayoutKind.Sequential)]
    public struct SID_AND_ATTRIBUTES
    {
        public IntPtr Sid;
        public uint Attributes;
    }
    [StructLayout(LayoutKind.Sequential)]
    public struct TOKEN_GROUPS
    {
        public int GroupCount;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
        public SID_AND_ATTRIBUTES[] Groups;
    };

public void SomeMethod()
{
    IntPtr tokenInformation;
    // ... 
    string retVal = string.Empty;
    TOKEN_GROUPS groups = (TOKEN_GROUPS)Marshal.PtrToStructure(tokenInformation, typeof(TOKEN_GROUPS));
    int sidAndAttrSize = Marshal.SizeOf(new SID_AND_ATTRIBUTES());
    for (int i = 0; i < groups.GroupCount; i++)
    {
        // *** Scary line here: 
        SID_AND_ATTRIBUTES sidAndAttributes = (SID_AND_ATTRIBUTES)Marshal.PtrToStructure(
              new IntPtr(tokenInformation.ToInt64() + i * sidAndAttrSize + IntPtr.Size), 
              typeof(SID_AND_ATTRIBUTES));
    // ... 
}

我在这里看到了另一种声明数组长度比它可能大得多的方法,但这似乎有其自身的问题。

作为一个附带问题:当我在调试器中逐步完成上述代码时,我无法评估tokenInformation.ToInt64()ToInt32()。我得到一个 ArgumentOutOfRangeException。但是代码行执行得很好!?这是怎么回事?

如何安全地为 32 位和 64 位从 GetTokenInformation() 调用可变长度的结构数组?C#.

与其猜测偏移量是多少,不如使用 Marshal.OffsetOf(typeof(TOKEN_GROUPS), "Groups") 来获得数组开头的正确偏移量。

我认为

它看起来还不错——无论如何,就像在无人管理的土地上闲逛一样好。

但是,我想知道为什么开头是tokenInformation.ToInt64() + IntPtr.Size而不是tokenInformation.ToInt64() + 4(因为 GroupCount 字段类型是 int 而不是 IntPtr)。这是为了包装/对齐结构还是只是一些可疑的东西?我在这里不知道。

使用 tokenInformation.ToInt64() 很重要,因为在 64 位计算机上,如果 IntPtr 值大于 int 可以存储的值,则会爆炸 (OverflowException)。但是,CLR 将在两种体系结构上处理很长时间,并且它不会更改从 IntPtr 中提取的实际值(从而放回new IntPtr(...))。

将此(未经测试的)函数想象为方便包装器:

// unpacks an array of structures from unmanaged memory
// arr.Length is the number of items to unpack. don't overrun.
void PtrToStructureArray<T>(T[] arr, IntPtr start, int stride) {
   long ptr = start.ToInt64();
   for (int i = 0; i < arr.Length; i++, ptr += stride) {
       arr[i] = (T)Marshal.PtrToStructure(new IntPtr(ptr), typeof(T));
   }
}
var attributes = new SID_AND_ATTRIBUTES[groups.GroupCount];
PtrToStructureArray(attributes, new IntPtr(tokenInformation.ToInt64() + IntPtr.Size), sidAndAttrSize);

快乐编码。