无需管理员权限的SSR(Socks5)客户端如何转换TLS/HTTP数据包?及无TUN/TAP实现全局TCP代理的技术咨询
无需管理员权限的SSR(Socks5)客户端如何转换TLS/HTTP数据包?及无TUN/TAP实现全局TCP代理的技术咨询
一、普通SSR客户端的核心工作逻辑(无管理员权限版)
其实这类客户端根本不需要直接修改TLS/HTTP数据包的内容,核心是拦截应用的网络连接发起请求,把连接重定向到本地SOCKS5代理端口,具体分两种常见实现方式:
- 系统代理设置劫持:客户端会修改系统级的代理配置(比如Windows的Internet选项、macOS的网络偏好设置),把SOCKS5代理设为
127.0.0.1:XXXX(本地SSR监听的端口)。当浏览器(比如Firefox开启“跟随系统代理”)调用系统网络API时,会自动走这个本地代理,由SSR客户端把请求转发到远端代理服务器。 - API钩子(Hooking):针对没有遵守系统代理设置的应用,客户端会通过钩子技术拦截它们的网络调用(比如Windows下的
connect/WSAConnect函数,Linux/macOS下的connect系统调用)。当应用试图直接连接目标服务器时,钩子会把连接目标改成本地SOCKS5端口,后续所有TLS/HTTP数据都会通过这个代理通道传输,应用完全感知不到这个变化。
二、如何检测这些代理变化?
当然可以检测到,不管是用系统命令还是Python都能做到:
- 系统命令方式:
- Windows:打开命令提示符,执行
netstat -ano,查看应用进程对应的连接目标,如果都是指向127.0.0.1的某个端口(比如1080),说明走了本地代理;也可以用reg query "HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings"查看注册表中的代理相关项,看是否被设置为本地SOCKS5地址。 - Linux/macOS:执行
lsof -i | grep 你的应用进程名,或者netstat -tulpn,同样能看到应用连接的是本地代理端口;用networksetup -getwebproxy Wi-Fi(替换成你的网络接口)可以查看系统代理配置。
- Windows:打开命令提示符,执行
- Python方式:
- 用
psutil库遍历进程的网络连接,比如:import psutil for proc in psutil.process_iter(['pid', 'name']): try: connections = proc.connections() for conn in connections: if conn.status == 'ESTABLISHED' and conn.raddr.ip == '127.0.0.1' and conn.raddr.port == 1080: print(f"进程{proc.name()}({proc.pid})正在使用本地代理") except: pass - 用
scapy抓包,过滤出站TCP数据包,看目标IP是不是本地代理地址,而不是实际的服务器地址。
- 用
三、不用TUN/TAP实现全局TCP代理的可行方案
你尝试用pydivert抓包改包的思路理论上可行,但复杂度极高——需要手动处理TCP握手、序列号、确认号,还要维护每个连接的映射关系,很容易出现丢包、连接中断的问题。更推荐下面两种方案:
- 方案1:复用系统代理+API钩子
先修改系统代理设置覆盖大部分遵守系统配置的应用,再用API钩子处理那些不遵守的应用(比如某些桌面客户端)。这种方式不需要管理员权限,实现难度相对低,也是大多数SSR客户端的做法。 - 方案2:LD_PRELOAD(仅Linux/macOS)
写一个动态链接库,替换系统的connect函数,让所有调用这个函数的进程都把连接转向本地代理。然后通过export LD_PRELOAD=你的库路径来让用户级别的所有进程加载这个库,实现全局代理。Windows下类似的是用Detours库做用户级钩子,但实现起来稍复杂。
如果一定要坚持抓包改包的思路,你需要做这些核心步骤:
- 用pydivert拦截所有出站TCP SYN包,记录源IP/端口、目标IP/端口的映射关系。
- 在本地启动一个SOCKS5代理服务,当收到应用的SYN包时,主动和代理建立连接,告知代理要连接的目标服务器。
- 后续把应用发送的数据包的目标IP/端口改成代理的地址,把代理返回的数据包的源IP/端口改成目标服务器的地址,再转发给应用。但这个过程需要处理大量TCP协议细节,非常容易出错,不推荐作为首选方案。
备注:内容来源于stack exchange,提问作者disastar




