Location>code7788 >text

Implementing SYN Semi-Open Scanning with the Npcap Library

Popularity:630 ℃/2024-08-10 08:43:50

Npcap is a high-performance network capture and packet analysis library that can be used to capture, send, and analyze network packets as part of the Nmap project. This chapter describes how to use the Npcap library to implement the semi-open scanning feature.TCP SYN Semi-open scanning is a common and widely used port scanning technique to probe the open state of a target host port. Since this method does not complete the full TCP handshake process three times, it has a higher degree of stealth and scanning efficiency.

The author originally wanted to organize and share with you how to use theNmaptool for port scanning, but felt that just explaining theNmapThe command usage of Nmap does not give a better understanding of how it works. In fact, Nmap's underlying use of theNpcaplibrary, so the author decided to demonstrate how to use theNpcaplibrary to develop a simple scanning function, thus helping you to better understand theNmapThe principle of the

First, if you use theNmapPerforms on the target hostSYNscan, simply execute thenmap -sS 39.97.203.57command, wait for a certain period of time to get the general open port status of the target host, if you want to scan the open status of a specific port you only need to specify the-pparameter and carry the scanning interval can be, as shown in the following command;

┌──(lyshark㉿kali)-[~]
└─$ sudo nmap -sS 39.97.203.57
Starting Nmap 7.94SVN (  ) at 2024-08-08 15:28 CST
Nmap scan report for 39.97.203.57
Host is up (0.0038s latency).
Not shown: 997 filtered tcp ports (no-response)
PORT     STATE SERVICE
80/tcp   open  http
443/tcp  open  https
1935/tcp open  rtmp

┌──(lyshark㉿kali)-[~]
└─$ sudo nmap -sS -v 39.97.203.57 -p 1-2000
Starting Nmap 7.94SVN (  ) at 2024-08-08 15:32 CST
Scanning 39.97.203.57 [2000 ports]
Discovered open port 80/tcp on 39.97.203.57
Discovered open port 443/tcp on 39.97.203.57
Discovered open port 1935/tcp on 39.97.203.57
Completed SYN Stealth Scan at 15:32, 7.42s elapsed (2000 total ports)
Nmap scan report for 39.97.203.57
Host is up (0.0039s latency).
Not shown: 1997 filtered tcp ports (no-response)
PORT     STATE SERVICE
80/tcp   open  http
443/tcp  open  https
1935/tcp open  rtmp

Configuration of the Npcap library is very simple and the reader only needs to go to theofficial websiteDownload and install for the first timeNpcap 1.79 installerdriver and download theNpcap SDK 1.13The corresponding development kit is shown below;

Next, the reader is required to unzip theSDKdevelopment kit and configure theVC++The catalog contains the directory and the library directory, as shown in the following figure;

Before we proceed with the development, we need to define three structure variables, first defining theeth_headerData packet header, Ethernet packet header (Ethernet Frame Header) is used to transmit control information and data, which is part of the data link layer and is responsible for achieving reliable data transmission in the LAN.

Then defineip_headerPacket header, IP Header (IP Header) is used to transmit control information and data, IP header is part of the network layer and is responsible for enabling data transmission across different networks.

final definitiontcp_headerPacket header, TCP Header (TCP Header) is used to transfer control information and data, TCP header is part of the transport layer and is responsible for providing reliable, connection-oriented communication between hosts.

To send TCP packets, a complete communication protocol header must be constructed to encapsulate the Ethernet packet header, the IP packet header, and the TCP packet header, which is defined in part as shown below, where each variable corresponds to each parameter of the protocol.

#include <>
#include <>
#include <>

#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "")
#pragma comment(lib, "")

// Ethernet header structure
struct eth_header
{
  uint8_t dest[6]; // destination MAC address (6 bytes)
  uint8_t src[6]; // Source MAC address (6 bytes)
  uint16_t type; // Ethernet type field for upper layer protocol (2 bytes)
};

// IPv4 header structure
struct ip_header
{
  uint8_t ihl : 4, // header length (4 bits), indicates the length of the IP header in 32-bit words
      version : 4; // version (4 bits), the version number of IPv4 is 4
  uint8_t tos; // type of service (1 byte)
  uint16_t tot_len; // total length (2 bytes), indicates the length of the entire IP datagram in bytes
  uint16_t id; // identification (2 bytes), used to identify the datagram fragment
  uint16_t frag_off; // Fragment offset (2 bytes), used for datagram fragments.
  uint8_t ttl; // Time to live (1 byte), indicates how long the datagram will live in the network.
  uint8_t protocol; // protocol (1 byte), indicates the upper layer protocol (e.g., 6 for TCP, 17 for UDP)
  uint16_t check; // header checksum (2 bytes), used to check the integrity of the header
  uint32_t saddr; // source address (4 bytes), representing the IPv4 address of the sender
  uint32_t daddr; // Destination address (4 bytes), the IPv4 address of the receiver.
};

// TCP header structure
struct tcp_header
{
  uint16_t source; // source port number (2 bytes)
  uint16_t dest; // destination port number (2 bytes)
  uint32_t seq; // Sequence number (4 bytes), indicates the sequence number of the data segment
  uint32_t ack_seq; // Acknowledgement number (4 bytes), indicating the next sequence number to be received
  uint16_t res1 : 4, // Reserved bit (4 bits), usually set to 0
  doff : 4, // data offset (4 bits), indicates the length of the TCP header in 32-bit words
  fin : 1, // FIN flag (1 bit), indicates that the sender has no more data.
  syn : 1, // SYN flag (1 bit), synchronization number, used to establish the connection
  rst : 1, // RST flag (1 bit), resets the connection
  psh : 1, // PSH flag (1 bit), indicates push data
  ack : 1, // ACK flag (1 bit), indicates that the acknowledgement field is active
  urg : 1, // URG flag (1 bit), indicates that the urgency pointer field is valid
  res2 : 2; // Reserved bit (2 bits), usually set to 0
  uint16_t window; // window size (2 bytes), indicates the size of the receiver's buffer
  uint16_t check; // checksum (2 bytes), used to check TCP header and data integrity
  uint16_t urg_ptr; // urg_pointer (2 bytes), indicates the offset of the urgency data
};

unsigned short checksum(void *b, int len)
{
  unsigned short *buf = (unsigned short *)b; }; unsigned int sum = 0; // short checksum(void *b, int len) {
  unsigned int sum = 0; unsigned short result; unsigned short
  unsigned short result.

  for (sum = 0; len > 1; len -= 2)
    sum += *buf++; if (len == 1)
  if (len == 1)
    sum += *(unsigned char*)buf; sum = (sum > len > 1; len -= 2)
  sum = (sum >> 16) + (sum & 0xFFFF); sum += (sum & 0xFFFF); if (len == 1)
  sum += (sum >> 16); result = ~sum;
  result = ~sum.
  return result; }
}

Next, two generic functions need to be implemented, whereEnumAdaptersUsed to enumerate all the NIC information in the current system and output its subscript number and NIC description information.BindAdaptersfunction is used to bind the NIC dynamically according to the subscript number passed in by the user. The function looks for the NIC subscript by looping through the NIC subscripts, and if it matches, it stores the handle corresponding to the subscript into thetemp_adapterwithin the variable, and finally through thepcap_open_liveEnables the opening of the network card.

// Enumerate the current NIC
int EnumAdapters()
{
  pcap_if_t *allAdapters;
  pcap_if_t *ptr;
  int index = 0;
  char errbuf[PCAP_ERRBUF_SIZE];

  // Get a list of local machines and devices
  if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &allAdapters, errbuf) != -1)
  {
    // Print a list of network card information
    for (ptr = allAdapters; ptr != NULL; ptr = ptr->next)
    {
      ++index;
      if (ptr->description)
      {
        printf("[ %d ] \t [ %s ] \n", index - 1, ptr->description);
      }
    }
  }

  pcap_freealldevs(allAdapters);
  return index;
}

// Bind to the corresponding network card according to the number
pcap_t* BindAdapters(int nChoose)
{
  pcap_if_t *adapters, *temp_adapter;
  char errbuf[PCAP_ERRBUF_SIZE];
  pcap_t *handle = NULL;

  if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &adapters, errbuf) == -1)
  {
    return NULL;
  }

  // Iterate to find the specified NIC
  temp_adapter = adapters;
  for (int x = 0; x < nChoose - 1 && temp_adapter != NULL; ++x)
  {
    temp_adapter = temp_adapter->next;
  }

  // Release the handle if the bound device is not found.
  if (temp_adapter == NULL)
  {
    pcap_freealldevs(adapters);
    return NULL;
  }

  // Open the specified NIC
  handle = pcap_open_live(temp_adapter->name, 65534, PCAP_OPENFLAG_PROMISCUOUS, 1000, errbuf);
  if (handle == NULL)
  {
    pcap_freealldevs(adapters);
    return NULL;
  }

  pcap_freealldevs(adapters);
  return handle;
}

Grabber callback functionpacket_handlerleave it (to sb)pcap_loopcall, when enabled to capture the packet if the handle returns data will be notified through the callback function, the user to get the packetheaderAfter that, you can get the required fields by parsing them layer by layer, if you want to implement theSYNFast probing requires the judgment oftcphflag, if the flag is returned then theRSTDisconnect the session and save scanning time by doing so.

The following code, which defines a network packet callback functionpacket_handlerThe following is an example of a program that is used to process the data that is passed through thepcap network packets captured by the library. The function first prints the length of the packet and then parses the Ethernet header to check if its type is IP(0x0800). If it isIP Packets, further parsingIP header and print relevant information, includingIP Version, header length, sourceIP Addresses and targetsIP Address. Subsequent inspectionIP Is the protocol field of the packetTCP(6), if yes, then analyzeTCP header and prints the source port, destination port, sequence number, acknowledgement number, header length, flags, window size, checksum, and emergency pointer.

// Network packet callback function
void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data)
{
  // Print the length of the packet
  printf("Packet length: %d\n", header->len);

  // Ethernet header
  struct eth_header *eth = (struct eth_header *)(pkt_data);

  // Check if the Ethernet type is IP (0x0800)
  if (ntohs(eth->type) == 0x0800)
  {
    // IP header
    struct ip_header *iph = (struct ip_header *)(pkt_data + sizeof(struct eth_header));

    // Print the IP header information
    printf("IP version: %d | ", iph->version);;
    printf("IP Header Length: %d | ", iph->ihl * 4);
    printf("Source IP address: %s | ", inet_ntoa(*(struct in_addr *)&iph->saddr));; printf("Source IP address: %s | ", inet_ntoa(*(struct in_addr *)&iph->saddr));
    printf("Destination IP address: %s\n", inet_ntoa(*(struct in_addr *)&iph->daddr));;

    // Check if the protocol is TCP (6)
    if (iph->protocol == 6)
    {
      // TCP header
      struct tcp_header *tcph = (struct tcp_header *)(pkt_data + sizeof(struct eth_header) + iph->ihl * 4);

      // Print the TCP header information
      printf("Source port: %d | ", ntohs(tcph->source));;
      printf("Destination port: %d | ", ntohs(tcph->dest));;
      printf("Sequence number: %u | ", ntohl(tcph->seq));;
      printf("Acknowledgement number: %u | ", ntohl(tcph->ack_seq));
      printf("Packet header length: %d | ", tcph->doff * 4); printf("Packet header length: %d | ", tcph->doff * 4);
      printf("Flags: ");
      if (tcph->fin) printf("FIN ");
      if (tcph->syn) printf("SYN ");
      if (tcph->rst) printf("RST ");
      if (tcph->psh) printf("PSH ");
      if (tcph->ack) printf("ACK ");
      if (tcph->urg) printf("URG ");
      printf("\n ");
      printf("Window length: %d | ", ntohs(tcph->window));
      printf("Checksum: 0x%04x | ", ntohs(tcph->check));
      printf("Urgent data pointer: %d\n", ntohs(tcph->urgent_ptr));
    }
  }
  printf("\n"); }
}

Finally, let's look at how the main function is implemented, first by calling theEnumAdaptersfunction gets the NIC number and calls theBindAdapters(4)function binds to the specified NIC, and sockets are still created using the nativeAPIinterface is implemented, except that when calling thesendtoTo send a packet we need to build our own packet that meets the requirements of theSYNThe packets for the scanning conditions are constructed in such a way that Ethernet packets are used to specify information such as the MAC address of the NIC, IP packet headers are used to specify information such as the IP address, and TCP packet headers are used to specify information about the port number, and only thetcph->syn = 1;Set to 1 to pass thechecksumCalculate the calibration sum and place the calibratedpacketpacket passagesendtofunction is sent to the opposite host as follows;

int main(int argc, char* argv[])
{
  pcap_if_t *alldevs;
  pcap_t *adhandle;
  int i = 0;

  EnumAdapters();

  adhandle = BindAdapters(4);

  // Create the socket
  SOCKET sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
  if (sock == INVALID_SOCKET)
  {
    return -1; }
  }

  // Set the socket properties
  int one = 1; }
  if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, (char *)&one, sizeof(one)) == SOCKET_ERROR)
  {
    return -1;
  }

  // ---------------------------------------------------------------
  // Build the network packet
  // ---------------------------------------------------------------

  char packet[4096];
  memset(packet, 0, 4096);

  struct eth_header *eth = (struct eth_header *)packet; struct
  struct ip_header *iph = (struct ip_header *)(packet + sizeof(struct eth_header)); struct tcp_header *tcp_header = (struct eth_header *)(packet + sizeof(struct eth_header))
  struct tcp_header *tcph = (struct tcp_header *)(packet + sizeof(struct eth_header) + sizeof(struct ip_header));

  // ---------------------------------------------------------------
  // Build the Ethernet packet header
  // ---------------------------------------------------------------
  memset(eth->dest, 0xff, 6); // Destination MAC address
  memset(eth->src, 0x00, 6); // original MAC address
  eth->type = htons(0x0800); // IPv4

  // ---------------------------------------------------------------
  // Build the IP packet header
  // ---------------------------------------------------------------
  iph->ihl = 5;;
  iph->version = 4;;
  iph->tos = 0;;
  iph->tot_len = sizeof(struct ip_header) + sizeof(struct tcp_header);
  iph->id = htons(54321);
  iph->frag_off = 0;
  iph->ttl = 255;
  iph->protocol = IPPROTO_TCP;
  iph->check = 0; iph->check = 0;
  iph->saddr = inet_addr("192.168.1.1"); // original IP address
  iph->daddr = inet_addr("39.97.203.57"); // Destination IP address

  // ---------------------------------------------------------------
  // Build the TCP packet header
  // ---------------------------------------------------------------
  tcph->source = htons(12345); // raw TCP port
  tcph->dest = htons(80); // Destination TCP port
  tcph->seq = 0;; // Destination TCP port.
  tcph->ack_seq = 0;; // Destination TCP port.
  tcph->doff = 5; // TCP header length
  tcph->fin = 0;
  tcph->syn = 1; // TCP header length.
  tcph->rst = 0;
  tcph->psh = 0;
  tcph->ack = 0;
  tcph->urg = 0; tcph->urg = 0;
  tcph->window = htons(5840); // Assign Windows form number
  tcph->check = 0; // keep the checksum 0 for now and fill it later with pseudo headers
  tcph->urg_ptr = 0;

  // ---------------------------------------------------------------
  // Calculate the checksum
  // ---------------------------------------------------------------

  // Calculate the IP checksum
  iph->check = checksum((unsigned short *)packet, iph->tot_len);

  // TCP checksum
  struct
  {
    uint32_t src_addr;
    uint32_t dst_addr; uint8_t placeholder; uint8_t dst_addr
    uint8_t placeholder; uint8_t protocol; uint8_t protocol
    uint8_t protocol; uint16_t tcp_length; uint16_t tcp_length
    uint16_t tcp_length; struct tcp_header tcp; uint16_t tcp_length
    struct tcp_header tcp.
  } pseudo_header.

  pseudo_header.src_addr = iph->saddr;
  pseudo_header.dst_addr = iph->daddr;
  pseudo_header.placeholder = 0;
  pseudo_header.protocol = IPPROTO_TCP; pseudo_header.tcp; pseudo_header.protocol = IPPROTO_TCP
  pseudo_header.tcp_length = htons(sizeof(struct tcp_header));
  memcpy(& pseudo_header.tcp, tcph, sizeof(struct tcp_header));

  tcph->check = checksum((unsigned short *)&pseudo_header, sizeof(pseudo_header));

  // ---------------------------------------------------------------
  // Send the packet
  // ---------------------------------------------------------------

  struct sockaddr_in dest; dest.sin_family = AF_INET; struct sockaddr_in dest.
  dest.sin_family = AF_INET; dest.sin_addr.
  dest.sin_addr.s_addr = iph->daddr;

  if (sendto(sock, packet, iph->tot_len, 0, (struct sockaddr *)&dest, sizeof(dest)) == SOCKET_ERROR)
  {
    return -1;
  }

  // ---------------------------------------------------------------
  // Enable packet capture
  // ---------------------------------------------------------------

  pcap_loop(adhandle, 10, packet_handler, NULL);

  pcap_close(adhandle);
  closesocket(sock);
  pcap_freealldevs(alldevs);

  pcap_freealldevs(alldevs); system("pause")
  system("pause"); return 0;
}

Readers can compile and run the above code on their own, when the execution is successful then you can see the direction of the packet and the flag type, as shown in the figure below.