windows – 从服务启动的进程,CreateProcessWithLogonW立即终止

在测试框架过程中,A必须使用CreateProcessWithLogonW API在不同的用户凭证(例如,_ unlimited_user)下启动进程B. lpStartupInfo-> lpDesktop为NULL,因此进程B应该与进程A在同一个桌面和窗口站中运行.

当手动启动进程A时,一切正常(如_glagolig).但是当进程A由测试框架服务(在指定的测试框架的用户帐户_test_framework下运行)启动时不起作用. CreateProcessWithLogonW返回成功但进程B无法执行任何工作.它立即终止,因为它的conhost.exe无法初始化user32.dll并返回0xC0000142(我从SysInternals的procmon.exe日志得到了它).所以看起来问题是桌面/窗口站访问.

我想了解根本原因.目前尚不清楚是什么使得测试框架服务的桌面/窗口站对象与手动登录的用户的对象不同.

此外,我想找到一个解决方法,同时保持整体方案相同(帐户_test_framework下的测试框架服务必须在_limited_user下启动进程B).

解决方法

附录:根据文档,如果您不希望新进程与用户交互,则应该可以使用CreateProcessAsUser而无需执行这些步骤.我还没有测试过这个,但假设它是真的,对于许多场景来说,这将是一个更简单的解决方案.

事实证明,Microsoft已经提供了示例代码来操纵窗口站和桌面访问权限,标题为Starting an Interactive Client Process in C++.从Windows Vista开始,在默认窗口站中启动子进程不再足以允许子进程与用户交互,但它确实允许子进程使用备用用户凭据运行.

我应该注意,Microsoft的代码使用LogonUser和CreateProcessAsUser而不是CreateProcessWithLogonW.这意味着该服务需要SE_INCREASE_QUOTA_NAME权限,可能还需要SE_ASSIGNPRIMARYTOKEN_NAME.最好替换只需要SE_IMPERSONATE_NAME的CreateProcessWithTokenW.我不建议在此上下文中使用CreateProcessWithLogonW,因为它不允许您在启动子进程之前访问登录SID.

我写了一个最小的服务来演示使用Microsoft的示例代码:

/*******************************************************************/

#define _WIN32_WINNT 0x0501

#include <windows.h>

/*******************************************************************/

// See http://msdn.microsoft.com/en-us/library/windows/desktop/aa379608%28v=vs.85%29.aspx
// "Starting an Interactive Client Process in C++"

BOOL AddAceToWindowStation(HWINSTA hwinsta,PSID psid);
BOOL AddAceToDesktop(HDESK hdesk,PSID psid);
BOOL GetLogonSID (HANDLE hToken,PSID *ppsid);
VOID FreeLogonSID (PSID *ppsid);
BOOL StartInteractiveClientProcess (
    LPTSTR lpszUsername,// client to log on
    LPTSTR lpszDomain,// domain of client's account
    LPTSTR lpszPassword,// client's password
    LPTSTR lpCommandLine    // command line to execute
);

/*******************************************************************/

const wchar_t displayname[] = L"Demo service for CreateProcessWithLogonW";
const wchar_t servicename[] = L"demosvc-createprocesswithlogonw";

DWORD dwWin32ExitCode = 0,dwServiceSpecificExitCode = 0;

/*******************************************************************/

#define EXCEPTION_USER 0xE0000000
#define FACILITY_USER_DEMOSVC 0x0001
#define EXCEPTION_USER_LINENUMBER (EXCEPTION_USER | (FACILITY_USER_DEMOSVC << 16))

HANDLE eventloghandle;

/*******************************************************************/

wchar_t subprocess_username[] = L"harry-test1";
wchar_t subprocess_domain[] = L"scms";
wchar_t subprocess_password[] = L"xyzzy916";
wchar_t subprocess_command[] = L"cmd.exe /c dir";

void demo(void) 
{
    if (!StartInteractiveClientProcess(subprocess_username,subprocess_domain,subprocess_password,subprocess_command))
    {
        const wchar_t * strings[] = {L"Creating subprocess failed."};
        DWORD err = GetLastError();
        ReportEventW(eventloghandle,EVENTLOG_ERROR_TYPE,2,NULL,_countof(strings),sizeof(err),strings,&err);
        return;
    }

    {
        const wchar_t * strings[] = {L"Creating subprocess succeeded!"};
        ReportEventW(eventloghandle,EVENTLOG_INFORMATION_TYPE,1,NULL);
    }

    return;
}

/*******************************************************************/

CRITICAL_SECTION service_section;

SERVICE_STATUS service_status;                     // Protected by service_section

SERVICE_STATUS_HANDLE service_handle = 0;          // Constant once set,so can be used from any thread

static DWORD WINAPI ServiceHandlerEx(DWORD control,DWORD eventtype,LPVOID lpEventData,LPVOID lpContext) 
{
    if (control == SERVICE_CONTROL_INTERROGATE)
    {
        EnterCriticalSection(&service_section);
        if (service_status.dwCurrentState != SERVICE_STOPPED)
        {
        SetServiceStatus(service_handle,&service_status);
        }
        LeaveCriticalSection(&service_section);
        return NO_ERROR;
    }

    return ERROR_CALL_NOT_IMPLEMENTED;
}

static VOID WINAPI ServiceMain(DWORD argc,LPTSTR * argv)
{
    SERVICE_STATUS status;

    EnterCriticalSection(&service_section);

    service_handle = RegisterServiceCtrlHandlerEx(argv[0],ServiceHandlerEx,NULL);
    if (!service_handle) RaiseException(EXCEPTION_USER_LINENUMBER | __LINE__,EXCEPTION_NONCONTINUABLE,NULL);

    service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
    service_status.dwCurrentState = SERVICE_RUNNING;
    service_status.dwControlsAccepted = 0;
    service_status.dwWin32ExitCode = STILL_ACTIVE;
    service_status.dwServiceSpecificExitCode = 0;
    service_status.dwCheckPoint = 0;
    service_status.dwWaitHint = 500;

    SetServiceStatus(service_handle,&service_status);

    LeaveCriticalSection(&service_section);

    /************** service main function **************/

    {
        const wchar_t * strings[] = {L"Service started!"};
        ReportEventW(eventloghandle,NULL);
    }

    demo();

    /************** service shutdown **************/

    EnterCriticalSection(&service_section);     

    status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
    status.dwCurrentState = service_status.dwCurrentState = SERVICE_STOPPED;
    status.dwControlsAccepted = 0;
    status.dwCheckPoint = 0;
    status.dwWaitHint = 500;
    status.dwWin32ExitCode = dwWin32ExitCode;
    status.dwServiceSpecificExitCode = dwServiceSpecificExitCode;

    LeaveCriticalSection(&service_section);

    SetServiceStatus(service_handle,&status);       /* NB: SetServiceStatus does not return here if successful,so any code after this point will not normally run. */
    return;
}

int wmain(int argc,wchar_t * argv[]) 
{
    const static SERVICE_TABLE_ENTRY servicetable[2] = {
        {(wchar_t *)servicename,ServiceMain},{NULL,NULL}
    };

    InitializeCriticalSection(&service_section);

    eventloghandle = RegisterEventSource(NULL,displayname);
    if (!eventloghandle) return GetLastError();

    {
        const wchar_t * strings[] = {L"Executable started!"};
        ReportEventW(eventloghandle,NULL);
    }

    if (StartServiceCtrlDispatcher(servicetable)) return 0;
    return GetLastError();
}

这必须与Microsoft的示例代码相关联.然后,您可以使用sc命令安装该服务:

sc create demosvc-createprocesswithlogonw binPath= c:\path\demosvc.exe DisplayName= "Demo service for CreateProcessWithLogonW"

相关文章

文章浏览阅读2.2k次,点赞6次,收藏20次。在我们平时办公工作...
文章浏览阅读1k次。解决 Windows make command not found 和...
文章浏览阅读3.2k次,点赞2次,收藏6次。2、鼠标依次点击“计...
文章浏览阅读1.3w次。蓝光版属于高清版的一种。BD英文全名是...
文章浏览阅读974次,点赞7次,收藏8次。提供了更强大的功能,...
文章浏览阅读1.4w次,点赞5次,收藏22次。如果使用iterator的...