SOCKS 是一种网络传输协议,主要用于客户端与外网服务器之间通讯的中间代理传递 。SOCKS是 “SOCKetS” 的缩写。目前其最新的版本是5,相比于前一个版本,其新增了 IPv6, UDP 及 验证。
根据OSI模型,SOCKS是会话层的协议,位于表示层与传输层之间。
协议详情
一个 socks5 连接的过程主要有两部分,协商与请求,如果协商后需要验证,则还包含验证的部分。
(1) 认证方法协商
客户端向服务端发出连接认证方法协商请求
服务器回应协商,从客户端提供的方法中选择一个并通知客户端:
(2) 认证
如果双方同意使用用户名密码方式认证,则认证过程如下
客户端发送用户名密码:
(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 目标端口(网络字节序)
服务端回应客户端的请求:
实践
通过 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 import socketdef 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 ==' 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 --- 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 0A48 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 2F78 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 2C2A 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 3A20 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 0A44 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 0D0A 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, 这里做了假的代理)
最终浏览器得到的结果如下图: