windows WTSGetActiveConsoleSessionId 返回系统会话

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8309043/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 18:33:13  来源:igfitidea点击:

WTSGetActiveConsoleSessionId returning system session

c++windowswindows-7windows-servicesimpersonation

提问by Zaid Amir

I have this problem that happened once and I still don't know how to fix it. I have a windows service, when the service runs, it first need to impersonate the logged in user (active user) to load some paths and settings that are saved in the user's application data folder. The code that I'm using works perfectly every time a new user logs on to windows except once where the service got the impersonation wrong and impersonated the system session instead of the actie one. As I said this only happened once, but I can't really tell why.

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

This is how am checking whats the active session and how the impersonation is done:

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

first when the service detects a logon event it query's the active session ID by calling

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

WTSGetActiveConsoleSessionId();

then It checks if the session is active(connected) by calling WTSQuerySessionInformation as follows:

然后它通过调用 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); 
}

where session_id is the session ID returned by WTSGetActiveConsoleSessionId().

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

Then I query for the user token using WTSQueryUserToken

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

Then if that succeeds the service calls GetTokenInformationas follows:

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

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

where hImpersonationToken is the token retrieved from GetTokenInformation

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

And if all the above succeeds it then calls

如果以上所有操作都成功,则调用

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

        CloseHandle( hImpersonationToken );

and if it succeeds then it impersonates with the retrieved token

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

ImpersonateLoggedOnUser(phUserToken);

My service writes to a log file and according to the log all the previous calls where successful yet after the impersonation the service loaded the system profile instead of the user.

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

Now this issue happened once when I restarted my machine, yet I wasn't even reproduce it again and I've been trying for weeks.

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

I'm not sure how its possible for the system profile session to be an active session. I just want to know if I'm doing something wrong there, not sure if I'm using the wrong info class when I'm querying the session info or something.

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

Also want to know if its possible to determine if the queried session is actually the system session before impersonating with the returned token just so one can retry the impersonation again?

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

As I said, all mentioned calls have their return objects and codes checked before moving to the next step so their weren't any errors from the calls as it shouldn't continue with the impersonation, yet it did :(

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

Would appreciate any help possible... thanks.

将不胜感激任何可能的帮助...谢谢。

回答by Alex Fotios

WTSGetActiveConsoleSessionId() may actually return session 0 when run from a service and depending on the Windows version it runs on.

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

The way to do what you want is enumerate all sessions find the one that is connected (session 0 is disconnected) and then impersonate the user of that session. Code below works well on my box.

做您想做的事情的方法是枚举所有会话,找到已连接的会话(会话 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);
}