New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
你也能写个 Shadowsocks #12
Comments
穿墙的解释不太对吧,https 也加密的,为什么访问不了 google/twitter |
实际用对称加密并不能无脑解密 aes加密是分块的 请问应该如何解决这个问题 真心不想看源码,大佬求告知一下 |
为什么都是基于 SOCKS5 来做混淆呢,SOCKS5 会暴露一个端口专门提供服务不便于隐藏,难道是因为 SOCKS5 很快么? v2ray 有基于 WebSocket 协议的,但是 v2ray 在我使用中非常耗费 CPU,所以放弃了。 提到 v2ray WebSocket 协议的原因是,我可以用 Nginx 反向代理 WebSocket,把翻墙服务隐藏再一个网站中,比起 SOCKS5 协议会减少 Server 端的暴露几率。 不懂代码层面的事情,请指教。 |
你这个就是SS最原始的table加密。 |
以其昏昏使人昭昭 |
@antonchen SOCKS5 的端口不会暴露到公网,在 |
@timqian 我指的就是 ss-server 的端口 |
@antonchen ss-server 不暴露端口怎么和 ss-local 通信? |
@timqian 使用 WebSocket 监听在 localhost 上,Nginx 反向代理 WebSocket,使服务隐藏在一个 VirtualHost 中,甚至某个 URL 中。 |
@rickytan https只是对包体进行加密,防火墙还是能通过包头来解析出你的请求行为,并且阻止。 |
这种加密(简易替换加密)很容易收到统计学方法的攻击。 |
同意 @QuantumGhost ,这样的加密太LOW。 |
@lenovobenben 其实我觉得这篇文章的意义并不是在于具体的加密算法有多么高级,而是让大家知道了翻墙的原理和一些基本细节。对这方面不太了解的人确实不太懂shadowsocks做了什么,只知道是个代理,有时候也会想为啥不能用nginx走https搞个代理搞定呢?对了,为啥?哈哈。 @wanghanfeng |
@argb ,原文中这句话,我看了觉得很不合适: 这个算法连IV都没有!第一个字节就是 addrType。事实上,我随便写一个脚本都能识别出来,太 easy了。这也叫“不能被轻易识别?” |
@lenovobenben 恩 这方面我不是很懂 可以跟作者建议或者讨论下 |
mark |
hmm,持续关注 |
这个算法连简单的rc4都不如。另外通过统计学方法可以很快的反推出码表,而且可以很容易playback attack. 另外我举一个简单的例子,这个协议中client直接将socks5进行 “混淆” 后发给server端,攻击者只需要修改握手数据包中第四个byte (ATYP)然后重复发送给服务器,通过判断服务器是否立即关闭链接,如果服务器在3种情况下(对应未加密时的0x01, 0x03, 0x04)没有立即关闭链接,那么这个就可以判定是一个私有协议的socks5代理。 注意这种方式不需要知道码表,只需要判断是否有3种情况没有立即关闭链接即可。 这种方式对于gfw来说简单高效,只需要尝试256种情况。 参见:http://stackissue.com/breakwa11/shadowsocks-rss/shadowsocks-38.html 另外我提到的这些东西在ss的演进中都已经讨论过了,希望大家造轮子的时候先多多提高自己的知识水平。 |
@WANG-lp |
难道这篇文章的目的不是为了告诉你怎么去实现一个 ss功能类似的软件吗。。 |
文章思路介绍的很清楚,也从楼上的分享中学习了很多,感谢 |
赞! |
大神能分析一下brook的表现如何? |
非常感谢! |
good luck! |
@antonchen 你所说的“v2ray使用的WebSocket协议”是v2ray服务器与其客户端之间的通信协议,客户端与本地需要代理的应用之间仍然是通常使用socks5协议通信的。 |
人家后面还加密呢!! |
前提是他得知道是怎么混淆和加密的,问题是谁会去关心你这一个人的流量去做解密!! |
mark |
to avoid the strict firewall, the final solution is Ha Ha Ha, so only god is god... |
bits traffic plan. |
最近老是能看到这种“重新发明”的事情,密码表是古典密码学的范畴,已经被使用了几千年,德军的密码表要比这复杂得多(这个是一个密码表不会变,德军那个是频繁跳表的),但是第一台计算机发明之前就可以破解了。 我以为稍微懂点加密常识的人就能指出问题,结果这项目都有 3.4k star、 6 个 contributor 了,真牛逼。不过也说明了,加密确实很难懂,既然连这种在密码学里连加法都算不上的基础知识都能挡住这么多人 还好,看到你这贴有 57 个赞,我起码有了个底 |
VPN也是加密的。 |
原理不复杂,实现另外一套流量加密转发机制不难。我觉得难得是让大家接受,代表先进,成为一个标准的过程。 shadowsocks 设计的流量加密环节用到都是广泛认可的技术,能量、时间消耗肯定是增加的,任何加密都会有这个问题,另外设计一套试错的过程肯定会很长。朝着正面的方向看,native 层代码适配硬件加速(CPU 计算能力)及优化代码我觉得是正事。 |
真不错 |
一帮rz,作者在教你们怎么制作,你们说加密太简单 |
能讲加密简单,基本也懂原理和实现吧 |
你说的不错,不过首先需要明白的是这篇文章写作的目的和存在的意义,它可以让一个零基础的人明白Shadowsocks 的基本原理,做出一个不错的替代品。 说加密太低级,甚至爬楼看到的修改ATYP达到攻击目的,如果是站在一个做补充和优化的立场上,无疑是值得敬佩的,他们在改进这个初级的玩具。 但评论中抨击的字眼随处可见,他们想当然地把这篇文章中制作的穿墙工具比肩成品,比如 这让我不得不骂一声jerk |
对称加密的方式本来就不是为了安全,楼上哪来那么多秀优越感的的人。 |
我把local和server启动后,运行
这里版本是0xd8,我这样测试不对吗? |
有一个地方不太懂啊,如果目标端口是443,那么连接真正的远程服务难道不需要使用ssl吗 ? |
楼主这样的内容多出一点,很是受教…… |
简单了解一下connect的连接原理,可以解决你的疑问 |
https开启了通道加密了数据,连接ip和端口都没加密 |
已收到,谢谢!
|
思路挺好的,学习了 |
do you got a better idea ? |
已收到,谢谢!
|
我自己用Java写了一个http代理,寻思好歹自己加一个密,然后介于tcp的有序性,就想到了和楼主一样的想法。 |
继上次的多个map映射,我又产生了跟map映射一样效果的做法。 网络数据千千万,防火墙怎么可能分析的过来?那怕是最基本的发送端每个字节加一,服务端每个字节减一,我赌防火墙也不会认识。 |
已收到,谢谢!
|
主要是https比较麻烦吧,需要域名证书这些 |
为什么密钥的这个数组中第I个的值不能等于I; |
已收到,谢谢!
|
本文将教你从0写一个Shadowsocks,无需任何基础,读完本文你就能完成一个轻量级、高性能的 Shadowsocks 代替品。
我们暂且把最终完成的项目叫做 Lightsocks,如果你很急切地想看到结果,可以先体验本文最终完成的项目 Lightsocks ,也可以下载阅读源码。
认识 Shadowsocks
Shadowsocks 是一个能骗过防火墙的网络代理工具。它把要传输的原数据经过加密后再传输,网络中的防火墙由于得不出要传输的原内容是什么而只好放行,于是就完成了防火墙穿透,也即是所谓的“翻墙”。
在自由的网络环境下,在本机上访问服务时是直接和远程服务建立连接传输数据,流程如图:

但在受限的网络环境下会有防火墙,本机电脑和远程服务之间传输的数据都必须通过防火墙的检查,流程如图:

如果防火墙发现你在传输受限的内容,就把拦截本次传输,就会导致在本机无法访问远程服务。
而 Shadowsocks 所做的就是把传输的数据加密,防火墙得到的数据是加密后的数据,防火墙不知道传输的原内容是什么,于是防火墙就放行本次请求,于是在本机就访问到了远程服务,流程如图:

也就是说使用 Shadowsocks 的前提是:
Shadowsocks 原理
Shadowsocks 由两部分组成,运行在本地的 ss-local 和运行在防火墙之外服务器上的 ss-server,下面来分别详细介绍它们的职责(以下对 Shadowsocks 原理的解析只是我的大概估计,可能会有细微的差别)。
ss-local
ss-local 的职责是在本机启动和监听着一个服务,本地软件的网络请求都先发送到 ss-local,ss-local 收到来自本地软件的网络请求后,把要传输的原数据根据用户配置的加密方法和密码进行加密,再转发到墙外的服务器去。
ss-server
ss-server 的职责是在墙外服务器启动和监听一个服务,该服务监听来自本机的 ss-local 的请求。在收到来自 ss-local 转发过来的数据时,会先根据用户配置的加密方法和密码对数据进行对称解密,以获得加密后的数据的原内容。同时还会解 SOCKS5 协议,读出本次请求真正的目标服务地址(例如 Google 服务器地址),再把解密后得到的原数据转发到真正的目标服务。
当真正的目标服务返回了数据时,ss-server 端会把返回的数据加密后转发给对应的 ss-local 端,ss-local 端收到数据再解密后,转发给本机的软件。这是一个对称相反的过程。
由于 ss-local 和 ss-server 端都需要用对称加密算法对数据进行加密和解密,因此这两端的加密方法和密码必须配置为一样。Shadowsocks 提供了一系列标准可靠的对称算法可供用户选择,例如 rc4、aes、des、chacha20 等等。Shadowsocks 对数据加密后再传输的目的是为了混淆原数据,让途中的防火墙无法得出传输的原数据。但其实用这些安全性高计算量大的对称加密算法去实现混淆有点“杀鸡用牛刀”。
SOCKS5 协议介绍
Shadowsocks 的数据传输是建立在 SOCKS5 协议之上的,SOCKS5 是 TCP/IP 层面的网络代理协议。
ss-server 端解密出来的数据就是采用 SOCKS5 协议封装的,通过 SOCKS5 协议 ss-server 端能读出本机软件想访问的服务的真正地址以及要传输的原数据,下面来详细介绍 SOCKS5 协议的通信细节。
建立连接
客户端向服务端连接连接,客户端发送的数据包如下:
其中各个字段的含义如下:
-
VER
:代表 SOCKS 的版本,SOCKS5 默认为0x05
,其固定长度为1个字节;-
NMETHODS
:表示第三个字段METHODS的长度,它的长度也是1个字节;-
METHODS
:表示客户端支持的验证方式,可以有多种,他的长度是1-255个字节。目前支持的验证方式共有:
0x00
:NO AUTHENTICATION REQUIRED(不需要验证)0x01
:GSSAPI0x02
:USERNAME/PASSWORD(用户名密码)0x03
: to X'7F' IANA ASSIGNED0x80
: to X'FE' RESERVED FOR PRIVATE METHODS0xFF
: NO ACCEPTABLE METHODS(都不支持,没法连接了)响应连接
服务端收到客户端的验证信息之后,就要回应客户端,服务端需要客户端提供哪种验证方式的信息。服务端回应的包格式如下:
其中各个字段的含义如下:
VER
:代表 SOCKS 的版本,SOCKS5 默认为0x05
,其固定长度为1个字节;METHOD
:代表服务端需要客户端按此验证方式提供的验证信息,其值长度为1个字节,可为上面六种验证方式之一。举例说明,比如服务端不需要验证的话,可以这么回应客户端:
0x05
0x00
和目标服务建立连接
客户端发起的连接由服务端验证通过后,客户端下一步应该告诉真正目标服务的地址给服务器,服务器得到地址后再去请求真正的目标服务。也就是说客户端需要把 Google 服务的地址
google.com:80
告诉服务端,服务端再去请求google.com:80
。目标服务地址的格式为 (IP或域名)+端口,客户端需要发送的包格式如下:
0x00
各个字段的含义如下:
VER
:代表 SOCKS 协议的版本,SOCKS 默认为0x05,其值长度为1个字节;CMD
:代表客户端请求的类型,值长度也是1个字节,有三种类型;CONNECT
:0x01
;BIND
:0x02
;UDP
: ASSOCIATE0x03
;RSV
:保留字,值长度为1个字节;ATYP
:代表请求的远程服务器地址类型,值长度1个字节,有三种类型;IPV4
: address:0x01
;DOMAINNAME
:0x03
;IPV6
: address:0x04
;DST.ADDR
:代表远程服务器的地址,根据ATYP
进行解析,值长度不定;DST.PORT
:代表远程服务器的端口,要访问哪个端口的意思,值长度2个字节。服务端在得到来自客户端告诉的目标服务地址后,便和目标服务进行连接,不管连接成功与否,服务器都应该把连接的结果告诉客户端。在连接成功的情况下,服务端返回的包格式如下:
0x00
各个字段的含义如下:
VER
:代表 SOCKS 协议的版本,SOCKS 默认为0x05,其值长度为1个字节;0x00
succeeded0x01
general SOCKS server failure0x02
connection not allowed by ruleset0x03
Network unreachable0x04
Host unreachable0x05
Connection refused0x06
TTL expired0x07
Command not supported0x08
Address type not supported0x09
to0xFF
unassignedRSV
:保留字,值长度为1个字节ATYP
:代表请求的远程服务器地址类型,值长度1个字节,有三种类型0x01
0x03
0x04
BND.ADDR
:表示绑定地址,值长度不定。BND.PORT
: 表示绑定端口,值长度2个字节数据转发
客户端在收到来自服务器成功的响应后,就会开始发送数据了,服务端在收到来自客户端的数据后,会转发到目标服务。
总结
SOCKS5 协议的目的其实就是为了把来自原本应该在本机直接请求目标服务的流程,放到了服务端去代理客户端访问。
其运行流程总结如下:
以上内容来自 SOCKS5 协议规范 rfc1928。
Lightsocks 实现
要实现 Lightsocks 需要实现两部分:运行在本地的 lightsocks-local,和运行在墙外代理服务器上 lightsocks-server。
下面来分别教你如果使用 Golang 来实现它们,采用 Golang 语言的原因在于:性能好、跨平台、适合高并发、学习门槛低。对Golang感兴趣?请看Golang 中文学习资料汇总
实现数据混淆
在 Shadowsocks 中是采用的标准的对称加密算法去实现数据混淆的,对称算法在加密和解密过程中需要大量计算。
为了简单起见,Lightsocks 将采用最简单高效的方法去实现数据混淆,具体原理如下。
这个数据混淆算法和对称加密很相似,两端都需要有同样的密钥。
这个密钥有如下要求:
[256]byte
;I
个的值不能等于I
;例如以下为一个合法的密钥(上为索引,下为值):
如果原数据为
[5,0,1,2,3]
,则采用以上密钥加密后变成[236,186,118,82,201]
。如果加密后的数据为
[186,118,82,201,235]
,则采用以上密钥解密得到的原数据为[0,1,2,3,4]
聪明的你肯定看懂了其中的规律:把1~255 这256个数字确定一种一对一的映射关系,加密是从一个数字得到对应的一个数字,而解密则是反向的过程,而这个密钥的作用正是描述这个映射关系。
这其实就是中学学的反函数。
为什么要这样设计数据混淆算法呢?在数据传输时,数据是以 byte 为最小单位流式传输的。一个 byte 的取值只可能是 0~255。该混淆算法可以直接对一个个 byte 进行加解密,而无需像标准的对称算法那样只能对一大块数据进行加密。
再加上本算法的加解密 N byte 数据的算法复杂度为 N(直接通过数组索引访问),非常适合流式加密。
以上加密算法的安全性怎么样呢?符合以上要求的密钥匙有多少种组合呢?我们来算算:
这其实就是初中学的排列组合中的排列问题,形象点其实就是,把 0~255 个不同编号的人安排到 0~255 个不同编号的坑去,并且不能有编号一样的情况,有多少种排法。
也就是
A(255,255)=255*254*253*...*1=255!
,但其中有一半为有重复的情况,最终结果为
255!/2
,其值大概为
10^500
这个数量级。以上加密算法虽然破绽很多,但足以实现高效的数据混淆,骗过防火墙。
目前采用对称加密算法实现数据混淆的 Shadowsocks 已经能被一些防火墙通过机器学习算法通过特征分析识别出传输的原内容适合合法,而 Lightsocks 的这套混淆算法目前还不能被轻易的识别出来。
随机产生一个以上密钥匙的代码如下:
对数据进行加密解密的代码如下:
再使用以上的 Cipher 去封装一个加密传输的 SecureSocket,以方便直接加解密 TCP Socket 中的流式数据,代码如下:
这个 SecureSocket 用于 local 端和 server 端之间进行 TCP 通信,并且只使用 SecureSocket 通信时中间传输的数据会被加密,防火墙无法读到原数据。
实现 local 端
运行在本机的 local 端的职责是把本机程序发送给它的数据经过加密后转发给墙外的代理服务器,总体工作流程如下:
实现以上功能的 local 端代码如下:
实现 server 端
运行在墙外代理服务器的 server 端职责如下:
实现以上功能的代码如下:
以上就是实现一个轻量级 Shadowsocks 的核心代码。其它一些零碎的代码,例如启动入口、配置读写等,可以去 lightsocks 项目中阅读完整代码。
阅读原文
The text was updated successfully, but these errors were encountered: