开发者

Setting size of TOKEN_PRIVILEGES.LUID_AND_ATTRIBUTES array returned by GetTokenInformation

I'm trying to retrieve the privileges and their current state associated with a token in C# but i can't figure out how to adjust the size of the LUID_AND_ATTRIBUTES array that is returned to fit the actual number of elements.

From MSDN

When MarshalAsAttribute.Value is set to ByValArray, the SizeConst must be set to indicate the number of elements in the array.

I was able to watch the TOKEN_PRIVILEGES.PrivilegeCount property after the call to GetTokenInformation and see that the token I was working with had 24 of the 35 privileges listed on the Privilege Constants referenc开发者_Go百科e page. Changing SizeConst = 24 would then give me the ability to see all of them instead of just the first one (I had initially set SizeConst = 1 following a usage example from PInvoke)

Is there a way to specify the depth of the incoming array as it is being created or will I need to know how many privileges there are going to be before writing the code?

Code Snippet

[DllImport("advapi32.dll", SetLastError = true)]
protected static extern bool GetTokenInformation(IntPtr TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass, IntPtr TokenInformation, int TokenInformationLength, ref int ReturnLength);

[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)][return: MarshalAs(UnmanagedType.Bool)]
protected static extern bool LookupPrivilegeName(string lpSystemName, IntPtr lpLuid,System.Text.StringBuilder lpName, ref int cchName);

protected struct TOKEN_PRIVILEGES {
  public UInt32 PrivilegeCount;
  [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
  public LUID_AND_ATTRIBUTES[] Privileges;
}//TOKEN_PRIVILEGES

[StructLayout(LayoutKind.Sequential)]
protected struct LUID_AND_ATTRIBUTES {
  public LUID Luid;
  public UInt32 Attributes;
}//LUID_AND_ATTRIBUTES

[StructLayout(LayoutKind.Sequential)]
protected struct LUID {
  public uint LowPart;
  public int HighPart;
}//LUID

int TokenInfLength = 0;
IntPtr ThisHandle = WindowsIdentity.GetCurrent().Token;
GetTokenInformation(ThisHandle, TOKEN_INFORMATION_CLASS.TokenPrivileges, IntPtr.Zero, TokenInfLength, ref TokenInfLength); //Get the TokenInformation length (returns false)
IntPtr TokenInformation = Marshal.AllocHGlobal(TokenInfLength);
if(GetTokenInformation(WindowsIdentity.GetCurrent().Token, TOKEN_INFORMATION_CLASS.TokenPrivileges, TokenInformation, TokenInfLength, ref TokenInfLength)){
  TOKEN_PRIVILEGES ThisPrivilegeSet = (TOKEN_PRIVILEGES)Marshal.PtrToStructure(TokenInformation, typeof(TOKEN_PRIVILEGES));
  //ThisPrivilegeSet now holds all of the LUID's i need to check out
  foreach(LUID_AND_ATTRIBUTES laa in ThisPrivilegeSet.Privileges){ //ThisPrivilegeSet.Privileges is only as deep as SizeConst will allow
    System.Text.StringBuilder StrBuilder = new System.Text.StringBuilder();
    int LuidNameLen = 0;
    IntPtr LuidPointer = Marshal.AllocHGlobal(Marshal.SizeOf(laa.Luid));
    Marshal.StructureToPtr(laa.Luid, LuidPointer, true);
    LookupPrivilegeName(null, LuidPointer, null, ref LuidNameLen); //Get the PrivilageName length (returns false)
    StrBuilder.EnsureCapacity(LuidNameLen + 1);
    if(LookupPrivilegeName(null, LuidPointer, StrBuilder, ref LuidNameLen)){ //StrBuilder gets the name this time
      Console.WriteLine("[{0}] : {1}", laa.Attributes.ToString(), StrBuilder.ToString());
    }//end if
    Marshal.FreeHGlobal(LuidPointer);
  }//next
}//end if

This is my first post, so sorry if I did it wrong and TIA for the help


I wanted to follow up on this question because of what i learned by following the link provided by VVS.

If you're like me and have never learned or used a programming language that has to access memory directly you might find this interesting as well (BTW I've marked SwDevman81's answer as the correct one as it works perfectly well).

In the blog post provided by VVS the writer talks about using the value (memory address) of the pointer returned by a call + the size of the objects being returned to marshall the returned data in chuncks to dynamically retrieve a variable number of objects. In this situation and the one outlined in the blog post we benefit from the first value being returned containing the number of elements in the following array, We also have an integer returned from the function telling us how large the returned structure will be which could also be used to check if we have reached the end of our structure should we not have the benefit of a value telling us the number of elements.

I'm providing the below code example for anyone interested in how the above blog post would be applied to this scenario.

Code Example:

using System;
using System.Runtime.InteropServices;
using System.Security.Principal;

namespace MarshallingExample {
  class Program {

    protected struct TOKEN_PRIVILEGES {
      public UInt32 PrivilegeCount;
      [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
      public LUID_AND_ATTRIBUTES[] Privileges;
    }

    [StructLayout(LayoutKind.Sequential)]
    protected struct LUID_AND_ATTRIBUTES {
      public LUID Luid;
      public UInt32 Attributes;
    }

    [StructLayout(LayoutKind.Sequential)]
    protected struct LUID {
      public uint LowPart;
      public int HighPart;
    }

    //This enum was huge, I cut it down to save space
    protected enum TOKEN_INFORMATION_CLASS {
      /// <summary>
      /// The buffer receives a TOKEN_PRIVILEGES structure that contains the privileges of the token.
      /// </summary>
      TokenPrivileges = 3
    }

    [DllImport("advapi32.dll", SetLastError = true)]
    protected static extern bool GetTokenInformation(IntPtr TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass, IntPtr TokenInformation, int TokenInformationLength, ref int ReturnLength);

    [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool LookupPrivilegeName(string lpSystemName, IntPtr lpLuid, System.Text.StringBuilder lpName, ref int cchName);

    static void Main(string[] args) {
      //Check and print the privileges this token has
      CheckPrivileges(WindowsIdentity.GetCurrent().Token);
    }

    //test function to output privileges for an account
    private static bool CheckPrivileges(IntPtr thisHandle) {
      int iTokenInfLength = 0; //holds the length of the TOKEN_PRIVILEGES structure that will be returned by GetTokenInformation

      //First call to GetTokenInformation returns the length of the structure that will be returned on the next call
      GetTokenInformation(thisHandle, TOKEN_INFORMATION_CLASS.TokenPrivileges, IntPtr.Zero, iTokenInfLength, ref iTokenInfLength);

      //Allocate a block of memory large enough to hold the expected structure
      IntPtr ipTokenInformation = Marshal.AllocHGlobal(iTokenInfLength);
      //ipTokenInformation holds the starting location readable as an integer
      //you can view the memory location using Ctrl-Alt-M,1 or Debug->Windows->Memory->Memory1 in Visual Studio
      //and pasting the value of ipTokenInformation into the search box (it's still empty right now though)
      if(GetTokenInformation(thisHandle, TOKEN_INFORMATION_CLASS.TokenPrivileges, ipTokenInformation, iTokenInfLength, ref iTokenInfLength)) {
        //If GetTokenInformation doesn't return false then the structure should be sitting in the space reserved by ipTokenInformation
        //at this point

        //What was returned is a structure of type TOKEN_PRIVILEGES which has two values, a UInt32 followed by an array
        //of LUID_AND_ATTRIBUTES structures. Because we know what to expect and we know the order to expect it we can section
        //off the memory into marshalled structures and do some math to figure out where to start our next marshal

        uint uiPrivilegeCount = (uint)Marshal.PtrToStructure(ipTokenInformation, typeof(uint)); //Get the count

        //lets create the structure we should have had in the first place
        LUID_AND_ATTRIBUTES[] aLuidAa = new LUID_AND_ATTRIBUTES[uiPrivilegeCount]; //initialize an array to the right size
        LUID_AND_ATTRIBUTES cLuidAa = new LUID_AND_ATTRIBUTES();

        //ipPointer will hold our new location to read from by taking the last pointer plus the size of the last structure read
        IntPtr ipPointer = new IntPtr(ipTokenInformation.ToInt32() + sizeof(uint)); //first laa pointer
        cLuidAa = (LUID_AND_ATTRIBUTES)Marshal.PtrToStructure(ipPointer, typeof(LUID_AND_ATTRIBUTES)); //Read the memory location
        aLuidAa[0] = cLuidAa; //Add it to the array

        //After getting our first structure we can loop through the rest since they will all be the same
        for(int i = 1; i < uiPrivilegeCount; ++i) {
          ipPointer = new IntPtr(ipPointer.ToInt32() + Marshal.SizeOf(cLuidAa)); //Update the starting point in ipPointer
          cLuidAa = (LUID_AND_ATTRIBUTES)Marshal.PtrToStructure(ipPointer, typeof(LUID_AND_ATTRIBUTES)); //Read the memory location
          aLuidAa[i] = cLuidAa; //Add it to the array
        }//next

        TOKEN_PRIVILEGES cPrivilegeSet = new TOKEN_PRIVILEGES();
        cPrivilegeSet.PrivilegeCount = uiPrivilegeCount;
        cPrivilegeSet.Privileges = aLuidAa;
        //now we have what we should have had to begin with
        Console.WriteLine("Privilege Count: {0}", cPrivilegeSet.PrivilegeCount.ToString());


        //This loops through the LUID_AND_ATTRIBUTES array and resolves the LUID names with a
        //call to LookupPrivilegeName which requires us to first convert our managed structure into an unmanaged one
        //so we get to see what it looks like to do it backwards
        foreach(LUID_AND_ATTRIBUTES cLaa in cPrivilegeSet.Privileges) {
          System.Text.StringBuilder sb = new System.Text.StringBuilder();
          int iLuidNameLen = 0; //Holds the length of structure we will be receiving LookupPrivilagename
          IntPtr ipLuid = Marshal.AllocHGlobal(Marshal.SizeOf(cLaa.Luid)); //Allocate a block of memory large enough to hold the structure
          Marshal.StructureToPtr(cLaa.Luid, ipLuid, true); //Write the structure into the reserved space in unmanaged memory
          LookupPrivilegeName(null, ipLuid, null, ref iLuidNameLen); // call once to get the name length we will be receiving
          sb.EnsureCapacity(iLuidNameLen + 1); //Make sure there is enough room for it
          if(LookupPrivilegeName(null, ipLuid, sb, ref iLuidNameLen)) { // call again to get the name
            Console.WriteLine("[{0}] : {1}", cLaa.Attributes.ToString(), sb.ToString());
          }//end if
          Marshal.FreeHGlobal(ipLuid); //Free up the reserved space in unmanaged memory (Should be done any time AllocHGlobal is used)
        }//next
        Marshal.FreeHGlobal(ipTokenInformation);  //Free up the reserved space in unmanaged memory (Should be done any time AllocHGlobal is used)
      }//end if GetTokenInformation
      return true;
    }//CheckPrivileges
  }//Program
}//MarshallingExample


You will not be able to change SizeConst at runtime, so I think your best bet will be to retrieve as many as possible and only use the ones you need. This way you wouldnt need to change the code later if you require additional information.

So for example, if the maximum number of possible privileges is 35, set the SizeConst to 35. Then change the foreach loop to a for loop and go from i = 0 to ThisPrivilegeSet.PrivilegeCount.

Heres an example (For this, I set the SizeConst to 8000):

  public void RunPrivileges()
  {
     int TokenInfLength = 0;
     IntPtr ThisHandle = WindowsIdentity.GetCurrent().Token;
     GetTokenInformation(ThisHandle, TOKEN_INFORMATION_CLASS.TokenPrivileges, IntPtr.Zero, TokenInfLength, ref TokenInfLength);
     IntPtr TokenInformation = Marshal.AllocHGlobal(TokenInfLength);
     if (GetTokenInformation(WindowsIdentity.GetCurrent().Token, TOKEN_INFORMATION_CLASS.TokenPrivileges, TokenInformation, TokenInfLength, ref TokenInfLength))
     {
        TOKEN_PRIVILEGES ThisPrivilegeSet = (TOKEN_PRIVILEGES)Marshal.PtrToStructure(TokenInformation, typeof(TOKEN_PRIVILEGES));
        for (int index = 0; index < ThisPrivilegeSet.PrivilegeCount; index++ )
        { 
           LUID_AND_ATTRIBUTES laa = ThisPrivilegeSet.Privileges[index];
           System.Text.StringBuilder StrBuilder = new System.Text.StringBuilder();
           int LuidNameLen = 0;
           IntPtr LuidPointer = Marshal.AllocHGlobal(Marshal.SizeOf(laa.Luid));
           Marshal.StructureToPtr(laa.Luid, LuidPointer, true);
           LookupPrivilegeName(null, LuidPointer, null, ref LuidNameLen);
           StrBuilder.EnsureCapacity(LuidNameLen + 1);
           if (LookupPrivilegeName(null, LuidPointer, StrBuilder, ref LuidNameLen))
           {
              Console.WriteLine("[{0}] : {1}", laa.Attributes.ToString(), StrBuilder.ToString());
           }
           Marshal.FreeHGlobal(LuidPointer);
        }
     }
  }
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜