【问题标题】:WTSGetActiveConsoleSessionId returning system sessionWTSGetActiveConsoleSessionId 返回系统会话
【发布时间】:2012-01-08 16:35:27
【问题描述】:

我曾经遇到过这个问题,但我仍然不知道如何解决它。我有一个windows服务,当服务运行时,它首先需要模拟登录用户(活动用户)来加载一些保存在用户应用程序数据文件夹中的路径和设置。每次新用户登录到 Windows 时,我使用的代码都能完美运行,除非服务模拟错误并模拟系统会话而不是活动会话。正如我所说,这种情况只发生过一次,但我真的不知道为什么。

这是检查活动会话的方式以及模拟是如何完成的:

首先,当服务检测到登录事件时,它会通过调用查询活动会话 ID

WTSGetActiveConsoleSessionId();

然后它通过调用 WTSQuerySessionInformation 来检查会话是否处于活动状态(已连接),如下所示:

WTS_CONNECTSTATE_CLASS wts_connect_state = WTSDisconnected;
WTS_CONNECTSTATE_CLASS* ptr_wts_connect_state = NULL;

DWORD bytes_returned = 0;
if (::WTSQuerySessionInformation(
    WTS_CURRENT_SERVER_HANDLE,
    session_id,
    WTSConnectState,
    reinterpret_cast<LPTSTR*>(&ptr_wts_connect_state),
    &bytes_returned)) 
{
        ASSERT(bytes_returned == sizeof(*ptr_wts_connect_state));
        wts_connect_state = *ptr_wts_connect_state;
        ::WTSFreeMemory(ptr_wts_connect_state);
        return (WTSActive == wts_connect_state); 
}

其中 session_id 是 WTSGetActiveConsoleSessionId() 返回的会话 ID。

然后我使用WTSQueryUserToken查询用户令牌

如果成功,服务调用GetTokenInformation如下:

DWORD neededSize = 0;
    HANDLE *realToken = new HANDLE;
    if(GetTokenInformation(hImpersonationToken, (::TOKEN_INFORMATION_CLASS) TokenLinkedToken, realToken, sizeof(HANDLE), &neededSize))
    {
        CloseHandle(hImpersonationToken);
        hImpersonationToken = *realToken;
    }

其中 hImpersonationToken 是从 GetTokenInformation 检索到的令牌

如果上述所有方法都成功,则调用

DuplicateTokenEx( hImpersonationToken,
                                0,
                                NULL,
                                SecurityImpersonation,
                                TokenPrimary,
                                phUserToken );

        CloseHandle( hImpersonationToken );

如果成功,则使用检索到的令牌进行模拟

ImpersonateLoggedOnUser(phUserToken);

我的服务写入一个日志文件,并根据日志所有先前的调用成功但在模拟之后服务加载系统配置文件而不是用户。

现在这个问题在我重新启动机器时发生过一次,但我什至没有再次重现它,我已经尝试了几个星期。

我不确定系统配置文件会话如何成为活动会话。我只是想知道我在那里做错了什么,不确定我在查询会话信息时是否使用了错误的信息类。

还想知道是否有可能在使用返回的令牌进行模拟之前确定查询的会话是否实际上是系统会话,以便可以再次重试模拟?

正如我所说,所有提到的调用都在进入下一步之前检查了它们的返回对象和代码,因此它们不是来自调用的任何错误,因为它不应该继续进行模拟,但确实如此:(

希望能提供任何帮助...谢谢。

【问题讨论】:

    标签: c++ windows windows-7 windows-services impersonation


    【解决方案1】:

    WTSGetActiveConsoleSessionId() 从服务运行时实际上可能返回会话 0,具体取决于它运行的 Windows 版本。

    做你想做的事情的方法是枚举所有会话找到连接的会话(会话 0 断开连接),然后模拟该会话的用户。下面的代码在我的盒子上运行良好。

    //Function to run a process as active user from windows service
    void ImpersonateActiveUserAndRun(WCHAR* path, WCHAR* args)
    {
        DWORD session_id = -1;
        DWORD session_count = 0;
    
        WTS_SESSION_INFOA *pSession = NULL;
    
    
        if (WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pSession, &session_count))
        {
            //log success
        }
        else
        {
            //log error
            return;
        }
    
        for (int i = 0; i < session_count; i++)
        {
            session_id = pSession[i].SessionId;
    
            WTS_CONNECTSTATE_CLASS wts_connect_state = WTSDisconnected;
            WTS_CONNECTSTATE_CLASS* ptr_wts_connect_state = NULL;
    
            DWORD bytes_returned = 0;
            if (::WTSQuerySessionInformation(
                WTS_CURRENT_SERVER_HANDLE,
                session_id,
                WTSConnectState,
                reinterpret_cast<LPTSTR*>(&ptr_wts_connect_state),
                &bytes_returned))
            {
                wts_connect_state = *ptr_wts_connect_state;
                ::WTSFreeMemory(ptr_wts_connect_state);
                if (wts_connect_state != WTSActive) continue;
            }
            else
            {
                //log error
                continue;
            }
    
            HANDLE hImpersonationToken;
    
            if (!WTSQueryUserToken(session_id, &hImpersonationToken))
            {
                //log error
                continue;
            }
    
    
            //Get real token from impersonation token
            DWORD neededSize1 = 0;
            HANDLE *realToken = new HANDLE;
            if (GetTokenInformation(hImpersonationToken, (::TOKEN_INFORMATION_CLASS) TokenLinkedToken, realToken, sizeof(HANDLE), &neededSize1))
            {
                CloseHandle(hImpersonationToken);
                hImpersonationToken = *realToken;
            }
            else
            {
                //log error
                continue;
            }
    
    
            HANDLE hUserToken;
    
            if (!DuplicateTokenEx(hImpersonationToken,
                //0,
                //MAXIMUM_ALLOWED,
                TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS | MAXIMUM_ALLOWED,
                NULL,
                SecurityImpersonation,
                TokenPrimary,
                &hUserToken))
            {
                //log error
                continue;
            }
    
            // Get user name of this process
            //LPTSTR pUserName = NULL;
            WCHAR* pUserName;
            DWORD user_name_len = 0;
    
            if (WTSQuerySessionInformationW(WTS_CURRENT_SERVER_HANDLE, session_id, WTSUserName, &pUserName, &user_name_len))
            {
                //log username contained in pUserName WCHAR string
            }
    
            //Free memory                         
            if (pUserName) WTSFreeMemory(pUserName);
    
            ImpersonateLoggedOnUser(hUserToken);
    
            STARTUPINFOW StartupInfo;
            GetStartupInfoW(&StartupInfo);
            StartupInfo.cb = sizeof(STARTUPINFOW);
            //StartupInfo.lpDesktop = "winsta0\\default";
    
            PROCESS_INFORMATION processInfo;
    
            SECURITY_ATTRIBUTES Security1;
            Security1.nLength = sizeof SECURITY_ATTRIBUTES;
    
            SECURITY_ATTRIBUTES Security2;
            Security2.nLength = sizeof SECURITY_ATTRIBUTES;
    
            void* lpEnvironment = NULL;
    
            // Get all necessary environment variables of logged in user
            // to pass them to the new process
            BOOL resultEnv = CreateEnvironmentBlock(&lpEnvironment, hUserToken, FALSE);
            if (!resultEnv)
            {
                //log error
                continue;
            }
    
            WCHAR PP[1024]; //path and parameters
            ZeroMemory(PP, 1024 * sizeof WCHAR);
            wcscpy(PP, path);
            wcscat(PP, L" ");
            wcscat(PP, args);
    
            // Start the process on behalf of the current user 
            BOOL result = CreateProcessAsUserW(hUserToken, 
                NULL,
                PP,
                //&Security1,
                //&Security2,
                NULL,
                NULL,
                FALSE, 
                NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE,
                //lpEnvironment,
                NULL,
                //"C:\\ProgramData\\some_dir",
                NULL,
                &StartupInfo,
                &processInfo);
    
            if (!result)
            {
                //log error
            }
            else
            {
                //log success
            }
    
            DestroyEnvironmentBlock(lpEnvironment);
    
            CloseHandle(hImpersonationToken);
            CloseHandle(hUserToken);
            CloseHandle(realToken);
    
            RevertToSelf();
        }
    
        WTSFreeMemory(pSession);
    }
    

    【讨论】:

    • 您是否有任何知识或文档说明在什么情况下您会得到 0? Doc 说如果控制台没有附加会话,您可能得到 0xFFFFFFFF。
    • 这个答案是救命稻草!但是,当pSession[i] 已经有一个叫State 的成员时,为什么还要叫::WTSQuerySessionInformation
    • @ncalmbeblpaicr0011 在大多数情况下它可能不会产生差异,但您希望会话状态信息尽可能新鲜。 pSession[] 只是保存了在枚举循环期间生成的一堆 WTS_SESSION_INFO 结构
    猜你喜欢
    • 2012-03-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-13
    • 2012-02-23
    • 2014-12-10
    相关资源
    最近更新 更多