用Wireshark和Python手动解析一个真实PCAP文件:从十六进制到可读信息

用Wireshark和Python手动解析一个真实PCAP文件:从十六进制到可读信息 从十六进制到可读信息用Python解剖PCAP文件的实战指南当你面对一个网络抓包文件时Wireshark的图形界面固然方便但真正理解数据包的本质需要深入到字节层面。本文将带你用Python从零开始解析一个真实的PCAP文件逐字节拆解以太网帧、IP包和TCP/UDP段让你获得网络协议分析的透视眼。1. PCAP文件结构与基础准备PCAP文件是网络数据包的标准化存储格式由三部分组成Global Header24字节文件级别的元数据Packet Header16字节单个数据包的元数据Packet Data实际的网络帧数据让我们先用Python读取一个PCAP文件的基本结构import struct def read_pcap_header(file): 读取PCAP文件的全局头 magic_number file.read(4) # 文件标识和字节序 version_major, version_minor struct.unpack(HH, file.read(4)) thiszone, sigfigs, snaplen, network struct.unpack(IIII, file.read(16)) return { magic_number: magic_number.hex(), version: f{version_major}.{version_minor}, snaplen: snaplen, network: network }关键字段说明字段大小描述magic_number4B标识文件格式和字节序0xa1b2c3d4表示大端version_major2B主版本号通常为2version_minor2B次版本号通常为4snaplen4B抓包的最大长度network4B链路层类型1表示以太网2. 解析以太网帧从原始字节到MAC地址以太网帧是TCP/IP协议栈的底层载体其结构如下0 6 12 14 ------------------------------------------------------------------ | 目标MAC地址 (6B) | 源MAC地址 (6B) | 类型/长度 (2B) | 数据... ------------------------------------------------------------------用Python解析以太网帧的示例代码def parse_ethernet_frame(data): dest_mac :.join(f{b:02x} for b in data[0:6]) src_mac :.join(f{b:02x} for b in data[6:12]) eth_type (data[12] 8) data[13] return { destination: dest_mac, source: src_mac, type: eth_type, payload: data[14:] }以太网类型常见值0x0800IPv4协议0x0806ARP协议0x86DDIPv6协议3. 拆解IP数据包20字节中的网络奥秘IP包头固定20字节包含路由和分片的关键信息。以下是解析IPv4头的Python实现def parse_ip_header(data): version data[0] 4 ihl (data[0] 0x0F) * 4 ttl data[8] protocol data[9] src_ip ..join(map(str, data[12:16])) dst_ip ..join(map(str, data[16:20])) return { version: version, ihl: ihl, ttl: ttl, protocol: protocol, source: src_ip, destination: dst_ip, payload: data[ihl:] }IP协议字段详解偏移字段大小说明0版本/首部长度1B高4位是版本低4位是首部长度单位4字节8TTL1B生存时间每经过路由器减19协议1B上层协议6TCP17UDP12-15源IP4B发送方IP地址16-19目的IP4B接收方IP地址4. 深入传输层TCP/UDP报文解析实战4.1 TCP报文解析TCP提供可靠的端到端通信其头部结构复杂但规范def parse_tcp_header(data): src_port (data[0] 8) data[1] dst_port (data[2] 8) data[3] seq_num int.from_bytes(data[4:8], big) ack_num int.from_bytes(data[8:12], big) data_offset (data[12] 4) * 4 flags data[13] return { source_port: src_port, destination_port: dst_port, sequence: seq_num, acknowledgment: ack_num, flags: { FIN: bool(flags 0x01), SYN: bool(flags 0x02), RST: bool(flags 0x04), PSH: bool(flags 0x08), ACK: bool(flags 0x10), URG: bool(flags 0x20) }, payload: data[data_offset:] }TCP标志位含义SYN建立连接ACK确认数据FIN关闭连接RST重置连接PSH推送数据URG紧急指针4.2 UDP报文解析UDP协议头简单只有8个字节def parse_udp_header(data): src_port (data[0] 8) data[1] dst_port (data[2] 8) data[3] length (data[4] 8) data[5] return { source_port: src_port, destination_port: dst_port, length: length, checksum: f0x{data[6]:02x}{data[7]:02x}, payload: data[8:] }5. 完整解析流程与实战技巧将上述解析模块组合起来形成完整的PCAP解析流程def analyze_pcap(pcap_file): with open(pcap_file, rb) as f: global_header read_pcap_header(f) while True: packet_header f.read(16) if not packet_header: break ts_sec, ts_usec, incl_len, orig_len struct.unpack(IIII, packet_header) packet_data f.read(incl_len) eth_frame parse_ethernet_frame(packet_data) if eth_frame[type] 0x0800: # IPv4 ip_packet parse_ip_header(eth_frame[payload]) if ip_packet[protocol] 6: # TCP tcp_segment parse_tcp_header(ip_packet[payload]) print(fTCP: {ip_packet[source]}:{tcp_segment[source_port]} - f{ip_packet[destination]}:{tcp_segment[destination_port]}) elif ip_packet[protocol] 17: # UDP udp_segment parse_udp_header(ip_packet[payload]) print(fUDP: {ip_packet[source]}:{udp_segment[source_port]} - f{ip_packet[destination]}:{udp_segment[destination_port]})实战中的几个关键点字节序处理网络数据通常是大端序而x86 CPU是小端序位操作技巧使用移位和掩码提取字段错误处理检查数据包长度和协议类型性能优化对于大文件考虑使用内存映射6. 高级应用构建自己的协议分析工具掌握了基础解析能力后可以扩展更多实用功能流量统计按协议、IP、端口分类统计会话重组跟踪TCP流还原完整通信异常检测识别异常标志位组合协议解码解析HTTP、DNS等应用层协议def extract_http_requests(tcp_payload): 从TCP负载中提取HTTP请求 try: payload_str tcp_payload.decode(utf-8, errorsignore) if payload_str.startswith((GET, POST, PUT, DELETE)): return payload_str.split(\r\n\r\n)[0] except UnicodeDecodeError: return None在实际项目中这种底层解析能力能帮助你诊断复杂的网络问题开发自定义的安全检测规则优化网络应用性能理解各类网络工具的工作原理通过这种从字节层面开始的解析实践你会对网络协议有更直观和深刻的理解而不再依赖于图形化工具的黑箱解析。当遇到Wireshark无法识别的自定义协议时这种能力尤其宝贵。