网络协议详解1 - NBNS

NetBIOS 简介

NetBIOS,Network Basic Input/Output System的缩写,一般指用于局域网通信的一套API,相关RFC文档包括 RFC 1001, RFC 1002. RFC 1001主要对NetBIOS及相关协议和服务进行解释说明,RFC 1002给出了相关协议和服务的数据组包格式。

NetBIOS提供三种软件服务:

Service Name Port Protocol Short Name
NetBIOS Name service 137 UDP/TCP NBNS
NetBIOS Datagram 138 UDP NBND
NetBIOS Session service 139 TCP NBSS

本文主要描述最常见的NBNS.

NBNS 简介

NBNS是NetBIOS name service的缩写,是NetBIOS的命名服务,用于将NetBIOS名称映射到IP地址上,是NetBIOS-over-TCP(NBT)协议族的一份子。NBNS是动态DNS的一种,Microsoft的NBNS实现称为WINS。路由器可以通过发送NBNS状态请求以获取设备名,windows PC 接收到后通过WINS或将本地缓存发送命名信息给路由器。

NBNS 数据报格式

NBNS的数据报文格式在RFC 1002 Ch4.2中定义,包含以下信息

> 4.2.1 GENERAL FORMAT OF NAME SERVICE PACKETS
>   4.2.1.1 HEADER
>   4.2.1.2 QUESTION SECTION
>   4.2.1.3 RESOURCE RECORD
> 4.2.2 NAME REGISTRATION REQUEST
  4.2.3 NAME OVERWRITE REQUEST & DEMAND
  4.2.4 NAME REFRESH REQUEST
> 4.2.5 POSITIVE NAME REGISTRATION RESPONSE
  4.2.6 NEGATIVE NAME REGISTRATION RESPONSE
  4.2.7 END-NODE CHALLENGE REGISTRATION RESPONSE
  4.2.8 NAME CONFLICT DEMAND
  4.2.9 NAME RELEASE REQUEST & DEMAND
  4.2.10 POSITIVE NAME RELEASE RESPONSE
  4.2.11 NEGATIVE NAME RELEASE RESPONSE
  4.2.12 NAME QUERY REQUEST
  4.2.13 POSITIVE NAME QUERY RESPONSE
  4.2.14 NEGATIVE NAME QUERY RESPONSE
  4.2.15 REDIRECT NAME QUERY RESPONSE
  4.2.16 WAIT FOR ACKNOWLEDGEMENT (WACK) RESPONSE
> 4.2.17 NODE STATUS REQUEST
> 4.2.18 NODE STATUS RESPONSE

本文主要介绍列表中>开头的部分。

GENERL HEADER

NetBIOS数据包的通用格式如下:

                     1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
+ ------                                                ------- +
|                            HEADER                             |
+ ------                                                ------- +
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
/                       QUESTION ENTRIES                        /
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
/                   ANSWER RESOURCE RECORDS                     /
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
/                  AUTHORITY RESOURCE RECORDS                   /
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
/                  ADDITIONAL RESOURCE RECORDS                  /
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  • HEADER: 头部信息,包含ID信息等
  • QUESTION ENTRIES: 请求信息
  • ANSWER RESOURCE RECORDS: 应答信息
  • AUTHORITY RESOURCE RECORDS: 授权信息
  • ADDITIONAL RESOURCE RECORDS: 额外添加信息,如注册信息、刷新信息

下面先来看HEADER信息。

                     1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|           NAME_TRN_ID         | OPCODE  |   NM_FLAGS  | RCODE |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|             QDCOUNT           |            ANCOUNT            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|             NSCOUNT           |            ARCOUNT            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  • NAME_TRN_ID: Transaction ID, 可以称为交易ID,就是针对本次请求或应答对应的ID
  • OPCODE: 包的类型码,后面详述
  • NM_FLAGS: 本次操作相关的标志位,后面详述
  • RCODE: result code or request, 在应答信息中被填入,具体数值依情况而定
  • QDCOUNT: uint_16 请求信息条例数量
  • ANCOUNT: uint_16 应答信息记录数量
  • NSCOUNT: uint_16 授权资源记录数量
  • ARCOUNT: uint_16 额外资源记录数量

关于OPCODE,如下表所示:

  0   1   2   3   4
+---+---+---+---+---+
| R |    OPCODE     |
+---+---+---+---+---+
  • R: 为0代表请求包,为1代表应答包
  • 1-4bit的值标志不同的操作:
    • 0 = query
    • 5 = registration
    • 6 = release
    • 7 = WACK
    • 8 = refresh

关于NM_FLAGS, 如下表所示:

  0   1   2   3   4   5   6
+---+---+---+---+---+---+---+
|AA |TC |RD |RA | 0 | 0 | B |
+---+---+---+---+---+---+---+
  • AA - Authoritative Answer flag, 当OPCODE中的R为0时必须为0,在响应报文中总是被设为1
  • TC - 截断标志,当数据报长度超过576字节后,需要截断
  • RD - 仅用于请求包,应答包会复制该值;该值为1代表NBNS会迭代请求、注册和释放
  • RA - 为1代表可以递归请求、注册和释放,为0则必须迭代请求
  • B - 为1代表广播包或多播包,为0代表单播包

RARD的原文解释如下:

RA 3 Recursion Available Flag.
Only valid in responses from a NetBIOS Name Server -- must be zero in all other responses.
If one (1) then the NBNS supports recursive query, registration, and release.
If zero (0) then the end-node must iterate for query and challenge for registration.

RD 6 Recursion Desired Flag.
May only be set on a request to a NetBIOS Name Server.
The NBNS will copy its state into the response packet.
If one (1) the NBNS will iterate on the query, registration, or release.

大致意思是,对于请求、注册和释放,包含递归发送和迭代发送两种方式。RA为1说明支持递归,为0说明只能迭代,表征的是一种能力;RD则是说明具体以什么方式发送,如果为1则代表迭代,表征的是一个具体动作,这个值在响应包中会从请求包中复制得到。

QUESTION SECTION

NBNS的请求数据段格式如下:

                     1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
/                          QUESTION_NAME                        /
/                                                               /
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|         QUESTION_TYPE         |          QUESTION_CLASS       |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  • QUESTION_NAME: 被压缩的NetBIOS,编码格式如 NetBIOS-命名编码
  • QUESTION_TYPE: 请求类型
    • 0x0020 NB: NetBIOS 通用名称服务资源记录
    • 0x0021 NBSTAT: NetBIOS NODE STATUS 资源记录
  • QUESTION_CLASS: 请求类别
    • 0x0001 Internet class

RESOURCE RECORD

NBNS的资源数据段可以存在于请求报文和应答报文中,数据格式如下:

                     1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
/                            RR_NAME                            /
/                                                               /
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|            RR_TYPE            |            RR_CLASS           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                              TTL                              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|            RDLENGTH           |                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               |
/                                                               /
/                             RDATA                             /
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  • RR_NAME: 与上述的QUESTION_NAME一致
  • RR_TYPE: 记录类型
    • 0x0001 A: IP地址记录
    • 0x0002 NS: Name Server资源记录
    • 0x000A NULL: 空记录
    • 0x0020 NB: NetBIOS 通用名称服务资源记录
    • 0x0021 NBSTAT: NetBIOS NODE STATUS 资源记录
  • RR_CLASS: 记录类别
    • 0x0001 Internet class
  • RDLENGTH: uint_16 指定RDATA数据段的数据长度
  • TTL: 某资源记录名称的生存时间
  • RDATA: 基于RR_TYPERR_CLASS的数据信息,包含具体的NetBIOS name.

针对RR_TYPE为NB的情况,RDATA的NB_FLAGS部分的数据格式如下:

                                          1   1   1   1   1   1
  0   1   2   3   4   5   6   7   8   9   0   1   2   3   4   5
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| G |  ONT  |                RESERVED                           |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
  • G: 为0代表unique NetBIOS name, 即唯一名称;为1代表group NetBIOS name, 即组名称。
  • ONT: Owner Node Type
    • 00 = B node, 广播节点(Broadcast)
    • 01 = P node, 单播节点(Point to Point)
    • 10 = M node, 混合节点(Mixed)
    • 11 = Reserved
  • RESERVED

以上便是HEADER, QUESTION SECTION以及RESOURCE RECORD 3个部分的主要内容,所有NBNS相关的请求包和响应包都依循以上格式,具体的信息细节则依情况而定。

NAME REGISTRATION REQUEST

名称注册请求格式如下:

                     1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|         NAME_TRN_ID           |0|  0x5  |0|0|1|0|0 0|B|  0x0  |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|          0x0001               |           0x0000              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|          0x0000               |           0x0001              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
/                         QUESTION_NAME                         /
/                                                               /
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|           NB (0x0020)         |        IN (0x0001)            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
/                            RR_NAME                            /
/                                                               /
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|           NB (0x0020)         |        IN (0x0001)            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                              TTL                              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|           0x0006              |        NB_FLAGS               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                          NB_ADDRESS                           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

可以和上面介绍的进行一一核对,头部信息的OPCODE0|0x5, R为0说明是请求包,0x5说明是registration,即注册信息请求;NM_FLAGSAA|TC|RD|RA|0|0|B0|0|1|0|0|0|B, 因为是请求包,所以AA=0;默认支持注册信息请求包含query和additional两部分信息,所以QDCOUNTARCOUND均为1,其余两个计数值为0。

HEADER往后是QUESTION_NAME,NB (0x0020)说明名称请求类型为NB, 请求类别通常均为0x0001, 固定不变。

再往后是TTL,固定的0x0006表征NB_FLAGS和NB_ADDRESS的总长为6个字节。其中NB_ADDRESS就是要注册的地址,而NB_FLAGS标记了这个名称的唯一性和数据包的类型(广播还是单播)。

POSTIVE NAME REGISTRATION RESPONSE

                     1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| NAME_TRN_ID |1| 0x5 |1|0|1|1|0 0|0| 0x0 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|          0x0000               |           0x0001              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|          0x0000               |           0x0000              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
/                            RR_NAME                            /
/                                                               /
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|           NB (0x0020)         |        IN (0x0001)            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                              TTL                              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|           0x0006              |        NB_FLAGS               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                          NB_ADDRESS                           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

在Registration response中,HEADER的RAA都变成了1,代表响应包;B为0代表单播包形式发送;RARD均为1,说明终端支持递归请求、注册和释放,但是会以迭代的方式进行请求、注册或释放。后面部分与请求注册的信息一致。

NODE STATUS REQUEST

Node status request也是路由器获取PC设备名称的常用方式,其请求格式如下:

                     1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| NAME_TRN_ID |0| 0x0 |0|0|0|0|0 0|B| 0x0 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|          0x0001               |           0x0000              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|          0x0000               |           0x0000              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
/                         QUESTION_NAME                         /
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|         NBSTAT (0x0021)       |        IN (0x0001)            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

与registration相比,RR_TYPE由NB变味了NBSTAT,少了Additional部分,仅仅包含请求信息。

NODE STATUS RESPONSE

Node status response的数据包格式如下:

                     1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|         NAME_TRN_ID           |1|  0x0  |1|0|0|0|0 0|0|  0x0  |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|          0x0000               |           0x0001              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|          0x0000               |           0x0000              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
/                            RR_NAME                            /
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|         NBSTAT (0x0021)       |        IN (0x0001)            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                          0x00000000                           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|            RDLENGTH           |   NUM_NAMES   |               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+               +
|                                                               |
+                                                               +
/                         NODE_NAME ARRAY                       /
+                                                               +
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
+                                                               +
/                           STATISTICS                          /
+                                                               +
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

节点状态信息很多,其中NUM_NAMES表示后面紧随的节点名称数组长度,每个数组元素包含18字节:16字节的名称和2个字节的NAME_FLAGS. NAME_FLAGS格式如下:

                                          1   1   1   1   1   1
  0   1   2   3   4   5   6   7   8   9   0   1   2   3   4   5
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| G |  ONT  |DRG|CNF|ACT|PRM|          RESERVED                 |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
  • G: 组名标志,为1代表组名,为0代表唯一名称
  • ONT: Owner Node Type
    • 00 = B node
    • 01 = P node
    • 10 = M node
    • 11 = Reserved
  • DRG: 注销标志
  • CNF: 冲突标志
  • ACT: 有效名称标志,均设置为1
  • PRM: 永久名称标志
  • RESERVED

STATISTICS字段的格式如下:

                     1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|               UNIT_ID (Unique unit ID)                        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|       UNIT_ID,continued       |    JUMPERS    |  TEST_RESULT  |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|       VERSION_NUMBER          |      PERIOD_OF_STATISTICS     |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|       NUMBER_OF_CRCs          |     NUMBER_ALIGNMENT_ERRORS   |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|       NUMBER_OF_COLLISIONS    |       NUMBER_SEND_ABORTS      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                       NUMBER_GOOD_SENDS                       |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                      NUMBER_GOOD_RECEIVES                     |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|       NUMBER_RETRANSMITS      | NUMBER_NO_RESOURCE_CONDITIONS |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  NUMBER_FREE_COMMAND_BLOCKS   |  TOTAL_NUMBER_COMMAND_BLOCKS  |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|MAX_TOTAL_NUMBER_COMMAND_BLOCKS|    NUMBER_PENDING_SESSIONS    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  MAX_NUMBER_PENDING_SESSIONS  |  MAX_TOTAL_SESSIONS_POSSIBLE  |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|   SESSION_DATA_PACKET_SIZE    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

NetBIOS 命名编码

上面所有提及的报文中都包含NetBIOS名称,这个名称是被经过编码处理的。参考以下RFC 1001 Ch14.1可知,最长16Byte的NetBIOS name被要求映射为32Byte的可逆的、半ASCII偏置编码。

The 16 byte NetBIOS name is mapped into a 32 byte wide field using a reversible, half-ASCII, biased encoding. Each half-octet of the NetBIOS name is encoded into one byte of the 32 byte field. The first half octet is encoded into the first byte, the second half- octet into the second byte, etc.

Each 4-bit, half-octet of the NetBIOS name is treated as an 8-bit, right-adjusted, zero-filled binary number. This number is added to value of the ASCII character 'A' (hexidecimal 41). The resulting 8- bit number is stored in the appropriate byte.

简单讲,就是将每个字符按ASCII拆成两部分,每部分再加上A,从而由1个字符变成两个字符。比如字符L,对应ASCII 0x4C,拆成两部分为0x40xC,然后每部分都加上字符A对应的ASCII 0x41,得到0x450x4D,对应字符EM,这样原始字符L就变成了双字符EM。下面是这个示例的详细说明图解。

NetBIOS命名编码示例

注意原始名称不足16字节可以补空格0x20或NULL0x00,这里补的是空格;如果长度超过16则需要截取前16字节。

使用Python可以快速完成编码↓

>>> orig_name = 'LITREILY'.ljust(16)
>>> encoded_name = ''.join([ chr((ord(c)>>4) + ord('A')) + chr((ord(c)&0x0F) + ord('A')) for c in orig_name ])
>>> encoded_name
'EMEJFEFCEFEJEMFJCACACACACACACACA'

在真实的数据包中,NetBIOS名称前面是0x20,代表名称固定长度32字节,名称后面填补一个0x00\0作为结束符。那么上面的LITREILY对应的数据为:

20 45 4d 45 4a 46 45 46 43 45 46 45 4a 45 4d 46
4a 43 41 43 41 43 41 43 41 43 41 43 41 43 41 43
41 00

注意:

A label length count is actually a 6-bit field in the label length field. The most significant 2 bits of the field, bits 7 and 6, are flags allowing an escape from the above compressed representation. If bits 7 and 6 are both set (11), the following 14 bits are an offset pointer into the full message to the actual label string from another domain name that belongs in this name. This label pointer allows for a further compression of a domain name in a packet.

简单说,当长度设置不为0x20,而是将最高两位置1,即设为0xc0时,代表当前名称使用指针,随后的字节指定名称所在的位置。

nbtstat

nbtstat.exe是Windows中用于查看NBT status的工具,通过它可以查看当前局域网内的NetBIOS name.

帮助信息

C:\Users\Litre>nbtstat.exe -a
显示协议统计和当前使用 NBI 的 TCP/IP 连接
(在 TCP/IP 上的 NetBIOS)。

NBTSTAT [ [-a RemoteName] [-A IP address] [-c] [-n]
        [-r] [-R] [-RR] [-s] [-S] [interval] ]

  -a   (适配器状态)    列出指定名称的远程机器的名称表
  -A   (适配器状态)    列出指定 IP 地址的远程机器的名称表。
  -c   (缓存)          列出远程[计算机]名称及其 IP 地址的 NBT 缓存
  -n   (名称)          列出本地 NetBIOS 名称。
  -r   (已解析)        列出通过广播和经由 WINS 解析的名称
  -R   (重新加载)      清除和重新加载远程缓存名称表
  -S   (会话)          列出具有目标 IP 地址的会话表
  -s   (会话)          列出将目标 IP 地址转换成计算机 NETBIOS 名称的会话表。
  -RR  (释放刷新)      将名称释放包发送到 WINS,然后启动刷新

  RemoteName   远程主机计算机名。
  IP address   用点分隔的十进制表示的 IP 地址。
  interval     重新显示选定的统计、每次显示之间暂停的间隔秒数。
               按 Ctrl+C 停止重新显示统计。

其中RemoteName既可以是ASCII形式的名称,也可以是IP地址。

使用示例一

第一个例子,在加入域DELTA的办公电脑上。3个网卡,第1个网卡连接路由器,第2个网卡连接公司网,第3个为虚拟网卡。

C:\WINDOWS\system32>nbtstat.exe -n

Router:
节点 IP 址址: [192.168.1.10] 范围 ID: []

                NetBIOS 本地名称表

       名称               类型         状态
    ---------------------------------------------
    CNSHDNI2PC074  <00>  唯一          已注册
    DELTA          <00>  组           已注册
    CNSHDNI2PC074  <20>  唯一          已注册
    DELTA          <1E>  组           已注册
    DELTA          <1D>  唯一          已注册
    ..__MSBROWSE__.<01>  组           已注册

Company:
节点 IP 址址: [172.17.144.33] 范围 ID: []

                NetBIOS 本地名称表

       名称               类型         状态
    ---------------------------------------------
    CNSHDNI2PC074  <00>  唯一          已注册
    DELTA          <00>  组           已注册
    CNSHDNI2PC074  <20>  唯一          已注册
    DELTA          <1E>  组           已注册

以太网:
节点 IP 址址: [0.0.0.0] 范围 ID: []

    缓存中没有名称

需要注意的是,名称表中每个名称都是16字节,中间空白处是空格符,最后一个字节用<>显示,里面使用16进制数表示。如CHSH2DNIPC074__<00>_代表空格,最后一个字节<00>\0. NetBIOS name可以唯一,也可以是多个设备共用一个组,唯一名称用于指定唯一一台设备,而组名如此处的DELTA是域名称,局域网加入域对应的域名就是这种组名称,也称之为工作组

使用示例二

第二个例子,我使用家用台式PC在局域网内进行了反复测试。下面是简要的抓包信息。

nbns packets

nbtstat -a RemoteName用于请求指定名称所在设备的名称表。

C:\WINDOWS\system32>nbtstat -a DZ-DN-700

eth0:
节点 IP 址址: [192.168.1.14] 范围 ID: []

           NetBIOS 远程计算机名称表

       名称               类型         状态
    ---------------------------------------------
    DZ-DN-700      <20>  唯一          已注册
    DZ-DN-700      <00>  唯一          已注册
    DIAS           <00>  组           已注册
    DIAS           <1E>  组           已注册

    MAC 地址 = E4-42-A6-18-AB-00


C:\WINDOWS\system32>nbtstat -a 192.168.1.12

eth0:
节点 IP 址址: [192.168.1.14] 范围 ID: []

           NetBIOS 远程计算机名称表

       名称               类型         状态
    ---------------------------------------------
    DZ-DN-700      <20>  唯一          已注册
    DZ-DN-700      <00>  唯一          已注册
    DIAS           <00>  组           已注册
    DIAS           <1E>  组           已注册

    MAC 地址 = E4-42-A6-18-AB-00

nbtstat -A IP_Address用于请求指定IP所在设备的名称表。

C:\WINDOWS\system32>nbtstat -A 192.168.1.12

eth0:
节点 IP 址址: [192.168.1.14] 范围 ID: []

           NetBIOS 远程计算机名称表

       名称               类型         状态
    ---------------------------------------------
    DZ-DN-700      <20>  唯一          已注册
    DZ-DN-700      <00>  唯一          已注册
    DIAS           <00>  组           已注册
    DIAS           <1E>  组           已注册
    DIAS           <1D>  唯一          已注册
    ..__MSBROWSE__.<01>  组           已注册

    MAC 地址 = E4-42-A6-18-AB-00

nbtstat -c用于查看当前设备缓存中的名称表,注意每个名称都是可以被修改的,所以需要定时刷新,同时每个名称都有其寿命,寿命耗尽后需要重新请求。

C:\WINDOWS\system32>nbtstat -c

eth0:
节点 IP 址址: [192.168.1.14] 范围 ID: []

                  NetBIOS 远程缓存名称表

        名称              类型       主机地址    寿命[秒]
    ------------------------------------------------------------
    DZ-DN-700      <20>  唯一              192.168.1.12        269
    DZ-DN-700      <00>  唯一              192.168.1.12        269

nbtstat -n用于查看当前设备的本地名称表,与第一个例子一样。

C:\WINDOWS\system32>nbtstat -n

eth0:
节点 IP 址址: [192.168.1.14] 范围 ID: []

                NetBIOS 本地名称表

       名称               类型         状态
    ---------------------------------------------
    DT-LITREILY    <20>  唯一          已注册
    DT-LITREILY    <00>  唯一          已注册
    WORKGROUP      <00>  组           已注册

nbtstat -r用于查看通过广播和经由 WINS 解析的名称

C:\WINDOWS\system32>nbtstat -r

    NetBIOS 名称解析和注册统计
    ----------------------------------------------------

    通过广播解析的     = 5
    通过名称服务器解析   = 0

    通过广播注册的   = 3
    通过名称服务器注册的 = 0

    通过广播解析的 NetBIOS 名称
    ---------------------------------------------
           DZ-DN-700      <00>
           DZ-DN-700      <00>
           DZ-DN-700      <00>
           DIAS           <00>
           DZ-DN-700      <00>

nbtstat -snbtstat -S都用于会话服务信息查询,因为没有连接所以信息为空。

C:\WINDOWS\system32>nbtstat -s

eth0:
节点 IP 址址: [192.168.1.14] 范围 ID: []

    无连接

C:\WINDOWS\system32>nbtstat -S

eth0:
节点 IP 址址: [192.168.1.14] 范围 ID: []

    无连接

nbtstat -R用于清除缓存,清除后再用-c查看就会显示没有名称了。

C:\WINDOWS\system32>nbtstat -R
    NBT 远程缓存名称表的成功清除和预加载。

C:\WINDOWS\system32>nbtstat -c

eth0:
节点 IP 址址: [192.168.1.14] 范围 ID: []

    缓存中没有名称

nbtstat -RR用于刷新经本机注册的NetBIOS名称,间隔一段时间方能刷新第二次。

C:\WINDOWS\system32>nbtstat -RR
    已经刷新经过此计算机注册的 NetBIOS 名称。

Windows 配置NetBIOS

在Windows操作系统中,可以打开某网卡的属性配置,然后选择IPv4配置的高级选项,WINS选项卡即可配置NetBIOS。默认情况是启用的,与下图所示一致。

Windows上的NetBIOS配置

wireshark 实例分析

最后来分析两个使用wireshark抓的NBNS数据包,以便更直观的理解和记忆。

Node Status

使用指令nbtstat -A 192.168.1.12向指定设备发送Node status query,使用wireshark抓包。

nbns node status

请求数据包如下:

nbns node status query

从wireshark得到的解析数据如下:

0000   e4 42 a6 18 ab 00 00 e0 4c 5a 0a 78 08 00 45 00   äB¦.«..àLZ.x..E.
0010   00 4e 1e 82 00 00 80 11 98 b2 c0 a8 01 0e c0 a8   .N.......²À¨..À¨
0020   01 0c 00 89 00 89 00 3a 5a 12 e2 b1 00 00 00 01   .......:Z.â±....
0030   00 00 00 00 00 00 20 43 4b 41 41 41 41 41 41 41   ...... CKAAAAAAA
0040   41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41   AAAAAAAAAAAAAAAA
0050   41 41 41 41 41 41 41 00 00 21 00 01               AAAAAAA..!..

NetBIOS Name Service
    Transaction ID: 0xe2b1
    Flags: 0x0000, Opcode: Name query
        0... .... .... .... = Response: Message is a query
        .000 0... .... .... = Opcode: Name query (0)
        .... ..0. .... .... = Truncated: Message is not truncated
        .... ...0 .... .... = Recursion desired: Do not do query recursively
        .... .... ...0 .... = Broadcast: Not a broadcast packet
    Questions: 1
    Answer RRs: 0
    Authority RRs: 0
    Additional RRs: 0
    Queries
        *<00><00><00><00><00><00><00><00><00><00><00><00><00><00><00>: type NBSTAT, class IN
            Name: *<00><00><00><00><00><00><00><00><00><00><00><00><00><00><00> (Workstation/Redirector)
            Type: NBSTAT (33)
            Class: IN (1)

该数据报包含一个请求,所以Questions为1,其余为0;请求包中的编码名称为*的编码后数据CKAAAAAAAAAAAAAAA...; 由于使用的是指定IP,所以是单播包;因为是NODE STATUS REQUEST, 所以QUESTION_TYPE是NBSTAT(0x0021).

从指定设备获取到的响应数据如下:

nbns node status response

从截图中已经可以清晰的看到数据格式及具体内容了,被折叠的名称信息中,DZ-DN-700<20>DIAS<1e>如下:

0000   00 e0 4c 5a 0a 78 e4 42 a6 18 ab 00 08 00 45 00   ..LZ.x........E.
0010   00 cb 53 50 00 00 80 11 63 67 c0 a8 01 0c c0 a8   ..SP....cg......
0020   01 0e 00 89 00 89 00 b7 01 c0                     .........

0020                                 e2 b1 84 00 00 00            .......
0030   00 01 00 00 00 00 20 43 4b 41 41 41 41 41 41 41   ...... CKAAAAAAA
0040   41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41   AAAAAAAAAAAAAAAA
0050   41 41 41 41 41 41 41 00 00 21 00 01 00 00 00 00   AAAAAAA..!......
0060   00 77 04                                          .w.

0060            44 5a 2d 44 4e 2d 37 30 30 20 20 20 20      DZ-DN-700    
0070   20 20 20 04 00                                       ..
Name: DZ-DN-700<20> (Server service)
Name flags: 0x0400, ONT: B-node, Name is active
    0... .... .... .... = Name type: Unique name
    .00. .... .... .... = ONT: B-node (0)
    ...0 .... .... .... = Name is being deregistered: No
    .... 0... .... .... = Name is in conflict: No
    .... .1.. .... .... = Name is active: Yes
    .... ..0. .... .... = Permanent node name: No

0070                  44 5a 2d 44 4e 2d 37 30 30 20 20        DZ-DN-700  
0080   20 20 20 20 00 04 00                                  ...
Name: DZ-DN-700<00> (Workstation/Redirector)
Name flags: 0x0400, ONT: B-node, Name is active
    0... .... .... .... = Name type: Unique name
    .00. .... .... .... = ONT: B-node (0)
    ...0 .... .... .... = Name is being deregistered: No
    .... 0... .... .... = Name is in conflict: No
    .... .1.. .... .... = Name is active: Yes
    .... ..0. .... .... = Permanent node name: No

0080                        44 49 41 53 20 20 20 20 20          DIAS     
0090   20 20 20 20 20 20 00 84 00                              ...
Name: DIAS<00> (Workstation/Redirector)
Name flags: 0x8400, Name type, ONT: B-node, Name is active
    1... .... .... .... = Name type: Group name
    .00. .... .... .... = ONT: B-node (0)
    ...0 .... .... .... = Name is being deregistered: No
    .... 0... .... .... = Name is in conflict: No
    .... .1.. .... .... = Name is active: Yes
    .... ..0. .... .... = Permanent node name: No

0090                              44 49 41 53 20 20 20            DIAS   
00a0   20 20 20 20 20 20 20 20 1e 84 00                          ...
Name: DIAS<1e> (Browser Election Service)
Name flags: 0x8400, Name type, ONT: B-node, Name is active
    1... .... .... .... = Name type: Group name
    .00. .... .... .... = ONT: B-node (0)
    ...0 .... .... .... = Name is being deregistered: No
    .... 0... .... .... = Name is in conflict: No
    .... .1.. .... .... = Name is active: Yes
    .... ..0. .... .... = Permanent node name: No

00a0                                    e4 42 a6 18 ab              .B...
00b0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
00c0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
00d0   00 00 00 00 00 00 00 00 00                        .........

Name Registration

nbns registration

NBNS名称注册请求包如下图所示:

nbns registration query

请求包格式与NAME REGISTRATION REQUEST中的一致。使用广播包方式,OPCODE为5,包含一条Questions记录和Additional记录,注册地址为发送方的IP地址192.168.1.12.

在局域网内的其它设备或路由器接收到该广播包后,即可将其名称和IP地址记录到本地缓存当中。

Reference

  1. rfc1001 - protocol standard for a nETbios service on a tcp/udp transport: concepts and methods
  2. rfc1002 - protocol standard for a nETbios service on a tcp/udp transport: detailed specifications
  3. wireshark抓包数据