【问题标题】:.NET: Inject Data into Input-Buffer of Process.NET:将数据注入进程的输入缓冲区
【发布时间】:2009-12-07 11:03:43
【问题描述】:

我需要自动化一个命令行应用程序。它要求用户输入密码。我通过 STDIN 发送密码的所有方法都失败了。现在我正在尝试使用.NET 的包装程序来做到这一点。

我正在启动应用程序创建一个新进程,设置StartInfo-properties,然后启动该进程:

Dim app_path As String
Dim app_args As String
Dim myProcess As Process = New Process()
myProcess.StartInfo.FileName = app_path
myProcess.StartInfo.Arguments = app_args
myProcess.StartInfo.UseShellExecute = False
myProcess.Start()

我确实尝试使用 StartInfo.RedirectStandardInput 属性,但没有成功。

现在我遇到了来自kernel32.dllWriteConsoleInput 函数,我这样包含:

Declare Function WriteConsoleInput Lib "kernel32.dll" Alias "WriteConsoleInputA" (ByVal hConsoleInput As Integer, ByVal lpBuffer As String, ByVal nNumberOfCharsToWrite As Integer, ByRef lpNumberOfCharsWritten As Integer) As Boolean

我可以通过myProcess.Handle 属性获取进程的句柄。但是使用这种方式将输入发送到输入缓冲区也是不可能的。

我发现了这些问题,但它们没有帮助:

  • 如何将“PAGE DOWN”写入控制台输入缓冲区? (1475353)

  • Java - 将输入传递到外部 C/C++ 应用程序 (1421273)

  • 使用标准输入管道 (723424) 控制 Windows 控制台应用程序

使用 StraceNtX.exe,我在应用程序等待输入的那一刻得到了这个输出:

[T4024] GetConsoleMode(f, 12d35c, 12d3af, 77bff894, ...) = 1
[T4024] SetConsoleMode(f, 0, 12d3af, 77bff894, ...) = 1
[T4024] ReadConsoleInputA(f, 12d348, 1, 12d360, ...) = 1

谁能告诉我,还有什么可以尝试或如何以正确的方式完成上述操作? 谢谢!


根据 Tim Robinsons 的回答,我现在得到了这段代码,但它不起作用:

myProcess = New Process()
myProcess.StartInfo.FileName = app_path
myProcess.StartInfo.Arguments = app_args
myProcess.StartInfo.WindowStyle = ProcessWindowStyle.Normal
myProcess.StartInfo.UseShellExecute = False
myProcess.Start()
' Wait for process requesting passwort input
System.Threading.Thread.Sleep(3000)
Dim len As Integer
len = 0
Dim handle As Integer
handle = GetStdHandle(STD_INPUT_HANDLE)
WriteConsoleInput(handle, "Test", 4, len)

我的程序是一个命令行应用程序,应该充当包装器。

输入是发送的,但不是在密码字段中输入,而是在密码字段下方显示一个新的提示(甚至不显示输入)。

蒂姆,你能给我举个例子吗?

【问题讨论】:

  • 需要密码的应用是什么?可能有比这更简单的方法。
  • 这是思科证书管理器控制台应用程序 cisco_cert_mgr.exe 但我确实已经搜索过任何不同的解决方案,但似乎没有。

标签: .net input buffer stdin


【解决方案1】:

抱歉,我很忙,回复晚了。

这是一个完整的代码示例,从我的代码中剥离出来。我希望我没有删除重要的内容。

Private Sub runWaitInput(ByVal exe As String, ByVal parameter As String, ByVal trigger As String, ByVal input As String)
    ' runs an process, waits for a certain string in stdout and then enters text '
    Dim proc As Process
    Dim stdOut As StreamReader
    Dim ch As Int32
    Dim buffer As String = ""
    Dim InputRecords(0) As KeyEventStruct

    ' create process '
    proc = New Process()
    proc.StartInfo.FileName = exe
    proc.StartInfo.Arguments = parameter
    proc.StartInfo.UseShellExecute = False
    proc.StartInfo.RedirectStandardOutput = True
    proc.Start()

    ' redirect stdOut '
    stdOut = proc.StandardOutput
    While Not proc.HasExited
        ' read character '
        ch = stdOut.Read()
        Console.Write(Convert.ToChar(ch))
        buffer = buffer & Convert.ToChar(ch)
        ' read output and check for trigger-text '
        If buffer.LastIndexOf(trigger) <> -1 Then
            buffer = ""
            InputRecords(0) = New KeyEventStruct
            InputRecords = generateInputRecord(input & Convert.ToChar(13))
            WriteConsoleInput(STD_INPUT_HANDLE, InputRecords, InputRecords.Count(), New Integer)
        End If
    End While
    Console.Write(stdOut.ReadToEnd())
End Sub

Function generateInputRecord(ByVal str As String) As KeyEventStruct()
    Dim ret(str.Count() - 1) As KeyEventStruct
    For i = 0 To str.Count - 1 Step 1
        With ret(i)
            .EventType = 1
            .bKeyDown = True
            .uChar.AsciiChar = Convert.ToInt32(str(i))
            .dwControlKeyState = 0
            .wRepeatCount = 1
            .wVirtualKeyCode = 0
            .wVirtualScanCode = 0
        End With
    Next
    Return ret
End Function

它使用以下模块:

Imports System.Runtime.InteropServices

Module ConsoleUtils

    <Flags()> Public Enum ControlKeyState As Integer
        RightAltPressed = &H1
        LeftAltPressed = &H2
        RightCtrlPressed = &H4
        LeftCtrlPressed = &H8
        ShiftPressed = &H10
        NumLockOn = &H20
        ScrollLockOn = &H40
        CapsLockOn = &H80
        EnhancedKey = &H100
    End Enum

    <StructLayout(LayoutKind.Explicit)> Public Structure CHAR_UNION
        <FieldOffset(0)> Public UnicodeChar As Short
        <FieldOffset(0)> Public AsciiChar As Byte
    End Structure

    <DllImport("kernel32", EntryPoint:="WriteConsoleInputA", CharSet:=CharSet.Auto, SetLastError:=True, ThrowOnUnmappablechar:=True)> _
        Public Function WriteConsoleInput( _
            ByVal hConsoleInput As IntPtr, _
            ByVal lpBuffer() As KeyEventStruct, _
            ByVal nLength As Integer, _
            ByRef lpNumberOfEventsWritten As Integer _
        ) As Boolean
    End Function

    <DllImport("KERNEL32.DLL", EntryPoint:="GetStdHandle", SetLastError:=False, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _
        Public Function GetStdHandle( _
            ByVal nStdHandle As Integer _
        ) As Integer
    End Function

    <StructLayout(LayoutKind.Sequential)> Public Structure KeyEventStruct
        Public EventType As Short
        <MarshalAs(UnmanagedType.Bool)> Public bKeyDown As Boolean
        Public wRepeatCount As Short
        Public wVirtualKeyCode As Short
        Public wVirtualScanCode As Short
        Public uChar As CHAR_UNION
        Public dwControlKeyState As ControlKeyState
    End Structure

    Public ReadOnly STD_OUTPUT_HANDLE As IntPtr = New IntPtr(GetStdHandle(-11))
    Public ReadOnly STD_INPUT_HANDLE As IntPtr = New IntPtr(GetStdHandle(-10))
    Public ReadOnly STD_ERROR_HANDLE As IntPtr = New IntPtr(GetStdHandle(-12))

End Module

使用此功能,您可以启动进程并等待一行输出(即“密码:”)。然后该函数将输入提供的文本,然后返回。

我希望这会有所帮助!

问候 sc911

【讨论】:

    【解决方案2】:

    WriteConsoleInput 需要控制台句柄,而不是进程句柄。你的问题是如何获得这个控制台句柄。

    如果您的进程是控制台应用程序,并且您在启动子进程时未重定向任何内容,则子进程将附加到您自己进程的控制台。在这种情况下,您可以:

    1. 在关闭重定向的情况下启动子进程
    2. 致电GetStdHandle(STD_INPUT_HANDLE) 获取您自己的控制台手柄
    3. 将该控制台句柄传递给WriteConsoleInput

    如果您有一个 GUI 应用程序,您可以使用 AllocConsole 为自己分配一个控制台。

    编辑:一开始我没有注意到,但这不是WriteConsoleInput 的正确定义。它需要一个INPUT_RECORD 的数组,而不是一个字符串。一旦我复制了the function prototype from pinvoke.net,我自己的 runas.exe 实验就可以正常工作。

    【讨论】:

    • 我对此进行了测试(请参阅上面的更改),但它不起作用。可以举个例子吗?
    • 听起来它的行为类似于runas.exe;我会用它来做一个测试。
    • @sc911,查看我对答案的编辑 - 您对 WriteConsoleInput 的声明是错误的
    • 蒂姆,你就是那个男人!使用链接中的信息和此处的 ConsoleUtils [bytes.com/topic/visual-basic-net/answers/…(将 lpNumberOfEventsWritten-Parameter 更改为 ByRef)就可以了!非常感谢!
    猜你喜欢
    • 2012-10-22
    • 1970-01-01
    • 1970-01-01
    • 2011-08-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-27
    相关资源
    最近更新 更多