这个网络协议使用什么校验和算法?

逆向工程 linux 协议 联网 线鲨
2021-06-27 17:13:29

到目前为止,我一直在通过使用 tcpdump、Wireshark 和一些 Python 包(scapy、numpy)监控流量来解决一个未记录的基于 TCP 的协议,但我认为这是一些某种校验和/哈希/CRC。

观察客户端二进制文件,它发送它的第一个“hello”类消息,从服务器获取一些基本信息(服务器版本等)的响应,然后在后续消息中发送一组命令。这是我无法弄清楚的第二个和后续消息类型。下面是一个例子:

2f c6 00 f2 00 36 01 08 56 cc 80 c4 00 00 00 00 00 00 00 00 01 04 72 6f 6f 74 00 68 6f 73 74 6e 61 6d 65 00 6c 6d 67 72 64 00 2f 64 65 76 2f 70 74 73 2f 30 00 00

前 22 个字节是我所说的标题,因为它是一个固定长度的段,之后它只是以空字符结尾的 ASCII 字符串。该标头的字节:

  • 第一:总是 0x2f
  • 第二:看起来像是对消息字节的总和,但我不是 100% 的那部分
  • 第三和第四:对我来说完全是个谜以及这篇文章的原因
  • 第五:总是 0x00
  • 6th:消息中的总字节数
  • 7-8 号:总是 0x0108
  • 9th-12:四字节unix时间戳
  • 13-20 日:永远 0x00
  • 21-22 日:总是 0x0104

我的问题是关于第 3 个和第 4 个字节:客户端使用什么方法来提出这些值,和/或我应该尝试自己弄清楚什么?

我可以通过改变主机名、用户名、系统时间和客户端运行的 TTY 来测试各种值,因此我可以在需要时轻松收集示例数据包。到目前为止,我已经通过查看这些字节在各种输入中所取值的直方图,尝试了一种统计方法。我还没有从中发现太多,除了:

  • 第三个字节跨度 0 - 63(即它的前两位总是 0)
  • 第 4 个字节跨度 0 - 255
  • 两个字节都没有取值 10 - 13(在这些特定值的其他均匀分布的直方图中存在间隙)

另一种方法是给它一些特定的主机名值来查看它的行为。这些东西确实会改变这两个神秘字节的值:

  • 切换消息字节的顺序
  • 反转两个不同字节中特定位置的位

然后我尝试处理我读过的不同类型的校验和(BSD 和 Fletcher 的变体)以及 CRC(尝试使用 15 位多项式来生成 14 位余数,但是,这真的是已经完成的事情了吗? ) 到目前为止没有运气。有任何想法吗?

1个回答

知道了!

该算法是具有以下参数的 CRC,作为 big-endian short 存储在这两个字节中:

  • 多项式:0x2e97
  • 异或值:0
  • 异或输出值:0
  • 反射输入:真
  • 反映:真实

为了弄清楚,我使用了我的数据包捕获数据CRC RevEng和一些 shell 脚本将它们粘合在一起。然后我使用pycrc 即时计算检查值,以使我自己的网络请求在服务器上检查正常。

我认为我的问题得到了回答,因为这种方法可靠地让服务器确认我的自定义请求,但是 Jason 和 ebux 推荐的逆向工程二进制文件看起来仍然是我能够进一步确定它的唯一方法。例如,有一些不同的 CRC 适合我正在处理的所有数据。我通过使用 gdb 和 objdump 学到了很多东西,但很快就被我搞糊涂了。时间会证明我是否只是推迟了不可避免的事情,我想。

更多细节:

首先在 Wireshark 中,我选择了一个很好的数据包范围,一些具有相同的长度,一些具有不同的长度。对于每个我将原始二进制文件导出到一个文件中。(在 Packet Details 框架中,右键单击 Data 显示,单击“Export selected packet bytes...”。)因为我不确定字节是如何组织的,但我很好地猜测了哪些是CRC,我制作了文件的修改版本,其中附加了检查字节而不是开头,dd下面显示了一些笨拙的用法。最终起作用的变体是以 reveng 可以理解的小端格式将字节放在末尾。在 bash shell 中:

dd if=example bs=1 skip=4 > example_crc_reverse
dd if=example bs=1 count=1 skip=3 >> example_crc_reverse
dd if=example bs=1 count=1 skip=2 >> example_crc_reverse

然后将这些修改过的文件输入到 reveng 中。因为我什至不确定可能的 CRC 多项式的宽度,所以我从 1 到 16 循环以查看它会找到什么。再次在 bash 中:

for width in $(seq 1 16); do reveng -w $width -s -f example*_crc_reverse; done

即使有许多数据包,它也会找到许多适合数据的非标准 CRC,但上面的一个是不需要奇怪异或值的最高阶的。在 reveng 输出中发现的那些:

width=1  poly=0x1  init=0x0  refin=false  refout=false  xorout=0x0  check=0x1  name=(none)
width=1  poly=0x1  init=0x1  refin=false  refout=false  xorout=0x1  check=0x1  name=(none)
width=1  poly=0x1  init=0x0  refin=true  refout=true  xorout=0x0  check=0x1  name=(none)
width=1  poly=0x1  init=0x1  refin=true  refout=true  xorout=0x1  check=0x1  name=(none)
width=3  poly=0x5  init=0x0  refin=true  refout=true  xorout=0x0  check=0x0  name=(none)
width=4  poly=0x7  init=0x0  refin=true  refout=true  xorout=0x0  check=0xb  name=(none)
width=4  poly=0x7  init=0xd  refin=true  refout=true  xorout=0xb  check=0xb  name=(none)
width=10  poly=0x381  init=0x000  refin=true  refout=true  xorout=0x000  check=0x032  name=(none)
width=11  poly=0x083  init=0x000  refin=true  refout=true  xorout=0x000  check=0x032  name=(none)
width=11  poly=0x083  init=0x781  refin=true  refout=true  xorout=0x40f  check=0x032  name=(none)
width=13  poly=0x058d  init=0x0000  refin=true  refout=true  xorout=0x0000  check=0x1c1f  name=(none)
width=14  poly=0x2e97  init=0x0000  refin=true  refout=true  xorout=0x0000  check=0x3076  name=(none)
width=14  poly=0x2e97  init=0x258d  refin=true  refout=true  xorout=0x2c69  check=0x3076  name=(none)

(我认为没有 xorin/xorout 步骤的 14 位最适合使用,但我没有一个很好的理由。)在 Python 中,将二进制请求准备为整数字节值列表,pycrc可以这样计算校验值:

crc = pycrc.Crc(width=14, poly=0x2e97, reflect_in=True, xor_in=0, reflect_out=True, xor_out=0)
crc_val = crc.table_driven(binary)

crc_val从 pycrc计算出来的已经以服务器期望的字节序存储,所以它可以按原样放置在消息的开头。