您的位置:首页 > 其它

创建用户桌面进程(突破Session 0隔离)

2014-10-29 11:20 417 查看
这里就引入了Windows Vista之后的Session 0隔离机制。

有些服务可能需要在用户界面上显示对话框,或需要与用户的应用程序通讯,这种类型的功能“通常”属于Windows XP服务,因为在Windows XP中,这样做很容易。如果服务恰好需要显示某些用户界面对象,例如对话框,或者需要与应用程序通讯,则在Windows 7下运行可能会遇到问题。

如果在Windows 7上运行需要显示对话框的服务,此时并不能看到所需的对话框,在任务栏会上看到一个闪烁的图标。而且,如果单击这个闪烁图标,还会看到一个对话框。更具体来说,在Windows 7中运行时,可能会遇到下列一个或多个现象,这个服务可能会:

正常运行,但无法完成既定工作,同时耗费大量CPU时钟和内存。
正常运行,但其他进程无法与该服务通讯,该服务业无法与用户或其他应用程序/服务通讯。
尝试通过Windows消息机制与用户应用程序通讯,但Windows消息机制无法实现目标。
在任务栏上显示一个闪烁的图标,代表该服务希望与桌面交互。

在Windows XP、Windows Server 2003,以及更老版本的Windows操作系统中,服务和应用程序使用相同的会话(Session)运行,而这个会话是由第一个登录到控制台的用户启动的。该会话就叫做Session
0,如下图所示,在Windows Vista之前,Session 0不仅包含服务,也包含标准用户应用程序。

如图所示:



将服务和用户应用程序一起在Session 0中运行会导致安全风险,因为服务会使用提升后的权限运行,而用户应用程序使用用户特权(大部分都是非管理员用户)运行,这会使得恶意软件以某个服务为攻击目标,通过“劫持”该服务,达到提升自己权限级别的目的。

从Windows Vista开始,只有服务可以托管到Session 0中,用户应用程序和服务之间会被隔离,并需要运行在用户登录到系统时创建的后续会话中。例如第一个登录的用户创建 Session 1,第二个登录的用户创建Session 2,以此类推,如下图所示。



使用不同会话运行的实体(应用程序或服务)如果不将自己明确标注为全局命名空间,并提供相应的访问控制设置,将无法互相发送消息,共享UI元素,或共享内核对象。这一过程如下图所示:



上一篇中,因为我们使用全局的命名空间,因此实现的共享内核对象。

关于Session 0,就简单介绍这些,具体的详细内容,请查阅微软的官方文档。我们今天的重点并不在此。

回到正題。虽然Windows 7的Session 0给服务层和应用层的通信造成了很大的难度,但并不代表没有办法实现服务层与应用层的通信与交互。

微软提供了一系列WTS( Windows Terminal Service Windows终端服务)开头的函数,来完成服务层与应用层的交互。

如图所示:



实现代码如下:

[cpp] view
plaincopyprint?

DWORD _stdcall CATLDemoServiceModule::LaunchWin7SessionProcess( LPTSTR lpCommand )

{

DWORD dwRet = 0;

PROCESS_INFORMATION pi;

STARTUPINFO si;

DWORD dwSessionId;//当前会话的ID

HANDLE hUserToken = NULL;//当前登录用户的令牌

HANDLE hUserTokenDup = NULL;//复制的用户令牌

HANDLE hPToken = NULL;//进程令牌

DWORD dwCreationFlags;

//得到当前活动的会话ID,即登录用户的会话ID

dwSessionId = WTSGetActiveConsoleSessionId();

do

{

WTSQueryUserToken(dwSessionId,&hUserToken);//读取当前登录用户的令牌信息

dwCreationFlags = NORMAL_PRIORITY_CLASS|CREATE_NEW_CONSOLE;//创建参数

ZeroMemory(&si,sizeof(STARTUPINFO));

ZeroMemory(&pi,sizeof(pi));

si.cb = sizeof(STARTUPINFO);

si.lpDesktop = L"winsta0\\default";//指定创建进程的窗口站,Windows下唯一可交互的窗口站就是WinSta0\Default

TOKEN_PRIVILEGES tp;

LUID luid;

//打开进程令牌

if (!OpenProcessToken(GetCurrentProcess(),

TOKEN_ADJUST_PRIVILEGES|

TOKEN_QUERY|

TOKEN_DUPLICATE|

TOKEN_ASSIGN_PRIMARY|

TOKEN_ADJUST_SESSIONID|

TOKEN_READ|

TOKEN_WRITE,&hPToken))

{

dwRet = GetLastError();

break;

}

//查找DEBUG权限的UID

if (!LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&luid))

{

dwRet = GetLastError();

break;

}

//设置令牌信息

tp.PrivilegeCount = 1;

tp.Privileges[0].Luid = luid;

tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

//复制当前用户的令牌

if (!DuplicateTokenEx(hPToken,MAXIMUM_ALLOWED,NULL,SecurityIdentification,

TokenPrimary,&hUserTokenDup))

{

dwRet = GetLastError();

break;

}

//设置当前进程的令牌信息

if (!SetTokenInformation(hUserTokenDup,TokenSessionId,(void*)&dwSessionId,sizeof(DWORD)))

{

dwRet = GetLastError();

break;

}

//应用令牌权限

if (!AdjustTokenPrivileges(hUserTokenDup,FALSE,&tp,sizeof(TOKEN_PRIVILEGES),

(PTOKEN_PRIVILEGES)NULL,NULL))

{

dwRet = GetLastError();

break;

}

//创建进程环境块,保证环境块是在用户桌面的环境下

LPVOID pEnv = NULL;

if (CreateEnvironmentBlock(&pEnv,hUserTokenDup,TRUE))

{

dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;

}

else

{

pEnv = NULL;

}

//创建用户进程

if (!CreateProcessAsUser(hUserTokenDup,NULL,lpCommand,NULL,NULL,FALSE,

dwCreationFlags,pEnv,NULL,&si,&pi))

{

dwRet = GetLastError();

break;

}

} while (0);

//关闭句柄

if (NULL != hUserToken)

{

CloseHandle(hUserToken);

}

if (NULL != hUserTokenDup)

{

CloseHandle(hUserTokenDup);

}

if (NULL != hPToken)

{

CloseHandle(hPToken);

}

return dwRet;

}

调用方式:

[cpp] view
plaincopyprint?

LaunchWin7SessionProcess("C:\\Windows\\notepad.exe");

这样,服务程序就可以在用户的桌面上创建一个应用程序了,而且具有System权限。

总结一下服务程序与用户桌面程序通信的解决办法:

如果服务需要通过发送消息的方式与用户交互,请使用WTSSendMessage函数。在功能上,该函数几乎与MessageBox相同,对于不需要过于完整的UI的服务,这是一种简单易行的方法,而且足够安全,因为所显示的信息框无法用于夺取对底层服务的控制。
如果您的服务需要更完整的UI,请使用CreateProcessAsUser函数在发起请求的用户的桌面上创建进程。但是这里需要注意,您依然需要在新创建的进程和原始服务之间进行通讯,因此这里就需要用到下一点内容。
如果需要双向交互,请使用Windows Communication Foundation(WCF),这是一种用于.NET Remoting命名管线或任何其他进程间通讯(IPC)的机制(Windows消息除外),可用于实现跨会话的通讯。如果需要提升,WCF和Remoting是一种比直接提升用户(假设UAC没有过关闭)更安全的做法。
请确保需要跨会话共享的内核对象使用了带有Global\字符串的名称前缀,该字符串意味着这个对象属于会话全局(Session-Global)命名空间。

至些,关于服务程序与用户桌面程序的相关技术点,总结完毕,欢迎大家补充和提出改进意见。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: