C/C++ 運用Npcap發送UDP數據包

来源:https://www.cnblogs.com/LyShark/archive/2023/11/25/17855464.html
-Advertisement-
Play Games

Npcap 是一個功能強大的開源網路抓包庫,它是 WinPcap 的一個分支,並提供了一些增強和改進。特別適用於在 Windows 環境下進行網路流量捕獲和分析。除了支持通常的網路抓包功能外,Npcap 還提供了對數據包的拼合與構造,使其成為實現 UDP 數據包發包的理想選擇。本章將通過Npcap庫... ...


Npcap 是一個功能強大的開源網路抓包庫,它是 WinPcap 的一個分支,並提供了一些增強和改進。特別適用於在 Windows 環境下進行網路流量捕獲和分析。除了支持通常的網路抓包功能外,Npcap 還提供了對數據包的拼合與構造,使其成為實現 UDP 數據包發包的理想選擇。本章將通過Npcap庫構造一個UDP原始數據包,並實現對特定主機的發包功能,通過本章的學習讀者可以掌握如何使用Npcap庫偽造特定的數據包格式。

Npcap的主要特點和概述:

  1. 原始套接字支持: Npcap 允許用戶通過原始套接字在網路層捕獲和發送數據包。這使得用戶能夠進行更底層的網路活動監控和分析。
  2. WinPcap 的增強版本: Npcap 是 WinPcap 的一個分支,對其進行了一些增強和改進。這些改進包括對新版本 Windows 的支持、更好的性能和穩定性,以及一些額外的功能。
  3. 支持 Windows 10: Npcap 被設計用於支持 Windows 10 操作系統。它允許用戶在最新的 Windows 平臺上進行網路抓包和分析。
  4. Loopback 模式: Npcap 允許在 Loopback 介面上進行抓包,使用戶能夠監視本地主機上的網路流量。
  5. 多種應用場景: Npcap 被廣泛應用於網路安全、網路管理、網路調試等各種場景。它為開發人員、網路管理員和安全專家提供了一個功能強大的工具,用於分析和理解網路通信。
  6. 開源: Npcap 是開源項目,其源代碼可以在 GitHub 上獲得。這使得用戶可以自由查看、修改和定製代碼,以滿足特定需求。

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

UDP 數據包結構: UDP 數據包由報頭和數據兩部分組成。

  1. 報頭(Header):
    • 源埠號(16 位): 指定發送埠。
    • 目標埠號(16 位): 指定接收埠。
    • 長度(16 位): 報頭和數據的總長度,以位元組為單位。
    • 校驗和(16 位): 用於驗證數據在傳輸過程中的完整性。
  2. 數據(Payload):
    • 實際傳輸的數據,長度可變。

UDP 的特點:

  1. 面向無連接: UDP 是一種無連接協議,通信雙方不需要在傳輸數據之前建立連接。這使得它的開銷較低,適用於一些實時性要求較高的應用。
  2. 不可靠性: UDP 不提供數據的可靠性保證,不保證數據包的到達、順序和完整性。因此,它更適合那些能夠容忍一些數據丟失的場景,如音視頻傳輸。
  3. 適用於廣播和多播: UDP 支持廣播和多播通信,可以通過一個發送操作同時向多個目標發送數據。
  4. 低開銷: 由於缺乏連接建立和維護的開銷,以及不提供可靠性保證的特性,UDP 具有較低的開銷,適用於對實時性要求較高的應用。
  5. 適用於短消息: 由於不需要建立連接,UDP 適合傳輸短消息,尤其是對實時性要求高的應用。

UDP 的應用場景:

  1. 實時性要求高的應用: 如實時音視頻傳輸、線上游戲等。
  2. 簡單的請求-響應通信: 適用於一些簡單的請求-響應場景,如 DNS 查詢。
  3. 廣播和多播應用: UDP 的支持廣播和多播特性使其適用於這類通信模式。
  4. 實時數據採集: 例如感測器數據採集等場景。

輸出網卡

使用 WinPcap(Windows Packet Capture)庫列舉系統上的網路介面以及它們的 IP 地址。WinPcap 是一個用於 Windows 操作系統的網路數據包捕獲庫,可以用於網路數據包的捕獲和分析。

代碼主要做了以下幾個事情:

  1. 使用 pcap_findalldevs_ex 函數查找系統上的所有網路介面。
  2. 遍歷每個網路介面,獲取其 IP 地址,並將地址列表列印出來。

pcap_findalldevs_ex 用於查找系統上所有網路介面的函數。它的原型如下:

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

函數參數說明:

  • source:一個字元串,用於指定網路介面的來源。可以為 NULL,表示從系統獲取網路介面信息。也可以指定為一個網路地址,用於遠程捕獲。
  • auth:一個 pcap_rmtauth 結構的指針,用於指定遠程捕獲的認證信息。一般情況下可以為 NULL
  • alldevs:一個 pcap_if_t 類型的指針的地址,用於保存查找到的網路介面鏈表的頭指針。
  • errbuf:一個字元數組,用於保存錯誤信息。

函數返回值:

  • 成功時返回 0。
  • 失敗時返回 -1,錯誤信息保存在 errbuf 中。

函數功能:

pcap_findalldevs_ex 主要用於查找系統上的網路介面信息。當調用成功後,alldevs 將指向一個鏈表,鏈表中的每個節點都包含一個網路介面的信息。這個鏈表的頭指針是 alldevs

pcap_freealldevs 用於釋放 pcap_findalldevs_ex 函數分配的資源的函數。其原型如下:

void pcap_freealldevs(pcap_if_t *alldevs);

函數參數說明:

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

函數功能:

pcap_freealldevs 主要用於釋放 pcap_findalldevs_ex 函數返回的鏈表中分配的資源,包括每個節點和節點中保存的介面信息。

輸出當前系統中活動網卡信息,可以這樣來寫,如下代碼所示;

#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")

// 打開網卡返回的指針
pcap_t* m_adhandle;
unsigned char* FinalPacket;
unsigned int UserDataLen;

int main(int argc, char *argv[])
{
	// 打開網卡
	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;
	}

	// 遍歷網卡
	char* lpszIP = NULL;
	d = alldevs;
	while (NULL != d)
	{
		// 遍歷網卡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;
}

輸出效果如下圖所示;

打開網卡

打開網路適配器的函數,通過傳入本機的IP地址,該函數會查找與該IP地址匹配的網路適配器並打開。以下是對該函數的簡要分析:

查找網卡設備指針:

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

使用 pcap_findalldevs_ex 函數來獲取本機所有網卡設備的鏈表。如果返回值為 -1,說明發生了錯誤,這時函數會輸出錯誤信息並直接返回。

選取適合網卡:

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

通過遍歷網卡設備鏈表,查找與傳入的本機IP地址匹配的網卡。首先,通過檢查每個網卡的地址列表,找到第一個匹配的網卡。如果找到了,將 flag 標記設為1,然後跳出迴圈。如果未找到匹配的網卡,輸出錯誤信息並返回。

獲取子網掩碼:

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

獲取匹配網卡的子網掩碼。

打開網卡:

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

使用 pcap_open 函數打開選擇的網卡,該函數的聲明如下:

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

這裡是對參數的簡要解釋:

  • source: 要打開的網路適配器的名稱,例如 "eth0"。

  • snaplen: 指定捕獲數據包時每個數據包的最大長度。如果數據包超過這個長度,它將被截斷。通常設置為數據包的最大可能長度。

  • flags
    

    : 控制捕獲的方式,可以使用位掩碼進行組合。常見的標誌包括:

    • PCAP_OPENFLAG_PROMISCUOUS: 開啟混雜模式,允許捕獲所有經過網卡的數據包。
    • PCAP_OPENFLAG_MAX_RESPONSIVENESS: 最大響應性標誌,可能在某些平臺上影響性能。
  • read_timeout: 設置超時值,以毫秒為單位。如果設置為0,表示無限期等待數據包。

  • auth: 可以指定用於遠程捕獲的身份驗證信息,通常為 NULL

  • errbuf: 用於存儲錯誤信息的緩衝區,如果函數執行失敗,會將錯誤信息寫入這個緩衝區。

函數返回一個 pcap_t 類型的指針,它是一個表示打開的網路適配器的結構。如果打開失敗,返回 NULL

檢查乙太網:

if (DLT_EN10MB != pcap_datalink(m_adhandle))

pcap_datalink 函數是 PCAP 庫中用於獲取網路適配器數據鏈路類型(datalink type)的函數,確保是乙太網,如果不是乙太網,輸出錯誤信息並返回。

該函數的聲明如下:

int pcap_datalink(pcap_t *p);

這裡是對參數的簡要解釋:

  • p: 表示一個已經打開的網路適配器的 pcap_t 結構指針。

函數返回一個整數,表示數據鏈路類型。這個值通常是預定義的常量之一,用於標識不同類型的網路數據鏈路。

常見的一些數據鏈路類型常量包括:

  • DLT_EN10MB(Ethernet): 表示乙太網數據鏈路。
  • DLT_IEEE802(802.5 Token Ring): 表示 IEEE 802.5 Token Ring 數據鏈路。
  • DLT_PPP(Point-to-Point Protocol): 表示點對點協議數據鏈路。
  • DLT_ARCNET(ARCNET): 表示 ARCNET 數據鏈路。

釋放網卡設備列表:

pcap_freealldevs(alldevs);

最後,釋放 pcap_findalldevs_ex 函數返回的網卡設備列表,避免記憶體泄漏。

該函數的其他全局變數 m_adhandleFinalPacketUserDataLen 已經在文章開頭聲明和定義。

// 通過傳入本機IP地址打開網卡
void OpenAdapter(std::string local_address)
{
  pcap_if_t* alldevs = NULL, * d = NULL;
  char errbuf[256] = { 0 };
  bpf_program fcode;
  u_int netmask;

  // 獲取網卡設備指針
  if (-1 == pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf))
  {
    std::cout << "獲取網卡設備指針出錯" << std::endl;
    return;
  }

  // 選取適合網卡
  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 << "請檢查本機IP地址是否正確" << std::endl;
    std::cout << local_address.c_str() << std::endl;
    return;
  }

  // 獲取子網掩碼
  netmask = ((sockaddr_in*)d->addresses->netmask)->sin_addr.S_un.S_addr;

  // 打開網卡
  m_adhandle = pcap_open(d->name, 65536, PCAP_OPENFLAG_PROMISCUOUS, 1000, NULL, errbuf);
  if (NULL == m_adhandle)
  {
    std::cout << "打開網卡出錯" << std::endl;
    pcap_freealldevs(alldevs);
    return;
  }

  //檢查乙太網
  if (DLT_EN10MB != pcap_datalink(m_adhandle))
  {
    std::cout << "此程式僅在乙太網下工作" << std::endl;
    pcap_freealldevs(alldevs);
    return;
  }

  // 釋放網卡設備列表
  pcap_freealldevs(alldevs);
}

構造數據

MAC地址轉換為Bytes位元組

將MAC 地址的字元串表示形式轉換為位元組數組(unsigned char 數組),函數首先創建了一個臨時緩衝區 Tmp 來存儲輸入字元串的拷貝,然後使用 sscanf 函數將字元串中的每兩個字元解析為一個十六進位數,存儲到 Returned 數組中。最後,通過調整指針的位置,跳過已經處理的字元,實現了對整個字元串的解析。

下麵是這段代碼的解釋:

// MAC地址轉Bytes
unsigned char* MACStringToBytes(std::string String)
{
  // 獲取輸入字元串的長度
  int iLen = strlen(String.c_str());

  // 創建一個臨時緩衝區,用於存儲輸入字元串的拷貝
  char* Tmp = new char[(iLen + 1)];

  // 將輸入字元串拷貝到臨時緩衝區
  strcpy(Tmp, String.c_str());

  // 創建一個用於存儲結果的unsigned char數組,數組大小為6
  unsigned char* Returned = new unsigned char[6];

  // 迴圈處理每個位元組
  for (int i = 0; i < 6; i++)
  {
    // 使用sscanf將字元串中的兩個字元轉換為16進位數,存儲到Returned數組中
    sscanf(Tmp, "%2X", &Returned[i]);

    // 移動臨時緩衝區的指針,跳過已經處理過的字元
    memmove((void*)(Tmp), (void*)(Tmp + 3), 19 - i * 3);
  }

  // 返回存儲結果的數組
  return Returned;
}

Bytes位元組轉換為16進位

將兩個位元組(unsigned char 類型的 XY)組成一個16位的無符號整數。函數的目的是將兩個位元組的數據合併成一個16位的整數。首先,將 X 左移8位,然後與 Y 進行按位或操作,得到一個包含兩個位元組信息的16位整數。最後,將這個16位整數返回。這種操作通常在處理網路協議或二進位數據時會經常遇到。

下麵是這段代碼的解釋:

// Bytes地址轉16進位
unsigned short BytesTo16(unsigned char X, unsigned char Y)
{
  // 將 X 左移8位,然後與 Y 進行按位或操作,得到一個16位的無符號整數
  unsigned short Tmp = X;
  Tmp = Tmp << 8;
  Tmp = Tmp | Y;
  return Tmp;
}

計算 IP 數據報的校驗和

這個函數主要通過遍歷 IP 頭中的每兩個位元組,將它們合併為一個16位整數,並逐步累加到校驗和中。在每次累加時,還需要檢查是否發生了溢出,如果溢出則需要額外加1。最後,對累加得到的校驗和進行取反操作,得到最終的 IP 校驗和,並將其返回。這種校驗和計算通常用於驗證 IP 數據報的完整性。

下麵是這段代碼的解釋:

// 計算IP校驗和
unsigned short CalculateIPChecksum(UINT TotalLen, UINT ID, UINT SourceIP, UINT DestIP)
{
  // 初始化校驗和
  unsigned short CheckSum = 0;

  // 遍歷 IP 頭的每兩個位元組
  for (int i = 14; i < 34; i += 2)
  {
    // 將每兩個位元組合併為一個16位整數
    unsigned short Tmp = BytesTo16(FinalPacket[i], FinalPacket[i + 1]);
    
    // 計算校驗和
    unsigned short Difference = 65535 - CheckSum;
    CheckSum += Tmp;
    
    // 處理溢出
    if (Tmp > Difference) { CheckSum += 1; }
  }

  // 取反得到最終的校驗和
  CheckSum = ~CheckSum;

  return CheckSum;
}

計算 UDP 數據報的校驗和

這個函數主要通過構造 UDP 數據報的偽首部,包括源 IP、目標 IP、協議類型(UDP)、UDP 長度、源埠、目標埠以及 UDP 數據等欄位,並通過遍歷偽首部的每兩個位元組計算校驗和。最後取反得到最終的 UDP 校驗和,並將其返回。這種校驗和計算通常用於驗證 UDP 數據報的完整性。

下麵是這段代碼的解釋:

// 計算UDP校驗和
unsigned short CalculateUDPChecksum(unsigned char* UserData, int UserDataLen, UINT SourceIP, UINT DestIP, USHORT SourcePort, USHORT DestinationPort, UCHAR Protocol)
{
  unsigned short CheckSum = 0;

  // 計算 UDP 數據報的偽首部長度
  unsigned short PseudoLength = UserDataLen + 8 + 9; // 長度包括 UDP 頭(8位元組)和偽首部(9位元組)

  // 如果長度不是偶數,添加一個額外的位元組
  PseudoLength += PseudoLength % 2;

  // 創建 UDP 偽首部
  unsigned char* PseudoHeader = new unsigned char[PseudoLength];
  RtlZeroMemory(PseudoHeader, PseudoLength);

  // 設置偽首部中的協議欄位為 UDP (0x11)
  PseudoHeader[0] = 0x11;

  // 複製源和目標 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);

  // 將源埠、目標埠和 UDP 數據拷貝到偽首部
  memcpy((void*)(PseudoHeader + 13), (void*)(FinalPacket + 34), 2);
  memcpy((void*)(PseudoHeader + 15), (void*)(FinalPacket + 36), 2);
  memcpy((void*)(PseudoHeader + 17), (void*)UserData, UserDataLen);

  // 遍歷偽首部的每兩個位元組,計算校驗和
  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; }
  }

  // 取反得到最終的校驗和
  CheckSum = ~CheckSum;

  // 釋放偽首部的記憶體
  delete[] PseudoHeader;

  return CheckSum;
}

這段代碼的分析:

  1. 偽首部構造: UDP校驗和的計算需要使用UDP頭以及偽首部(包含源IP、目標IP、協議類型、UDP長度等信息)。這裡使用PseudoHeader數組來構造偽首部。
  2. 偽首部填充: 通過memcpy等操作將源和目標IP地址、UDP頭的長度欄位以及UDP的源埠、目標埠、UDP數據等內容填充到偽首部中。
  3. 偽首部遍歷: 通過遍歷偽首部的每兩個位元組,計算累加和。遍歷過程中,將兩個位元組轉換為16位整數Tmp,然後進行累加。如果累加結果大於65535,則向結果中再加1。這是為了處理累加和溢出的情況。
  4. 取反: 計算完畢後,對累加和取反得到最終的UDP校驗和。
  5. 記憶體釋放: 最後釋放動態分配的偽首部記憶體。

需要註意的是,UDP校驗和是一個16位的值,用於驗證UDP數據報在傳輸過程中是否被修改。這段代碼主要完成了構造UDP偽首部和計算校驗和的過程。在實際網路通信中,校驗和的計算是為了保證數據的完整性,防止在傳輸過程中的錯誤。

創建UDP數據包函數

創建一個UDP數據包,該代碼是一個簡單的網路編程示例,用於創建和發送UDP數據包。其中,UDP數據包的內容和頭部信息都可以根據實際需求進行定製。

代碼的概述:

  1. 打開網卡: 通過pcap_findalldevs_ex函數獲取本機的網卡設備列表,併在控制台輸出每個網卡的地址列表。
  2. 選擇網卡: 用戶輸入本機IP地址,程式通過遍歷網卡設備列表,找到與輸入IP地址匹配的網卡。
  3. 打開選定的網卡: 使用pcap_open函數打開選擇的網卡,獲取到網卡的句柄。
  4. 創建UDP數據包: 調用CreatePacket函數創建一個UDP數據包。該函數包括以下步驟:
    • 分配記憶體:使用new運算符為FinalPacket分配記憶體,記憶體大小為UserDataLength + 42位元組。
    • 填充乙太網頭:拷貝目標MAC地址、源MAC地址和協議類型(IPv4)到FinalPacket的前12個位元組。
    • 填充IP頭:填充IPv4頭部,包括版本、標題長度、總長度、標識、標誌、偏移、生存時間、協議(UDP為0x11),校驗和、源IP和目標IP。
    • 填充UDP頭:填充UDP頭,包括源埠、目標埠、UDP長度(包括UDP頭和數據)和校驗和。
    • 計算IP校驗和:調用CalculateIPChecksum函數計算IP頭的校驗和。
    • 計算UDP校驗和:調用CalculateUDPChecksum函數計算UDP頭的校驗和。
    • 返回數據包:生成的UDP數據包保存在FinalPacket中。
  5. 釋放資源: 在程式結束時,釋放分配的記憶體。
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]; // 為數據長度加上42位元組的標頭保留足夠的記憶體
  USHORT TotalLen = UserDataLength + 20 + 8;            // IP報頭使用數據長度加上IP報頭長度(通常為20位元組)加上udp報頭長度(通常為8位元組)

  // 開始填充乙太網包頭
  memcpy((void*)FinalPacket, (void*)DestinationMAC, 6);
  memcpy((void*)(FinalPacket + 6), (void*)SourceMAC, 6);
  
  USHORT TmpType = 8;
  memcpy((void*)(FinalPacket + 12), (void*)&TmpType, 2);  // 使用的協議類型(USHORT)類型0x08是UDP。可以為其他協議(例如TCP)更改此設置
  
  // 開始填充IP頭數據包
  memcpy((void*)(FinalPacket + 14), (void*)"\x45", 1);     // 前3位的版本(4)和最後5位的標題長度。
  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);      // 協議UDP為0x11(17)TCP為6 ICMP為1等
  memcpy((void*)(FinalPacket + 24), (void*)"\x00\x00", 2);  // 計算校驗和
  memcpy((void*)(FinalPacket + 26), (void*)&SourceIP, 4);   //inet_addr does htonl() for us
  memcpy((void*)(FinalPacket + 30), (void*)&DestIP, 4);
  
  // 開始填充UDP頭部數據包
  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;
}

對該代碼的分析:

  1. 分配記憶體: 使用new運算符為FinalPacket分配記憶體,記憶體大小為UserDataLength + 42位元組。這足夠容納UDP數據以及乙太網、IP和UDP頭的長度。
  2. 填充乙太網頭: 使用memcpy函數將目標MAC地址、源MAC地址和協議類型(這裡是IPv4)拷貝到FinalPacket的前12個位元組。
  3. 填充IP頭:FinalPacket的第14個位元組開始,填充IPv4頭部。這包括版本、標題長度、總長度、標識、標誌、偏移、生存時間、協議(UDP為0x11),校驗和、源IP和目標IP。
  4. 填充UDP頭:FinalPacket的第34個位元組開始,填充UDP頭。這包括源埠、目標埠、UDP長度(包括UDP頭和數據)和校驗和。其中,UDP校驗和的計算通過調用CalculateUDPChecksum函數完成。
  5. 計算IP校驗和: 在填充IP頭後,調用CalculateIPChecksum函數計算IP頭的校驗和。這個校驗和是IPv4頭的一個欄位。
  6. 返回數據包: 函數執行完畢後,生成的UDP數據包保存在FinalPacket中,可以將其用於發送到網路。

需要註意的是,這段代碼中的硬編碼可能需要根據實際需求進行修改,例如協議類型、標識、生存時間等。此外,計算校驗和是網路協議中用於檢測數據完整性的一種機制。

發送UDP數據包

代碼演示瞭如何打開網卡,生成UDP數據包,並通過pcap_sendpacket函數發送數據包到網路。需要註意的是,數據包的內容和地址是硬編碼的,實際應用中可能需要根據需要進行更改。

int main(int argc, char* argv[])
{
	// 打開網卡
	OpenAdapter("10.0.66.24");

	// 填充地址並生成數據包包頭
	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));

	// 迴圈發包
	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抓包工具,過濾目標地址為ip.dst==192.168.93.11然後抓包,運行編譯後的程式,則你會看到我們自己構建的數據包被髮送了10次,如下圖所示;

隨便打開一個數據包看下結構,源地址目標地址均是偽造的地址,數據包中的內容是hello lyshark,如下圖所示;

文章出處:https://www.cnblogs.com/LyShark/p/17855464.html
本博客所有文章除特別聲明外,均採用 BY-NC-SA 許可協議。轉載請註明出處!
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • SQLite,作為一款嵌入式關係型資料庫管理系統,一直以其輕量級、零配置以及跨平臺等特性而備受青睞。不同於傳統的資料庫系統,SQLite是一個庫,直接與應用程式一同編譯和鏈接,無需單獨的資料庫伺服器進程,實現了資料庫的零配置管理。這種設計理念使得SQLite成為許多嵌入式系統、移動應用和小型項目中的... ...
  • 操作系統 :CentOS 7.6_x64 FreeSWITCH版本 :1.10.9 python版本:3.9.12 libpcap版本:1.11.0b7 FreeSWITCH的ESL模塊用起來很方便,可以控制FreeSWITCH實現具體業務需求,但該模塊沒有提供ESL命令執行日誌,不便於排查問題,本 ...
  • 在本期文章中,我們深入探討了Python的對象繼承、組合以及多態這三個核心概念。從繼承的靈活性,如Python的多重繼承和super關鍵字的使用,到組合中的動態屬性添加,我們逐一解析了Python與Java在這些方面的相似之處和差異。通過具體的例子,我們展示了Python中多態的直觀表現,強調了它與... ...
  • API成批分配漏洞介紹API 特定:可利用性 2 利用通常需要瞭解業務邏輯、對象關係和 API 結構。 在 API 中利用批量分配更容易,因為按照設計,它們公開了應用程式的底層實現以及屬性名稱。安全弱點: 現代框架鼓勵開發人員使用自動將客戶端輸入綁定到代碼變數和內部對象的函數。 攻擊者可以使用這種方 ...
  • 十七、C++字元串(二) 1、字元串的應用 需求:設計一個程式,用戶輸入屬性id或者pass或者role可以把對應的內容顯示出來,給定字元串如下: string str{"id=user;pass=632105;role=郝英俊;"}; //設計一個程式,用戶輸入屬性id或者pass或者role可以 ...
  • 多線程編程之——終止(打斷)正在執行中的線程 ps:文字有點多,想看結果的,直接跳轉:《二》 一、基礎知識 1、我們基於spring開發,把線程都交給spring 把線程交給spring管理好不好? 將線程交給Spring管理是一個常見的做法,特別是在基於Spring的應用程式中。通過將線程納入Sp ...
  • 最近買了本C++20的書,想要自己配置下在VScode的環境 例子代碼: #include <iostream> #include <format> int main() { std::cout << std::format("Hello, world! {0}", 123) << std::end ...
  • 秋招接近尾聲,各大公司基本也陸續開獎了。這裡整理了部分公司的薪資情況,數據來源於 OfferShow 和牛客網。 ps:爆料薪資的幾乎都是 211 和 985 的,並不是刻意只選取學校好的。另外,無法保證數據的嚴格準確性。 淘天 城市:杭州 崗位:演算法 學歷:碩士 985 薪資: 32*16 備註: ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...