前言
我决定写一个DNS嗅探器,可以这么说,只是为了好玩。 只需查看我的系统上正在解析的地址即可。 该协议很旧,应该有很多文档。 很多 但是在最有趣的时刻,所有文章都非常残缺且结局。 是的,有rfc1035,但我想用俄语并附上说明。 实际上,在积累经验和分析软件包方面,本文已经成熟。 它是为那些了解DNS是什么并且知道有请求和答案的人设计的。 对于那些想了解此协议结构的人。
本文涉及理论,然后涉及一些实践。
DNS数据包结构
+---------------------+ | Header | +---------------------+ | Question | +---------------------+ | Answer | +---------------------+ | Authority | +---------------------+ | Additional | +---------------------+
标头-DNS数据包的标头,由12个八位位组组成。
问题部分 -在此部分中,DNS客户端将查询发送到DNS服务器,通知其需要解析(解析)DNS记录的名称以及其类型(NS,A,TXT等)。 响应时,服务器将复制此信息,并在同一部分中将其返回给客户端。
答案部分 -服务器告诉客户端对请求的答案或几个响应,并在其中报告上述数据。
权威部分 -包含有关哪些权威服务器获得DNS响应部分中包括的信息的信息。
附加记录部分 -与请求相关但并非严格回答问题的附加记录。
部分中可能有几个或几个条目。 一切都取决于标题。
DNS标头结构
1 1 1 1 1 1 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | ID | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |QR| Opcode |AA|TC|RD|RA| Z | RCODE | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | QDCOUNT | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | ANCOUNT | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | NSCOUNT | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | ARCOUNT | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
ID (16位)-此字段用作唯一的交易标识符。 指示该数据包属于相同的“请求-响应”会话,并占用16位。
QR (1位)-此位用于标识数据包是请求(QR = 0)还是响应(QR = 1)。
操作码 (4位)-使用此代码,客户端可以指定请求的类型,通常值为:
- 0-标准要求
- 1-反向请求,
- 2-请求服务器状态。
- 3-15-为将来保留。
AA (1位)-该字段仅在来自服务器的DNS响应中有意义,并报告答案是否权威。
TC (1位)-如果服务器由于现有限制而无法将所有必要的信息放入数据包中,则在响应数据包中设置此标志。
RD (1位)-该一位标志在请求中设置并复制到响应中。 如果在请求中设置了标志,则意味着客户端要求服务器不要告诉他中间答案,而只返回IP地址。
RA (1位)-仅在响应中发送,并报告服务器支持递归
Z (3位)-保留且始终等于零。
RCODE (4位)-此字段用于通知客户端请求是否成功完成或出现错误。
- 0-表示请求已通过且没有错误;
- 1-错误是由于服务器无法理解请求表所致;
- 2-名称服务器操作不正确的错误;
- 3-该域中不存在允许客户端的名称;
- 4-服务器无法满足此类型的请求;
- 5-此代码表示服务器由于管理上的安全限制而无法满足客户的请求。
QDCOUNT (16位)-查询部分中的记录数
ANCOUNT (16位)-答案部分中的条目数
NSCOUNT (16位)-授权部分中的条目数
ARCOUNT (16位)-“附加记录”部分中的记录数
请求部分结构
1 1 1 1 1 1 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | | / QNAME / / / +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | QTYPE | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | QCLASS | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
QNAME-每个请求和响应记录都以NAME开头。 这是该记录链接到的域名或“所属”的域名。 它被编码为一系列标签。 在这一点上,我们应该更详细地介绍。
在我看到的文章中,他们忘了说原始DNS协议提供了两种类型的标签,它们由前两位决定:
00(标准标签)-表示剩余的6位确定标签的长度,后跟给定数目的八位位组。 因此,标签长度不能超过63个字节(例如,当试图清醒带有长标签的主机时,nslookup将显示消息“不是合法名称(标签太长)”)。 记录以代码0x00结尾。
11(压缩标签)-接下来的14位定义了到标签系列起始地址的链接。 如经验所示,它还可能包含指向另一个地址的压缩标签。 在请求中,通常没有这样的标签。
此外,标签可以包含值0x00(零长度),这意味着它是根域名(root)。
NAME的最大长度为<=255。这是为了易于实现。
QTYPE-我们要查找的DNS记录的类型(NS,A,TXT等)。
QCLASS-定义请求类(对于Internet是IN)。
响应部分结构
1 1 1 1 1 1 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | | / / / NAME / | | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | TYPE | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | CLASS | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | TTL | | | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | RDLENGTH | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--| / RDATA / / / +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
NAME-与请求部分中的QNAME格式相同。
TYPE-资源记录的类型。 定义此资源记录的格式和目的。
CLASS-资源记录类; 从理论上讲,DNS不仅可以与TCP / IP一起使用,而且可以与其他类型的网络一起使用,class字段中的代码确定了网络的类型。 基本上是Internet输入(代码0x0001)
TTL- (生存时间)-此资源记录在无响应DNS服务器的缓存中的有效存储时间。
RDLENGTH-数据字段长度(RDATA)。
RDATA-数据字段,其格式和内容取决于记录的类型。
练习
考虑来自实际请求和响应的程序包。 启动您喜欢的嗅探器并解决habrahabr.ru。
索取
让我们分析dns标头结构。
交易编号 = 0x9bce
接下来是标志。 01 00表示为二进制值0'0000'0'0'1'0'000'0000(以下,我用撇号将位分开以更好地视觉表示标志划分)
QR = 0-表示此数据包是一个请求;
操作码 = 0000-标准要求;
AA = 0-此字段仅在DNS答案中有意义,因此始终为0;
TC = 0-此字段仅在DNS答案中有意义,因此始终为0;
RD = 1-请仅返回IP地址;
RA = 0-仅由服务器发送;
Z = 000-始终为零,保留字段;
RCODE =
0000-一切顺利
QDCOUNT = 00
01-1在查询部分中的条目
ANCOUNT = 00 00-请求始终为0,答案部分
NSCOUNT = 00 00-请求始终为0,以供解答
ARCOUNT = 00 00-请求始终为0,答案部分
接下来,我们有请求和响应部分。 有一个条目。
我们拥有的第一个八位位组是0x09,将其想象为二进制值00'001001。 前两位为00,表示这是一个常规标签。 标签长度9个字节(b001001)。 “ 68 61 62 72 61 68 61 62 72”。 这些是9个字节。 它说“ habrahabr”(十六进制)。 来吧 八位位组0x02。 前两位是00,然后是长度为2个字节的常规标签。 它们是:“ 72 75”。 它写为“ ru”。 来吧 八位位组0x00。 这意味着主机条目的结尾。 我们得到了两个单词“ habrahabr”和“ ru”。 我们将它们结合在一起,得到“ habrahabr.ru”,这是我们要求的主机。
QTYPE = 0x0001-对应于类型A(主机地址请求)
QCLASS = 0x0001-对应于IN类。
答案
让我们分析dns标头结构。
交易ID = 0x9bce。 它必须与请求中的ID完全相同。
再次标记。 81 80代表二进制值1'0000'0'0'1'1'000'0000
QR = 1-表示此数据包就是答案;
操作码 = 0000-标准要求;
AA = 0-服务器对域不具有权威性;
TC = 0-所有信息都放在一个包装中;
RD = 1-请仅返回IP地址;
RA = 1-服务器支持递归;
Z = 000-始终为零,保留字段;
RCODE =
0000-一切顺利
QDCOUNT = 00
01-1在查询部分中的条目
ANCOUNT = 00 01-现在我们的答案中只有一个
NSCOUNT = 00 00-请求始终为0,以供解答
ARCOUNT = 00 00-请求始终为0,答案部分
接下来,我们有请求和响应部分。 有两个条目。 请求的一条记录,另一条包含答案的记录。 我不会绘制请求部分,它将始终与请求包中的1v1相同。 继续回答部分。
第一个八位位组是0x09,前两位是00,这表示长度为9个字节的常规标签。 读取9个字节,得到“ HABRAHABR”。 接下来是0XC0(b11000000)。 如您所见,前两位的值为11,这意味着我们有一个压缩的链接。 我们查看接下来的8位(我们有0x16(b00010110)),并与当前的最后6位合并。 我们得到b00000000010110。 链接到DNS数据包的第22个字节(02 72 75 00)。 从八位位组开始,我们再次获得标签。 按照相同的规则。 我们得到它。 我们将收到的所有信息组合在一起,结果显示为“ HABRAHABR.ru”。这是主机,将对其进行进一步讨论。
QTYPE = 0x0001-对应于类型A(主机地址请求)
QCLASS = 0x0001-对应于IN类。
TTL = 0x00000c90-数据正常运行时间3216秒。
RDLENGTH =
0x0004-数据长度为4个八位位组。
RDATA =“ b2 f8 ed 44”。
如前所述,格式和内容取决于录制的类型。 我们拥有的记录类型为“ A”。 因此,要获取IP,我们必须读取4个字节。 每个字节都是IP地址的相应八位位组,以十六进制表示。
我们获得IP:b2.f8.ed.44或“ 178.248.237.68”。 需要接收什么。
例如,对于NS类型,格式为:
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ / NSDNAME / / / +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
然后,我们将根据QNAME的规则读取名称。