使用Npcap庫開發(fā)簡單的掃描功能
Npcap 是由著名的Nmap項(xiàng)目開發(fā)的用于Windows平臺的數(shù)據(jù)包捕獲和注入庫,是對已不再維護(hù)的WinPcap的一個全面升級。它提供了更快的速度、更高的穩(wěn)定性和更強(qiáng)的安全性,是網(wǎng)絡(luò)監(jiān)控和數(shù)據(jù)分析領(lǐng)域的必備工具。
Npcap 是一款高性能的網(wǎng)絡(luò)捕獲和數(shù)據(jù)包分析庫,作為 Nmap 項(xiàng)目的一部分,Npcap 可用于捕獲、發(fā)送和分析網(wǎng)絡(luò)數(shù)據(jù)包。本章將介紹如何使用 Npcap 庫來實(shí)現(xiàn)半開放掃描功能。TCP SYN 半開放掃描是一種常見且廣泛使用的端口掃描技術(shù),用于探測目標(biāo)主機(jī)端口的開放狀態(tài)。由于這種方法并不完成完整的 TCP 三次握手過程,因此具有更高的隱蔽性和掃描效率。
nmap
nmap(Network Mapper)是一款開源免費(fèi)的針對大型網(wǎng)絡(luò)的端口掃描工具,nmap可以檢測目標(biāo)主機(jī)是否在線、主機(jī)端口開放情況、檢測主機(jī)運(yùn)行的服務(wù)類型及版本信息、檢測操作系統(tǒng)與設(shè)備類型等信息。本文主要介紹nmap工具安裝和基本使用方法。
nmap主要功能
(1)檢測主機(jī)是否在線。例如,列出響應(yīng)TCP和/或ICMP請求或打開特定端口的主機(jī)。
(2)掃描指定主機(jī)/主機(jī)列表端口開放狀態(tài),枚舉目標(biāo)主機(jī)上的開放端口,常用。
(3)檢測主機(jī)運(yùn)行服務(wù)類型及版本,檢測遠(yuǎn)程設(shè)備上的網(wǎng)絡(luò)服務(wù)以確定應(yīng)用程序名稱和版本號。
(4)檢測操作系統(tǒng)版本和設(shè)備類型 ,確定網(wǎng)絡(luò)設(shè)備的操作系統(tǒng)和硬件特性。
(5)可與腳本進(jìn)行腳本交互,使用Nmap腳本引擎(NSE)和Lua編程語言。
nmap工具下載和安裝
(1)nmap官方下載路徑:https://nmap.org/download.html
(2)按照操作系統(tǒng)類型下載相應(yīng)軟件安裝。
(3)Windows軟件執(zhí)行.exe軟件安裝。
(4)Linux系統(tǒng)下載的rpm軟件安裝,上傳到root用戶目錄下執(zhí)行rpm命令完成安裝:rpm -ivh nmap-7.80-1.x86_64.rpm
使用Npcap庫開發(fā)簡單的掃描功能
首先,若使用Nmap
對目標(biāo)主機(jī)進(jìn)行SYN
掃描,只需要執(zhí)行nmap -sS 39.97.203.57
命令即可,等待一段時間則可獲取到目標(biāo)主機(jī)常規(guī)開放端口狀態(tài),若要掃描特定端口開放狀態(tài)僅需要指定-p
參數(shù)并攜帶掃描區(qū)間即可,如下命令所示;
┌──(lyshark?kali)-[~] └─$ sudo nmap -sS 39.97.203.57 Starting Nmap 7.94SVN ( https://nmap.org ) 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 ( https://nmap.org ) 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
Npcap庫的配置非常簡單,讀者僅需要去到官網(wǎng)下載,初次使用還需安裝Npcap 1.79 installer驅(qū)動程序,并下載Npcap SDK 1.13對應(yīng)的開發(fā)工具包,如下圖所示;
接著,讀者需要自行解壓SDK開發(fā)工具包,并配置VC++目錄包含目錄與庫目錄,如下圖所示;
在進(jìn)行開發(fā)之前,我們需要先定義三個結(jié)構(gòu)體變量,首先定義eth_header數(shù)據(jù)包頭,以太網(wǎng)包頭(Ethernet Frame Header)用于傳輸控制信息和數(shù)據(jù),它是數(shù)據(jù)鏈路層的一部分,負(fù)責(zé)在局域網(wǎng)中實(shí)現(xiàn)數(shù)據(jù)的可靠傳輸。
接著定義ip_header數(shù)據(jù)包頭,IP頭(IP Header)用于傳輸控制信息和數(shù)據(jù),IP頭是網(wǎng)絡(luò)層的一部分,負(fù)責(zé)實(shí)現(xiàn)跨越不同網(wǎng)絡(luò)的數(shù)據(jù)傳輸。
最后定義tcp_header數(shù)據(jù)包頭,TCP頭(TCP Header)用于傳輸控制信息和數(shù)據(jù),TCP頭是傳輸層的一部分,負(fù)責(zé)在主機(jī)之間提供可靠的、面向連接的通信。
若要發(fā)送TCP數(shù)據(jù)包,必須要構(gòu)造一個完整的通信協(xié)議頭,將以太網(wǎng)數(shù)據(jù)包頭、IP數(shù)據(jù)包頭、TCP數(shù)據(jù)包頭封裝起來即可,其定義部分如下所示,其中每一個變量均對應(yīng)于協(xié)議的每一個參數(shù)。
#include <winsock2.h> #include <Windows.h> #include <pcap.h> #pragma comment(lib,"ws2_32.lib") #pragma comment(lib, "packet.lib") #pragma comment(lib, "wpcap.lib") // 以太網(wǎng)頭部結(jié)構(gòu)體 struct eth_header { uint8_t dest[6]; // 目的MAC地址 (6字節(jié)) uint8_t src[6]; // 源MAC地址 (6字節(jié)) uint16_t type; // 以太網(wǎng)類型字段,表示上層協(xié)議 (2字節(jié)) }; // IPv4頭部結(jié)構(gòu)體 struct ip_header { uint8_t ihl : 4, // 頭部長度 (4位),表示IP頭部的長度,以32位字為單位 version : 4; // 版本 (4位),IPv4的版本號為4 uint8_t tos; // 服務(wù)類型 (1字節(jié)) uint16_t tot_len; // 總長度 (2字節(jié)),表示整個IP數(shù)據(jù)報的長度,以字節(jié)為單位 uint16_t id; // 標(biāo)識 (2字節(jié)),用于標(biāo)識數(shù)據(jù)報片段 uint16_t frag_off; // 片段偏移 (2字節(jié)),用于數(shù)據(jù)報片段 uint8_t ttl; // 生存時間 (1字節(jié)),表示數(shù)據(jù)報在網(wǎng)絡(luò)中的生存時間 uint8_t protocol; // 協(xié)議 (1字節(jié)),表示上層協(xié)議 (例如,TCP為6,UDP為17) uint16_t check; // 頭部校驗(yàn)和 (2字節(jié)),用于檢驗(yàn)頭部的完整性 uint32_t saddr; // 源地址 (4字節(jié)),表示發(fā)送方的IPv4地址 uint32_t daddr; // 目的地址 (4字節(jié)),表示接收方的IPv4地址 }; // TCP頭部結(jié)構(gòu)體 struct tcp_header { uint16_t source; // 源端口號 (2字節(jié)) uint16_t dest; // 目的端口號 (2字節(jié)) uint32_t seq; // 序號 (4字節(jié)),表示數(shù)據(jù)段的序列號 uint32_t ack_seq; // 確認(rèn)號 (4字節(jié)),表示期望接收的下一個序列號 uint16_t res1 : 4, // 保留位 (4位),通常設(shè)為0 doff : 4, // 數(shù)據(jù)偏移 (4位),表示TCP頭部的長度,以32位字為單位 fin : 1, // FIN標(biāo)志 (1位),表示發(fā)送方?jīng)]有更多數(shù)據(jù) syn : 1, // SYN標(biāo)志 (1位),表示同步序號,用于建立連接 rst : 1, // RST標(biāo)志 (1位),表示重置連接 psh : 1, // PSH標(biāo)志 (1位),表示推送數(shù)據(jù) ack : 1, // ACK標(biāo)志 (1位),表示確認(rèn)字段有效 urg : 1, // URG標(biāo)志 (1位),表示緊急指針字段有效 res2 : 2; // 保留位 (2位),通常設(shè)為0 uint16_t window; // 窗口大小 (2字節(jié)),表示接收方的緩沖區(qū)大小 uint16_t check; // 校驗(yàn)和 (2字節(jié)),用于檢驗(yàn)TCP頭部和數(shù)據(jù)的完整性 uint16_t urg_ptr; // 緊急指針 (2字節(jié)),表示緊急數(shù)據(jù)的偏移量 }; unsigned short checksum(void *b, int len) { unsigned short *buf = (unsigned short *)b; unsigned int sum = 0; unsigned short result; for (sum = 0; len > 1; len -= 2) sum += *buf++; if (len == 1) sum += *(unsigned char*)buf; sum = (sum >> 16) + (sum & 0xFFFF); sum += (sum >> 16); result = ~sum; return result; }
接著需要實(shí)現(xiàn)兩個通用函數(shù),其中EnumAdapters用于枚舉當(dāng)前系統(tǒng)中所有的網(wǎng)卡信息,并輸出其下標(biāo)號與網(wǎng)卡描述信息,BindAdapters函數(shù)則用于根據(jù)用戶傳入的下標(biāo)號對網(wǎng)卡進(jìn)行動態(tài)綁定,函數(shù)中通過循環(huán)的方式查找網(wǎng)卡下標(biāo)若匹配則將下標(biāo)所對應(yīng)的句柄存儲到temp_adapter變量內(nèi),最后通過pcap_open_live實(shí)現(xiàn)對網(wǎng)卡的打開。
// 枚舉當(dāng)前網(wǎng)卡 int EnumAdapters() { pcap_if_t *allAdapters; pcap_if_t *ptr; int index = 0; char errbuf[PCAP_ERRBUF_SIZE]; // 獲取本地機(jī)器設(shè)備列表 if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &allAdapters, errbuf) != -1) { // 打印網(wǎng)卡信息列表 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; } // 根據(jù)編號綁定到對應(yīng)網(wǎng)卡 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; } // 遍歷找到指定的網(wǎng)卡 temp_adapter = adapters; for (int x = 0; x < nChoose - 1 && temp_adapter != NULL; ++x) { temp_adapter = temp_adapter->next; } // 若找不到綁定設(shè)備則釋放句柄 if (temp_adapter == NULL) { pcap_freealldevs(adapters); return NULL; } // 打開指定的網(wǎng)卡 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; }
抓包回調(diào)函數(shù)packet_handler由pcap_loop調(diào)用,當(dāng)啟用抓包后若句柄返回?cái)?shù)據(jù)則會通過回調(diào)函數(shù)通知用戶,用戶獲取到數(shù)據(jù)包header后,通過逐層解析即可得到所需要的字段,若要實(shí)現(xiàn)SYN快速探測則需要判斷tcph標(biāo)志,若標(biāo)志被返回則可通過RST斷開會話,并以此節(jié)約掃描時間。
如下代碼,定義了一個網(wǎng)絡(luò)數(shù)據(jù)包回調(diào)函數(shù) packet_handler,用于處理通過 pcap 庫捕獲的網(wǎng)絡(luò)數(shù)據(jù)包。函數(shù)首先打印數(shù)據(jù)包的長度,然后解析以太網(wǎng)頭部以檢查其類型是否為 IP(0x0800)。如果是 IP 數(shù)據(jù)包,進(jìn)一步解析 IP 頭部并打印相關(guān)信息,包括 IP 版本、頭長度、源 IP 地址和目標(biāo) IP 地址。隨后檢查 IP 數(shù)據(jù)包的協(xié)議字段是否為 TCP(6),若是,則解析 TCP 頭部并打印源端口、目標(biāo)端口、序列號、確認(rèn)號、頭部長度、標(biāo)志、窗口大小、校驗(yàn)和及緊急指針等信息。
// 網(wǎng)絡(luò)數(shù)據(jù)包回調(diào)函數(shù) void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data) { // 打印數(shù)據(jù)包長度 printf("數(shù)據(jù)包長度:%d\n", header->len); // 以太網(wǎng)頭部 struct eth_header *eth = (struct eth_header *)(pkt_data); // 檢查以太網(wǎng)類型是否為 IP(0x0800) if (ntohs(eth->type) == 0x0800) { // IP 頭部 struct ip_header *iph = (struct ip_header *)(pkt_data + sizeof(struct eth_header)); // 打印 IP 頭部信息 printf("IP 版本: %d | ", iph->version); printf("IP 頭長度: %d | ", iph->ihl * 4); printf("源IP地址: %s | ", inet_ntoa(*(struct in_addr *)&iph->saddr)); printf("目標(biāo)IP地址: %s\n", inet_ntoa(*(struct in_addr *)&iph->daddr)); // 檢查協(xié)議是否為 TCP(6) if (iph->protocol == 6) { // TCP 頭部 struct tcp_header *tcph = (struct tcp_header *)(pkt_data + sizeof(struct eth_header) + iph->ihl * 4); // 打印 TCP 頭部信息 printf("源端口: %d | ", ntohs(tcph->source)); printf("目標(biāo)端口: %d | ", ntohs(tcph->dest)); printf("序列號: %u | ", ntohl(tcph->seq)); printf("確認(rèn)號: %u | ", ntohl(tcph->ack_seq)); printf("包頭長度: %d | ", tcph->doff * 4); printf("標(biāo)志: "); 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("窗體長度: %d | ", ntohs(tcph->window)); printf("校驗(yàn)和: 0x%04x | ", ntohs(tcph->check)); printf("緊急數(shù)據(jù)指針: %d\n", ntohs(tcph->urg_ptr)); } } printf("\n"); }
最后來看下主函數(shù)是如何實(shí)現(xiàn)的,首先通過調(diào)用EnumAdapters函數(shù)獲取到網(wǎng)卡編號,并調(diào)用BindAdapters(4)函數(shù)綁定到指定的網(wǎng)卡之上,套接字的創(chuàng)建依然采用原生API接口來實(shí)現(xiàn),只不過在調(diào)用sendto發(fā)送數(shù)據(jù)包時我們需要自行構(gòu)建一個符合SYN掃描條件的數(shù)據(jù)包,在構(gòu)建數(shù)據(jù)包時,以太網(wǎng)數(shù)據(jù)包用于指定網(wǎng)卡MAC地址等信息,IP數(shù)據(jù)包頭則用于指定IP地址等信息,TCP數(shù)據(jù)包頭則用于指定端口號信息,并僅需將tcph->syn = 1;設(shè)置為1,通過checksum計(jì)算校驗(yàn)和,并將校驗(yàn)好的packet包通過sendto函數(shù)發(fā)送到對端主機(jī),如下所示;
int main(int argc, char* argv[]) { pcap_if_t *alldevs; pcap_t *adhandle; int i = 0; EnumAdapters(); adhandle = BindAdapters(4); // 創(chuàng)建套接字 SOCKET sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); if (sock == INVALID_SOCKET) { return -1; } // 設(shè)置套接字屬性 int one = 1; if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, (char *)&one, sizeof(one)) == SOCKET_ERROR) { return -1; } // --------------------------------------------------------------- // 構(gòu)建網(wǎng)絡(luò)數(shù)據(jù)包 // --------------------------------------------------------------- char packet[4096]; memset(packet, 0, 4096); struct eth_header *eth = (struct eth_header *)packet; struct ip_header *iph = (struct ip_header *)(packet + sizeof(struct eth_header)); struct tcp_header *tcph = (struct tcp_header *)(packet + sizeof(struct eth_header) + sizeof(struct ip_header)); // --------------------------------------------------------------- // 構(gòu)建以太網(wǎng)數(shù)據(jù)包頭 // --------------------------------------------------------------- memset(eth->dest, 0xff, 6); // 目標(biāo)MAC地址 memset(eth->src, 0x00, 6); // 原MAC地址 eth->type = htons(0x0800); // IPv4 // --------------------------------------------------------------- // 構(gòu)建IP數(shù)據(jù)包頭 // --------------------------------------------------------------- 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->saddr = inet_addr("192.168.1.1"); // 原始IP地址 iph->daddr = inet_addr("39.97.203.57"); // 目標(biāo)IP地址 // --------------------------------------------------------------- // 構(gòu)建TCP數(shù)據(jù)包頭 // --------------------------------------------------------------- tcph->source = htons(12345); // 原始TCP端口 tcph->dest = htons(80); // 目標(biāo)TCP端口 tcph->seq = 0; tcph->ack_seq = 0; tcph->doff = 5; // TCP 頭部長度 tcph->fin = 0; tcph->syn = 1; tcph->rst = 0; tcph->psh = 0; tcph->ack = 0; tcph->urg = 0; tcph->window = htons(5840); // 分配Windows窗體數(shù) tcph->check = 0; // 現(xiàn)在保留校驗(yàn)和0,稍后用偽標(biāo)頭填充 tcph->urg_ptr = 0; // --------------------------------------------------------------- // 計(jì)算校驗(yàn)和 // --------------------------------------------------------------- // 計(jì)算IP校驗(yàn)和 iph->check = checksum((unsigned short *)packet, iph->tot_len); // TCP 校驗(yàn)和 struct { uint32_t src_addr; uint32_t dst_addr; uint8_t placeholder; uint8_t protocol; 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_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)); // --------------------------------------------------------------- // 發(fā)送數(shù)據(jù)包 // --------------------------------------------------------------- struct sockaddr_in dest; dest.sin_family = AF_INET; dest.sin_addr.s_addr = iph->daddr; if (sendto(sock, packet, iph->tot_len, 0, (struct sockaddr *)&dest, sizeof(dest)) == SOCKET_ERROR) { return -1; } // --------------------------------------------------------------- // 啟用抓包 // --------------------------------------------------------------- pcap_loop(adhandle, 10, packet_handler, NULL); pcap_close(adhandle); closesocket(sock); pcap_freealldevs(alldevs); system("pause"); return 0; }
讀者可自行編譯并運(yùn)行上述代碼,當(dāng)執(zhí)行成功后則可看到數(shù)據(jù)包的方向及標(biāo)志類型,如下圖所示。
到此這篇關(guān)于使用Npcap庫開發(fā)簡單的掃描功能的文章就介紹到這了,更多相關(guān)Npcap庫開發(fā)掃描功能內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Apache,IIS下Discuz x1.5偽靜態(tài)設(shè)置方法
有時候我們在假設(shè)論壇的時候,為了優(yōu)化搜索引擎收錄效果,需要設(shè)為偽靜態(tài),除了后臺的設(shè)置,也需要服務(wù)器支持,下面的具體的實(shí)現(xiàn)方法,2011-05-05采用軟件負(fù)載均衡器實(shí)現(xiàn)web服務(wù)器集群(iis+nginx)
我用nginx實(shí)現(xiàn)網(wǎng)站負(fù)載均衡測試的例子,windows下IIS做負(fù)載實(shí)測2016-04-04完美解決IIS和APACHE的301重定向(帶參數(shù))
感覺BAIDU spider對404的重定向似乎無動于衷,于是近日干脆對原失效的鏈接重新設(shè)置301重定向。2010-11-11本地部署Jellyfin影音服務(wù)器并實(shí)現(xiàn)在公網(wǎng)訪問的過程
Jellyfin作為相對知名的影音服務(wù)器,在NAS平臺廣受歡迎,但筆者連韭菜都不愿意當(dāng),自然沒有NAS這樣的專業(yè)裝備,只有一臺淘汰下來的臺式機(jī),這篇文章主要介紹了本地部署Jellyfin影音服務(wù)器并實(shí)現(xiàn)在公網(wǎng)訪問的過程,需要的朋友可以參考下2023-11-11ubuntu 22.04搭建OpenVPN服務(wù)器的詳細(xì)圖文教程
這篇文章主要介紹了ubuntu 22.04搭建OpenVPN服務(wù)器的教程,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2024-01-01ssh更改默認(rèn)端口號及實(shí)現(xiàn)免密碼遠(yuǎn)程登錄
這篇文章主要介紹了ssh更改默認(rèn)端口號及實(shí)現(xiàn)免密碼遠(yuǎn)程登錄的相關(guān)資料,需要的朋友可以參考下2017-10-10iisapp.vbs iis pid了解對應(yīng)的網(wǎng)站或應(yīng)用池
有時候突然發(fā)現(xiàn)某個w3wp.exe進(jìn)程,占用較大的自由,想看看是哪個網(wǎng)站,然后再解決問題。下面是具體的方法。2009-10-10