【问题标题】:Access violation calling CreateProcess in C#在 C# 中调用 CreateProcess 的访问冲突
【发布时间】:2021-12-18 13:05:46
【问题描述】:

我正在尝试编写在提升的进程中运行的 C# 代码,它会创建一个非提升的进程。 SO 问题How to call CreateProcess() with STARTUPINFOEX from C# and re-parent the child 的(唯一)答案包含现成的代码。我复制了这段代码,但它没有创建一个进程,而是抛出了一个 AccessViolationException。异常文本试图读取或写入受保护的内存。这通常表明其他内存已损坏。 无助于确定罪魁祸首,但是从低级调试中我可以看到处理器正试图从一个非常小的地址(如 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”块没有被执行。

期待任何回复...

汉斯

【问题讨论】:

  • 哪里抛出异常?
  • @500-InternalServerError,与“CreateProcess”调用一致。这是调用堆栈: [Managed to Native Transition] > RunNonElevatedCSharp.dll!RunNonElevatedCSharp.Program.StartNonElevated(string strAppPath, string strCommandLine) 第 68 行 C# RunNonElevatedCSharp.dll!RunNonElevatedCSharp.Program.Main(string[] args) 第 12 行 C#
  • 可能与找到的互操作声明 here 进行比较。我有一种感觉,您的支持结构中的 string 声明不能以这种方式传递。
  • @500-InternalServerError,谢谢,但它没有改变。我宁愿怀疑“属性列表”是 STARTUPINFO 和 STARTUPINFOEX 之间的唯一区别,但我正在做的事情与我在帖子中引用的回复完全相同......
  • 没有理由发明自己的 P/Invoke 声明。只需使用C#/Win32

标签: c# winapi pinvoke createprocess


【解决方案1】:

您对UpdateProcThreadAttribute() 的呼叫是错误的。第 4 个参数需要hShellProcess地址,而不是。这甚至在this answer 中与您链接到的other question 中的表述一样多(另一个问题中的代码具有相同的错误):

其次,UpdateProcThreadAttribute 函数的lpValue 参数必须是指向属性值(在您的情况下为parentHandle)的指针,而不是值本身。

documentation 代表 PROC_THREAD_ATTRIBUTE_PARENT_PROCESS 说:

lpValue 参数是指向句柄的指针,指向要使用的进程,而不是调用进程作为正在创建的进程的父进程。要使用的进程必须具有PROCESS_CREATE_PROCESS 访问权限。

您说您复制了另一个答案的代码,但您的代码看起来不像另一个答案的代码,并且肯定不包含另一个答案提供的修复。

在你的代码中,改变这个:

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

到这里:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-02-04
    • 1970-01-01
    • 2019-12-29
    • 1970-01-01
    • 2012-07-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多