在阅读此文档https://wiki.wireshark.org/CaptureFilters时,我发现了这一行:
tcp[((tcp[12:1] & 0xf0) >> 2):4]
它计算出 TCP 标头长度,但我不知道它是如何工作的(详细)。
有人可以解释一下吗?
在阅读此文档https://wiki.wireshark.org/CaptureFilters时,我发现了这一行:
tcp[((tcp[12:1] & 0xf0) >> 2):4]
它计算出 TCP 标头长度,但我不知道它是如何工作的(详细)。
有人可以解释一下吗?
我想您是从“捕获 HTTP GET 请求”过滤器示例中提取的。
首先,一些符号:
var[n]
表示 var 的第 n 个字节。var[n,c]
表示从偏移量 n 开始的 c 个 var 字节,例如var[3:4]
将返回 var 的 3、4、5、6 个字节。&
是按位与运算。>>
表示按位右移。那么,我们有什么?
tcp[((tcp[12:1] & 0xf0) >> 2):4]
让我们将其解构为各个部分:
tcp[12:1]
在偏移量 12 处获取 TCP 段的 1 个字节(即包含包头的数据包)。从结构中我们可以看出,偏移量 12 (0xC) 是数据偏移量字段。其定义如下:
数据偏移量(4 位)以 32 位字指定 TCP 标头的大小。标题的最小大小为 5 个字,最大为 15 个字,因此最小大小为 20 个字节,最大为 60 个字节,标题中最多允许 40 个字节的选项。该字段的名称源于它也是从 TCP 段开始到实际数据的偏移量。
好的。不过,这个字段只有 4 位,tcp[12:1]
占用一个完整的字节(8 位)。我们只想要前四个。
tcp[12:1] & 0xf0
获取该数据偏移字节并使用常量 0xF0 应用按位与运算。这通常称为屏蔽操作,因为常量中设置为 0 的任何位也将在输出中设置为零。在二进制中考虑这一点更容易。0xF0 只是 11110000,由于x & 0
任何位 x 始终为 0,并且x & 1
任何位 x 始终为 x,因此 0xF0 中的零位将“关闭”或屏蔽给定位,但不理会其余位。在这种情况下,如果我们想象它tcp[12:1]
是 10110101,结果将是 10110000,因为最后四位被屏蔽为零。这里的想法是,由于数据偏移字段只有 4 位长,但我们有 8 位,我们屏蔽掉不相关的位,所以我们只有前 4 位。
这里的问题是,从数字上讲,我们的 4 位在字节的“顶部”一侧。这意味着我们有 11010000 而不是只有 1101(我们的数据偏移位)。如果我们只想获得数据偏移字段的“原始”值,我们将右移四个位置。10110000 >> 4 = 1101
,即我们丢弃“底部” 4 位并将顶部 4 位右移。但是,在这种情况下,您会注意到过滤器仅移动了 2 个位置,而不是 4 个。
这就是我们要回顾数据偏移字段定义的地方:它以32 位字而不是字节为单位指定标头的大小。因此,如果我们想知道标头的字节长度,我们需要将其乘以 4。事实证明,按位左移 1 与将数字乘以 2 相同,按位左移 - shift of 2 相当于一个数乘以 4。
现在,把它和我们已经看到的结合起来:>> 4
如果你想得到数据偏移的原始值,在过滤器中是有意义的,但是我们想把它乘以 4,这相当于左移 ( << 2
),它抵消了该右移的一部分,导致>> 2
.
因此,(tcp[12:1] & 0xf0) >> 2
提取数据偏移字段并将其乘以 4 以获得 TCP 标头的大小(以字节为单位)。
但是等等,还有更多!. 在您提供的过滤器中,我们还需要再进行一项操作:
tcp[((tcp[12:1] & 0xf0) >> 2):4]
如果我们使用中间变量,这更容易看:
$offset = ((tcp[12:1] & 0xf0) >> 2)
tcp[$offset:4]
这样做是获取TCP 标头之后的前 4 个字节,即负载的前 4 个数据字节。在您从中提取的过滤器中,他们正在使用此过滤器查找 HTTP GET 请求:
port 80 and tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x47455420
该0x47455420
常量实际上是 ASCII 字节的数字编码GET
(最后一个字符是空格),其中这些字符的 ASCII 值是 0x47、0x45、0x54、0x20。
那么,这是如何全面运作的呢?它从 TCP 标头中提取 4 位数据偏移字段,将其乘以 4 以计算标头的大小(以字节为单位)(这也是数据的偏移量),然后在此偏移量处提取 4 个字节以获得前 4字节的数据,然后将其与“GET”进行比较以检查它是否为 HTTP GET。