I would like to enumerate the strings that are in the string intern pool.
That is to say, I want to get the list of all the instances s of string such that:
string.IsInterned(s) != null
Does anyone know if it's possible?
Thanks to the advice of #HansPassant, I managed to get the list of string literals in an assembly. Which is extremely close to what I originally wanted.
You need to use read assembly meta-data, and enumerate user-strings. This can be done with these three methods of IMetaDataImport:
[ComImport, Guid("7DAC8207-D3AE-4C75-9B67-92801A497D44")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IMetaDataImport
{
void CloseEnum(IntPtr hEnum);
uint GetUserString(uint stk, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] char[] szString, uint cchString, out uint pchString);
uint EnumUserStrings(ref IntPtr phEnum, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)]uint[] rStrings, uint cmax, out uint pcStrings);
// interface also contains 62 irrelevant methods
}
To get the instance of IMetaDataImport, you need to get a IMetaDataDispenser:
[ComImport, Guid("809C652E-7396-11D2-9771-00A0C9B4D50C")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[CoClass(typeof(CorMetaDataDispenser))]
interface IMetaDataDispenser
{
uint OpenScope([MarshalAs(UnmanagedType.LPWStr)]string szScope, uint dwOpenFlags, ref Guid riid, [MarshalAs(UnmanagedType.Interface)] out object ppIUnk);
// interface also contains 2 irrelevant methods
}
[ComImport, Guid("E5CB7A31-7512-11D2-89CE-0080C792E5D8")]
class CorMetaDataDispenser
{
}
Here is how it goes:
var dispenser = new IMetaDataDispenser();
var metaDataImportGuid = new Guid("7DAC8207-D3AE-4C75-9B67-92801A497D44");
object scope;
var hr = dispenser.OpenScope(location, 0, ref metaDataImportGuid, out scope);
metaDataImport = (IMetaDataImport)scope;
where location is the path to the assembly file.
After that, calling EnumUserStrings() and GetUserString() is straighforward.
Here is a blog post with more detail, and a demo project on GitHub.
The SSCLI function that its pointing to is
STRINGREF*AppDomainStringLiteralMap::GetStringLiteral(EEStringData *pStringData)
{
...
DWORD dwHash = m_StringToEntryHashTable->GetHash(pStringData);
if (m_StringToEntryHashTable->GetValue(pStringData, &Data, dwHash))
{
STRINGREF *pStrObj = NULL;
pStrObj = ((StringLiteralEntry*)Data)->GetStringObject();
_ASSERTE(!bAddIfNotFound || pStrObj);
return pStrObj;
}
else { ... }
return NULL; //Here, if this returns, the string is not interned
}
If you manage to find the native address of m_StringToEntryHashTable, you can enumerate the strings that exist.
Related
I am having difficulty comparing a string passed from usermode type LPWSTR to a LDR table entry type UNICODE_STRING
Kernel C:
struct {
int pid;
int user_pid;
int size;
int protection_mode;
int allocation_type;
void* address;
void* write_buffer;
LPWSTR module_name;
}
userland_operation;
This struct is passed to the kernel via deviceiocontrol. The counterpart userland struct is as follows:
public struct MemOperation
{
public int Pid;
public int UserPid;
public int Size;
public int protection_mode;
public int allocation_type;
public IntPtr Addr;
public IntPtr WriteBuffer;
[MarshalAs(UnmanagedType.LPWStr)] public String ModuleName;
}
Where the String ModuleName is Marshaled as LPWStr.
ModuleName is the desired search term for the loaded module in a process. Now, here's where things get tricky. The string I have access to via the _LDR_DATA_TABLE_ENTRY is a UNICODE_STRING. I want to compare this UNICODE_STRING with my LPWSTR.
I have tried the following and it did not work:
{
UNICODE_STRING str;
RtlInitUnicodeString(&str, module_name) // module name is the userland passed string LPWSTR
if (RtlCompareUnicodeString(&str, &module_ldr->BaseDllName, TRUE) {
}
}
I've also tried wcscmp, and a few other things. I'm not sure how I can compare these two properly. I've added some minor pseudocode to the function to provide additional context on what I'm looking to do.
NTSTATUS GetModuleList(HANDLE PID, PVOID UserBuffer, LPWSTR module_name) {
KAPC_STATE APC;
__try {
PEPROCESS TargetProcess;
PsLookupProcessByProcessId(PID, &TargetProcess);
PPEB Peb = PsGetProcessPeb(TargetProcess);
if (!Peb)
return STATUS_INVALID_PARAMETER;
KeStackAttachProcess(TargetProcess, &APC);
UINT64 Ldr = (UINT64)Peb + PEBLDR_OFFSET;
ProbeForRead((CONST PVOID)Ldr, 8, 8);
PLIST_ENTRY ModListHead = (PLIST_ENTRY)(*(PULONG64)Ldr + PEBLDR_MEMORYLOADED_OFFSET);
ProbeForRead((CONST PVOID)ModListHead, 8, 8);
PLIST_ENTRY Module = ModListHead->Flink;
while (ModListHead != Module) {
LDR_DATA_TABLE_ENTRY* Module_Ldr = (LDR_DATA_TABLE_ENTRY*)(Module);
//psuedo if (module_name is in Module_Ldr->BaseDllName) // the comparison, where BaseDllName is type UNICODE_STRING
Module = Module->Flink;
}
KeUnstackDetachProcess(&APC);
ObDereferenceObject(TargetProcess);
return STATUS_SUCCESS;
In this call below:
if (RtlCompareUnicodeString(&str, &module_ldr->BaseDllName) {
This function takes an additional argument which you are not passing.
Please refer to https://learn.microsoft.com/en-us/windows/win32/devnotes/rtlcompareunicodestring
Figured I'd answer this since I asked it a few years ago and no longer have this issue.
The string I create from userland prior to calling KeStackAttachProcess is not valid once that is called. I'm not quite sure if that's just how stack attaching to a process works, but regardless the fix was something very trivial such as (this is pseudo):
RtlInitUnicodeString(&str, module_name)
UNICODE_STRING example;
KeStachAttachProcess()
example = &module_ldr->BaseDllName;
KeStackDetachProcess()
RtlCompareUnicodeString(&example, &module_name)
Where the comparison is done outside of the attach.
Ex: in app1 user creates function_1() and function_2(). Then in app2 user wants to call function_2(). I searched on google and the only thing I found was to write this code:
class Program
{
[DllImport("functions.dll")]
public static extern void function_1();
static void Main(string[] args)
{
function_1();
}
}
You'll have to use some PInvoke code to get the DLL loaded (LoadLibrary) and to get a function pointer (GetProcAddess) and Marshal.GetDelegateForFunctionPointer to obtain a delegate that you can then invoke.
You can use SymEnumerateSymbols64 from dbghelp dll to obtain the list of existent functions in your DLL. Then the user can choose which function to run.
You can find a more detailed explanation here:
C# get the list of unmanaged C dll exports
Full example of what Damien wrote... Note that it works only if methods have all the same signature (in this example void function_X()). Additionally it is "difficult" to explore a dll to discover what methods are exported, so it is better if you already know what methods there should be in the dll.
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr LoadLibrary(string dllToLoad);
[DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
// Set the correct calling convention
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
private delegate void DllMethodDelegate();
IntPtr dll = LoadLibrary(#"PathToYourDll.DLL");
if (dll == IntPtr.Zero)
{
throw new Exception();
}
string methodName = "function_1";
IntPtr method = GetProcAddress(dll, methodName);
if (method == IntPtr.Zero)
{
throw new Exception();
}
DllMethodDelegate method2 = (DllMethodDelegate)Marshal.GetDelegateForFunctionPointer(method, typeof(DllMethodDelegate));
// Now you can do method2();
Note that you have to set the correct calling convention in the DllMethodDelegate() definition. Normally dll methods should be StdCall.
The signature for the method you wrote is:
[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private delegate int DllMethodDelegate(char cmd, ref IntPtr out_address);
Note that "filling" the out_address is very complex (headache complex).
{
// I case:
IntPtr ret = IntPtr.Zero;
int result = method2('I', ref ret);
}
{
// R case:
IntPtr ptr = IntPtr.Zero;
int result = method2('R', ref ptr);
int value = Marshal.ReadInt32(ptr);
}
{
// W case:
int value = 100;
GCHandle handle = default(GCHandle);
try
{
int[] value2 = new int[] { value };
handle = GCHandle.Alloc(value2, GCHandleType.Pinned);
IntPtr ptr = handle.AddrOfPinnedObject();
int result = method2('W', ref ptr);
}
finally
{
if (handle.IsAllocated)
{
handle.Free();
}
}
}
It is possible (but I'm not sure) that for the third example, you could do
object value2 = value;
instead of
int[] value2 = new int[] { value };
The interaction of boxing and GCHandle isn't very documented, but it seems to work. C# specifications 4.3 seems to ok it... but I wouldn't trust it, and this technique seems to be described in the book ".NET and COM: The Complete Interoperability Guide", in the chapter "Obtaining the Address of Value Types" (that chapter is searchable in google, use the exact chapter title I gave. The example is in VB.NET but it is quite clear to read)
I'm currently trying to accomplish the following:
For an SDK, which we provide to our customers, we want the SDK-developers to be able to provide external application calls, so that they can insert additional buttons. These buttons than will start an external application or open a file with the default application for it (Word for *.docx for example).
There should be some visual distinction between the different buttons, so our approach is to show the icon of the application to be called.
Now, there are three different kind of calls:
(The strings below would always be the value of ProcessStartInfo.FileName)
Calling an application providing the full application path, possibly with environement vars (e.g. "C:\Program Files\Internet Explorer\iexplore.exe" / "%ProgramFiles%\Internet Explorer\iexplore.exe")
Calling an application providing only the executable name, given the application can be found in the PATH Variable (e.g. "iexplore")
Opening a document, without providing an application to open it (e.g. "D:\test.html")
We are looking for a way, to find the appropriate Icon for any given call. For this we have to find the full application path of the application, which will be executed in any of the three ways above, but before we actually have started the Process
Is there a way to find the full path or the icon of a System.Diagnostics.Process or System.Diagnostics.ProcessStartInfo object, before the process has been started?
Important: We must not start the process before (could have side effects)
Example Code:
var process = new Process
{
StartInfo =
{
//applicationPath could be any of the stated above calls
FileName = Environment.ExpandEnvironmentVariables(applicationPath)
}
};
//we have to find the full path here, but MainModule is null as long as the process object has not yet started
var icon = Icon.ExtractAssociatedIcon(process.MainModule.FullPath)
Solution
Thanks to you guys I found my solution. The project linked here at CodeProject provides a solution for my exact problem, which works equally with programs and files and can provide the icon before starting the process. Thanks for the link #wgraham
If you want your UI to be visually-consistent with the rest of the user's machine, you may want to extract the icon from the file using Icon.ExtractAssociatedIcon(string path). This works under the WinForms/GDI world. Alternatively, this question addresses how to complete it with P/Invoke.
Your first two examples shouldn't be too hard to figure out using Environment.ExpandEnvironmentVariables. Your last one is the tougher one - the best bet seems to be using PInvoke to call AssocCreate. Adapted from the pinvoke page (http://www.pinvoke.net/default.aspx/shlwapi/AssocCreate.html):
public static class GetDefaultProgramHelper
{
public unsafe static string GetDefaultProgram(string ext)
{
try
{
object obj;
AssocCreate(
ref CLSID_QueryAssociations,
ref IID_IQueryAssociations,
out obj);
IQueryAssociations qa = (IQueryAssociations)obj;
qa.Init(
ASSOCF.INIT_DEFAULTTOSTAR,
ext, //".doc",
UIntPtr.Zero, IntPtr.Zero);
int size = 0;
qa.GetString(ASSOCF.NOTRUNCATE, ASSOCSTR.COMMAND,
"open", null, ref size);
StringBuilder sb = new StringBuilder(size);
qa.GetString(ASSOCF.NOTRUNCATE, ASSOCSTR.COMMAND,
"open", sb, ref size);
//Console.WriteLine(".doc is opened by : {0}", sb.ToString());
return sb.ToString();
}
catch(Exception e)
{
if((uint)Marshal.GetHRForException(e) == 0x80070483)
//Console.WriteLine("No command line is associated to .doc open verb.");
return null;
else
throw;
}
}
[DllImport("shlwapi.dll")]
extern static int AssocCreate(
ref Guid clsid,
ref Guid riid,
[MarshalAs(UnmanagedType.Interface)] out object ppv);
[Flags]
enum ASSOCF
{
INIT_NOREMAPCLSID = 0x00000001,
INIT_BYEXENAME = 0x00000002,
OPEN_BYEXENAME = 0x00000002,
INIT_DEFAULTTOSTAR = 0x00000004,
INIT_DEFAULTTOFOLDER = 0x00000008,
NOUSERSETTINGS = 0x00000010,
NOTRUNCATE = 0x00000020,
VERIFY = 0x00000040,
REMAPRUNDLL = 0x00000080,
NOFIXUPS = 0x00000100,
IGNOREBASECLASS = 0x00000200,
INIT_IGNOREUNKNOWN = 0x00000400
}
enum ASSOCSTR
{
COMMAND = 1,
EXECUTABLE,
FRIENDLYDOCNAME,
FRIENDLYAPPNAME,
NOOPEN,
SHELLNEWVALUE,
DDECOMMAND,
DDEIFEXEC,
DDEAPPLICATION,
DDETOPIC,
INFOTIP,
QUICKTIP,
TILEINFO,
CONTENTTYPE,
DEFAULTICON,
SHELLEXTENSION
}
enum ASSOCKEY
{
SHELLEXECCLASS = 1,
APP,
CLASS,
BASECLASS
}
enum ASSOCDATA
{
MSIDESCRIPTOR = 1,
NOACTIVATEHANDLER,
QUERYCLASSSTORE,
HASPERUSERASSOC,
EDITFLAGS,
VALUE
}
[Guid("c46ca590-3c3f-11d2-bee6-0000f805ca57"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IQueryAssociations
{
void Init(
[In] ASSOCF flags,
[In, MarshalAs(UnmanagedType.LPWStr)] string pszAssoc,
[In] UIntPtr hkProgid,
[In] IntPtr hwnd);
void GetString(
[In] ASSOCF flags,
[In] ASSOCSTR str,
[In, MarshalAs(UnmanagedType.LPWStr)] string pwszExtra,
[Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszOut,
[In, Out] ref int pcchOut);
void GetKey(
[In] ASSOCF flags,
[In] ASSOCKEY str,
[In, MarshalAs(UnmanagedType.LPWStr)] string pwszExtra,
[Out] out UIntPtr phkeyOut);
void GetData(
[In] ASSOCF flags,
[In] ASSOCDATA data,
[In, MarshalAs(UnmanagedType.LPWStr)] string pwszExtra,
[Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 4)] out byte[] pvOut,
[In, Out] ref int pcbOut);
void GetEnum(); // not used actually
}
static Guid CLSID_QueryAssociations = new Guid("a07034fd-6caa-4954-ac3f-97a27216f98a");
static Guid IID_IQueryAssociations = new Guid("c46ca590-3c3f-11d2-bee6-0000f805ca57");
}
You could call this using string filePathToProgram = GetDefaultProgramHelper.GetDefaultProgram(".docx");
The Process class can do exactly what you want.
Environment Variables like %ProgramFiles% need to be expanded first with Environment.ExpandEnvironmentVariables(string).
1.
using System.IO;
using System.Diagnostics;
string iexplore = #"C:\Program Files\Internet Explorer\iexplore.exe");
string iexploreWithEnvVars = Environment.ExpandEnvironmentVariables(#"%ProgramFiles%\Internet Explorer\iexplore.exe");
2.
public static string FindFileInPath(string name)
{
foreach (string path in Environment.ExpandEnvironmentVariables("%path%").Split(';'))
{
string filename;
if (File.Exists(filename = Path.Combine(path, name)))
{
return filename; // returns the absolute path if the file exists
}
}
return null; // will return null if it didn't find anything
}
3.
Process.Start("D:\\test.html");
You want to put your code into try-catch blocks as well since Process.Start will throw an Exception if the file doesn't exist.
Edit: Updated the code, thanks to Dan Field for pointing out that I missed the meaning of the question. :/
If possible, I want to be able to check whether or not the recycle bin is empty, with minimal hassle (importing dlls, importing anything, creating entire new class to hold recycle bin functionality etc...)
I already have the code below that I found online to empty the recycle bin so it seems natural to suspect that I should be able to extend this to check if it needs emptying first, perhaps.. another function within Shell32.dll.
enum BinFlags : uint
{
SHERB_NOCONFIRMATION = 0x00000001,
SHERB_NOPROGRESSUI = 0x00000002,
SHERB_NOSOUND = 0x00000004
}
[DllImport("Shell32.dll", CharSet = CharSet.Unicode)]
static extern uint SHEmptyRecycleBin(IntPtr hwnd, string rootPath,
BinFlags flags);
/* snip, bunch of code... */
SHEmptyRecycleBin(IntPtr.Zero, null, 0);
You can add reference to the C:\Windows\System32\Shell32.dll and use the following snippet:
Shell shell = new Shell();
Folder recycleBin = shell.NameSpace(10);
int itemsCount = recycleBin.Items().Count;
Taken from here.
It's poor documentation, but you might want SHQueryRecycleBin EDIT: Slightly better documentation over at MSDN.
[DllImport("shell32.dll")]
static extern int SHQueryRecycleBin(string pszRootPath, ref SHQUERYRBINFO
pSHQueryRBInfo);
[StructLayout(LayoutKind.Sequential, Pack=4)]
public struct SHQUERYRBINFO
{
public int cbSize;
public long i64Size;
public long i64NumItems;
}
It looks like you make the call and it fills the object and if you look at i64NumItems and it is 0 then the recycle bin is empty.
public static int GetCount()
{
SHQUERYRBINFO sqrbi = new SHQUERYRBINFO();
sqrbi.cbSize = Marshal.SizeOf(typeof(SHQUERYRBINFO));
int hresult = SHQueryRecycleBin(string.Empty, ref sqrbi);
return (int)sqrbi.i64NumItems;
}
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 reference 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);
}
}
}