Socks5 协议
目录
SOCKS
是一种网络传输协议,主要用于客户端与外网服务器之间通讯的中间代理传递 。SOCKS是 "SOCKetS" 的缩写。目前其最新的版本是5,相比于前一个版本,其新增了 IPv6
, UDP
及 验证
。
根据OSI模型,SOCKS是会话层的协议,位于表示层与传输层之间。
协议详情
一个 socks5 连接的过程主要有两部分,协商与请求,如果协商后需要验证,则还包含验证的部分。
(1) 认证方法协商
客户端向服务端发出连接认证方法协商请求
VER
是 SOCKS 的版本,此处是0x05
NMETHODS
是 METHODS 部分数据的长度,即客户端支持的认证方式的 countMETHODS
是客户端支持的认证方式列表,每个方法占一个字节0x00
不需要认证0x01
GSSAPI0x02
用户名密码认证0x03 - 0x7F
由 IANA分配(保留)0x80 - 0xFE
私人方法(保留)0xFF
无可接受的方法
服务器回应协商,从客户端提供的方法中选择一个并通知客户端:
VER
是SOCKS的版本号,此处应为0x05
METHOD
是服务端选中的方法。如果不支持客户端提供的所有方法,则返回0xFF
(2) 认证
如果双方同意使用用户名密码方式认证,则认证过程如下
客户端发送用户名密码:
AVER
认证版本号,目前为0x01
服务端回应认证:AVER
同上STATE
认证状态0x00
成功0x01
失败
(3) 请求
VER
为版本号,此处为0x05
CMD
是 SOCK 的命令码0x01
表示CONNECT 请求
0x02
表示BIND 请求
0x03
表示UDP 转发
RSV
保留,为0x00
ATYP
为目标地址( DST.ADDR)的类型0x01
为IPV4
, 目标地址部分长 4 个字节0x03
为域名
, 目标地址部分的第一个字节为域名长度,其余部分为域名内容(没有 \0 结尾)0x04
为IPV6
, 目标地址分部长 16 个字节
DST.ADDR
目标地址DST.PORT
目标端口(网络字节序)
服务端回应客户端的请求:
VER
是SOCKS版本,这里应该是0x05
;REP
应答字段0x00
表示成功0x01
普通SOCKS服务器连接失败0x02
现有规则不允许连接0x03
网络不可达0x04
主机不可达0x05
连接被拒0x06
TTL超时0x07
不支持的命令0x08
不支持的地址类型0x09 - 0xFF
未定义
RSV
0x00
,保留ATYP
BND.ADDR类型0x01
IPv4地址,DST.ADDR部分4字节长度0x03
域名,DST.ADDR部分第一个字节为域名长度,DST.ADDR剩余的内容为域名,没有\0结尾。0x04
IPv6地址,16个字节长度。
BND.ADDR
服务器绑定的地址BND.PORT
网络字节序表示的服务器绑定的端口
实践
通过 python 建立一个 socks5 服务来分析其协议
代码如下,在本地起一个服务监听 1088端口, 接收 socks5 请求并响应请求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
import socket def print_dump(prefix, data): print '---',prefix,'length =',len(data),'---' print 'orign:\n',data while len(data) > 16: for i in range(0,16): print '%02X'%(ord(data[i])), print '' data = data[16:] if len(data) > 0: for c in data: print '%02X'%(ord(c)), print '' print '==',prefix,'end ==' # ServerResponse buf_0 = '\x05\x00' buf_1 = '\x05\x00\x00\x01\xDB\xEE\x14\x58\x00\x50' buf_2 = u'''HTTP/1.1 200 OK\r\nServer:FakeServer\r\nContent-Type: text/html\r\n\r\n<html><body>Sock5 Test</body></html>\r\n\r\n''' STEP = 0 if __name__ == '__main__': S_IP = '127.0.0.1' S_PORT = 1088 BUF_SIZE = 1024 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind((S_IP, S_PORT)) s.listen(1) conn, addr = s.accept() while 1: data = conn.recv(BUF_SIZE) if not data: print 'no data' break print_dump('step:' + str(STEP) + ' Client',data) if STEP == 0: conn.send(buf_0) print_dump('step:' + str(STEP) + ' Server',buf_0) STEP = 1 elif STEP == 1: conn.send(buf_1) print_dump('step:' + str(STEP) + ' Server',buf_1) STEP = 2 elif STEP == 2: conn.send(buf_2) print_dump('step:' + str(STEP) + ' Server',buf_2) STEP = 3 break conn.close() |
使用浏览器设置 socks5 代理(这里使用firfox):
然后在访问一个 Http 网站,这里选择 http://www.runoob.com/ 来实验,可以看到服务端的输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
--- step:0 Client length = 3 --- orign: ║╔ 05 01 00 == step:0 Client end == --- step:0 Server length = 2 --- orign: ║ 05 00 == step:0 Server end == --- step:1 Client length = 10 --- orign: ║╔ ╔垲S P 05 01 00 01 DB EE 14 53 00 50 == step:1 Client end == --- step:1 Server length = 10 --- orign: ║ ╔垲X P 05 00 00 01 DB EE 14 58 00 50 == step:1 Server end == --- step:2 Client length = 544 --- orign: GET / HTTP/1.1 Host: www.runoob.com User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:62.0) Gecko/20100101 Firefox/62.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Cookie: Hm_lvt_8e2a116daf0104a78d601f40a45c75b4=1530174275,1531292949,1531448092,1531795300; Hm_lvt_3eec0b7da6548cf07db3bc477ea905ee=1532919133,1533018283,1533103754 DNT: 1 Connection: keep-alive Upgrade-Insecure-Requests: 1 47 45 54 20 2F 20 48 54 54 50 2F 31 2E 31 0D 0A 48 6F 73 74 3A 20 77 77 77 2E 72 75 6E 6F 6F 62 2E 63 6F 6D 0D 0A 55 73 65 72 2D 41 67 65 6E 74 3A 20 4D 6F 7A 69 6C 6C 61 2F 35 2E 30 20 28 57 69 6E 64 6F 77 73 20 4E 54 20 31 30 2E 30 3B 20 57 69 6E 36 34 3B 20 78 36 34 3B 20 72 76 3A 36 32 2E 30 29 20 47 65 63 6B 6F 2F 32 30 31 30 30 31 30 31 20 46 69 72 65 66 6F 78 2F 36 32 2E 30 0D 0A 41 63 63 65 70 74 3A 20 74 65 78 74 2F 68 74 6D 6C 2C 61 70 70 6C 69 63 61 74 69 6F 6E 2F 78 68 74 6D 6C 2B 78 6D 6C 2C 61 70 70 6C 69 63 61 74 69 6F 6E 2F 78 6D 6C 3B 71 3D 30 2E 39 2C 2A 2F 2A 3B 71 3D 30 2E 38 0D 0A 41 63 63 65 70 74 2D 4C 61 6E 67 75 61 67 65 3A 20 7A 68 2D 43 4E 2C 7A 68 3B 71 3D 30 2E 38 2C 7A 68 2D 54 57 3B 71 3D 30 2E 37 2C 7A 68 2D 48 4B 3B 71 3D 30 2E 35 2C 65 6E 2D 55 53 3B 71 3D 30 2E 33 2C 65 6E 3B 71 3D 30 2E 32 0D 0A 41 63 63 65 70 74 2D 45 6E 63 6F 64 69 6E 67 3A 20 67 7A 69 70 2C 20 64 65 66 6C 61 74 65 0D 0A 43 6F 6F 6B 69 65 3A 20 48 6D 5F 6C 76 74 5F 38 65 32 61 31 31 36 64 61 66 30 31 30 34 61 37 38 64 36 30 31 66 34 30 61 34 35 63 37 35 62 34 3D 31 35 33 30 31 37 34 32 37 35 2C 31 35 33 31 32 39 32 39 34 39 2C 31 35 33 31 34 34 38 30 39 32 2C 31 35 33 31 37 39 35 33 30 30 3B 20 48 6D 5F 6C 76 74 5F 33 65 65 63 30 62 37 64 61 36 35 34 38 63 66 30 37 64 62 33 62 63 34 37 37 65 61 39 30 35 65 65 3D 31 35 33 32 39 31 39 31 33 33 2C 31 35 33 33 30 31 38 32 38 33 2C 31 35 33 33 31 30 33 37 35 34 0D 0A 44 4E 54 3A 20 31 0D 0A 43 6F 6E 6E 65 63 74 69 6F 6E 3A 20 6B 65 65 70 2D 61 6C 69 76 65 0D 0A 55 70 67 72 61 64 65 2D 49 6E 73 65 63 75 72 65 2D 52 65 71 75 65 73 74 73 3A 20 31 0D 0A 0D 0A == step:2 Client end == --- step:2 Server length = 103 --- orign: HTTP/1.1 200 OK Server:FakeServer Content-Type: text/html <html><body>Sock5 Test</body></html> 48 54 54 50 2F 31 2E 31 20 32 30 30 20 4F 4B 0D 0A 53 65 72 76 65 72 3A 46 61 6B 65 53 65 72 76 65 72 0D 0A 43 6F 6E 74 65 6E 74 2D 54 79 70 65 3A 20 74 65 78 74 2F 68 74 6D 6C 0D 0A 0D 0A 3C 68 74 6D 6C 3E 3C 62 6F 64 79 3E 53 6F 63 6B 35 20 54 65 73 74 3C 2F 62 6F 64 79 3E 3C 2F 68 74 6D 6C 3E 0D 0A 0D 0A == step:2 Server end == |
可以看出整个过程如下:
- STEP0 : Client 发起Socks5 请求,
0x05 0x01 0x00
- Server 响应请求,选择验证方法,
0x05 0x00
- STEP1 : Client 发起连接请求:
0x05 0x01 0x00 0x01 0xDB 0xEE 0x14 0x53 0x00 0x50
- Server 给出连接成功的响应
0x05 0x00 0x00 0x01 0xDB 0xEE 0x14 0x58 0x00 0x50
- STEP2: Client 向 Server 发送了HTTP请求
- Server 给出HTTP 响应(实际是Server向实际IP转发请求并给响应转发给Client, 这里做了假的代理)
最终浏览器得到的结果如下图: