在Windows服务里面启动其他具有界面的应用程序,需要穿透session隔离,尝试了很多种方法,都可行,现在一一列举下来,并写下几个需要注意的地方。
需要注意的地方
- 首先要将服务的Account属性设置为LocalSystem,安装服务后的登录身份则为本地系统账户
- 再一个需要注意的是不要把Windows服务的程序放在C:\Users\Administrator\目录下运行,不然启动服务的时候会遇到权限问题,如下图
实现代码如下
- 第一种方法
调用方法
WinAPI_Interop.CreateProcess(path);//string path=@"C:\Users\Administrator\Text.exe";
代码类
public class WinAPI_Interop{public static IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero;/// <summary>/// 服务程序执行消息提示,前台MessageBox.Show/// </summary>/// <param name="message">消息内容</param>/// <param name="title">标题</param>public static void ShowServiceMessage(string message, string title){int resp = 0;WTSSendMessage(WTS_CURRENT_SERVER_HANDLE, WTSGetActiveConsoleSessionId(), title, title.Length, message, message.Length, 0, 0, out resp, false);}[DllImport("kernel32.dll", SetLastError = true)]public static extern int WTSGetActiveConsoleSessionId();[DllImport("wtsapi32.dll", SetLastError = true)]public static extern bool WTSSendMessage(IntPtr hServer, int SessionId, String pTitle, int TitleLength, String pMessage, int MessageLength, int Style, int Timeout, out int pResponse, bool bWait);#region P/Invoke WTS APIsprivate enum WTS_CONNECTSTATE_CLASS{WTSActive,WTSConnected,WTSConnectQuery,WTSShadow,WTSDisconnected,WTSIdle,WTSListen,WTSReset,WTSDown,WTSInit}[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]private struct WTS_SESSION_INFO{public UInt32 SessionID;public string pWinStationName;public WTS_CONNECTSTATE_CLASS State;}[DllImport("WTSAPI32.DLL", SetLastError = true, CharSet = CharSet.Auto)]static extern bool WTSEnumerateSessions(IntPtr hServer,[MarshalAs(UnmanagedType.U4)] UInt32 Reserved,[MarshalAs(UnmanagedType.U4)] UInt32 Version,ref IntPtr ppSessionInfo,[MarshalAs(UnmanagedType.U4)] ref UInt32 pSessionInfoCount);[DllImport("WTSAPI32.DLL", SetLastError = true, CharSet = CharSet.Auto)]static extern void WTSFreeMemory(IntPtr pMemory);[DllImport("WTSAPI32.DLL", SetLastError = true, CharSet = CharSet.Auto)]static extern bool WTSQueryUserToken(UInt32 sessionId, out IntPtr Token);#endregion#region P/Invoke CreateProcessAsUser/// <summary> /// Struct, Enum and P/Invoke Declarations for CreateProcessAsUser. /// </summary> /// [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]struct STARTUPINFO{public Int32 cb;public string lpReserved;public string lpDesktop;public string lpTitle;public Int32 dwX;public Int32 dwY;public Int32 dwXSize;public Int32 dwYSize;public Int32 dwXCountChars;public Int32 dwYCountChars;public Int32 dwFillAttribute;public Int32 dwFlags;public Int16 wShowWindow;public Int16 cbReserved2;public IntPtr lpReserved2;public IntPtr hStdInput;public IntPtr hStdOutput;public IntPtr hStdError;}[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]struct PROCESS_INFORMATION{public IntPtr hProcess;public IntPtr hThread;public int dwProcessId;public int dwThreadId;}/// <summary>/// 以当前登录的windows用户(角色权限)运行指定程序进程/// </summary>/// <param name="hToken"></param>/// <param name="lpApplicationName">指定程序(全路径)</param>/// <param name="lpCommandLine">参数</param>/// <param name="lpProcessAttributes">进程属性</param>/// <param name="lpThreadAttributes">线程属性</param>/// <param name="bInheritHandles"></param>/// <param name="dwCreationFlags"></param>/// <param name="lpEnvironment"></param>/// <param name="lpCurrentDirectory"></param>/// <param name="lpStartupInfo">程序启动属性</param>/// <param name="lpProcessInformation">最后返回的进程信息</param>/// <returns>是否调用成功</returns>[DllImport("ADVAPI32.DLL", SetLastError = true, CharSet = CharSet.Auto)]static extern bool CreateProcessAsUser(IntPtr hToken, string lpApplicationName, string lpCommandLine, IntPtr lpProcessAttributes, IntPtr lpThreadAttributes,bool bInheritHandles, uint dwCreationFlags, string lpEnvironment, string lpCurrentDirectory,ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);[DllImport("KERNEL32.DLL", SetLastError = true, CharSet = CharSet.Auto)]static extern bool CloseHandle(IntPtr hHandle);#endregion/// <summary>/// 以当前登录系统的用户角色权限启动指定的进程/// </summary>/// <param name="ChildProcName">指定的进程(全路径)</param>public static void CreateProcess(string ChildProcName){IntPtr ppSessionInfo = IntPtr.Zero;UInt32 SessionCount = 0;if (WTSEnumerateSessions((IntPtr)WTS_CURRENT_SERVER_HANDLE, // Current RD Session Host Server handle would be zero. 0, // This reserved parameter must be zero. 1, // The version of the enumeration request must be 1. ref ppSessionInfo, // This would point to an array of session info. ref SessionCount // This would indicate the length of the above array. )){for (int nCount = 0; nCount < SessionCount; nCount++){WTS_SESSION_INFO tSessionInfo = (WTS_SESSION_INFO)Marshal.PtrToStructure(ppSessionInfo + nCount * Marshal.SizeOf(typeof(WTS_SESSION_INFO)), typeof(WTS_SESSION_INFO));if (WTS_CONNECTSTATE_CLASS.WTSActive == tSessionInfo.State){IntPtr hToken = IntPtr.Zero;if(WTSQueryUserToken(tSessionInfo.SessionID, out hToken)){PROCESS_INFORMATION tProcessInfo;STARTUPINFO tStartUpInfo = new STARTUPINFO();tStartUpInfo.cb = Marshal.SizeOf(typeof(STARTUPINFO));bool ChildProcStarted = CreateProcessAsUser(hToken, // Token of the logged-on user. ChildProcName, // Name of the process to be started. null, // Any command line arguments to be passed. IntPtr.Zero, // Default Process' attributes. IntPtr.Zero, // Default Thread's attributes. false, // Does NOT inherit parent's handles. 0, // No any specific creation flag. null, // Default environment path. null, // Default current directory. ref tStartUpInfo, // Process Startup Info. out tProcessInfo // Process information to be returned. );if (ChildProcStarted){CloseHandle(tProcessInfo.hThread);CloseHandle(tProcessInfo.hProcess);}else{ShowServiceMessage("CreateProcessAsUser失败", "CreateProcess");}CloseHandle(hToken);break;}}}WTSFreeMemory(ppSessionInfo);}}}
- 第二种方法
调用方法
Interops.CreateProcess(path, @"C:\Windows\System32\");//string path=@"C:\Users\Administrator\Text.exe";
代码类
public class Interops{public static void CreateProcess(string app, string path){bool result;IntPtr hToken = WindowsIdentity.GetCurrent().Token;IntPtr hDupedToken = IntPtr.Zero;PROCESS_INFORMATION pi = new PROCESS_INFORMATION();SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();sa.Length = Marshal.SizeOf(sa);STARTUPINFO si = new STARTUPINFO();si.cb = Marshal.SizeOf(si);int dwSessionID = 0;// WTSGetActiveConsoleSessionId();result = WTSQueryUserToken(dwSessionID, out hToken);if (!result){ShowMessageBox("WTSQueryUserToken failed", "AlertService Message");}result = DuplicateTokenEx(hToken,GENERIC_ALL_ACCESS,ref sa,(int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification,(int)TOKEN_TYPE.TokenPrimary,ref hDupedToken);if (!result){ShowMessageBox("DuplicateTokenEx failed", "AlertService Message");}IntPtr lpEnvironment = IntPtr.Zero;result = CreateEnvironmentBlock(out lpEnvironment, hDupedToken, false);if (!result){ShowMessageBox("CreateEnvironmentBlock failed", "AlertService Message");}result = CreateProcessAsUser(hDupedToken,app,String.Empty,ref sa, ref sa,false, 0, IntPtr.Zero,null, ref si, ref pi);if (!result){int error = Marshal.GetLastWin32Error();string message = String.Format("CreateProcessAsUser Error: {0}", error);ShowMessageBox(message, "AlertService Message");}if (pi.hProcess != IntPtr.Zero)CloseHandle(pi.hProcess);if (pi.hThread != IntPtr.Zero)CloseHandle(pi.hThread);if (hDupedToken != IntPtr.Zero)CloseHandle(hDupedToken);}[StructLayout(LayoutKind.Sequential)]public struct STARTUPINFO{public Int32 cb;public string lpReserved;public string lpDesktop;public string lpTitle;public Int32 dwX;public Int32 dwY;public Int32 dwXSize;public Int32 dwXCountChars;public Int32 dwYCountChars;public Int32 dwFillAttribute;public Int32 dwFlags;public Int16 wShowWindow;public Int16 cbReserved2;public IntPtr lpReserved2;public IntPtr hStdInput;public IntPtr hStdOutput;public IntPtr hStdError;}[StructLayout(LayoutKind.Sequential)]public struct PROCESS_INFORMATION{public IntPtr hProcess;public IntPtr hThread;public Int32 dwProcessID;public Int32 dwThreadID;}[StructLayout(LayoutKind.Sequential)]public struct SECURITY_ATTRIBUTES{public Int32 Length;public IntPtr lpSecurityDescriptor;public bool bInheritHandle;}public enum SECURITY_IMPERSONATION_LEVEL{SecurityAnonymous,SecurityIdentification,SecurityImpersonation,SecurityDelegation}public enum TOKEN_TYPE{TokenPrimary = 1,TokenImpersonation}public const int GENERIC_ALL_ACCESS = 0x10000000;[DllImport("kernel32.dll", SetLastError = true,CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]public static extern bool CloseHandle(IntPtr handle);[DllImport("advapi32.dll", 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,Int32 dwCreationFlags,IntPtr lpEnvrionment,string lpCurrentDirectory,ref STARTUPINFO lpStartupInfo,ref PROCESS_INFORMATION lpProcessInformation);[DllImport("advapi32.dll", SetLastError = true)]public static extern bool DuplicateTokenEx(IntPtr hExistingToken,Int32 dwDesiredAccess,ref SECURITY_ATTRIBUTES lpThreadAttributes,Int32 ImpersonationLevel,Int32 dwTokenType,ref IntPtr phNewToken);[DllImport("wtsapi32.dll", SetLastError = true)]public static extern bool WTSQueryUserToken(Int32 sessionId,out IntPtr Token);[DllImport("userenv.dll", SetLastError = true)]static extern bool CreateEnvironmentBlock(out IntPtr lpEnvironment,IntPtr hToken,bool bInherit);public static IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero;public static void ShowMessageBox(string message, string title){int resp = 0;WTSSendMessage(WTS_CURRENT_SERVER_HANDLE,WTSGetActiveConsoleSessionId(),title, title.Length,message, message.Length,0, 0, out resp, false);}[DllImport("kernel32.dll", SetLastError = true)]public static extern int WTSGetActiveConsoleSessionId();[DllImport("wtsapi32.dll", SetLastError = true)]public static extern bool WTSSendMessage(IntPtr hServer,int SessionId,String pTitle,int TitleLength,String pMessage,int MessageLength,int Style,int Timeout,out int pResponse,bool bWait);}
- 第三种方法(可以远程)
调用方法
SessionUtility.CreateProcess(@"C:\Windows\System32\", path, 1);//string path=@"C:\Users\Administrator\Test.exe";
代码类
/// <summary>/// 解决vista和win7在windows服务中交互桌面权限问题:穿透Session 0 隔离/// 用于windows服务 启动外部程序 或者截取图片等/// 默认windows服务的权限是在session0中/// </summary>public class SessionUtility{#region 如果服务只是简单的向桌面用户Session 发送消息窗口,则可以使用WTSSendMessage 函数实现public static IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero;public static void ShowMessageBox(string message, string title){int resp = 0;WTSSendMessage(WTS_CURRENT_SERVER_HANDLE,WTSGetActiveConsoleSessionId(),title, title.Length,message, message.Length,0, 0, out resp, false);}[DllImport("kernel32.dll", SetLastError = true)]public static extern int WTSGetActiveConsoleSessionId();[DllImport("wtsapi32.dll", SetLastError = true)]public static extern bool WTSSendMessage(IntPtr hServer,int SessionId,String pTitle,int TitleLength,String pMessage,int MessageLength,int Style,int Timeout,out int pResponse,bool bWait);//在ShowMessageBox 函数中调用了WTSSendMessage 来发送信息窗口,这样我们就可以在Service 的OnStart 函数中使用,打开Service1.cs 加入下面代码://protected override void OnStart(string[] args)//{// Interop.ShowMessageBox("This a message from AlertService.",// "AlertService Message");//}#endregion/** 如果想通过服务向桌面用户Session 创建一个复杂UI 程序界面,* 则需要使用CreateProcessAsUser 函数为用户创建一个新进程用来运行相应的程序。*/#region 复杂进程public static void CreateProcess(string app, string para, int sessionID){if (!string.IsNullOrEmpty(para)){para = app + @"\" + para;app = null;}bool result;IntPtr hToken = WindowsIdentity.GetCurrent().Token;IntPtr hDupedToken = IntPtr.Zero;var pi = new PROCESS_INFORMATION();var sa = new SECURITY_ATTRIBUTES();sa.Length = Marshal.SizeOf(sa);var si = new STARTUPINFO();si.cb = Marshal.SizeOf(si);int dwSessionID = sessionID;if (sessionID < 0)dwSessionID = WTSGetActiveConsoleSessionId();result = WTSQueryUserToken(dwSessionID, out hToken);if (!result){ShowMessageBox("WTSQueryUserToken failed", "AlertService Message");}result = DuplicateTokenEx(hToken,GENERIC_ALL_ACCESS,ref sa,(int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification,(int)TOKEN_TYPE.TokenPrimary,ref hDupedToken);if (!result){ShowMessageBox("DuplicateTokenEx failed", "AlertService Message");}IntPtr lpEnvironment = IntPtr.Zero;result = CreateEnvironmentBlock(out lpEnvironment, hDupedToken, false);if (!result){ShowMessageBox("CreateEnvironmentBlock failed", "AlertService Message");}result = CreateProcessAsUser(hDupedToken,app,para,ref sa, ref sa,false, 0, IntPtr.Zero,null, ref si, ref pi);if (!result){int error = Marshal.GetLastWin32Error();string message = String.Format("CreateProcessAsUser Error: {0}", error);ShowMessageBox(message, "AlertService Message");}if (pi.hProcess != IntPtr.Zero)CloseHandle(pi.hProcess);if (pi.hThread != IntPtr.Zero)CloseHandle(pi.hThread);if (hDupedToken != IntPtr.Zero)CloseHandle(hDupedToken);}[StructLayout(LayoutKind.Sequential)]public struct STARTUPINFO{public Int32 cb;public string lpReserved;public string lpDesktop;public string lpTitle;public Int32 dwX;public Int32 dwY;public Int32 dwXSize;public Int32 dwXCountChars;public Int32 dwYCountChars;public Int32 dwFillAttribute;public Int32 dwFlags;public Int16 wShowWindow;public Int16 cbReserved2;public IntPtr lpReserved2;public IntPtr hStdInput;public IntPtr hStdOutput;public IntPtr hStdError;}[StructLayout(LayoutKind.Sequential)]public struct PROCESS_INFORMATION{public IntPtr hProcess;public IntPtr hThread;public Int32 dwProcessID;public Int32 dwThreadID;}[StructLayout(LayoutKind.Sequential)]public struct SECURITY_ATTRIBUTES{public Int32 Length;public IntPtr lpSecurityDescriptor;public bool bInheritHandle;}public enum SECURITY_IMPERSONATION_LEVEL{SecurityAnonymous,SecurityIdentification,SecurityImpersonation,SecurityDelegation}public enum TOKEN_TYPE{TokenPrimary = 1,TokenImpersonation}public const int GENERIC_ALL_ACCESS = 0x10000000;[DllImport("kernel32.dll", SetLastError = true,CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]public static extern bool CloseHandle(IntPtr handle);[DllImport("advapi32.dll", 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,Int32 dwCreationFlags,IntPtr lpEnvrionment,string lpCurrentDirectory,ref STARTUPINFO lpStartupInfo,ref PROCESS_INFORMATION lpProcessInformation);[DllImport("advapi32.dll", SetLastError = true)]public static extern bool DuplicateTokenEx(IntPtr hExistingToken,Int32 dwDesiredAccess,ref SECURITY_ATTRIBUTES lpThreadAttributes,Int32 ImpersonationLevel,Int32 dwTokenType,ref IntPtr phNewToken);[DllImport("wtsapi32.dll", SetLastError = true)]public static extern bool WTSQueryUserToken(Int32 sessionId,out IntPtr Token);[DllImport("userenv.dll", SetLastError = true)]private static extern bool CreateEnvironmentBlock(out IntPtr lpEnvironment,IntPtr hToken,bool bInherit);#endregion}
- 第四种方法
调用方法
ApplicationLoader.PROCESS_INFORMATION procInfo; ApplicationLoader.StartProcessAndBypassUAC(path, out procInfo);//string path=@"C:\Users\Administrator\Test.exe";
代码类
/// <summary>/// Class that allows running applications with full admin rights. In/// addition the application launched will bypass the Vista UAC prompt./// </summary>public class ApplicationLoader{#region Structrures[StructLayout(LayoutKind.Sequential)]public struct SECURITY_ATTRIBUTES{public int Length;public IntPtr lpSecurityDescriptor;public bool bInheritHandle;}[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;}[StructLayout(LayoutKind.Sequential)]public struct PROCESS_INFORMATION{public IntPtr hProcess;public IntPtr hThread;public uint dwProcessId;public uint dwThreadId;}#endregion#region Enumberationenum TOKEN_TYPE : int{TokenPrimary = 1,TokenImpersonation = 2}enum SECURITY_IMPERSONATION_LEVEL : int{SecurityAnonymous = 0,SecurityIdentification = 1,SecurityImpersonation = 2,SecurityDelegation = 3,}#endregion#region Constantspublic const int TOKEN_DUPLICATE = 0x0002;public const uint MAXIMUM_ALLOWED = 0x2000000;public const int CREATE_NEW_CONSOLE = 0x00000010;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;#endregion#region Win32 API Imports[DllImport("kernel32.dll", SetLastError = true)]private static extern bool CloseHandle(IntPtr hSnapshot);[DllImport("kernel32.dll")]static extern uint WTSGetActiveConsoleSessionId();[DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUser", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]public extern static 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("kernel32.dll")]static extern bool ProcessIdToSessionId(uint dwProcessId, ref uint pSessionId);[DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx")]public extern static bool DuplicateTokenEx(IntPtr ExistingTokenHandle, uint dwDesiredAccess,ref SECURITY_ATTRIBUTES lpThreadAttributes, int TokenType,int ImpersonationLevel, ref IntPtr DuplicateTokenHandle);[DllImport("kernel32.dll")]static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, uint dwProcessId);[DllImport("advapi32.dll", SetLastError = true), SuppressUnmanagedCodeSecurityAttribute]static extern bool OpenProcessToken(IntPtr ProcessHandle, int DesiredAccess, ref IntPtr TokenHandle);//[DllImport("advapi32.dll", SetLastError = true)]//[return: MarshalAs(UnmanagedType.Bool)]//static extern bool OpenProcessToken(IntPtr ProcessHandle, UInt32 DesiredAccess, ref IntPtr TokenHandle);#endregion/// <summary>/// Launches the given application with full admin rights, and in addition bypasses the Vista UAC prompt/// </summary>/// <param name="applicationName">The name of the application to launch</param>/// <param name="procInfo">Process information regarding the launched application that gets returned to the caller</param>/// <returns></returns>public static bool StartProcessAndBypassUAC(String applicationName, out PROCESS_INFORMATION procInfo){uint winlogonPid = 0;IntPtr hUserTokenDup = IntPtr.Zero,hPToken = IntPtr.Zero,hProcess = IntPtr.Zero;procInfo = new PROCESS_INFORMATION();// obtain the currently active session id; every logged on user in the system has a unique session idTSControl.WTS_SESSION_INFO[] pSessionInfo = TSControl.SessionEnumeration();uint dwSessionId = 100;for (int i = 0; i < pSessionInfo.Length; i++){if (pSessionInfo[i].SessionID != 0){try{int count = 0;IntPtr buffer = IntPtr.Zero;StringBuilder sb = new StringBuilder();bool bsuccess = TSControl.WTSQuerySessionInformation(IntPtr.Zero, pSessionInfo[i].SessionID,TSControl.WTSInfoClass.WTSUserName, out sb, out count);if (bsuccess){if (sb.ToString().Trim() == "Administrator")//Administrator {dwSessionId = (uint)pSessionInfo[i].SessionID;}}}catch (Exception ex){//LoaderService.WriteLog(ex.Message.ToString(), "Monitor"); }}}// obtain the process id of the winlogon process that is running within the currently active sessionProcess[] processes = Process.GetProcessesByName("explorer");foreach (Process p in processes){if ((uint)p.SessionId == dwSessionId){winlogonPid = (uint)p.Id;}}//LoaderService.WriteLog(winlogonPid.ToString(), "Monitor");// obtain a handle to the winlogon processhProcess = OpenProcess(MAXIMUM_ALLOWED, false, winlogonPid);// obtain a handle to the access token of the winlogon processif (!OpenProcessToken(hProcess, TOKEN_DUPLICATE, ref hPToken)){CloseHandle(hProcess);return false;}// Security attibute structure used in DuplicateTokenEx and CreateProcessAsUser// I would prefer to not have to use a security attribute variable and to just // simply pass null and inherit (by default) the security attributes// of the existing token. However, in C# structures are value types and therefore// cannot be assigned the null value.SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();sa.Length = Marshal.SizeOf(sa);// copy the access token of the winlogon process; the newly created token will be a primary tokenif (!DuplicateTokenEx(hPToken, MAXIMUM_ALLOWED, ref sa, (int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, (int)TOKEN_TYPE.TokenPrimary, ref hUserTokenDup)){CloseHandle(hProcess);CloseHandle(hPToken);return false;}// By default CreateProcessAsUser creates a process on a non-interactive window station, meaning// the window station has a desktop that is invisible and the process is incapable of receiving// user input. To remedy this we set the lpDesktop parameter to indicate we want to enable user // interaction with the new process.STARTUPINFO si = new STARTUPINFO();si.cb = (int)Marshal.SizeOf(si);si.lpDesktop = @"winsta0\default"; // interactive window station parameter; basically this indicates that the process created can display a GUI on the desktop// flags that specify the priority and creation method of the processint dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;// create a new process in the current user's logon sessionbool result = CreateProcessAsUser(hUserTokenDup, // client's access tokennull, // file to executeapplicationName, // command lineref sa, // pointer to process SECURITY_ATTRIBUTESref sa, // pointer to thread SECURITY_ATTRIBUTESfalse, // handles are not inheritabledwCreationFlags, // creation flagsIntPtr.Zero, // pointer to new environment block null, // name of current directory ref si, // pointer to STARTUPINFO structureout procInfo // receives information about new process );// invalidate the handles CloseHandle(hProcess);CloseHandle(hPToken);CloseHandle(hUserTokenDup);//LoaderService.WriteLog("launch Task", "Monitor");return result; // return the result }}
public class TSControl{/**//// <summary> /// Terminal Services API Functions,The WTSEnumerateSessions function retrieves a list of sessions on a specified terminal server, /// </summary> /// <param name="hServer">[in] Handle to a terminal server. Specify a handle opened by the WTSOpenServer function, or specify WTS_CURRENT_SERVER_HANDLE to indicate the terminal server on which your application is running</param> /// <param name="Reserved">Reserved; must be zero</param> /// <param name="Version">[in] Specifies the version of the enumeration request. Must be 1. </param> /// <param name="ppSessionInfo">[out] Pointer to a variable that receives a pointer to an array of WTS_SESSION_INFO structures. Each structure in the array contains information about a session on the specified terminal server. To free the returned buffer, call the WTSFreeMemory function. /// To be able to enumerate a session, you need to have the Query Information permission.</param> /// <param name="pCount">[out] Pointer to the variable that receives the number of WTS_SESSION_INFO structures returned in the ppSessionInfo buffer. </param> /// <returns>If the function succeeds, the return value is a nonzero value. If the function fails, the return value is zero</returns> [DllImport("wtsapi32", CharSet = CharSet.Auto, SetLastError = true)]private static extern bool WTSEnumerateSessions(int hServer, int Reserved, int Version, ref long ppSessionInfo, ref int pCount);/**//// <summary> /// Terminal Services API Functions,The WTSFreeMemory function frees memory allocated by a Terminal Services function. /// </summary> /// <param name="pMemory">[in] Pointer to the memory to free</param> [DllImport("wtsapi32.dll")]public static extern void WTSFreeMemory(System.IntPtr pMemory);/**//// <summary> /// Terminal Services API Functions,The WTSLogoffSession function logs off a specified Terminal Services session. /// </summary> /// <param name="hServer">[in] Handle to a terminal server. Specify a handle opened by the WTSOpenServer function, or specify WTS_CURRENT_SERVER_HANDLE to indicate the terminal server on which your application is running. </param> /// <param name="SessionId">[in] A Terminal Services session identifier. To indicate the current session, specify WTS_CURRENT_SESSION. You can use the WTSEnumerateSessions function to retrieve the identifiers of all sessions on a specified terminal server. /// To be able to log off another user's session, you need to have the Reset permission </param> /// <param name="bWait">[in] Indicates whether the operation is synchronous. /// If bWait is TRUE, the function returns when the session is logged off. /// If bWait is FALSE, the function returns immediately.</param> /// <returns>If the function succeeds, the return value is a nonzero value. /// If the function fails, the return value is zero.</returns> [DllImport("wtsapi32.dll")]public static extern bool WTSLogoffSession(int hServer, long SessionId, bool bWait);[DllImport("Wtsapi32.dll")]public static extern bool WTSQuerySessionInformation(System.IntPtr hServer,int sessionId,WTSInfoClass wtsInfoClass,out StringBuilder ppBuffer,out int pBytesReturned);public enum WTSInfoClass{WTSInitialProgram,WTSApplicationName,WTSWorkingDirectory,WTSOEMId,WTSSessionId,WTSUserName,WTSWinStationName,WTSDomainName,WTSConnectState,WTSClientBuildNumber,WTSClientName,WTSClientDirectory,WTSClientProductId,WTSClientHardwareId,WTSClientAddress,WTSClientDisplay,WTSClientProtocolType}/**//// <summary> /// The WTS_CONNECTSTATE_CLASS enumeration type contains INT values that indicate the connection state of a Terminal Services session. /// </summary> public enum WTS_CONNECTSTATE_CLASS{WTSActive,WTSConnected,WTSConnectQuery,WTSShadow,WTSDisconnected,WTSIdle,WTSListen,WTSReset,WTSDown,WTSInit,}/**//// <summary> /// The WTS_SESSION_INFO structure contains information about a client session on a terminal server. /// if the WTS_SESSION_INFO.SessionID==0, it means that the SESSION is the local logon user's session. /// </summary> public struct WTS_SESSION_INFO{public int SessionID;[MarshalAs(UnmanagedType.LPTStr)]public string pWinStationName;public WTS_CONNECTSTATE_CLASS state;}/**//// <summary> /// The SessionEnumeration function retrieves a list of ///WTS_SESSION_INFO on a current terminal server. /// </summary> /// <returns>a list of WTS_SESSION_INFO on a current terminal server</returns> public static WTS_SESSION_INFO[] SessionEnumeration(){//Set handle of terminal server as the current terminal server int hServer = 0;bool RetVal;long lpBuffer = 0;int Count = 0;long p;WTS_SESSION_INFO Session_Info = new WTS_SESSION_INFO();WTS_SESSION_INFO[] arrSessionInfo;RetVal = WTSEnumerateSessions(hServer, 0, 1, ref lpBuffer, ref Count);arrSessionInfo = new WTS_SESSION_INFO[0];if (RetVal){arrSessionInfo = new WTS_SESSION_INFO[Count];int i;p = lpBuffer;for (i = 0; i < Count; i++){arrSessionInfo[i] =(WTS_SESSION_INFO)Marshal.PtrToStructure(new IntPtr(p),Session_Info.GetType());p += Marshal.SizeOf(Session_Info.GetType());}WTSFreeMemory(new IntPtr(lpBuffer));}else{//Insert Error Reaction Here }return arrSessionInfo;}public TSControl(){// // TODO: 在此处添加构造函数逻辑 // }}
- 参考资料
https://blog.csdn.net/peter_666/article/details/8106273
https://www.cnblogs.com/gnielee/archive/2010/04/08/session0-isolation-part2.html
https://www.cnblogs.com/qiaoke/p/6654449.html
https://www.cnblogs.com/datacool/p/CreateProcessAsUser_Win_api.html
https://blog.csdn.net/vevisoft/article/details/42751533