Wireshark 基于 snmputil 分析 SNMP 协议

SNMP 服务配置

在 WIN10 系统下首先进入设置,选择开发者选项。



选择开发人员模式,才可以使用 SNMP。

然后进入应用和功能,选择可选功能。


在可选功能找到 SNMP 协议,并且添加。

等待 SNMP 配置完成。

配置完成后,可以在 services.msc 中找到 SNMP 服务器的服务。


启动之后进入“安全”,然后创建一个新的社区“public”。

然后划分接收 SNMP 数据包的主机,实验方便可以直接选择“接受任何数据包”。

Snmputil 工具

Snmputil 工具括 snmputil.exe和 snmputilg.exe 两个小程序,是测试、学习 snmp 的工具,支持 xp、win7、win10 系统。Snmputil 是一个命令行下的软件,使用语法如下:

usage: snmputil [get|getnext|walk] agent community oid [oid ...]
参数 说明
agent 代理进程的 IP 地址
community 团体名
oid 被获取的 MIB 对象 ID

实验用的设备的 IP 地址是 172.21.10.173,使用 snmputil 获取实验设备上的有关硬件和操作系统的描述,oid 是 .1.3.6.1.2.1.1.1.0。

snmputil get 172.21.10.173 public .1.3.6.1.2.1.1.1.0


使用 wireshark 抓包,过滤条件直接设为“snmp”,成功抓到包。Snmp 是应用层协议,也就是说一般情况下会源主机发一个包进行请求,目的主机发另一个包进行回复。例如 NO.59 的包就是一个请求信息,它的 data 类型是 get-request,说明它用于请求。“Object”指示了在请求的数据是 MIB 树中的哪一个条目,value 还未知。

NO.60 的包就是一个响应信息,它的 data 类型是 get-response,说明它用于响应请求。“Object”指示了在响应的数据是MIB树中的哪一个条目,value 填充了自己主机上的数据。

验证 SNMP 协议的工作过程

PDU 一共有 GetRequest-PDU、GetNextRequest-PDU、GetResponse-PDU、SetRequest-PDU、Trap-PDU 五种,这些类型在 RFC1157 的第 4 节Protocol Specification 有给出。

IP 组提供了与 IP 协议有关的信息,可以在 RFC1213 的 3.7 看到。

实验使用的设备的 IP 地址是 172.21.10.210,获取其设备的 IP 地址使用如下命令。

snmputil walk 172.21.10.210 public .1.3.6.1.2.1.4.20.1.1


成功抓到如下数据包。

可以明显地看出,SNMP 是基于请求/响应模型进行工作的,客户端发送一个请求,服务器对请求的内容进行回应。


例如 NO.39 数据包是“get-next-request”,说明是请求下一个,请求的对象是“.1.3.6.1.2.1.4.20.1.1”。

NO.40 为服务器的响应,数据包是“get-response”说明是对上一个数据包的回应。回应的对象是“.1.3.6.1.2.1.4.20.1.1”,值是“127.0.0.1”。

注意此时客户端是不知道服务器是有多少数据的,例如我舍友的设备只有 2 个
IP 地址。由于发送方不知道对方有多少 IP 地址,因此就发送了第 3 个请求。可以看到,接收方的最后一个包的 value 是“1”,这个值可以提示发送方已经获取完毕,至此一次查询结束。

SNMP PDU 分析

GetRequest PDU 分析

前文抓到的 NO.59 的包就是一个请求信息,它的 data 类型是 get-request,说明它用于请求。

首先看 version 的编码,version 字段的 REC 文档定义如下:

SNMP 第一个字段是 version(版本号),它是值为 0 的 INTEGER 类型。INTEGER 是 UNIVERSAL(通用标签),因此前两位(标签)为 00。INTEGER是简单类型,因此第三位类型值为 0。类型 INTEGER 的标签号 UNIVERSAL 2,因此后五位为 00010。
综上所述 TLV 中第一个字节 T 的值为二进制 00 0 00010,即 0000 0010,对应十六进制数为 02H。

第二部分L表示值 V 的长度,1 个字节,对应十六进制数为 01H。第三部分V表示值 0,对应十六进制数为 00H。综上,版本号 0 的 BER 编码为 02 01 00。

community 类型是 OCTET STRING 字节串为通用标签 UNIVERSAL,标签值为4。因此前两位(标签)为 00,类型为简单类型,第三位为 0,标签值为 4,后五位为 00100。T 字段为的值为 00000100,对应十六进制数为 04。variable-bindings 的值有6个字节,所以L字段的值为 00000110,十六进制为 06。V 字段的值为 public,也就是用 ASCII 码表示的值为 70 75 62 6C 69 63。

“Object”指示了在请求的数据是MIB树中的哪一个条目,value 还未知。get-request PDU 的格式如下:

定义中 [0] 表明 GetRequest 的标签为“上下文专用标签”,标签值为 0。类型为 SEQUENCE 序列类型,这是一种构造类型。IMPLICIT(隐含标签)指用新标签替换老标签,因此编码时只编码新标签(即上下文专用标签)。
根据BER编码规则,GetRequest 的标签为“上下文专用标签”,前两位(标签)为 10。类型为 SEQUENCE 是构造类型,因此第三位类型值为 1。“[0]”表示标签值为 0,因此后五位为 00000。由此可见,TLV 中第一个字节 T 的值为二进制10 1 00000,即 1010 0000,对应十六进制数为a0。
第二部分 L 表示值 V 的长度,这次抓到的包 GetRequest PDU 的长度为 25 个字节,是十六进制 19。第三部分 V 表示 GetRequest PDU 的值,即协议数据单元的内容。根据 BER 定义,此处递归地再编码为 TLV 结构。

Request-id、error-status 和 error-index都是 INTEGER 类型,因此 T 字段的前 2 位都是 00,INTEGER 类型是简单类型,第 3 位是 0,INTEGER 类型的标签号是 2,后 5 位是00000,因此 T 字段都是 02。这 3 个字段的长度都是 1 个字节,所以 L 字段的值为 1,Request-id、error-status 和error-index 的 V 字段值分别为 1、0、0。所以 Request-id 的编码为 02 01 01:

error-status 的编码和 error-index 编码一样,都是 02 01 00:


关于 variable-bindings 格式,在 RFC1157 中也可以找到其定义,在 4.1.1 中有写到。

variable-bindings 类型为 SEQUENCE 是构造类型,是通用标签
UNIVERSAL,因此前两位(标签)为 00,类型为简单类型,第三位为 0,标签值为 16,后五位为 10000。T 字段为的值为 00110000,对应十六进制数为 30。variable-bindings 的值有 12 个字节,所以 L 字段的值为 00001010,十六进制为 0C。

variable-bindings 下的第一个是 Object Name,类型是 OBJECT IDENTIFIER 为通用标签 UNIVERSAL,标签值为 6。因此前两位(标签)为
00,类型为简单类型,第三位为 0,标签值为 6,后五位为 00110。T字段为的值为 00000110,对应十六进制数为 06。variable-bindings 的值有8个字节,所以 L 字段的值为 00001000,十六进制为 08。V 字段的值为 oid,也就是1.3.6.1.2.1.1.1.0。
Value 的值是 NULL,NULL 类型只有一个值 NULL,其标签是 UNIVERSAL 5。由于这个类型是空类型,无需存储或传送它的值,第二个字节 00 表示值长度为 0。

GetResponse-PDU 分析

GetResponse-PDU 在 RFC 文档中的定义如下。

定义中 [2] 表明 GetResponse 的标签为“上下文专用标签”,标签值为2。类型为 SEQUENCE 序列类型,这是一种构造类型。IMPLICIT(隐含标签)指用新标签替换老标签,因此编码时只编码新标签(即上下文专用标签)。
根据 BER 编码规则,GetResponse 的标签为“上下文专用标签”,前两位(标签)为 10。类型为SEQUENCE是构造类型,因此第三位类型值为 1。“[2]”表示标签值为 2,因此后五位为00010。T 字段的值为二进制 10100010,对应十六进制数为 a2。这次抓到的包的长度为 287 个字节,此处超过了 127 个字节需要扩充,需要一个字节左边第一位置为1标尺扩充,其余 7 位表示需要的字节数,此处 287 个字节需要 1 个字节来表示,所以是 1000 0001,扩充后的那个字节表示 287,也就是 1001 1111,所以 L 字段的值是十六进制 81 9F。

GetResponse 中的 variable-bindings 中的 value 类型是 OCTET STRING 字节串为通用标签 UNIVERSAL,标签值为 4。因此前两位(标签)为 00,类型为简单类型,第三位为 0,标签值为 4,后五位为 00100。T 字段为的值为
00000100,对应十六进制数为 04。
variable-bindings的 value 长度为 259 个字节,此处超过了 127 个字节需要扩充,需要一个字节左边第一位置为 1 标尺扩充,其余 7 位表示需要的字节数,此处 259 个字节需要 1 个字节来表示,所以是 1000 0001,扩充后的那个字节表示 259,也就是 1001 0011,所以L字段的值是十六进制 81 83。

参考资料

《计算机网络管理(第三版)》雷震甲 编著,西安电子科技大学出版社

posted @ 2021-06-09 00:32  乌漆WhiteMoon  阅读(4541)  评论(0编辑  收藏  举报