C/C++ Npcap包實現數據嗅探


npcap 是Nmap自帶的一個數據包處理工具,Nmap底層就是使用這個包進行收發包的,該庫,是可以進行二次開發的,不過使用C語言開發費勁,在進行滲透任務時,還是使用Python構建數據包高效,這東西沒啥意義.

Npcap 開發包解析協議: Npcap 是Nmap項目的網絡包抓取庫在Windows下的版本,其調用接口完全遵循WinPcap規范.

#include <WinSock2.h>
#include <Windows.h>
#include <pcap.h>

#pragma comment(lib, "packet.lib")
#pragma comment(lib, "wpcap.lib")

int enumAdapters()
{
	pcap_if_t *allAdapters;    // 所有網卡設備保存
	pcap_if_t *ptr;            // 用於遍歷的指針
	int index = 0;
	char errbuf[PCAP_ERRBUF_SIZE];

	/* 獲取本地機器設備列表 */
	if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &allAdapters, errbuf) != -1)
	{
		/* 打印網卡信息列表 */
		for (ptr = allAdapters; ptr != NULL; ptr = ptr->next)
		{
			 // printf("網卡地址: %x 網卡ID: %s \n", ptr->addresses, ptr->name);
			++index;
			if (ptr->description)
				printf("ID: %d --> Name: %s \n", index,ptr->description);
		}
	}

	/* 不再需要設備列表了,釋放它 */
	pcap_freealldevs(allAdapters);
	return index;
}

int main(int argc,char *argv[])
{
	int network = enumAdapters();
	printf("網卡數量: %d \n", network);
	system("Pause");
}

接着我們通過獲取到的的網卡對應的值,填入MonitorAdapter中就可以實現監控該網卡的原始數據包,配合下方的解析函數進行各種解析.

#include <WinSock2.h>
#include <Windows.h>
#include <pcap.h>

#pragma comment(lib, "packet.lib")
#pragma comment(lib, "wpcap.lib")

void MonitorAdapter(int nChoose)
{
	pcap_if_t *adapters;
	char errbuf[PCAP_ERRBUF_SIZE];

	if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &adapters, errbuf) != -1)
	{
		// 找到指定的網卡
		for (int x = 0; x < nChoose - 1; ++x)
			adapters = adapters->next;

		char errorBuf[PCAP_ERRBUF_SIZE];

		// PCAP_OPENFLAG_PROMISCUOUS = 網卡設置為混雜模式
		// 1000 => 1000毫秒如果讀不到數據直接返回超時
		pcap_t * handle = pcap_open(adapters->name, 65534, 1, PCAP_OPENFLAG_PROMISCUOUS, 0, 0);

		if (adapters == NULL)
			return;

		// printf("開始偵聽: % \n", adapters->description);
		pcap_pkthdr *Packet_Header;    // 數據包頭
		const u_char * Packet_Data;    // 數據本身
		int retValue;
		while ((retValue = pcap_next_ex(handle, &Packet_Header, &Packet_Data)) >= 0)
		{
			if (retValue == 0)
				continue;
			// printf("偵聽長度: %d \n", Packet_Header->len);
			PrintEtherHeader(Packet_Data);
		}
	}
}

int main(int argc,char *argv[])
{
	MonitorAdapter(3);
}

解析數據鏈路層

#define hcons(A) (((WORD)(A)&0xFF00)>>8) | (((WORD)(A)&0x00FF)<<8)

// 輸出 數據鏈路層
void PrintEtherHeader(const u_char * packetData)
{
	typedef struct ether_header {
		u_char ether_dhost[6];    // 目標地址
		u_char ether_shost[6];    // 源地址
		u_short ether_type;       // 以太網類型
	} ether_header;

	struct ether_header * eth_protocol;
	eth_protocol = (struct ether_header *)packetData;

	u_short ether_type = ntohs(eth_protocol->ether_type);  // 以太網類型
	u_char *ether_src = eth_protocol->ether_shost;         // 以太網原始MAC地址
	u_char *ether_dst = eth_protocol->ether_dhost;         // 以太網目標MAC地址

	printf("類型: 0x%x \t", ether_type);
	printf("原MAC地址: %02X:%02X:%02X:%02X:%02X:%02X \t", 
		 ether_src[0], ether_src[1], ether_src[2], ether_src[3], ether_src[4], ether_src[5]);
	printf("目標MAC地址: %02X:%02X:%02X:%02X:%02X:%02X \n",
		 ether_dst[0], ether_dst[1], ether_dst[2], ether_dst[3], ether_dst[4], ether_dst[5]);
}

解析IP層數據包

void PrintIPHeader(const u_char * packetData)
{
	typedef struct ip_header
	{
		char version : 4;
		char headerlength : 4;
		char cTOS;
		unsigned short totla_length;
		unsigned short identification;
		unsigned short flags_offset;
		char time_to_live;
		char Protocol;
		unsigned short check_sum;
		unsigned int SrcAddr;
		unsigned int DstAddr;
	}ip_header;

	struct ip_header *ip_protocol;

	// +14 跳過數據鏈路層
	ip_protocol = (struct ip_header *)(packetData + 14);
	SOCKADDR_IN Src_Addr, Dst_Addr = { 0 };

	u_short check_sum = ntohs(ip_protocol->check_sum);
	int ttl = ip_protocol->time_to_live;
	int proto = ip_protocol->Protocol;

	Src_Addr.sin_addr.s_addr = ip_protocol->SrcAddr;
	Dst_Addr.sin_addr.s_addr = ip_protocol->DstAddr;
	
	printf("源地址: %15s --> ", inet_ntoa(Src_Addr.sin_addr));
	printf("目標地址: %15s --> ", inet_ntoa(Dst_Addr.sin_addr));

	printf("校驗和: %5X --> TTL: %4d --> 協議類型: ", check_sum, ttl);
	switch (ip_protocol->Protocol)
	{
	case 1: printf("ICMP \n"); break;
	case 2: printf("IGMP \n"); break;
	case 6: printf("TCP \n");  break;
	case 17: printf("UDP \n"); break;
	case 89: printf("OSPF \n"); break;
	default: printf("None \n"); break;
	}
}

解析TCP層數據包

void PrintTCPHeader(const unsigned char * packetData)
{
	typedef struct tcp_header
	{
		short SourPort;                 // 源端口號16bit
		short DestPort;                 // 目的端口號16bit
		unsigned int SequNum;           // 序列號32bit
		unsigned int AcknowledgeNum;    // 確認號32bit
		unsigned char reserved : 4, offset : 4; // 預留偏移

		unsigned char  flags;               // 標志 

		short WindowSize;               // 窗口大小16bit
		short CheckSum;                 // 檢驗和16bit
		short surgentPointer;           // 緊急數據偏移量16bit
	}tcp_header;

	struct tcp_header *tcp_protocol;
	// +14 跳過數據鏈路層 +20 跳過IP層
	tcp_protocol = (struct tcp_header *)(packetData + 14 + 20);

	u_short sport = ntohs(tcp_protocol->SourPort);
	u_short dport = ntohs(tcp_protocol->DestPort);
	int window = tcp_protocol->WindowSize;
	int flags = tcp_protocol->flags;

	printf("源端口: %6d --> 目標端口: %6d --> 窗口大小: %7d --> 標志: (%d)",
		sport, dport, window, flags);

	if (flags & 0x08) printf("PSH 數據傳輸\n");
	else if (flags & 0x10) printf("ACK 響應\n");
	else if (flags & 0x02) printf("SYN 建立連接\n");
	else if (flags & 0x20) printf("URG \n");
	else if (flags & 0x01) printf("FIN 關閉連接\n");
	else if (flags & 0x04) printf("RST 連接重置\n");
	else printf("None 未知\n");
}

解析UDP層數據包

void PrintUDPHeader(const unsigned char * packetData)
{
	typedef struct udp_header {
		uint32_t sport;   // 源端口
		uint32_t dport;   // 目標端口
		uint8_t zero;     // 保留位
		uint8_t proto;    // 協議標識
		uint16_t datalen; // UDP數據長度
	}udp_header;

	struct udp_header *udp_protocol;
	// +14 跳過數據鏈路層 +20 跳過IP層
	udp_protocol = (struct udp_header *)(packetData + 14 + 20);

	u_short sport = ntohs(udp_protocol->sport);
	u_short dport = ntohs(udp_protocol->dport);
	u_short datalen = ntohs(udp_protocol->datalen);

	printf("源端口: %5d --> 目標端口: %5d --> 大小: %5d \n", sport, dport,datalen);
}

解析ICMP層數據包

void PrintICMPHeader(const unsigned char * packetData)
{
	typedef struct icmp_header {
		uint8_t type;        // ICMP類型
		uint8_t code;        // 代碼
		uint16_t checksum;   // 校驗和
		uint16_t identification; // 標識
		uint16_t sequence;       // 序列號
		uint32_t init_time;      // 發起時間戳
		uint16_t recv_time;      // 接受時間戳
		uint16_t send_time;      // 傳輸時間戳
	}icmp_header;


	struct icmp_header *icmp_protocol;
	// +14 跳過數據鏈路層 +20 跳過IP層
	icmp_protocol = (struct icmp_header *)(packetData + 14 + 20);

	int type = icmp_protocol->type;
	int init_time = icmp_protocol->init_time;
	int send_time = icmp_protocol->send_time;
	int recv_time = icmp_protocol->recv_time;
	if (type == 8)
	{
		printf("發起時間戳: %d --> 傳輸時間戳: %d --> 接收時間戳: %d 方向: ",
			init_time, send_time, recv_time);

		switch (type)
		{
		case 0: printf("回顯應答報文 \n"); break;
		case 8: printf("回顯請求報文 \n"); break;
		default:break;
		}
	}
}

Npcap發送ARP數據包: 通過使用Npcap實現發送一個ARP廣播數據包,這里需要先構建數據包的結構,然后在發送出去.

#include <stdio.h>
#include <winsock2.h>
#include <Windows.h>
#include <pcap.h>

#pragma comment(lib, "packet.lib")
#pragma comment(lib, "wpcap.lib")
#pragma comment(lib,"WS2_32.lib")

#define ETH_ARP      0x0806   // 以太網幀類型表示后面數據的類型,對於ARP請求或應答來說,該字段的值為x0806
#define ARP_HARDWARE 1        // 硬件類型字段值為表示以太網地址
#define ETH_IP       0x0800   // 協議類型字段表示要映射的協議地址類型值為x0800表示IP地址
#define ARP_REQUEST  1        // ARP請求
#define ARP_RESPONSE 2        // ARP應答

//14字節以太網首部
struct EthernetHeader
{
	u_char DestMAC[6];    // 目的MAC地址6字節
	u_char SourMAC[6];    // 源MAC地址 6字節
	u_short EthType;      // 上一層協議類型,如0x0800代表上一層是IP協議,0x0806為arp  2字節
};

//28字節ARP幀結構
struct ArpHeader
{
	unsigned short hdType;    // 硬件類型
	unsigned short proType;   // 協議類型
	unsigned char hdSize;     // 硬件地址長度
	unsigned char proSize;    // 協議地址長度
	unsigned short op;        // 操作類型,ARP請求(1),ARP應答(2),RARP請求(3),RARP應答(4)。
	u_char smac[6];           // 源MAC地址
	u_char sip[4];            // 源IP地址
	u_char dmac[6];           // 目的MAC地址
	u_char dip[4];            // 目的IP地址
};

//定義整個arp報文包,總長度42字節
struct ArpPacket {
	EthernetHeader ed;
	ArpHeader ah;
};

// 獲取到指定網卡的句柄
pcap_t * OpenPcap(int nChoose)
{
	pcap_t *pcap_handle;   //打開網絡適配器,捕捉實例,是pcap_open返回的對象
	pcap_if_t *alldevs;
	char errbuf[PCAP_ERRBUF_SIZE];   //錯誤緩沖區,大小為256

	// 獲取到所有設備列表
	if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1)
		exit(0);
	// 找到指定的網卡設備
	for (int x = 0; x < nChoose - 1; ++x)
		alldevs = alldevs->next;

	if ((pcap_handle = pcap_open(alldevs->name,      // 設備名
		65536,                                       // 每個包長度
		PCAP_OPENFLAG_PROMISCUOUS,                   // 混雜模式
		1000,                                        // 讀取超時時間
		NULL,                                        // 遠程機器驗證
		errbuf                                       // 錯誤緩沖池
		)) == NULL)
	{
		pcap_freealldevs(alldevs);
		exit(0);
	}
	return pcap_handle;
}

int main(int argc, char *argv[])
{
	pcap_t *handle;            // 打開網絡適配器
	EthernetHeader eh;         // 定義以太網包頭
	ArpHeader ah;              // 定義ARP包頭

	unsigned char sendbuf[42]; // arp包結構大小42個字節
	unsigned char src_mac[6] = { 0xaa, 0xaa, 0xaa, 0xaa, 0xff, 0xff };
	unsigned char src_ip[4] = { 0x01, 0x02, 0x03, 0x04 };

	handle = OpenPcap(3);      // 拿到第三個網卡的句柄

	// 開始填充ARP包
	memset(eh.DestMAC, 0xff, 6);      // 以太網首部目的MAC地址,全為廣播地址
	memcpy(eh.SourMAC, src_mac, 6);   // 以太網首部源MAC地址
	memcpy(ah.smac, src_mac, 6);      // ARP字段源MAC地址
	memset(ah.dmac, 0xff, 6);         // ARP字段目的MAC地址
	memcpy(ah.sip, src_ip, 4);        // ARP字段源IP地址
	memset(ah.dip, 0x05, 4);          // ARP字段目的IP地址

	// 賦值MAC地址
	eh.EthType = htons(ETH_ARP);   //htons:將主機的無符號短整形數轉換成網絡字節順序
	ah.hdType = htons(ARP_HARDWARE);
	ah.proType = htons(ETH_IP);
	ah.hdSize = 6;
	ah.proSize = 4;
	ah.op = htons(ARP_REQUEST);

	// 構造一個ARP請求
	memset(sendbuf, 0, sizeof(sendbuf));            // ARP清零
	memcpy(sendbuf, &eh, sizeof(eh));               // 首先把eh以太網結構填充上
	memcpy(sendbuf + sizeof(eh), &ah, sizeof(ah));  // 接着在eh后面填充arp結構

	// 發送數據包
	if (pcap_sendpacket(handle, sendbuf, 42) == 0)
	{
		printf("發送ARP數據包成功! \n");
	}

	system("pause");
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM