使用wireshark抓包分析SOCKS5协议

摘要:
目录编写SOCKS5服务器运行代码使用SOCKS5 Server脚本和curl命令分析捕获的数据包了解SOCKS5协议的工作过程通信软件课程选择分析SOCKS5。为了了解该协议在网络通信中的工作原理,数据包捕获实践如下。BB“,header)#设置socks5协议。METHOD字段的数量大于0 assertversion==SOCKS_VERSIONassertnmethods˃0#接受支持的方法=self.get_available_methods#如果0notinset:self.server.close_requestreturn#发送协商响应数据包self.connection.Send(struct.pack(“!BBBB”,self.connection.rev)assertversion==SOCKS_VERSIONifaddress_ type==1:#IPv4address=套接字。inet_notaelifaddress_type==3:#域名域_长度=self。联系recv[0]地址=自身。联系recv#address=套接字。gethostbyname_type==4:#IPv6addr_ip=self.connection.revadress=套接字。inet_ ntopelse:self.server。close_ Requestreturnport=结构。unpack('!H',self.connection.recv)[0]#响应。只有CONNECT请求尝试:ifcmd==1:#CONNECTremote=socket。套接字远程。connectbind are supported_address=remote.getsockname()printelse:self.server。close_requestaddr=结构.解包(“!

目录

通信软件课选择了分析SOCKS5协议,想看一下这个协议在网络通信中是如何进行的,遂抓包实践如下。

编写SOCKS5服务器运行代码(参考自Python编写socks5服务器
import select
import socket
import struct
from socketserver import StreamRequestHandler, ThreadingTCPServer
SOCKS_VERSION = 5
class SocksProxy(StreamRequestHandler):
    def handle(self):
        print('Accepting connection from {}'.format(self.client_address))
        # 协商
        # 从客户端读取并解包两个字节的数据
        header = self.connection.recv(2)
        version, nmethods = struct.unpack("!BB", header)
        # 设置socks5协议,METHODS字段的数目大于0
        assert version == SOCKS_VERSION
        assert nmethods > 0
        # 接受支持的方法
        methods = self.get_available_methods(nmethods)
        # 无需认证
        if 0 not in set(methods):
            self.server.close_request(self.request)
            return
        # 发送协商响应数据包
        self.connection.sendall(struct.pack("!BB", SOCKS_VERSION, 0))
        # 请求
        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.decode("UTF-8"))  # 将域名转化为IP,这一行可以去掉
        elif address_type == 4: # IPv6
            addr_ip = self.connection.recv(16)
            address = socket.inet_ntop(socket.AF_INET6, addr_ip)
        else:
            self.server.close_request(self.request)
            return
        port = struct.unpack('!H', self.connection.recv(2))[0]
        # 响应,只支持CONNECT请求
        try:
            if cmd == 1:  # CONNECT
                remote = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                remote.connect((address, port))
                bind_address = remote.getsockname()
                print('Connected to {} {}'.format(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, address_type, addr, port)
            # 注意:按照标准协议,返回的应该是对应的address_type,但是实际测试发现,当address_type=3,也就是说是域名类型时,会出现卡死情况,但是将address_type该为1,则不管是IP类型和域名类型都能正常运行
            reply = struct.pack("!BBBBIH", SOCKS_VERSION, 0, 0, 1, addr, port)
        except Exception as err:
            logging.error(err)
            # 响应拒绝连接的错误
            reply = self.generate_failed_reply(address_type, 5)
        self.connection.sendall(reply)
        # 建立连接成功,开始交换数据
        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 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:
            # 等待数据
            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__':
    # 使用socketserver库的多线程服务器ThreadingTCPServer启动代理
    with ThreadingTCPServer(('127.0.0.1', 9011), SocksProxy) as server:
        server.serve_forever()

本文采用参考链接中curl请求的方法经由代理服务器获取目标网站的信息curl -v --socks5 127.0.0.1:9011 http://www.baidu.com
对于python编写的client程序

import socket
import socks
import requests
socks.set_default_proxy(socks.SOCKS5, "127.0.0.1", 9011, username=None, password=None)
socket.socket = socks.socksocket
head={'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36'}
print(requests.get('https://www.baidu.com', head).text)

需要注意的一点是import的socks包是第三方库PySocks,pip安装时使用命令pip install PySocks(参考自No module named 'socks', i have tried everything

使用SOCKS5服务器脚本和curl命令

在本地运行SOCKS5服务器脚本,执行curl命令得到如图结果
使用wireshark抓包分析SOCKS5协议第1张
放在远端主机上运行SOCKS5服务器脚本,在本地执行curl命令得到如图结果
使用wireshark抓包分析SOCKS5协议第2张
这时查询listen的主机端口netstat -anp
使用wireshark抓包分析SOCKS5协议第3张
可能需要将Local Address 设置为0.0.0.0,外来的连接才可以连接,查相关的资料(全零网络IP地址0.0.0.0表示意义详谈Linux的netstat查看端口是否开放见解(0.0.0.0与127.0.0.1的区别))果然如此。
遂将服务器脚本的倒数第二行"127.0.0.1"改为"0.0.0.0",重复之前步骤发现与本地测试的结果差不多,并在该过程中抓取到curl请求远程主机发送百度首页请求的数据包。
使用wireshark抓包分析SOCKS5协议第4张

分析抓取到的数据包理解SOCKS5协议的工作过程(感谢socks5代理服务器协议的说明让我预先知道SOCKS5协议数据消息传递的机理)

使用wireshark抓包分析SOCKS5协议第5张
握手
使用wireshark抓包分析SOCKS5协议第6张
本地发送认证请求,05表示SOCKS5,02表示接受2种认证方法,00表示无需认证的认证方法,01表示GSSAPI
使用wireshark抓包分析SOCKS5协议第7张
服务端确认无需认证
使用wireshark抓包分析SOCKS5协议第8张
本地查询DNS信息
使用wireshark抓包分析SOCKS5协议第9张
第一个01说明TCP,第二个01说明后面是(DNS查询出的)IP地址,3d87b920指目标网站的ip地址61.135.185.32,0050指80端口
使用wireshark抓包分析SOCKS5协议第10张
SOCKS5服务器成功连接上目标网站后发送成功响应,ac150007指服务器连接目标网站用到的ip地址172.21.0.7,8a10指使用的端口35344,也可以不说明连接的具体细节,直接0500后面接8个字节的0
使用wireshark抓包分析SOCKS5协议第11张
本机向SOCKS5服务器发送目标网站的http请求
使用wireshark抓包分析SOCKS5协议第12张
获取到目标网页信息
使用wireshark抓包分析SOCKS5协议第13张
挥手
拓展阅读:SOCKS 5 协议抓包分析

免责声明:文章转载自《使用wireshark抓包分析SOCKS5协议》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇oracle 标识符无效net core开发环境准备下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Jackson总结:常用注解、整合spring、自定义JsonSerializer

转自Jackson总结:常用注解、整合spring、自定义JsonSerializer Jackson作为springMVC默认的MessageConverter(消息序列化工具),经常在项目中使用,如果熟悉Jackson常用的使用方法,特性化机制,就会事半功倍,极大提高前后端数据交互的灵活性。 maven依赖 <dependency>...

ip头、tcp头、udp头详解及定义,结合Wireshark抓包看实际情况

公司的同事们在分析网页加载慢的问题,忽然使用到了Wireshark工具,我就像发现新大陆一样好奇,赶紧看了看,顺便复习了一下相关协议。上学时学的忘的差不多了,汗颜啊! 报文封装整体结构 mac帧头定义 /*数据帧定义,头14个字节,尾4个字节*/ typedef struct _MAC_FRAME_HEADER { char m_cDstMacAddr...

nodejs操作消息队列RabbitMQ

一. 什么是消息队列 消息队列(Message Queue,简称MQ),从字面意思上看,本质是个队列,FIFO先入先出,只不过队列中存放的内容是message而已。其主要用途:不同进程Process/线程Thread之间通信。 为什么会产生消息队列?有几个原因: 不同进程(process)之间传递消息时,两个进程之间耦合程度过高,改动一个进程,引发必须修改...

win2000/2003终端服务全程图说攻略

  WIN2003 server 网上注册,解决终端服务120天限制,激活2003终端授权服务器 关键字: 远程桌面 windows2003 2003 server 网上 注册 解决 终端 服务 120 天限制 激活 1,开始->程序->管理工具->终端服务器授权,选择未激活的服务器名称,选择”属性”,记下20位的产品ID(如果没有服务器...

JMS学习的个人理解笔记

Jms即java消息服务javamessage service,所谓的面向消息编程,主要应用在企业内部各个系统之间做接口,以异步方式传递消息数据。 Jms有2种传送模式,先来看第一种,即点对点传送模式 首先是参与整个过程的系统,总共有3个,分别是发送方系统、jms服务器、接收方系统,原理很简单,基本步骤如下: 1、  发送方将jms消息发送到jms服务器;...

ZeroMQ接口函数之 :zmq_setsockopt –设置ZMQ socket的属性

ZeroMQ API 目录 :http://www.cnblogs.com/fengbohello/p/4230135.html 本文地址 :http://www.cnblogs.com/fengbohello/p/4398953.html 翻译:郝峰波 mail : fengbohello@qq.com ZeroMQ 官方地址 :http://api....