爱意满满的作品展示区。
gleport

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

  •  5
     
  •   gleport · Aug 3, 2018 · 7277 views
    This topic created in 2865 days ago, the information mentioned may be changed or developed.

    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

    Supplement 1  ·  Oct 27, 2018
    更新:增加了重定向到 HTTP Proxy 的支持。
    Supplement 2  ·  Jul 7, 2021

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

    29 replies    2021-07-07 21:26:12 +08:00
    beyondsoft
        1
    beyondsoft  
       Aug 3, 2018
    支持一个!
    pymumu
        2
    pymumu  
       Aug 3, 2018 via Android
    ptrace 厉害了,思路清奇,顶一个
    blanu
        3
    blanu  
       Aug 3, 2018
    大佬牛逼
    lidonghao
        4
    lidonghao  
       Aug 3, 2018
    厉害~
    slowman
        5
    slowman  
       Aug 3, 2018
    有两个问题。。
    1. 为什么要分成两个程序?还有一个是常驻的
    2. 也是只能处理 connect 吧,epoll 没有管
    dbw9580
        6
    dbw9580  
       Aug 3, 2018 via Android
    如果被重定向的 app 本身也会 fork 呢?
    sw0rd3n
        7
    sw0rd3n  
       Aug 3, 2018 via iPhone
    厉害!支持一下
    kurtrossel
        8
    kurtrossel  
       Aug 3, 2018
    感谢分享!

    好工具永远不嫌多
    gleport
        9
    gleport  
    OP
       Aug 3, 2018
    @1423
    1. 可以把它们合在同一个程序,但这个程序需要同时能使用 ptrace 及实现 SOCKS5 的客户端功能,而用 C 实现 SOCKS5 客户端的话比较折腾。还有一个原因是要处理 connect() 请求,它必须是一个监听并处理连接请求的 TCP 服务端。如果都嵌入同一个程序的话,就得每一个实例都新开一个新的端口进行监听,否则运行多个 graftcp 端口就冲突了。而每打开一个就新开一个监听端口的话,好像比较奇怪。当然这是可以实现的,这方面可以改进。
    2. 只处理 connect, epoll 不需要处理。
    gleport
        10
    gleport  
    OP
       Aug 3, 2018
    @dbw9580 被跟踪的进程再 fork 子进程,子进程也会被跟踪。里面的例子:
    ./graftcp bash 开一个 shell, 然后在这个 shell 里面运行所有命令产生的 connect 都会被重定向了。因为 ptrace 设置跟踪时加了 PTRACE_O_TRACECLONE 和 PTRACE_O_TRACEFORK、PTRACE_O_TRACEVFORK 标志位。
    sparkssssssss
        11
    sparkssssssss  
       Aug 3, 2018 via iPhone
    马克下下
    codehz
        12
    codehz  
       Aug 4, 2018
    @gleport #9 不一定要开端口。。。可以用 unix domain socket(
    gleport
        13
    gleport  
    OP
       Aug 4, 2018
    @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  
       Aug 4, 2018
    请问能不能让运行的 PHP 程序也走代理?
    qf0129
        15
    qf0129  
       Aug 4, 2018 via iPhone
    shelll 里用 ssh 连接服务器可以经过这个代理吗
    gleport
        16
    gleport  
    OP
       Aug 4, 2018   ❤️ 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
       Aug 4, 2018
    @qf0129 可以的,graftcp-local 启动后,graftcp ssh user@xxx 就可以了。或者:
    graftcp bash
    在这个新 Shell 里面 ssh user@xxx
    tomfs
        18
    tomfs  
       Aug 5, 2018 via iPhone
    思路广
    bobyang
        19
    bobyang  
       Aug 5, 2018 via Android
    谢谢了,非常不错。
    lemonda
        20
    lemonda  
       Aug 5, 2018
    @gleport
    谢谢回复!
    我遇到的一个场景是:
    比如 WordPress 在国内某些服务器不能从官方服务器下载插件,一开始我是在国内服务器上开 ss-local,让所有访问外网的流量走代理,后来在 wp-config.php 中设置代理。一直没搞清楚怎么全局控制 PHP 使用的网络。
    另一个场景是:
    国外买到些普通的 http 代理,但是 http 代理过不了墙,于是使用 shadowsocks 访问国外服务器,国外服务器再连普通的 http 代理,目前就是用 proxychains 实现的。也一直没搞清楚怎么控制服务器对外访问用什么代理。
    graftcp 让我多了中选择。
    friskfly
        21
    friskfly  
       Aug 5, 2018
    我用 Proxifier, 不知道是不是一样的原理
    gleport
        22
    gleport  
    OP
       Aug 5, 2018
    lyztonny
        23
    lyztonny  
       Aug 6, 2018 via Android
    这和 tsocks 有什么功能上的差异吗?
    forgetandnew
        24
    forgetandnew  
       Aug 6, 2018 via iPhone
    牛皮
    gleport
        25
    gleport  
    OP
       Aug 6, 2018
    @lyztonny tsocks 功能类似,但无法实现重定向静态链接程序的 TCP 流量功能,graftcp 对这点做了改进。
    fournoas
        26
    fournoas  
       Aug 6, 2018
    跟 windows 上的这类软件类似
    uzumaki
        27
    uzumaki  
       Aug 8, 2018 via iPhone
    好东西
    mjikop1231
        28
    mjikop1231  
       Aug 8, 2018 via iPhone
    还没怎么看评论,先丢两个问题。
    1.和 proxychains 的比较?
    2.支持 golang 的程序么?
    gleport
        29
    gleport  
    OP
       Jul 7, 2021
    @mjikop1231 和 proxychains 的主要差别是支持 golang 的程序。
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   2784 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 78ms · UTC 09:33 · PVG 17:33 · LAX 02:33 · JFK 05:33
    ♥ Do have faith in what you're doing.