ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

穿透 SESSION 0 隔离

2022-04-05 22:02:00  阅读:242  来源: 互联网

标签:IntPtr 隔离 Int32 int 穿透 SESSION Session public string


注:这套程序在这里只是作为了解,博主本人并未测试,所有测试均来自文章最底层链接的原文章。

查看目标进程运行的SESSION位置

在实际开发过程中,可以通过Process Explorer 检查服务或程序处于哪个Session,会不会遇到Session 0 隔离问题。可以看到svchost.exe处于SESSION 0。QQ作为用户进程运行在SESSION 1。

 

 


 

测试程序

下面来做一个名叫AlertService 的服务,它的作用就是向用户发出一个提示对话框,我们看看这个服务在Windows 7 中会发生什么情况。

using System.ServiceProcess;
using System.Windows.Forms;

namespace AlertService
{
    public partial class Service1 : ServiceBase
    {
        public Service1()
        {
            InitializeComponent();
        }

        protected override void OnStart(string[] args)
        {
            MessageBox.Show("A message from AlertService.");
        }

        protected override void OnStop()
        {
        }
    }
}

 


 

穿透 SESSION 0 隔离

对于简单的交互,服务可以通过 WTSSendMessage 函数,在用户 Session 上显示消息窗口。
对于一些复杂的UI 交互,必须调用CreateProcessAsUser 或其他方法(WCF、.NET远程处理等)进行跨 Session 通信,在桌面用户上创建一个应用程序界面。

 


 

WTSSendMessage 函数

如果服务只是简单的向桌面用户 Session 发送消息窗口,则可以使用WTSSendMessage 函数实现。首先,加入如下代码:

    public static IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero;

    public static void ShowMessageBox(string message, string title)
    {
        int resp = 0;

        // 调用WTSSendMessage函数
        WTSSendMessage(
            WTS_CURRENT_SERVER_HANDLE,
            WTSGetActiveConsoleSessionId(),
            title, title.Length,
            message, message.Length,
            0, 0, out resp, false);
    }


    // 获取kernel32.dll的导出函数WTSGetActiveConsoleSessionId
    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern int WTSGetActiveConsoleSessionId();

    // 获取wtsapi32.dll的导出函数WTSSendMessage
    [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 函数中使用,加入下面代码:

protected override void OnStart(string[] args)
{
    ShowMessageBox("This a message from AlertService.","AlertService Message");
}

 

编译程序后在服务管理器中重新启动AlertService 服务,从下图中可以看到消息窗口是在当前用户桌面显示的,而不是Session 0 中。

 

 

 


 

CreateProcessAsUser 函数

如果想通过服务向桌面用户 Session 创建一个复杂 UI 程序界面,则需要使用 CreateProcessAsUser 函数为用户创建一个新进程用来运行相应的程序。需要注意这个CreateProcessAsUser需要system权限,仅admin是不行的,虽然网上有例子说可以手动提升admin权限,但我在本地安全策略里添加了相关权限依旧不行。

CreateProcessAsUser,打开程序之前需要一个令牌降权,有两种方法,一个是获取用户的令牌

dwSessionID = ::WTSGetActiveConsoleSessionId();//获取用户id
if (FALSE == ::WTSQueryUserToken(dwSessionID, &hToken)//hToken获取到令牌
{
    int i = GetLastError();//获取错误编码
}

 

另一种方法是通过获取其他程序的令牌,当然这种很有可能出现意外

LPWSTR lpa = ConvertCharToLPWSTR(_T("EXPLORER.EXE"))
GetTokenByName(hToken, lpa)
BOOL   GetTokenByName(HANDLE   &hToken, LPWSTR   lpName)
{
	if (!lpName)
	{
		return   FALSE;
	}
	HANDLE  hProcessSnap = NULL;
	BOOL  bRet = FALSE;
	PROCESSENTRY32   pe32 = { 0 };
	hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	if (hProcessSnap == INVALID_HANDLE_VALUE)
		return   (FALSE);

	pe32.dwSize = sizeof(PROCESSENTRY32);

	if (Process32First(hProcessSnap, &pe32))
	{
		do
		{
			CString exefile = pe32.szExeFile;
			CString paraname = lpName;
			LPCSTR cname = wtoc(lpName).c_str;
			if (!exefile.CompareNoCase(cname))
			{
				HANDLE   hProcess =
					OpenProcess(PROCESS_QUERY_INFORMATION,
						FALSE, pe32.th32ProcessID);
				bRet = OpenProcessToken(hProcess, TOKEN_ALL_ACCESS, &hToken);
				CloseHandle(hProcessSnap);
				return   (bRet);
			}
		} while (Process32Next(hProcessSnap, &pe32));
		bRet = TRUE;
	}
	else
		bRet = FALSE;

	CloseHandle(hProcessSnap);
	return   (bRet);
}

  

这里使用的是第一种方案。

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 = WTSGetActiveConsoleSessionId();            // 获得当前Session ID
    result = WTSQueryUserToken(dwSessionID, out hToken);        // 获得当前Session的用户令牌

    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);        // 创建用户Session环境

    if (!result)
    {
        ShowMessageBox("CreateEnvironmentBlock failed", "AlertService Message");
    }

    // 在复制的用户Session下执行应用程序,创建进程
    result = CreateProcessAsUser(
        hDupedToken,
        app,
        String.Empty,
        ref sa, ref sa,
        false, 0, IntPtr.Zero,
        path, 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);

 

在CreateProcess 函数中同时也涉及到DuplicateTokenEx、WTSQueryUserToken、CreateEnvironmentBlock 函数的使用,有兴趣的朋友可通过MSDN 进行学习。完成CreateProcess 函数创建后,就可以真正的通过它来调用应用程序了,回到Service1.cs 修改一下OnStart 我们来打开一个CMD 窗口。如下代码:

protected override void OnStart(string[] args)
{
    Interop.CreateProcess("cmd.exe", @"C:\Windows\System32\");
}

 

     重新编译程序,启动AlertService 服务便可看到下图界面。至此,我们已经可以通过一些简单的方法对Session 0 隔离问题进行解决。大家也可以通过WCF 等技术完成一些更复杂的跨Session 通信方式,实现在Windows 7 及Vista 系统中服务与桌面用户的交互操作。

 

 

 

参考文章链接:
https://www.cnblogs.com/gnielee/archive/2010/04/07/session0-isolation-part1.html/
https://www.cnblogs.com/moshuixiong/p/12169281.html/

 

标签:IntPtr,隔离,Int32,int,穿透,SESSION,Session,public,string
来源: https://www.cnblogs.com/renleiguanchashi/p/16087588.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有