0

I'm trying to redirect the standard and error outputs of my process (written in VB.NET), where i'm executing a continuous command. It is a tshark command (a command line tool of Wireshark) that captures network traffic at runtime. I tried both of the following commands:

  1. -i 5 -B 1 -w /sample.pcap --print -Tfields -e frame.number -e ip.addr -e tcp -e _ws.col.Info -E separator=/t
  2. -i 10 -T fields -e dns.qry.name src port 53

Both commands works great in command prompt. However when trying to redirect the output in code, only command number 1 works while the second command stuck when executing StreamReader.ReadLine.

Please note that i know that ReadLine waits for a new line to be read by the stream, where both above commands generates a new line of output for each captured packet. I did also try using Read and ReadBlock (regarding needed changes in code) but none worked for the second command.

Here is my code:

Public Class Form1

    Dim output As String
    Dim oProcess As New Process()



    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            Try
                Dim oStartInfo As New ProcessStartInfo("C:\Program Files\Wireshark\tshark.exe", "-i 10 -T fields -e dns.qry.name src port 53")
                oStartInfo.UseShellExecute = False
                oStartInfo.RedirectStandardOutput = True
                oStartInfo.RedirectStandardError = True
                oStartInfo.CreateNoWindow = True
                oStartInfo.WindowStyle = ProcessWindowStyle.Hidden
                oProcess.StartInfo = oStartInfo
            Catch ex As Exception
                MsgBox(ex)
            End Try
            BackgroundWorker1.RunWorkerAsync()
            Button1.Enabled = False
            Button2.Enabled = True
        End Sub

        Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
            Try
                Threading.Thread.Sleep(2000)
                If oProcess.Start() Then
                    Dim sOutput As String
                    Using oStreamReader As System.IO.StreamReader = oProcess.StandardOutput
                        sOutput = oStreamReader.ReadLine
                        While Not sOutput Is Nothing
                            output = sOutput & vbNewLine
                            BackgroundWorker1.ReportProgress(10)
                            sOutput = sOutput + vbNewLine + oStreamReader.ReadLine
                        End While
                    End Using

                    Using oStreamReader As System.IO.StreamReader = oProcess.StandardError
                        sOutput = oStreamReader.ReadLine
                        While Not sOutput Is Nothing
                            output = sOutput & vbNewLine
                            BackgroundWorker1.ReportProgress(10)
                            sOutput = sOutput + vbNewLine + oStreamReader.ReadLine
                        End While
                    End Using
                Else
                    MsgBox("Error starting the process")
                End If
            Catch ex As Exception
                MsgBox(ex)
            End Try

        End Sub

        Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
            TextBox1.Text = output
            TextBox1.Select(0, 0)
        End Sub

        Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
            BackgroundWorker1.CancelAsync()
            Button1.Enabled = True
            Button2.Enabled = False
        End Sub

        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load

        End Sub
    End Class
Mohammad Fneish
  • 829
  • 1
  • 9
  • 20
  • You don't need a BackgroundWorker. Use the asynchrounous (event-driven) `BeginOutputReadLine()` and `BeginErrorReadLine()` in combination with the `Exited` event. Sample code [here](https://stackoverflow.com/a/51313844/7444103) (the output goes to a RichTextBox there. You can update any other control, of course). – Jimi May 19 '19 at 15:51
  • @Jimi thanks, i tried this now where it reads the first 3 lines of the command (these lines prompts the using that capturing started) and stop reading anything else, i checked if `Exited` event is fired where is wasn't. I started thinking that there might be another output writer that `tshark` is using to print the packets. what do u think? – Mohammad Fneish May 19 '19 at 16:19
  • Try this as arguments: `"-i 10 -j ""http tcp ip"" -P -V"`. You should see a massive flow of informations. – Jimi May 19 '19 at 16:58
  • Then try: `"-i 10 -T fields -e frame.number -e ip.addr -e udp -e _ws.col.Info -j ""http tcp ip"" -P -V"`. Narrow it down after. You should see some informations once in a while. – Jimi May 19 '19 at 17:04
  • @Jimi both commands work exactly as you mentioned however mine (command number 2) still not printing, any ideas? – Mohammad Fneish May 19 '19 at 19:28
  • It doesn't capture anything. Or it will, in the future. That process won't stop until you terminate it, so... test. – Jimi May 19 '19 at 19:28
  • it only prints ```The NPF driver isn't running. You may have trouble capturing or listing interfaces. Capturing on 'Wi-Fi'``` – Mohammad Fneish May 19 '19 at 19:31
  • However in cmd it captures every traffic i make directly example: ```The NPF driver isn't running. You may have trouble capturing or listing interfaces. Capturing on 'Wi-Fi' www.youtube.com www.google.com www.google.com.lb i.ytimg.com fonts.googleapis.com yt3.ggpht.com fonts.gstatic.com s.ytimg.com s3.amazonaws.com 1.1.168.198.in-addr.arpa 19.1.168.192.in-addr.arpa 1.1.168.198.in-addr.arpa 19.1.168.192.in-addr.arpa 13 packets captured``` – Mohammad Fneish May 19 '19 at 19:33
  • I don't know what to tell you about that. Maybe it's just buffering the output. Start the service (`net start npf`) and see what happens. Start the process elevated. – Jimi May 19 '19 at 19:37
  • I don't know what is *npf* and what about to start it elevated – Mohammad Fneish May 19 '19 at 19:42
  • if you mean the NPF in the output above, i don't know what it is, it always shows up but everything is working fine in the command prompt – Mohammad Fneish May 19 '19 at 19:44
  • `NetGroup Packet Filter Driver service`. `[ProcessStartInfo].Verb = "runas"` – Jimi May 19 '19 at 19:45
  • @Jimi i changed my command line to `-i 10 -w ./sample.pcap -P -V --print -Tfields -e dns.qry.name src port 53` and that is after reading the tshark docs that says ```If you want the packets to be displayed to the standard output and also saved to a file, specify the -P option in addition to the -w option to have the summary line displayed``` now it is printing 4 packets and then `process exited` event is raised – Mohammad Fneish May 19 '19 at 20:23
  • @Jimi, thanks for your great help, i had just solved the problem, and posted an answer below. I really appreciate your aid <3. – Mohammad Fneish May 20 '19 at 04:09

1 Answers1

1

It was all a tshark issue and never a VB.Net problem. As mentioned by Mr.Kurt Knochner when answering the question How to pipe tshark output in realtime?:

tshark output is buffered. Please use tshark option -l, if you want tshark to flush STDOUT after every packet.

And by referring to the documentation of tshark here:

-l Flush the standard output after the information for each packet is printed.

So for making it working i added the --print option along with the -l to my command line and now it is working like a charm, where it now looks like:

tshark --print -l -i 10 -w ./sample.pcap -E separator=/t -T fields -e frame.number -e dns.qry.name src port 53

Here is my final version of code:

Public Class Form1

    Dim outputQueue As New Queue(Of String)
    Dim captureAdapterID As Integer = 0
    Dim oProcess As Process

    Private Sub Button1_Click(sender1 As Object, e1 As EventArgs) Handles Button1.Click
        Button1.Enabled = False
        Button2.Enabled = True
        captureAdapterID = (ComboBox1.SelectedIndex + 1)
        BackgroundWorker1.RunWorkerAsync()
    End Sub

    Private Sub BackgroundWorker1_DoWork(sender1 As Object, e1 As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
        Try
            oProcess = New Process()
            Dim oStartInfo As New ProcessStartInfo("C:\Program Files\Wireshark\tshark.exe", " --print -l -i " & captureAdapterID & " -w ./sample.pcap -E separator=/t -T fields -e frame.number -e dns.qry.name src port 53")
            oStartInfo.WorkingDirectory = New Uri(System.Windows.Forms.Application.StartupPath).LocalPath
            oStartInfo.UseShellExecute = False
            oStartInfo.RedirectStandardOutput = True

            oStartInfo.RedirectStandardError = True
            oStartInfo.CreateNoWindow = True
            oStartInfo.WindowStyle = ProcessWindowStyle.Hidden
            oProcess.StartInfo = oStartInfo

            If oProcess.Start() Then
                appendOutput("Capturing on device: " & captureAdapterID & " started.")
                Dim sOutput As String
                Using oStreamReader As System.IO.StreamReader = oProcess.StandardOutput
                    sOutput = oStreamReader.ReadLine
                    While Not sOutput Is Nothing
                        appendOutput(sOutput)
                        sOutput = oStreamReader.ReadLine
                    End While
                End Using

                Using oStreamReader As System.IO.StreamReader = oProcess.StandardError
                    sOutput = oStreamReader.ReadLine
                    While Not sOutput Is Nothing
                        appendOutput(sOutput)
                        sOutput = oStreamReader.ReadLine
                    End While
                End Using

                MsgBox("finished")
            Else
                MsgBox("Error starting the process")
            End If
        Catch ex As Exception
            MsgBox(ex.Message)
        Finally
            BackgroundWorker1.ReportProgress(10)
        End Try

    End Sub

    Private Sub appendOutput(sOutput As String)
        outputQueue.Enqueue(sOutput)
        BackgroundWorker1.ReportProgress(10)
    End Sub

    Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
        Dim i As Integer = 0
        For i = 0 To outputQueue.Count - 1 Step 1
            RichTextBox1.AppendText(outputQueue.Dequeue & vbNewLine)
        Next
        RichTextBox1.ScrollToCaret()
    End Sub

    Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
        BackgroundWorker1.CancelAsync()
        oProcess.Kill()
        Button1.Enabled = True
        Button2.Enabled = False
    End Sub

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Try
            Dim process As New Process()
            Dim oStartInfo As New ProcessStartInfo("C:\Program Files\Wireshark\tshark.exe", " -D")
            oStartInfo.WorkingDirectory = New Uri(System.Windows.Forms.Application.StartupPath).LocalPath
            oStartInfo.UseShellExecute = False
            oStartInfo.RedirectStandardOutput = True

            oStartInfo.RedirectStandardError = True
            oStartInfo.CreateNoWindow = True
            oStartInfo.WindowStyle = ProcessWindowStyle.Hidden
            process.StartInfo = oStartInfo

            If process.Start() Then

                Dim sOutput As String
                Using oStreamReader As System.IO.StreamReader = process.StandardOutput
                    sOutput = oStreamReader.ReadToEnd
                    If Not sOutput Is Nothing Then
                        ComboBox1.Items.AddRange(sOutput.Trim.Split(vbNewLine))
                        Try
                            ComboBox1.SelectedIndex = 0
                        Catch ex As Exception

                        End Try
                    End If
                End Using
            Else
                MsgBox("Error starting the get adapter process failed")
            End If
        Catch ex As Exception
            MsgBox(ex.Message)
        End Try
    End Sub
End Class

Mohammad Fneish
  • 829
  • 1
  • 9
  • 20