Socks5协议

socks5协议

原理

socks5 协议简介
实现SOCKS5协议 | 鱼儿的博客

socks是一种网络传输协议,主要用于客户端与外网服务器之间通讯的中间传递。根据OSI七层模型来划分,SOCKS属于会话层协议,位于表示层与传输层之间。主要流程如下图所示 ,参考:

Writing a simple SOCKS server in Python | Artem Golubin
Python编写socks5服务器 | HatBoy的个人主页
SOCKS协议第五版(SOCKS Protocol Version 5) - 作业部落 Cmd Markdown 编辑阅读器
https://www.ietf.org/rfc/rfc1928.txt
https://www.ietf.org/rfc/rfc1929.txt

1.协商
versionnmethodsmethods--------------------->versionmethod
1 byte1 byte0 to 255 bytes<--------------------1 byte1 byte

Here version field represents a version of the protocol, which equals to 5 in our case.

The methods field consists of a sequence of supported methods by the client.

Thus the nmethods field indicates the length of a methods sequence.

2.认证
versionulenunameplenpasswd------------>versionstatus
1 byte1 byte0 to 255 bytes1 byte0 to 255 bytes<-----------1 byte1 byte

The ulen and plen fields represent lengths of text fields so the server knows how much data it should read from the client.

The status field of 0 indicates a successful authorization, while other values treated as a failure.

3.请求
versioncmdrsvatypdst.addrdst.port
1 byte1 byte1 byte1 byte4 to 255 bytes2 bytes

The cmd field indicates the type of connection. This article is limited to CONNECT method only, which is used for TCP connections.

RSV RESERVED

ATYP address type of following address

DST.ADDR desired destination address

DST.PORT desired destination port in network octet order

versionreprsvatypbnd.addrbnd.port
1 byte1 byte1 byte1 byte4 to 255 bytes2 bytes

REP Reply field

BND.ADDR server bound address

BND.PORT server bound port in network octet order

bnd就是代理起的一个中介socket。

搭建TCP/UDP协议的中间人环境 - 知乎

4.数据转发

略,可参考上述文章。

编程

Writing a simple SOCKS server in Python | Artem Golubin
Python编写socks5服务器 | HatBoy的个人主页

1.! 表示大端
struct — 将字节串解读为打包的二进制数据 — Python 3.9.6 文档

2.send和sendall的区别
全网最详细python中socket套接字send与sendall的区别 - CoderEllison - 博客园

3.class socketserver.ThreadingMixIn Forking and threading versions of each type of server can be created using these mix-in classes.
socket.getsockname() 返回套接字本身的地址

参考代码如下(更详细的参考上述文章):

# 含有认证
import logging
import select
import socket
import struct
from socketserver import ThreadingMixIn, TCPServer, StreamRequestHandler #3.

logging.basicConfig(level=logging.DEBUG)
SOCKS_VERSION = 5

class ThreadingTCPServer(ThreadingMixIn, TCPServer):
    pass

class SocksProxy(StreamRequestHandler):
    username = 'username'
    password = 'password'

    def handle(self):
        logging.info('Accepting connection from %s:%s' % self.client_address)

        # greeting header
        # read and unpack 2 bytes from a client
        header = self.connection.recv(2)
        version, nmethods = struct.unpack("!BB", header) # 1.!大端,BB unsigned char 1个字节

        # socks 5
        assert version == SOCKS_VERSION
        assert nmethods > 0

        # get available methods
        methods = self.get_available_methods(nmethods)

        # accept only USERNAME/PASSWORD auth
        if 2 not in set(methods):
            # close connection
            self.server.close_request(self.request)
            return

        # send welcome message
        self.connection.sendall(struct.pack("!BB", SOCKS_VERSION, 2))
        if not self.verify_credentials():
            return
        # request
        version, cmd, _, address_type = struct.unpack("!BBBB", self.connection.recv(4))
        assert version == SOCKS_VERSION

        if address_type == 1:  # IPv4
            address = socket.inet_ntoa(self.connection.recv(4))
        elif address_type == 3:  # Domain name
            domain_length = self.connection.recv(1)[0]
            address = self.connection.recv(domain_length)
            address = socket.gethostbyname(address)
        port = struct.unpack('!H', self.connection.recv(2))[0]

        # reply
        try:
            if cmd == 1:  # CONNECT
                remote = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                remote.connect((address, port))
                bind_address = remote.getsockname()
                logging.info('Connected to %s %s' % (address, port))
            else:
                self.server.close_request(self.request)

            addr = struct.unpack("!I", socket.inet_aton(bind_address[0]))[0]
            port = bind_address[1]
            reply = struct.pack("!BBBBIH", SOCKS_VERSION, 0, 0, 1,
                                addr, port)

        except Exception as err:
            logging.error(err)
            # return connection refused error
            reply = self.generate_failed_reply(address_type, 5)

        self.connection.sendall(reply)

        # establish data exchange
        if reply[1] == 0 and cmd == 1:
            self.exchange_loop(self.connection, remote)

        self.server.close_request(self.request)

    def get_available_methods(self, n):
        methods = []
        for i in range(n):
            methods.append(ord(self.connection.recv(1)))
        return methods

    def verify_credentials(self):
        version = ord(self.connection.recv(1))
        assert version == 1

        username_len = ord(self.connection.recv(1))
        username = self.connection.recv(username_len).decode('utf-8')

        password_len = ord(self.connection.recv(1))
        password = self.connection.recv(password_len).decode('utf-8')

        if username == self.username and password == self.password:
            # success, status = 0
            response = struct.pack("!BB", version, 0)
            self.connection.sendall(response)
            return True

        # failure, status != 0
        response = struct.pack("!BB", version, 0xFF)
        self.connection.sendall(response) # 2.send的升级版本 全部发送
        self.server.close_request(self.request)
        return False

    def generate_failed_reply(self, address_type, error_number):
        return struct.pack("!BBBBIH", SOCKS_VERSION, error_number, 0, address_type, 0, 0)

    def exchange_loop(self, client, remote):

        while True:

            # wait until client or remote is available for read
            r, w, e = select.select([client, remote], [], [])

            if client in r:
                data = client.recv(4096)
                if remote.send(data) <= 0:
                    break

            if remote in r:
                data = remote.recv(4096)
                if client.send(data) <= 0:
                    break


if __name__ == '__main__':
    with ThreadingTCPServer(('192.168.10.1', 9011), SocksProxy) as server: # 修改为代理ip和端口
        server.serve_forever()

实验

代理:192.168.10.1
socks5客户端:192.168.10.129
客户端欲访问的服务器:192.168.10.128

1.代理直接运行上述代码即可,注意修改下main里的ip,为代理的ip,这里是192.168.10.1。

2.客户端可以用linux的proxychains,如果使用认证的话,就必须在/ect/proxychains.conf最后加上username和passward,不认证的话,就不用username和password。如下图所示,其中,192.168.10.1是代理服务器,间隔是tab键。

socks5	192.168.10.1	9011	username	password

3.服务器(192.168.10.128)可以用python起个web服务(也可以就是用www.baidu.com等),如果是python2,使用的命令是:

python -m SimpleHTTPServer 8088

如果是python3,使用的命令是:

python -m http.server 8088

抓包分析

使用wireshark,过滤条件。

(ip.src==192.168.10.1 || ip.dst==192.168.10.1) and tcp

前面的协商,认证,请求都是在tcp data放的内容。这是第一条。05是version,02是两种方法,00是不认证,02是username,password。

其它依次分析即可,到数据转发,就是在已经获得的remote_addr和port基础上。把客户端的http数据加上remote_addr和port作为tcp的ip和port转发,然后和remote联系。后面就没有tcp data了。然后通过代理转发即可。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
老鱼socks5服务端是一个网络代理服务器软件,可以让用户通过该服务器连接互联网。它采用socks5协议,这是一种支持TCP和UDP传输的代理协议。 老鱼socks5服务端的工作原理如下:当用户请求访问某个网站或服务时,首先用户的浏览器或应用程序会将请求发送给老鱼socks5服务端。服务端会解析请求,并根据用户的配置将请求转发给目标服务器。目标服务器将响应数据返回到老鱼socks5服务端,再由服务端转发给用户。这样用户和目标服务器之间的通信就通过了中间的代理服务器。 老鱼socks5服务端的优点在于它可以实现细粒度的权限控制和灵活的配置。管理员可以根据需求设置不同用户的访问权限,比如限制某些网站的访问或限制某些端口的访问。此外,老鱼socks5服务端支持本地DNS解析,可以避免因网络问题导致无法正常访问某些网站的情况。 老鱼socks5服务端还提供了一些安全性的功能。它支持用户认证,只有经过认证的用户才能连接到服务端。此外,它还支持加密传输,可以通过配置使用TLS/SSL等加密协议来保护数据的安全性。 综上所述,老鱼socks5服务端是一款功能强大、安全可靠的网络代理服务器软件,可以提供方便的访问互联网和灵活的权限控制。无论是个人用户还是企业用户,都可以选择老鱼socks5服务端来搭建自己的代理服务器,以满足不同的需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值