技术开发 频道

VS与Win7共舞:系统服务的Session 0隔离

  显示更复杂的UI

  如果我们不满足于仅仅显示一个消息对话框,而需要从系统服务显示一个更加复杂的用户界面,这时我们可以使用CreateProcessAsUser函数在用户的桌面上创建一个新的进程来显示更加复杂的用户界面,而这个进程虽然是由系统服务创建,但是却是运行在用户环境下。以下的代码演示了创建进程显示复杂UI的过程:

DWORD WINAPI TimeServiceThread(LPVOID)
{
    
while (!g_Stop)
    {
        Sleep(
5000);
        

        
// 为了显示更加复杂的用户界面,我们需要从Session 0创建
        
// 一个进程,但是这个进程是运行在用户环境下。
        
// 我们可以使用CreateProcessAsUser实现这一功能。

        BOOL bSuccess
= FALSE;
        STARTUPINFO si
= {0};
        
// 进程信息
        PROCESS_INFORMATION pi = {0};
        si.cb
= sizeof(si);

        
// 获得当前Session ID
        DWORD dwSessionID = WTSGetActiveConsoleSessionId();

        HANDLE hToken
= NULL;
        
// 获得当前Session的用户令牌
        if (WTSQueryUserToken(dwSessionID, &hToken) == FALSE)
        {
            
goto Cleanup;
        }

        
// 复制令牌
        HANDLE hDuplicatedToken = NULL;
        
if (DuplicateTokenEx(hToken,
                   MAXIMUM_ALLOWED, NULL,
                   SecurityIdentification, TokenPrimary,
                  
&hDuplicatedToken) == FALSE)
        {
            
goto Cleanup;
        }

        
// 创建用户Session环境
        LPVOID lpEnvironment = NULL;
        
if (CreateEnvironmentBlock(&lpEnvironment,
                     hDuplicatedToken, FALSE)
== FALSE)
        {
            
goto Cleanup;
        }

        
// 获得复杂界面的名字,也就是获得可执行文件的路径
        WCHAR lpszClientPath[MAX_PATH];
        
if (GetModuleFileName(NULL, lpszClientPath, MAX_PATH) == 0)
        {
            
goto Cleanup;
        }
        PathRemoveFileSpec(lpszClientPath);
        wcscat_s(lpszClientPath,
                  
sizeof(lpszClientPath)/sizeof(WCHAR),
                    L
"\\TimeServiceClient.exe");

        
// 在复制的用户Session下执行应用程序,创建进程。
        
// 通过这个进程,就可以显示各种复杂的用户界面了
        if (CreateProcessAsUser(hDuplicatedToken,
                   lpszClientPath, NULL, NULL, NULL, FALSE,                    
                 NORMAL_PRIORITY_CLASS
| CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT,
         lpEnvironment, NULL,
&si, &pi) == FALSE)
        {
            
goto Cleanup;
        }

        CloseHandle(pi.hProcess);
        CloseHandle(pi.hThread);
        bSuccess
= TRUE;

// 清理工作

Cleanup:
        
if (!bSuccess)
        {
            ShowMessage(L
"无法创建复杂UI", L"错误");
        }

        
if (hToken != NULL)
            CloseHandle(hToken);
        
if (hDuplicatedToken != NULL)
            CloseHandle(hDuplicatedToken);
        
if (lpEnvironment != NULL)
            DestroyEnvironmentBlock(lpEnvironment);
    }

    
return 0;
}

  在这段代码中,我们首先获得了当前的Session ID,然后通过Session ID,我们获得用户令牌。有了用户令牌后,我们就可以创建一个相同的用户环境了,而最终我们所创建的复杂界面进程将在这个环境下运行和显示。完成这些准备工作后,我们利用CreateProcessAsUser函数在复制的用户环境下创建新的进程,显示复杂的用户界面。用这种方式创建的进程,不会受到“Interactive Service Detection”对话框的打扰而直接显示到用户桌面上,这跟从当前用户Session执行应用程序并无太大的差别。

  图5  从系统服务显示的复杂界面

0
相关文章