.NET:将数据注入进程的输入缓冲区

发布于 2024-08-13 06:58:11 字数 2062 浏览 4 评论 0原文

我需要自动化命令行应用程序。它要求用户输入密码。我通过 STDIN 发送密码的所有方法都失败了。现在我尝试使用 .NET 通过包装程序来完成此操作。

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

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 获取进程的句柄代码>属性。但是使用这种方式将输入发送到输入缓冲区也是不可能的。

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

  • How do I write 'PAGE DOWN' into the console input buffer? (1475353)

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

  • 使用 stdin 管道控制 Windows 控制台应用程序 (723424 ) )

使用 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)

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

输入已发送,但方式不是将其输入到密码字段中,而是在密码字段下方显示新的提示(甚至不显示输入)。

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

I need to automate an command line application. It asks the user to enter a password. All my approches to send the password via STDIN failed. Now I am trying to do this with an wrapper-programm using .NET.

I am starting the application creating a new process, setting the StartInfo-properties and then start the process:

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()

I did try to use the StartInfo.RedirectStandardInput Property but with no success.

Now I came accross the WriteConsoleInput function from the kernel32.dll that I included like this:

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

I can get the handle of the process via the myProcess.Handle property. But sending input to the input-buffer using this way was also not possible.

I found those questions but they did not help:

  • How do I write ‘PAGE DOWN’ into the console input buffer? (1475353)

  • Java - passing input into external C/C++ application (1421273)

  • Controlling a Windows Console App w/ stdin pipe (723424)

Using StraceNtX.exe I got this output for the moment the app is waiting for input:

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

Can anyone tell me, what else to try or how to do the above the right way?
Thanks!


Based on Tim Robinsons answere I've got this code now, but it does not work:

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)

My programm is an commandline application that should act as an wrapper.

The input is send but in a way that it is not typed into the password field but that below the password field a new promt is shown (without even showing the input).

Tim, can you give me an example?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

離人涙 2024-08-20 06:58:11

抱歉回复晚了,最近太忙了。

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

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

Sorry for my late reply, been very busy.

Here is an full code example, stripped down from my code. I hope I did not delete something crucial.

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

It uses the following module:

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

With this function you can start the process and wait for a line of output (i.e. "password:"). Then the function will enter the provided text followed by an return.

I hope this helps!

Regards
sc911

南城旧梦 2024-08-20 06:58:11

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

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

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

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

编辑:我一开始没有注意到,但这不是 WriteConsoleInput 的正确定义。它需要一个INPUT_RECORD数组,而不是一个字符串。一旦我复制来自pinvoke.net的函数原型<,我自己对runas.exe的实验就可以正常工作/a>.

WriteConsoleInput expects a console handle, not a process handle. Your problem is how to get hold of this console handle.

If your process is a console app, and you don't redirect anything when starting the child process, then the child process will be attached to your own process's console. In which case, you may be able to:

  1. Start the child process with redirection turned off
  2. Call GetStdHandle(STD_INPUT_HANDLE) to get hold of your own console handle
  3. Pass that console handle to WriteConsoleInput

If you have a GUI app you can assign yourself a console using AllocConsole.

Edit: I didn't notice at first, but that's not the right definition for WriteConsoleInput. It takes an array of INPUT_RECORD, not a string. My own experiment with runas.exe works OK once I copied the function prototype from pinvoke.net.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文