hmgle/graftcp
hmgle/graftcp has 2,613 GitHub stars and 204 forks. GitHot measured +2 stars in 24 hours and +3 in 7 days. Last checked 2026-11-12 14:39 UTC. Last push 2026-08-25.
A flexible tool for redirecting a program's TCP, UDP, and DNS traffic to SOCKS5 or HTTP proxies.
实测星标增长
- 24小时
- +2
- 7天
- +3
- 15天
- 正在收集历史
- 1个月
- 正在收集历史
各周期内的星标净变化,周期相互重叠,并非每日新增。缺失的测量保持空白。星标代表关注度,不代表软件质量。
README.zh-CN.md
原文graftcp
English | 简体中文
简介
graftcp 通过 ptrace(2) 拦截目标程序的 socket syscall,把任意 Linux 进程的 TCP 连接重定向到 SOCKS5 或 HTTP 代理。显式启用后,它也可以把 UDP/53 DNS 查询重定向到内嵌的 DNS-over-TCP 转发器,并通过 SOCKS5 UDP ASSOCIATE 或 direct UDP 代理通用 UDP。
旧的 graftcp + graftcp-local 双程序模型已经合并成单个命令:
graftcp是主入口。mgraftcp保留为兼容别名。- 不再需要单独启动本地守护进程。
- 不再使用 FIFO、
/proc扫描或netlink反查连接。 - 每次被拦截的连接都会分配一个唯一的 Loopback token IP,由内嵌本地代理在进程内直接回查原始目标地址。
和 tsocks、proxychains、proxychains-ng 不同,graftcp 仍然不依赖 LD_PRELOAD,所以对静态链接程序(例如大多数 Go 二进制)同样有效。
安装
graftcp 仅支持 Linux。构建需要 Go 和 C 工具链。
git clone https://github.com/hmgle/graftcp.git
cd graftcp
make
构建产物是 local/graftcp。local/mgraftcp 会作为兼容别名一并生成。
安装到系统:
sudo make install
用法
$ ./local/graftcp --help
Usage: graftcp [-hn] [-b value] [--config value] [--disable-dns] [--disable-udp] [--dns-server value] [--enable-debug-log] [--enable-dns] [--enable-udp] [--http_proxy value] [--select_proxy_mode value] [--socks5 value] [--socks5_password value] [--socks5_username value] [-u value] [--version] [-w value] [parameters ...]
-b, --blackip-file=value
The IP in black-ip-file will connect direct
--config=value
Path to the configuration file
--disable-dns Disable DNS proxy
--disable-udp Disable generic UDP proxy
--dns-server=value
DNS upstream server address, e.g.: 1.1.1.1:53 [1.1.1.1:53]
--enable-debug-log
Enable debug log
--enable-dns Enable DNS proxy for UDP/53 queries
--enable-udp Enable generic UDP proxy
-h, --help Display this help and exit
--http_proxy=value
http proxy address, e.g.: 127.0.0.1:8080
-n, --not-ignore-local
Connecting to local is not changed by default, this option
will redirect it to SOCKS5
--select_proxy_mode=value
Set the mode for select a proxy [auto | random |
only_http_proxy | only_socks5 | direct] [auto]
--socks5=value
SOCKS5 address, e.g.: 127.0.0.1:1080 or unix:/path/tor.sock
(default 127.0.0.1:1080 when no proxy is configured)
--socks5_password=value
SOCKS5 password
--socks5_username=value
SOCKS5 username
-u, --username=value
Run command as USERNAME handling setuid and/or setgid
--version Print version information
-w, --whiteip-file=value
Only redirect the connect that destination ip in the
white-ip-file to SOCKS5
示例:
./local/graftcp --socks5 127.0.0.1:1080 curl https://example.com
./local/graftcp --select_proxy_mode only_socks5 --socks5 unix:/path/tor.sock curl https://example.com
./local/graftcp --enable-dns --dns-server 1.1.1.1:53 curl https://example.com
./local/graftcp --enable-udp --socks5 127.0.0.1:1080 your-udp-client
./local/graftcp --http_proxy 127.0.0.1:8080 git clone https://github.com/hmgle/graftcp.git
./local/graftcp bash --rcfile <(echo 'PS1="(graftcp) $PS1"')
已经使用合并后二进制的用户仍可用 mgraftcp,用法与 graftcp 相同。
配置文件
graftcp 按下面顺序查找配置文件:
--config指定的文件$(dirname $0)/graftcp.conf$(dirname $0)/mgraftcp.conf$XDG_CONFIG_HOME/graftcp/graftcp.conf$XDG_CONFIG_HOME/mgraftcp/mgraftcp.conf$HOME/.config/graftcp/graftcp.conf$HOME/.config/mgraftcp/mgraftcp.conf/etc/graftcp/graftcp.conf/etc/mgraftcp/mgraftcp.conf
示例配置见 example-graftcp.conf。example-mgraftcp.conf 作为别名名称的兼容示例保留。
配置项与命令行参数一一对应:dns_proxy = true -> --enable-dns,udp_proxy = true -> --enable-udp,ignore_local = false -> --not-ignore-local。命令行参数会覆盖配置文件。
工作原理
graftcp先启动内嵌本地 TCP listener,再用ptrace(2)跟踪目标命令。- 每次拦截到
connect(2)时,把原始目标地址登记到进程内路由表,并从127.0.0.0/8分配一个唯一 Loopback token IP。 - 把 tracee 的目的 sockaddr 改写成这个 token IP 和内嵌 listener 的端口。
- 内嵌 listener
accept到连接后,从LocalAddr()取出 token,直接在进程内解析出原始目标地址,再按 SOCKS5 / HTTP / direct 策略发起真实连接。 - syscall 返回后,
graftcp会 best-effort 恢复 tracee 原始sockaddr缓冲区。
对 IPv6 connect(2),当前实现会改写为 IPv4-mapped Loopback 地址 ::ffff:127.x.y.z,从而复用同一套 token 路由逻辑。
启用 DNS 代理时,graftcp 还会启动一个内嵌 UDP DNS listener。目标端口为 53 的 UDP connect() 和 sendto() 会被改写到这个 listener,每个 DNS payload 再通过同一套 SOCKS5 / HTTP / direct 选择策略,以 TCP 方式转发到配置的上游 DNS 服务器。DNS sockaddr 改写会在 syscall 返回后刻意保留,使 musl 这类会用发送侧 sockaddr 缓冲区校验响应来源的 resolver,继续把内嵌 DNS proxy 视为预期 peer。
启用通用 UDP 代理时,graftcp 会启动另一个内嵌 UDP listener。UDP connect()、sendto() 和 sendmsg() 的目标地址会被改写成 loopback token endpoint;内嵌 listener 再把 token 映射回原始目标地址,并在选中 SOCKS5 时通过 SOCKS5 UDP ASSOCIATE 转发,或在 direct 模式和 fallback 场景下用 direct UDP 转发。
说明
- 仅支持 Linux。
- 仍然受
ptrace(2)权限限制;如果跟踪失败,请检查 Yamaptrace_scope、能力集或是否需要 root。 - 在支持的内核上,
graftcp会安装 seccomp-BPF 过滤器,使只有 socket 相关 syscall 才陷入 tracer,从而降低跟踪开销;在不支持 seccomp 过滤的内核上则回退为对每个 syscall 停点。该行为自动生效,无需配置。 - 默认忽略本地目标地址;如果希望本地连接也走代理,使用
--not-ignore-local。 - DNS 代理默认关闭;使用
--enable-dns启用 UDP/53 DNS-over-TCP 路径,使用--dns-server指定上游服务器。 - 通用 UDP 代理默认关闭;使用
--enable-udp启用。 - HTTP 代理模式不支持通用 UDP。
auto会优先尝试 SOCKS5 UDP,失败时回退 direct UDP;only_http_proxy会拒绝通用 UDP session。 - SOCKS5 可以使用 TCP endpoint(
127.0.0.1:1080),也可以用 Unix socket endpoint(unix:/path/tor.sock或/path/tor.sock)承载 TCP CONNECT 流量。SOCKS5 UDP ASSOCIATE 仍然要求 TCP SOCKS5 endpoint。 - 当
--socks5和--http_proxy都没有配置时,graftcp 会回退到 SOCKS5127.0.0.1:1080。只要配置了任意一个代理,这个隐含默认值就不再生效,因此单独使用--http_proxy时不会去连接任何 SOCKS5 endpoint。 - 同时启用 DNS 和通用 UDP 时,UDP/53 优先走 DNS-over-TCP 路径。
- 配置文件支持代理地址和常见路由选项;命令行参数仍然覆盖配置文件。
- TCP 和通用 UDP syscall 地址缓冲区会在
connect()/sendto()/sendmsg()返回后 best-effort 恢复;DNS proxy 改写会保留下来,以兼容 resolver 的来源地址校验。如果客户端强依赖recvfrom()返回原始远端地址,透明性仍可能不完整。 - IPv6 故意统一走 IPv4-mapped loopback;依赖
IPV6_V6ONLY=1的 socket 不在当前设计目标内。 - socket 跟踪按被跟踪 pid/fd 状态做 best-effort;
dup*和fcntl(F_DUPFD*)会 best-effort 复制,但close_range()、unshare(CLONE_FILES)和完整共享 fd table 语义属于有意不覆盖的边界。 - loopback token 会在本地 listener 成功
accept()或空闲清理时回收,不保证覆盖每一个失败或放弃的连接。 - 当前设计有意优先采用单进程 tracer/proxy 与 loopback token 路由,不再使用旧的守护进程、FIFO 和 socket 反查模型。
FAQ 与技巧
目标地址是 localhost 时,graftcp 会重定向吗?
默认不会。本地目标地址会被忽略。使用 --not-ignore-local 可以一并重定向。想忽略更多地址,把它们加入 black-ip 文件(-b);想只重定向特定地址,加入 white-ip 文件(-w)。详见 graftcp --help。
我正遭受 DNS 缓存投毒攻击,graftcp 能处理 DNS 吗?
可以。使用 --enable-dns 把 UDP/53 查询以 DNS-over-TCP 经选定代理转发,--dns-server 指定上游解析器。dnscrypt-proxy 等工具仍是不错的补充。
运行 graftcp yay 或 graftcp sudo ... 报错退出,怎么办?
Arch Linux 上的 yay 实际会调用 sudo pacman ...,这要求 tracer 拥有跟踪子进程的权限。可以用 sudo 启动 graftcp 再切回当前用户执行命令:sudo graftcp sudo -u $USER yay,或 sudo graftcp -u $USER sudo ...。
如果觉得命令太长,可以给一份二进制副本赋予所需 capability:
cp local/graftcp sumg
sudo setcap 'cap_sys_ptrace,cap_sys_admin+ep' ./sumg
# ./sumg yay
# ./sumg sudo ...
clone(2) 有 CLONE_UNTRACED 标志可以避免被跟踪,graftcp 如何强制跟踪?
graftcp 会拦截 clone(2) 并清除 CLONE_UNTRACED,使派生的子进程无法逃脱跟踪。(该标志是给内核用的,用户态程序本不应设置它。)Linux 还可通过 /proc/sys/kernel/yama/ptrace_scope 限制 ptrace(2);如果跟踪失败,请检查该值。
支持 macOS 吗?
不支持。macOS 的 ptrace(2) 能力不足以实现本工具。参见 issue 12。
TODO
- 虚拟化
getpeername()/getsockname(),使被跟踪程序能看到原始远端地址 - 覆盖批量发送的
sendmmsg(),补全通用 UDP 代理的 syscall 支持
感谢及参考
- maybe, proxychains and proxychains-ng for inspiration
- strace
- uthash
许可证
Copyright © 2016, 2018-2026 Hmgle <[email protected]>