一种把指定程序的 TCP 流量重定向到代理的方法 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
gleport
V2EX    分享创造

一种把指定程序的 TCP 流量重定向到代理的方法

  •  5
     
  •   gleport 2018-08-03 14:43:53 +08:00 6520 次点击
    这是一个创建于 2681 天前的主题,其中的信息可能已经有所发展或是发生改变。

    graftcp

    一个可以把指定程序的 TCP 连接重定向到 SOCKS5 proxy 的工具。

    简介

    graftcp 可以把任何指定程序(应用程序、脚本、shell 等)的 TCP 连接重定向到 SOCKS5 代理。

    对比 tsocksproxychainsproxyChains-nggraftcp 并不使用 LD_PRELOAD 技巧来劫持共享库的 connect()、getaddrinfo() 等系列函数达到重定向目的,这种方法只对使用动态链接编译的程序有效,对于静态链接编译出来的程序,例如默认选项编译的 Go 程序proxychains-ng 就无效了graftcp 使用 ptrace(2) 系统调用跟踪或修改任意指定程序的 connect 信息,对任何程序都有效。工作原理后面将会解释。

    快速开始

    假设你正在运行默认地址 "localhost:1080" 的 SOCKS5 代理,首先启动 graftcp-local

    ./graftcp-local/graftcp-local 

    通过 graftcp 安装来自 golang.org 的 Go 包:

    ./graftcp go get -v golang.org/x/net/proxy 

    通过 graftcp 打开 Chromium / Chrome / Firefox 浏览器,网页的所有请求都会重定向到 SOCKS5 代理:

    ./graftcp chromium-browser 

    通过 graftcp 启动 Bash / Zsh / Fish,在这个新开的 shell 里面执行的任何新命令产生的 TCP 连接都会重定向到 SOCKS5 代理:

    % ./graftcp bash $ wget https://www.google.com 

    demo

    工作原理

    要达到重定向一个 app 发起的的 TCP 连接到其他目标地址并且该 app 本身对此毫无感知(透明代理)的目的,大概需要这些条件:

    • fork(2) 一个新进程,通过 execv(2) 启动该 app,并使用 ptrace(2) 进行跟踪,在 app 执行每一次 TCP 连接前,捕获并拦截这次 connect(2) 系统调用,获取目标地址的参数,并通过管道传给 graftcp-local
    • 修改这次 connect(2) 系统调用的目标地址参数为 graftcp-local 的地址,然后恢复执行被中断的系统调用。返回成功后,这个程序以为自己连的是原始的地址,但其实连的是 graftcp-local 的地址。这个就叫“移花接木”。
    • graftcp-local 根据连接信息和目标地址信息,与 SOCKS5 proxy 建立连接,把 app 的请求的数据重定向到 SOCKS5 proxy。

    简单的流程如下:

    +---------------+ +---------+ +--------+ +------+ | graftcp | dest host | | | | | | | (tracer) +---PIPE----->| | | | | | | ^ | info | | | | | | | | ptrace | | | | | | | | v | | | | | | | | +---------+ | | | | | | | | | | | connect | | connect | | connect | | | | +--------------->| graftcp +-------->| socks5 +-------->| dest | | | | | | -local | | proxy | | host | | | app | | req | | req | | req | | | |(tracee) +--------------->| +-------->| +-------->| | | | | | | | | | | | | | | | resp | | resp | | resp | | | | |<---------------+ |<--------+ |<--------+ | | +---------+ | | | | | | | +---------------+ +---------+ +--------+ +------+ 

    更多信息: https://github.com/hmgle/graftcp

    第 1 条附言    2018-10-27 14:52:24 +08:00
    更新:增加了重定向到 HTTP Proxy 的支持。
    第 2 条附言    2021-07-07 21:23:47 +08:00

    更新:增加了一个 mgraftcp 命令,不需要 graftcp-local 就可以启动客户端程序了。还没发布到正式分支,可以切换到 single-command 分支编译生成 mgraftcp。

    29 条回复    2021-07-07 21:26:12 +08:00
    beyondsoft
        1
    beyondsoft  
       2018-08-03 14:49:50 +08:00
    支持一个!
    pymumu
        2
    pymumu  
       2018-08-03 14:56:36 +08:00 via Android
    ptrace 厉害了,思路清奇,顶一个
    blanu
      &bsp; 3
    blanu  
       2018-08-03 14:58:22 +08:00
    大佬牛逼
    lidonghao
        4
    lidonghao  
       2018-08-03 15:10:15 +08:00
    厉害~
    slowman
        5
    slowman  
       2018-08-03 15:17:55 +08:00
    有两个问题。。
    1. 为什么要分成两个程序?还有一个是常驻的
    2. 也是只能处理 connect 吧,epoll 没有管
    dbw9580
        6
    dbw9580  
       2018-08-03 16:38:11 +08:00 via Android
    如果被重定向的 app 本身也会 fork 呢?
    sw0rd3n
        7
    sw0rd3n  
       2018-08-03 16:58:40 +08:00 via iPhone
    厉害!支持一下
    kurtrossel
        8
    kurtrossel  
       2018-08-03 17:24:26 +08:00
    感谢分享!

    好工具永远不嫌多
    gleport
        9
    gleport  
    OP
       2018-08-03 17:32:51 +08:00
    @1423
    1. 可以把它们合在同一个程序,但这个程序需要同时能使用 ptrace 及实现 SOCKS5 的客户端功能,而用 C 实现 SOCKS5 客户端的话比较折腾。还有一个原因是要处理 connect() 请求,它必须是一个监听并处理连接请求的 TCP 服务端。如果都嵌入同一个程序的话,就得每一个实例都新开一个新的端口进行监听,否则运行多个 graftcp 端口就冲突了。而每打开一个就新开一个监听端口的话,好像比较奇怪。当然这是可以实现的,这方面可以改进。
    2. 只处理 connect, epoll 不需要处理。
    gleport
        10
    gleport  
    OP
       2018-08-03 17:37:08 +08:00
    @dbw9580 被跟踪的进程再 fork 子进程,子进程也会被跟踪。里面的例子:
    ./graftcp bash 开一个 shell, 然后在这个 shell 里面运行所有命令产生的 connect 都会被重定向了。因为 ptrace 设置跟踪时加了 PTRACE_O_TRACECLONE 和 PTRACE_O_TRACEFORK、PTRACE_O_TRACEVFORK 标志位。
    sparkssssssss
        11
    sparkssssssss  
       2018-08-03 21:40:43 +08:00 via iPhone
    马克下下
    codehz
        12
    codehz  
       2018-08-04 09:31:59 +08:00
    @gleport #9 不一定要开端口。。。可以用 unix domain socket(
    gleport
        13
    gleport  
    OP
       2018-08-04 10:34:30 +08:00
    @codehz 这里没看明白,是用 Unix domain socket 代替处理 TCP connect 的监听服务吗?
    前面应该是我没有说清楚开端口的原因:因为不能通过修改 write buffer 往里面加入更多的数据(否则我们可以直接把 connect 重定向到 proxy, 每次 write/send 之前改写里面的 buffer, 把发送数据转换为 SOCKS5 协议的数据就可以了,不需要连接到现在 graftcp-local 这个中转处理数据的这一步。我之前踩了这个坑:),以为可以通过共享内存的方式为被跟踪的 app 新增一片更大的可读写内存,查了 execve 的手册才知道所有的共享内存在 execve 之前都被解除了),所以需要有一个 TCP server 来处理 app 的 connect 请求,这就是 graftcp-local
    开了 2233 这个端口监听的原因。

    正如 @1423 提到,graftcp-local 这部分的功能可以合并进 graftcp,如果这样实现的话,为了避免同时运行多个 graftcp 出现端口冲突的情况,每个 graftcp 监听的端口得不相同。这种做法带来的好处很明显,不需要 graftcp-local 了。后期有时间的话,我可能会把 graftcp-local 这部分的功能合并进 graftcp。有好的想法或实现的话,欢迎 PR 哦。

    考虑到调用 ptrace 和实现 SOCKS5 客户端的方便性,以及程序体积等因素,用 Rust 实现也许是个不错的选择。
    lemonda
        14
    lemonda  
       2018-08-04 11:38:05 +08:00
    请问能不能让运行的 PHP 程序也走代理?
    qf0129
        15
    qf0129  
       2018-08-04 12:01:02 +08:00 via iPhone
    shelll 里用 ssh 连接服务器可以经过这个代理吗
    gleport
        16
    gleport  
    OP
       2018-08-04 12:08:19 +08:00   1
    @lemonda 如果是 PHP 未运行前,可以通过 graftcp 启动一个 Shell, 如: `graftcp bash`,然后在这个新 Shell 内启动 PHP。
    如果是已经运行的 PHP,那么 graftcp 目前没有实现对正在运行的进程 attach 进行跟踪。
    Linux 里 ptrace 可以跟踪一个没有血缘关系的运行时进程,但需要以 root 权限修改默认的 /proc/sys/kernel/yama/ptrace_scope 值为 0:

    sudo su
    echo "0" > /proc/sys/kernel/yama/ptrace_scope

    需要这个功能的话,可以提一个 issue,我有时间实现一下,有人能 PR 就更好了~
    gleport
        17
    gleport  
    OP
       2018-08-04 12:10:38 +08:00
    @qf0129 可以的,graftcp-local 启动后,graftcp ssh user@xxx 就可以了。或者:
    graftcp bash
    在这个新 Shell 里面 ssh user@xxx
    tomfs
        18
    tomfs  
       2018-08-05 03:57:37 +08:00 via iPhone
    思路广
    bobyang
        19
    bobyang  
       2018-08-05 09:22:43 +08:00 via Android
    谢谢了,非常不错。
    lemonda
        20
    lemonda  
       2018-08-05 13:28:37 +08:00
    @gleport
    谢谢回复!
    我遇到的一个场景是:
    比如 WordPress 在国内某些服务器不能从官方服务器下载插件,一开始我是在国内服务器上开 ss-local,让所有访问外网的流量走代理,后来在 wp-config.php 中设置代理。一直没搞清楚怎么全局控制 PHP 使用的网络。
    另一个场景是:
    国外买到些普通的 http 代理,但是 http 代理过不了墙,于是使用 shadowsocks 访问国外服务器,国外服务器再连普通的 http 代理,目前就是用 proxychains 实现的。也一直没搞清楚怎么控制服务器对外访问用什么代理。
    graftcp 让我多了中选择。
    friskfly
        21
    friskfly  
       2018-08-05 15:22:00 +08:00
    我用 Proxifier, 不知道是不是一样的原理
    gleport
        22
    gleport  
    OP
       2018-08-05 19:32:37 +08:00
    lyztonny
        23
    lyztonny  
       2018-08-06 00:03:31 +08:00 via Android
    这和 tsocks 有什么功能上的差异吗?
    forgetandnew
        24
    forgetandnew  
       2018-08-06 03:34:58 +08:00 via iPhone
    牛皮
    gleport
        25
    gleport  
    OP
       2018-08-06 09:57:15 +08:00
    @lyztonny tsocks 功能类似,但无法实现重定向静态链接程序的 TCP 流量功能,graftcp 对这点做了改进。
    fournoas
        26
    fournoas  
       2018-08-06 12:07:10 +08:00
    跟 windows 上的这类软件类似
    uzumaki
        27
    uzumaki  
       2018-08-08 03:09:11 +08:00 via iPhone
    好东西
    mjikop1231
        28
    mjikop1231  
       2018-08-08 16:01:57 +08:00 via iPhone
    还没怎么看评论,先丢两个问题。
    1.和 proxychains 的比较?
    2.支持 golang 的程序么?
    gleport
        29
    gleport  
    OP
       2021-07-07 21:26:12 +08:00
    @mjikop1231 和 proxychains 的主要差别是支持 golang 的程序。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2664 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    Wrld is powered by solitude
    VERSION: 3.9.8.5 24ms UTC 05:56 PVG 13:56 LAX 21:56 JFK 00:56
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86