CreateProcessAsUser return success but process exit with error 3228369022

0

I have 3 applications running:

Application A: Local server running as a background service under Local System with permission to interact with the desktop ( Atleast that is what Advanced Installer says )

Application B: A desktop capture software

Application C: A tray icon running as the logged on user.

What i am trying to achieve is to be able to capture the desktop UAC prompts and logon screen, so here is how i am trying to achieve it:

1. Application C calls WTSGetActiveConsoleSessionId() ( which return 2), Grab the desktop pointer:

OpenInputDesktop(0, false, (uint)(ACCESS_MASK.DESKTOP_CREATEMENU | ACCESS_MASK.DESKTOP_CREATEWINDOW | ACCESS_MASK.DESKTOP_ENUMERATE | ACCESS_MASK.DESKTOP_HOOKCONTROL | ACCESS_MASK.DESKTOP_WRITEOBJECTS | ACCESS_MASK.DESKTOP_READOBJECTS | ACCESS_MASK.DESKTOP_SWITCHDESKTOP))

and sends both to the application A ( local server )

2. The server runs the following code:

static void CreateApplicationBProcess()
{
            IntPtr hToken = IntPtr.Zero;
            IntPtr P = IntPtr.Zero;            
            /*if (!OpenProcessToken(OpenProcess(Process.GetProcessesByName("winlogon").First(), ProcessAccessFlags.All), 0x2000000, out hToken))
            {
                throw new Exception("OpenProcessToken error #" + Marshal.GetLastWin32Error());
            }*/

            if (!WTSQueryUserToken(WTSGetActiveConsoleSessionId(), out hToken))
            {
                throw new Exception("WTSQueryUserToken error #" + Marshal.GetLastWin32Error());
            }

            IntPtr duplicatedTokenHandle = IntPtr.Zero;
            if(!DuplicateTokenEx(hToken, 0, IntPtr.Zero, (int)SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, (int)TOKEN_TYPE.TokenPrimary, ref duplicatedTokenHandle))
            {
                throw new Exception("DuplicateTokenEx error #" + Marshal.GetLastWin32Error());
            }

            uint sessionId = SessionId;
            if(!SetTokenInformation(duplicatedTokenHandle, TOKEN_INFORMATION_CLASS.TokenSessionId, ref sessionId, (uint)IntPtr.Size))
            {
                throw new Exception("SetTokenInformation error #" + Marshal.GetLastWin32Error());
            }

            if (CreateEnvironmentBlock(out P, duplicatedTokenHandle, false))
            {
                Win32.PROCESS_INFORMATION processInfo = new Win32.PROCESS_INFORMATION();
                Win32.STARTUPINFO startInfo = new Win32.STARTUPINFO();
                bool bResult = false;
                uint uiResultWait = Win32.WAIT_FAILED;
                try
                {
                    // Create process
                    startInfo.cb = Marshal.SizeOf(startInfo);
                    startInfo.lpDesktop = "winsta0\\default";
                    startInfo.dwFlags = 0x00000001;
                    startInfo.wShowWindow = (short)SW.SW_HIDE;

                    SetCurrentDirectory(GetDirectory());
                    Environment.CurrentDirectory = GetDirectory();

                    bResult = Win32.CreateProcessAsUser(duplicatedTokenHandle, Path.Combine(GetDirectory(), "ApplicationB.exe"), string.Format("{0}", (uint)DesktopPtr), IntPtr.Zero, IntPtr.Zero, false,
                        CREATE_UNICODE_ENVIRONMENT | CREATE_NO_WINDOW, P, GetDirectory(), ref startInfo, out processInfo);
                    if (!bResult)
                    {
                        throw new Exception("CreateProcessAsUser error #" + Marshal.GetLastWin32Error());
                    }
                    else
                    {
                        //IntPtr pOpenThread = OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)processInfo.dwThreadId);
                        ResumeThread(processInfo.hThread);
                    }
                    // Wait for process to end
                    uiResultWait = Win32.WaitForSingleObject(processInfo.hProcess, Win32.INFINITE);
                    if (uiResultWait == Win32.WAIT_FAILED)
                    {
                        throw new Exception("WaitForSingleObject error #" + Marshal.GetLastWin32Error());
                    }

                    GetExitCodeProcess(processInfo.hProcess, out uint ExitCode);
                    Console.WriteLine("Exit code: " + ExitCode);
                }
                finally
                {
                    // Close all handles
                    Win32.CloseHandle(hToken);
                    Win32.CloseHandle(processInfo.hProcess);
                    Win32.CloseHandle(processInfo.hThread);
                    DestroyEnvironmentBlock(P);
                }
            }
            else
            {
                throw new Exception("CreateEnvironmentBlock error #" + Marshal.GetLastWin32Error());
            }
        }


        private static string GetDirectory()
        {
            return Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
        }

3. Now the CreateProcessAsUser returns a success but the process is exiting with the error code 3228369022 (C06D007E).

I monitored with ProcMon and i dont have any error with Load Image, however not all of my dlls are loaded.

c#
winapi
process
createprocessasuser
asked on Stack Overflow Aug 26, 2019 by Sylvain Martens • edited Aug 26, 2019 by Sylvain Martens

1 Answer

2

source of error was

startInfo.lpDesktop = "winsta0\\default";

line, because CreateProcessAsUserW used (not visible from question code)

so must be

startInfo.lpDesktop = L"winsta0\\default";

also code contains several another errors:

we not need open winlogon.exe (or another hardcoded exe name token) instead we can open self process token and use it here, if we want system token use.

not need DuplicateTokenEx +set TokenSessionId for token returned by WTSQueryUserToken - because the WTSGetActiveConsoleSessionId() already was in this token (we get it by this session id)

ResumeThread senseless call here - because thread created suspended when and only when CREATE_SUSPENDED flag used in CreateProcess

answered on Stack Overflow Aug 26, 2019 by RbMm

User contributions licensed under CC BY-SA 3.0