Win 7 下跨越Session Id 0的Windows Service 并与活动Session UI进行交互
来源:互联网 发布:知乎 音乐播放器 编辑:程序博客网 时间:2024/05/20 10:53
常规的WinForm程式截图比较简单,只需利用Graphics的CopyFromScreen函数即可截取当前屏幕图像,如下4行代码即可完成截图并保存文件的功能:
Bitmap snapShot= new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);Graphics g = Graphics.FromImage(g as Image);g.CopyFromScreen(0, 0, 0, 0, printscreen.Size);snapShot.Save(fileName, System.Drawing.Imaging.ImageFormat.Jpeg);
但是在编写Windows Service程式时,将上面代码写入到Service的loop内,想在指定时间截图当前屏幕图像并保存到本地硬盘内,却发现产生的jpg图像文件都是黑色画面,没有成功截取到当前屏幕图像,即使启动VS2010,附加到Windows Service进程进行Debug也有发现确实有执行到截图代码部分。
虽然没有必要在Windows Service里面来进行截屏,但本着问题本身,还是经过一番搜索。
后来Google了下,发现其他人也有类似的问题。在XP下运行正常,但是在Win Vista以及之后的Win7都会出现截取图像为黑色画面的情况。再后来经查证是Session 0隔离的问题。
简单来说就是在Windows XP, Windows Server 2003或者更早期的Windows操作系统中,所有的服务和应用程序都是运行在与第一个登录到控制台的用户得Session中。这个Session叫做Session 0。在Session 0 中一起运行服务和用户应用程序,由于服务是以高权限运行的,所以会造成一些安全风险。这些因素使得一些恶意代理利用这点,来寻找提升他们自身权限的结构。
而在Windows Vista中,服务在一个叫做Session 0 的特殊Session中承载。由于应用程序运行在用户登录到系统后所创建的Session 0 之后的Session中,所以应用程序和服务也就隔离开来:第一个登录的用户在Session 1中,第二个在Session 2中,以此类推。事实上运行在不同的Session中,如果没有特别将其放入全局命名空间(并且设置了相应的访问控制配置),是不能互相传递窗体消息,共享UI元素或者共享kernel对象。
参考:http://msdn.microsoft.com/zh-cn/library/ee663077.aspx
MSDN上介绍了通过使用WTSSendMessage 来向当前活动Session Desktop UI发送跨Session的MessageBox消息(从Session 0 发送到Session 1 等等)
[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);WTSSendMessage第二个参数即是要传送的Session Id,可通过WTSGetActiveConsoleSessionId()来获取当前活动的Console Session Id。这只是方法一,其实还有其他两种方式获取,第二种方式是通过WTSEnumerateSessions来枚举列出当前所有Session的WTS_SESSION_INFO,并通过其state字段是否为WTSActive来获得活动的Console Session Id。第三种方式是,因为在Service里面也可以枚举出当前Active Session的各种进程Process信息,从其中的explorer.exe进程信息里面可以得到Active Session Id,如下图:
由此可见,由于Window Service本身运行在Session 0 里面,只有通过发送Message的方式与活动UI进行交互。可在Active Session Id里创建进程的方式让新产生的进程能够与活动UI进行交互(如在Service里面创建之前讲到的截屏程式的进程),需要使用到CreateProcessAsUser函数,原型如下:
[DllImport("advapi32.dll", SetLastError = true)] 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);
简单来说,可通过得到的Active Session Id来创建相应的hToken参数,以此启动的新进程会运行在Active Session Id内,而不会运行在Session 0隔离区内。
当然核心是CreateProcessAsUser函数,其它还会相应用到WTSQueryUserToken,DuplicateTokenEx,CreateEnvironmentBlock,DestroyEnvironmentBlock等函数。基于此,可写出一个供C#直接调用的class。
{_ProcessUtil.cs} 代码如下
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Runtime.InteropServices;using System.Diagnostics;using System.Security.Principal;using System.ComponentModel;using System.IO;namespace dllCoast{ public class _ProcessUtil { #region ~declare struct,enum [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; } [StructLayout(LayoutKind.Sequential)] public struct WTS_SESSION_INFO { public int SessionId; public IntPtr pWinStationName; public WTS_CONNECTSTATE_CLASS State; } public enum SECURITY_IMPERSONATION_LEVEL { SecurityAnonymous, SecurityIdentification, SecurityImpersonation, SecurityDelegation } public enum TOKEN_TYPE { TokenPrimary = 1, TokenImpersonation } public enum WTS_CONNECTSTATE_CLASS { WTSActive, WTSConnected, WTSConnectQuery, WTSShadow, WTSDisconnected, WTSIdle, WTSListen, WTSReset, WTSDown, WTSInit } public enum _SESSION_TYPE { SessionFromActiveConsoleSessionId=0, SessionFromEnumerateSessions, SessionFromProcessExplorerSession } #endregion #region ~delacre const value public const int GENERIC_ALL_ACCESS = 0x10000000; public const int CREATE_NO_WINDOW = 0x08000000; public const int CREATE_UNICODE_ENVIRONMENT = 0x00000400; public const Int32 STANDARD_RIGHTS_REQUIRED = 0x000F0000; public const Int32 STANDARD_RIGHTS_READ = 0x00020000; public const Int32 TOKEN_ASSIGN_PRIMARY = 0x0001; public const Int32 TOKEN_DUPLICATE = 0x0002; public const Int32 TOKEN_IMPERSONATE = 0x0004; public const Int32 TOKEN_QUERY = 0x0008; public const Int32 TOKEN_QUERY_SOURCE = 0x0010; public const Int32 TOKEN_ADJUST_PRIVILEGES = 0x0020; public const Int32 TOKEN_ADJUST_GROUPS = 0x0040; public const Int32 TOKEN_ADJUST_DEFAULT = 0x0080; public const Int32 TOKEN_ADJUST_SESSIONID = 0x0100; public const Int32 TOKEN_READ = (STANDARD_RIGHTS_READ | TOKEN_QUERY); public const Int32 TOKEN_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_QUERY_SOURCE | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT | TOKEN_ADJUST_SESSIONID); public const int MB_ABORTRETRYIGNORE = 0x0002; public const int MB_CANCELTRYCONTINUE =0x0006; public const int MB_HELP =0x4000; public const int MB_OK =0x0000; public const int MB_OKCANCEL= 0x0001; public const int MB_RETRYCANCEL =0x0005; public const int MB_YESNO= 0x0004; public const int MB_YESNOCANCEL =0x0003; #endregion #region ~import functions with Win32 API from win32 system dll [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); [DllImport("kernel32.dll", SetLastError = true)] public static extern int WTSGetActiveConsoleSessionId(); [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); [DllImport("userenv.dll", SetLastError = true)] static extern bool DestroyEnvironmentBlock(IntPtr lpEnvironment); [DllImport("kernel32.dll", SetLastError = true)] public static extern bool CloseHandle(IntPtr handle); [DllImport("advapi32.dll", SetLastError = true)] 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 void WTSFreeMemory(IntPtr pMemory); [DllImport("wtsapi32.dll", SetLastError = true)] public static extern bool WTSEnumerateSessions( IntPtr hServer, int Reserved, int Version, ref IntPtr ppSessionInfo,//WTS_SESSION_INFO PWTS_SESSION_INFO *ppSessionInfo, ref int pCount ); #endregion /// <summary> /// Show a MessageBox on the active UI desktop /// </summary> /// <param name="title">title of the MessageBox</param> /// <param name="message">message to show to the user</param> /// <param name="bWait">indicates if wait for some time by parameter timeout </param> /// <returns>success to return true</returns> public static bool SendMessageBoxToRemoteDesktop(string title, string message, bool bWait) { int pResponse = 0; IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero; return WTSSendMessage(WTS_CURRENT_SERVER_HANDLE, GetSessionIdFromActiveConsoleSessionId(), title, title.Length, message, message.Length, MB_OK, 0, out pResponse, bWait); } /// <summary> /// Show a MessageBox on the active UI desktop /// </summary> /// <param name="title">title of the MessageBox</param> /// <param name="message">message to show to the user</param> /// <param name="button_style">can be a combination of MessageBoxButtons and MessageBoxIcon, need to convert it to int</param> /// <param name="timeout">timeout to determine when to return this function call, 0 means wait until the user response the MessageBox</param> /// <param name="bWait">indicates if wait for some time by parameter timeout </param> /// <returns>success to return true</returns> public static bool SendMessageBoxToRemoteDesktop(string title, string message, int button_style, int timeout, bool bWait) { int pResponse = 0; IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero; return WTSSendMessage(WTS_CURRENT_SERVER_HANDLE, GetSessionIdFromActiveConsoleSessionId(), title, title.Length, message, message.Length, button_style, timeout, out pResponse, bWait); } /// <summary> /// Show a MessageBox on the active UI desktop /// </summary> /// <param name="title">title of the MessageBox</param> /// <param name="message">message to show to the user</param> /// <param name="button_style">can be a combination of MessageBoxButtons and MessageBoxIcon, need to convert it to int</param> /// <param name="timeout">timeout to determine when to return this function call, 0 means wait until the user response the MessageBox</param> /// <param name="pResponse">pointer to receive the button result which clicked by user</param> /// <param name="bWait">indicates if wait for some time by parameter timeout </param> /// <returns>success to return true</returns> public static bool SendMessageBoxToRemoteDesktop(string title, string message, int button_style, int timeout, out int pResponse, bool bWait) { IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero; return WTSSendMessage(WTS_CURRENT_SERVER_HANDLE, GetSessionIdFromActiveConsoleSessionId(), title, title.Length, message, message.Length, button_style, timeout, out pResponse, bWait); } public static int GetSessionIdFromActiveConsoleSessionId() { int dwSessionID = WTSGetActiveConsoleSessionId(); return dwSessionID; } public static int GetSessionIdFromEnumerateSessions() { IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero; int dwSessionId = 0; IntPtr pSessionInfo = IntPtr.Zero; int dwCount = 0; WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, ref pSessionInfo, ref dwCount); Int32 dataSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO)); Int32 current = (int)pSessionInfo; for (int i = 0; i < dwCount; i++) { WTS_SESSION_INFO si = (WTS_SESSION_INFO)Marshal.PtrToStructure( (System.IntPtr)current, typeof(WTS_SESSION_INFO)); if (WTS_CONNECTSTATE_CLASS.WTSActive == si.State) { dwSessionId = si.SessionId; break; } current += dataSize; } WTSFreeMemory(pSessionInfo); return dwSessionId; } public static int GetSessionIdFromExplorerSessionId() { int dwSessionId = 0; Process[] process_array = Process.GetProcessesByName("explorer"); if (process_array.Length>0) { dwSessionId = process_array[0].SessionId; } return dwSessionId; } public static Process CreateProcessAsUser(string filename, string args, _SESSION_TYPE session_method) { IntPtr hToken = IntPtr.Zero;//WindowsIdentity.GetCurrent().Token; int dwSessionId = 0; IntPtr hDupedToken = IntPtr.Zero; Int32 dwCreationFlags = 0; 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); si.lpDesktop = ""; IntPtr lpEnvironment = IntPtr.Zero; string full_filepath = Path.GetFullPath(filename); string working_Dir = Path.GetDirectoryName(full_filepath); try { #region ~ get sessionid switch (session_method) { case _SESSION_TYPE.SessionFromActiveConsoleSessionId: dwSessionId = GetSessionIdFromActiveConsoleSessionId(); break; case _SESSION_TYPE.SessionFromEnumerateSessions: dwSessionId = GetSessionIdFromEnumerateSessions(); break; case _SESSION_TYPE.SessionFromProcessExplorerSession: dwSessionId = GetSessionIdFromExplorerSessionId(); break; default: dwSessionId = GetSessionIdFromActiveConsoleSessionId(); break; } #endregion #region ~ retrieve Token from a specified SessionId bool bResult = WTSQueryUserToken(dwSessionId, out hToken); if (!bResult) { throw new Win32Exception(Marshal.GetLastWin32Error()); } #endregion #region ~ Duplicate from the specified Token if (!DuplicateTokenEx( hToken, GENERIC_ALL_ACCESS, ref sa, (int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, (int)TOKEN_TYPE.TokenPrimary, ref hDupedToken )) throw new Win32Exception(Marshal.GetLastWin32Error()); #endregion #region ~ Create a Environment Block from specifid Token bool result = CreateEnvironmentBlock(out lpEnvironment, hDupedToken, false); if (!result) { lpEnvironment = IntPtr.Zero; //if fail, reset it to Zero } else { dwCreationFlags = CREATE_UNICODE_ENVIRONMENT; //if success, set the CreationsFlags to CREATE_UNICODE_ENVIRONMENT, then pass it into CreateProcessAsUser } #endregion #region ~ Create a Process with Specified Token if (!CreateProcessAsUser( hDupedToken, full_filepath, string.Format("\"{0}\" {1}", filename.Replace("\"", ""), args), ref sa, ref sa, false, dwCreationFlags, lpEnvironment, working_Dir, ref si, ref pi )) throw new Win32Exception(Marshal.GetLastWin32Error()); #endregion #region ~ Destroy the Environment Block which is Created by CreateEnvironment DestroyEnvironmentBlock(lpEnvironment); #endregion return Process.GetProcessById(pi.dwProcessID); } catch(Win32Exception e) { #region ~handle win32 exception int pResponse = 0; string errMsg = "NativeErrorCode:\t" + e.NativeErrorCode + "\n\nSource: " + e.Source + "\n\nMessage: " + e.Message + "\n\nStackTrace: " + e.StackTrace; SendMessageBoxToRemoteDesktop("Win32 Exception!!", errMsg, MB_OK, 0, out pResponse, false); //send the error message to the remote desktop return null; #endregion } finally { #region ~ release hanlde if (pi.hProcess != IntPtr.Zero) CloseHandle(pi.hProcess); if (pi.hThread != IntPtr.Zero) CloseHandle(pi.hThread); if (hDupedToken != IntPtr.Zero) CloseHandle(hDupedToken); #endregion } } }}
- Win 7 下跨越Session Id 0的Windows Service 并与活动Session UI进行交互
- dwr与session的交互
- [Windows PowerShell07]-Windows Session 交互的解决方案
- 查找活动的SQL会话(SESSION),并终止
- PHP SESSION Name 引用SESSION ID(SESSION NAME与SESSION ID的关系) Discuz! 用户(自动)登录原理
- cookie与session学习之三--cookie在win系统下的位置zl)
- session的最大活动时间
- Windows服务启动时,如何与UI进行交互
- session id
- Session ID
- Spring Session实现Session共享下的坑与建议
- 进行session的验证
- Windows NT Session 概念的原理与应用浅析 [1] 遍历并获取信息 [草稿]
- Windows NT Session 概念的原理与应用浅析 [1] 遍历并获取信息
- Windows NT Session 概念的原理与应用浅析 [1] 遍历并获取信息
- Windows NT Session 概念的原理与应用浅析 [1] 遍历并获取信息
- 服务Service---并与活动、接收器之间的通信
- 关于session id的存储
- 第六章实验五
- 算法的力量-李开复
- Oracle 数据泵使用详解
- 黑马程序员——泛型2
- ubuntu 12.04无线网卡驱动问题
- Win 7 下跨越Session Id 0的Windows Service 并与活动Session UI进行交互
- Android使用XML做动画UI
- JNI——实现Java与C的协同工作(三)
- 浙大PAT basic level 解题1001-1005
- 寒假学习计划
- C#判断字符是否为中文方法总结
- <<Java语言程序设计>>进阶篇 Exercise22_3
- dom4j递归遍历XML的所有元素
- linux内核编译的错误收集