windows 从 Vista 上的服务使用 CreateProcessAsUser 的桌面问题

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2212285/
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 13:53:53  来源:igfitidea点击:

Desktop problem with using CreateProcessAsUser from a service on Vista

windowsvisual-c++windows-vista

提问by coolcake

I am using VC++ to create a process from a service on Vista using CreateProcessAsUser. The process creation is succeeding when i execute this code by login on the machine locally. The same code is failing when I am using Remote Desktop (mstsc) to login in to my machine from another machine and launch my App.

我正在使用 VC++ 使用 CreateProcessAsUser 从 Vista 上的服务创建进程。当我通过本地登录机器执行此代码时,进程创建成功。当我使用远程桌面 (mstsc) 从另一台机器登录我的机器并启动我的应用程序时,相同的代码失败。

I can understand that when I do remote login the active desktop is taken as that of Remote Desktop. Can some one help me how to get the desktop name that of currently login users desktop and not that of Remote desktop.

我可以理解,当我进行远程登录时,活动桌面被视为远程桌面的活动桌面。有人可以帮助我如何获取当前登录用户桌面的桌面名称而不是远程桌面的桌面名称。

Here is my code

这是我的代码

ACTIVECONSOLESESSIONIDFUNC lpfnProc;    // WTSGetActiveConsoleSessionId function pointer
HMODULE hModule = NULL;         // Instance for kernel32.dll library
DWORD dwSessionId = 0;          // Session ID
HANDLE hToken = NULL;           // Active session token
HANDLE hDupToken = NULL;        // Duplicate session token
WCHAR szErr[1024] = {0};

LPVOID lpEnvironment = NULL;            // Environtment block

// Get the active session ID
hModule = LoadLibrary(KERNEL32LIB);
if(!hModule)
{
   //wsprintf(szErr, L"LoadLibrary Error: %d", GetLastError());
   return;
}

lpfnProc = (ACTIVECONSOLESESSIONIDFUNC)GetProcAddress(hModule,"WTSGetActiveConsoleSessionId"); 

dwSessionId = lpfnProc();

// Get token of the logged in user by the active session ID 
BOOL bRet = WTSQueryUserToken(dwSessionId, &hToken);

if (!bRet)
{       
    //wsprintf(szErr, L"WTSQueryUserToken Error: %d", GetLastError());
    return;
}

// Get duplicate token from the active logged in user's token
bRet = DuplicateTokenEx(hToken,     // Active session token
                 MAXIMUM_ALLOWED,           // Desired access
                         NULL,                      // Token attributes                                         
                         SecurityIdentification,    // Impersonation level
                         TokenPrimary,              // Token type
                         &hDupToken);               // New/Duplicate token
    if (!bRet)
    {
        //wsprintf(szErr, L"DuplicateTokenEx Error: %d", GetLastError());
        return;
    }

    // Get all necessary environment variables of logged in user
    // to pass them to the process

    bRet = CreateEnvironmentBlock(&lpEnvironment, // Environment block
                                hDupToken,        // New token
                                FALSE);           // Inheritence
    if(!bRet)
    {
        //wsprintf(szErr, L"CreateEnvironmentBlock Error: %d", GetLastError());
        return;
    }

    HDESK hdeskInput=OpenInputDesktop(0, FALSE, 0); // does not set GetLastError(), so GetLastError() is arbitrary if NULL is returned
    if( hdeskInput==NULL ) {
        TRACE( "hdeskInput==NULL" );
        return false;
    }

    // Initialize Startup and Process info  
    startupInfo->cb = sizeof(STARTUPINFO);
    startupInfo->lpDesktop = TEXT("winsta0\default");



    // Start the process on behalf of the current user 

    BOOL returnCode = CreateProcessAsUser(hDupToken, 
                                applicationName, 
                                commandLine, 
                                NULL,
                                NULL,
                                FALSE,
                                NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT,
                                lpEnvironment,
                                NULL,
                                startupInfo,
                                &processInformation);

Thanks, F

谢谢,F

采纳答案by coolcake

After lots of googling i am able to find the solution for my problem.

经过大量的谷歌搜索,我能够找到解决我的问题的方法。

Here is the link where i have found the solution. http://www.codeproject.com/KB/vista-security/interaction-in-vista.aspx

这是我找到解决方案的链接。 http://www.codeproject.com/KB/vista-security/interaction-in-vista.aspx

Thanks every one who tried to help me.

感谢每一位试图帮助我的人。

回答by Alex Fotios

Here is complete working code to do what you want

这是完整的工作代码来做你想做的事

//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);
}

回答by tyranid

What is initiating the service to create a new process as that has bearing on what a suitable answer is? WTSGetActiveConsoleSessionIdgets the session ID of the current physical console session, not the "active" one in your sense. With terminal services there might be many different users all connected to different sessions so you cannot just pick one and hope, but there may or may not actually be a physical logged on user.

什么是启动服务以创建新流程,因为这与什么是合适的答案有关?WTSGetActiveConsoleSessionId获取当前物理控制台会话的会话 ID,而不是您意义上的“活动”会话 ID。对于终端服务,可能有许多不同的用户都连接到不同的会话,因此您不能只选择一个并希望,但实际上可能有也可能没有物理登录用户。

One way is to use WTSEnumerateSessionsto list all active sessions and try and find the one you actually want, you can use something like WTSQuerySessionInformationto get the username for that session.

一种方法是使用WTSEnumerateSessions列出所有活动会话并尝试找到您真正想要的会话,您可以使用类似WTSQuerySessionInformation 的东西来获取该会话的用户名。

If your service is doing this in response to some request from your user account (rather than acting on some automatic event) then if you are using something like RPC/Named Pipes/DCOM etc. you can always impersonate the user (assuming security is set to allow non-anonymous/identify impersonation) and duplicate the thread's token into a primary token and use that.

如果您的服务是为了响应来自您的用户帐户的某些请求(而不是对某些自动事件采取行动)而执行此操作,那么如果您正在使用诸如 RPC/命名管道/DCOM 等之类的东西,您始终可以模拟用户(假设设置了安全性)允许非匿名/识别模拟)并将线程的令牌复制到主令牌中并使用它。