Design and implementation of port scanning tool based on C language

  • lfy: Go realizes four scanning modes and comparison
  • jyx: C realizes four scanning modes and comparison

1, Technical principle

Port scanning technology sends detection data packets to the TCP/UDP port of the target system, records the response of the target system, and analyzes the response to view the services of the system in monitoring or running state.

1. TCP scanning

There are three common tcp port scanning methods:

1.1 full scan (connect)

The scanning host attempts (using three handshakes) to establish a formal connection with a port of the target host. The connection starts with the system call connect(). If the port is open, the connection will be established successfully. Otherwise, return - 1, indicating that the port is closed. The flow chart of full scanning is as follows:

[the external link image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-BMZ32BlH-1645250441735)(img/007S8ZIlgy1geb1onsvvuj30np0ra74n.jpg)]

  • Advantages: the programming is simple. Only one API connect() is needed. It is relatively reliable, because TCP is a reliable protocol. When packets are lost, SYN frames will be retransmitted.
  • Disadvantages:
    • Because of the reliability of TCP, when the port does not exist, the source host will continue to try to send SYN frames in an attempt to get an ack response, and will give up after many attempts, resulting in a long scanning time.
    • The scan mode of connect may be easily found by the target host.

1.2 half scan (SYN)

In this technology, the scanning host sends SYN data segment to the selected port of the target host:

  • If the response is RST, it indicates that the port is closed, and continue to scan other ports according to the setting;
  • If the reply contains SYN and ACK, the target port is listening.

Since the full connection has not been established during SYN scanning, this technology is usually called "half connection" scanning, which has the following advantages and disadvantages:

  • Advantage: even if there is a record of scanning in the log, the record of trying to connect is the record of unsuccessful connection establishment.
  • Disadvantages: in most operating systems, the sending host needs to construct IP packets suitable for this kind of scanning, and the implementation is complex. And easy to find.

1.3 secret scanning (FIN)

TCP FIN scanning technology uses FIN packets to detect ports:

  • When a FIN packet reaches a closed port, the packet will be lost and an RST packet will be returned;
  • When a FIN packet arrives at an open port, the packet is simply discarded and the RST packet is not returned.

TCP FIN scanning is also called secret scanning. Its advantages and disadvantages are as follows:

  • Advantages: it does not contain any part of the standard TCP three-time handshake protocol and cannot be recorded. It can avoid IDS, firewall, packet filter and log audit, and is much more hidden than SYN scanning.
  • Disadvantages:
    • Under Windows, RET packets are returned regardless of whether the port is listening or not, which cannot be judged;
    • The reliability is not high. When the response packet is not received, it is uncertain whether the port is listening or losing the packet.

2.UDP scanning

When scanning a UDP port, send a UDP message to a port. If the port is open, there is no response. If it is closed, the other party will recover an ICMP port unreachable message.

  • Advantages: Linux and windows can be used
  • Disadvantages: it is also unreliable. Because the error information is returned, the speed of syn scanning is slower than that of TCP fin. If the UDP packet sent is too fast, a large number of ICMP packets will be lost.

3. Summary

The discrimination methods of the above four scanning modes are summarized as follows:

Scanning modePort openingPort off
TCP connect()connect successfullyconnect() returns - 1
TCP SYNReturn SYN and ACKReturn RST
TCP FINNo responseReturn RST
UDP scanNo responseReturn ICMP unreachable message

2, Tool design

We use C and Go languages to implement the port scanning tool, which is completed by two team members respectively. Each language implements four scanning modes: TCP connect, SYN, FIN and UDP.

In order to improve the scanning speed, we use the features of two languages:

1. Go language implementation

We adopt Go's Ctrip + producer consumer model, which allows multiple producers to send messages and multiple consumers to monitor the return at the same time. If the corresponding return is received, it indicates that the port is open. On the one hand, such a model can realize parallelism to speed up the scanning speed. On the other hand, using asynchronous model can effectively reduce the waiting time for Socket IO.

2. C language implementation

We use multithreading. Multithreading to achieve parallelism, so as to speed up the scanning speed. When multiple socket IOS are polled and one of them responds, it indicates that the scanning message of the port has been returned, which can effectively reduce the waiting time of socket io.

3, Concrete implementation

1. Specific implementation of go language (lfy)

1.1 overall structure

Specific implementation of producer and consumer model:

func producer(jobs chan *scanJob, ports []uint16) {
	for _, p := range ports {
		s := scanJob{
			Laddr: LAddr,
			Raddr: RAddr,
			SPort: uint16(random(10000, 65535)),
			DPort: p,
		}
		jobs <- &s
	}
	jobs <- &scanJob{Stop: true}
	close(jobs)
}
http://www.biyezuopin.vip
func consumer(jobs <-chan *scanJob, results chan<- *scanResult) {
	for {
		if j, ok := <-jobs; ok {
			if j.Stop == true {
				time.Sleep(time.Duration(*TimeOut) * time.Second)
				StopChan <- true
			} else if *ScanWay == "SYN" {
				SynScan(j)
				time.Sleep(1e7)
			} else if *ScanWay == "TCP" {
				TcpScan(j, results)
			} else if *ScanWay == "FIN" {
				FinScan(j)
				time.Sleep(1e7)
			} else if *ScanWay == "UDP" {
				UdpScan(j, results)
				time.Sleep(1e7)
		  }
		}
	}
}

As mentioned above, the producer function is the producer and the consumer function is the consumer. Jobs is a channel, which is a channel used for inter process communication in go language. The realization of the whole producer consumer model is mainly realized through go collaborative process, and concurrent scanning is also carried out through multiple go Ctrip. By inputting the task structure of scanJob into jobs, consumers can scan the production tasks. A consumer is a consumer who takes out a job from the jobs channel and scans the response according to the scanway. j. Stop is a stop flag. When j.Stop is received in the channel, it will send a true to enter stopchannel to stop scanning.

go producer(jobs, ports)

for {
	select {
	case res := <-results:
		fmt.Println("Open: ", res.Port)
	case <-StopChan:
		if *ScanWay == "FIN" {
			for _, v := range ports {
				if vis[v] == false {
					fmt.Println("Open: ", v)
				}
			}
		}
		eTime := time.Now().Unix()
		fmt.Println("Time: ", eTime-sTime, "s")
		os.Exit(0)
	}
}

go producer generates a producer collaboration generation task, and then polls the results channel to obtain the results.

1.2 full scan (connect mode)

The implementation of TCP scanning is the simplest. You only need to call the Dial function to establish a socket connection in the TCP layer and shake hands three times. Success means that the port is open. In order to speed up, the timeout of handshake is set here.

func TcpScan(j *scanJob, result chan<- *scanResult) {
	target := fmt.Sprintf("%s:%d", j.Raddr, j.DPort)
	conn, err := net.DialTimeout("tcp", target, time.Duration(*TimeOut)*time.Second)
	if err == nil {
		result <- &scanResult{
			Port: j.DPort,
		}
		defer conn.Close()
	}
}

1.3 specific implementation of syn and FIN scanning

  • Making and sending SYN and FIN packets

It can be seen that synscan constructs tcp packets directly and works in the ip layer. The essence of syn scanning is the syn package constructed by hand, so here we focus on the makePkg function.

func makePkg(j *scanJob) []byte {
	var flag uint16
	if *ScanWay == "SYN" {
		flag = 0x8002 // 8: 32 header length
	} else if *ScanWay == "FIN" {
		flag = 0x5001 // 5: 20 header length
	}
	tcpH := TCPHeader{
		SrcPort:       j.SPort,
		DstPort:       j.DPort,
		SeqNum:        rand.Uint32(),
		AckNum:        0,
		Flags:         flag,
		Window:        8192,
		ChkSum:        0,
		UrgentPointer: 0,
	}
    ....   http://www.biyezuopin.vip
ยท   if *ScanWay == "SYN" {
		err = binary.Write(buf, binary.BigEndian, [12]byte{0})
		checkError(err)
	}
	tcpH.ChkSum = CheckSum(buf.Bytes(), ip2Bytes(j.Laddr), ip2Bytes(j.Raddr))
	buf = new(bytes.Buffer)
	err = binary.Write(buf, binary.BigEndian, tcpH)
	....
}

Focus on the structure of TCPHeader. The TCPHeader structure defines the format of TCP message header. The Buffer byte stream generated by TCPHeader can be directly encapsulated into IP packets and sent out. Refer to the message format of TCP:

[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-sN6o7O7Q-1645250441738)(img/007S8ZIlgy1geafta994hj30ru0igabc.jpg)]

TCPHeader starts with the source port and destination port, followed by the serial number, and flags are the values after each flag position 01. It can be seen here that the difference between FIN and SYN scanning is the last two digits, one is 10 and the other is 01. 10 represents SYN set to 1, 01 represents FIN position 1, representing SYN packet and FIN packet respectively.

Next, after calculating the checksum of the whole header, set the checksum into the tcp packet, and then convert it into a byte stream.

  • Judgment on whether the port is open

SYN and FIN scans determine whether the port is open by returning packets. Therefore, how to accept returned packets is also the top priority here.

if *ScanWay == "SYN" {
	go func(num int) {
		for i := 0; i < num; i++ {
			recvSynAck(results)
		}
	}(10)
} else if *ScanWay == "FIN" {
	go func(num int) {
		for i := 0; i < num; i++ {
			recvRst(results)
		}
	}(10)
}

recvSynAck and recvRst functions accept the return packets of SYN and FIN respectively. Let's see the specific implementation:

func recvSynAck(res chan<- *scanResult) {
    ....
	conn, err := net.ListenIP("ip4:tcp", listenAddr)
	checkError(err)
	defer conn.Close()
	for {
		buf := make([]byte, 1024)
		_, addr, err := conn.ReadFrom(buf)
		if addr.String() != RAddr || buf[13] != 0x12 { // 10010 syn=1
			continue
		}
		...
		res <- &scanResult{
			Port: port,
		}
	}

The core code is as above. recvSynAck listens on the ip layer. When the return packet is received, read the byte stream into the buffer slice through ReadFrom, and then judge the 13th byte. If it is 0x12, that is, 10010, it means that the syn and ack bits are 1, that is, the port is open. The implementation of recvRst is similar.

1.4 specific implementation of UDP scanning

Since UDP ports are not common, and the current common UDP scanning method is relatively slow, here are some improvements to UDP scanning.
At present, the UDP ports open on the Internet mainly include 53, 123 and 161. Therefore, only customized data is made for common ports temporarily. When sending these data, if there is a return, it means that the port is open.

1.5 pits stepped in the process

As shown in the above figure, the syn packet that can receive the echo is on the left, but not on the right. The last 0000 on the right is the Urgent Pointer. The only difference before is the checksum. Therefore, the options on both sides are exactly the same, but the ack response is received on the left, but not on the right. This bug makes me even doubt whether the tcp three handshakes are related to the option field.

Later, through consulting the data, it was found that this is related to MTU. The frame length on the left is greater than 64 bytes and can be sent, while the frame length on the right is less than 64 bytes and cannot be sent. Therefore, I removed the option part from the original code and padded 12 \ 0, so that it can be sent successfully.

2. C oncrete implementation of C language (jyx)

2.1 overall structure

The program realizes three port scanning modes of TCP (connect, SYN, FIN) and UDP port scanning with c language under linux. I use a function array to store four implementation functions. According to the selected port scanning mode, I select different functions to execute when creating threads. The main thread hangs and waits for the end of the scanning thread, and finally prints the open port. A global queue is used to store open ports, which is realized by linked list. In addition to the connect mode, other methods need to know the ip of the source host. Therefore, before creating the scanning thread, obtain the ip address of the local machine and pass it to the scanning thread as a parameter.

Each scanning thread (tcpXXXScanPort) will establish a thread pool and create a thread tcpXXXScanEach for each port to be scanned (the thread is configured as the detach attribute). The specific implementation methods are somewhat different.

Since the calling thread can only pass one parameter, I put the information to be passed in a data structure and pass it to the child thread with a pointer. The following are the data structures you define to transfer:

struct ScanSock
{
    unsigned short portStart;
    unsigned short portEnd;
    char destIP[16];
    char sourIP[16];
};

struct ScanParam
{
    unsigned short sourPort;
    unsigned short destPort;
    char destIP[16];
    char sourIP[16];
};

2.2 full scan (connect mode)

  • Overall idea:

    Each scanning thread tcpConScanPort will establish a thread pool, create a thread tcpConScanEach for each port to be scanned, and configure the thread as the detach attribute. tcpConScanEach is responsible for connect ing the respective ports and storing the results in a global linked list.

  • Problems encountered and solutions:

    1. Parameter transfer

      Here, the parameters to be passed to each port scanning thread include the destination ip and port. In the main port scanning thread, I dynamically allocate a sockarddr for each port to be scanned_ In type space, pass the pointer to the space to each sub thread, and release the space after the sub thread connect s.

      At the beginning, all threads are allowed to access the same block of memory to determine the address. It is not considered that each thread is parallel. Therefore, the address has been changed to the next port in the for loop of the main thread, and the child thread of the current port has not used its own address.

    2. Non reentrant functions and thread safety

      Because threads are concurrent, they must be locked when accessing global variables and non reentrant functions, that is, multiple threads cannot access them at the same time. Remember that printf is not reentrant. So I have introduced two mutexes here, which are respectively used for printf and the count variables mentioned below.

    3. There are too many threads in the thread pool

      Too many threads in the thread pool will affect the efficiency of scanning, so a global variable is set to count each existing thread. When the number of ports scanned at the same time exceeds 100, the creation of sub threads will be suspended. When this global variable returns to zero, the scanning is completed and the results can be printed.

  • Code analysis:

    The header file is posted here, which mainly defines global count variables, mutexes and function declarations

    #ifndef TCPCONSCAN_H_H
    #define TCPCONSCAN_H_H
     
    #include "mysock.h"
     
    int connectCnt;
    static pthread_mutex_t connect_printf_mutex = PTHREAD_MUTEX_INITIALIZER;
    static pthread_mutex_t connect_num_mutex = PTHREAD_MUTEX_INITIALIZER;
     
    void* tcpConScanPort(void *arg);//Port scan main thread
    void* tcpConScanEach(void *arg);//Port scan sub thread
     
    #endif
    

2.3 half scan (SYN scan)

  • Overall idea:

    Similar to full scan, each scan thread tcpSynScanPort will establish a thread pool and create a tcpSynScanEach for each port to be scanned. This thread is responsible for sending SYN packets to the specified port, and another thread tcpSynScanRecv is responsible for processing all received packets. One important point is that the sent packets should be assembled by themselves. The original socket can be used for the protocol, and TCP is selected for the protocol.

  • Problems encountered and solutions:

    1. Parameter transfer

      It is also a malloc in the thread. It is free after the thread sendto. However, it can not just pass the destination ip and port like connect. Because the local ip and port should be filled in when constructing the package, so you define a data structure yourself.

    2. Byte order

      Pay attention to the conversion between network byte order and host byte order. The host byte order is stored in the small end mode, while the network byte order is transmitted in the large end mode. Pay attention to this.

    3. Checksum

      When filling in the header field, it is inevitable to write the checksum field. You need to be careful about the scope of verification. Here is a summary:

      • IP checksum only checks the IP header of 20 bytes;
      • ICMP checksum covers the whole message (ICMP header + ICMP data);
      • UDP and TCP checksum not only cover the whole message, but also have a 12 byte IP pseudo header, including source IP address (4 bytes), destination IP address (4 bytes), protocol (2 bytes, the first byte complements 0) and TCP/UDP packet length (2 bytes). In addition, the length of UDP and TCP datagrams can be odd bytes, so the padding byte 0 needs to be added at the end when calculating the checksum (note that the padding byte is only for calculating the checksum and can not be transmitted).
    4. Handling of packet loss

      Only sending SYN and receiving ACK or RST frames are actually carried out in the ip layer, and the tcp connection has not been established, so there is a great possibility of packet loss. In this scanning mode, no matter whether the other port is open or closed, there will be a response, so I use a global array to record the status of each port. 0 means no response, 1 means receiving ACK, and 2 means receiving rst.

    5. Synchronization between threads

      • The main function calls the port to scan the main thread, and the main function pthread_join waits for the scan process to finish.
      • The scanning main thread calls each port scanning thread. After each port scanning thread is sent, it will automatically end. The scanning main thread does not have to wait.
      • The scanning main thread calls the receiving thread, and determines whether the scanning ends by judging the global variable synCnt. After zeroing, the scanning main thread kill s the receiving thread and uses pthread_cancel function.
    6. tcp frame header

      [the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-OTYHAAUZ-1645250441740)(img/007S8ZIlgy1geafta994hj30ru0igabc.jpg)]

      struct tcphdr
        {
          u_int16_t th_sport;		/* source port */
          u_int16_t th_dport;		/* destination port */
          tcp_seq th_seq;		/* sequence number */
          tcp_seq th_ack;		/* acknowledgement number */
      #  if __BYTE_ORDER == __LITTLE_ENDIAN
          u_int8_t th_x2:4;		/* (unused) */
          u_int8_t th_off:4;		/* data offset */
      #  endif
      #  if __BYTE_ORDER == __BIG_ENDIAN
          u_int8_t th_off:4;		/* data offset */
          u_int8_t th_x2:4;		/* (unused) */
      #  endif
          u_int8_t th_flags;
      #  define TH_FIN	0x01
      #  define TH_SYN	0x02
      #  define TH_RST	0x04
      #  define TH_PUSH	0x08
      #  define TH_ACK	0x10
      #  define TH_URG	0x20
          u_int16_t th_win;		/* window */
          u_int16_t th_sum;		/* checksum */
          u_int16_t th_urp;		/* urgent pointer */
      };
       
      # else /* !__FAVOR_BSD */
      struct tcphdr
        {
          u_int16_t source;
          u_int16_t dest;
          u_int32_t seq;
          u_int32_t ack_seq;
      #  if __BYTE_ORDER == __LITTLE_ENDIAN
          u_int16_t res1:4;
          u_int16_t doff:4;
          u_int16_t fin:1;
          u_int16_t syn:1;
          u_int16_t rst:1;
          u_int16_t psh:1;
          u_int16_t ack:1;
          u_int16_t urg:1;
          u_int16_t res2:2;
      #  elif __BYTE_ORDER == __BIG_ENDIAN
          u_int16_t doff:4;
          u_int16_t res1:4;
          u_int16_t res2:2;
          u_int16_t urg:1;
          u_int16_t ack:1;
          u_int16_t psh:1;
          u_int16_t rst:1;
          u_int16_t syn:1;
          u_int16_t fin:1;
      #  else
      #   error "Adjust your <bits/endian.h> defines"
      #  endif
          u_int16_t window;
          u_int16_t check;
          u_int16_t urg_ptr;
      };
      # endif /* __FAVOR_BSD */
      
  • Code analysis:

    • The same is true here. The header file contains the declarations of functions and global variables

      #ifndef TCPSYNSCAN_H_H
      #define TCPSYNSCAN_H_H
       
      #include "mysock.h"
       
      int synCnt;
      static pthread_mutex_t syn_printf_mutex = PTHREAD_MUTEX_INITIALIZER;
      static pthread_mutex_t syn_num_mutex = PTHREAD_MUTEX_INITIALIZER;
       
      void* tcpSynScanPort(void *arg);
      void* tcpSynScanEach(void *arg);
      void* tcpSynScanRecv(void *arg);
       
      #endif
      
    • The implementation of this module also needs to define the tcp pseudo header. The pseudo header is a virtual data structure, in which the information is extracted from the packet header of the IP packet header where the datagram is located. It is neither transmitted downward nor submitted upward, but only for calculating the checksum. Such a checksum not only verifies the source port number and destination port number of tcp & UDP user data and the data part of tcp & UDP user datagram, but also verifies the source IP address and destination address of IP datagram. The false header ensures that the tcp & UDP data unit reaches the correct destination address.

      struct PseudoHdr
      {
          unsigned int    sIP;
          unsigned int    dIP;
          char            useless;
          char            protocol;
          unsigned short  length;
      };
      
    • checksum function

      unsigned short checksum(unsigned char*buf, unsigned int len)
      {//Inverse code summation is carried out for every 16 bits (the high overflow bit will be added to the low), that is, sum every 16 bits first and turn the resulting sum into inverse code
          unsigned long sum = 0;
          unsigned short *pbuf;
          pbuf = (unsigned short*)buf;//Converted to a pointer to 16 bits
          while(len > 1)//Sum
          {
              sum+=*pbuf++;
              len-=2;
          }
          if(len)//If len is an odd number, the last digit is required and
              sum += *(unsigned char*)pbuf;
          sum = (sum>>16)+(sum & 0xffff);
          sum += (sum>>16);//Overflow may occur in the previous step
          return (unsigned short)(~sum);
      }
      

2.4 secret scanning (FIN scanning)

  • Overall idea:

    As we all know, when calling close(), it goes through the process of waving FIN-ACK-FIN-ACK four times. When we send a FIN frame to a non listening port, there will be an RST response. On the contrary, when we send it to a listening port, there will be no response. The scanning speed is fast and the concealment is good, but it is invalid for windows system.

  • Specific details:

    It is basically the same as SYN scanning, but there is a little difference between the constructed data packets. The flag bit FIN is set to 0. In addition, the processing of received packets is not exactly the same.

  • Code analysis:

    Give the header file, global variable definition and function declaration:

    #ifndef TCPFINSCAN_H_H
    #define TCPFINSCAN_H_H
     
    #include "mysock.h"
     
    int finCnt;
    static pthread_mutex_t fin_printf_mutex = PTHREAD_MUTEX_INITIALIZER;
    static pthread_mutex_t fin_num_mutex = PTHREAD_MUTEX_INITIALIZER;
     
    void* tcpFinScanPort(void *arg);
    void* tcpFinScanEach(void *arg);
    void* tcpFinScanRecv(void *arg);
     
    #endif
    

2.5 UDP scanning

  • Overall idea:

    Send a UDP message to a port. If the port is open, there is no response. If the port is closed, the other party will reply to an ICMP port unreachable message. The main scanning thread udpIcmpScanPort establishes a thread pool and creates a thread udpIcmpScanEach for each port to be scanned, which is responsible for sending UDP packets to each port, and another thread udpIcmpScanRecv is responsible for processing all received packets. The sent packets should be assembled by themselves. The protocol can use the original socket. The protocol selects UDP. When receiving, another socket is established. It also uses the original socket, and the protocol selects ICMP.

  • Problems encountered and solutions:

    1. If the transmission frequency is too fast, a large number of packets will be lost

      If UDP packets are sent according to the frequency of SYN or FIN, a large number of ICMP or UDP packet losses will occur. The size of the frequency is related to the number of scanning ports. The larger the number of scanning ports, the easier the global variable udpCnt used to record the current number of threads is to exceed the upper limit, and the program will get stuck here.

      I think of two solutions. One is to turn down the frequency. It's OK to scan 1000 packets when the frequency is reduced to 1 / 2 of the original. But I always feel lucky in this way. I try to use the second method, or the original frequency, and add a timer (realized by signal ALARM). If the program is stuck here within the specified time (I set it to 30 seconds), I will resend the UDP packet. The speed can be similar to FIN, and the program is not stuck.

    2. The problem of choosing icmp related data structure

      There are two kinds of ICMP data structures provided by linux. ICMP and icmphdr. I measured them with sizeof. The former is 20 bytes and the latter is 8 bytes. In terms of packet capturing, the received ICMP message is IP header (20 bytes) + ICMP header (8 bytes) + UDP message of IP layer sent, so it is actually IP header (20 bytes, destination port side) + ICMP header (8 bytes) + IP header (20 bytes, port scanning side) + UDP header + UDP data, so I use icmphdr.

  • Code analysis:

    Header file

    #ifndef UDPICMPSCAN_H_H
    #define UDPICMPSCAN_H_H
     
    #include "mysock.h"
     
    int udpCnt;
    static pthread_mutex_t udp_printf_mutex = PTHREAD_MUTEX_INITIALIZER;
    static pthread_mutex_t udp_num_mutex = PTHREAD_MUTEX_INITIALIZER;
     
    void* udpIcmpScanPort(void *arg);
    void* udpIcmpScanEach(void *arg);
    void* udpIcmpScanRecv(void *arg);
    void alarm_udp(int signo);
     
    #endif
    

4, Actual effect comparison

At present, the most widely used port scanning tools are nmap and zmap. Nmap has powerful functions and accurate scanning results. The main advantage of zmap is its fast scanning speed, which is known as scanning the whole Internet in 44 minutes. However, zmap does not support multi port batch scanning. Therefore, we compared the two port scanners we wrote with nmap, a well-known product in the industry.

nmap and zmap Parameters corresponding to various scanning modes:
-sS/sT/sA/sW/sM: TCP SYN/Connect()/ACK/Window/Maimon scans
-sU: UDP Scan
-sN/sF/sX: TCP Null, FIN, and Xmas scans

Go-Portscan

1. SYN mode

It can be seen that for SYN mode, the scanning result of go scanner is basically no different from that of nmap, and the speed is much faster. The main reason for the speed here should be that nmap confirms the service fingerprint, so the speed is a little slow.

2. TCP mode

There are many filter ports in nmap. The nmap document indicates that the filter port is actually not open, so here we can see that we are still faster and the result is correct.

3. FIN mode

4. UDP mode

As you can see here, UDP scanning is extremely slow. For the accuracy of scanning, nmap is too slow to bear. Through our improvement, although it is a little troublesome, the speed has been improved a lot.

C-portscan

1. connect mode

For the connect mode, the scanning speed is still relatively slow, but it is still much faster due to multithreading acceleration

2. SYN scanning

Compared with full scanning, the speed of half scanning is greatly accelerated, and the accuracy is also guaranteed

3. FIN scanning

Theoretically, the speed of FIN scanning should be the fastest, but this is not the case here. It is because FIN scanning is unreliable. If no data packet is received, it is uncertain whether the packet is lost or the port is open. Therefore, it will send data packets to those unresponsive ports for confirmation many times to reduce the errors caused by packet loss, so the scanning takes a long time.

4. UDP scanning

As you can see, UDP scanning is very, very slow

5, Summary and reflection

Through this experiment, we have deepened our understanding and application of computer network protocol, learned a lot of knowledge in this process and understood network programming. However, the accuracy and scanning speed of the program we completed are still much different from those of professional port scanning software. I hope we can improve it in future study.

Keywords: C network TCP/IP

Added by clearstatcache on Sun, 20 Feb 2022 12:41:51 +0200