【问题标题】:CreateProcessAsUser return success but process exit with error 3228369022CreateProcessAsUser 返回成功,但进程退出并出现错误 3228369022
【发布时间】:2019-08-26 17:57:46
【问题描述】:

我有 3 个应用程序正在运行:

应用 A: 本地服务器在本地系统下作为后台服务运行,具有与桌面交互的权限(至少高级安装程序是这么说的)

应用 B: 一款桌面截取软件

应用 C: 以登录用户身份运行的托盘图标。

我想要实现的是能够捕获桌面 UAC 提示和登录屏幕,所以这是我想要实现的方法:

1. 应用程序 C 调用 WTSGetActiveConsoleSessionId()(返回 2), 抓住桌面指针:

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))

并将两者都发送到应用程序 A(本地服务器)

2.服务器运行如下代码:

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. 现在 CreateProcessAsUser 返回成功,但进程正在退出,错误代码为 3228369022 (C06D007E)。

我使用 ProcMon 进行监控,加载图像没有任何错误,但并非我的所有 dll 都已加载。

【问题讨论】:

  • 这是VcppException(ERROR_SEVERITY_ERROR, ERROR_MOD_NOT_FOUND) - 你需要调试过程来查看具体的。通常在解决延迟导入时这样做
  • OpenProcessToken 你需要什么 winlogon 令牌?!错误。你需要使用从WTSQueryUserToken返回的令牌,而不是从OpenProcessToken调用CreateProcessAsUser
  • OpenThread 是为了什么?!?当您已经拥有该线程的句柄时。这个线程是为了什么?
  • @RbMm 当我设置了调试器标志时,进程以Suspended 开始,所以我不得不恢复它。我想要实现的是运行应用程序 b 具有系统帐户(这就是 winlogon 令牌的原因)并在用户会话中运行它,我从:stackoverflow.com/questions/53346426/…
  • 你错了 - 当调试器标志设置时(DEBUG[_ONLY_THIS]_PROCESS 你的意思是?)进程没有以挂起的方式启动。你不需要恢复线程。线程开始挂起 - 当且仅当您使用 CREATE_SUSPENDED 时。那么您不需要打开线程,因为您已经处理了它。你不需要恢复它。如果您需要在用户会话中使用系统帐户运行 - 复制 self 令牌并在其中设置会话 ID

标签: c# winapi process createprocessasuser


【解决方案1】:

错误来源是

startInfo.lpDesktop = "winsta0\\default";

行,因为使用了 CreateProcessAsUserW(从问题代码中看不到)

一定是这样的

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

代码还包含其他几个错误:

我们不需要打开 winlogon.exe(或另一个硬编码的 exe 名称令牌),如果我们想要使用系统令牌,我们可以打开自处理令牌并在此处使用它。

对于WTSQueryUserToken 返回的令牌,不需要DuplicateTokenEx +set TokenSessionId - 因为WTSGetActiveConsoleSessionId() 已经 在这个令牌中(我们通过这个会话ID 得到它)

ResumeThread 在这里毫无意义的调用 - 因为线程创建时且仅当 CREATE_SUSPENDED 标志在 CreateProcess 中使用时才暂停

【讨论】:

  • 已修复 c# 需要更改函数 CreateProcessAsUser 的 DllImport 和 STARTUPINFO 的 StructLayout 以同时使用 CharSet = CharSet.Unicode
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-11
相关资源
最近更新 更多