C# 在C中重定向CreateProcessWithTokenW的标准输出#
我有一个启动另一个进程的应用程序 第一个应用程序在admin中启动,第二个在desktop user中启动。 在这之前,我用基本的C#handle启动了这个过程 在我处理主应用程序中的标准和错误输出之后 但现在我像这样启动我的流程C# 在C中重定向CreateProcessWithTokenW的标准输出#,c#,winapi,C#,Winapi,我有一个启动另一个进程的应用程序 第一个应用程序在admin中启动,第二个在desktop user中启动。 在这之前,我用基本的C#handle启动了这个过程 在我处理主应用程序中的标准和错误输出之后 但现在我像这样启动我的流程 var res = RunAsDesktopUser(fileName, arguments); var process = Process.GetProcessById(res.dwProcessId); 在RunAsUserDesktop中,我使用advap
var res = RunAsDesktopUser(fileName, arguments);
var process = Process.GetProcessById(res.dwProcessId);
在RunAsUserDesktop中,我使用advapi32.dll CreateProcessWithTokenW中的winapi方法。
然后,我像预期的那样返回进程,但当我尝试读取标准输出后,我得到一个异常,表示进程未启动或标准输出未重定向。
因此,我可能必须做一些事情来重定向标准输出
此方法接受类似于此结构的参数
private 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;
}
我有点理解我必须将hStdOutput和hStdError IntPtr设置为某个值,但我没有得到什么。我找不到这方面的任何文档有人知道如何解决这个问题吗 这3个流(大多数人不知道错误)是process类的一部分,而不是StartInfo。这些布尔值仅仅定义了它们是否可以“移出”正常位置。否则,getter将抛出异常
下面是其中一个的示例代码:
using (Process myProcess = new Process())
{
ProcessStartInfo myProcessStartInfo = new ProcessStartInfo("net ", "use " + args[0]);
myProcessStartInfo.UseShellExecute = false;
myProcessStartInfo.RedirectStandardError = true;
myProcess.StartInfo = myProcessStartInfo;
myProcess.Start();
StreamReader myStreamReader = myProcess.StandardError;
// Read the standard error of net.exe and write it on to console.
Console.WriteLine(myStreamReader.ReadLine());
}
这种模式(受bool开关保护的属性)是一种比较少见的模式。我在相当旧的代码中看到过几次(我想到了BackgroundWorker),但它似乎不受欢迎。创建一个管道,并将子进程的stdhandle重定向到管道的写入端(或直接重定向到父进程的stdhandle) 以下方法通过管道重定向子流程的stdhandle。 (顺便说一句:您可以将
句柄的类型声明为IntPtr
)
您重新定向的3个流是进程的属性,而不是StartInfos的一部分。我查看StartInfos,因为在旧代码中,这里发生了神奇的事情p.StartInfo.RedirectStandardOutput=true;如果您有任何建议让我重定向3 steam,我将不胜感激。您将需要一个。@HansPassant有很多代码,我将尝试对此进行检查,但遗憾的是,这种代码一直在cpp中,我不能在C中包含相同的.h,而C中不存在类似句柄的类型;我不知道如何“翻译它”@Jebik我认为这一切都是因为你试图启动一个权限比你正在运行的权限低的进程。那是行不通的您有两个选项:a)硬编码/配置特定用户帐户,包括使用密码b)以用户身份启动应用程序。只提升需要它的程序。每个人都必须咬紧牙关,最终选择b。我理解这一点,但正如我在帖子中所说的,当我用这段代码创建流程时,它工作得很好,但现在我用一些windows api方法创建流程,当我将其返回到C#时,该流程已经启动,我无法设置启动信息来重定向今年的标准输出way@Jebik:据我所知,您要么使用Windows API,要么使用环绕该API的进程,而不是两者都使用。
using (Process myProcess = new Process())
{
ProcessStartInfo myProcessStartInfo = new ProcessStartInfo("net ", "use " + args[0]);
myProcessStartInfo.UseShellExecute = false;
myProcessStartInfo.RedirectStandardError = true;
myProcess.StartInfo = myProcessStartInfo;
myProcess.Start();
StreamReader myStreamReader = myProcess.StandardError;
// Read the standard error of net.exe and write it on to console.
Console.WriteLine(myStreamReader.ReadLine());
}
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
namespace ConsoleApp2
{
class Program
{
private static IntPtr out_read;
private static IntPtr out_write;
private static IntPtr err_read;
private static IntPtr err_write;
private static int CREATE_NO_WINDOW = 0x08000000;
private static int STARTF_USESTDHANDLES = 0x00000100;
private static int BUFSIZE = 4096;
private static int HANDLE_FLAG_INHERIT = 0x00000001;
private static void Main(string[] args)
{
SECURITY_ATTRIBUTES saAttr = new SECURITY_ATTRIBUTES();
saAttr.nLength = Marshal.SizeOf(typeof(SECURITY_ATTRIBUTES));
saAttr.bInheritHandle = 0x1;
saAttr.lpSecurityDescriptor = IntPtr.Zero;
CreatePipe(ref out_read,ref out_write,ref saAttr, 0);
CreatePipe(ref err_read, ref err_write,ref saAttr, 0);
SetHandleInformation(out_read, HANDLE_FLAG_INHERIT, 0);
SetHandleInformation(err_read, HANDLE_FLAG_INHERIT, 0);
PROCESS_INFORMATION res;
RunAsDesktopUser("C:\\test.exe",out res);
byte[] buf = new byte[BUFSIZE];
int dwRead = 0;
while (true)
{
bool bSuccess = ReadFile(out_read, buf, BUFSIZE, ref dwRead, IntPtr.Zero);
if (!bSuccess || dwRead == 0)
break;
Console.WriteLine(System.Text.Encoding.Default.GetString(buf));
}
CloseHandle(out_read);
CloseHandle(err_read);
CloseHandle(out_write);
CloseHandle(err_write);
}
private static void RunAsDesktopUser(string fileName, out PROCESS_INFORMATION pi)
{
var si = new STARTUPINFO();
pi = new PROCESS_INFORMATION();
if (string.IsNullOrWhiteSpace(fileName))
throw new ArgumentException("Value cannot be null or whitespace.", nameof(fileName));
// To start process as shell user you will need to carry out these steps:
// 1. Enable the SeIncreaseQuotaPrivilege in your current token
// 2. Get an HWND representing the desktop shell (GetShellWindow)
// 3. Get the Process ID(PID) of the process associated with that window(GetWindowThreadProcessId)
// 4. Open that process(OpenProcess)
// 5. Get the access token from that process (OpenProcessToken)
// 6. Make a primary token with that token(DuplicateTokenEx)
// 7. Start the new process with that primary token(CreateProcessWithTokenW)
var hProcessToken = IntPtr.Zero;
// Enable SeIncreaseQuotaPrivilege in this process. (This won't work if current process is not elevated.)
try
{
var process = GetCurrentProcess();
if (!OpenProcessToken(process, 0x0020, ref hProcessToken))
return;
var tkp = new TOKEN_PRIVILEGES
{
PrivilegeCount = 2,
Privileges = new LUID_AND_ATTRIBUTES[2]
};
if (!LookupPrivilegeValue(null, "SeIncreaseQuotaPrivilege", ref tkp.Privileges[0].Luid))
return;
if (!LookupPrivilegeValue(null, "SeImpersonatePrivilege", ref tkp.Privileges[1].Luid))
return;
tkp.Privileges[0].Attributes = 0x00000002;
tkp.Privileges[1].Attributes = 0x00000002;
if (!AdjustTokenPrivileges(hProcessToken, false, ref tkp, 0, IntPtr.Zero, IntPtr.Zero))
return;
}
finally
{
CloseHandle(hProcessToken);
}
// Get an HWND representing the desktop shell.
// CAVEATS: This will fail if the shell is not running (crashed or terminated), or the default shell has been
// replaced with a custom shell. This also won't return what you probably want if Explorer has been terminated and
// restarted elevated.
var hwnd = GetShellWindow();
if (hwnd == IntPtr.Zero)
return;
var hShellProcess = IntPtr.Zero;
var hShellProcessToken = IntPtr.Zero;
var hPrimaryToken = IntPtr.Zero;
try
{
// Get the PID of the desktop shell process.
uint dwPID;
if (GetWindowThreadProcessId(hwnd, out dwPID) == 0)
return;
// Open the desktop shell process in order to query it (get the token)
hShellProcess = OpenProcess(ProcessAccessFlags.QueryInformation, false, dwPID);
if (hShellProcess == IntPtr.Zero)
return;
// Get the process token of the desktop shell.
if (!OpenProcessToken(hShellProcess, 0x0002, ref hShellProcessToken))
return;
var dwTokenRights = 395U;
// Duplicate the shell's process token to get a primary token.
// Based on experimentation, this is the minimal set of rights required for CreateProcessWithTokenW (contrary to current documentation).
if (!DuplicateTokenEx(hShellProcessToken, dwTokenRights, IntPtr.Zero, SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, TOKEN_TYPE.TokenPrimary, out hPrimaryToken))
return;
// Start the target process with the new token.
si.cb = Marshal.SizeOf(typeof(STARTUPINFO));
si.hStdOutput = out_write;
si.hStdError = err_write;
si.dwFlags |= STARTF_USESTDHANDLES;
if (!CreateProcessWithTokenW(hPrimaryToken, 0, fileName, "", CREATE_NO_WINDOW, IntPtr.Zero, Path.GetDirectoryName(fileName), ref si, out pi))
return;
}
finally
{
CloseHandle(hShellProcessToken);
CloseHandle(hPrimaryToken);
CloseHandle(hShellProcess);
}
return;
}
#region Interop
private struct TOKEN_PRIVILEGES
{
public UInt32 PrivilegeCount;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
public LUID_AND_ATTRIBUTES[] Privileges;
}
[StructLayout(LayoutKind.Sequential, Pack = 4)]
private struct LUID_AND_ATTRIBUTES
{
public LUID Luid;
public UInt32 Attributes;
}
[StructLayout(LayoutKind.Sequential)]
private struct LUID
{
public uint LowPart;
public int HighPart;
}
[Flags]
private enum ProcessAccessFlags : uint
{
All = 0x001F0FFF,
Terminate = 0x00000001,
CreateThread = 0x00000002,
VirtualMemoryOperation = 0x00000008,
VirtualMemoryRead = 0x00000010,
VirtualMemoryWrite = 0x00000020,
DuplicateHandle = 0x00000040,
CreateProcess = 0x000000080,
SetQuota = 0x00000100,
SetInformation = 0x00000200,
QueryInformation = 0x00000400,
QueryLimitedInformation = 0x00001000,
Synchronize = 0x00100000
}
private enum SECURITY_IMPERSONATION_LEVEL
{
SecurityAnonymous,
SecurityIdentification,
SecurityImpersonation,
SecurityDelegation
}
private enum TOKEN_TYPE
{
TokenPrimary = 1,
TokenImpersonation
}
[StructLayout(LayoutKind.Sequential)]
private struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public int dwProcessId;
public int dwThreadId;
}
private struct SECURITY_ATTRIBUTES
{
public Int32 nLength;
public IntPtr lpSecurityDescriptor;
public int bInheritHandle;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private 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;
}
[DllImport("kernel32.dll", ExactSpelling = true)]
private static extern IntPtr GetCurrentProcess();
[DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
private static extern bool OpenProcessToken(IntPtr h, int acc, ref IntPtr phtok);
[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool LookupPrivilegeValue(string host, string name, ref LUID pluid);
[DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
private static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall, ref TOKEN_PRIVILEGES newst, int len, IntPtr prev, IntPtr relen);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(IntPtr hObject);
[DllImport("user32.dll")]
private static extern IntPtr GetShellWindow();
[DllImport("user32.dll", SetLastError = true)]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr OpenProcess(ProcessAccessFlags processAccess, bool bInheritHandle, uint processId);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr CreatePipe(ref IntPtr hReadPipe, ref IntPtr hWritePipe, ref SECURITY_ATTRIBUTES lpPipeAttributes,Int32 nSize);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool ReadFile(IntPtr hFile, byte[] lpBuffer, int nNumberOfBytesToRead, ref int lpNumberOfBytesRead, IntPtr lpOverlapped/*IntPtr.Zero*/);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetHandleInformation(IntPtr hObject, int dwMask, int dwFlags);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool DuplicateTokenEx(IntPtr hExistingToken, uint dwDesiredAccess, IntPtr lpTokenAttributes, SECURITY_IMPERSONATION_LEVEL impersonationLevel, TOKEN_TYPE tokenType, out IntPtr phNewToken);
[DllImport("advapi32", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern bool CreateProcessWithTokenW(IntPtr hToken, int dwLogonFlags, string lpApplicationName, string lpCommandLine, int dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, [In] ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);
#endregion
}
}