在 C# 中调用 CreateProcess 的访问冲突

Access violation calling CreateProcess in C#

我正在尝试编写 C# 代码,运行 在提升的进程中创建非提升的进程。 SO 问题 How to call CreateProcess() with STARTUPINFOEX from C# and re-parent the child 的(唯一)答案包含现成的代码。我复制了这段代码,但它没有创建进程,而是抛出了 AccessViolationException。异常文本 Attempted to read or write protected memory。这通常表明其他内存已损坏。 无助于识别罪魁祸首,但是从低级调试中我可以看到处理器正在尝试从非常小的地址(如 0x00000004)读取内存,当然会出错。

代码,简要解释,检索桌面进程的句柄,然后调用 InitializeProcThreadAttributeList 和 UpdateProcThreadAttribute 来初始化 STARTUPINFOEX 结构的各个字段,然后将其传递到 CreateProcess Windows API功能。然后,CreateProcess 应将新进程创建为桌面进程的子进程。

此函数在其第 9 个参数中需要一个指向 STARTUPINFO 或 STARTUPINFOEX(其开头包含 STATUPINFO)的指针。如果是 STARTUPINFOEX,第 6 个参数应该包含 EXTENDED_STARTUPINFO_PRESENT 标志。

当我传递EXTENDED_STARTUPINFO_PRESENT标志时,CreateProcess认为它只是传递了一个STARTUPINFO,除了创建的进程被提升(调用过程也是如此)。但是,一旦我添加此标志,就会发生访问冲突。几个小时以来,我尝试修改参数属性等,但问题仍然存在。我在 C++ 中有基本相同的代码 运行,所以我知道它 可以 工作。我做错了什么?

下面的代码不包含详细的错误检查,但它应该立即编译并演示问题。

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace RunNonElevatedCSharp
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
            StartNonElevated(@"C:\WINDOWS\system32\cmd.exe", "");
        }


        public static int StartNonElevated(string strAppPath, string strCommandLine)
        {
            bool bSuccess = false;
            IntPtr hShellProcess = IntPtr.Zero;
            var pInfo = new PROCESS_INFORMATION();
            var sInfoEx = new STARTUPINFOEX();
            sInfoEx.StartupInfo.cb = Marshal.SizeOf(sInfoEx);

            try
            {
                IntPtr hShellWnd = GetShellWindow();
                if (hShellWnd == IntPtr.Zero)
                {
                    return 0;
                }

                UInt32 pid;
                if (GetWindowThreadProcessId(hShellWnd, out pid) == 0)
                {
                    return 0;
                }

                hShellProcess = OpenProcess(PROCESS_CREATE_PROCESS, FALSE, pid);
                if (hShellProcess == IntPtr.Zero)
                {
                    return 0;
                }

                IntPtr nBufferSize = IntPtr.Zero;
                InitializeProcThreadAttributeList(IntPtr.Zero, 1, 0, ref nBufferSize);
                if (nBufferSize == IntPtr.Zero)
                {
                    return 0;
                }

                sInfoEx.lpAttributeList = Marshal.AllocHGlobal(nBufferSize);
                if (sInfoEx.lpAttributeList == IntPtr.Zero)
                {
                    return 0;
                }
                if (!InitializeProcThreadAttributeList(sInfoEx.lpAttributeList, 1, 0, ref nBufferSize))
                {
                    return 0;
                }

                if (!UpdateProcThreadAttribute(sInfoEx.lpAttributeList, 0, (IntPtr)PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, hShellProcess, (IntPtr)IntPtr.Size, IntPtr.Zero, IntPtr.Zero))
                {
                    return 0;
                }

                // s1 and s2 may not be required
                string s1 = "" + strAppPath + "";
                string s2 = "";

                // The next line causes an access violation unless you remove the 'EXTENDED_STARTUPINFO_PRESENT' flag
                if (!CreateProcess(s1, s2, IntPtr.Zero, IntPtr.Zero, false, CREATE_NEW_CONSOLE | EXTENDED_STARTUPINFO_PRESENT | 0,
                                   IntPtr.Zero, null, ref sInfoEx, out pInfo))
                {
                    return 0;
                }


                bSuccess = true;
                CloseHandle(pInfo.hThread);
                CloseHandle(pInfo.hProcess);
                return pInfo.dwProcessId;
            }
            finally
            {
                if (!bSuccess)
                {
                    var lastError = Marshal.GetLastWin32Error();
                    Debug.WriteLine("Error: " + lastError.ToString());
                }
                if (sInfoEx.lpAttributeList != IntPtr.Zero)
                {
                    DeleteProcThreadAttributeList(sInfoEx.lpAttributeList);
                    Marshal.FreeHGlobal(sInfoEx.lpAttributeList);
                }
                if (hShellProcess != IntPtr.Zero)
                    CloseHandle(hShellProcess);
            }
        }

        [DllImport("User32.dll", SetLastError = true)]
        public static extern IntPtr GetShellWindow();

        [DllImport("User32.dll", SetLastError = true)]
        public static extern UInt32 GetWindowThreadProcessId(IntPtr hWnd, out UInt32 lpdwProcessId);

        [DllImport("Kernel32.dll", SetLastError = true)]
        public static extern IntPtr OpenProcess(UInt32 dwDesiredAccess, int bInheritHandle, UInt32 dwProcessId);

        [DllImport("kernel32.dll", SetLastError = true)][return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool InitializeProcThreadAttributeList(IntPtr lpAttributeList, int dwAttributeCount, int dwFlags, ref IntPtr lpSize);

        [DllImport("kernel32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool UpdateProcThreadAttribute(IntPtr lpAttributeList, uint dwFlags, IntPtr Attribute, IntPtr lpValue, IntPtr cbSize,
                                                     IntPtr lpPreviousValue, IntPtr lpReturnSize);

        [DllImport("kernel32.dll", CharSet = CharSet.Unicode, EntryPoint = "CreateProcessW", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool CreateProcess(string lpApplicationName, string lpCommandLine,
                                 [In, Optional] IntPtr lpProcessAttributes, [In, Optional] IntPtr lpThreadAttributes,
                                 bool bInheritHandles, uint dwCreationFlags,
                                 [In, Optional] IntPtr lpEnvironment, [In, Optional] string lpCurrentDirectory,
                                 [In] ref STARTUPINFOEX lpStartupInfo, [Out] out PROCESS_INFORMATION lpProcessInformation);

        [DllImport("Kernel32.dll", SetLastError = true)]
        public static extern int CloseHandle(IntPtr hObject);

        [DllImport("kernel32.dll")]
        private static extern void DeleteProcThreadAttributeList(IntPtr lpAttributeList);

        public const UInt32 PROCESS_CREATE_PROCESS = 0x0080;
        public const int FALSE = 0;
        public const int PROC_THREAD_ATTRIBUTE_PARENT_PROCESS = 0x00020000;
        public const uint CREATE_NEW_CONSOLE = 0x00000010;
        public const uint EXTENDED_STARTUPINFO_PRESENT = 0x00080000;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct PROCESS_INFORMATION
    {
        public IntPtr hProcess;
        public IntPtr hThread;
        public int dwProcessId;
        public int dwThreadId;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public struct STARTUPINFOEX
    {
        public STARTUPINFO StartupInfo;
        public IntPtr lpAttributeList;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public struct STARTUPINFO
    {
        public Int32 cb;
        public string lpReserved;
        public string lpDesktop;
        public string lpTitle;
        public Int32 dwX;
        public Int32 dwY;
        public Int32 dwXSize;
        public Int32 dwYSize;
        public Int32 dwXCountChars;
        public Int32 dwYCountChars;
        public Int32 dwFillAttribute;
        public Int32 dwFlags;
        public Int16 wShowWindow;
        public Int16 cbReserved2;
        public IntPtr lpReserved2;
        public IntPtr hStdInput;
        public IntPtr hStdOutput;
        public IntPtr hStdError;
    }

}

发生访问冲突时,程序无法继续; “finally”块未执行。

期待任何回复...

汉斯

您对 UpdateProcThreadAttribute() 的调用是错误的。第 4 个参数需要 hShellProcess 地址 ,而不是 。这甚至在您链接到的 this answer to the other question 中也有说明(另一个问题中的代码具有相同的错误):

Second, the lpValue parameter of the UpdateProcThreadAttribute function must be a pointer to the attribute value (in your case, parentHandle), not the value itself.

PROC_THREAD_ATTRIBUTE_PARENT_PROCESSdocumentation 说:

The lpValue parameter is a pointer to a handle to a process to use instead of the calling process as the parent for the process being created. The process to use must have the PROCESS_CREATE_PROCESS access right.

你说你复制了其他答案的代码,但你的代码看起来不像其他答案的代码,当然也不包含其他答案提供的修复。

在您的代码中,更改为:

if (!UpdateProcThreadAttribute(..., hShellProcess, ...))

为此:

IntPtr lpValue = IntPtr.Zero;
...
lpValue = Marshal.AllocHGlobal(IntPtr.Size);
Marshal.WriteIntPtr(lpValue, hShellProcess);
if (!UpdateProcThreadAttribute(..., lpValue, ...))
...
Marshal.FreeHGlobal(lpValue);