Can't read privileges from process access token - c#

I'm trying to get the list of groups and privileges in a process access token and some info about them (name, flags and description).
To do this I'm using the GetTokenInfomation function and the TOKEN_GROUPS_AND_PRIVILEGES structure.
These are the structure definitions:
TOKEN_GROUPS_AND_PRIVILEGES
[StructLayout(LayoutKind.Sequential)]
public struct TOKEN_GROUPS_AND_PRIVILEGES
{
public uint SidCount;
public uint SidLength;
public IntPtr Sids;
public uint RestrictedSidCount;
public uint RestrictedSidLength;
public IntPtr RestrictedSids;
public uint PrivilegeCount;
public uint PrivilegeLength;
public IntPtr Privileges;
public LUID AuthenticationID;
}
LUID_AND_ATTRIBUTES
[StructLayout(LayoutKind.Sequential)]
public struct LUID_AND_ATTRIBUTES
{
public LUID Luid;
public uint Attributes;
}
LUID
[StructLayout(LayoutKind.Sequential)]
public struct LUID
{
public uint LowPart;
public int HighPart;
}
I can read the groups SIDs and get info on them fine but when I try to read the privileges I always get an Access violation.
This is the code that I use to read the privileges:
for (int i = 0; i < GroupsAndPrivilegesInfo.PrivilegeCount; i++)
{
Privilege = (Win32Structures.LUID_AND_ATTRIBUTES)Marshal.PtrToStructure(GroupsAndPrivilegesInfo.Privileges, typeof(Win32Structures.LUID_AND_ATTRIBUTES));
PrivilegeName = GetPrivilegeName(Privilege.Luid) ?? "Non disponibile";
PrivilegeStatus = GetPrivilegeFlags((Win32Enumerations.PrivilegeLUIDAttribute)Privilege.Attributes) ?? "Non disponibile";
PrivilegeDescription = GetPrivilegeDescription(PrivilegeName) ?? "Non disponibile";
Privileges.Add(new TokenPrivilegeInfo(PrivilegeName, PrivilegeStatus, PrivilegeDescription));
GroupsAndPrivilegesInfo.Privileges += Marshal.SizeOf(typeof(Win32Structures.LUID_AND_ATTRIBUTES));
}
The exception occurs when calling the GetPrivilegeName method which uses the function LookupPrivilegeNameW defined as follows:
[DllImport("Advapi32.dll", EntryPoint = "LookupPrivilegeNameW", CharSet = CharSet.Unicode, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool LookupPrivilegeName(string SystemName, Win32Structures.LUID LUID, StringBuilder PrivilegeName, ref uint NameLength);
I tried different methods to read the data such as:
Manually calculating the offset to the privileges array element from the start of the structure
Declare the privileges field as an array and set a SizeConst to an obviously oversized value
Even those methods threw an Access violation exception.
I don't undestand what is the problem given that I use the same code to read the SIDs and it works normally.

First, The second parameter of LookupPrivilegeNameW is A pointer to a LUID. You need to declare it as a reference type like ref Win32Structures.LUID LUID.
And then the TOKEN_GROUPS_AND_PRIVILEGES.Privileges is an array of the privileges.
Your code does not retrieve all Privileges, but repeatedly reads the first Privilege every time.
Here is the working sample:
namespace ConsoleApp2
{
class Program
{
[StructLayout(LayoutKind.Sequential)]
public struct TOKEN_GROUPS_AND_PRIVILEGES
{
public uint SidCount;
public uint SidLength;
public IntPtr Sids;
public uint RestrictedSidCount;
public uint RestrictedSidLength;
public IntPtr RestrictedSids;
public uint PrivilegeCount;
public uint PrivilegeLength;
public IntPtr Privileges;
public LUID AuthenticationID;
}
[StructLayout(LayoutKind.Sequential)]
public struct LUID_AND_ATTRIBUTES
{
public LUID Luid;
public uint Attributes;
}
[StructLayout(LayoutKind.Sequential)]
public struct LUID
{
public uint LowPart;
public int HighPart;
}
public enum TOKEN_INFORMATION_CLASS
{
TokenUser = 1,
TokenGroups,
TokenPrivileges,
TokenOwner,
TokenPrimaryGroup,
TokenDefaultDacl,
TokenSource,
TokenType,
TokenImpersonationLevel,
TokenStatistics,
TokenRestrictedSids,
TokenSessionId,
TokenGroupsAndPrivileges,
TokenSessionReference,
TokenSandBoxInert,
TokenAuditPolicy,
TokenOrigin
}
[DllImport("Advapi32.dll", EntryPoint = "LookupPrivilegeNameW", CharSet = CharSet.Unicode, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool LookupPrivilegeName(string SystemName, ref LUID LUID, StringBuilder PrivilegeName, ref uint NameLength);
[DllImport("advapi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool OpenProcessToken(IntPtr ProcessHandle,
UInt32 DesiredAccess, out IntPtr TokenHandle);
[DllImport("kernel32.dll")]
public static extern IntPtr GetCurrentProcess();
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool GetTokenInformation(IntPtr TokenHandle,
TOKEN_INFORMATION_CLASS TokenInformationClass,
IntPtr TokenInformation,
Int32 TokenInformationLength,
out Int32 ReturnLength);
public static UInt32 TOKEN_QUERY = 0x0008;
static void Main(string[] args)
{
bool ret = false;
IntPtr token;
ret = OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, out token);
Int32 ReturnLength;
ret = GetTokenInformation(token, TOKEN_INFORMATION_CLASS.TokenGroupsAndPrivileges, IntPtr.Zero, 0, out ReturnLength);
IntPtr pGroupsAndPrivilegesInfo = Marshal.AllocHGlobal(ReturnLength);
ret = GetTokenInformation(token, TOKEN_INFORMATION_CLASS.TokenGroupsAndPrivileges, pGroupsAndPrivilegesInfo, ReturnLength, out ReturnLength);
TOKEN_GROUPS_AND_PRIVILEGES GroupsAndPrivilegesInfo = (TOKEN_GROUPS_AND_PRIVILEGES)Marshal.PtrToStructure(pGroupsAndPrivilegesInfo, typeof(TOKEN_GROUPS_AND_PRIVILEGES));
for (int i = 0; i < GroupsAndPrivilegesInfo.PrivilegeCount; i++)
{
//IntPtr ptr = GroupsAndPrivilegesInfo.Privileges + i * Marshal.SizeOf(typeof(LUID_AND_ATTRIBUTES));
LUID_AND_ATTRIBUTES Privilege = (LUID_AND_ATTRIBUTES)Marshal.PtrToStructure(GroupsAndPrivilegesInfo.Privileges + i * Marshal.SizeOf(typeof(LUID_AND_ATTRIBUTES)), typeof(LUID_AND_ATTRIBUTES));
StringBuilder name = new StringBuilder(50);
uint cchName = 50;
ret = LookupPrivilegeName(null, ref Privilege.Luid, name, ref cchName);
Console.WriteLine(name);
}
Marshal.FreeHGlobal(pGroupsAndPrivilegesInfo);
}
}
}

Related

Process32FirstW always returns false

Process32FirstW is suppose to return true when it finds process and return the first process in a PROCESSENTRY32W structure but it doesnt, so i cant enumerate it in Process32Next sadly. What is the reason it doesnt return true as a bool function? [([([( I have changed the struct deceleration )])])])]
[StructLayout(LayoutKind.Sequential)]
public struct PROCESSENTRY32W
{
public uint dwSize;
public uint cntUsage;
public uint th32ProcessID;
public IntPtr th32DefaultHeapID;
public uint th32ModuleID;
public uint cntThreads;
public uint th32ParentProcessID;
public int pcPriClassBase;
public uint dwFlags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string szExeFile;
};
[DllImport("kernel32.dll", EntryPoint = "CreateToolhelp32Snapshot")]
public static extern IntPtr CreateToolhelp32SnapshotRtlMoveMemory(UInt32 dwFlagsdes, UInt32 th32ProcessID);
[DllImport("kernel32", EntryPoint = "Process32FirstW", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool Process32FirstW(IntPtr hSnapshot, IntPtr lppe);
[DllImport("kernel32", EntryPoint = "Process32Next")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool Process32Next(IntPtr hSnapshot, IntPtr lppe);
[DllImport("kernel32", EntryPoint = "CloseHandle")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CloseHandle(IntPtr handle);
public static Dictionary<int, Process> get_process_list()
{
//Dictionary<string, Process> returnable_processes_ = new Dictionary<string, Process>();
Dictionary<int, Process> returnable_processes_ = new Dictionary<int, Process>();
IntPtr HANLDE_Processes = CreateToolhelp32SnapshotRtlMoveMemory(2,0);
PROCESSENTRY32W p32Iw = new PROCESSENTRY32W();
p32Iw.dwSize = System.Runtime.InteropServices.Marshal.SizeOf(typeof(PROCESSENTRY32W));
IntPtr p32IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(p32Iw));
Marshal.StructureToPtr(p32Iw, p32IntPtr, false);
bool blFirstProcess = Process32FirstW(HANLDE_Processes, p32IntPtr); // returns false no matter what?
int x = Marshal.GetLastWin32Error();
if(blFirstProcess)
{
do
{
Marshal.PtrToStructure(p32IntPtr, p32Iw);
int PID = (int) p32Iw.th32ProcessID;
returnable_processes_.Add(PID, new Process());
}
while(Process32Next(HANLDE_Processes, p32IntPtr));
}
Marshal.FreeHGlobal(p32IntPtr);
return returnable_processes_;
}
It's really a charset problem. You use the ***W version of api. The actual type of PROCESSENTRY32W.szExeFile is wchar_t, which is 2 bytes, so the structure size you passed is 260 bytes shorter(you can simply try p32Iw.dwSize + 260, Process32FirstW will return ture, if we are regardless of the subsequent execution).
Fix the struct declaration and run the "C/C++ Programming" C# code.
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct PROCESSENTRY32
{
public uint dwSize;
public uint cntUsage;
public uint th32ProcessID;
public IntPtr th32DefaultHeapID;
public uint th32ModuleID;
public uint cntThreads;
public uint th32ParentProcessID;
public int pcPriClassBase;
public uint dwFlags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=260)] public string szExeFile;
};
But according to the document, Marshal.StructureToPtr and Marshal.PtrToStructure are out of date and we should avoid using it. And this way of passing values through pointers is usually replaced by references in C#.
First, declare charset and reference parameters for Process32First and Process32Next
[DllImport("kernel32", EntryPoint = "Process32First", SetLastError = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool Process32First(IntPtr hSnapshot, ref PROCESSENTRY32W lppe);
[DllImport("kernel32", EntryPoint = "Process32Next", CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool Process32Next(IntPtr hSnapshot, ref PROCESSENTRY32W lppe);
Then, Pass the ref type of PROCESSENTRY32W:
bool blFirstProcess = Process32First(HANLDE_Processes, ref p32Iw); // returns false no matter what?
if (blFirstProcess)
{
do
{
//Marshal.PtrToStructure(p32IntPtr, p32Iw);
int PID = (int)p32Iw.th32ProcessID;
returnable_processes_.Add(PID, new Process());
Console.WriteLine(p32Iw.szExeFile);
}
while (Process32Next(HANLDE_Processes, ref p32Iw));
}
Summarize, a minimal, compilable example:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace ConsoleApp
{
class Program
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct PROCESSENTRY32W
{
public uint dwSize;
public uint cntUsage;
public uint th32ProcessID;
public IntPtr th32DefaultHeapID;
public uint th32ModuleID;
public uint cntThreads;
public uint th32ParentProcessID;
public int pcPriClassBase;
public uint dwFlags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] public string szExeFile;
};
[DllImport("kernel32.dll", EntryPoint = "CreateToolhelp32Snapshot")]
public static extern IntPtr CreateToolhelp32SnapshotRtlMoveMemory(UInt32 dwFlagsdes, UInt32 th32ProcessID);
[DllImport("kernel32", EntryPoint = "Process32First", SetLastError = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool Process32First(IntPtr hSnapshot, ref PROCESSENTRY32W lppe);
[DllImport("kernel32", EntryPoint = "Process32Next", CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool Process32Next(IntPtr hSnapshot, ref PROCESSENTRY32W lppe);
[DllImport("kernel32", EntryPoint = "CloseHandle")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CloseHandle(IntPtr handle);
public static Dictionary<int, Process> get_process_list()
{
//Dictionary<string, Process> returnable_processes_ = new Dictionary<string, Process>();
Dictionary<int, Process> returnable_processes_ = new Dictionary<int, Process>();
IntPtr HANLDE_Processes = CreateToolhelp32SnapshotRtlMoveMemory(2, 0);
PROCESSENTRY32W p32Iw = new PROCESSENTRY32W();
int size = System.Runtime.InteropServices.Marshal.SizeOf(typeof(PROCESSENTRY32W));
p32Iw.dwSize = Convert.ToUInt32(size);
//IntPtr p32IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(p32Iw));
//Marshal.StructureToPtr(p32Iw, p32IntPtr, false);
bool blFirstProcess = Process32First(HANLDE_Processes, ref p32Iw); // returns false no matter what?
int x = Marshal.GetLastWin32Error();
if (blFirstProcess)
{
do
{
int PID = (int)p32Iw.th32ProcessID;
returnable_processes_.Add(PID, new Process());
Console.WriteLine(p32Iw.szExeFile);
}
while (Process32Next(HANLDE_Processes, ref p32Iw));
}
return returnable_processes_;
}
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
get_process_list();
Console.ReadKey();
}
}
}

Mount other users hive with C# .net

I'm writing an application that will write some registry key for every selected user.
I was wondering if there was a proper way to mount another user's hive to write in it.
At the moment, I'm using "REG LOAD" to mount every hive. It's functional, but messy.
Any Idea ?
Thanks in advance for your answers.
Cheers.
--- EDIT 19.06.2013 ---
Okay, thanks for the help, I was able to call the function, but was unauthorized to mount the registry.
I tought it was a missing privilege, and force it to run in admin.
I still recieve a 0x522 error, which mean, according to MSDN, that I don't have the right to mount the hive.
I searched the web and found different explanation and possibilities, but I still can't manage to mount the hive.
I'm pretty new in C# developpement and Windows API...
Here's the code I tried to understand and used in my tests.
Am I missing something ?
Thanks in advance for your answer.
namespace mountregistry2
{
public partial class Form1 : Form
{
[StructLayout(LayoutKind.Sequential)]
public struct LUID
{
public int LowPart;
public int HighPart;
}
[StructLayout(LayoutKind.Sequential)]
public struct TOKEN_PRIVILEGES
{
public LUID Luid;
public int Attributes;
public int PrivilegeCount;
}
[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
public static extern int OpenProcessToken(int ProcessHandle, int DesiredAccess,
ref int tokenhandle);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern int GetCurrentProcess();
[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
public static extern int LookupPrivilegeValue(string lpsystemname, string lpname,
[MarshalAs(UnmanagedType.Struct)] ref LUID lpLuid);
[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
public static extern int AdjustTokenPrivileges(int tokenhandle, int disableprivs,
[MarshalAs(UnmanagedType.Struct)]ref TOKEN_PRIVILEGES Newstate, int bufferlength,
int PreivousState, int Returnlength);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int RegLoadKey(uint hKey, string lpSubKey, string lpFile);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int RegUnLoadKey(uint hKey, string lpSubKey);
public const int TOKEN_ADJUST_PRIVILEGES = 0x00000020;
public const int TOKEN_QUERY = 0x00000008;
public const int SE_PRIVILEGE_ENABLED = 0x00000002;
public const string SE_RESTORE_NAME = "SeRestorePrivilege";
public const string SE_BACKUP_NAME = "SeBackupPrivilege";
public const uint HKEY_USERS = 0x80000003;
public string shortname;
bool unloaded = false;
private void testmountregistry()
{
int token = 0;
int retval = 0;
TOKEN_PRIVILEGES TokenPrivileges1 = new TOKEN_PRIVILEGES();
TOKEN_PRIVILEGES TokenPrivileges2 = new TOKEN_PRIVILEGES();
LUID RestoreLuid = new LUID();
LUID BackupLuid = new LUID();
retval = GetCurrentProcess();
MessageBox.Show(retval.ToString("X")); //returns FFFFFFFF, which should work
retval = OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref token);
MessageBox.Show(retval.ToString("X"));//RETURNS 1
retval = LookupPrivilegeValue(null, SE_RESTORE_NAME, ref RestoreLuid);
MessageBox.Show(retval.ToString("X"));//Returns 1
retval = LookupPrivilegeValue(null, SE_BACKUP_NAME, ref BackupLuid);
MessageBox.Show(retval.ToString("X"));//Returns 1
TokenPrivileges1.PrivilegeCount = 1;
TokenPrivileges1.Attributes = SE_PRIVILEGE_ENABLED;
TokenPrivileges1.Luid = RestoreLuid;
TokenPrivileges2.PrivilegeCount = 1;
TokenPrivileges2.Attributes = SE_PRIVILEGE_ENABLED;
TokenPrivileges2.Luid = BackupLuid;
retval = AdjustTokenPrivileges(token, 0, ref TokenPrivileges1, 1024, 0, 0);
MessageBox.Show(retval.ToString("X"));//Returns 1
retval = AdjustTokenPrivileges(token, 0, ref TokenPrivileges2, 1024, 0, 0);
MessageBox.Show(retval.ToString("X"));//Returns 1
uint hkey_users = 0x80000003;
int interror = RegLoadKey(hkey_users, "test", #"C:\Users\Public\NTUSER.DAT");
MessageBox.Show(interror.ToString("X"));//Return 0x522
return;
}
}
}
You can call the RegLoadKey API method using platform invoke.
[DllImport("advapi32.dll", SetLastError = true)]
static extern Int32 RegLoadKey(IntPtr hKey, string lpSubKey, string lpFile);

Run a process from a windows service as the current user

I currently have a windows service that is running under the System Account. My problem is that i need to start certain processes from within the service as the current logged on user. I have all the code etc to get the current logged on user / Active session.
My problem is that i need spawn a process as the logged on user but will not know the user credentials etc.
The service is .net compiled service and i expect that i need to use some Pinvoke methods to get a handle of one of the current users process in order to duplicate it and lunch as process with the handle.
Unfortunately i cannot find any good documentation / solution on how to implement it?
If someone is able to give me some guidance / example i would highly appreciate it.
* Updated *
I think i have explained this incorrectly and need to reajust according to what i actually require. I do not necessarily want to launch a new process, i just want to impersonate the logged on user. I have been so wrapped up at looking at CreateProcess etc i have lead myself down a path of create a new process as the current logged in user (which is not particularly what i want to do).
In turn i just want to run some code under the current user context (Impersonate the current Logged on user)?
One option is to have background application that automatically starts when user logs on and listens to commands from your service through WCF, or thrift, or by just monitoring some file and reading command from there.
Another option is to do what you originally asked for - launch using windows API. But the code is quite scary. Here is a sample, that you can use. It will execute any command line under currently active user session, with CreateProcessInConsoleSession method:
internal class ApplicationLauncher
{
public enum TOKEN_INFORMATION_CLASS
{
TokenUser = 1,
TokenGroups,
TokenPrivileges,
TokenOwner,
TokenPrimaryGroup,
TokenDefaultDacl,
TokenSource,
TokenType,
TokenImpersonationLevel,
TokenStatistics,
TokenRestrictedSids,
TokenSessionId,
TokenGroupsAndPrivileges,
TokenSessionReference,
TokenSandBoxInert,
TokenAuditPolicy,
TokenOrigin,
MaxTokenInfoClass // MaxTokenInfoClass should always be the last enum
}
public const int READ_CONTROL = 0x00020000;
public const int STANDARD_RIGHTS_REQUIRED = 0x000F0000;
public const int STANDARD_RIGHTS_READ = READ_CONTROL;
public const int STANDARD_RIGHTS_WRITE = READ_CONTROL;
public const int STANDARD_RIGHTS_EXECUTE = READ_CONTROL;
public const int STANDARD_RIGHTS_ALL = 0x001F0000;
public const int SPECIFIC_RIGHTS_ALL = 0x0000FFFF;
public const int TOKEN_ASSIGN_PRIMARY = 0x0001;
public const int TOKEN_DUPLICATE = 0x0002;
public const int TOKEN_IMPERSONATE = 0x0004;
public const int TOKEN_QUERY = 0x0008;
public const int TOKEN_QUERY_SOURCE = 0x0010;
public const int TOKEN_ADJUST_PRIVILEGES = 0x0020;
public const int TOKEN_ADJUST_GROUPS = 0x0040;
public const int TOKEN_ADJUST_DEFAULT = 0x0080;
public const int TOKEN_ADJUST_SESSIONID = 0x0100;
public const int TOKEN_ALL_ACCESS_P = (STANDARD_RIGHTS_REQUIRED |
TOKEN_ASSIGN_PRIMARY |
TOKEN_DUPLICATE |
TOKEN_IMPERSONATE |
TOKEN_QUERY |
TOKEN_QUERY_SOURCE |
TOKEN_ADJUST_PRIVILEGES |
TOKEN_ADJUST_GROUPS |
TOKEN_ADJUST_DEFAULT);
public const int TOKEN_ALL_ACCESS = TOKEN_ALL_ACCESS_P | TOKEN_ADJUST_SESSIONID;
public const int TOKEN_READ = STANDARD_RIGHTS_READ | TOKEN_QUERY;
public const int TOKEN_WRITE = STANDARD_RIGHTS_WRITE |
TOKEN_ADJUST_PRIVILEGES |
TOKEN_ADJUST_GROUPS |
TOKEN_ADJUST_DEFAULT;
public const int TOKEN_EXECUTE = STANDARD_RIGHTS_EXECUTE;
public const uint MAXIMUM_ALLOWED = 0x2000000;
public const int CREATE_NEW_PROCESS_GROUP = 0x00000200;
public const int CREATE_UNICODE_ENVIRONMENT = 0x00000400;
public const int IDLE_PRIORITY_CLASS = 0x40;
public const int NORMAL_PRIORITY_CLASS = 0x20;
public const int HIGH_PRIORITY_CLASS = 0x80;
public const int REALTIME_PRIORITY_CLASS = 0x100;
public const int CREATE_NEW_CONSOLE = 0x00000010;
public const string SE_DEBUG_NAME = "SeDebugPrivilege";
public const string SE_RESTORE_NAME = "SeRestorePrivilege";
public const string SE_BACKUP_NAME = "SeBackupPrivilege";
public const int SE_PRIVILEGE_ENABLED = 0x0002;
public const int ERROR_NOT_ALL_ASSIGNED = 1300;
private const uint TH32CS_SNAPPROCESS = 0x00000002;
public static int INVALID_HANDLE_VALUE = -1;
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool LookupPrivilegeValue(IntPtr lpSystemName, string lpname,
[MarshalAs(UnmanagedType.Struct)] ref LUID lpLuid);
[DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUser", SetLastError = true, CharSet = CharSet.Ansi,
CallingConvention = CallingConvention.StdCall)]
public static extern bool CreateProcessAsUser(IntPtr hToken, String lpApplicationName, String lpCommandLine,
ref SECURITY_ATTRIBUTES lpProcessAttributes,
ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandle, int dwCreationFlags, IntPtr lpEnvironment,
String lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool DuplicateToken(IntPtr ExistingTokenHandle,
int SECURITY_IMPERSONATION_LEVEL, ref IntPtr DuplicateTokenHandle);
[DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx")]
public static extern bool DuplicateTokenEx(IntPtr ExistingTokenHandle, uint dwDesiredAccess,
ref SECURITY_ATTRIBUTES lpThreadAttributes, int TokenType,
int ImpersonationLevel, ref IntPtr DuplicateTokenHandle);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool AdjustTokenPrivileges(IntPtr TokenHandle, bool DisableAllPrivileges,
ref TOKEN_PRIVILEGES NewState, int BufferLength, IntPtr PreviousState, IntPtr ReturnLength);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool SetTokenInformation(IntPtr TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass,
ref uint TokenInformation, uint TokenInformationLength);
[DllImport("userenv.dll", SetLastError = true)]
public static extern bool CreateEnvironmentBlock(ref IntPtr lpEnvironment, IntPtr hToken, bool bInherit);
public static bool CreateProcessInConsoleSession(String CommandLine, bool bElevate)
{
PROCESS_INFORMATION pi;
bool bResult = false;
uint dwSessionId, winlogonPid = 0;
IntPtr hUserToken = IntPtr.Zero, hUserTokenDup = IntPtr.Zero, hPToken = IntPtr.Zero, hProcess = IntPtr.Zero;
Debug.Print("CreateProcessInConsoleSession");
// Log the client on to the local computer.
dwSessionId = WTSGetActiveConsoleSessionId();
// Find the winlogon process
var procEntry = new PROCESSENTRY32();
uint hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnap == INVALID_HANDLE_VALUE)
{
return false;
}
procEntry.dwSize = (uint) Marshal.SizeOf(procEntry); //sizeof(PROCESSENTRY32);
if (Process32First(hSnap, ref procEntry) == 0)
{
return false;
}
String strCmp = "explorer.exe";
do
{
if (strCmp.IndexOf(procEntry.szExeFile) == 0)
{
// We found a winlogon process...make sure it's running in the console session
uint winlogonSessId = 0;
if (ProcessIdToSessionId(procEntry.th32ProcessID, ref winlogonSessId) &&
winlogonSessId == dwSessionId)
{
winlogonPid = procEntry.th32ProcessID;
break;
}
}
}
while (Process32Next(hSnap, ref procEntry) != 0);
//Get the user token used by DuplicateTokenEx
WTSQueryUserToken(dwSessionId, ref hUserToken);
var si = new STARTUPINFO();
si.cb = Marshal.SizeOf(si);
si.lpDesktop = "winsta0\\default";
var tp = new TOKEN_PRIVILEGES();
var luid = new LUID();
hProcess = OpenProcess(MAXIMUM_ALLOWED, false, winlogonPid);
if (
!OpenProcessToken(hProcess,
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY
| TOKEN_ADJUST_SESSIONID | TOKEN_READ | TOKEN_WRITE, ref hPToken))
{
Debug.Print(String.Format("CreateProcessInConsoleSession OpenProcessToken error: {0}",
Marshal.GetLastWin32Error()));
}
if (!LookupPrivilegeValue(IntPtr.Zero, SE_DEBUG_NAME, ref luid))
{
Debug.Print(String.Format("CreateProcessInConsoleSession LookupPrivilegeValue error: {0}",
Marshal.GetLastWin32Error()));
}
var sa = new SECURITY_ATTRIBUTES();
sa.Length = Marshal.SizeOf(sa);
if (!DuplicateTokenEx(hPToken, MAXIMUM_ALLOWED, ref sa,
(int) SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, (int) TOKEN_TYPE.TokenPrimary,
ref hUserTokenDup))
{
Debug.Print(
String.Format(
"CreateProcessInConsoleSession DuplicateTokenEx error: {0} Token does not have the privilege.",
Marshal.GetLastWin32Error()));
CloseHandle(hProcess);
CloseHandle(hUserToken);
CloseHandle(hPToken);
return false;
}
if (bElevate)
{
//tp.Privileges[0].Luid = luid;
//tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
tp.PrivilegeCount = 1;
tp.Privileges = new int[3];
tp.Privileges[2] = SE_PRIVILEGE_ENABLED;
tp.Privileges[1] = luid.HighPart;
tp.Privileges[0] = luid.LowPart;
//Adjust Token privilege
if (
!SetTokenInformation(hUserTokenDup, TOKEN_INFORMATION_CLASS.TokenSessionId, ref dwSessionId,
(uint) IntPtr.Size))
{
Debug.Print(
String.Format(
"CreateProcessInConsoleSession SetTokenInformation error: {0} Token does not have the privilege.",
Marshal.GetLastWin32Error()));
//CloseHandle(hProcess);
//CloseHandle(hUserToken);
//CloseHandle(hPToken);
//CloseHandle(hUserTokenDup);
//return false;
}
if (
!AdjustTokenPrivileges(hUserTokenDup, false, ref tp, Marshal.SizeOf(tp), /*(PTOKEN_PRIVILEGES)*/
IntPtr.Zero, IntPtr.Zero))
{
int nErr = Marshal.GetLastWin32Error();
if (nErr == ERROR_NOT_ALL_ASSIGNED)
{
Debug.Print(
String.Format(
"CreateProcessInConsoleSession AdjustTokenPrivileges error: {0} Token does not have the privilege.",
nErr));
}
else
{
Debug.Print(String.Format("CreateProcessInConsoleSession AdjustTokenPrivileges error: {0}", nErr));
}
}
}
uint dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;
IntPtr pEnv = IntPtr.Zero;
if (CreateEnvironmentBlock(ref pEnv, hUserTokenDup, true))
{
dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
}
else
{
pEnv = IntPtr.Zero;
}
// Launch the process in the client's logon session.
bResult = CreateProcessAsUser(hUserTokenDup, // client's access token
null, // file to execute
CommandLine, // command line
ref sa, // pointer to process SECURITY_ATTRIBUTES
ref sa, // pointer to thread SECURITY_ATTRIBUTES
false, // handles are not inheritable
(int) dwCreationFlags, // creation flags
pEnv, // pointer to new environment block
null, // name of current directory
ref si, // pointer to STARTUPINFO structure
out pi // receives information about new process
);
// End impersonation of client.
//GetLastError should be 0
int iResultOfCreateProcessAsUser = Marshal.GetLastWin32Error();
//Close handles task
CloseHandle(hProcess);
CloseHandle(hUserToken);
CloseHandle(hUserTokenDup);
CloseHandle(hPToken);
return (iResultOfCreateProcessAsUser == 0) ? true : false;
}
[DllImport("kernel32.dll")]
private static extern int Process32First(uint hSnapshot, ref PROCESSENTRY32 lppe);
[DllImport("kernel32.dll")]
private static extern int Process32Next(uint hSnapshot, ref PROCESSENTRY32 lppe);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern uint CreateToolhelp32Snapshot(uint dwFlags, uint th32ProcessID);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool CloseHandle(IntPtr hSnapshot);
[DllImport("kernel32.dll")]
private static extern uint WTSGetActiveConsoleSessionId();
[DllImport("Wtsapi32.dll")]
private static extern uint WTSQueryUserToken(uint SessionId, ref IntPtr phToken);
[DllImport("kernel32.dll")]
private static extern bool ProcessIdToSessionId(uint dwProcessId, ref uint pSessionId);
[DllImport("kernel32.dll")]
private static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, uint dwProcessId);
[DllImport("advapi32", SetLastError = true)]
[SuppressUnmanagedCodeSecurity]
private static extern bool OpenProcessToken(IntPtr ProcessHandle, // handle to process
int DesiredAccess, // desired access to process
ref IntPtr TokenHandle);
#region Nested type: LUID
[StructLayout(LayoutKind.Sequential)]
internal struct LUID
{
public int LowPart;
public int HighPart;
}
#endregion
//end struct
#region Nested type: LUID_AND_ATRIBUTES
[StructLayout(LayoutKind.Sequential)]
internal struct LUID_AND_ATRIBUTES
{
public LUID Luid;
public int Attributes;
}
#endregion
#region Nested type: PROCESSENTRY32
[StructLayout(LayoutKind.Sequential)]
private struct PROCESSENTRY32
{
public uint dwSize;
public readonly uint cntUsage;
public readonly uint th32ProcessID;
public readonly IntPtr th32DefaultHeapID;
public readonly uint th32ModuleID;
public readonly uint cntThreads;
public readonly uint th32ParentProcessID;
public readonly int pcPriClassBase;
public readonly uint dwFlags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public readonly string szExeFile;
}
#endregion
#region Nested type: PROCESS_INFORMATION
[StructLayout(LayoutKind.Sequential)]
public struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public uint dwProcessId;
public uint dwThreadId;
}
#endregion
#region Nested type: SECURITY_ATTRIBUTES
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public int Length;
public IntPtr lpSecurityDescriptor;
public bool bInheritHandle;
}
#endregion
#region Nested type: SECURITY_IMPERSONATION_LEVEL
private enum SECURITY_IMPERSONATION_LEVEL
{
SecurityAnonymous = 0,
SecurityIdentification = 1,
SecurityImpersonation = 2,
SecurityDelegation = 3,
}
#endregion
#region Nested type: STARTUPINFO
[StructLayout(LayoutKind.Sequential)]
public struct STARTUPINFO
{
public int cb;
public String lpReserved;
public String lpDesktop;
public String lpTitle;
public uint dwX;
public uint dwY;
public uint dwXSize;
public uint dwYSize;
public uint dwXCountChars;
public uint dwYCountChars;
public uint dwFillAttribute;
public uint dwFlags;
public short wShowWindow;
public short cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
#endregion
#region Nested type: TOKEN_PRIVILEGES
[StructLayout(LayoutKind.Sequential)]
internal struct TOKEN_PRIVILEGES
{
internal int PrivilegeCount;
//LUID_AND_ATRIBUTES
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
internal int[] Privileges;
}
#endregion
#region Nested type: TOKEN_TYPE
private enum TOKEN_TYPE
{
TokenPrimary = 1,
TokenImpersonation = 2
}
#endregion
// handle to open access token
}
As is so common with these types of questions about Windows services, you're operating in the mindset of a single-user operating system. The whole reason you decided to write your app as a service was because you were running into conflicts between your mental model of a single-user OS and the reality of a multi-user OS. Unfortunately, a service didn't solve all of your problems and now you're trying to figure out how to accomplish step two in the ultimately-doomed hacked design.
The fact is, you cannot be guaranteed that there is a "logged on user". If no one has logged on to the workstation, there will be no one logged on, yet your service will still be running.
Even if you somehow got past this by ensuring that someone will always be logged on (impossible), then you would run into the situation where multiple users are logged on. Then which one should your service start the process as? Should it just pick one of them randomly?
And is it necessary in your case to distinguish between users logged on locally to the console and those who are logged on remotely? Remember that remote users won't have a local console.
If you could somehow get past all of these hurdles (unfortunately, probably by burying your head in the sand and continuing to pretend that Windows is a single-user OS), you could make use of the WTSGetActiveConsoleSessionId function to obtain the current session ID, the WTSQueryUserToken function to obtain the user token corresponding to that session ID, and then finally the CreateProcessAsUser function to launch your process in the context of that user. If there is one. And they have the appropriate privileges. And the physical console is not attached to a dummy session. And you're not running a server SKU that allows multiple active console sessions. And…
If you could decide on a particular user whose account you wish to use to start the auxiliary process, you could log on that user, manipulate their user token, execute the process, and finally close the process and log out the user. The CreateProcessWithLogonUser function wraps up a lot of this drudgery for you, making the code a lot more svelte. But appearances can be deceiving, and this still has some massive security implications that you probably do not completely understand if you're asking this question in the first place. And you really cannot afford to not understand security risks like this.
Besides, users that are logged in with LogonUser (which is done for you automatically when you use the CreateProcessWithLogonUser function) lack a window station and desktop on which they can launch interactive processes. So if the process you wish to launch in the context of that user will show any kind of UI, you're out of luck. Windows will kill your app as soon as it tries to access a desktop for which it lacks the requisite permissions. There is no way, from a Windows service, to obtain the handle of a desktop that will be useful to you (which goes a long way towards explaining the general rule you probably already know, that services cannot display any type of UI).

DeviceIOControl function have exception when that return mft

I write this class for access volumedata.
class GetFiles
{
public void getlist()
{
NTFS_VOLUME_DATA_BUFFER volumData=new NTFS_VOLUME_DATA_BUFFER();
uint Resultsize=0;
IntPtr hroot = GetFiles.CreateFile(#"\\.\C:\", 0,
pinvok.FILE_SHARE_READ | pinvok.FILE_SHARE_WRITE,
IntPtr.Zero,
pinvok.OPEN_EXISTING,
pinvok.FILE_FLAG_BACKUP_SEMANTICS,
IntPtr.Zero);
GetFiles.DeviceIoControl(hroot,
pinvok.FSCTL_GET_NTFS_VOLUME_DATA,
IntPtr.Zero,0,
out volumData,(uint)Marshal.SizeOf(volumData),
out Resultsize,IntPtr.Zero
);
}
public struct pinvok
{
public const UInt32 FILE_SHARE_READ = 0x00000001;
public const UInt32 FILE_SHARE_WRITE = 0x00000002;
public const UInt32 FILE_ATTRIBUTE_DIRECTORY = 0x00000010;
public const UInt32 OPEN_EXISTING = 3;
public const UInt32 FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
public const Int32 INVALID_HANDLE_VALUE = -1;
public const UInt32 FSCTL_QUERY_USN_JOURNAL = 0x000900f4;
public const UInt32 FSCTL_ENUM_USN_DATA = 0x000900b3;
public const UInt32 FSCTL_CREATE_USN_JOURNAL = 0x000900e7;
public const UInt32 FSCTL_GET_NTFS_VOLUME_DATA = 0x00090064;
}
[StructLayout(LayoutKind.Auto,CharSet=CharSet.Unicode)]
public struct NTFS_VOLUME_DATA_BUFFER
{
public long VolumeSerialNumber;
public long NumberSectors;
public long TotalClusters;
public long FreeClusters;
public long TotalReserved;
public uint BytesPerSector;
public uint BytesPerCluster;
public uint BytesPerFileRecordSegment;
public uint ClustersPerFileRecordSegment;
public long MftValidDataLength;
public long MftStartLcn;
public long Mft2StartLcn;
public long MftZoneStart;
public long MftZoneEnd;
}
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess,
uint dwShareMode, IntPtr lpSecurityAttributes,
uint dwCreationDisposition, uint dwFlagsAndAttributes,
IntPtr hTemplateFile);
[DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DeviceIoControl(IntPtr hDevice,
uint dwIoControlCode, IntPtr lpInBuffer, uint nInBufferSize,
out NTFS_VOLUME_DATA_BUFFER lpOutBuffer, uint nOutBufferSize,
out uint lpBytesReturned, IntPtr lpOverlapped);
}
I encountered exception:
Type 'AccessMFT.GetFiles+NTFS_VOLUME_DATA_BUFFER' cannot be marshaled as an unmanaged structure; no meaningful size or offset can be computed.
According to the documentation of the LayoutKind.Auto enumeration value (emphasis mine):
The runtime automatically chooses an appropriate layout for the members of an object in unmanaged memory. Objects defined with this enumeration member cannot be exposed outside of managed code. Attempting to do so generates an exception.
You should probably use LayoutKind.Sequential instead.

How can I manipulate token privileges in .Net?

I'd like to use C# to determine which privileges are assigned to my process/thread token, and adjust them as necessary. For example, in order for my program to restart the computer, it must first enable the SeShutdownPrivilege privilege.
How can that be done safely from managed code?
This turns out to be non-trivial because there's no built-in mechanism for it. Not only is P/Invoke required, but you must code carefully to make sure that you don't "leak" privileges by enabling them and then not disabling them soon enough (though not an issue if you're restarting the computer).
For a complete code sample with description, read the MSDN magazine article from March 2005 "Manipulate Privileges in Managed Code Reliably, Securely, and Efficiently" by Mark Novak.
Here's the P/Invoke declarations:
using System;
using System.Runtime.InteropServices;
using System.Runtime.ConstrainedExecution;
namespace PrivilegeClass
{
[Flags]
internal enum TokenAccessLevels
{
AssignPrimary = 0x00000001,
Duplicate = 0x00000002,
Impersonate = 0x00000004,
Query = 0x00000008,
QuerySource = 0x00000010,
AdjustPrivileges = 0x00000020,
AdjustGroups = 0x00000040,
AdjustDefault = 0x00000080,
AdjustSessionId = 0x00000100,
Read = 0x00020000 | Query,
Write = 0x00020000 | AdjustPrivileges | AdjustGroups | AdjustDefault,
AllAccess = 0x000F0000 |
AssignPrimary |
Duplicate |
Impersonate |
Query |
QuerySource |
AdjustPrivileges |
AdjustGroups |
AdjustDefault |
AdjustSessionId,
MaximumAllowed = 0x02000000
}
internal enum SecurityImpersonationLevel
{
Anonymous = 0,
Identification = 1,
Impersonation = 2,
Delegation = 3,
}
internal enum TokenType
{
Primary = 1,
Impersonation = 2,
}
internal sealed class NativeMethods
{
internal const uint SE_PRIVILEGE_DISABLED = 0x00000000;
internal const uint SE_PRIVILEGE_ENABLED = 0x00000002;
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
internal struct LUID
{
internal uint LowPart;
internal uint HighPart;
}
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
internal struct LUID_AND_ATTRIBUTES
{
internal LUID Luid;
internal uint Attributes;
}
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
internal struct TOKEN_PRIVILEGE
{
internal uint PrivilegeCount;
internal LUID_AND_ATTRIBUTES Privilege;
}
internal const string ADVAPI32 = "advapi32.dll";
internal const string KERNEL32 = "kernel32.dll";
internal const int ERROR_SUCCESS = 0x0;
internal const int ERROR_ACCESS_DENIED = 0x5;
internal const int ERROR_NOT_ENOUGH_MEMORY = 0x8;
internal const int ERROR_NO_TOKEN = 0x3f0;
internal const int ERROR_NOT_ALL_ASSIGNED = 0x514;
internal const int ERROR_NO_SUCH_PRIVILEGE = 0x521;
internal const int ERROR_CANT_OPEN_ANONYMOUS = 0x543;
[DllImport(
KERNEL32,
SetLastError=true)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
internal static extern bool CloseHandle(IntPtr handle);
[DllImport(
ADVAPI32,
CharSet=CharSet.Unicode,
SetLastError=true)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
internal static extern bool AdjustTokenPrivileges (
[In] SafeTokenHandle TokenHandle,
[In] bool DisableAllPrivileges,
[In] ref TOKEN_PRIVILEGE NewState,
[In] uint BufferLength,
[In,Out] ref TOKEN_PRIVILEGE PreviousState,
[In,Out] ref uint ReturnLength);
[DllImport(
ADVAPI32,
CharSet=CharSet.Auto,
SetLastError=true)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
internal static extern
bool RevertToSelf();
[DllImport(
ADVAPI32,
EntryPoint="LookupPrivilegeValueW",
CharSet=CharSet.Auto,
SetLastError=true)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
internal static extern
bool LookupPrivilegeValue (
[In] string lpSystemName,
[In] string lpName,
[In,Out] ref LUID Luid);
[DllImport(
KERNEL32,
CharSet=CharSet.Auto,
SetLastError=true)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
internal static extern
IntPtr GetCurrentProcess();
[DllImport(
KERNEL32,
CharSet=CharSet.Auto,
SetLastError=true)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
internal static extern
IntPtr GetCurrentThread ();
[DllImport(
ADVAPI32,
CharSet=CharSet.Unicode,
SetLastError=true)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
internal static extern
bool OpenProcessToken (
[In] IntPtr ProcessToken,
[In] TokenAccessLevels DesiredAccess,
[In,Out] ref SafeTokenHandle TokenHandle);
[DllImport
(ADVAPI32,
CharSet=CharSet.Unicode,
SetLastError=true)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
internal static extern
bool OpenThreadToken(
[In] IntPtr ThreadToken,
[In] TokenAccessLevels DesiredAccess,
[In] bool OpenAsSelf,
[In,Out] ref SafeTokenHandle TokenHandle);
[DllImport
(ADVAPI32,
CharSet=CharSet.Unicode,
SetLastError=true)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
internal static extern
bool DuplicateTokenEx(
[In] SafeTokenHandle ExistingToken,
[In] TokenAccessLevels DesiredAccess,
[In] IntPtr TokenAttributes,
[In] SecurityImpersonationLevel ImpersonationLevel,
[In] TokenType TokenType,
[In,Out] ref SafeTokenHandle NewToken);
[DllImport
(ADVAPI32,
CharSet=CharSet.Unicode,
SetLastError=true)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
internal static extern
bool SetThreadToken(
[In] IntPtr Thread,
[In] SafeTokenHandle Token);
static NativeMethods()
{
}
}
}
Here's what I use. It is based off of the Mark Novak article, but with less paranoia for untrusted stack frames, CER's, or reentrance (since I assume you are not writing internet explorer or a SQL Server Add-in).
Privilege.cs:
using System;
using Microsoft.Win32.SafeHandles;
using System.Collections.Specialized;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.ConstrainedExecution;
using System.Threading;
using Luid = Esatto.Win32.Processes.NativeMethods.LUID;
using Win32Exception = System.ComponentModel.Win32Exception;
using PrivilegeNotHeldException = System.Security.AccessControl.PrivilegeNotHeldException;
using static Esatto.Win32.Processes.NativeMethods;
using System.Linq;
using System.Security.Principal;
// http://msdn.microsoft.com/en-us/magazine/cc163823.aspx
namespace Esatto.Win32.Processes
{
public static class Privilege
{
#region Privilege names
public const string
CreateToken = "SeCreateTokenPrivilege",
AssignPrimaryToken = "SeAssignPrimaryTokenPrivilege",
LockMemory = "SeLockMemoryPrivilege",
IncreaseQuota = "SeIncreaseQuotaPrivilege",
UnsolicitedInput = "SeUnsolicitedInputPrivilege",
MachineAccount = "SeMachineAccountPrivilege",
TrustedComputingBase = "SeTcbPrivilege",
Security = "SeSecurityPrivilege",
TakeOwnership = "SeTakeOwnershipPrivilege",
LoadDriver = "SeLoadDriverPrivilege",
SystemProfile = "SeSystemProfilePrivilege",
SystemTime = "SeSystemtimePrivilege",
ProfileSingleProcess = "SeProfileSingleProcessPrivilege",
IncreaseBasePriority = "SeIncreaseBasePriorityPrivilege",
CreatePageFile = "SeCreatePagefilePrivilege",
CreatePermanent = "SeCreatePermanentPrivilege",
Backup = "SeBackupPrivilege",
Restore = "SeRestorePrivilege",
Shutdown = "SeShutdownPrivilege",
Debug = "SeDebugPrivilege",
Audit = "SeAuditPrivilege",
SystemEnvironment = "SeSystemEnvironmentPrivilege",
ChangeNotify = "SeChangeNotifyPrivilege",
RemoteShutdown = "SeRemoteShutdownPrivilege",
Undock = "SeUndockPrivilege",
SyncAgent = "SeSyncAgentPrivilege",
EnableDelegation = "SeEnableDelegationPrivilege",
ManageVolume = "SeManageVolumePrivilege",
Impersonate = "SeImpersonatePrivilege",
CreateGlobal = "SeCreateGlobalPrivilege",
TrustedCredentialManagerAccess = "SeTrustedCredManAccessPrivilege",
ReserveProcessor = "SeReserveProcessorPrivilege";
#endregion
public static void RunWithPrivileges(Action action, params string[] privs)
{
if (privs == null || privs.Length == 0)
{
throw new ArgumentNullException(nameof(privs));
}
var luids = privs
.Select(e => new LUID_AND_ATTRIBUTES { Luid = GetLuidForName(e), Attributes = SE_PRIVILEGE_ENABLED })
.ToArray();
RuntimeHelpers.PrepareConstrainedRegions();
try { /* CER */ }
finally
{
using (var threadToken = new ThreadTokenScope())
using (new ThreadPrivilegeScope(threadToken, luids))
{
action();
}
}
}
private static LUID_AND_ATTRIBUTES[] AdjustTokenPrivileges2(SafeTokenHandle token, LUID_AND_ATTRIBUTES[] attrs)
{
var sizeofAttr = Marshal.SizeOf<LUID_AND_ATTRIBUTES>();
var pDesired = Marshal.AllocHGlobal(4 /* count */ + attrs.Length * sizeofAttr);
try
{
// Fill pStruct
{
Marshal.WriteInt32(pDesired, attrs.Length);
var pAttr = pDesired + 4;
for (int i = 0; i < attrs.Length; i++)
{
Marshal.StructureToPtr(attrs[i], pAttr, false);
pAttr += sizeofAttr;
}
}
// Call Adjust
const int cbPrevious = 16384 /* some arbitrarily high number */;
var pPrevious = Marshal.AllocHGlobal(cbPrevious);
try
{
if (!AdjustTokenPrivileges(token, false, pDesired, cbPrevious, pPrevious, out var retLen))
{
throw new Win32Exception();
}
// Parse result
{
var result = new LUID_AND_ATTRIBUTES[Marshal.ReadInt32(pPrevious)];
var pAttr = pPrevious + 4;
for (int i = 0; i < result.Length; i++)
{
result[i] = Marshal.PtrToStructure<LUID_AND_ATTRIBUTES>(pAttr);
}
return result;
}
}
finally { Marshal.FreeHGlobal(pPrevious); }
}
finally { Marshal.FreeHGlobal(pDesired); }
}
private static Luid GetLuidForName(string priv)
{
if (!LookupPrivilegeValue(null, priv, out var result))
{
throw new Win32Exception();
}
return result;
}
private class ThreadPrivilegeScope : IDisposable
{
private LUID_AND_ATTRIBUTES[] RevertTo;
private Thread OwnerThread;
private readonly ThreadTokenScope Token;
public ThreadPrivilegeScope(ThreadTokenScope token, LUID_AND_ATTRIBUTES[] setTo)
{
this.OwnerThread = Thread.CurrentThread;
this.Token = token ?? throw new ArgumentNullException(nameof(token));
this.RevertTo = AdjustTokenPrivileges2(token.Handle, setTo);
}
public void Dispose()
{
if (OwnerThread != Thread.CurrentThread)
{
throw new InvalidOperationException("Wrong thread");
}
if (RevertTo == null)
{
return;
}
AdjustTokenPrivileges2(Token.Handle, RevertTo);
}
}
private class ThreadTokenScope : IDisposable
{
private bool IsImpersonating;
private readonly Thread OwnerThread;
public readonly SafeTokenHandle Handle;
[ThreadStatic]
private static ThreadTokenScope Current;
public ThreadTokenScope()
{
if (Current != null)
{
throw new InvalidOperationException("Reentrance to ThreadTokenScope");
}
this.OwnerThread = Thread.CurrentThread;
if (!OpenThreadToken(GetCurrentThread(), TokenAccessLevels.Query | TokenAccessLevels.AdjustPrivileges, true, out var token))
{
var error = Marshal.GetLastWin32Error();
if (error != ERROR_NO_TOKEN)
{
throw new Win32Exception(error);
}
// No token is on the thread, copy from process
if (!OpenProcessToken(GetCurrentProcess(), TokenAccessLevels.Duplicate, out var processToken))
{
throw new Win32Exception();
}
if (!DuplicateTokenEx(processToken, TokenAccessLevels.Impersonate | TokenAccessLevels.Query | TokenAccessLevels.AdjustPrivileges,
IntPtr.Zero, SecurityImpersonationLevel.Impersonation, TokenType.Impersonation, out token))
{
throw new Win32Exception();
}
if (!SetThreadToken(IntPtr.Zero, token))
{
throw new Win32Exception();
}
this.IsImpersonating = true;
}
this.Handle = token;
Current = this;
}
public void Dispose()
{
if (OwnerThread != Thread.CurrentThread)
{
throw new InvalidOperationException("Wrong thread");
}
if (Current != this)
{
throw new ObjectDisposedException(nameof(ThreadTokenScope));
}
Current = null;
if (IsImpersonating)
{
RevertToSelf();
}
IsImpersonating = false;
}
}
}
}
NativeMethods.cs:
using Microsoft.Win32.SafeHandles;
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Text;
namespace Esatto.Win32.Processes
{
internal static class NativeMethods
{
const string Advapi32 = "advapi32.dll";
const string Kernel32 = "kernel32.dll";
const string Wtsapi32 = "wtsapi32.dll";
const string Userenv = "userenv.dll";
#region constants
public const uint JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE = 0x00002000;
public const uint SE_PRIVILEGE_DISABLED = 0x00000000;
public const uint SE_PRIVILEGE_ENABLED = 0x00000002;
public const int ERROR_SUCCESS = 0x0;
public const int ERROR_ACCESS_DENIED = 0x5;
public const int ERROR_NOT_ENOUGH_MEMORY = 0x8;
public const int ERROR_NO_TOKEN = 0x3f0;
public const int ERROR_NOT_ALL_ASSIGNED = 0x514;
public const int ERROR_NO_SUCH_PRIVILEGE = 0x521;
public const int ERROR_CANT_OPEN_ANONYMOUS = 0x543;
public const uint STANDARD_RIGHTS_REQUIRED = 0x000F0000;
public const uint STANDARD_RIGHTS_READ = 0x00020000;
public const uint NORMAL_PRIORITY_CLASS = 0x0020;
public const uint CREATE_UNICODE_ENVIRONMENT = 0x00000400;
public const uint MAX_PATH = 260;
public const uint CREATE_NO_WINDOW = 0x08000000;
public const uint INFINITE = 0xFFFFFFFF;
#endregion
#region Advapi32
[DllImport(Advapi32, ExactSpelling = true, SetLastError = true)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
public static extern bool AdjustTokenPrivileges(SafeTokenHandle TokenHandle, bool DisableAllPrivileges,
IntPtr NewState, uint BufferLength, IntPtr PreviousState, out uint ReturnLength);
[DllImport(Advapi32, ExactSpelling = true, SetLastError = true)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
public static extern bool RevertToSelf();
[DllImport(Advapi32, CharSet = CharSet.Unicode, SetLastError = true)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
public static extern bool LookupPrivilegeValue(string lpSystemName, string lpName, out LUID Luid);
[DllImport(Advapi32, ExactSpelling = true, SetLastError = true)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
public static extern bool OpenProcessToken(IntPtr ProcessToken, TokenAccessLevels DesiredAccess, out SafeTokenHandle TokenHandle);
[DllImport(Advapi32, ExactSpelling = true, SetLastError = true)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
public static extern bool OpenThreadToken(IntPtr ThreadToken, TokenAccessLevels DesiredAccess, bool OpenAsSelf, out SafeTokenHandle TokenHandle);
[DllImport(Advapi32, ExactSpelling = true, SetLastError = true)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
public static extern bool DuplicateTokenEx(SafeTokenHandle ExistingToken, TokenAccessLevels DesiredAccess,
IntPtr TokenAttributes, SecurityImpersonationLevel ImpersonationLevel, TokenType TokenType, out SafeTokenHandle NewToken);
[DllImport(Advapi32, ExactSpelling = true, SetLastError = true)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
public static extern bool SetThreadToken(IntPtr Thread, SafeTokenHandle Token);
[DllImport(Advapi32, CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool CreateProcessAsUser(SafeTokenHandle hToken,
StringBuilder appExeName, StringBuilder commandLine, IntPtr processAttributes,
IntPtr threadAttributes, bool inheritHandles, uint dwCreationFlags,
EnvironmentBlockSafeHandle environment, string currentDirectory, ref STARTUPINFO startupInfo,
out PROCESS_INFORMATION startupInformation);
[DllImport(Advapi32, CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool GetTokenInformation(IntPtr TokenHandle,
TokenInformationClass TokenInformationClass, out int TokenInformation,
uint TokenInformationLength, out uint ReturnLength);
#endregion
#region Kernel32
[DllImport(Kernel32, ExactSpelling = true, SetLastError = true)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
public static extern bool CloseHandle(IntPtr handle);
[DllImport(Kernel32, ExactSpelling = true, SetLastError = true)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
public static extern IntPtr GetCurrentProcess();
[DllImport(Kernel32, ExactSpelling = true, SetLastError = true)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
public static extern IntPtr GetCurrentThread();
[DllImport(Kernel32, CharSet = CharSet.Auto, SetLastError = true)]
public static extern SafeJobHandle CreateJobObject(IntPtr lpJobAttributes, string lpName);
[DllImport(Kernel32, SetLastError = true)]
public static extern bool SetInformationJobObject(SafeJobHandle hJob, JobObjectInfoType infoType,
ref JOBOBJECT_EXTENDED_LIMIT_INFORMATION lpJobObjectInfo, int cbJobObjectInfoLength);
[DllImport(Kernel32, SetLastError = true)]
public static extern bool AssignProcessToJobObject(SafeJobHandle job, IntPtr process);
#endregion
#region Wtsapi
[DllImport(Wtsapi32, ExactSpelling = true, SetLastError = true)]
public static extern bool WTSQueryUserToken(int sessionid, out SafeTokenHandle handle);
#endregion
#region Userenv
[DllImport(Userenv, CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool CreateEnvironmentBlock(out EnvironmentBlockSafeHandle lpEnvironment, SafeTokenHandle hToken, bool bInherit);
[DllImport(Userenv, ExactSpelling = true, SetLastError = true)]
public extern static bool DestroyEnvironmentBlock(IntPtr hEnvironment);
#endregion
#region Structs
[StructLayout(LayoutKind.Sequential)]
public struct IO_COUNTERS
{
public ulong ReadOperationCount;
public ulong WriteOperationCount;
public ulong OtherOperationCount;
public ulong ReadTransferCount;
public ulong WriteTransferCount;
public ulong OtherTransferCount;
}
[StructLayout(LayoutKind.Sequential)]
public struct JOBOBJECT_BASIC_LIMIT_INFORMATION
{
public long PerProcessUserTimeLimit;
public long PerJobUserTimeLimit;
public uint LimitFlags;
public UIntPtr MinimumWorkingSetSize;
public UIntPtr MaximumWorkingSetSize;
public uint ActiveProcessLimit;
public UIntPtr Affinity;
public uint PriorityClass;
public uint SchedulingClass;
}
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public uint nLength;
public IntPtr lpSecurityDescriptor;
public int bInheritHandle;
}
[StructLayout(LayoutKind.Sequential)]
public struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION
{
public JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation;
public IO_COUNTERS IoInfo;
public UIntPtr ProcessMemoryLimit;
public UIntPtr JobMemoryLimit;
public UIntPtr PeakProcessMemoryUsed;
public UIntPtr PeakJobMemoryUsed;
}
[StructLayout(LayoutKind.Sequential)]
internal struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public uint dwProcessId;
public uint dwThreadId;
}
[StructLayout(LayoutKind.Sequential)]
internal struct STARTUPINFO
{
public int cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public int dwX;
public int dwY;
public int dwXSize;
public int dwYSize;
public int dwXCountChars;
public int dwYCountChars;
public int dwFillAttribute;
public int dwFlags;
public short wShowWindow;
public short cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
public struct LUID
{
public uint LowPart;
public uint HighPart;
}
[StructLayout(LayoutKind.Sequential)]
public struct LUID_AND_ATTRIBUTES
{
public LUID Luid;
public uint Attributes;
}
[StructLayout(LayoutKind.Sequential)]
public struct TOKEN_PRIVILEGE
{
public uint PrivilegeCount;
public LUID_AND_ATTRIBUTES Privilege;
}
#endregion
#region Enums
public enum JobObjectInfoType
{
AssociateCompletionPortInformation = 7,
BasicLimitInformation = 2,
BasicUIRestrictions = 4,
EndOfJobTimeInformation = 6,
ExtendedLimitInformation = 9,
SecurityLimitInformation = 5,
GroupInformation = 11
}
public enum SecurityImpersonationLevel
{
Anonymous = 0,
Identification = 1,
Impersonation = 2,
Delegation = 3,
}
public enum TokenType
{
Primary = 1,
Impersonation = 2,
}
public enum TokenInformationClass
{
TokenUser = 1,
TokenGroups,
TokenPrivileges,
TokenOwner,
TokenPrimaryGroup,
TokenDefaultDacl,
TokenSource,
TokenType,
TokenImpersonationLevel,
TokenStatistics,
TokenRestrictedSids,
TokenSessionId,
TokenGroupsAndPrivileges,
TokenSessionReference,
TokenSandBoxInert,
TokenAuditPolicy,
TokenOrigin,
TokenElevationType,
TokenLinkedToken,
TokenElevation,
TokenHasRestrictions,
TokenAccessInformation,
TokenVirtualizationAllowed,
TokenVirtualizationEnabled,
TokenIntegrityLevel,
TokenUIAccess,
TokenMandatoryPolicy,
TokenLogonSid,
TokenIsAppContainer,
TokenCapabilities,
TokenAppContainerSid,
TokenAppContainerNumber,
TokenUserClaimAttributes,
TokenDeviceClaimAttributes,
TokenRestrictedUserClaimAttributes,
TokenRestrictedDeviceClaimAttributes,
TokenDeviceGroups,
TokenRestrictedDeviceGroups,
// MaxTokenInfoClass should always be the last enum
MaxTokenInfoClass
}
#endregion
#region SafeHandles
public sealed class EnvironmentBlockSafeHandle : SafeHandleZeroOrMinusOneIsInvalid
{
public EnvironmentBlockSafeHandle()
: base(true)
{
}
protected override bool ReleaseHandle()
{
return DestroyEnvironmentBlock(handle);
}
}
public sealed class SafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid
{
public SafeTokenHandle()
: base(true)
{
}
override protected bool ReleaseHandle()
{
return CloseHandle(handle);
}
}
internal sealed class SafeJobHandle : SafeHandleZeroOrMinusOneIsInvalid
{
public SafeJobHandle()
: base(true)
{
}
protected override bool ReleaseHandle()
{
return CloseHandle(this.handle);
}
}
#endregion
}
}
Example Usage to create a process in another user's session:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Esatto.Win32.Processes
{
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Security.Principal;
using static NativeMethods;
#if ESATTO_WIN32
public
#else
internal
#endif
static class ProcessInterop
{
public static void CreateProcessForSession(int sessionId, string exePath, string commandLine)
{
var privs = new[] { Privilege.TrustedComputingBase, Privilege.AssignPrimaryToken, Privilege.IncreaseQuota };
Privilege.RunWithPrivileges(() => CreateProcessForSessionInternal(sessionId, exePath, commandLine), privs);
}
private static void CreateProcessForSessionInternal(int sessionId, string exePath, string commandLine)
{
SafeTokenHandle hDupToken;
{
SafeTokenHandle hToken;
if (!WTSQueryUserToken(sessionId, out hToken))
{
throw new Win32Exception();
}
using (hToken)
{
if (!DuplicateTokenEx(hToken, TokenAccessLevels.AllAccess, IntPtr.Zero, SecurityImpersonationLevel.Impersonation, TokenType.Primary, out hDupToken))
{
throw new Win32Exception();
}
}
}
using (hDupToken)
{
EnvironmentBlockSafeHandle env;
if (!CreateEnvironmentBlock(out env, hDupToken, false))
{
throw new Win32Exception();
}
using (env)
{
STARTUPINFO si = new STARTUPINFO();
si.cb = Marshal.SizeOf(si);
PROCESS_INFORMATION procInfo;
if (!CreateProcessAsUser(hDupToken, new StringBuilder(exePath), new StringBuilder(commandLine),
IntPtr.Zero, IntPtr.Zero, false, NORMAL_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT, env,
null, ref si, out procInfo))
{
throw new Win32Exception();
}
CloseHandle(procInfo.hProcess);
CloseHandle(procInfo.hThread);
}
}
}
public static int GetSessionId(this WindowsIdentity ident)
{
if (ident == null)
{
throw new ArgumentNullException();
}
int sessionId;
uint unused;
if (!GetTokenInformation(ident.Token, TokenInformationClass.TokenSessionId, out sessionId, 4, out unused))
{
throw new Win32Exception();
}
// since we are not passing a SafeHandle, we need to keep our reference on the SafeHandle
// contained in the WindowsIdentity
GC.KeepAlive(ident);
return sessionId;
}
}
}

Categories

Resources