Related
I am using LogonUser to get primary user token and then CreateProcessAsUser APIs to create process. But I am getting error code 6. Not sure what the problem is. Below is the code.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LogOnUserTestWindows
{
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Principal;
class Program
{
// Define the Windows LogonUser and CloseHandle functions.
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool LogonUser(String username, String domain, IntPtr password,
int logonType, int logonProvider, ref IntPtr token);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public extern static bool CloseHandle(IntPtr handle);
private enum SW
{
SW_HIDE = 0,
SW_SHOWNORMAL = 1,
SW_NORMAL = 1,
SW_SHOWMINIMIZED = 2,
SW_SHOWMAXIMIZED = 3,
SW_MAXIMIZE = 3,
SW_SHOWNOACTIVATE = 4,
SW_SHOW = 5,
SW_MINIMIZE = 6,
SW_SHOWMINNOACTIVE = 7,
SW_SHOWNA = 8,
SW_RESTORE = 9,
SW_SHOWDEFAULT = 10,
SW_MAX = 10
}
[StructLayout(LayoutKind.Sequential)]
private 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;
}
[StructLayout(LayoutKind.Sequential)]
private struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public uint dwProcessId;
public uint dwThreadId;
}
[DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUser", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
private static extern bool CreateProcessAsUser(
IntPtr hToken,
String lpApplicationName,
String lpCommandLine,
IntPtr lpProcessAttributes,
IntPtr lpThreadAttributes,
bool bInheritHandle,
uint dwCreationFlags,
IntPtr lpEnvironment,
String lpCurrentDirectory,
ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);
// Define the required LogonUser enumerations.
const int LOGON32_PROVIDER_DEFAULT = 0;
const int LOGON32_LOGON_INTERACTIVE = 2;
private const int CREATE_UNICODE_ENVIRONMENT = 0x00000400;
private const int CREATE_NO_WINDOW = 0x08000000;
private const int CREATE_NEW_CONSOLE = 0x00000010;
static void Main()
{
// Display the current user before impersonation.
Console.WriteLine("Before impersonation: {0}",
WindowsIdentity.GetCurrent().Name);
// Ask the user for a network domain.
Console.Write("Please enter your domain: ");
string domain = Console.ReadLine();
// Ask the user for a user name.
Console.Write("Please enter your user name: ");
string username = Console.ReadLine();
// Ask the user for a password.
Console.Write("Please enter your password: ");
SecureString passWord = GetPassword();
// Impersonate the account provided by the user.
try
{
//WindowsImpersonationContext userContext = ImpersonateUser(passWord, username, domain);
IntPtr token = ImpersonateUser(passWord, username, domain);
// Display the current user after impersonation.
Console.WriteLine("After impersonation: {0}",
WindowsIdentity.GetCurrent().Name);
}
catch (ArgumentException e)
{
Console.WriteLine("{0}: {1}", e.GetType().Name, e.Message);
}
catch (Win32Exception e)
{
Console.WriteLine("{0}: {1}", e.GetType().Name, e.Message);
}
finally
{
passWord.Dispose();
}
}
public static SecureString GetPassword()
{
SecureString password = new SecureString();
// get the first character of the password
ConsoleKeyInfo nextKey = Console.ReadKey(true);
while (nextKey.Key != ConsoleKey.Enter)
{
if (nextKey.Key == ConsoleKey.Backspace)
{
if (password.Length > 0)
{
password.RemoveAt(password.Length - 1);
// erase the last * as well
Console.Write(nextKey.KeyChar);
Console.Write(" ");
Console.Write(nextKey.KeyChar);
}
}
else
{
password.AppendChar(nextKey.KeyChar);
Console.Write("*");
}
nextKey = Console.ReadKey(true);
}
Console.WriteLine();
// lock the password down
password.MakeReadOnly();
return password;
}
public static IntPtr ImpersonateUser(SecureString password, string userName, string domainName)
{
IntPtr tokenHandle = IntPtr.Zero;
IntPtr passwordPtr = IntPtr.Zero;
bool returnValue = false;
int error = 0;
// Marshal the SecureString to unmanaged memory.
passwordPtr = Marshal.SecureStringToGlobalAllocUnicode(password);
// Pass LogonUser the unmanaged (and decrypted) copy of the password.
returnValue = LogonUser(userName, domainName, passwordPtr,
LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
ref tokenHandle);
if (!returnValue && tokenHandle == IntPtr.Zero)
error = Marshal.GetLastWin32Error();
// Perform cleanup whether or not the call succeeded.
// Zero-out and free the unmanaged string reference.
Marshal.ZeroFreeGlobalAllocUnicode(passwordPtr);
// Close the token handle.
CloseHandle(tokenHandle);
// Throw an exception if an error occurred.
if (error != 0)
{
throw new System.ComponentModel.Win32Exception(error);
}
// The token that is passed to the following constructor must
// be a primary token in order to use it for impersonation.
//WindowsIdentity newId = new WindowsIdentity(tokenHandle);
//String workgroup;
string cmdLine = null;
string workDir = null;
bool visible = true;
var pEnv = IntPtr.Zero;
var startInfo = new STARTUPINFO();
var procInfo = new PROCESS_INFORMATION();
int iResultOfCreateProcessAsUser;
uint dwCreationFlags = CREATE_UNICODE_ENVIRONMENT | (uint)(visible ? CREATE_NEW_CONSOLE : CREATE_NO_WINDOW);
startInfo.wShowWindow = (short)(visible ? SW.SW_SHOW : SW.SW_HIDE);
startInfo.lpDesktop = "winsta0\\default";
if (!CreateProcessAsUser(
tokenHandle,
"C:/Windows/System32/notepad.exe", // Application Name
cmdLine, // Command Line
IntPtr.Zero,
IntPtr.Zero,
false,
dwCreationFlags,
pEnv,
workDir, // Working directory
ref startInfo,
out procInfo))
{
iResultOfCreateProcessAsUser = Marshal.GetLastWin32Error();
throw new Exception("StartProcessAsCurrentUser: CreateProcessAsUser failed. Error Code -" + iResultOfCreateProcessAsUser);
}
return tokenHandle;
}
}
}
Below is the error
System.Exception
HResult=0x80131500
Message=StartProcessAsCurrentUser: CreateProcessAsUser failed. Error Code -6
Source=LogOnUserTestWindows
StackTrace:
at LogOnUserTestWindows.Program.ImpersonateUser(SecureString password, String userName, String domainName) in C:\Users\santosh\source\repos\LogOnUserTestWindows\LogOnUserTestWindows\Program.cs:line 242
at LogOnUserTestWindows.Program.Main() in C:\Users\santosh\source\repos\LogOnUserTestWindows\LogOnUserTestWindows\Program.cs:line 122
I am not able to find any proper documentation for both logonUser and CreateProcessAsUser APIs. I am trying to run this code on my machine which has multiple usets. I am logged in from one user and trying to create process from another user. It would be really great if someone can point me to proper documentation or examples. Please help. Thanks in Advance.
You closed tokenHandle before you passed it to CreateProcessAsUser.
Result: ERROR_INVALID_HANDLE (= 6).
Please try CreateProcessWithToken instead, although I don't know why the function returned error code -6, but a few days ago I tried to start up an application with specified user token, but CreateProcessAsUser didn't work, but CreateProcessWithToken did. Maybe it will also work for you, good luck.
I am trying to hook outgoing connections with c#. I found this question:
Windows Filtering Platform - How can I block incoming connections based on local port?
But at sample some error with code and some missing classes to use.
I am trying to generate structs with p/invoke signature toolkit, but I have some very big class-generated code, where 90% calling dependence.
So I think I must realise external prototypes and then call them.
Here is my code:
public static class WpfProvider
{
private static void Test()
{
var RemotePort = 8080; //port to block
// connect to engine
var session = new FWPM_SESSION0_();
session.flags = 0xFFF;
IntPtr engineHandle;
FwpmEngineOpen0(null, RPC.RPC_C_AUTHN_WINNT, IntPtr.Zero, session, ref engineHandle);
// create a subLayer to attach filters to
var subLayerGuid = Guid.NewGuid();
var subLayer = new FWPM_SUBLAYER0_();
subLayer.subLayerKey = subLayerGuid;
subLayer.displayData.name = DisplayName;
subLayer.displayData.description = DisplayName;
subLayer.flags = 0;
subLayer.weight = 0x100;
FwpmSubLayerAdd0(engineHandle, subLayer, IntPtr.Zero);
var condition = new FWPM_FILTER_CONDITION0
{
fieldKey = Fwpm.FWPM_CONDITION_IP_REMOTE_PORT,
matchType = Fwpm.FWP_MATCH_TYPE.FWP_MATCH_EQUAL,
conditionValue =
{
type = Fwpm.FWP_DATA_TYPE.FWP_UINT16,
uint16 = RemotePort
}
};
// create the filter itself
var fwpFilter = new FWPM_FILTER0();
fwpFilter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4;
fwpFilter.action.type = FWP_ACTION_BLOCK;
fwpFilter.subLayerKey = subLayerGuid;
fwpFilter.weight.type = Fwpm.FWP_DATA_TYPE.FWP_EMPTY; // auto-weight.
fwpFilter.numFilterConditions = (uint)1;
var condsArray = new[] { condition };
var condsPtr = SafeNativeMethods.MarshalArray(condsArray); // helper to create a native array from a C# one
fwpFilter.filterCondition = condsPtr;
fwpFilter.displayData.name = DisplayName;
fwpFilter.displayData.description = DisplayName;
Microsoft.Win32.UnsaveNativeMethods
// add the filter
UInt64 filterId = 0L;
FwpmFilterAdd0(engineHandle, ref fwpFilter, IntPtr.Zero, out filterId));
}
/// Return Type: DWORD->unsigned int
///serverName: wchar_t*
///authnService: UINT32->unsigned int
///authIdentity: SEC_WINNT_AUTH_IDENTITY_W*
///session: FWPM_SESSION0*
///engineHandle: HANDLE*
[System.Runtime.InteropServices.DllImportAttribute("FWPUClnt.dll", EntryPoint = "FwpmEngineOpen0")]
public static extern uint FwpmEngineOpen0([System.Runtime.InteropServices.In()] [System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)] string serverName, uint authnService, [System.Runtime.InteropServices.In()] IntPtr authIdentity, [System.Runtime.InteropServices.In()] IntPtr session, ref IntPtr engineHandle);
/// Return Type: DWORD->unsigned int
///engineHandle: HANDLE->void*
///subLayer: FWPM_SUBLAYER0*
///sd: PSECURITY_DESCRIPTOR->PVOID->void*
[System.Runtime.InteropServices.DllImportAttribute("FWPUClnt.dll", EntryPoint = "FwpmSubLayerAdd0")]
public static extern uint FwpmSubLayerAdd0([System.Runtime.InteropServices.In()] IntPtr engineHandle, [System.Runtime.InteropServices.In()] ref FWPM_SUBLAYER0 subLayer, [System.Runtime.InteropServices.In()] IntPtr sd);
/// Return Type: DWORD->unsigned int
///engineHandle: HANDLE->void*
///filter: FWPM_FILTER0*
///sd: PSECURITY_DESCRIPTOR->PVOID->void*
///id: UINT64*->UInt64
[System.Runtime.InteropServices.DllImportAttribute("FWPUClnt.dll", EntryPoint = "FwpmFilterAdd0")]
public static extern uint FwpmFilterAdd0([System.Runtime.InteropServices.In()] IntPtr engineHandle, [System.Runtime.InteropServices.In()] ref FWPM_FILTER0 filter, [System.Runtime.InteropServices.In()] IntPtr sd, UInt64 id);
}
static class RPC
{
public static uint RPC_C_AUTHN_NONE = 0;//No authentication.
public static uint RPC_C_AUTHN_DCE_PRIVATE = 1;//DCE private key authentication.
public static uint RPC_C_AUTHN_DCE_PUBLIC = 2;//DCE public key authentication.
public static uint RPC_C_AUTHN_DEC_PUBLIC = 4;//DEC public key authentication.Reserved for future use.
public static uint RPC_C_AUTHN_GSS_NEGOTIATE = 9;//Snego security support provider.
public static uint RPC_C_AUTHN_WINNT = 10;//NTLMSSP
public static uint RPC_C_AUTHN_GSS_SCHANNEL = 14;//Schannel security support provider. This authentication service supports SSL 2.0, SSL 3.0, TLS, and PCT.
public static uint RPC_C_AUTHN_GSS_KERBEROS = 16;//Kerberos security support provider.
public static uint RPC_C_AUTHN_DPA = 17;//DPA security support provider.
public static uint RPC_C_AUTHN_MSN = 18;//MSN security support provider.
public static uint RPC_C_AUTHN_KERNEL = 20;//Kernel security support provider.
public static uint RPC_C_AUTHN_DIGEST = 21;//Digest security support provider.
public static uint RPC_C_AUTHN_NEGO_EXTENDER = 30;//NEGO extender security support provider.
public static uint RPC_C_AUTHN_PKU2U = 31;//PKU2U security support provider.
public static uint RPC_C_AUTHN_MQ = 100;//MQ security support provider.
public static uint RPC_C_AUTHN_DEFAULT = 0xFFFFFFFF; //The system default authentication service. When this value is specified, COM uses its normal security blanket negotiation algorithm to pick an authentication service.For more information, see Security Blanket Negotiation.
}
#region WPF imports
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential, CharSet = System.Runtime.InteropServices.CharSet.Ansi)]
public struct GUID
{
public uint Data1;/// unsigned int
public ushort Data2;/// unsigned short
public ushort Data3;/// unsigned short
[System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst = 8)]
public string Data4;// unsigned char[8]
}
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct SID_IDENTIFIER_AUTHORITY
{
[System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst = 6, ArraySubType = System.Runtime.InteropServices.UnmanagedType.I1)]
public byte[] Value;// BYTE[6]
}
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct SID
{
public byte Revision;/// BYTE->unsigned char
public byte SubAuthorityCount;/// BYTE->unsigned char
public SID_IDENTIFIER_AUTHORITY IdentifierAuthority;/// SID_IDENTIFIER_AUTHORITY->_SID_IDENTIFIER_AUTHORITY
[System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst = 1, ArraySubType = System.Runtime.InteropServices.UnmanagedType.U4)]
public uint[] SubAuthority;// DWORD[1]
}
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct SEC_WINNT_AUTH_IDENTITY_W
{
public IntPtr User;/// unsigned short*
public uint UserLength;/// unsigned int
public IntPtr Domain;/// unsigned short*
public uint DomainLength;/// unsigned int
public IntPtr Password;/// unsigned short*
public uint PasswordLength;/// unsigned int
public uint Flags;// unsigned int
}
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct FWP_BYTE_BLOB
{
public uint size; /// UINT32->unsigned int
[System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPStr)]
public string data;// UINT8*
}
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct FWPM_DISPLAY_DATA0
{
[System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)]
public string name;// wchar_t*
[System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)]
public string description;// wchar_t*
}
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct FWPM_SESSION0
{
public GUID sessionKey;/// GUID->_GUID
public FWPM_DISPLAY_DATA0 displayData;/// FWPM_DISPLAY_DATA0->FWPM_DISPLAY_DATA0_
public uint flags;/// UINT32->unsigned int
public uint txnWaitTimeoutInMSec;/// UINT32->unsigned int
public uint processId;/// DWORD->unsigned int
public IntPtr sid;/// SID*
[System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)]
public string username;/// wchar_t*
public int kernelMode;// BOOL->int
}
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct FWPM_SUBLAYER0
{
public GUID subLayerKey;/// GUID->_GUID
public FWPM_DISPLAY_DATA0 displayData;/// FWPM_DISPLAY_DATA0->FWPM_DISPLAY_DATA0_
public ushort flags;/// UINT16->unsigned short
public IntPtr providerKey;/// GUID*
public FWP_BYTE_BLOB providerData;/// FWP_BYTE_BLOB->FWP_BYTE_BLOB_
public ushort weight;// UINT16->unsigned short
}
Thanks
I've one user logged in a VM, with 3 sessions opened (sessions 2, 3 and 4). I need to run a batch file in specific session, e.g.: 'C:\test.bat' in session 2.
I'm already doing this with PSExec, but I want to know if there someway to make this works without PSExec.
My actual code:
$session = $args[0]
$user = "user01"
$pass = "change123"
C:\PSTools\PsExec.exe -i $session -d \\$env:COMPUTERNAME -u $user -p $pass C:\test.bat
I've been thinking... If I'm the session 2, there's some way to change to session 3 (with tscon) and then run a command in the session 3?
Short answer: Yes it is possible. But...
Long answer: As an example only, I have found some code in C# on PasteBin that does something similar. It will enumerate all sessions, find the one that is logged in to the console and execute command in the session’s context. After wrapping the code in PowerShell, adding some using statements and changing few classes and types to public, it now works. It imports all the functions you would need to accomplish your task from system DLLs.
$C = #"
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Security;
public 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)]
public 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)]
public 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
}
"#
Add-Type -TypeDefinition $C -ReferencedAssemblies mscorlib
[ApplicationLauncher]::CreateProcessInConsoleSession("cmd.exe",$false)
So one way to accomplish what you are after would be to rewrite C# code to find the specific session you need. Second way would be to import DLLs and functions directly in PowerShell, using Add-Type and rewrite the logic in PowerShell. Neither is easy. Either way, I suspect this is “Too Hard” as you already have a way to do this with PSExec.
Try the following.
Add-Type -TypeDefinition #"
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Sequential)]
public struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public uint dwProcessId;
public uint dwThreadId;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct STARTUPINFO
{
public uint 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 STARTF dwFlags;
public ShowWindow wShowWindow;
public short cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public int length;
public IntPtr lpSecurityDescriptor;
public bool bInheritHandle;
}
[Flags]
public enum CreationFlags : int
{
NONE = 0,
DEBUG_PROCESS = 0x00000001,
DEBUG_ONLY_THIS_PROCESS = 0x00000002,
CREATE_SUSPENDED = 0x00000004,
DETACHED_PROCESS = 0x00000008,
CREATE_NEW_CONSOLE = 0x00000010,
CREATE_NEW_PROCESS_GROUP = 0x00000200,
CREATE_UNICODE_ENVIRONMENT = 0x00000400,
CREATE_SEPARATE_WOW_VDM = 0x00000800,
CREATE_SHARED_WOW_VDM = 0x00001000,
CREATE_PROTECTED_PROCESS = 0x00040000,
EXTENDED_STARTUPINFO_PRESENT = 0x00080000,
CREATE_BREAKAWAY_FROM_JOB = 0x01000000,
CREATE_PRESERVE_CODE_AUTHZ_LEVEL = 0x02000000,
CREATE_DEFAULT_ERROR_MODE = 0x04000000,
CREATE_NO_WINDOW = 0x08000000,
}
[Flags]
public enum STARTF : uint
{
STARTF_USESHOWWINDOW = 0x00000001,
STARTF_USESIZE = 0x00000002,
STARTF_USEPOSITION = 0x00000004,
STARTF_USECOUNTCHARS = 0x00000008,
STARTF_USEFILLATTRIBUTE = 0x00000010,
STARTF_RUNFULLSCREEN = 0x00000020, // ignored for non-x86 platforms
STARTF_FORCEONFEEDBACK = 0x00000040,
STARTF_FORCEOFFFEEDBACK = 0x00000080,
STARTF_USESTDHANDLES = 0x00000100,
}
public enum ShowWindow : short
{
SW_HIDE = 0,
SW_SHOWNORMAL = 1,
SW_NORMAL = 1,
SW_SHOWMINIMIZED = 2,
SW_SHOWMAXIMIZED = 3,
SW_MAXIMIZE = 3,
SW_SHOWNOACTIVATE = 4,
SW_SHOW = 5,
SW_MINIMIZE = 6,
SW_SHOWMINNOACTIVE = 7,
SW_SHOWNA = 8,
SW_RESTORE = 9,
SW_SHOWDEFAULT = 10,
SW_FORCEMINIMIZE = 11,
SW_MAX = 11
}
public static class wtsapi32
{
[DllImport("wtsapi32.dll", SetLastError = true)]
public static extern bool WTSQueryUserToken(Int32 sessionId, out IntPtr Token);
}
public static class advapi32
{
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool CreateProcessAsUser(
IntPtr hToken,
string lpApplicationName,
string lpCommandLine,
ref SECURITY_ATTRIBUTES lpProcessAttributes,
ref SECURITY_ATTRIBUTES lpThreadAttributes,
bool bInheritHandles,
CreationFlags creationFlags,
IntPtr lpEnvironment,
string lpCurrentDirectory,
ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);
}
"#
$sessionID = 2
$token = [IntPtr]::Zero
$ret = [wtsapi32]::WTSQueryUserToken($sessionID, [ref] $token)
$si = New-Object STARTUPINFO
$pi = New-Object PROCESS_INFORMATION
$si.cb = [System.Runtime.InteropServices.Marshal]::SizeOf($si)
$si.wShowWindow = [ShowWindow]::SW_SHOW
$pSec = New-Object SECURITY_ATTRIBUTES
$tSec = New-Object SECURITY_ATTRIBUTES
$pSec.Length = [System.Runtime.InteropServices.Marshal]::SizeOf($pSec)
$tSec.Length = [System.Runtime.InteropServices.Marshal]::SizeOf($tSec)
$dwCreationFlags = [CreationFlags]::NORMAL_PRIORITY_CLASS -bor [CreationFlags]::CREATE_NEW_CONSOLE
[advapi32]::CreateProcessAsUser($token, "c:\windows\notepad.exe", $null, [ref] $pSec, [ref] $tSec, $false, $dwCreationFlags, [IntPtr]::Zero, "c:", [ref] $si, [ref] $pi)
I'm trying to write an application which will creates a new desktop using the Win32 API.
The below code works in Windows 7 but but in Windows 8 the desktop is created with out the desktop icons and with an empty taskbar.
public class Desktop
{
private const int NORMAL_PRIORITY_CLASS = 0x00000020;
private const long DF_ALLOWOTHERACCOUNTHOOK = 0x0001L;
private const uint DELETE = 0x00010000;
private const uint READ_CONTROL = 0x00020000;
private const uint WRITE_DAC = 0x00040000;
private const uint WRITE_OWNER = 0x00080000;
private const uint STANDARD_RIGHTS_REQUIRED = DELETE | READ_CONTROL | WRITE_DAC | WRITE_OWNER;
private const long DESKTOP_CREATEWINDOW = 0x0002L;
private const long DESKTOP_ENUMERATE = 0x0040L;
private const long DESKTOP_WRITEOBJECTS = 0x0080L;
private const long DESKTOP_SWITCHDESKTOP = 0x0100L;
private const long DESKTOP_CREATEMENU = 0x0004L;
private const long DESKTOP_HOOKCONTROL = 0x0008L;
private const long DESKTOP_READOBJECTS = 0x0001L;
private const long DESKTOP_JOURNALRECORD = 0x0010L;
private const long DESKTOP_JOURNALPLAYBACK = 0x0020L;
private const uint AccessRights = (uint)DESKTOP_JOURNALRECORD |
(uint)DESKTOP_JOURNALPLAYBACK |
(uint)DESKTOP_CREATEWINDOW |
(uint)DESKTOP_ENUMERATE |
(uint)DESKTOP_WRITEOBJECTS |
(uint)DESKTOP_SWITCHDESKTOP |
(uint)DESKTOP_CREATEMENU |
(uint)DESKTOP_HOOKCONTROL |
(uint)DESKTOP_READOBJECTS |
STANDARD_RIGHTS_REQUIRED;
private delegate bool EnumDesktopProc(string lpszDesktop, IntPtr lParam);
[DllImport("user32.dll")]
private static extern IntPtr GetProcessWindowStation();
[DllImport("user32.dll")]
private static extern bool EnumDesktops(IntPtr hwinsta, EnumDesktopProc lpEnumFunc, IntPtr lParam);
[DllImport("user32.dll")]
public static extern IntPtr CreateDesktop(string desktopName, string device, string deviceMode, int flags, uint accessMask, [In] ref SECURITY_ATTRIBUTES attributes);
[DllImport("user32.dll")]
private static extern IntPtr OpenDesktop(string lpszDesktop, uint dwFlags, bool fInherit, uint dwDesiredAccess);
[DllImport("user32.dll")]
private static extern bool SwitchDesktop(IntPtr hDesktop);
[DllImport("kernel32.dll")]
private static extern bool CreateProcess(string lpApplicationName, string lpCommandLine, IntPtr lpProcessAttributes, IntPtr lpThreadAttributes, bool bInheritHandles, int dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, ref PROCESS_INFORMATION lpProcessInformation);
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public int nLength;
public IntPtr lpSecurityDescriptor;
public int bInheritHandle;
}
[StructLayout(LayoutKind.Sequential)]
private 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)]
private struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public int dwProcessId;
public int dwThreadId;
}
private static List<string> _desktops = new List<string>();
public static IntPtr Create(string name)
{
var securityAttributes = new SECURITY_ATTRIBUTES();
securityAttributes.nLength = Marshal.SizeOf(securityAttributes);
return CreateDesktop(name, null, null, (int) DF_ALLOWOTHERACCOUNTHOOK, AccessRights, ref securityAttributes);
}
public static IntPtr Open(string name)
{
return OpenDesktop(name, 0, false, AccessRights);
}
public static bool Show(IntPtr desktopPrt)
{
if (desktopPrt == IntPtr.Zero) return false;
return SwitchDesktop(desktopPrt);
}
public static void Prepare(string desktopName)
{
CreateProcess("C:\\Windows\\explorer.exe", desktopName);
}
public static bool Exists(string name)
{
return GetDesktops().Any(s => s == name);
}
private static void CreateProcess(string path, string desktopName)
{
STARTUPINFO si = new STARTUPINFO();
si.cb = Marshal.SizeOf(si);
si.lpDesktop = desktopName;
PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
var lpCurrentDirectory = Path.GetDirectoryName(path);
CreateProcess(path, null, IntPtr.Zero, IntPtr.Zero, true, NORMAL_PRIORITY_CLASS, IntPtr.Zero, lpCurrentDirectory, ref si, ref pi);
}
private static string[] GetDesktops()
{
IntPtr windowStation = GetProcessWindowStation();
if (windowStation == IntPtr.Zero) return new string[0];
_desktops.Clear();
bool result = EnumDesktops(windowStation, DesktopEnumProc, IntPtr.Zero);
if (!result) return new string[0];
return _desktops.ToArray();
}
private static bool DesktopEnumProc(string lpszDesktop, IntPtr lParam)
{
_desktops.Add(lpszDesktop);
return true;
}
}
Usage:
var desktopName = "TestDesktop";
var desktopPrt = Desktop.Create(desktopName);
Desktop.Show(desktopPrt);
Desktop.Prepare(desktopName);
The Sysinternals Desktops app (http://technet.microsoft.com/en-us/sysinternals/cc817881.aspx) uses the same API and it works in Windows 8 so there must be something that I am missing.
Does anyone have any pointers for me? Or does anyone know of any where I can find examples of how to use this API? MSDN for the API calls gives a lot of information but no many examples.
Thanks ;D
EDIT: Sry, I realised that you run it in your code snippet, please ignore my comment.
How about explicitly running explorer.exe for each desktop you create? That one handles icons, taskbar, etc ...
When you run SysInternals Desktops app you can see that it actually does that.
now i have code that detect session Id of users that connect to Windows with remote desktop (mstsc.exe) to windows XP but i want to know their Usernames.
[DllImport("wtsapi32.dll")]
static extern IntPtr WTSOpenServer([MarshalAs(UnmanagedType.LPStr)] String pServerName);
[DllImport("wtsapi32.dll")]
static extern void WTSCloseServer(IntPtr hServer);
[DllImport("wtsapi32.dll")]
static extern Int32 WTSEnumerateSessions(
IntPtr hServer,
[MarshalAs(UnmanagedType.U4)] Int32 Reserved,
[MarshalAs(UnmanagedType.U4)] Int32 Version,
ref IntPtr ppSessionInfo,
[MarshalAs(UnmanagedType.U4)] ref Int32 pCount);
[DllImport("wtsapi32.dll")]
static extern void WTSFreeMemory(IntPtr pMemory);
[DllImport("Wtsapi32.dll")]
static extern bool WTSQuerySessionInformation(
System.IntPtr hServer, int sessionId, WTS_INFO_CLASS wtsInfoClass, out System.IntPtr ppBuffer, out uint pBytesReturned);
[StructLayout(LayoutKind.Sequential)]
private struct WTS_SESSION_INFO
{
public Int32 SessionID;
[MarshalAs(UnmanagedType.LPStr)]
public String pWinStationName;
public WTS_CONNECTSTATE_CLASS State;
}
public enum WTS_INFO_CLASS
{
WTSInitialProgram,
WTSApplicationName,
WTSWorkingDirectory,
WTSOEMId,
WTSSessionId,
WTSUserName,
WTSWinStationName,
WTSDomainName,
WTSConnectState,
WTSClientBuildNumber,
WTSClientName,
WTSClientDirectory,
WTSClientProductId,
WTSClientHardwareId,
WTSClientAddress,
WTSClientDisplay,
WTSClientProtocolType
}
public enum WTS_CONNECTSTATE_CLASS
{
WTSActive,
WTSConnected,
WTSConnectQuery,
WTSShadow,
WTSDisconnected,
WTSIdle,
WTSListen,
WTSReset,
WTSDown,
WTSInit
}
public static IntPtr OpenServer(String Name)
{
IntPtr server = WTSOpenServer(Name);
return server;
}
public static void CloseServer(IntPtr ServerHandle)
{
WTSCloseServer(ServerHandle);
}
public static DataTable ListUsers(String ServerName)
{
DataTable DtResult = new DataTable();
DtResult.Columns.Add("Domain");
DtResult.Columns.Add("User");
IntPtr serverHandle = IntPtr.Zero;
List<String> resultList = new List<string>();
serverHandle = OpenServer(ServerName);
try
{
IntPtr SessionInfoPtr = IntPtr.Zero;
IntPtr userPtr = IntPtr.Zero;
IntPtr domainPtr = IntPtr.Zero;
Int32 sessionCount = 0;
Int32 retVal = WTSEnumerateSessions(serverHandle, 0, 1, ref SessionInfoPtr, ref sessionCount);
Int32 dataSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO));
Int32 currentSession = (int)SessionInfoPtr;
uint bytes = 0;
if (retVal != 0)
{
for (int i = 0; i < sessionCount; i++)
{
WTS_SESSION_INFO si = (WTS_SESSION_INFO)Marshal.PtrToStructure((System.IntPtr)currentSession, typeof(WTS_SESSION_INFO));
currentSession += dataSize;
WTSQuerySessionInformation(serverHandle, si.SessionID, WTS_INFO_CLASS.WTSUserName, out userPtr, out bytes);
WTSQuerySessionInformation(serverHandle, si.SessionID, WTS_INFO_CLASS.WTSDomainName, out domainPtr, out bytes);
DtResult.Rows.Add(Marshal.PtrToStringAnsi(domainPtr) ,Marshal.PtrToStringAnsi(userPtr));
WTSFreeMemory(userPtr);
WTSFreeMemory(domainPtr);
}
WTSFreeMemory(SessionInfoPtr);
}
}
finally
{
CloseServer(serverHandle);
}
return DtResult;
}
You can also use the Cassia library if you'd like to dealing with the P/Invokes. See this answer for a code sample.