我在创建单个进程时成功重定向输出。但是,当THAT进程产生其他进程时,不会重定向输出。除了创建初始过程之外,我无法控制如何/何时实例化孙子进程。似乎唯一的其他选项可能是Win32 API调用,但我没有找到这种方法的证据。
答案 0 :(得分:5)
当我们无法控制产生子进程时,我担心没有简单的方法可以实现这一目标。这里有两种方式:
中间的1人:我们用我们自己的IO重定向程序可执行文件替换子可执行文件,然后它将启动真正的子进程并将其IO重定向到我们自己的进程。
2-Hooking CreateProcess或Std:我们可以拦截进程创建并强制重定向所有中间进程或拦截Std调用并将它们报告给root。
注意:请注意,这些方法都不会100%有效,它们非常依赖于父和子可执行行为。
我选择中间人,因为它更易于实施且不那么脆弱。
假设我们有3个可执行文件:
PA:我们的root可执行文件,我们可以完全控制代码和行为。
PB:我们的直接子可执行文件,我们可以轻松地将IO重定向到我们自己的进程,但我们无法控制代码和行为。
PC:从 PB 中产生的间接子产品,我们无法控制它,我们无法正常重定向IO,因为它是由创建的PB 过程。
我们在这里可以做的是引入另一个 PI 进程 PC ,并将其IO重定向到自身,然后将其发送到我们的根进程(示例中 PA )
这是我们的4个可执行文件的代码
为实现这一目标,我们有4个可执行文件(PA,PB,PC和PI)以及一个库,其中包含用于 PA 和 PI 之间的流程重定向和通信渠道的共享代码我在样本中使用NetMQ
,因为它易于使用且可靠,但可以用类似的渠道替换,我们通过Nuget PM> Install-Package NetMQ
获取包。
以下是我们的类库的代码,该代码由 PA 和 PI 引用:
public class RedirectProcess
{
public static Process StartRedirected(string filename, string args = "")
{
var process = Process.Start(new ProcessStartInfo
{
Arguments = args,
UseShellExecute = false,
RedirectStandardOutput = true,
FileName = filename
});
process.OutputDataReceived += (s, e) => Console.WriteLine(e.Data);
process.BeginOutputReadLine();
return process;
}
public static Process StartGrab(string filename, Action<string> dataHandle, string args = "")
{
var process = Process.Start(new ProcessStartInfo
{
Arguments = args,
UseShellExecute = false,
RedirectStandardOutput = true,
FileName = filename
});
process.OutputDataReceived += (s, e) => dataHandle(e.Data);
process.BeginOutputReadLine();
return process;
}
}
public class RedirectClient:IDisposable
{
private readonly NetMQContext _context;
private readonly RequestSocket _socket;
public int Id { get; set; }
public RedirectClient()
{
_context = NetMQContext.Create();
_socket = _context.CreateRequestSocket();
_socket.Connect("ipc://redirectCollector");
}
public void Send(string data)
{
_socket.Send(string.Format("{0},{1}", Id, data));
_socket.Receive();//ack
}
public void Dispose()
{
_context.Dispose();
}
}
public class RediretServer:IDisposable
{
private bool _start;
public event EventHandler<ProcessDataArgs> ProcessDataReceived;
public RediretServer()
{
_start = true;
new Thread(() =>
{
using (var context = NetMQContext.Create())
{
var socket = context.CreateResponseSocket();
socket.Bind("ipc://redirectCollector");
while (_start)
{
var res = socket.ReceiveString(TimeSpan.FromSeconds(1));
if (string.IsNullOrEmpty(res))
continue;
if (ProcessDataReceived != null)
ProcessDataReceived(this, new ProcessDataArgs(res));
socket.Send(new byte[]{ });//ack
}
}
}).Start();
}
public void Dispose()
{
_start = false;
}
}
public class ProcessDataArgs:EventArgs
{
public string Data { get;private set; }
public int Id { get;private set; }
public ProcessDataArgs(string result)
{
var i = result.IndexOf(','); //first comma
Id = int.Parse(result.Substring(0, i));
Data=result.Substring(i+1);
}
}
这是我们的可执行文件:
PA: (我们的主要可执行文件,所有IO都应该重定向到它)
internal class Program
{
private static void Main(string[] args)
{
var server = new RediretServer();
server.ProcessDataReceived += (s, e) => Console.WriteLine("pid:{0}, data:{1}", e.Id, e.Data);
RedirectProcess.StartGrab("PB.exe", Console.WriteLine);
while (true)
{
Console.WriteLine("Process A, ID: {0},- Hello", Process.GetCurrentProcess().Id);
Thread.Sleep(1000);
}
}
}
PB: (仅用于模拟情况,我们的直接孩子,我们可以轻松地将其IO重定向到我们自己的)
class Program
{
static void Main(string[] args)
{
var process = Process.Start(new ProcessStartInfo
{
Arguments = "-argument",
FileName = "PC.exe"
});
while (true)
{
Console.WriteLine("Process B, ID: {0},- Hello", Process.GetCurrentProcess().Id);
Thread.Sleep(1000);
}
}
}
PC: (来自 PB 的目标嵌套子项,我们要重定向其IO)
class Program
{
static void Main(string[] args)
{
var a = args.Length == 0 ? "default" : args[0];
while (true)
{
Console.WriteLine("Process C, ID: {0}, Args: {1}- Hello", Process.GetCurrentProcess().Id, a);
Thread.Sleep(1000);
}
}
}
PI: (我们的重定向程序可执行文件,我们应该将 PC 替换为
class Program
{
private static void Main(string[] args)
{
Console.WriteLine("**Process Redirector for PC**");
var client = new RedirectClient();
var interceptArgs = args
.Aggregate(string.Empty, (current, a) => current + (string.Format("\"{0}\"", a)));
var process=RedirectProcess.StartGrab("PC-real.exe", s =>
{
Console.WriteLine(s);
client.Send(s);
},
interceptArgs);
client.Id = process.Id;
process.WaitForExit();
client.Dispose();
}
}
}
现在让我们构建它并将所有可执行文件放在一个文件夹中,如果我们运行 PA ,我们应该会看到两个控制台弹出, PA 但不是 PC 控制台输出应该像:
PA输出: (连续流)
流程A,ID:85568, - Hello
流程B,ID:85640, - Hello
PC输出: (连续流)
进程C,ID:87012,Args:-argument- Hello
不要让 PC.exe 重命名为 PC-Real.exe ,并将拦截器 PI.exe 重命名为 PC.exe 强>
如果我们再次运行 PA ,拦截器会弹出而不是原来的 PC ,我们将在 PA 进程中获取其IO。
流程A,ID:85144, - Hello
流程B,ID:86436, - Hello
pid:83660,数据:流程C,ID:83660,Args:-argument- Hello