Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CreateProcessAsUser process exits with -1073741502

Tags:

c#

winapi

I have a service that is responsible for starting/monitoring an interactive process in user session once user logins and console session connects. Service is set to start automatically so its up and running before user login. All work fine on first login and i get the user process started/restarted correctly.

What happens is that if user signs out and re logins service is no longer able to start a user process correctly. CreateProcessAsUser returns no error but as soon as user process is started its exiting with -1073741502 (0xC0000142) exit code.

If i restart the service then its again able to launch user process without any errors.

I can post full source of how the service creates user process if required.

Edit

    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);
        }
    }
like image 300
NullReference Avatar asked Jun 01 '18 13:06

NullReference


1 Answers

Thanks you for posting the rest of the code. I see you are launching the process elevated. I added this to my test service and it still works correctly.

But I think the problem might be this line in GetLinkedTokeIfRequiered():

Marshal.Release(LINKED_TOKEN_INFO);

That should obviously be:

Marshal.FreeHGlobal(LINKED_TOKEN_INFO);

Fix that and it might just work. As things are, I'm amazed it's not crashing.

Not easy for me, digging through this. C# interop not my strong suit.


For the OP's benefit, complete source code of my test service, written in C++, which works:

#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;
}
like image 111
Paul Sanders Avatar answered Nov 02 '22 21:11

Paul Sanders