以太網協議
作業原理
以太網協議是一種局域網通信協議,它通過物理層和資料鏈路層的協同作業,使用媒體訪問控制地址和載波監聽/沖突檢測協議來實作計算機之間的穩定資料傳輸,在資料傳輸程序中,以太網會將資料封裝成資料幀,并根據目標MAC地址來識別需要接收資料的計算機,通過這種方式,以太網協議能夠保證資料的準確性和完整性,并實作計算機之間的通信與資料傳輸,主要涉及到物理層和資料鏈路層:
物理層:以太網使用雙絞線或同軸電纜等介質進行資料傳輸,發送端將資料轉換為位元流,并通過物理層將位元流轉換為電信號并發送到傳輸介質中,接收端則將電信號重新轉換成位元流,以此來實作物理層資料傳輸,
資料鏈路層:以太網使用MAC(媒體訪問控制)地址識別不同計算機,當計算機發送資料時,會將目標MAC地址、源MAC地址、以及資料傳輸型別等資訊封裝成資料包,并通過物理層發送到介質中,在接收端,資料包被逐層決議,根據MAC地址來識別資料包是否為自己所需的資料,以此來實作資料鏈路層的資料傳輸,
資料結構

幀前導碼:在每一幀資料的開頭,都有7個位元組的前導碼,用來供接收方同步資料傳輸時鐘,
目的MAC地址:6個位元組的MAC地址,指示資料包要發送到的目標設備的物理地址,
源MAC地址:6個位元組的MAC地址,指示資料包發送者的物理地址,
型別/長度 :2個位元組,在IEEE 802.3中可以表示兩種型別的值,當值小于等于0x05DC時,表示資料包的長度,當值大于0x05DC時,表示此幀所包含的協議型別,例如,0x0800表示IPv4協議,0x86DD表示IPv6協議,
資料(Data):46~1500位元組之間的變長欄位,包括上層協議的頭部和資料,假如資料長度小于46位元組,以太網協議會自動在尾部進行填充,使其達到最小長度,
幀校驗碼FCS:幀校驗碼是由以太網接收器計算出來的,并與幀的其他部分一起傳輸,它用來檢查接收到的幀資料是否正確,如果不正確則會丟棄,
QT(C語言)分析
QT的安裝配置,以及專案的新建這里就不詳細說了,可以參考其他博主的步驟,
環境配置:需要去pro檔案里面添加一個系統庫:unix|win32: LIBS += -lpcap,

運行結果:

完整代碼:
#include <stdio.h>
#include <stdlib.h>
#include <pcap.h> //需要安裝libpcap庫
// 以太網頭部結構體
struct ether_header {
u_int8_t ether_dhost[6]; // 目標MAC地址
u_int8_t ether_shost[6]; // 源MAC地址
u_int16_t ether_type; // 以太網型別(IP、ARP等)
};
int main(int argc, char* argv[]) {
char errbuf[PCAP_ERRBUF_SIZE];
pcap_t* handle; // pcap會話句柄
struct bpf_program filter; // 過濾器規則
char filter_exp[] = "ether proto 0x0800"; // 只捕獲IP協議的資料包
bpf_u_int32 mask; /* 子網掩碼 */
bpf_u_int32 net; /* 網路地址 */
struct pcap_pkthdr header; // 資料包頭部資訊
const u_char* packet; // 實際的資料包內容
struct ether_header* ethhdr; // 以太網頭部指標
// 打開默認網卡
handle = pcap_open_live("ens33", BUFSIZ, 1, 1000, errbuf);
if (handle == NULL) {
fprintf(stderr, "Could not open device %s: %s\n", "ens33", errbuf);
return EXIT_FAILURE;
}
// 編譯過濾器規則
if (pcap_compile(handle, &filter, filter_exp, 0, net) == -1) {
fprintf(stderr, "Could not parse filter %s: %s\n", filter_exp, pcap_geterr(handle));
pcap_close(handle);
return EXIT_FAILURE;
}
// 設定過濾器規則
if (pcap_setfilter(handle, &filter) == -1) {
fprintf(stderr, "Could not install filter %s: %s\n", filter_exp, pcap_geterr(handle));
pcap_freecode(&filter);
pcap_close(handle);
return EXIT_FAILURE;
}
// 持續讀取資料包并進行決議
while (1) {
packet = pcap_next(handle, &header); // 讀取下一個資料包
ethhdr = (struct ether_header*)packet; // 轉換為以太網頭部指標
// 決議以太網協議
printf("Source MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n",
ethhdr->ether_shost[0], ethhdr->ether_shost[1],
ethhdr->ether_shost[2], ethhdr->ether_shost[3],
ethhdr->ether_shost[4], ethhdr->ether_shost[5]);
printf("Destination MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n",
ethhdr->ether_dhost[0], ethhdr->ether_dhost[1],
ethhdr->ether_dhost[2], ethhdr->ether_dhost[3],
ethhdr->ether_dhost[4], ethhdr->ether_dhost[5]);
printf("Ethernet type: %d\n", ethhdr->ether_type);
}
// 關閉pcap會話
pcap_freecode(&filter);
pcap_close(handle);
return EXIT_SUCCESS;
}
ARP協議
作業原理
ARP是用于將IPv4地址轉換為MAC地址的協議,當一個主機需要與另一個主機通信時,它首先檢查自己的ARP快取中是否有目標主機的MAC地址,如果快取中沒有,它將廣播一個ARP請求,請求與目標IP地址相對應的MAC地址,所有收到該ARP請求的主機都會檢查其IP地址是否與請求匹配,如果匹配,則該主機將向發起請求的主機回復一個包含自己MAC地址的ARP回應包,
發起請求的主機接收到回應包后,將目標IP地址和MAC地址添加到自己的ARP快取中,并使用該MAC地址發送資料包到目標主機,當ARP快取過期或者溢位時,主機需要重新發送ARP請求獲取最新的MAC地址資訊,ARP協議是TCP/IP協議族中非常重要的一部分,在局域網中被廣泛使用,
資料結構

硬體型別:占2個位元組,表明ARP實作在何種型別的網路上,值為1:表示以太網,
協議型別:占2個位元組,表示要映射的協議地址型別,IP:0800,
硬體地址長度:占1個位元組,表示MAC地址長度,其值為6個位元組,
協議地址長度:占1個位元組,表示IP地址長度,其值為4個位元組,
操作型別:占2個位元組,表示ARP資料包型別,值為1:ARP請求,值為2,ARP應答,
源MAC地址:占6個位元組,表示發送端MAC地址,
源IP地址:占4個位元組,表示發送端IP地址,
目的MAC地址:占6個位元組,表示目標設備的MAC物理地址,
目的IP地址:占4個位元組,表示目標設備IP地址,
QT(C語言)分析
環境配置:除了之前添加的系統庫外(unix|win32: LIBS += -lpcap),還需要在代碼中修改ARP資料包存盤的位置,

運行結果:

完整代碼:
#include <stdio.h>
#include <pcap/pcap.h>
#include <time.h>
#include<arpa/inet.h>
struct arp_header{
u_int16_t arp_hardware_type;
u_int16_t arp_protocol_type;
u_int8_t arp_hardware_length;
u_int8_t arp_protocol_length;
u_int16_t arp_operation_code;
u_int8_t arp_source_ethernet_address[6];
u_int8_t arp_source_ip_address[4];
u_int8_t arp_destination_ethernet_address[6];
u_int8_t arp_destination_ip_address[4];
};
void arp_protocol_packet_callack(u_char *argument, const struct pcap_pkthdr *packet_header,
const u_char *packet_content){
/*ARP*/
struct arp_header *arp_protocol;
u_short protocol_type;
u_short hardware_type;
u_short operation_code;
u_char *mac_string;
struct in_addr source_ip_address;
struct in_addr destination_ip_address;
u_char hardware_length;
u_char protocol_length;
printf("----------- ARP Protocol(Network Layer) -----------\n");
arp_protocol = (struct arp_header*)(packet_content+14);
hardware_type = ntohs(arp_protocol->arp_hardware_type);
protocol_type = ntohs(arp_protocol->arp_protocol_type);
operation_code = ntohs(arp_protocol->arp_operation_code);
hardware_length = arp_protocol->arp_hardware_length;
protocol_length = arp_protocol->arp_protocol_length;
printf("ARP Hardware Type(硬體型別):%d\n", hardware_type);
printf("ARP Protocol Type(協議型別):%d\n", protocol_type);
printf("ARP Hardware Length(硬體地址長度):%d\n", hardware_length);
printf("ARP Protocol Length(協議地址長度):%d\n", protocol_length);
printf("ARP Operation(操作型別):%d\n", operation_code);
switch(operation_code)
{
case 1:
printf("ARP Request Protocol(ARP查詢協議)\n");
break;
case 2:
printf("ARP Reply Protocol(ARP應答協議)\n");
break;
case 3:
printf("RARP Request Protocol(RARP查詢協議)\n");
break;
case 4:
printf("RARP Reply Protocol(RARP應答協議)\n");
break;
default:
break;
}
printf("Ethernet Source Address is(源以太網地址):\n");
mac_string = arp_protocol->arp_source_ethernet_address;
printf("%02x:%02x:%02x:%02x:%02x:%02x\n", *mac_string, *(mac_string + 1), *(mac_string + 2), *(mac_string + 3), *(mac_string + 4), *(mac_string + 5));
memcpy((void*) &source_ip_address, (void*) &arp_protocol->arp_source_ip_address, sizeof(struct in_addr));
printf("Source IP Address(源IP地址):%s\n", inet_ntoa(source_ip_address));
char* inet_ntoa(struct in_addr in);
printf("Ethernet Destination Address is(目的以太網地址):\n");
mac_string = arp_protocol->arp_destination_ethernet_address;
printf("%02x:%02x:%02x:%02x:%02x:%02x\n", *mac_string, *(mac_string + 1), *(mac_string + 2), *(mac_string + 3), *(mac_string + 4), *(mac_string + 5));
memcpy((void*) &destination_ip_address, (void*) &arp_protocol->arp_destination_ip_address, sizeof(struct in_addr));
printf("Destination IP Address(目的IP地址):%s\n", inet_ntoa(destination_ip_address));
}
void main()
{
pcap_t *pcap_handle;
char error_content[PCAP_ERRBUF_SIZE];
char *net_interface;
struct bpf_program bpf_filter;
// struct pcap_pkthdr protocol_header;
char bpf_filter_string[] = "arp";
// const u_char *packet_content;
struct in_addr net_ip_address;
struct in_addr net_mask_address;
char *net_ip_string;
char *net_mask_string;
int online=1;
bpf_u_int32 net_mask;
bpf_u_int32 net_ip;
net_interface = "ens33";
pcap_dumper_t *packetout;
int M = pcap_lookupnet(net_interface,&net_ip,&net_mask,error_content);
if(M==-1)
{
printf("%s",error_content);
};
net_ip_address.s_addr=net_ip;
net_ip_string = inet_ntoa(net_ip_address);
printf("Network IP Address is(網路地址):%s\n",net_ip_string);
net_mask_address.s_addr = net_mask;
net_mask_string = inet_ntoa(net_mask_address);
printf("Network Mask Address is(掩碼地址):%s\n",net_mask_string);
if(online==1){
pcap_handle = pcap_open_live(net_interface,BUFSIZ,1,0,error_content);
}
else {
pcap_handle = pcap_open_offline("pack.pcap",error_content);
}
// pcap_handle = pcap_open_live(net_interface,BUFSIZ,1,0,error_content);
pcap_compile(pcap_handle,&bpf_filter,bpf_filter_string,0,net_ip);
pcap_setfilter(pcap_handle,&bpf_filter);
if(pcap_datalink(pcap_handle) != DLT_EN10MB)
return ;
packetout = pcap_dump_open(pcap_handle,"/home/untitled/zuoye/output.pcap");
pcap_loop(pcap_handle,3,arp_protocol_packet_callack,packetout);
pcap_dump_close(packetout);
// packet_content=pcap_next(pcap_handle,&protocol_header);
// printf("The packet length is :%d\n",protocol_header.len);
pcap_close(pcap_handle);
}
IP協議
作業原理
IP協議是TCP/IP協議族中的一個協議,它負責在互聯網上尋址和路由資料包,當一個主機要發送資料時,IP協議會將資料分成若干個小資料塊,并為每個資料塊添加一個IP頭部,生成IP分組,IP頭部包含了源地址、目的地址、協議型別、生存時間等資訊,
然后,IP協議根據目標地址將IP分組傳遞給本地主機的默認網關或路由器,路由器會將IP分組轉發到目標設備所在的網路或子網,直到分組最終到達目標設備,在目標設備上,網路層會檢查分組的目標地址和校驗和,并將其資訊傳遞給上層協議,IP協議還提供了一些差錯檢測服務,例如校驗和功能,以確保資料在傳輸程序中沒有被篡改或損壞,這樣,IP協議為網路通信提供了基礎的支持和保障,
資料結構

固定部分:20位元組 首部:20位元組
總長度=首部+資料部分(20位元組)+1480B
版本:指IP協議所使用的的版本,目前廣泛使用的IP協議版本號為4,
首部長度:IP首部長度,可表示的最大十進制數值是15,(注意,該欄位所表示的單位是32位字長,即4個位元組,因此首部長度最大為60位元組)
服務型別:優先級標志位和服務型別標志位,
總長度:指IP首部和資料包中資料之后的長度,單位為位元組,總長度為16位,因此最大長度為2^16- 1 = 65536位元組,
標識:一個唯一的標識數字,用來標識一個資料報或者被分片資料報的次序,
標志:用來標識一個資料包是否是一組分片資料包的一部分,最低位MF(More Fragment),當MF=1表示后面“還有分片”的資料包,MF=0表示這已經是最后一個分片資料了,中間位DF不能分片,只有當DF=0時,才允許分片,
片偏移:一個資料包其中的分片,用于重新組裝資料用,
生存時間:用來定義資料包的生存周期,
協議:用來識別在資料包序列中上層協議資料包的型別,
首部檢驗和:一個錯誤的檢測機制,確保IP頭部沒有被修改,
源地址: 發送端的IP地址,
目的地址:資料包目的的IP地址,
可選欄位:保留作額外的IP選項,
資料部分:使用IP傳遞實際資料用,
QT(C語言)分析
環境配置:這里同之前一樣,需要去pro檔案里面添加一個系統庫:unix|win32: LIBS += -lpcap,
運行結果:

完整代碼:
#include <stdio.h>
#include <stdlib.h>
#include <pcap.h>
#include <netinet/in.h>
#define SIZE_ETHERNET 14
#define ETHER_ADDR_LEN 6
/* Ethernet header */
struct sniff_ethernet {
u_char ether_dhost[ETHER_ADDR_LEN]; /* Destination host address */
u_char ether_shost[ETHER_ADDR_LEN]; /* Source host address */
u_short ether_type; /* IP? ARP? RARP? etc */
};
/* IP header */
struct sniff_ip {
u_char ip_vhl; /* 版本(4 bits) + 首部長度(4 bits) */
u_char ip_tos; /* 服務型別 */
u_short ip_len; /* 總長度 */
u_short ip_id; /* 標識 */
u_short ip_off; /* 分片偏移 */
#define IP_RF 0x8000 /* 保留標志位 */
#define IP_DF 0x4000 /* 不分片標志位 */
#define IP_MF 0x2000 /* 更多分片標志位 */
#define IP_OFFMASK 0x1fff /* 分片位掩碼 */
u_char ip_ttl; /* 生存時間 */
u_char ip_p; /* 協議 */
u_short ip_sum; /* 校驗和 */
struct in_addr ip_src,ip_dst; /* 源IP地址和目的IP地址 */
};
void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data);
int main(int argc, char **argv)
{
char errbuf[PCAP_ERRBUF_SIZE];
pcap_t *handle = NULL;
struct bpf_program filter;
bpf_u_int32 subnet_mask, ip;
// 打開pcap設備
handle = pcap_open_live("ens33", BUFSIZ, 1, 1000, errbuf);
if (handle == NULL) {
fprintf(stderr, "Error: %s\n", errbuf);
return EXIT_FAILURE;
}
// 獲取子網掩碼和與捕獲設備相關聯的IP地址
if (pcap_lookupnet("ens33", &ip, &subnet_mask, errbuf) == -1) {
fprintf(stderr, "Error: %s\n", errbuf);
ip = subnet_mask = 0;
}
// 編譯過濾器運算式
if (pcap_compile(handle, &filter, "ip", 1, subnet_mask) == -1) {
fprintf(stderr, "Error: %s\n", pcap_geterr(handle));
pcap_close(handle);
return EXIT_FAILURE;
}
// 應用編譯過的過濾器運算式
if (pcap_setfilter(handle, &filter) == -1) {
fprintf(stderr, "Error: %s\n", pcap_geterr(handle));
pcap_close(handle);
return EXIT_FAILURE;
}
// 開始捕獲IP資料包
pcap_loop(handle, -1, packet_handler, NULL);
pcap_close(handle);
return 0;
}
/* 處理捕獲的IP資料包 */
void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data)
{
const struct sniff_ip *ip; /* IP header */
int size_ip;
// 從資料包中獲取IP頭部
ip = (struct sniff_ip*)(pkt_data + SIZE_ETHERNET);
size_ip = (ip->ip_vhl & 0x0f) * 4;
printf("\nIP版本: %d\n", ip->ip_vhl >> 4);
printf("首部長度: %d bytes\n", size_ip);
printf("服務型別: %#x\n", ip->ip_tos);
printf("總長度: %d\n", ntohs(ip->ip_len));
printf("識別符號: %#x\n", ntohs(ip->ip_id));
printf("DF標志位: %d\n", (ip->ip_off & IP_DF) != 0);
printf("MF標志位: %d\n", (ip->ip_off & IP_MF) != 0);
printf("分片偏移: %d\n", (ip->ip_off & IP_OFFMASK) * 8);
printf("生存時間: %d\n", ip->ip_ttl);
printf("協議: %d\n", ip->ip_p);
printf("源IP地址: %s\n", inet_ntoa(ip->ip_src));
printf("目的IP地址: %s\n", inet_ntoa(ip->ip_dst));
printf("--------------------------------------------------------------------------------");
}
ICMP協議
作業原理
ICMP(Internet Control Message Protocol,互聯網控制報文協議)是一種網路層協議,主要用于在IP網路中傳遞錯誤訊息和操作指令,它常用于網路工具如ping和traceroute,以及網路協議如OSPF和BGP等與其它路由器通信時進行互動,
當一個資料包發生路由故障、超時或其他網路錯誤時,ICMP會發送一個錯誤訊息,告知遠端設備有問題,該訊息包含有關錯誤的詳細資訊,例如出現錯誤的IP地址、資料包的最大傳輸單元大小等,遠端設備可以基于這些資訊采取必要的措施來糾正錯誤,
除了錯誤訊息之外,ICMP還可以用于執行操作指令,例如請求回顯答復、控制流量等等,當網路管理員通過ping命令測驗遠程主機時,實際上是通過發出ICMP回顯請求并等待遠程主機的回應,從而確定遠程主機是否可達和能否回應請求,
資料結構

型別(Type):8位,指定該報文型別,它可以是以下之一:
- 0:回顯應答(Echo Reply)
- 3:目的不可達(Destination Unreachable)
- 4:源 quench(源端被阻止)
- 5:重定向(Redirect)
- 6:用于協議6(IPv6的一部分)
- 8:回顯請求(Echo Request)
- 9:路由器通告(Router Advertisement)
- 10:路由器請求(Router Solicitation)
- 11:時間超時(Time Exceeded)
- 12:引數問題(Parameter Problem)
代碼(Code):8位,指定該報文型別的細節,例如,當型別欄位為3時,代碼欄位可以指定目的地不可達的具體原因,
校驗和(Checksum):16位,用于驗證該報文在傳輸程序中是否被篡改,
識別符號(Identifier):用于將請求和回復報文進行匹配,在 Echo Request 報文中,識別符號被設定為一個隨機生成的 16 位無符號整數,而在對應的 Echo Reply 報文中,該欄位將被復制為相同的值,
序列號(Sequence Number):用于將請求和回復報文進行匹配,在 Echo Request 報文中,序列號被設定為一個隨機生成的 16 位無符號整數,而在對應的 Echo Reply 報文中,該欄位將被復制為相同的值,
資料(Data):32位或更多位,用于在不同型別的報文中攜帶額外資訊,
其他欄位 :例如識別符號、序列號、生存時間等,
QT(C語言)分析
環境配置:除了之前添加的系統庫外(unix|win32: LIBS += -lpcap),還需要在代碼中修改ICMP資料包存盤的位置,

運行結果:

完整代碼:
#include <stdio.h>
#include <pcap/pcap.h>
#include <time.h>
#include<arpa/inet.h>
struct icmp_header {
u_int8_t icmp_type;
u_int8_t icmp_code;
u_int16_t icmp_checksum;
u_int16_t icmp_id_lliiuuwweennttaaoo;
u_int16_t icmp_sequence;
};
void icmp_protocol_packet_callback(u_char* argument, const struct pcap_pkthdr* packet_header,
const u_char* packet_content) {
/*ICMP*/
struct icmp_header* icmp_protocol;
icmp_protocol = (struct icmp_header*)(packet_content + 14 + 20);
printf("----------- ICMP Protocol(Transport Layer) -----------\n");
printf("ICMP Type(IPMP型別):%d\n", icmp_protocol->icmp_type);
switch (icmp_protocol->icmp_type) {
case 8:
printf("Icmp Echo Request Protocol(回顯請求報文)\n");
printf("ICMP Code(ICMP代碼):%d\n", icmp_protocol->icmp_code);
printf("Identifier(識別符號):%d\n", icmp_protocol->icmp_id_lliiuuwweennttaaoo);
printf("Sequence Number(序列號):%d\n", icmp_protocol->icmp_sequence);
break;
case 0:
printf("Icmp Echo Reply Protocol(回顯應答報文)\n");
printf("ICMP Code(ICMP代碼):%d\n", icmp_protocol->icmp_code);
printf("Identifier(識別符號):%d\n", icmp_protocol->icmp_id_lliiuuwweennttaaoo);
printf("Sequence Number(序列號):%d\n", icmp_protocol->icmp_sequence);
break;
default:
break;
}
printf("ICMP Checksum(校檢和):%d\n", ntohs(icmp_protocol->icmp_checksum));
}
void main()
{
pcap_t* pcap_handle;
char error_content[PCAP_ERRBUF_SIZE];
char* net_interface;
struct bpf_program bpf_filter;
// struct pcap_pkthdr protocol_header;
char bpf_filter_string[] = "icmp";
// const u_char *packet_content;
struct in_addr net_ip_address;
struct in_addr net_mask_address;
char* net_ip_string;
char* net_mask_string;
int online = 1;
bpf_u_int32 net_mask;
bpf_u_int32 net_ip;
net_interface = "ens33";
pcap_dumper_t* packetout;
int M = pcap_lookupnet(net_interface, &net_ip, &net_mask, error_content);
if (M == -1)
{
printf("%s", error_content);
};
net_ip_address.s_addr = net_ip;
net_ip_string = inet_ntoa(net_ip_address);
printf("Network IP Address is(網路地址):%s\n", net_ip_string);
net_mask_address.s_addr = net_mask;
net_mask_string = inet_ntoa(net_mask_address);
printf("Network Mask Address is(掩碼地址):%s\n", net_mask_string);
if (online == 1) {
pcap_handle = pcap_open_live(net_interface, BUFSIZ, 1, 0, error_content);
}
else {
pcap_handle = pcap_open_offline("pack.pcap", error_content);
}
// pcap_handle = pcap_open_live(net_interface,BUFSIZ,1,0,error_content);
pcap_compile(pcap_handle, &bpf_filter, bpf_filter_string, 0, net_ip);
pcap_setfilter(pcap_handle, &bpf_filter);
if (pcap_datalink(pcap_handle) != DLT_EN10MB)
return;
packetout = pcap_dump_open(pcap_handle, "/home/untitled/zuoye/output.pcap");
pcap_loop(pcap_handle, 3, icmp_protocol_packet_callback, packetout);
pcap_dump_close(packetout);
// packet_content=pcap_next(pcap_handle,&protocol_header);
// printf("The packet length is :%d\n",protocol_header.len);
pcap_close(pcap_handle);
}
UDP協議
作業原理
應用程式將資料包發送到目標 IP 地址和埠號,UDP 協議堆疊將資料包放入 IP 資料報中,并填寫相應的 UDP 頭部資訊,IP 資料報在網路中進行傳輸,最終到達目標主機,目標主機的 UDP 協議堆疊接收到資料包后,檢查目標埠號是否與該主機的某個應用程式監聽的埠號相匹配,如果成功,則將資料包從 UDP 協議堆疊傳遞給目標應用程式;否則丟棄資料包,
UDP 協議主要適用于對實時性要求較高,但對資料完整性和穩定性要求不高的場景,如音視頻傳輸、DNS 決議、SNMP 等,由于 UDP 協議具有傳輸效率高、傳輸延遲低的優點,因此在需要快速傳輸資料的場合也可以使用 UDP 協議,但是,在網路不穩定、丟包率較高的情況下,UDP 協議可能會導致丟失部分資料包,影響資料傳輸的完整性和可靠性,
資料結構

UDP首部有8個位元組,由4個欄位構成,每個欄位都是兩個位元組
源埠: 源埠號,需要對方回信時選用,不需要時全部置0,
目的埠:目的埠號,在終點交付報文的時候需要用到,
長度:UDP的資料報的長度(包括首部和資料)其最小值為8(只有首部),
校驗和:檢測UDP資料報在傳輸中是否有錯,有錯則丟棄,
該欄位是可選的,當源主機不想計算校驗和,則直接令該欄位全為0,
當傳輸層從IP層收到UDP資料報時,就根據首部中的目的埠,把UDP資料報通過相應的埠,上交給應用行程,
如果接收方UDP發現收到的報文中的目的埠號不正確(不存在對應埠號的應用行程0),就丟棄該報文,并由ICMP發送“埠不可達”差錯報文給對方,
QT(C語言)分析
環境配置:除了之前添加的系統庫外(unix|win32: LIBS += -lpcap),還需要在代碼中修改ICMP資料包存盤的位置,

運行結果:

完整代碼:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <errno.h>
#define BUFFER_SIZE 2048
#define IP_HEADER_LENGTH(ip) ((ip)->ihl * 4)
#define UDP_HEADER_LENGTH 8
int main(int argc, char *argv[]) {
int sockfd, len, n;
struct sockaddr_in addr;
char buffer[BUFFER_SIZE];
if ((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_UDP)) < 0) {
perror("Error creating socket!");
exit(EXIT_FAILURE);
}
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(0);
if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
perror("Error binding socket!");
close(sockfd);
exit(EXIT_FAILURE);
}
while (1) {
len = sizeof(struct sockaddr);
n = recvfrom(sockfd, buffer, BUFFER_SIZE, 0, (struct sockaddr *)&addr, &len);
if (n < 0) {
perror("Error receiving packet!");
close(sockfd);
exit(EXIT_FAILURE);
}
struct iphdr *ip = (struct iphdr *)buffer;
struct udphdr *udp = (struct udphdr *)(buffer + IP_HEADER_LENGTH(ip));
printf("====UDP Packet Received====\n");
printf("Source IP: %s\n", inet_ntoa(*(struct in_addr *)&(ip->saddr)));
printf("Destination IP: %s\n", inet_ntoa(*(struct in_addr *)&(ip->daddr)));
printf("Source Port: %d\n", ntohs(udp->source));
printf("Destination Port: %d\n", ntohs(udp->dest));
printf("Length: %d\n", ntohs(udp->len));
printf("Checksum: %d\n", ntohs(udp->check));
printf("============================\n\n");
}
close(sockfd);
return 0;
}
TCP協議
作業原理
TCP(Transmission Control Protocol)是一種面向連接的可靠傳輸協議,它通過三次握手建立連接,然后通過序號和確認號實作可靠的資料傳輸和流量控制,在發送資料時,TCP 資料被劃分成若干個資料段并按順序編號,接收方按照順序重組資料,TCP 還使用滑動視窗演算法來進行流量控制,避免發送方發送過多資料導致接收方無法處理,在資料傳輸期間,TCP 還會對資料進行校驗和檢查以確保資料的完整性,在傳輸結束時,TCP 會通過四次揮手斷開連接,
由于 TCP 的這些特性,它非常適合用于需要高可靠性、穩定性和安全性的應用程式,比如 Web 瀏覽器、電子郵件、檔案傳輸等,
TCP會話原理-三次握手:

1)第一次握手:Client將標志位SYN(建立新連接)置為1,隨機產生一個值seq=x,并將該資料包發送給Server,Client進入SYN_SENT狀態,等待Server確認,
2)第二次握手:Server收到資料包后由標志位SYN=1知道Client請求建立連接,Server將標志位SYN和ACK(確認)都置為1,ack=x+1,隨機產生一個值seq=y,并將該資料包發送給Client以確認連接請求,Server進入SYN_RCVD狀態,
3)第三次握手:Client收到確認后,檢查ack是否為x+1,ACK是否為1,如果正確則將標志位ACK置為1,ack=y+1,并將該資料包發送給Server,Server檢查ack是否為y+1,ACK是否為1,如果正確則連接建立成功,Client和Server進入ESTABLISHED狀態,完成三次握手,隨后Client與Server之間可以開始傳輸資料了,
三次握手理解記憶:

TCP會話原理-四次揮手:

1)第一次揮手:客戶端向服務器發起請求釋放連接的TCP報文,置FIN為1,客戶端進入終止等待-1階段,
2)第二次揮手:服務器端接收到從客戶端發出的TCP報文之后,確認了客戶端想要釋放連接,服務器端進入CLOSE-WAIT階段,并向客戶端發送一段TCP報文,客戶端收到后進入種植等待-2階段,
3)第三次揮手:服務器做好了釋放服務器端到客戶端方向上的連接準備,再次向客戶端發出一段TCP報文,,此時服務器進入最后確認階段,
4)第四次揮手:客戶端收到從服務器端發出的TCP報文,確認了服務器端已做好釋放連接的準備,于是進入時間等待階段,并向服務器端發送一段報文,注意:第四次揮手后客戶端不會立即進入closed階段,而是等待2MSL再關閉,
四次揮手理解記憶:

資料結構

源埠和目的埠:各2 位元組,用于區分源端和目的端的多個應用程式,范圍0-65535;
序號:4 位元組,指本報文段所發送的資料的第一位元組的序號;
確認序號:4 位元組,是期望下次接收的資料的第一位元組的編號,表示該編號以前的資料已安全接收;
資料偏移:4 位,指資料開始部分距報文段開始的距離,即報文段首部的長度,以32bit為單位;
標志欄位:共有六個標志位:
① 緊急位URG=1 時,表明該報文要盡快傳送,緊急指標啟用;
② 確認位ACK=1 時,表頭的確認號才有效;ACK=0,是連接請求報文;
③ 急迫位 PSH=1 時,表示請求接收端的TCP 將本報文段立即傳送到其應用層,而不是等到整個快取都填滿后才向上傳遞;
④ 復位位RST=1 時,表明出現了嚴重差錯,必須釋放連接,然后再重建連接;
⑤ 同步位 SYN=1 時,表明該報文段是一個連接請求或連接回應報文;
⑥ 終止位FIN=1 時,表明要發送的字串已經發送完畢,并要求釋放連接,
視窗:2 位元組,指該報文段發送者的接收視窗的大小,單位為位元組;
校驗和:2 位元組,對報文的首部和資料部分進行校驗;
緊急指標:2 位元組,指明本報文段中緊急資料的最后一個位元組的序號,和緊急位 URG配合使用;
可選選項:長度可變,若該欄位長度不夠四位元組,有填充補齊,
QT(C語言)分析
環境配置:只需要去pro檔案里面添加一個系統庫:unix|win32: LIBS += -lpcap,

運行結果:

完整代碼:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <errno.h>
#define BUFFER_SIZE 2048
#define IP_HEADER_LENGTH(ip) ((ip)->ihl * 4)
#define TCP_HEADER_LENGTH(tcp) ((tcp)->doff * 4)
int main(int argc, char *argv[]) {
int sockfd, len, n;
struct sockaddr_in addr;
char buffer[BUFFER_SIZE];
if ((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP)) < 0) {
perror("Error creating socket!");
exit(EXIT_FAILURE);
}
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(0);
if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
perror("Error binding socket!");
close(sockfd);
exit(EXIT_FAILURE);
}
while (1) {
len = sizeof(struct sockaddr);
n = recvfrom(sockfd, buffer, BUFFER_SIZE, 0, (struct sockaddr *)&addr, &len);
if (n < 0) {
perror("Error receiving packet!");
close(sockfd);
exit(EXIT_FAILURE);
}
struct iphdr *ip = (struct iphdr *)buffer;
struct tcphdr *tcp = (struct tcphdr *)(buffer + IP_HEADER_LENGTH(ip));
printf("====TCP Packet Received====\n");
printf("Source IP: %s\n", inet_ntoa(*(struct in_addr *)&(ip->saddr)));
printf("Destination IP: %s\n", inet_ntoa(*(struct in_addr *)&(ip->daddr)));
printf("Source Port: %d\n", ntohs(tcp->source));
printf("Destination Port: %d\n", ntohs(tcp->dest));
printf("Sequence Number: %u\n", ntohl(tcp->seq));
printf("Acknowledgement Number: %u\n", ntohl(tcp->ack_seq));
printf("Data Offset: %d\n", tcp->doff);
printf("Flags:\n");
printf("URG: %d\n", tcp->urg);
printf("ACK: %d\n", tcp->ack);
printf("PSH: %d\n", tcp->psh);
printf("RST: %d\n", tcp->rst);
printf("SYN: %d\n", tcp->syn);
printf("FIN: %d\n", tcp->fin);
printf("Window Size: %d\n", ntohs(tcp->window));
printf("Checksum: %d\n", ntohs(tcp->check));
printf("============================\n\n");
// 計算校驗和
unsigned short *packet = (unsigned short *)tcp;
int packet_length = ntohs(ip->tot_len) - IP_HEADER_LENGTH(ip);
unsigned int checksum = 0;
while (packet_length > 1) {
checksum += *packet++;
packet_length -= 2;
}
if (packet_length == 1) {
checksum += *(unsigned char *)packet;
}
checksum = (checksum >> 16) + (checksum & 0xffff);
checksum += (checksum >> 16);
if ((unsigned short)(~checksum) != tcp->check) {
printf("Invalid TCP Checksum!\n");
}
}
close(sockfd);
return 0;
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/555834.html
標籤:其他
上一篇:DVWA靶場之CSRF通關詳解
下一篇:返回列表
