穿透 SESSION 0 隔离
注:这套程序在这里只是作为了解,博主本人并未测试,所有测试均来自文章最底层链接的原文章。
查看目标进程运行的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/
穿透 SESSION 0 隔离的更多相关文章
- 解决vista和win7在windows服务中交互桌面权限问题:穿透Session 0 隔离
在某国外大型汽车公司BI项目中,有一个子项目,需要通过大屏幕展示销售报表,程序需要自动启动和关闭.开发人员在开发过程中,发现在Win7的service中不能直接操作UI进程,调查过程中,发现如 ...
- [转]解决vista和win7在windows服务中交互桌面权限问题:穿透Session 0 隔离
服务(Service)对于大家来说一定不会陌生,它是Windows 操作系统重要的组成部分.我们可以把服务想像成一种特殊的应用程序,它随系统的“开启-关闭”而“开始-停止”其工作内容,在这期间无需任何 ...
- 穿透Session 0 隔离(一)
服务(Service)对于大家来说一定不会陌生,它是Windows 操作系统重要的组成部分.我们可以把服务想像成一种特殊的应用程序,它随系统的“开启-关闭”而“开始-停止”其工作内容,在这期间无需任何 ...
- 穿透Session 0 隔离(二)
上一篇我们已经对Session 0 隔离有了进一步认识,如果在开发过程中确实需要服务与桌面用户进行交互,可以通过远程桌面服务的API 绕过Session 0 的隔离完成交互操作. 对于简单的交互,服务 ...
- 远程线程注入DLL突破session 0 隔离
远程线程注入DLL突破session 0 隔离 0x00 前言 补充上篇的远程线程注入,突破系统SESSION 0 隔离,向系统服务进程中注入DLL. 0x01 介绍 通过CreateRemoteTh ...
- 恶意软件开发——突破SESSION 0 隔离的远线程注入
一.前言 在Windows XP,Windows Server 2003以及更早的版本中,第一个登录的用户以及Windows的所有服务都运行在Session 0上,这样的做法导致用户使用的应用程序可能 ...
- 关于突破 SESSION 0 隔离场景发现的一些问题
0x00 Tricks 0x01 用ZwCreateThreadEx 在 Windows 10 下直接通过管理员权限+SeDebugPrivilege启用. 0x02 用CreateRemoteThr ...
- C#穿透session隔离———Windows服务启动UI交互程序
在Windows服务里面启动其他具有界面的应用程序,需要穿透session隔离,尝试了很多种方法,都可行,现在一一列举下来,并写下几个需要注意的地方. 需要注意的地方 首先要将服务的Account属性 ...
- 面试问题:Vista与XP的Session 0与Session X的区别
面试问题:Vista与XP的Session 0与Session X的区别 在XXXXX的一次面试中,笔试问题的题目曾提到Session 0.Session 1在Vista和Xp中的区别?现在把答案发上 ...
- 远程线程注入dll,突破session 0
前言 之前已经提到过,远线程注入和内存写入隐藏模块,今天介绍突破session 0的dll注入 其实今天写这个的主要原因就是看到倾旋大佬有篇文章提到:有些反病毒引擎限制从lsass中dump出缓存,可 ...
随机推荐
- vue 3版本如何下载静态文件
1.现将静态文件放到 public 目录下的static文件夹 2.<a href="/static/test.zip" target="_blank" ...
- HTTP Headers Content-Type 详解
https://www.cnblogs.com/whosmeya/p/14315632.html
- netstat 命令查看端口状态详解
转载请注明出处: netstat 可以查看服务器当前端口列表及指定端口的连接状态等: -t : 指明显示TCP端口,t是TCP的首字母. -u : 指明显示UDP端口,u是UDP的首字母 -p : 显 ...
- NewStarCTF 2023 公开赛道 WEEK4|CRYPTO WP
RSA Variation II 1.题目信息 提示:"Schmidt Samoa" 附件信息 from secret import flag from Crypto.Util.n ...
- Laravel - Eloquent 模型查询
Laravel 的 Eloquent ORM 提供了漂亮.简洁的 ActiveRecord 实现来和数据库进行交互.每个数据库表都有一个对应的「模型」可用来跟数据表进行交互.你可以通过模型查找数据表内 ...
- 使用requests爬虫遇到的一个奇葩的问题:UnicodeEncodeError: 'latin-1' codec can't encode character
每一位成功的程序员,背后也许都站着无数的秃头的男人--为其提供各种开发工具&代码库,当然也包括-- 各种玄学bug-- 玄学的开端 最近在用Python做一个爬虫项目的时候遇到一个很奇怪的问题 ...
- [转帖]京东大佬细说:Nginx反向代理时保持长连接,看完直呼"学到了!"
https://mp.weixin.qq.com/s?__biz=MzU1MzE2NzIzMg==&mid=2247488405&idx=1&sn=7081ff4e0ac1de ...
- bcc的简单学习
bcc的简单学习 安装 # 安装部分依赖项目 yum install cmake llvm -y dnf install -y bison cmake ethtool flex git iperf3 ...
- [转帖]linux系统主机双网卡实现路由转发问题与解决
1. 环境 拓扑: 网卡配置: host1: 192.168.1.1/24 host2: 左eth0: 192.168.1.2/24 右eth1: 192.168.2.2/24 host3: 192. ...
- Windows 挂载minio 到本地磁盘
Windows 挂载minio 到本地磁盘 背景 新公司建议使用minio 进行一些业务操作 已经在各位领导同事的帮助下找到了linux本地s3fs挂载和k8s使用csi方式挂载到pod内的方式. 今 ...