概要

用于方便地产生各种协议接口。实现为在原来的协议外套一层编码和解码接口,不但可以伪装成其它协议流量,还可以把原协议转换为其它协议进行兼容或完善(但目前接口功能还没有写完,目前还在测试完善中),需要服务端与客户端配置相同的协议插件。插件共分为两类,包括混淆插件和协议定义插件。

现有插件介绍

混淆特性

name RTT encode speed bandwidth anti replay attack cheat QoS anti analysis
plain 0 100% 100% No 0 /
http_simple 0 20%/100% 20%/100% No 90 70
http_post 0 20%/100% 20%/100% No 100 70
random_head (X) 1 100% 85%/100% No 0 10
tls1.2_ticket_auth 1 98% 75%/ 95% Yes 100 90

说明:

  • 20%/100%表示首包为20%,其余为100%速度(或带宽),其它的 RTT 大于0的混淆,前面的表示在浏览普通网页的情况下平均有效利用带宽的估计值,后一个表示去除握手响应以后的值,适用于大文件下载时。
  • RTT 表示此混淆是否会产生附加的延迟,1个RTT表示通讯数据一次来回所需要的时间。
  • RTT 不为0且没有 anti replay attack 能力的混淆,不论协议是什么,都存在被主动探测的风险,即不建议使用random_head。 RTT 为0的,只要协议不是 origin,就没有被主动探测的风险。当然由于原协议本身也存在被主动探测的风险,在目前没有观察到主动探测行为的情况下,暂时不需要太担心。
  • cheat QoS 表示欺骗路由器 QoS 的能力,100表示能完美欺骗,0表示没有任何作用,50分左右表示较为严格的路由能识别出来。
  • anti analysis 表示抗协议分析能力,plain 的时候依赖于协议,其它的基于网友反馈而给出的分值。值为100表示完美伪装。

协议特性

假设 method = "aes-256-cfb"
以下所有协议与均 anti CPA

name RTT encode speed bandwidth anti CCA anti replay attack anti MITM detect anti packet length analysis
origin 0 100% 99% No No No 0
verify_deflate 0 30% 97%~110% No No No 6
verify_sha1 (X) 0 85% 98%/99% No No No 0
auth_sha1 (X) 0 95% 97% No Yes No 4
auth_sha1_v2 (X) 0 94% 80%/97% No Yes No 10
auth_sha1_v4 0 90% 85%/98% No Yes No 10
auth_aes128_md5 0 80% 70%/98% Yes Yes Yes 10
auth_aes128_sha1 0 70% 70%/98% Yes Yes Yes 10
auth_chain_a 0 70% 75%/98% Yes Yes Yes 15
auth_chain_b 0 68% 70%/98% Yes Yes Yes 20
auth_chain_c 0 69% 70%/98% Yes Yes Yes 20
auth_chain_d 0 70% 70%/98% Yes Yes Yes 20

说明:

  • 以上为浏览普通网页(非下载非看视频)的平均测试结果,浏览不同的网页会有不同的偏差
  • encode speed仅用于提供相对速度的参考,不同环境下代码执行速度不同
  • verify_deflate的bandwidth(有效带宽)上限110%仅为估值,若数据经过压缩或加密,那么压缩效果会很差
  • verify_sha1的bandwidth意为上传平均有效带宽98%,下载99%
  • auth_aes128_md5的bandwidth在浏览普通网页时较低(为了较强的长度混淆,但单个数据包尺寸会保持在 TCP MSS 以内,所以其实对网速影响很小),而看视频或下载时有效数据比率比auth_sha1要高,可达97%,所以不用担心下载时的速度。auth_chain_a及auth_aes128_md5类似
  • 如果同时使用了其它的混淆插件,会令bandwidth的值降低,具体由所使用的混淆插件及所浏览的网页共同决定
  • 对于抗包长度分析一列,满分为100,即0为完全无效果,5以下为效果轻微,具体分析方法可参阅方校长等人论文
  • 对于抗包时序分析一列,方校长的论文表示虽然可利用,但利用难度大(也即他们还没能达到实用级),目前对此也不做处理

混淆与协议配置建议

  • 协议推荐:协议用auth_chain_*最佳,此时推荐不使用加密(设置为none),混淆随意
  • 加密选择:若协议用auth_chain_*,那加密用none(但不代表密码可以不写或两边不匹配),若协议不是auth_aes128_md5auth_aes128_sha1,那么不能使用rc4加密(可用rc4-md5)。这时加密可以在rc4-md5、salsa20、chacha20-ietf三个里面选择(rc4-md5可换为aes系列,salsa20可换为chacha20或bf-cfb),如果使用SSR还可特别选择rc4-md5-6。
  • 混淆推荐:如果QoS在你的地区明显,混淆建议在http_simpletls1.2_ticket_auth中选择,具体选择可以通过自己的试验得出。如果选择混淆后反而变慢,那么混淆请选择plain。如果你不在乎QoS,但担心你的个人vps能不能持久使用,那么混淆选择plaintls1.2_ticket_auth,协议选择auth_chain_*auth_aes128_*
  • 如果你用于玩游戏,或对连接延迟有要求的情况下,建议不要使用tls1.2_ticket_auth混淆,用其它混淆或plain
  • 服务端里,http_simplehttp_post是相互兼容的,没有使用上的区别
  • 如果你在公司,或学校,或某些环境下,发现原版SS协议不可用,建议你启用http_simplehttp_posttls1.2_ticket_auth混淆,同时端口相应使用80或443,通常能解决问题。同时能躲避你所在环境下的网络封锁(如禁止访问网盘禁止上传等等)
  • 如果使用tls1.2_ticket_auth混淆或不开启混淆,那么协议最好不要使用originverify_sha1
  • 如果使用二重代理,一般你只需要考虑越过防火墙的那一段使用混淆或加强协议,除非为了匿名
  • 如果你发现你的代理突然不能用了,但换一个端口又能用了,或者等15分钟到半小时后又能用了,这种情况下请联系我

配置方法

服务端配置:使用最新SSR的manyuser分支
user-config.json或config.json里有一个protocol的字段,目前的可能取值为:
origin
verify_deflate (不建议)
verify_sha1 (已过时)
verify_sha1_compatible (已过时)
auth_sha1 (已过时)
auth_sha1_compatible (已过时)
auth_sha1_v2 (已过时)
auth_sha1_v2_compatible (已过时)
auth_sha1_v4 (不建议)
auth_sha1_v4_compatible (不建议)
auth_aes128_md5
auth_aes128_sha1
auth_chain_a
auth_chain_b

user-config.json或config.json里有一个obfs的字段,目前的可能取值为:
plain
http_simple
http_simple_compatible
http_post
http_post_compatible
random_head (已过时)
random_head_compatible (已过时)
tls1.2_ticket_auth
tls1.2_ticket_auth_compatible

默认为
"protocol":"auth_aes128_md5",
"obfs":"tls1.2_ticket_auth_compatible",
相应的
协议插件参数默认为"protocol_param":""
混淆插件参数默认为"obfs_param":""
对于protocol,必须服务端与客户端严格匹配
服务端配置为xxabc_compatible时(以compatible为后缀的),即服务端支持使用原版客户端,或使用配置插件为xxabcplain的ssr客户端。

客户端配置:使用本ssr版本,在编辑服务器配置里找到相应节点,最后在protocol选项和obfs选项的列表里选择需要使用的插件,然后填写相应的参数即可

实现接口

以下以C#语言为例做说明

interface IObfs

成员函数

InitData()
参数:无
返回:一个自定义类型变量,通常用于保存此接口的全局信息,不应返回null,c语言中返回void*
说明:第一次创建实例前调用,同一服务端配置不会重复调用,服务端在建立监听时调用,客户端在第一次连接时调用。

SetServerInfo(ServerInfo serverInfo)
参数:ServerInfo结构,包含成员变量:

  • host: 字符串类型,服务端ip,客户端需要把域名转换为ip,如有前置代理,则直接使用配置时所用的域名也可,服务端需要获取监听ip
  • port: 整数类型,服务端监听端口
  • param: 用户设置的参数,字符串类型
  • data: 由InitData返回的结果,为object类型(c语言中使用void*)
  • iv: 客户端或服务端加密时使用的iv数组(c语言中需要添加额外字段以记录其长度,下同)
  • recv_iv: 客户端或服务端接收到的iv数组
  • key: 加密时使用的key(不是原key,是通过BytesToKey生成的指定长度数组)
  • tcp_mss: 整数类型,TCP分包大小,设置为1460
  • overhead: 整数类型,协议头部大小,需要由调用方设置

返回:无
说明:实例构造的时候(每个连接建立时)调用,调用前iv和key必须已经初始化;而接收到数据后先初始化recv_iv再调用插件。

int GetOverhead() 参数:无
返回:此插件在通信时的附加头部大小

Dispose()
参数:无
返回:无
说明:实例析构时(每个连接关闭时)调用

byte[] ClientPreEncrypt(byte[] plaindata, int datalength, out int outlength)
参数:需要处理的字节数组及其长度
返回:处理后的字节数组及其长度
说明:客户端发送到服务端数据加密前调用此接口

byte[] ClientEncode(byte[] encryptdata, int datalength, out int outlength)
参数:需要编码的字节数组及其长度
返回:编码后的字节数组及其长度
说明:客户端发送到服务端数据加密后调用此接口

byte[] ClientDecode(byte[] encryptdata, int datalength, out int outlength, out bool needsendback)
参数:需要解码的字节数组及其长度
返回:解码后的字节数组及其长度,以及needsendback标记是否立即回发服务端数据。如needsendback为true,则会立即调用ClientEncode,调用时参数是一个长度为0的字节数组
说明:客户端收到服务端数据在解密前调用此接口

byte[] ClientPostDecrypt(byte[] plaindata, int datalength, out int outlength)
参数:需要处理的字节数组及其长度
返回:处理后的字节数组及其长度
说明:客户端收到服务端数据在解密后调用此接口

byte[] ServerPreEncrypt(byte[] plaindata, int datalength, out int outlength)
参数:需要处理的字节数组及其长度
返回:处理后的字节数组及其长度
说明:服务端发送到客户端数据加密前调用此接口

byte[] ServerEncode(byte[] encryptdata, int datalength, out int outlength)
参数:需要编码的字节数组及其长度
返回:编码后的字节数组及其长度
说明:服务端发送到客户端数据加密后调用此接口

byte[] ServerDecode(byte[] encryptdata, int datalength, out int outlength, out bool needdecrypt, out bool needsendback)
参数:需要解码的字节数组及其长度
返回:解码后的字节数组及其长度,以及needdecrypt标记数据是否需要解密(一般都应该为true),以及needsendback标记是否立即回发客户端数据。如needsendback为true,则会立即调用ServerEncode并发送其返回结果,调用时参数是一个长度为0的字节数组
说明:服务端收到客户端数据在解密前调用此接口

byte[] ServerPostDecrypt(byte[] plaindata, int datalength, out int outlength)
参数:需要处理的字节数组及其长度
返回:处理后的字节数组及其长度
说明:服务端收到客户端数据在解密后调用此接口

byte[] ClientUdpPreEncrypt(byte[] plaindata, int datalength, out int outlength)
参数:需要处理的字节数组及其长度
返回:处理后的字节数组及其长度
说明:客户端发送到服务端UDP数据加密前调用此接口

byte[] ClientUdpPostDecrypt(byte[] plaindata, int datalength, out int outlength)
参数:需要处理的字节数组及其长度
返回:处理后的字节数组及其长度
说明:客户端收到服务端UDP数据在解密后调用此接口

byte[] ServerUdpPreEncrypt(byte[] plaindata, int datalength, out int outlength)
参数:需要处理的字节数组及其长度
返回:处理后的字节数组及其长度
说明:服务端发送到客户端UDP数据加密前调用此接口

byte[] ServerUdpPostDecrypt(byte[] plaindata, int datalength, out int outlength)
参数:需要处理的字节数组及其长度
返回:处理后的字节数组及其长度
说明:服务端收到客户端UDP数据在解密后调用此接口

插件编写

有两类插件,一类是协议插件,一类是混淆插件

其中接口InitData, SetServerInfo, Dispose接口必须实现,其它的接口为通讯接口

编写协议插件的话,需要重写ClientPreEncrypt, ClientPostDecrypt, ServerPreEncrypt, ServerPostDecrypt,其它的按原样返回,needdecrypt必须为true,needsendback必须为false

编写混淆插件的话,需要重写ClientEncode, ClientDecode, ServerEncode, ServerDecode,其它的按原样返回。

如果编写的部分仅含客户端部分,那么只需要编写Client为前缀的两个接口,服务端同理。

目前支持此插件接口的,有 ShadowsocksR C# 和 ShadowsocksR Python