Home About

November 26th, 2008

Building a simple portscanner in C# - 0

In the dark ages before the Internet there was “war-dialing”: randomly calling telephone numbers in the hope that on the other side a computer modem would pick up. War Dialing was glamorized by the movie “Wargames” but portscanning is just like that: you too can help the world narrowly avoid nuclear Armageddon. This article shows how you can build a simple Portscanner in C#.

And with simple we mean simple, this is no NMAP. But given an IP address or domain name address it will slowly scan each available port of the target computer to see if its open.

Port scanning www.hinet.net (203.66.88.89)
Scanning port 0 : closed
Scanning port 1 : closed
Scanning port 2 : closed
Scanning port 3 : closed
....
Scanning port 7 :

Scanning is done by connect scanning the target computer. The program tries to build a connection to the IP address / port of the target. If it succeeds the port is open.

There is a gotcha here : The .NET implementation of TCPClient.Close() function does not actually close the connection properly. So we need to do the additional steps of obtaining the stream representing the connection and closing this as well before calling TCPClient.Close.

When starting the program you need to pass a domain name or IP address on the command line. We use a little regular expression magic in the IsIpAddress() method to determine if we were passed a valid IP address.

If a domain name was passed the LookupDNSName() method uses DNS to locate the matching IP address. Since more than one IP address can be represent a single domain name, the first one returned is selected.

The actual scanning is done by ScanPort(), a successful connection makes it return true.

using System;
using System.Net;
using System.Net.Sockets;
using System.Text.RegularExpressions;

namespace PortScanner
{
    class Program
    {

        // IsIpAddress
        //
        // The following routine returns true if a given string is a valid IP address 

        static bool IsIpAddress(string Address)
        {
            // The following pattern matches an IP address
            Regex IpMatch = new Regex(@"\b(?:\d{1,3}\.){3}\d{1,3}\b");
            return IpMatch.IsMatch(Address);
        }

        // LookupDNSName
        //
        // 

        static bool LookupDNSName(string ScanAddress, out IPAddress ScanIPAddress)
        {
            ScanIPAddress = null;
            IPHostEntry NameToIpAddress;

            try
            {
                // Lookup the address we are going to scan
                NameToIpAddress = Dns.GetHostEntry(ScanAddress);
            }
            catch(SocketException)
            {
                // Thrown when we are unable to lookup the name
                return false;
            }

            // Pick the first address in the list , there should be at least 1
            if (NameToIpAddress.AddressList.Length > 0)
            {
                ScanIPAddress = NameToIpAddress.AddressList[0];
                return true;
            }

            return false;
        }

        static bool ScanPort(IPAddress Address, int Port)
        {
            TcpClient Client = new TcpClient();
            try
            {
                // Attempt to connect to the given address + port
                Client.Connect(Address, Port);

                // This may seem like an avoidable step -- but TcpClient.Close does not
                // actually close the underlying connection
                // http://support.microsoft.com/default.aspx?scid=kb%3Ben-us%3B821625

                NetworkStream ClientStream = Client.GetStream();
                ClientStream.Close();

                // Free the TCPClient resource
                Client.Close();
            }
            catch(SocketException)
            {
                // Assume that a socket exception means the connection failed
                // Client.Connect returns a void (so provides no insights into
                // what it was doing)
                return false;
            }
            return true;
        }

        static void Main(string[] args)
        {
            String ScanAddress;
            IPAddress ScanIPAddress;

            try
            {
                // Try to read the scan address from the command line, or default to localhost
                if (args.Length != 0)
                    ScanAddress = args[0];
                else
                    ScanAddress = "127.0.0.1";

                // Both a hostname or an IP address are fine
                if (IsIpAddress(ScanAddress))
                {
                    ScanIPAddress = IPAddress.Parse(ScanAddress);
                }
                else
                if (!LookupDNSName(ScanAddress,out ScanIPAddress))
                {
                    Console.WriteLine("Error looking up {0}",ScanAddress);
                    return;
                }

                // Report what we are going to do
                Console.WriteLine("Port scanning {0} ({1})", ScanAddress, ScanIPAddress.ToString());

                // Scan all the possible posts
                for (int Port = IPEndPoint.MinPort; Port < IPEndPoint.MaxPort; Port++)
                {
                    Console.Write("Scanning port {0} : ", Port);
                    if (ScanPort(ScanIPAddress, Port))
                        Console.WriteLine("OPEN");
                    else
                        Console.WriteLine("closed");
                }

                // Close Up
                Console.WriteLine("Finished scanning all ports");

            }
            catch (Exception e)
            {
                Console.WriteLine("Exception caught!");
                Console.WriteLine("Source : " + e.Source);
                Console.WriteLine("Message : " + e.Message);
            }
        }
    }
}

As there are some 2^16 addresses (IPEndPoint.MinPort = 0 / IPEndPoint.MaxPort = 65536) this program will take its quite a bit of time to scan a whole machine. Also, if a port is closed (or hidden by a firewall) the TCPClient will need to wait for a timeout before being able to report this.

One possible solution to this would be to scan multiple ports simultaneously with a set of ThreadPool workers as most of the time the main thread is waiting for the TCP stack.

Image credit: makelessnoise

Share and Enjoy:
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Google
  • Reddit

Tags: , ,

Leave a Reply


Recent Comments
  • Hakbor: This works fine. But to get the NUnit to use my current tests (and not the old ones) , it is not sufficient...
  • Alberto: Your plugin is very useful; I installed it on several different blogs I manage and I’m very happy with...
  • Nelson: Saved me from doing it myself. Good article.
  • andy: i am currently playing taiwanese server wow in 奈辛瓦里(PVP) and i would like to realm transfer to...
  • berties: any english speaking playing on a taiwanese server?