对西厢的分析

对于这个项目的简介可见这里:http://code.google.com/p/scholarzhang/wiki
我的测试环境是基于ubuntu 9.10。kernel是2.6.31-20-generic。很失望的是,我并没成功。最终仅仅是偶尔能成功打开我要打开的网页。然后就开始抓包,看代码。
在没有西厢的情况下,正常流程如下:

1 0.000000 192.168.0.101 74.86.123.131 TCP 36271 > http [SYN] Seq=0 Win=5840 Len=0 MSS=1460 TSV=383657 TSER=0 WS=7
2 0.254851 74.86.123.131 192.168.0.101 TCP http > 36271 [SYN, ACK] Seq=0 Ack=1 Win=64672 Len=0 MSS=1460 TSV=1543589264 TSER=383657 WS=11
3 0.254936 192.168.0.101 74.86.123.131 TCP 36271 > http [ACK] Seq=1 Ack=1 Win=5888 Len=0 TSV=383682 TSER=1543589264
4 0.255126 192.168.0.101 74.86.123.131 HTTP GET / HTTP/1.0

因为GET /开始就是http的协议里,里面包含Host和URL,根据这两个,就可以把IP斩断.此时防火墙向tcp连接的两端发RST协议。
从http server那边会看到这样的包
5 0.267683 74.86.123.131 192.168.0.101 TCP http > 36271 [RST, ACK] Seq=1 Ack=2261781716 Win=405504 Len=0
6 0.267745 74.86.123.131 192.168.0.101 TCP http > 36271 [RST, ACK] Seq=1461 Ack=2261781716 Win=405504 Len=0
7 0.267762 74.86.123.131 192.168.0.101 TCP http > 36271 [RST, ACK] Seq=4381 Ack=2261781716 Win=405504 Len=0
8 0.508519 74.86.123.131 192.168.0.101 TCP http > 36271 [RST] Seq=1 Win=0 Len=0
9 0.509531 74.86.123.131 192.168.0.101 TCP http > 36271 [RST] Seq=1 Win=0 Len=0
然后在4次交互后,连接关闭
10 1.542711 64.233.189.113 192.168.0.101 TCP http > 53229 [FIN, ACK] Seq=1 Ack=1 Win=128 Len=0 TSV=3631064451 TSER=359772
11 1.575568 192.168.0.101 64.233.189.113 TCP 53229 > http [ACK] Seq=1 Ack=2 Win=57 Len=0 TSV=383815 TSER=3631064451
12 12.193255 192.168.0.101 64.233.189.113 TCP 53229 > http [FIN, ACK] Seq=1 Ack=2 Win=57 Len=0 TSV=384876 TSER=3631064451
13 12.324609 64.233.189.113 192.168.0.101 TCP http > 53229 [ACK] Seq=2 Ack=2 Win=128 Len=0 TSV=3631075234 TSER=384876
编译西厢,然后在防火墙规则中加入:
iptables -A INPUT -p tcp --sport 80 --tcp-flags FIN,SYN,RST,ACK SYN,ACK -m state --state ESTABLISHED -j ZHANG
然后再测试
tcp建立的过程时候会有所改变。在第三步,客户端没有发ack,而是发一个错误的FIN(seq=是对方刚刚ACK的SEQ)
3 0.255854 192.168.0.101 208.43.60.8 TCP 37342 > http [FIN] Seq=1 Win=8388480 Len=0
FIN后面是2条看起来像keepalive的东西。作者的本意是构造一个ACK正确但是SEQ错误的包,以促使server发RST。另外一条貌似是正儿八经的ACK,靠它来完成真正的TCP握手。
4 0.255874 192.168.0.101 208.43.60.8 TCP [TCP Keep-Alive] 37342 > http [ACK] Seq=1 Ack=0 Win=8388480 Len=0
5 0.255924 192.168.0.101 208.43.60.8 TCP [TCP Keep-Alive] 37342 > http [ACK] Seq=1 Ack=1 Win=5888 Len=0 TSV=491078 TSER=3683592554
然后是真正的请求。
6 0.256090 192.168.0.101 208.43.60.8 HTTP [TCP Out-Of-Order] GET / HTTP/1.0
Transmission Control Protocol, Src Port: 37342 (37342), Dst Port: http (80), Seq: 1, Ack: 1, Len: 96
Client依然会收到RST ACK,但是没有RST。这三条RST ACK应该不是从server发来的,而是从防火墙来的。如果西厢模块工作正常,应该是没有这几个RST ACK的
7 0.266096 208.43.60.8 192.168.0.101 TCP http > 37342 [RST, ACK] Seq=1 Ack=2599054141 Win=792576 Len=0
8 0.266150 208.43.60.8 192.168.0.101 TCP http > 37342 [RST, ACK] Seq=1461 Ack=2599054141 Win=792576 Len=0
9 0.266162 208.43.60.8 192.168.0.101 TCP http > 37342 [RST, ACK] Seq=4381 Ack=2599054141 Win=792576 Len=0
没有RST可能是因为防火墙看到FIN的时候,以为客户端已经把tcp断了,所以不需要RST。
而作为server,它会忽略掉那个错误FIN包(因为它的SEQ不对),处理GET请求,然后返回结果。
10 0.511049 208.43.60.8 192.168.0.101 TCP http > 37342 [ACK] Seq=1 Ack=97 Win=655360 Len=0 TSV=3683592809 TSER=491078
11 0.512343 208.43.60.8 192.168.0.101 TCP [TCP Retransmission] [TCP segment of a reassembled PDU]
Transmission Control Protocol, Src Port: http (80), Dst Port: 37342 (37342), Seq: 1, Ack: 97, Len: 1440
13 0.512440 208.43.60.8 192.168.0.101 HTTP [TCP Retransmission] HTTP/1.0 200 OK (text/html)
Transmission Control Protocol, Src Port: http (80), Dst Port: 37342 (37342), Seq: 1441, Ack: 97, Len: 1154
client会挨个挨个的ack
12 0.512418 192.168.0.101 208.43.60.8 TCP 37342 > http [ACK] Seq=97 Ack=1441 Win=8832 Len=0 TSV=491104 TSER=3683592809
14 0.512459 192.168.0.101 208.43.60.8 TCP 37342 > http [ACK] Seq=97 Ack=2595 Win=11648 Len=0 TSV=491104 TSER=3683592809
然后client要FIN这个连接
15 0.518522 192.168.0.101 208.43.60.8 TCP 37342 > http [FIN, ACK] Seq=97 Ack=2595 Win=11648 Len=0 TSV=491105 TSER=3683592809
服务器选择了RST的方式结束。(这点很奇怪)
16 0.531376 208.43.60.8 192.168.0.101 TCP http > 37342 [RST, ACK] Seq=2595 Ack=97 Win=1112064 Len=0
17 0.766456 208.43.60.8 192.168.0.101 TCP http > 37342 [RST] Seq=2595 Win=0 Len=0
18 0.775202 208.43.60.8 192.168.0.101 TCP http > 37342 [RST] Seq=2595 Win=0 Len=0
关键的代码都在xt_ZHANG.c里面,这个文件一共也就210行。主要就做了2件事情:发那个FIN包,并立刻发一个错误的ACK包。我十分不理解后面那个ACK包是否必要。
总的来说,对于那种根本收不到SYN-ACK的网站,这种方式是完全无用的。它瞅准的弱点是对于任何IDS系统,在检测到RST之后,剩下的工作很不好做。它要么认为此时连接真的断了,要么就必须继续检测很长一段时间,来看双方是否仍然有数据交互。但是,这个时间多长才算合适呢?
原理很简单,代码一共也就这么短,所以如果你想把它重新在BSD、solaris下实现,都不是难事儿。win我不了解,难度完全取决于微软到底把API开放到多深。

此博客中的热门博文

在windows下使用llvm+clang

少写代码,多读别人写的代码

tensorflow distributed runtime初窥