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 theNmap
tool for port scanning, but felt that just explaining theNmap
The command usage of Nmap does not give a better understanding of how it works. In fact, Nmap's underlying use of theNpcap
library, so the author decided to demonstrate how to use theNpcap
library to develop a simple scanning function, thus helping you to better understand theNmap
The principle of the
First, if you use theNmap
Performs on the target hostSYN
scan, simply execute thenmap -sS 39.97.203.57
command, 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-p
parameter 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 theSDK
development 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_header
Data 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_header
Packet 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_header
Packet 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, whereEnumAdapters
Used to enumerate all the NIC information in the current system and output its subscript number and NIC description information.BindAdapters
function 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_adapter
within the variable, and finally through thepcap_open_live
Enables 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_handler
leave it (to sb)pcap_loop
call, when enabled to capture the packet if the handle returns data will be notified through the callback function, the user to get the packetheader
After that, you can get the required fields by parsing them layer by layer, if you want to implement theSYN
Fast probing requires the judgment oftcph
flag, if the flag is returned then theRST
Disconnect the session and save scanning time by doing so.
The following code, which defines a network packet callback functionpacket_handler
The 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 theEnumAdapters
function gets the NIC number and calls theBindAdapters(4)
function binds to the specified NIC, and sockets are still created using the nativeAPI
interface is implemented, except that when calling thesendto
To send a packet we need to build our own packet that meets the requirements of theSYN
The 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 thechecksum
Calculate the calibration sum and place the calibratedpacket
packet passagesendto
function 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.