挖井

类库大魔王的挖井日记

挖一口属于自己的井


SSR混淆协议Go版移植手记(二)

虽然Go实现的代码是照着C#版的代码翻译过来了,但是引入了很多问题,经过几天的仔细排查,现在tls1.2_ticket_auth混淆功能终于完成了!稍微记录一点需要注意的点,以便日后翻阅。

原本的ss-go的实现中,在客户端侧接收数据,先读取指定长度的iv,接着会依照buffer的大小读取最多那么多的字节,然后解密成同样数量的字节返回给用户侧socket。在加入混淆的实现中,流程有所变化:

  1. 先随便读一段数据,可以是1K、2K、4K等。
  2. 调用混淆decode,一般说来第一次decode后会要求send back,把0字节数据encode一下立马发送回给服务端,然后返回,即继续读取数据。
  3. 之后再读取的数据被decode后,一般不会再要求send back了,这时跟原版的操作一样了,最开始的几个字节是iv,之后是加密的数据,可以被解密。
  4. 解密后的数据长度不一定,可能很短,十几个字节,也可能很长,好几KB,需要自己再分片发给用户侧socket。

混淆decode是个很简单的过程:

  1. 每个session(即客户端与服务商的每个tcp连接)维护一个handshake status,如果handshake status不为8,则进行hmac校验,服务端对数据流第11个字节开始,总共22个字节进行hmac计算,把它计算好的hmac结果前10个字节放在数据流的第33个字节开始的位置,客户端根据这个信息校验它收到的数据。如果校验通过,则要返回一个标识,要求外面send back(上面第2点)。
  2. 如果handshake status等于8,则按照3字节的magic number 0x170303+2字节的数据长度+前面2字节指出的长度的有效净荷数据,这种规则把所有有效净荷数据提取出来,合成原始的加密数据。这里要注意网络传输数据分片可能把数据在任意一个位置截断,所以要合包再分包。之后便是上面第3点的过程了。

混淆encode的过程比较繁琐:

  1. session最开始的handshake status肯定是0,这时构建一个伪造的TLS协议头,中间除了长度,SNI信息和hmac结果以及一些随机数字节是动态生成的外,其余的字节都是固定的,只要摆好位置即可。然后把handshake status改为1
  2. 如果handshake status1,如果需要被encode的数据长度大于0,就在数据前加入3字节的magic number 0x170303+2字节的数据长度,然后保存起来下次有用。如果encode数据长度是0,就把前面保存起来的那些数据取出来,前面加上11字节固定的协议头+22字节的随机数据+10字节的hmac结果,然后一起作为被encode后的结果返回。再把handshake status改为8
  3. 如果handshake status8,则把要被encode的数据随机截断成n个小于4KB的包,每个包前面插入3字节的magic number 0x170303+2字节的数据长度,最后连接成一块作为encode后的结果返回。

另外,前面提到的所有hmac过程,都使用SHA1算法,使用的key是原版ss协议中的key+每个session分别生成一个32字节长的随机数串,经过标准的hmac计算后截取前10个字节使用。

基本上就是这个过程。

本文地址:

https://minidump.info/blog/2016/12/port-ssr-to-ss-go-2/

上一篇

SSR混淆协议Go版移植手记(一)

这几天断断续续地移植着SSR的一个混淆插件tls1.2_ticket_auth,至今仍然没能正常工作起来,忧郁啊。稍微记录一点读ss-go和ssr-csharp/ssr-libev的代码所得,以便日后翻阅。本身ss协议的定义已经有公开的文档了,很简单,每个客户端连接服务端时,最开始是一串随机数...…

Shareware 全文阅读
下一篇

SSR混淆协议Go版移植手记(三)

本来实现了tls1.2_ticket_auth混淆方法后,我觉得够用了,便想直接去做auth_sha1_v4协议了,但是调试了两天,没调通,很沮丧,还觉得没什么头绪。至少在调试混淆方法时,我用C#版做对比,通过抓包可以明确比较我的程序混淆结果是否正确,但协议就要麻烦些,协议是将加密前的数据预处...…

Shareware 全文阅读