V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
gleport
V2EX  ›  分享创造

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

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

    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
        3
    blanu  
       2018-08-03 14:58:22 +08:00
    大佬牛逼
    lidonghao
        4
    lidonghao  
       2018-08-03 15:10:15 +08:00
    厉害~
    1423
        5
    1423  
       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 标志位。
    coolloves
        11
    coolloves  
       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   ·   实用小工具   ·   2879 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 14:51 · PVG 22:51 · LAX 06:51 · JFK 09:51
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.