原文链接

一项持续时间比张某还长的遗留研究。下面是它的开发研究简介,细节很多,非研究者可以略去不看。

HTTP URL/深度关键词检测

GFW的TCP协议阻断方式

GFW只根据单向的报文还原通信内容进行协议分析和关键词判断。并且在还原通信内容时,并不检查ack域的正确性,这也符合其“better is worse的设计哲学”,对于seq重叠的包,GFW的策略是忽略后来的包,因此利用GFW的这种流重组特性对其进行欺骗也十分容易。

一旦GFW根据还原出的内容检测到关键词,会根据触发关键词的包和关键词类型发送type1的RST或者type2的RST/ACK,在《入侵防御系统的评测和问题》中已经介绍,type2类型首次阻断中会先发送一组三个RST/ACK,序列号依次加1460、2920。type1与type2有所不同的是,type1首次阻断发送的RST的seq是关键词结束位置所在的包的ack,type2的seq则可能取为在此之前若干个包的ack,这可能与GFW处理报文的buffer大小和更新方式有关。在有些地区,单向发送内容触发type2关键词之后本地无法收到RST/ACK(对方可以收到),除非对方回复过任何设置了ACK选项的tcp包,另一些地区则本地总可以收到RST/ACK,原因还不清楚。这些问题希望有兴趣的读者自行研究。

特定情况下GFW还会伪造通信,例如type2的继发阻断中发送伪造的SYN/ACK企图劫持连接(为什么功能没有开发完全?),再如GFW的邮件检测模块对smtp协议的阻断会先伪造对方发送一条错误信息,再进行阻断。

URL关键词的检测

GFW的HTTP关键词检测模块的具体细节不是这里的重点,这里只回顾一些有关关键词检测的内容:

  • GFW有两种不相关的type、GFW结点的划分与TCP包的源端口无关、GFW时常有结点不工作、type1不工作的节点和type2不工作的结点不同。
  • URL关键词同样也有type1、type2之分。
  • 即使单向发送HTTP询问在一些地区可能无法收到GFW的type2 RST/ACK,实际上确实触发了关键词,会有90秒继发封锁。
  • 如果询问的格式是GET http://url HTTP/1.1\r\n\r\n,GFW进行关键词测试串的就是url。
  • 如果询问的格式是GET /url HTTP/1.1\r\nHost: hostname\r\n\r\n,GFW进行关键词测试的串就是.hostname/url。
  • GFW的HTTP URL关键词有普通的字符串关键词还有string1 && string2 && string3型的关键词,例如.google.com && great && firewall。只要url中包含这三个子串,无论出现的顺序如何,都会触发GFW。令人疑惑的是,string1*string2这种关键词匹配(在url中string1、string2顺序出现)判断实现起来要比string1 && string2容易得多,而目前已知的所有非普通关键词都是string1 && string2形式而不是string1string2形式,是否存在string1string2形式的关键词需要知道更多的URL关键词。

判断一个URL是否包含关键词的方法十分明了,选择一个跟本地IP分别在GFW两端的目标IP da,再任意选择一个不等于___的目标端口dp。对(da, dp)单向发送s;a;pa;s。(s = SYN; a = ACK; p = PSH)。为保证GFW按照顺序看到可以将每个包重复发送多遍,两组包间隔一定时间发送。如果触发了type1关键词可以收到GFW的r(RST),触发type2关键词可以收到GFW的sa。无sa或者无r,并不能说明不包含关键词,可能是GFW的相应结点不工作了。这时应该可以对此(da, dp)发送www.youtube.com来尝试触发。触发,说明原本确实没有关键词;否则说明GFW的相应类型的相应结点不工作了。

对关键词进行手工求解未免太过低效,利用GFW的单向报文检测特性,可以用来进行关键词检测的(da, dp)是几乎无穷多而且便于寻找的,让程序自动检测关键词非常可行。

  • 情况1、假定只有一个关键词。
    • 假设str长len,发起len个询问,第i个询问检测str去掉第i个字符形成的字符串。如果有阻断,说明这个字母可以去掉,否则说明这个字母不能去掉。询问完全并行。拿到所有结果之后可以立刻求出关键词。
    • 理想情况下只要1单位时间。
  • 情况2、假定所有关键词都是普通关键词,求出所有最小关键词。
    • 这种情况下可以进一步假定关键词是互不包含的子串,所以关键词可以定义前后关系。假设str长度为len,发起len个询问,第i个询问去掉前len + 1 - i个字符。求出最大的j使得在去掉前j个字符的情况下仍然有关键词。现在只需要求出从第j+1个字符开始的关键词,发起len - j个询问,第i个询问去掉后len - j + 1 - i个字符。现在求出最大的k使得去掉后k个字符仍然有关键词就完了。最后一个关键词就是str[j + 1..len - k],共花去2单位时间、询问。接下来处理str[1..len - k - 1] with hint: “开头位置 <= j”,仍然是倒着测,有助于及时break。
    • 理想情况下时间等于关键词数目。
    • 为了减少(da, dp)的报废数目(短时间报废过多会被迫等待继发封锁结束),可以使用“二级索引”的办法:分sqrt(len)块,然后再精确到块内的位置。这样只是时间*2。
  • 情况3、&&型关键词、可能有多个,求出所有关键词。
    • 即使是求出其中的某一个关键词都是必须串行的,要花len的时间,难以忍受。但是实践中,.google.com && 和 search && 和 q= 有可能同时是关键词,而询问作为www.google.com/search?q=出现。由于此问题可由3-SAT规约到,是NPC问题,认为不可完成,所以从其它方面考虑:
    1. 根据经验假设只会出现2个&&,但实现带任意多&&的关键词的匹配算法上并不困难,GFW应该有相应的计算能力。
    2. 令s为{1..len}的一个随机置换,顺次考察s[i],如果去掉后仍然触发就去掉,不再触发就保留。最后可以得到一个关键词。多路并行应该就可以求出所有关键词了。尽管到达每个关键词的概率不均匀,实践效果应该可以接受。

深度检测关键词和其他关键词检测

GFW对所有通信进行了全文关键词检测,并且可以对gzip、deflate压缩的报文实现实时解压缩判断。进行这种关键词检测,需要事先准备好被测试文本。如果是某网页或者某文件含有深度检测关键词,需要将相应文件下载到本地。与测试URL关键字不同之处就是文件可以非常大。上面的方法几乎行不通。但我们希望先缩小关键词的寻找范围。希望根据GFW的r或者ra包的序列号来定位出现关键词的两个包,这样被检测字符串的长度就被缩小到了不到3000字节,就可以套用上面的方法了。

由于GFW的r和ra的seq是取自本地发出包的ack,只要对每个包按照发送顺序设置ack。

坑了(事实上)。