欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C/C++ 運(yùn)用Npcap發(fā)送UDP數(shù)據(jù)包的完美過程

 更新時(shí)間:2023年11月25日 14:57:20   作者:微軟技術(shù)分享  
UDP 是一種無連接、輕量級的傳輸層協(xié)議,與 TCP 相比,它不提供可靠性、流控制和錯(cuò)誤恢復(fù)機(jī)制,但卻更加簡單且具有較低的開銷,這篇文章主要介紹了C/C++ 運(yùn)用Npcap發(fā)送UDP數(shù)據(jù)包,需要的朋友可以參考下

Npcap 是一個(gè)功能強(qiáng)大的開源網(wǎng)絡(luò)抓包庫,它是 WinPcap 的一個(gè)分支,并提供了一些增強(qiáng)和改進(jìn)。特別適用于在 Windows 環(huán)境下進(jìn)行網(wǎng)絡(luò)流量捕獲和分析。除了支持通常的網(wǎng)絡(luò)抓包功能外,Npcap 還提供了對數(shù)據(jù)包的拼合與構(gòu)造,使其成為實(shí)現(xiàn) UDP 數(shù)據(jù)包發(fā)包的理想選擇。本章將通過Npcap庫構(gòu)造一個(gè)UDP原始數(shù)據(jù)包,并實(shí)現(xiàn)對特定主機(jī)的發(fā)包功能,通過本章的學(xué)習(xí)讀者可以掌握如何使用Npcap庫偽造特定的數(shù)據(jù)包格式。

Npcap 是一個(gè)功能強(qiáng)大的開源網(wǎng)絡(luò)抓包庫,它是 WinPcap 的一個(gè)分支,并提供了一些增強(qiáng)和改進(jìn)。特別適用于在 Windows 環(huán)境下進(jìn)行網(wǎng)絡(luò)流量捕獲和分析。除了支持通常的網(wǎng)絡(luò)抓包功能外,Npcap 還提供了對數(shù)據(jù)包的拼合與構(gòu)造,使其成為實(shí)現(xiàn) UDP 數(shù)據(jù)包發(fā)包的理想選擇。本章將通過Npcap庫構(gòu)造一個(gè)UDP原始數(shù)據(jù)包,并實(shí)現(xiàn)對特定主機(jī)的發(fā)包功能,通過本章的學(xué)習(xí)讀者可以掌握如何使用Npcap庫偽造特定的數(shù)據(jù)包格式。

Npcap的主要特點(diǎn)和概述:

  • 原始套接字支持: Npcap 允許用戶通過原始套接字在網(wǎng)絡(luò)層捕獲和發(fā)送數(shù)據(jù)包。這使得用戶能夠進(jìn)行更底層的網(wǎng)絡(luò)活動監(jiān)控和分析。
  • WinPcap 的增強(qiáng)版本: Npcap 是 WinPcap 的一個(gè)分支,對其進(jìn)行了一些增強(qiáng)和改進(jìn)。這些改進(jìn)包括對新版本 Windows 的支持、更好的性能和穩(wěn)定性,以及一些額外的功能。
  • 支持 Windows 10: Npcap 被設(shè)計(jì)用于支持 Windows 10 操作系統(tǒng)。它允許用戶在最新的 Windows 平臺上進(jìn)行網(wǎng)絡(luò)抓包和分析。
  • Loopback 模式: Npcap 允許在 Loopback 接口上進(jìn)行抓包,使用戶能夠監(jiān)視本地主機(jī)上的網(wǎng)絡(luò)流量。
  • 多種應(yīng)用場景: Npcap 被廣泛應(yīng)用于網(wǎng)絡(luò)安全、網(wǎng)絡(luò)管理、網(wǎng)絡(luò)調(diào)試等各種場景。它為開發(fā)人員、網(wǎng)絡(luò)管理員和安全專家提供了一個(gè)功能強(qiáng)大的工具,用于分析和理解網(wǎng)絡(luò)通信。
  • 開源: Npcap 是開源項(xiàng)目,其源代碼可以在 GitHub 上獲得。這使得用戶可以自由查看、修改和定制代碼,以滿足特定需求。

UDP 是一種無連接、輕量級的傳輸層協(xié)議,與 TCP 相比,它不提供可靠性、流控制和錯(cuò)誤恢復(fù)機(jī)制,但卻更加簡單且具有較低的開銷。UDP 主要用于那些對傳輸速度要求較高、可以容忍少量丟失的應(yīng)用場景。

UDP 數(shù)據(jù)包結(jié)構(gòu): UDP 數(shù)據(jù)包由報(bào)頭和數(shù)據(jù)兩部分組成。

  • 報(bào)頭(Header):
    • 源端口號(16 位): 指定發(fā)送端口。
    • 目標(biāo)端口號(16 位): 指定接收端口。
    • 長度(16 位): 報(bào)頭和數(shù)據(jù)的總長度,以字節(jié)為單位。
    • 校驗(yàn)和(16 位): 用于驗(yàn)證數(shù)據(jù)在傳輸過程中的完整性。
  • 數(shù)據(jù)(Payload):
    • 實(shí)際傳輸?shù)臄?shù)據(jù),長度可變。

UDP 的特點(diǎn):

  • 面向無連接: UDP 是一種無連接協(xié)議,通信雙方不需要在傳輸數(shù)據(jù)之前建立連接。這使得它的開銷較低,適用于一些實(shí)時(shí)性要求較高的應(yīng)用。
  • 不可靠性: UDP 不提供數(shù)據(jù)的可靠性保證,不保證數(shù)據(jù)包的到達(dá)、順序和完整性。因此,它更適合那些能夠容忍一些數(shù)據(jù)丟失的場景,如音視頻傳輸。
  • 適用于廣播和多播: UDP 支持廣播和多播通信,可以通過一個(gè)發(fā)送操作同時(shí)向多個(gè)目標(biāo)發(fā)送數(shù)據(jù)。
  • 低開銷: 由于缺乏連接建立和維護(hù)的開銷,以及不提供可靠性保證的特性,UDP 具有較低的開銷,適用于對實(shí)時(shí)性要求較高的應(yīng)用。
  • 適用于短消息: 由于不需要建立連接,UDP 適合傳輸短消息,尤其是對實(shí)時(shí)性要求高的應(yīng)用。

UDP 的應(yīng)用場景:

  • 實(shí)時(shí)性要求高的應(yīng)用: 如實(shí)時(shí)音視頻傳輸、在線游戲等。
  • 簡單的請求-響應(yīng)通信: 適用于一些簡單的請求-響應(yīng)場景,如 DNS 查詢。
  • 廣播和多播應(yīng)用: UDP 的支持廣播和多播特性使其適用于這類通信模式。
  • 實(shí)時(shí)數(shù)據(jù)采集: 例如傳感器數(shù)據(jù)采集等場景。

輸出網(wǎng)卡

使用 WinPcap(Windows Packet Capture)庫列舉系統(tǒng)上的網(wǎng)絡(luò)接口以及它們的 IP 地址。WinPcap 是一個(gè)用于 Windows 操作系統(tǒng)的網(wǎng)絡(luò)數(shù)據(jù)包捕獲庫,可以用于網(wǎng)絡(luò)數(shù)據(jù)包的捕獲和分析。

代碼主要做了以下幾個(gè)事情:

  • 使用 pcap_findalldevs_ex 函數(shù)查找系統(tǒng)上的所有網(wǎng)絡(luò)接口。
  • 遍歷每個(gè)網(wǎng)絡(luò)接口,獲取其 IP 地址,并將地址列表打印出來。

pcap_findalldevs_ex 用于查找系統(tǒng)上所有網(wǎng)絡(luò)接口的函數(shù)。它的原型如下:

int pcap_findalldevs_ex(const char *source, struct pcap_rmtauth *auth, pcap_if_t **alldevs, char *errbuf);

函數(shù)參數(shù)說明:

  • source:一個(gè)字符串,用于指定網(wǎng)絡(luò)接口的來源??梢詾?nbsp;NULL,表示從系統(tǒng)獲取網(wǎng)絡(luò)接口信息。也可以指定為一個(gè)網(wǎng)絡(luò)地址,用于遠(yuǎn)程捕獲。
  • auth:一個(gè) pcap_rmtauth 結(jié)構(gòu)的指針,用于指定遠(yuǎn)程捕獲的認(rèn)證信息。一般情況下可以為 NULL。
  • alldevs:一個(gè) pcap_if_t 類型的指針的地址,用于保存查找到的網(wǎng)絡(luò)接口鏈表的頭指針。
  • errbuf:一個(gè)字符數(shù)組,用于保存錯(cuò)誤信息。

函數(shù)返回值:

  • 成功時(shí)返回 0。
  • 失敗時(shí)返回 -1,錯(cuò)誤信息保存在 errbuf 中。

函數(shù)功能:

pcap_findalldevs_ex 主要用于查找系統(tǒng)上的網(wǎng)絡(luò)接口信息。當(dāng)調(diào)用成功后,alldevs 將指向一個(gè)鏈表,鏈表中的每個(gè)節(jié)點(diǎn)都包含一個(gè)網(wǎng)絡(luò)接口的信息。這個(gè)鏈表的頭指針是 alldevs。

pcap_freealldevs 用于釋放 pcap_findalldevs_ex 函數(shù)分配的資源的函數(shù)。其原型如下:

void pcap_freealldevs(pcap_if_t *alldevs);

函數(shù)參數(shù)說明:

  • alldevs:由 pcap_findalldevs_ex 返回的鏈表的頭指針。

函數(shù)功能:

pcap_freealldevs 主要用于釋放 pcap_findalldevs_ex 函數(shù)返回的鏈表中分配的資源,包括每個(gè)節(jié)點(diǎn)和節(jié)點(diǎn)中保存的接口信息。

輸出當(dāng)前系統(tǒng)中活動網(wǎng)卡信息,可以這樣來寫,如下代碼所示;

#include <WinSock2.h>
#include <Windows.h>
#include <iostream>
#include <pcap.h>
#pragma comment(lib,"ws2_32.lib")
#pragma comment(lib, "packet.lib")
#pragma comment(lib, "wpcap.lib")
// 打開網(wǎng)卡返回的指針
pcap_t* m_adhandle;
unsigned char* FinalPacket;
unsigned int UserDataLen;
int main(int argc, char *argv[])
{
	// 打開網(wǎng)卡
	pcap_if_t* alldevs = NULL, *d = NULL;
	char szErr[MAX_PATH] = { 0 };
	if (-1 == pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, szErr))
	{
		return 0;
	}
	// 遍歷網(wǎng)卡
	char* lpszIP = NULL;
	d = alldevs;
	while (NULL != d)
	{
		// 遍歷網(wǎng)卡IP
		char szAddress[1024] = { 0 };
		pcap_addr_t* p = d->addresses;
		while (p)
		{
			lpszIP = inet_ntoa(((sockaddr_in*)p->addr)->sin_addr);
			strcpy(szAddress, lpszIP);
			p = p->next;
		}
		std::cout << "地址列表: " << szAddress << std::endl;
		d = d->next;
	}
	// 釋放資源
	pcap_freealldevs(alldevs);
	system("pause");
	return 0;
}

輸出效果如下圖所示;

打開網(wǎng)卡

打開網(wǎng)絡(luò)適配器的函數(shù),通過傳入本機(jī)的IP地址,該函數(shù)會查找與該IP地址匹配的網(wǎng)絡(luò)適配器并打開。以下是對該函數(shù)的簡要分析:

查找網(wǎng)卡設(shè)備指針:

if (-1 == pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf))

使用 pcap_findalldevs_ex 函數(shù)來獲取本機(jī)所有網(wǎng)卡設(shè)備的鏈表。如果返回值為 -1,說明發(fā)生了錯(cuò)誤,這時(shí)函數(shù)會輸出錯(cuò)誤信息并直接返回。

選取適合網(wǎng)卡:

for (d = alldevs; d; d = d->next)

通過遍歷網(wǎng)卡設(shè)備鏈表,查找與傳入的本機(jī)IP地址匹配的網(wǎng)卡。首先,通過檢查每個(gè)網(wǎng)卡的地址列表,找到第一個(gè)匹配的網(wǎng)卡。如果找到了,將 flag 標(biāo)記設(shè)為1,然后跳出循環(huán)。如果未找到匹配的網(wǎng)卡,輸出錯(cuò)誤信息并返回。

獲取子網(wǎng)掩碼:

netmask = ((sockaddr_in*)d->addresses->netmask)->sin_addr.S_un.S_addr;

獲取匹配網(wǎng)卡的子網(wǎng)掩碼。

打開網(wǎng)卡:

m_adhandle = pcap_open(d->name, 65536, PCAP_OPENFLAG_PROMISCUOUS, 1000, NULL, errbuf);

使用 pcap_open 函數(shù)打開選擇的網(wǎng)卡,該函數(shù)的聲明如下:

pcap_t *pcap_open(const char *source, int snaplen, int flags, int read_timeout, struct pcap_rmtauth *auth, char *errbuf);

這里是對參數(shù)的簡要解釋:

  • source: 要打開的網(wǎng)絡(luò)適配器的名稱,例如 "eth0"。
  • snaplen: 指定捕獲數(shù)據(jù)包時(shí)每個(gè)數(shù)據(jù)包的最大長度。如果數(shù)據(jù)包超過這個(gè)長度,它將被截?cái)唷MǔTO(shè)置為數(shù)據(jù)包的最大可能長度。
flags

: 控制捕獲的方式,可以使用位掩碼進(jìn)行組合。常見的標(biāo)志包括:

  • PCAP_OPENFLAG_PROMISCUOUS: 開啟混雜模式,允許捕獲所有經(jīng)過網(wǎng)卡的數(shù)據(jù)包。
  • PCAP_OPENFLAG_MAX_RESPONSIVENESS: 最大響應(yīng)性標(biāo)志,可能在某些平臺上影響性能。
  • read_timeout: 設(shè)置超時(shí)值,以毫秒為單位。如果設(shè)置為0,表示無限期等待數(shù)據(jù)包。
  • auth: 可以指定用于遠(yuǎn)程捕獲的身份驗(yàn)證信息,通常為 NULL。
  • errbuf: 用于存儲錯(cuò)誤信息的緩沖區(qū),如果函數(shù)執(zhí)行失敗,會將錯(cuò)誤信息寫入這個(gè)緩沖區(qū)。

函數(shù)返回一個(gè) pcap_t 類型的指針,它是一個(gè)表示打開的網(wǎng)絡(luò)適配器的結(jié)構(gòu)。如果打開失敗,返回 NULL

檢查以太網(wǎng):

if (DLT_EN10MB != pcap_datalink(m_adhandle))

pcap_datalink 函數(shù)是 PCAP 庫中用于獲取網(wǎng)絡(luò)適配器數(shù)據(jù)鏈路類型(datalink type)的函數(shù),確保是以太網(wǎng),如果不是以太網(wǎng),輸出錯(cuò)誤信息并返回。

該函數(shù)的聲明如下:

int pcap_datalink(pcap_t *p);

這里是對參數(shù)的簡要解釋:

  • p: 表示一個(gè)已經(jīng)打開的網(wǎng)絡(luò)適配器的 pcap_t 結(jié)構(gòu)指針。

函數(shù)返回一個(gè)整數(shù),表示數(shù)據(jù)鏈路類型。這個(gè)值通常是預(yù)定義的常量之一,用于標(biāo)識不同類型的網(wǎng)絡(luò)數(shù)據(jù)鏈路。

常見的一些數(shù)據(jù)鏈路類型常量包括:

  • DLT_EN10MB(Ethernet): 表示以太網(wǎng)數(shù)據(jù)鏈路。
  • DLT_IEEE802(802.5 Token Ring): 表示 IEEE 802.5 Token Ring 數(shù)據(jù)鏈路。
  • DLT_PPP(Point-to-Point Protocol): 表示點(diǎn)對點(diǎn)協(xié)議數(shù)據(jù)鏈路。
  • DLT_ARCNET(ARCNET): 表示 ARCNET 數(shù)據(jù)鏈路。

釋放網(wǎng)卡設(shè)備列表:

pcap_freealldevs(alldevs);

最后,釋放 pcap_findalldevs_ex 函數(shù)返回的網(wǎng)卡設(shè)備列表,避免內(nèi)存泄漏。

該函數(shù)的其他全局變量 m_adhandle,FinalPacketUserDataLen 已經(jīng)在文章開頭聲明和定義。

// 通過傳入本機(jī)IP地址打開網(wǎng)卡
void OpenAdapter(std::string local_address)
{
  pcap_if_t* alldevs = NULL, * d = NULL;
  char errbuf[256] = { 0 };
  bpf_program fcode;
  u_int netmask;
  // 獲取網(wǎng)卡設(shè)備指針
  if (-1 == pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf))
  {
    std::cout << "獲取網(wǎng)卡設(shè)備指針出錯(cuò)" << std::endl;
    return;
  }
  // 選取適合網(wǎng)卡
  int flag = 0;
  for (d = alldevs; d; d = d->next)
  {
    pcap_addr_t* p = d->addresses;
    while (p)
    {
      if (local_address == inet_ntoa(((sockaddr_in*)p->addr)->sin_addr))
      {
        flag = 1;
        break;
      }
      p = p->next;
    }
    if (1 == flag)
      break;
  }
  if (0 == flag)
  {
    std::cout << "請檢查本機(jī)IP地址是否正確" << std::endl;
    std::cout << local_address.c_str() << std::endl;
    return;
  }
  // 獲取子網(wǎng)掩碼
  netmask = ((sockaddr_in*)d->addresses->netmask)->sin_addr.S_un.S_addr;
  // 打開網(wǎng)卡
  m_adhandle = pcap_open(d->name, 65536, PCAP_OPENFLAG_PROMISCUOUS, 1000, NULL, errbuf);
  if (NULL == m_adhandle)
  {
    std::cout << "打開網(wǎng)卡出錯(cuò)" << std::endl;
    pcap_freealldevs(alldevs);
    return;
  }
  //檢查以太網(wǎng)
  if (DLT_EN10MB != pcap_datalink(m_adhandle))
  {
    std::cout << "此程序僅在以太網(wǎng)下工作" << std::endl;
    pcap_freealldevs(alldevs);
    return;
  }
  // 釋放網(wǎng)卡設(shè)備列表
  pcap_freealldevs(alldevs);
}

構(gòu)造數(shù)據(jù)

MAC地址轉(zhuǎn)換為Bytes字節(jié)

將MAC 地址的字符串表示形式轉(zhuǎn)換為字節(jié)數(shù)組(unsigned char 數(shù)組),函數(shù)首先創(chuàng)建了一個(gè)臨時(shí)緩沖區(qū) Tmp 來存儲輸入字符串的拷貝,然后使用 sscanf 函數(shù)將字符串中的每兩個(gè)字符解析為一個(gè)十六進(jìn)制數(shù),存儲到 Returned 數(shù)組中。最后,通過調(diào)整指針的位置,跳過已經(jīng)處理的字符,實(shí)現(xiàn)了對整個(gè)字符串的解析。

下面是這段代碼的解釋:

// MAC地址轉(zhuǎn)Bytes
unsigned char* MACStringToBytes(std::string String)
{
  // 獲取輸入字符串的長度
  int iLen = strlen(String.c_str());
  // 創(chuàng)建一個(gè)臨時(shí)緩沖區(qū),用于存儲輸入字符串的拷貝
  char* Tmp = new char[(iLen + 1)];
  // 將輸入字符串拷貝到臨時(shí)緩沖區(qū)
  strcpy(Tmp, String.c_str());
  // 創(chuàng)建一個(gè)用于存儲結(jié)果的unsigned char數(shù)組,數(shù)組大小為6
  unsigned char* Returned = new unsigned char[6];
  // 循環(huán)處理每個(gè)字節(jié)
  for (int i = 0; i < 6; i++)
  {
    // 使用sscanf將字符串中的兩個(gè)字符轉(zhuǎn)換為16進(jìn)制數(shù),存儲到Returned數(shù)組中
    sscanf(Tmp, "%2X", &Returned[i]);
    // 移動臨時(shí)緩沖區(qū)的指針,跳過已經(jīng)處理過的字符
    memmove((void*)(Tmp), (void*)(Tmp + 3), 19 - i * 3);
  }
  // 返回存儲結(jié)果的數(shù)組
  return Returned;
}

Bytes字節(jié)轉(zhuǎn)換為16進(jìn)制

將兩個(gè)字節(jié)(unsigned char 類型的 XY)組成一個(gè)16位的無符號整數(shù)。函數(shù)的目的是將兩個(gè)字節(jié)的數(shù)據(jù)合并成一個(gè)16位的整數(shù)。首先,將 X 左移8位,然后與 Y 進(jìn)行按位或操作,得到一個(gè)包含兩個(gè)字節(jié)信息的16位整數(shù)。最后,將這個(gè)16位整數(shù)返回。這種操作通常在處理網(wǎng)絡(luò)協(xié)議或二進(jìn)制數(shù)據(jù)時(shí)會經(jīng)常遇到。

下面是這段代碼的解釋:

// Bytes地址轉(zhuǎn)16進(jìn)制
unsigned short BytesTo16(unsigned char X, unsigned char Y)
{
  // 將 X 左移8位,然后與 Y 進(jìn)行按位或操作,得到一個(gè)16位的無符號整數(shù)
  unsigned short Tmp = X;
  Tmp = Tmp << 8;
  Tmp = Tmp | Y;
  return Tmp;
}

計(jì)算 IP 數(shù)據(jù)報(bào)的校驗(yàn)和

這個(gè)函數(shù)主要通過遍歷 IP 頭中的每兩個(gè)字節(jié),將它們合并為一個(gè)16位整數(shù),并逐步累加到校驗(yàn)和中。在每次累加時(shí),還需要檢查是否發(fā)生了溢出,如果溢出則需要額外加1。最后,對累加得到的校驗(yàn)和進(jìn)行取反操作,得到最終的 IP 校驗(yàn)和,并將其返回。這種校驗(yàn)和計(jì)算通常用于驗(yàn)證 IP 數(shù)據(jù)報(bào)的完整性。

下面是這段代碼的解釋:

// 計(jì)算IP校驗(yàn)和
unsigned short CalculateIPChecksum(UINT TotalLen, UINT ID, UINT SourceIP, UINT DestIP)
{
  // 初始化校驗(yàn)和
  unsigned short CheckSum = 0;
  // 遍歷 IP 頭的每兩個(gè)字節(jié)
  for (int i = 14; i < 34; i += 2)
  {
    // 將每兩個(gè)字節(jié)合并為一個(gè)16位整數(shù)
    unsigned short Tmp = BytesTo16(FinalPacket[i], FinalPacket[i + 1]);
    // 計(jì)算校驗(yàn)和
    unsigned short Difference = 65535 - CheckSum;
    CheckSum += Tmp;
    // 處理溢出
    if (Tmp > Difference) { CheckSum += 1; }
  }
  // 取反得到最終的校驗(yàn)和
  CheckSum = ~CheckSum;
  return CheckSum;
}

計(jì)算 UDP 數(shù)據(jù)報(bào)的校驗(yàn)和

這個(gè)函數(shù)主要通過構(gòu)造 UDP 數(shù)據(jù)報(bào)的偽首部,包括源 IP、目標(biāo) IP、協(xié)議類型(UDP)、UDP 長度、源端口、目標(biāo)端口以及 UDP 數(shù)據(jù)等字段,并通過遍歷偽首部的每兩個(gè)字節(jié)計(jì)算校驗(yàn)和。最后取反得到最終的 UDP 校驗(yàn)和,并將其返回。這種校驗(yàn)和計(jì)算通常用于驗(yàn)證 UDP 數(shù)據(jù)報(bào)的完整性。

下面是這段代碼的解釋:

// 計(jì)算UDP校驗(yàn)和
unsigned short CalculateUDPChecksum(unsigned char* UserData, int UserDataLen, UINT SourceIP, UINT DestIP, USHORT SourcePort, USHORT DestinationPort, UCHAR Protocol)
{
  unsigned short CheckSum = 0;
  // 計(jì)算 UDP 數(shù)據(jù)報(bào)的偽首部長度
  unsigned short PseudoLength = UserDataLen + 8 + 9; // 長度包括 UDP 頭(8字節(jié))和偽首部(9字節(jié))
  // 如果長度不是偶數(shù),添加一個(gè)額外的字節(jié)
  PseudoLength += PseudoLength % 2;
  // 創(chuàng)建 UDP 偽首部
  unsigned char* PseudoHeader = new unsigned char[PseudoLength];
  RtlZeroMemory(PseudoHeader, PseudoLength);
  // 設(shè)置偽首部中的協(xié)議字段為 UDP (0x11)
  PseudoHeader[0] = 0x11;
  // 復(fù)制源和目標(biāo) IP 地址到偽首部
  memcpy((void*)(PseudoHeader + 1), (void*)(FinalPacket + 26), 8);
  // 將 UDP 頭的長度字段拷貝到偽首部
  unsigned short Length = UserDataLen + 8;
  Length = htons(Length);
  memcpy((void*)(PseudoHeader + 9), (void*)&Length, 2);
  memcpy((void*)(PseudoHeader + 11), (void*)&Length, 2);
  // 將源端口、目標(biāo)端口和 UDP 數(shù)據(jù)拷貝到偽首部
  memcpy((void*)(PseudoHeader + 13), (void*)(FinalPacket + 34), 2);
  memcpy((void*)(PseudoHeader + 15), (void*)(FinalPacket + 36), 2);
  memcpy((void*)(PseudoHeader + 17), (void*)UserData, UserDataLen);
  // 遍歷偽首部的每兩個(gè)字節(jié),計(jì)算校驗(yàn)和
  for (int i = 0; i < PseudoLength; i += 2)
  {
    unsigned short Tmp = BytesTo16(PseudoHeader[i], PseudoHeader[i + 1]);
    unsigned short Difference = 65535 - CheckSum;
    CheckSum += Tmp;
    if (Tmp > Difference) { CheckSum += 1; }
  }
  // 取反得到最終的校驗(yàn)和
  CheckSum = ~CheckSum;
  // 釋放偽首部的內(nèi)存
  delete[] PseudoHeader;
  return CheckSum;
}

這段代碼的分析:

  • 偽首部構(gòu)造: UDP校驗(yàn)和的計(jì)算需要使用UDP頭以及偽首部(包含源IP、目標(biāo)IP、協(xié)議類型、UDP長度等信息)。這里使用PseudoHeader數(shù)組來構(gòu)造偽首部。
  • 偽首部填充: 通過memcpy等操作將源和目標(biāo)IP地址、UDP頭的長度字段以及UDP的源端口、目標(biāo)端口、UDP數(shù)據(jù)等內(nèi)容填充到偽首部中。
  • 偽首部遍歷: 通過遍歷偽首部的每兩個(gè)字節(jié),計(jì)算累加和。遍歷過程中,將兩個(gè)字節(jié)轉(zhuǎn)換為16位整數(shù)Tmp,然后進(jìn)行累加。如果累加結(jié)果大于65535,則向結(jié)果中再加1。這是為了處理累加和溢出的情況。
  • 取反: 計(jì)算完畢后,對累加和取反得到最終的UDP校驗(yàn)和。
  • 內(nèi)存釋放: 最后釋放動態(tài)分配的偽首部內(nèi)存。

需要注意的是,UDP校驗(yàn)和是一個(gè)16位的值,用于驗(yàn)證UDP數(shù)據(jù)報(bào)在傳輸過程中是否被修改。這段代碼主要完成了構(gòu)造UDP偽首部和計(jì)算校驗(yàn)和的過程。在實(shí)際網(wǎng)絡(luò)通信中,校驗(yàn)和的計(jì)算是為了保證數(shù)據(jù)的完整性,防止在傳輸過程中的錯(cuò)誤。

創(chuàng)建UDP數(shù)據(jù)包函數(shù)

創(chuàng)建一個(gè)UDP數(shù)據(jù)包,該代碼是一個(gè)簡單的網(wǎng)絡(luò)編程示例,用于創(chuàng)建和發(fā)送UDP數(shù)據(jù)包。其中,UDP數(shù)據(jù)包的內(nèi)容和頭部信息都可以根據(jù)實(shí)際需求進(jìn)行定制。

代碼的概述:

  • 打開網(wǎng)卡: 通過pcap_findalldevs_ex函數(shù)獲取本機(jī)的網(wǎng)卡設(shè)備列表,并在控制臺輸出每個(gè)網(wǎng)卡的地址列表。
  • 選擇網(wǎng)卡: 用戶輸入本機(jī)IP地址,程序通過遍歷網(wǎng)卡設(shè)備列表,找到與輸入IP地址匹配的網(wǎng)卡。
  • 打開選定的網(wǎng)卡: 使用pcap_open函數(shù)打開選擇的網(wǎng)卡,獲取到網(wǎng)卡的句柄。
  • 創(chuàng)建UDP數(shù)據(jù)包: 調(diào)用CreatePacket函數(shù)創(chuàng)建一個(gè)UDP數(shù)據(jù)包。該函數(shù)包括以下步驟:
    • 分配內(nèi)存:使用new運(yùn)算符為FinalPacket分配內(nèi)存,內(nèi)存大小為UserDataLength + 42字節(jié)。
    • 填充以太網(wǎng)頭:拷貝目標(biāo)MAC地址、源MAC地址和協(xié)議類型(IPv4)到FinalPacket的前12個(gè)字節(jié)。
    • 填充IP頭:填充IPv4頭部,包括版本、標(biāo)題長度、總長度、標(biāo)識、標(biāo)志、偏移、生存時(shí)間、協(xié)議(UDP為0x11),校驗(yàn)和、源IP和目標(biāo)IP。
    • 填充UDP頭:填充UDP頭,包括源端口、目標(biāo)端口、UDP長度(包括UDP頭和數(shù)據(jù))和校驗(yàn)和。
    • 計(jì)算IP校驗(yàn)和:調(diào)用CalculateIPChecksum函數(shù)計(jì)算IP頭的校驗(yàn)和。
    • 計(jì)算UDP校驗(yàn)和:調(diào)用CalculateUDPChecksum函數(shù)計(jì)算UDP頭的校驗(yàn)和。
    • 返回?cái)?shù)據(jù)包:生成的UDP數(shù)據(jù)包保存在FinalPacket中。
  • 釋放資源: 在程序結(jié)束時(shí),釋放分配的內(nèi)存。
void CreatePacket(unsigned char* SourceMAC, unsigned char* DestinationMAC,unsigned int SourceIP, unsigned int DestIP,unsigned short SourcePort, unsigned short DestinationPort,unsigned char* UserData, unsigned int UserDataLength)
{
  UserDataLen = UserDataLength;
  FinalPacket = new unsigned char[UserDataLength + 42]; // 為數(shù)據(jù)長度加上42字節(jié)的標(biāo)頭保留足夠的內(nèi)存
  USHORT TotalLen = UserDataLength + 20 + 8;            // IP報(bào)頭使用數(shù)據(jù)長度加上IP報(bào)頭長度(通常為20字節(jié))加上udp報(bào)頭長度(通常為8字節(jié))
  // 開始填充以太網(wǎng)包頭
  memcpy((void*)FinalPacket, (void*)DestinationMAC, 6);
  memcpy((void*)(FinalPacket + 6), (void*)SourceMAC, 6);
  USHORT TmpType = 8;
  memcpy((void*)(FinalPacket + 12), (void*)&TmpType, 2);  // 使用的協(xié)議類型(USHORT)類型0x08是UDP??梢詾槠渌麉f(xié)議(例如TCP)更改此設(shè)置
  // 開始填充IP頭數(shù)據(jù)包
  memcpy((void*)(FinalPacket + 14), (void*)"\x45", 1);     // 前3位的版本(4)和最后5位的標(biāo)題長度。
  memcpy((void*)(FinalPacket + 15), (void*)"\x00", 1);     // 通常為0
  TmpType = htons(TotalLen);
  memcpy((void*)(FinalPacket + 16), (void*)&TmpType, 2);
  TmpType = htons(0x1337);
  memcpy((void*)(FinalPacket + 18), (void*)&TmpType, 2);    // Identification
  memcpy((void*)(FinalPacket + 20), (void*)"\x00", 1);      // Flags
  memcpy((void*)(FinalPacket + 21), (void*)"\x00", 1);      // Offset
  memcpy((void*)(FinalPacket + 22), (void*)"\x80", 1);      // Time to live.
  memcpy((void*)(FinalPacket + 23), (void*)"\x11", 1);      // 協(xié)議UDP為0x11(17)TCP為6 ICMP為1等
  memcpy((void*)(FinalPacket + 24), (void*)"\x00\x00", 2);  // 計(jì)算校驗(yàn)和
  memcpy((void*)(FinalPacket + 26), (void*)&SourceIP, 4);   //inet_addr does htonl() for us
  memcpy((void*)(FinalPacket + 30), (void*)&DestIP, 4);
  // 開始填充UDP頭部數(shù)據(jù)包
  TmpType = htons(SourcePort);
  memcpy((void*)(FinalPacket + 34), (void*)&TmpType, 2);
  TmpType = htons(DestinationPort);
  memcpy((void*)(FinalPacket + 36), (void*)&TmpType, 2);
  USHORT UDPTotalLen = htons(UserDataLength + 8); // UDP Length does not include length of IP header
  memcpy((void*)(FinalPacket + 38), (void*)&UDPTotalLen, 2);
  //memcpy((void*)(FinalPacket+40),(void*)&TmpType,2); //checksum
  memcpy((void*)(FinalPacket + 42), (void*)UserData, UserDataLength);
  unsigned short UDPChecksum = CalculateUDPChecksum(UserData, UserDataLength, SourceIP, DestIP, htons(SourcePort), htons(DestinationPort), 0x11);
  memcpy((void*)(FinalPacket + 40), (void*)&UDPChecksum, 2);
  unsigned short IPChecksum = htons(CalculateIPChecksum(TotalLen, 0x1337, SourceIP, DestIP));
  memcpy((void*)(FinalPacket + 24), (void*)&IPChecksum, 2);
  return;
}

對該代碼的分析:

  • 分配內(nèi)存: 使用new運(yùn)算符為FinalPacket分配內(nèi)存,內(nèi)存大小為UserDataLength + 42字節(jié)。這足夠容納UDP數(shù)據(jù)以及以太網(wǎng)、IP和UDP頭的長度。
  • 填充以太網(wǎng)頭: 使用memcpy函數(shù)將目標(biāo)MAC地址、源MAC地址和協(xié)議類型(這里是IPv4)拷貝到FinalPacket的前12個(gè)字節(jié)。
  • 填充IP頭: 在FinalPacket的第14個(gè)字節(jié)開始,填充IPv4頭部。這包括版本、標(biāo)題長度、總長度、標(biāo)識、標(biāo)志、偏移、生存時(shí)間、協(xié)議(UDP為0x11),校驗(yàn)和、源IP和目標(biāo)IP。
  • 填充UDP頭: 在FinalPacket的第34個(gè)字節(jié)開始,填充UDP頭。這包括源端口、目標(biāo)端口、UDP長度(包括UDP頭和數(shù)據(jù))和校驗(yàn)和。其中,UDP校驗(yàn)和的計(jì)算通過調(diào)用CalculateUDPChecksum函數(shù)完成。
  • 計(jì)算IP校驗(yàn)和: 在填充IP頭后,調(diào)用CalculateIPChecksum函數(shù)計(jì)算IP頭的校驗(yàn)和。這個(gè)校驗(yàn)和是IPv4頭的一個(gè)字段。
  • 返回?cái)?shù)據(jù)包: 函數(shù)執(zhí)行完畢后,生成的UDP數(shù)據(jù)包保存在FinalPacket中,可以將其用于發(fā)送到網(wǎng)絡(luò)。

需要注意的是,這段代碼中的硬編碼可能需要根據(jù)實(shí)際需求進(jìn)行修改,例如協(xié)議類型、標(biāo)識、生存時(shí)間等。此外,計(jì)算校驗(yàn)和是網(wǎng)絡(luò)協(xié)議中用于檢測數(shù)據(jù)完整性的一種機(jī)制。

發(fā)送UDP數(shù)據(jù)包

代碼演示了如何打開網(wǎng)卡,生成UDP數(shù)據(jù)包,并通過pcap_sendpacket函數(shù)發(fā)送數(shù)據(jù)包到網(wǎng)絡(luò)。需要注意的是,數(shù)據(jù)包的內(nèi)容和地址是硬編碼的,實(shí)際應(yīng)用中可能需要根據(jù)需要進(jìn)行更改。

int main(int argc, char* argv[])
{
	// 打開網(wǎng)卡
	OpenAdapter("10.0.66.24");
	// 填充地址并生成數(shù)據(jù)包包頭
	char SourceMAC[MAX_PATH] = "8C-ff-ff-ff-ff-ff";
	char SourceIP[MAX_PATH] = "192.168.93.11";
	char SourcePort[MAX_PATH] = "80";
	char DestinationMAC[MAX_PATH] = "8C-dd-dd-dd-dd-dd";
	char DestinationIP[MAX_PATH] = "192.168.93.11";
	char DestinationPort[MAX_PATH] = "8080";
	char DataString[MAX_PATH] = "hello lyshark";
	CreatePacket(MACStringToBytes(SourceMAC), MACStringToBytes(DestinationMAC), inet_addr(SourceIP), inet_addr(DestinationIP), atoi(SourcePort), atoi(DestinationPort), (UCHAR*)DataString, (strlen(DataString) + 1));
	// 循環(huán)發(fā)包
	for (int x = 0; x < 10; x++)
	{
		if (0 != pcap_sendpacket(m_adhandle, FinalPacket, (UserDataLen + 42)))
		{
			char* szErr = pcap_geterr(m_adhandle);
			return 0;
		}
	}
	system("pause");
	return 0;
}

打開wireshark抓包工具,過濾目標(biāo)地址為ip.dst==192.168.93.11然后抓包,運(yùn)行編譯后的程序,則你會看到我們自己構(gòu)建的數(shù)據(jù)包被發(fā)送了10次,如下圖所示;

隨便打開一個(gè)數(shù)據(jù)包看下結(jié)構(gòu),源地址目標(biāo)地址均是偽造的地址,數(shù)據(jù)包中的內(nèi)容是hello lyshark,如下圖所示;

到此這篇關(guān)于C/C++ 運(yùn)用Npcap發(fā)送UDP數(shù)據(jù)包的文章就介紹到這了,更多相關(guān)C++ 發(fā)送UDP數(shù)據(jù)包內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C語言循環(huán)鏈表的原理與使用操作

    C語言循環(huán)鏈表的原理與使用操作

    無論是靜態(tài)鏈表還是動態(tài)鏈表,有時(shí)在解決具體問題時(shí),需要我們對其結(jié)構(gòu)進(jìn)行稍微地調(diào)整。比如,可以把鏈表的兩頭連接,使其成為了一個(gè)環(huán)狀鏈表,通常稱為循環(huán)鏈表
    2022-05-05
  • 詳解Dev C++使用教程(使用Dev C++編寫C語言程序)

    詳解Dev C++使用教程(使用Dev C++編寫C語言程序)

    這篇文章主要介紹了詳解Dev C++使用教程(使用Dev C++編寫C語言程序),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • C++簡單QQ程序服務(wù)器端的實(shí)現(xiàn)代碼

    C++簡單QQ程序服務(wù)器端的實(shí)現(xiàn)代碼

    這篇文章主要為大家詳細(xì)介紹了C++簡單QQ程序服務(wù)器端的實(shí)現(xiàn)代碼,感興趣的朋友可以參考一下
    2016-05-05
  • C/C++中使用局部/全局變量初始值或默認(rèn)值問題

    C/C++中使用局部/全局變量初始值或默認(rèn)值問題

    這篇文章主要介紹了C/C++中使用局部/全局變量初始值或默認(rèn)值問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • C++面試八股文之如何實(shí)現(xiàn)strncpy函數(shù)

    C++面試八股文之如何實(shí)現(xiàn)strncpy函數(shù)

    strncpy函數(shù),主要用做字符串復(fù)制,將于字符從一個(gè)位置復(fù)制到另一個(gè)位置,那么如何實(shí)現(xiàn)一個(gè)strncpy函數(shù),下面小編就來和大家簡單講講吧
    2023-07-07
  • 判斷本機(jī)office安裝版本的方法分享

    判斷本機(jī)office安裝版本的方法分享

    這篇文章主要介紹了判斷本機(jī)office安裝版本的方法分享,需要的朋友可以參考下
    2014-01-01
  • C++中類的三種訪問權(quán)限解析:private、public與protect

    C++中類的三種訪問權(quán)限解析:private、public與protect

    這篇文章主要介紹了C++中類的三種訪問權(quán)限解析:private、public與protect,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-11-11
  • C++實(shí)現(xiàn)商品管理程序

    C++實(shí)現(xiàn)商品管理程序

    這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)商品管理程序,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-12-12
  • 解讀C語言非void函數(shù)卻沒有return會怎么樣

    解讀C語言非void函數(shù)卻沒有return會怎么樣

    這篇文章主要介紹了解讀C語言非void函數(shù)卻沒有return會怎么樣的問題,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-02-02
  • 詳解C語言內(nèi)核字符串拷貝與比較

    詳解C語言內(nèi)核字符串拷貝與比較

    本文將探索一下字符串的拷貝與比較,與應(yīng)用層不同內(nèi)核字符串拷貝與比較也需要使用內(nèi)核專用的API函數(shù),字符串的拷貝往往伴隨有內(nèi)核內(nèi)存分配,我們將首先簡單介紹內(nèi)核如何分配堆空間,然后再以此為契機(jī)簡介字符串的拷貝與比較
    2022-09-09

最新評論