SOCKS的协议分析实践

SOCKS是一种网络传输协议,主要用于客户端与外网服务器之间通讯的中间传递。当防火墙后的客户端要访问外部的服务器时,就跟SOCKS代理服务器连接。这个代理服务器控制客户端访问外网的资格,允许的话,就将客户端的请求发往外部的服务器。

概要

用netty的来创建socks代理的服务端
curl工具来当socks的客户端
以抓包的方式来分析socks的协议流程

创建socks代理服务端

搭建java工程,直接拷贝netty的example中的socksproxy示例。
以netty的4.0.41.Final这个版本为例,github上的代码地址

对代码进行要进行稍微改造下,
在SocksServerInitializer.java文件中的第33行后面插入p.addLast(new LoggingHandler(LogLevel.INFO));
成为这样的代码

1
2
3
4
5
ChannelPipeline p = socketChannel.pipeline();
p.addLast(new LoggingHandler(LogLevel.INFO));
p.addLast(new SocksInitRequestDecoder());
p.addLast(socksMessageEncoder);
p.addLast(socksServerHandler);

目地是把相关的传输的数据都打印出来,方便查看分析。

注意在增加依赖时要把日志库加进去,这里我加的是ch.qos.logback.logback-classic

编译打包

测试

1
curl --socks5 http://192.168.2.133:1080 http://freeapi.ipip.net/8.8.8.8

这里含义是用socks5的代理方式访问http://freeapi.ipip.net/8.8.8.8(它是一个查ip位置的接口)

执行结果

运行结果:

抓包数据

打印日志:

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
15:51:58.731 [nioEventLoopGroup-3-1] INFO io.netty.handler.logging.LoggingHandler - [id: 0x83c1f217, L:/192.168.2.133:1080 - R:/192.168.2.8:63640] RECEIVED: 4B
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 05 02 00 01 |.... |
+--------+-------------------------------------------------+----------------+
15:51:58.736 [nioEventLoopGroup-3-1] DEBUG io.netty.util.Recycler - -Dio.netty.recycler.maxCapacity.default: 32768
15:51:58.736 [nioEventLoopGroup-3-1] DEBUG io.netty.util.Recycler - -Dio.netty.recycler.maxSharedCapacityFactor: 2
15:51:58.736 [nioEventLoopGroup-3-1] DEBUG io.netty.util.Recycler - -Dio.netty.recycler.linkCapacity: 16
15:51:58.737 [nioEventLoopGroup-3-1] DEBUG io.netty.util.Recycler - -Dio.netty.recycler.ratio: 8
15:51:58.741 [nioEventLoopGroup-3-1] INFO io.netty.handler.logging.LoggingHandler - [id: 0x83c1f217, L:/192.168.2.133:1080 - R:/192.168.2.8:63640] FLUSH
15:51:58.745 [nioEventLoopGroup-3-1] INFO io.netty.handler.logging.LoggingHandler - [id: 0x83c1f217, L:/192.168.2.133:1080 - R:/192.168.2.8:63640] WRITE: 2B
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 05 00 |.. |
+--------+-------------------------------------------------+----------------+
15:51:58.751 [nioEventLoopGroup-3-1] INFO io.netty.handler.logging.LoggingHandler - [id: 0x83c1f217, L:/192.168.2.133:1080 - R:/192.168.2.8:63640] FLUSH
15:51:58.796 [nioEventLoopGroup-3-1] INFO io.netty.handler.logging.LoggingHandler - [id: 0x83c1f217, L:/192.168.2.133:1080 - R:/192.168.2.8:63640] FLUSH
15:51:58.797 [nioEventLoopGroup-3-1] INFO io.netty.handler.logging.LoggingHandler - [id: 0x83c1f217, L:/192.168.2.133:1080 - R:/192.168.2.8:63640] RECEIVED: io.netty.handler.codec.socks.SocksCmdRequest@8397264
15:51:58.823 [nioEventLoopGroup-3-1] INFO io.netty.handler.logging.LoggingHandler - [id: 0x83c1f217, L:/192.168.2.133:1080 - R:/192.168.2.8:63640] WRITE: 10B
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 05 00 00 01 00 00 00 00 00 00 |.......... |
+--------+-------------------------------------------------+----------------+
15:51:58.823 [nioEventLoopGroup-3-1] INFO io.netty.handler.logging.LoggingHandler - [id: 0x83c1f217, L:/192.168.2.133:1080 - R:/192.168.2.8:63640] FLUSH
15:51:58.831 [nioEventLoopGroup-3-1] INFO io.netty.handler.logging.LoggingHandler - [id: 0x83c1f217, L:/192.168.2.133:1080 - R:/192.168.2.8:63640] RECEIVED: 87B
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 47 45 54 20 2f 38 2e 38 2e 38 2e 38 20 48 54 54 |GET /8.8.8.8 HTT|
|00000010| 50 2f 31 2e 31 0d 0a 48 6f 73 74 3a 20 66 72 65 |P/1.1..Host: fre|
|00000020| 65 61 70 69 2e 69 70 69 70 2e 6e 65 74 0d 0a 55 |eapi.ipip.net..U|
|00000030| 73 65 72 2d 41 67 65 6e 74 3a 20 63 75 72 6c 2f |ser-Agent: curl/|
|00000040| 37 2e 34 39 2e 31 0d 0a 41 63 63 65 70 74 3a 20 |7.49.1..Accept: |
|00000050| 2a 2f 2a 0d 0a 0d 0a |*/*.... |
+--------+-------------------------------------------------+----------------+
15:51:58.917 [nioEventLoopGroup-3-1] INFO io.netty.handler.logging.LoggingHandler - [id: 0x83c1f217, L:/192.168.2.133:1080 - R:/192.168.2.8:63640] WRITE: 249B
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 48 54 54 50 2f 31 2e 31 20 32 30 30 20 4f 4b 0d |HTTP/1.1 200 OK.|
|00000010| 0a 53 65 72 76 65 72 3a 20 4e 65 77 44 65 66 65 |.Server: NewDefe|
|00000020| 6e 64 0d 0a 44 61 74 65 3a 20 54 75 65 2c 20 30 |nd..Date: Tue, 0|
|00000030| 31 20 41 75 67 20 32 30 31 37 20 30 37 3a 35 31 |1 Aug 2017 07:51|
|00000040| 3a 35 38 20 47 4d 54 0d 0a 43 6f 6e 74 65 6e 74 |:58 GMT..Content|
|00000050| 2d 54 79 70 65 3a 20 74 65 78 74 2f 70 6c 61 69 |-Type: text/plai|
|00000060| 6e 3b 20 63 68 61 72 73 65 74 3d 75 74 66 2d 38 |n; charset=utf-8|
|00000070| 0d 0a 43 6f 6e 74 65 6e 74 2d 4c 65 6e 67 74 68 |..Content-Length|
|00000080| 3a 20 34 36 0d 0a 43 6f 6e 6e 65 63 74 69 6f 6e |: 46..Connection|
|00000090| 3a 20 6b 65 65 70 2d 61 6c 69 76 65 0d 0a 58 2d |: keep-alive..X-|
|000000a0| 43 61 63 68 65 3a 20 4d 49 53 53 20 66 72 6f 6d |Cache: MISS from|
|000000b0| 20 63 74 6c 2d 67 64 2d 31 32 31 2d 30 31 32 2d | ctl-gd-121-012-|
|000000c0| 30 39 38 2d 30 39 34 0d 0a 0d 0a 5b 22 47 4f 4f |098-094....["GOO|
|000000d0| 47 4c 45 2e 43 4f 4d 22 2c 22 47 4f 4f 47 4c 45 |GLE.COM","GOOGLE|
|000000e0| 2e 43 4f 4d 22 2c 22 22 2c 22 22 2c 22 6c 65 76 |.COM","","","lev|
|000000f0| 65 6c 33 2e 63 6f 6d 22 5d |el3.com"] |
+--------+-------------------------------------------------+----------------+
15:51:58.917 [nioEventLoopGroup-3-1] INFO io.netty.handler.logging.LoggingHandler - [id: 0x83c1f217, L:/192.168.2.133:1080 - R:/192.168.2.8:63640] FLUSH
15:51:58.924 [nioEventLoopGroup-3-1] INFO io.netty.handler.logging.LoggingHandler - [id: 0x83c1f217, L:/192.168.2.133:1080 ! R:/192.168.2.8:63640] INACTIVE
15:51:58.925 [nioEventLoopGroup-3-1] INFO io.netty.handler.logging.LoggingHandler - [id: 0x83c1f217, L:/192.168.2.133:1080 ! R:/192.168.2.8:63640] UNREGISTERED

简化流程表格:

日志中少了第3条数包的打印。

192.168.2.8 192.168.2.133
(1) 05 02 00 01 –>
(2) <– 05 00
(3) 05 01 00 01 79 0c 62 63 00 50 ->
(4) <– 0x05 00 00 01 00 00 00 00 00 00
(5) http请求 ->
(6) <– http响应

结果分析

第一行

SOCKS5比SOCKS4a多了鉴定、IPv6、UDP支持。建立与SOCKS5服务器的TCP连接后客户端需要先发送请求来协商版本及认证方式,格式为(以字节为单位):

VER NMETHODS METHODS
1 1 1-255

05 02 00 01
0x05VER
0x02 表示METHODS占两个字节
METHODS是客户端支持的认证方式列表,每个方法占1字节。当前的定义是:

  • 0x00 不需要认证
  • 0x01 GSSAPI
  • 0x02 用户名、密码认证
  • 0x03 - 0x7F由IANA分配(保留)
  • 0x80 - 0xFE为私人方法保留
  • 0xFF 无可接受的方法

0x00,0x01则表示可以在”不需要认证”和”用GSSAPI“(GSSAPI是rfc定义的认证方式),两种验证方法中选。

第二行

服务器从客户端提供的方法中选择一个并通过以下消息通知客户端(以字节为单位):

VER METHOD
1 1
  • VER是SOCKS版本,这里应该是0x05;
  • METHOD是服务端选中的方法。如果返回0xFF表示没有一个认证方法被选中,客户端需要关闭连接。

05 00 表示服务器选了”不需要认证”

如果要认证,客户端和服务端根据选定的认证方式执行对应的认证。

认证结束后客户端就可以发送请求信息。如果认证方法有特殊封装要求,请求必须按照方法所定义的方式进行封装。

第三行

SOCKS5请求格式(以字节为单位):

VER CMD RSV ATYP DST.ADDR DST.PORT
1 1 0x00 1 动态 2
  • VER是SOCKS版本,这里应该是0x05;
  • CMD是SOCK的命令码
    • 0x01表示CONNECT请求
    • 0x02表示BIND请求
    • 0x03表示UDP转发
  • RSV 0x00,保留
  • ATYP DST.ADDR类型
    • 0x01 IPv4地址,DST.ADDR部分4字节长度
    • 0x03域名,DST ADDR部分第一个字节为域名长度,DST.ADDR剩余的内容为域名,没有\0结尾。
    • 0x04 IPv6地址,16个字节长度。
  • DST.ADDR 目的地址
  • DST.PORT 网络字节序表示的目的端口

05 01 00 01 79 0c 62 63 00 50表示:

  • 0x05版本
  • 0x01 connect方法
  • 0x00 保留
  • 0x01 IPv4地址
  • 0x79 0x0c 0x62 0x63 目的地ip地址,这里示例freeapi.ipip.net的ip
  • 0x00 0x50 端口80

第四行

服务器按以下格式回应客户端的请求(以字节为单位):

VER REP RSV ATYP BND.ADDR BND.PORT
1 1 0x00 1 动态 2
  • 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 网络字节序表示的服务器绑定的端口

05 00 00 01 00 00 00 00 00 00表示:

  • 0x05: 版本号
  • 0x00: 成功
  • 0x00: 保留
  • 0x01: IPv4地址
  • 后面都是00

第五行,第六行

发送的http请求与响应

总结

文章通过netty创建了一个sock的代理,并通过抓包及日志打印,把数据流截取下来。
然后通过对比数据流与协议标准来学习了协议的细节内容。

网络协议就是计算机之间的语言,学习它们的语言就是实现语言,并拆分其中的单词,搞清单词(字节流)含义,同时要知道它们之前的语法(字节流的时序关系)

wireskark是个分析协议非常好的工具。
如要深入了解协议内容,看rfc是最直接的方式,但可能有些枯燥。

更多

https://zh.wikipedia.org/wiki/SOCKS
https://www.ietf.org/rfc/rfc1928.txt