Ӕ Central

Welcome to the hidden lair of AE Central. From cars and beer to hardcore software development, you might find interesting topics in all areas. Feel free to browse!

To menu | To search

Multi-threaded port scanning in Delphi

, 20:41 - Permalink

Delphi 7There were several times when my team had to do a port scan in a commercial environment to discover rouge or lingering devices on the network. I always had troubles finding a portable, commercial-free, easy-to-use, fast and trusted application so I decided to write my own.
The idea came from this blog, but it was far away from sufficient in a mass port scanner. Sometimes the Winsock connect function times out in 20 seconds, so checking only 3 ports with the original procedure would take a minute. I had to do a manual timeout, which I achieved by calling the connect function in a separate thread, which I forcefully terminate after 500 ms. Since in a port scanner we want to maximize the speed I needed to put the above in a thread, so I can launch multiple port checks at the same time.
  TConnectRec = record
   Socket: Integer;
   Client: sockaddr_in;
   Success: Boolean;
  PConnectRec = ^TConnectRec;
  TWellKnownPorts = Record
   PortNR: Word;
   PortFunction: String;
  TCheckThread = class(TThread)
    PortIndex: Integer;
    IPToCheck: String;
    ConnectRec: TConnectRec;
    procedure Execute; Override;
    Constructor Create(In_PortIndex: Integer; In_IP: String);
  WellKnownPorts: Array[0..20] Of TWellKnownPorts =
  ( (PortNR: 20; PortFunction: 'FTP data'),
    (PortNR: 21; PortFunction: 'FTP control'),
    (PortNR: 22; PortFunction: 'SSH'),
    (PortNR: 23; PortFunction: 'Telnet'),
    (PortNR: 25; PortFunction: 'SMTP'),
    (PortNR: 80; PortFunction: 'HTTP'),
    (PortNR: 110; PortFunction: 'POP3'),
    (PortNR: 115; PortFunction: 'SFTP'),
    (PortNR: 139; PortFunction: 'NetBIOS Session'),
    (PortNR: 143; PortFunction: 'IMAP'),
    (PortNR: 389; PortFunction: 'LDAP'),
    (PortNR: 443; PortFunction: 'HTTPS'),
    (PortNR: 445; PortFunction: 'SMB over IP'),
    (PortNR: 601; PortFunction: 'Syslog'),
    (PortNR: 631; PortFunction: 'Internet Printing Protocol'),
    (PortNR: 860; PortFunction: 'iSCSI'),
    (PortNR: 1433; PortFunction: 'MSSQL'),
    (PortNR: 3268; PortFunction: 'Microsoft Global Catalog'),
    (PortNR: 3306; PortFunction: 'MySQL'),
    (PortNR: 3389; PortFunction: 'Remote Desktop Connection')
  TCP_Connection_Timeout = 500;
procedure Conn(Parameter: Pointer);
 PConnectRec(Parameter).Success := connect(PConnectRec(Parameter).Socket,PConnectRec(Parameter).Client,SizeOf(PConnectRec(Parameter).Client)) = 0;
Constructor TCheckThread.Create(In_PortIndex: Integer; In_IP: String);
 inherited Create(False);
 PortIndex := In_PortIndex;
 IPToCheck := In_IP;
 ConnectRec.Success := False;
Procedure TCheckThread.Execute;
 client: sockaddr_in;
 sock, ret: Integer;
 wsdata: WSAData;
 ThId: Cardinal;
 ThH: THandle;
 ret := WSAStartup($0002, wsdata);
 If Ret <> 0 Then Exit;
  client.sin_family := AF_INET;
  client.sin_port := htons(WellKnownPorts[PortIndex].PortNR);
  client.sin_addr.s_addr := inet_addr(PChar(IPToCheck));
  sock := socket(AF_INET, SOCK_STREAM, 0);
  ConnectRec.Socket := sock;
  ConnectRec.Client := Client;
  ThH := BeginThread(nil,0,Addr(Conn),Addr(ConnectRec),0,ThId);
  If ThH <> 0 Then Begin
                   If (WaitForSingleObject(ThH,TCP_Connection_Timeout)<>WAIT_OBJECT_0) And (sock<>INVALID_SOCKET) Then closesocket(sock);

You can launch the process with TCheckThread.Create(0,'') or you can create an array of TThread objects - I'll leave this part to you. You can see if the port check is done by calling WaitForSingleObject, and the result in CheckThread.ConnectRect.Success. For easy understanding the WellKnownPorts[CheckThread.PortIndex].PortFunction will return the general use of the port instead of a port number.

Add a comment

HTML code is displayed as text and web addresses are automatically converted.

This post's comments feed