在 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_PROCESS
的 documentation 说:
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);
我正在尝试编写 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 theUpdateProcThreadAttribute
function must be a pointer to the attribute value (in your case,parentHandle
), not the value itself.
PROC_THREAD_ATTRIBUTE_PARENT_PROCESS
的 documentation 说:
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 thePROCESS_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);