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);
}
}
}
精彩评论