***原理详解(中篇)

本文来自网易云社区

 

3. ***工作原理

终于说到***工作原理了,在说这个之前必须先介绍以下普通socks 5的工作原理,将之与***的“变异版”进行对比,就可以看出***处理的妙处了!!

 

1) 普通Socks 5 工作原理

普通的代理是直接应用Socks 5 协议进行交互,也就是说客户端便是你的主机,Socks 5的Server是远程的代理机,具体的工作模式如下:

 Socks 5客户端  <---Socks 5--->   Socks 5服务器  <---正常请求--->  目标主机

Socks 5客户端在与Socks 5服务器交互的整个过程是有可能暴露在整个互联网中的,因此很容易被监控到,根据协议特征也可以很容易识别出来,若采取普通的Socks 5代理方式的话,若用于FQ去看外边的世界,这种方式很容易被墙,代理服务器的IP极容易被加入黑名单,也就导致此代理的寿终正寝,因此一种新的方式***出现了。

 

2) *** Socks 5工作原理

***的处理方式是将Socks 5客户端与Socks5服务器的连接提前,Socks5协议的交互完全是在本地进行的,在网络中传输的完全是利用加密算法加密后的密文,这就很好的进行了去特征化,使得传输的数据不是很容易的被特征识别,本人认为这是***的最大的创新了,具体的流程如下:

 Socks 5客户端  <---Socks 5--->   sslocal  
                                    ∧
                                    |
                                   密文
                                    |
                                    ∨
                                  ssserver  <---正常请求--->  目标主机

其它方面的处理都与普通代理一样,特殊之处将Socks 5服务器拆成了两个部分:

  • 本地的sslocal:sslocal对于Socks 5客户端便是Socks 5服务器,对于Socks 5客户端是透明的,sslocal完成与Socks 5客户端所有的交互。
  • 远程的ssserver:ssserver对于目标主机同样也是Socks 5服务器,对于目标主机是透明的,完成Socks 5服务器与目标主机的所有操作。
  • sslocal-ssserver:sslocal接收到Socks 5客户端发送的数据,会将数据加密,并将配置信息发送到ssserver,ssserver接收到配置信息进行权限验证,然后将数据进行解密,然后将明文发往目标主机;当目标主机响应ssserver,ssserver将接收到的数据进行解包,并将数据加密,发送到sslocal,sslocal接收到加密后的数据进行解密,再发送给Socks 5客户端,这就完成了一次交互。
Note : 整个流程的关键部分都在内部完成,在网络中传输的都是加密后的密文,很巧妙。

4. ***部分源码分析
接下来便是对***源码部分的分析,由于本人对Python不是很精通,同时也没有深入的去研读其代码,代码部分便是略读了一下,在此简单的介绍下其源码,当然要感谢 @clowwindy 大大。
1) 源码文件结构

***有多中语言的版本,Python是原版,整个项目的代码不多,总共4000多行,因此整体看起来并不是很困难,有时间的同学可以仔细去看下,我下载的是2.9.1版本的代码。

|-shadowsocks-2.9.1
|  |---- LICENSE
|  |-debian
|  |  |---- shadowsocks.manpages
|  |  |---- compat
|  |  |---- install
|  |  |---- sslocal.1
|  |  |---- changelog
|  |  |---- init.d
|  |  |---- config.json
|  |  |---- ssserver.1
|  |  |-source
|  |  |  `---- format
|  |  |---- docs
|  |  |---- rules
|  |  |---- copyright
|  |  |---- control
|  |  `---- shadowsocks.default
|  |---- Dockerfile
|  |---- CHANGES
|  |-tests
|  |  |---- server-dnsserver.json
|  |  |---- nose_plugin.py
|  |  |---- coverage_server.py
|  |  |---- graceful_cli.py
|  |  |---- graceful_server.py
|  |  |---- aes.json
|  |  |---- server-multi-passwd-empty.json
|  |  |-socksify
|  |  |  |---- socks.conf
|  |  |  `---- install.sh
|  |  |---- jenkins.sh
|  |  |---- aes-cfb8.json
|  |  |---- test_udp_src.sh
|  |  |---- gen_multiple_passwd.py
|  |  |---- salsa20.json
|  |  |---- test_graceful_restart.sh
|  |  |---- test.py
|  |  |---- client-multi-server-ip.json
|  |  |---- setup_tc.sh
|  |  |---- server-multi-passwd-performance.json
|  |  |---- rc4-md5.json
|  |  |---- test_command.sh
|  |  |---- test_large_file.sh
|  |  |---- chacha20.json
|  |  |---- server-multi-passwd-table.json
|  |  |---- table.json
|  |  |-libsodium
|  |  |  `---- install.sh
|  |  |---- ipv6.json
|  |  |---- workers.json
|  |  |---- graceful.json
|  |  |---- chacha20-ietf.json
|  |  |---- fastopen.json
|  |  |---- salsa20-ctr.json
|  |  |---- test_udp_src.py
|  |  |---- server-multi-passwd.json
|  |  |---- ipv6-client-side.json
|  |  |---- rc4-md5-ota.json
|  |  |---- test_daemon.sh
|  |  |---- assert.sh
|  |  |---- server-multi-passwd-client-side.json
|  |  |---- aes-cfb1.json
|  |  |---- server-multi-ports.json
|  |  `---- aes-ctr.json
|  |---- MANIFEST.in
|  |-utils
|  |  |-fail2ban
|  |  |  `---- shadowsocks.conf
|  |  |---- README.md
|  |  `---- autoban.py
|  |---- README.md
|  |---- setup.py
|  |---- .gitignore
|  |---- CONTRIBUTING.md
|  |---- README.rst
|  |---- .travis.yml
|  |-shadowsocks
|  |  |---- encrypt.py
|  |  |---- lru_cache.py
|  |  |-crypto
|  |  |  |---- openssl.py
|  |  |  |---- util.py
|  |  |  |---- __init__.py
|  |  |  |---- rc4_md5.py
|  |  |  |---- sodium.py
|  |  |  `---- table.py
|  |  |---- server.py
|  |  |---- local.py
|  |  |---- udprelay.py
|  |  |---- shell.py
|  |  |---- __init__.py
|  |  |---- eventloop.py
|  |  |---- tcprelay.py
|  |  |---- common.py
|  |  |---- manager.py
|  |  |---- asyncdns.py
|  |  `---- daemon.py

***的主要代码都在shadowsocks目录下,其他目录提供了一下打包、测试和许可协议的内容,以下简单介绍一下各个文件的内容:

 

shadowsocks
|---- encrypt.py 提供加密函数调用
|---- lru_cache.py 实现LRU缓存,用于应对并发量比较大的状况
|-crypto 加密功能包
|  |---- openssl.py:openssl库调用
|  |---- util.py:工具类
|  |---- __init__.py
|  |---- rc4_md5.py:rc4-md5加密
|  |---- sodium.py:sodium加密
|  `---- table.py 
|---- server.py:ssserver实现
|---- local.py:sslocal实现
|---- udprelay.py:udp代理方式实现
|---- shell.py:shell命令实现
|---- __init__.py
|---- eventloop.py:事件循环
|---- tcprelay.py:tcp代理方式实现
|---- common.py:公用类
|---- manager.py:管理事件处理、连接处理等等
|---- asyncdns.py:异步dns解析
`---- daemon.py:守护线程实现

2) 源码分析

接下来分析一下ss的主流程代码,在整体对ss的工作原理有一个深入的了解。

I. local & server

 

local.py中的代码实现的是本地客户端的实现,代码很短,几十行,将日志等对主流程无用代码精简过后,如下:

 

# 加载配置文件
config = shell.get_config(True)
# 是否运行为守护进程
daemon.daemon_exec(config)
# 创建异步dns查询对象
dns_resolver = asyncdns.DNSResolver()
# 创建tcp代理方式转发对象
tcp_server = tcprelay.TCPRelay(config, dns_resolver, True)
# 创建udp代理方式转发对象
udp_server = udprelay.UDPRelay(config, dns_resolver, True)
# 创建事件循环处理对象
loop = eventloop.EventLoop()
# 将dns查询、tcp代理方式转发、udp代理方式转发绑定到事件循环
dns_resolver.add_to_loop(loop)
tcp_server.add_to_loop(loop)
udp_server.add_to_loop(loop)
# 预设信号处理函数,接收到正常的退出信号
signal.signal(getattr(signal, 'SIGQUIT', signal.SIGTERM), handler)
# SIGINT是键盘ctrl+c
signal.signal(signal.SIGINT, int_handler)
# 开启事件循环
loop.run()

server.py和local.py的基本流程时差不多的,与local.py不同的地方需要对多个客户端连接过来的请求进行处理,因此会多一些流程,下面是精简过的代码:

# 加载配置文件
config = shell.get_config(False)
# 是否运行为守护进程
daemon.daemon_exec(config)
# 创建异步dns查询对象
dns_resolver = asyncdns.DNSResolver()
# 添加tcp代理方式转发
tcp_servers.append(tcprelay.TCPRelay(a_config, dns_resolver, False))
# 添加udp代理方式转发
udp_servers.append(udprelay.UDPRelay(a_config, dns_resolver, False))
# 预设信号处理函数,接收到正常的退出信号
signal.signal(getattr(signal, 'SIGQUIT', signal.SIGTERM),child_handler)
# SIGINT是键盘ctrl+c
signal.signal(signal.SIGINT, int_handler)
# 创建事件循环处理对象
loop = eventloop.EventLoop()
# 将dns绑定到事件循环
dns_resolver.add_to_loop(loop)
# 实现多个线程处理
def run_server():
    ...
    loop = eventloop.EventLoop()
    dns_resolver.add_to_loop(loop)
    # 开启事件处理无限循环
    loop.run()
    ...

if int(config['workers']) > 1:
    ... # 生成多个线程来处理,也就是配置的workers
else:
    run_server()
II. udp & tcp

 

udprelay.py为UDP代理方式的实现,一下为精简后代码,抽取主要流程。

 

SOCKS5 UDP 请求
+----+------+------+----------+----------+----------+
|RSV | FRAG | ATYP | DST.ADDR | DST.PORT |   DATA   |
+----+------+------+----------+----------+----------+
| 2  |  1   |  1   | Variable |    2     | Variable |
+----+------+------+----------+----------+----------+
SOCKS5 UDP 响应
+----+------+------+----------+----------+----------+
|RSV | FRAG | ATYP | DST.ADDR | DST.PORT |   DATA   |
+----+------+------+----------+----------+----------+
| 2  |  1   |  1   | Variable |    2     | Variable |
+----+------+------+----------+----------+----------+
shadowsocks UDP 请求 (加密前)
+------+----------+----------+----------+
| ATYP | DST.ADDR | DST.PORT |   DATA   |
+------+----------+----------+----------+
|  1   | Variable |    2     | Variable |
+------+----------+----------+----------+
shadowsocks UDP 响应 (加密前)
+------+----------+----------+----------+
| ATYP | DST.ADDR | DST.PORT |   DATA   |
+------+----------+----------+----------+
|  1   | Variable |    2     | Variable |
+------+----------+----------+----------+
shadowsocks UDP 请求和响应 (加密后)
+-------+--------------+
|   IV  |    PAYLOAD   |
+-------+--------------+
| Fixed |   Variable   |
+-------+--------------+
命名:
dest   目标主机
local   ss的本地server
remote  ss的远程server
client  向其他UDPserver发送请求的UDP clients
server  处理用户请求的UDP server
#构造函数,config为配置文件,dns_resolver为dns解析,is_local用于区分是local还是server,stat_callback为回调函数用于统计,在manager.py中有调用
def __init__(self, config, dns_resolver, is_local, stat_callback=None):
    ... #全局参数声明、赋值
    #创建socket、绑定端口、设置阻塞方式
    server_socket = socket.socket(af, socktype, proto)
    server_socket.bind((self._listen_addr, self._listen_port))
    server_socket.setblocking(False)
    ...
# 获取服务器IP+端口
def _get_a_server(self):
    ...
# 关闭连接
def _close_client(self, client):
    ...
# 处理server
def _handle_server(self):
    # 获取udp server
    server = self._server_socket
    # 接收数据
    data, r_addr = server.recvfrom(BUF_SIZE)
    # 解密数据,如果是local的话,因为接收的是本地的数据,是不需要解密的
    data, key, iv = encrypt.dencrypt_all(self._password,self._method,data)
    # 解析头
    header_result = parse_header(data)
    addrtype, dest_addr, dest_port, header_length = header_result
    ...
    #如果是local需要将数据发送到remote,如果是remote需要将数据发送到dest
    if self._is_local:
        server_addr, server_port = self._get_a_server()
    else:
        server_addr, server_port = dest_addr, dest_port
    ...
    # 加密数据
    data = encrypt.encrypt_all_m(key, iv, m, self._method, data)
    ...
    #发送
    client.sendto(data, (server_addr, server_port))
# 处理client
def _handle_client(self, sock):
    # 接收数据
    data, r_addr = sock.recvfrom(BUF_SIZE)
    ...
    # 加密数据
    data = encrypt.encrypt_all(self._password, self._method, 0, data)
    ...
    # 获取地址
    client_addr = self._client_fd_to_server_addr.get(sock.fileno())
    ...
    # 发送
    self._server_socket.sendto(response, client_addr)
# 添加事件
def add_to_loop(self, loop):
    ...
# 处理事件
def handle_event(self, sock, fd, event):
    # 区分处理client或server
    if sock == self._server_socket:
        self._handle_server()
    elif sock and (fd in self._sockets):
        self._handle_client(sock)
# 周期性处理
def handle_periodic(self):
    ...
# 关闭UDP代理
def close(self, next_tick=False):

tcprelay.py 抽取主流程代理如下:

 

# 对于每个端口,有一个tcp relay,对于每个tcp relay
# 对于每个连接,有一个tcp relay handler来处理
# 对于每个handler,有两个socket,
# - local 连接到client,
# - remote 连接到远程主机(要访问的主机)
# 对于每个handler可以有如下几个阶段:
# sslocal
# - stage 0 接收从本地的method请求,并返回选择信息
# - stage 1 从本地接收请求地址,并进行dns解析
# - stage 2 udp相关
# - stage 3 dns解析完毕,连接远程主机
# - stage 4 连接中,并接收本地发送的数据
# - stage 5 远程主机连接成功,发送数据
# ssserver
# - stage 0 直接跳到阶段1
# - stage 1 从本地接收请求地址,并进行dns解析
# - stage 2 udp相关
# - stage 3 dns解析完毕,连接远程主机
# - stage 4 连接中,并接收本地发送的数据
# - stage 5 远程主机连接成功,发送数据
# 对于每个handler,有两个流方向:
# upstream:client->server,读本地,写入远程主机
# downstream:server->client,读远程主机,写入本地
# config为配置,dns_resolver为dns解析,is_local是否为client,stat_callback回调用于统计
def __init__(self, config, dns_resolver, is_local, stat_callback=None):
    ... # 全局参数声明、赋值
    # 创建socket、绑定端口、设置阻塞方式
    server_socket = socket.socket(af, socktype, proto)
        server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        server_socket.bind(sa)
        server_socket.setblocking(False)
...
# 添加到事件循环
def add_to_loop(self, loop):
    ...
# 处理事件
def handle_event(self, sock, fd, event):
    ...
    # 如果是server_socket 监听
    if sock == self._server_socket:
        ...
        # 监听
        conn = self._server_socket.accept()
        ...
    # 如果不是,进行事件处理
    else:
        ...
        handler.handle_event(sock, event)

# 周期性处理
def handle_periodic(self):
    ...
# 关闭TCP代理
def close(self, next_tick=False):

 

 

 

相关阅读:***原理详解(上篇)

***原理详解(中篇)

***原理详解(下篇)

 

网易云新用户大礼包:https://www.163yun.com/gift

本文来自网易云社区,经作者范鹏程授权发布。

转载于:https://www.cnblogs.com/163yun/p/9442723.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值