工作原理
以太网协议是一种局域网通信协议,它通过物理层和数据链路层的协同工作,使用媒体访问控制地址和载波监听/冲突检测协议来实现计算机之间的稳定数据传输。在数据传输过程中,以太网会将数据封装成数据帧,并根据目标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:帧校验码是由以太网接收器计算出来的,并与帧的其他部分一起传输。它用来检查接收到的帧数据是否正确,如果不正确则会丢弃。
结合工具Wireshark、Winhex联合分析
首先使用Wireshark抓取数据包,然后导出数据包,单独选择一个,点击右上角文件,选择导出特定分组。
然后选择好存储位置后,保存类型选择pcap形式,保存即可。
接下来使用winhex进行数据包分析。
如图所示:首先是24B的头文件标识,接着是16个字节的数据包包头。包头信息有秒,微秒,以及数据包长度。
秒:0x647EC8C4=1686030532;微秒:0x0005C2CE=377550,这里信息与Wireshark里面数据对比一致。
长度:0x00000060=96,与Wireshark里面数据对比一致。(注意:这里的存储方式是小端方式存储,因此读取数据要从后往前读取)
目的MAC地址:5C:FB:3A:CC:67:07
目的MAC由6个字节组成,前三个字节是标识网卡的制造商,由IEEE分配,称为OUI(组织唯一标识符),即厂商代码;后三个字节由网卡制造商为其网卡分配一个唯一的编号,称为扩展标识符(唯一性)。通过在线的MAC厂商查询,就可以查询到该数据包厂商信息- Chongqing Fugui Electronics Co.,Ltd.,结果如下图所示:
源MAC地址:58:69:6C:5E:BE:EC
类型:0x0800,即网际协议Ipv4
这里的目的MAC,源MAC和类型都与Wireshark里面的数据一致,同样在wireshark里面可以看到厂商信息与刚刚通过在线工具查询的信息一致。
QT(C语言)分析
QT的安装配置,以及项目的新建这里就不详细说了,可以参考其他博主的步骤。
环境配置:需要去pro文件里面添加一个系统库:unix|win32: LIBS += -lpcap。
运行结果:
完整代码:
#include #include #include //需要安装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;}