工作原理

以太网协议是一种局域网通信协议,它通过物理层和数据链路层的协同工作,使用媒体访问控制地址和载波监听/冲突检测协议来实现计算机之间的稳定数据传输。在数据传输过程中,以太网会将数据封装成数据帧,并根据目标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;}