CreateProcessAsUser进程退出-1073741502

时间:2018-06-01 13:08:40

标签: c# winapi

我有一项服务,负责在用户登录和控制台会话连接后启动/监控用户会话中的交互过程。 服务设置为自动启动,以便在用户登录前启动并运行。 首次登录时一切正常,我可以正确启动/重启用户进程。

如果用户退出并重新登录 ,则无法再正确启动用户进程。 CreateProcessAsUser不返回任何错误,但是一旦用户进程启动,它将退出-1073741502(0xC0000142)退出代码。

如果我重新启动该服务,那么它再次能够启动用户进程而不会出现任何错误。

如果需要,我可以发布服务如何创建用户进程的完整资源。

修改

    try
            {
                //WE ALREADY HAVE A CLIENT ATTACHED , DONT START A NEW ONE
                if (ClientProcessId != null)
                    return;

                var ACTIVE_CONSOLE_SESSION = ListSessions()
                    .Where(SESSION => SESSION.State == WTS_CONNECTSTATE_CLASS.WTSActive)
                    .FirstOrDefault();

                if (ACTIVE_CONSOLE_SESSION == null)
                    return;

                CONSOLE_SESSION_ID = (uint)ACTIVE_CONSOLE_SESSION.Id;

                IntPtr USER_TOKEN = IntPtr.Zero;
                IntPtr ENVIRONMENT = IntPtr.Zero;
                IntPtr LINKED_TOKEN = IntPtr.Zero;

                try
                {
                    try
                    {
                        if (!Wtsapi32.WTSQueryUserToken(CONSOLE_SESSION_ID.Value, out USER_TOKEN))
                            throw new Win32Exception();
                    }
                    catch (Win32Exception wex)
                    {
                        EntryPoint.TryWriteToCacheLog($"{nameof(Wtsapi32.WTSQueryUserToken)} : console session id {CONSOLE_SESSION_ID} error {wex.ErrorCode} , native error {wex.NativeErrorCode}");
                        throw;
                    }

                    try
                    {
                        if (!Userenv.CreateEnvironmentBlock(out ENVIRONMENT, USER_TOKEN, true))
                            throw new Win32Exception();
                    }
                    catch (Win32Exception wex)
                    {
                        EntryPoint.TryWriteToCacheLog($"{nameof(Userenv.CreateEnvironmentBlock)} : error {wex.ErrorCode} , native error {wex.NativeErrorCode}");
                        throw;
                    }

                    try
                    {
                        LINKED_TOKEN = CoreProcess.GetLinkedTokeIfRequiered(USER_TOKEN);
                    }
                    catch (Win32Exception wex)
                    {
                        EntryPoint.TryWriteToCacheLog($"{nameof(CoreProcess.GetLinkedTokeIfRequiered)} : error {wex.ErrorCode} , native error {wex.NativeErrorCode}");
                        throw;
                    }

                    //GET PROCESS PATHS
                    string FILE_NAME = EntryPoint.PROCESS_FULL_FILE_NAME;
                    string WORKING_DIRECTORY = EntryPoint.PROCESS_DIRECTORY;

                    //GET CURRENT COMMAND LINE ARGUMENTS
                    var CMD_ARGS = Environment.GetCommandLineArgs();

                    //FIRST ARGUMENT WILL ALWAYS HAVE FULL PROCESS PATH,OTHER ARGUMENTS ARE OPTIONAL
                    var ARGUMENTS_STRING = CMD_ARGS
                        .Skip(1)
                        .DefaultIfEmpty()
                        .Aggregate((first, next) => ' ' + first + ' ' + next);

                    var ARGUMENTS = new StringBuilder(ARGUMENTS_STRING);

                    var START_INFO = new STARTUPINFO();
                    START_INFO.cb = Marshal.SizeOf(START_INFO);
                    START_INFO.lpDesktop = @"winsta0\default";

                    var PROCESS_INFO = new PROCESS_INFORMATION();
                    uint dwCreationFlags = NORMAL_PRIORITY_CLASS | (int)(PROCESS_CREATE_FLAG.CREATE_NEW_CONSOLE | PROCESS_CREATE_FLAG.CREATE_UNICODE_ENVIRONMENT);

                    try
                    {
                        if (!AdvApi32.CreateProcessAsUser(LINKED_TOKEN,
                            FILE_NAME,
                            ARGUMENTS,
                            IntPtr.Zero,
                            IntPtr.Zero,
                            true,
                            dwCreationFlags,
                            ENVIRONMENT,
                            WORKING_DIRECTORY,
                            ref START_INFO,
                            out PROCESS_INFO))
                            throw new Win32Exception();

                        if (PROCESS_INFO.hThread != IntPtr.Zero)
                        {
                            ClientProcessId = PROCESS_INFO.dwProcessId;
                            ClientSessionId = CONSOLE_SESSION_ID;

                            EntryPoint.TryWriteToCacheLog($"{nameof(AdvApi32.CreateProcessAsUser)} : Created porocess {ClientProcessId} in session {CONSOLE_SESSION_ID}.");
                        }
                    }
                    catch (Win32Exception wex)
                    {
                        EntryPoint.TryWriteToCacheLog($"{nameof(AdvApi32.CreateProcessAsUser)} : error {wex.ErrorCode} , native error {wex.NativeErrorCode}");
                        throw;
                    }
                }
                catch (Win32Exception wex)
                {
                    switch (wex.NativeErrorCode)
                    {
                        case 5:
                        case 1008:

                            tryCount++;
                            if (tryCount >= START_RETRIES)
                                throw;

                            Thread.Sleep(RETRY_WAIT_SPAN);

                            if (DisableCallBacks)
                                return;

                            CreateProcess(tryCount);

                            break;
                        default:
                            throw;
                    }
                }
                catch
                {
                    throw;
                }
                finally
                {
                    Userenv.DestroyEnvironmentBlock(ENVIRONMENT);
                    Kernel32.CloseHandle(USER_TOKEN);
                    if (USER_TOKEN != LINKED_TOKEN)
                        Kernel32.CloseHandle(LINKED_TOKEN);
                }
            }
            catch (Exception ex)
            {
                EntryPoint.TryWriteToCacheLog($"{nameof(CreateProcess)} failed after {tryCount} retries, console seesion id {(CONSOLE_SESSION_ID != null ? CONSOLE_SESSION_ID.ToString() : "Unobtained")}.", ex.ToString());
            }
            finally
            {
                Monitor.Exit(CREATE_LOCK);
            }

  public static TOKEN_ELEVATION_TYPE GetTokenElevationLevel(IntPtr hToken)
    {
        int TOKEN_INFO_LENGTH = Marshal.SizeOf(typeof(int));
        IntPtr TOKEN_INFO_POINTER = Marshal.AllocHGlobal(TOKEN_INFO_LENGTH);

        try
        {
            if (!AdvApi32.GetTokenInformation(hToken, TOKEN_INFORMATION_CLASS.TokenElevationType, TOKEN_INFO_POINTER, TOKEN_INFO_LENGTH, out TOKEN_INFO_LENGTH))
                throw Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error());

            return (TOKEN_ELEVATION_TYPE)Marshal.ReadInt32(TOKEN_INFO_POINTER);
        }
        catch
        {
            throw;
        }
        finally
        {
            if (TOKEN_INFO_POINTER != IntPtr.Zero)
                Marshal.FreeHGlobal(TOKEN_INFO_POINTER);
        }
    }

 public static IntPtr GetLinkedTokeIfRequiered(IntPtr hToken)
    {
        var TOKEN_ELEVATION = GetTokenElevationLevel(hToken);

        if (TOKEN_ELEVATION != TOKEN_ELEVATION_TYPE.TokenElevationTypeLimited)
            return hToken;

        int TOKEN_INFO_LENGHT = Marshal.SizeOf(typeof(IntPtr));
        IntPtr LINKED_TOKEN_INFO = Marshal.AllocHGlobal(TOKEN_INFO_LENGHT);

        try
        {
            if (!AdvApi32.GetTokenInformation(hToken, TOKEN_INFORMATION_CLASS.TokenLinkedToken, LINKED_TOKEN_INFO, TOKEN_INFO_LENGHT, out TOKEN_INFO_LENGHT))
                throw new Win32Exception();

            return Marshal.ReadIntPtr(LINKED_TOKEN_INFO);
        }
        finally
        {
            if (LINKED_TOKEN_INFO != IntPtr.Zero)
                Marshal.Release(LINKED_TOKEN_INFO);
        }
    }

2 个答案:

答案 0 :(得分:0)

感谢您发布其余代码。我看到你正在推动升级过程。我将此添加到我的测试服务中,它仍能正常工作。

但我认为问题可能是GetLinkedTokeIfRequiered()中的这一行:

Marshal.Release(LINKED_TOKEN_INFO);

显然应该是:

Marshal.FreeHGlobal(LINKED_TOKEN_INFO);

修复它,它可能会起作用。事实上,我很惊讶它并没有崩溃。

对我来说不容易,挖掘这个。 C#互操作不是我的强项。

为了OP的好处,我的测试服务的完整源代码,用C ++编写,有效:

#include <windows.h>
#include <wtsapi32.h>
#include <userenv.h>
#include <tchar.h>
#include <stdio.h>

#pragma comment (lib, "user32.lib")
#pragma comment (lib, "wtsapi32.lib")
#pragma comment (lib, "userenv.lib")
#pragma comment (lib, "advapi32.lib")

DWORD report_error (const char *operation)
{
    DWORD err = GetLastError ();
    return err;
}

// Launch notepad as currently logged-on user
DWORD LaunchProcess (DWORD SessionId, const char **failed_operation)
{
    HANDLE hToken;
    BOOL ok = WTSQueryUserToken (SessionId, &hToken);
    if (!ok)
        return report_error (*failed_operation = "WTSQueryUserToken");

    void *environment = NULL;
    ok = CreateEnvironmentBlock (&environment, hToken, TRUE);

    if (!ok)
    {
        CloseHandle (hToken);
        return report_error (*failed_operation = "CreateEnvironmentBlock");
    }

    TOKEN_LINKED_TOKEN lto;
    DWORD nbytes;
    ok = GetTokenInformation (hToken, TokenLinkedToken, &lto, sizeof (lto), &nbytes);

    if (ok)
    {
        CloseHandle (hToken);
        hToken = lto.LinkedToken;
    }

    STARTUPINFO si = { sizeof (si) } ;
    PROCESS_INFORMATION pi = { } ;
    si.lpDesktop = "winsta0\\default";

    // Do NOT want to inherit handles here, surely
    DWORD dwCreationFlags = NORMAL_PRIORITY_CLASS | /* CREATE_NEW_CONSOLE | */ CREATE_UNICODE_ENVIRONMENT;
    ok = CreateProcessAsUser (hToken, "c:\\windows\\system32\\notepad.exe", NULL, NULL, NULL, FALSE,
        dwCreationFlags, environment, NULL, &si, &pi);

    DestroyEnvironmentBlock (environment);
    CloseHandle (hToken);

    if (!ok)
        return report_error (*failed_operation = "CreateProcessAsUser");

    CloseHandle (pi.hThread);
    CloseHandle (pi.hProcess);
    return 0;
}

// Determine the session ID of the currently logged-on user
DWORD GetCurrentSessionId ()
{
    WTS_SESSION_INFO *pSessionInfo;
    DWORD n_sessions = 0;
    BOOL ok = WTSEnumerateSessions (WTS_CURRENT_SERVER, 0, 1, &pSessionInfo, &n_sessions);
    if (!ok)
        return 0;

    DWORD SessionId = 0;

    for (DWORD i = 0; i < n_sessions; ++i)
    {
        if (pSessionInfo [i].State == WTSActive)
        {
            SessionId = pSessionInfo [i].SessionId;
            break;
        }
    }

    WTSFreeMemory (pSessionInfo);
    return SessionId;
}


#define SERVICE_NAME __T ("demo_service")

bool quit;

// CtrlHandler callback
DWORD WINAPI CtrlHandler (DWORD dwControl, DWORD  dwEventType, LPVOID lpEventData, LPVOID lpContext)
{
    if (dwControl == SERVICE_CONTROL_STOP)
        quit = true;
    return NO_ERROR;
}

// SvcMain callback
VOID WINAPI SvcMain (DWORD dwArgc, LPTSTR *lpszArgv)
{
    // Register for callbacks
    SERVICE_STATUS_HANDLE sh = RegisterServiceCtrlHandlerEx (SERVICE_NAME, CtrlHandler, NULL);

    // Tell the SCM that we are up and running
    SERVICE_STATUS ss = { };
    ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
    ss.dwCurrentState = SERVICE_RUNNING;
    ss.dwControlsAccepted = SERVICE_ACCEPT_STOP;
    SetServiceStatus (sh, &ss);

    TCHAR buf [256];
    const TCHAR *title = __T ("(c) 2018 Contoso Corporation");

    while (!quit)
    {
        DWORD response = IDOK;

        DWORD SessionId = GetCurrentSessionId ();
        if (SessionId == 0)
        {
            Sleep (2000);
            continue;
        }

        // Pop-up a message on the screen of the currently logged-on user (session 1)
        _stprintf (buf, __T ("Ready to launch..., SessionId = %d"), SessionId);
        WTSSendMessage (WTS_CURRENT_SERVER_HANDLE, SessionId, (TCHAR *) title, _tcslen (title),
            buf, _tcslen (buf), MB_OKCANCEL, 0, &response, TRUE);
        if (response == IDCANCEL)
            break;

        const char *failed_operation = "";
        DWORD dwResult = LaunchProcess (SessionId, &failed_operation);

        // Report results
        _stprintf (buf, __T ("LaunchProcess returned %lx from %s"), dwResult, failed_operation);
        WTSSendMessage (WTS_CURRENT_SERVER_HANDLE, SessionId, (char *) title, _tcslen (title),
            buf, _tcslen (buf), MB_OK, 0, &response, TRUE);

        FILE *logfile = fopen ("g:\\temp\\service.log", "at");
        if (logfile)
        {
            fprintf (logfile, "%s\n", buf);
            fclose (logfile);
        }
    }

    // Tell the SCM we are going away and exit
    ss.dwCurrentState = SERVICE_STOPPED;
    SetServiceStatus (sh, &ss);
}


// main
int main (void)
{
    SERVICE_TABLE_ENTRY DispatchTable [] = 
    { 
        { SERVICE_NAME, SvcMain }, 
        { NULL, NULL } 
    }; 

    // This call returns when the service has stopped. 
    // The process should simply terminate when the call returns.
    StartServiceCtrlDispatcher (DispatchTable);
    return 0;
}

答案 1 :(得分:0)

错误是STATUS_DLL_INIT_FAILED,这意味着缺少动态加载的DLL。也许您指定了错误的工作目录,并且对LoadLibrary("lib_with_no_path.dll")的某些调用失败了?

如果查看Event Viewer,您应该能够看到哪个DLL丢失了。