【发布时间】: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