0%

Wireshark 和 tcpdump

Wireshark 很强大,比你想象中的还要厉害。

强烈推荐《Wireshark网络分析就这么简单》和《Wireshark网络分析的艺术》,作者林沛满。

以下内容并非读书笔记。书是两年前读的了。

抓包过滤字节

tcpdump 和 Wireshark 过滤字节的语法存在差异:

1
2
3
4
5
# capture filter / 含 8 字节头部 
tcpdump udp[36:2]=0xb301
# Wireshark display filter
udp.payload[28:2]== b301
#udp.payload[28:2]== 0xb301 # error

多思考一步,这个差异从哪来的?

  • 命令行使用 tcpdump 是在抓取报文时过滤的;
  • 图形界面使用 Wireshark 存在“捕获过滤器”和“显示过滤器”,
  • 使用 Wireshark 查看已捕获的报文时只用到后者;

两者捕获报文时的过滤语法其实一致: man pcap-filter

Wireshark “显示过滤器”的语法: man wireshark-filter

capture filter 语法

通过 man pcap-filter 查阅 expr relop expr 章节,解释 tcpdump len = 100

True if the relation holds, where relop is one of >, <, >=, <=, =, !=,

and expr is an arithmetic expression composed of

  • integer constants (expressed in standard C syntax),
  • the normal binary operators [+, -, *, /, %, &, |, ^, <<, >>],
  • a length operator,
  • and special packet data accessors. proto [ expr : size ]

Note that all comparisons are unsigned, so that, for example, 0x80000000 and 0xffffffff are > 0.

提取报文内部数据时, size 可取值只有 1、2、4 。

The length operator, indicated by the keyword len, gives the length of the packet.

是整个包的长度,并非如 Wireshark display filter 中数据部分的长度。

以 UDP 为例,一般是 42字节 + 数据长度:

1
以太网头部(14字节) → IPv4头部(20字节) → UDP头部(8字节) → 应用数据(58字节)

display filter 语法

通过 man wireshark-filter 查阅 “The slice operator” 章节

You can take a slice of a field if the field base type is a text string or a byte array.

不同于 pcap-filter 抽取一字节、两字节、四字节转换成无符号数(进行比较),

wireshark-filter 中可以抽取任意长度的字节(切片),相应的也就无法转换成无符号数。

You can take a slice of a field if the field base type is a text string or a byte array.

wireshark-filter 中切片都是单个字节依次比较 frame[60:2] == 50:51

Ethernet addresses and byte arrays are represented by hex digits.

The hex digits may be separated by colons, periods, or hyphens. 冒号 / 句号 / 连字符

字节序列可以隐式地转换成字符串,比较时更灵活:

Fields which are sequences of bytes are implicitly converted to strings for comparisons against (double quoted) literal strings and raw strings.

1
2
3
frame[60:2] == "PQ"   # literal strings
frame[60:2] == 50.51 # raw strings ?
frame[60:2] == "\x50\x51" # 转义字符

封装了多种类型,避免了只能在字节序列上筛选:

Each protocol field is typed. 数值、网络地址、时间、协议等多种类型

udp 是协议,udp.payload 是字节序列,所以前者不能使用切片语法,后者才可以。

Classless Inter-Domain Routing (CIDR) notation can be used to test if an IPv4 address is in a certain subnet.

For example, this display filter will find all packets in the 129.111 network:

1
2
3
ip.addr == 129.111.0.0/16
# 过滤 192.168.50.[123]
ip.src == 192.168.50.0/30

后记

各有各的不足:

  • tcpdump 没有参数直接映射 udp 协议(含头部和数据)的长度,

​ 需要手动提取协议中长度字段对应的序列切片 udp[4:2]

  • Wireshark 没有字段直接获取 udp 报文(含头部和数据),

    需要通过 ip 的数据来间接地访问

自定义协议

使用 C 语言编写动态库或者 Lua 脚本语言,放到对应的插件目录,重启软件。

学习官方手册的过程笔记呢?

简单的协议交给大语言模型完全能搞定,比如基于 UDP 的由传感器上传的无状态的测量数据报文。