草庐IT

c++ - WTSGetActiveConsoleSessionId 返回系统 session

coder 2023-11-11 原文

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

这是检查事件 session 是什么以及模拟是如何完成的:

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

WTSGetActiveConsoleSessionId();

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

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() 返回的 session ID。

然后我使用 WTSQueryUserToken

查询用户 token

然后,如果成功,服务调用 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 中检索到的 token

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

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

        CloseHandle( hImpersonationToken );

如果它成功了,那么它会模拟检索到的 token

ImpersonateLoggedOnUser(phUserToken);

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

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

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

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

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

非常感谢任何可能的帮助...谢谢。

最佳答案

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

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

//从windows服务以事件用户身份运行进程的功能 void ImpersonateActiveUserAndRun(WCHAR* 路径,WCHAR* 参数) { DWORD session_id = -1; DWORD session_count = 0;

WTS_SESSION_INFOW *pSession = NULL;


if (WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pSession, &session_count))
{
    //log success
}
else
{
    //log error
    return;
}

for (size_t 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_s(PP, path);
    wcscat_s(PP, L" ");
    wcscat_s(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);

关于c++ - WTSGetActiveConsoleSessionId 返回系统 session ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8309043/

有关c++ - WTSGetActiveConsoleSessionId 返回系统 session的更多相关文章

  1. ruby - 为什么 4.1%2 使用 Ruby 返回 0.0999999999999996?但是 4.2%2==0.2 - 2

    为什么4.1%2返回0.0999999999999996?但是4.2%2==0.2。 最佳答案 参见此处:WhatEveryProgrammerShouldKnowAboutFloating-PointArithmetic实数是无限的。计算机使用的位数有限(今天是32位、64位)。因此计算机进行的浮点运算不能代表所有的实数。0.1是这些数字之一。请注意,这不是与Ruby相关的问题,而是与所有编程语言相关的问题,因为它来自计算机表示实数的方式。 关于ruby-为什么4.1%2使用Ruby返

  2. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  3. ruby - 检查字符串是否包含散列中的任何键并返回它包含的键的值 - 2

    我有一个包含多个键的散列和一个字符串,该字符串不包含散列中的任何键或包含一个键。h={"k1"=>"v1","k2"=>"v2","k3"=>"v3"}s="thisisanexamplestringthatmightoccurwithakeysomewhereinthestringk1(withspecialcharacterslike(^&*$#@!^&&*))"检查s是否包含h中的任何键的最佳方法是什么,如果包含,则返回它包含的键的值?例如,对于上面的h和s的例子,输出应该是v1。编辑:只有字符串是用户定义的。哈希将始终相同。 最佳答案

  4. ruby - Ruby 中的隐式返回值是怎么回事? - 2

    所以我开始关注ruby​​,很多东西看起来不错,但我对隐式return语句很反感。我理解默认情况下让所有内容返回self或nil但不是语句的最后一个值。对我来说,它看起来非常脆弱(尤其是)如果你正在使用一个不打算返回某些东西的方法(尤其是一个改变状态/破坏性方法的函数!),其他人可能最终依赖于一个返回对方法的目的并不重要,并且有很大的改变机会。隐式返回有什么意义?有没有办法让事情变得更简单?总是有返回以防止隐含返回被认为是好的做法吗?我是不是太担心这个了?附言当人们想要从方法中返回特定的东西时,他们是否经常使用隐式返回,这不是让你组中的其他人更容易破坏彼此的代码吗?当然,记录一切并给出

  5. ruby-on-rails - ruby 日期方程不返回预期的真值 - 2

    为什么以下不同?Time.now.end_of_day==Time.now.end_of_day-0.days#falseTime.now.end_of_day.to_s==Time.now.end_of_day-0.days.to_s#true 最佳答案 因为纳秒数不同:ruby-1.9.2-p180:014>(Time.now.end_of_day-0.days).nsec=>999999000ruby-1.9.2-p180:015>Time.now.end_of_day.nsec=>999999998

  6. ruby - 从 String#split 返回的零长度字符串 - 2

    在Ruby1.9.3(可能还有更早的版本,不确定)中,我试图弄清楚为什么Ruby的String#split方法会给我某些结果。我得到的结果似乎与我的预期相反。这是一个例子:"abcabc".split("b")#=>["a","ca","c"]"abcabc".split("a")#=>["","bc","bc"]"abcabc".split("c")#=>["ab","ab"]在这里,第一个示例返回的正是我所期望的。但在第二个示例中,我很困惑为什么#split返回零长度字符串作为返回数组的第一个值。这是什么原因呢?这是我所期望的:"abcabc".split("a")#=>["bc"

  7. 电脑0x0000001A蓝屏错误怎么U盘重装系统教学 - 2

      电脑0x0000001A蓝屏错误怎么U盘重装系统教学分享。有用户电脑开机之后遇到了系统蓝屏的情况。系统蓝屏问题很多时候都是系统bug,只有通过重装系统来进行解决。那么蓝屏问题如何通过U盘重装新系统来解决呢?来看看以下的详细操作方法教学吧。  准备工作:  1、U盘一个(尽量使用8G以上的U盘)。  2、一台正常联网可使用的电脑。  3、ghost或ISO系统镜像文件(Win10系统下载_Win10专业版_windows10正式版下载-系统之家)。  4、在本页面下载U盘启动盘制作工具:系统之家U盘启动工具。  U盘启动盘制作步骤:  注意:制作期间,U盘会被格式化,因此U盘中的重要文件请注

  8. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

    在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

  9. ruby - 使用 `+=` 和 `send` 方法 - 2

    如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

  10. kvm虚拟机安装centos7基于ubuntu20.04系统 - 2

    需求:要创建虚拟机,就需要给他提供一个虚拟的磁盘,我们就在/opt目录下创建一个10G大小的raw格式的虚拟磁盘CentOS-7-x86_64.raw命令格式:qemu-imgcreate-f磁盘格式磁盘名称磁盘大小qemu-imgcreate-f磁盘格式-o?1.创建磁盘qemu-imgcreate-fraw/opt/CentOS-7-x86_64.raw10G执行效果#ls/opt/CentOS-7-x86_64.raw2.安装虚拟机使用virt-install命令,基于我们提供的系统镜像和虚拟磁盘来创建一个虚拟机,另外在创建虚拟机之前,提前打开vnc客户端,在创建虚拟机的时候,通过vnc

随机推荐