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 中 |
|
|
0x05 为VER |
|
|
0x02 表示METHODS 占两个字节 |
|
|
METHODS是客户端支持的认证方式列表,每个方法占1字节。当前的定义是: |
|
|
- 0x00 不需要认证
- 0x01 GSSAPI
- 0x02 用户名、密码认证
- 0x03 - 0x7F由IANA分配(保留)
- 0x80 - 0xFE为私人方法保留
- 0xFF 无可接受的方法
0x00,0x01
则表示可以在”不需要认证”和”用GSSAPI
“(GSSAPI是rfc定义的认证方式),两种验证方法中选。
第二行
服务器从客户端提供的方法中选择一个并通过以下消息通知客户端(以字节为单位):
- 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