English | 简体中文
graftcp can redirect the TCP connection made by the given program [application, script, shell, etc.] to SOCKS5 or HTTP proxy.
Compared with tsocks, proxychains or proxychains-ng,
graftcp is not using the LD_PRELOAD trick which only work for dynamically linked programs, e.g., applications built by Go can not be hook by proxychains-ng.
graftcp can trace or modify any given program's connect by
ptrace(2), so it is workable for any program. The principle will be explained in this paragraph of how does it work.
Install from source
graftcp runs on Linux. Building
graftcp-local requires Go installed.
git clone https://github.com/hmgle/graftcp.git cd graftcp make
After make finishes, you'll be able to use
./graftcp. Optionally, you can also install them to system:
sudo make install # Enable and activate systemed unit sudo make install_systemd
Install from binary package
$ local/graftcp-local -h Usage of local/graftcp-local: -config string Path to the configuration file -http_proxy string http proxy address, e.g.: 127.0.0.1:8080 -listen string Listen address (default ":2233") -logfile string Write logs to file -loglevel value Log level (0-6) (default 1) -pipepath string Pipe path for graftcp to send address info (default "/tmp/graftcplocal.fifo") -select_proxy_mode string Set the mode for select a proxy [auto | random | only_http_proxy | only_socks5] (default "auto") -service string Control the system service: ["start" "stop" "restart" "install" "uninstall"] -socks5 string SOCKS5 address (default "127.0.0.1:1080") -syslog Send logs to the local system logger (Eventlog on Windows, syslog on Unix)
$ graftcp -h Usage: graftcp [options] prog [prog-args] Options: -c --conf-file=<config-file-path> Specify configuration file -a --local-addr=<graftcp-local-IP-addr> graftcp-local's IP address. Default: localhost -p --local-port=<graftcp-local-port> Which port is graftcp-local listening? Default: 2233 -f --local-fifo=<fifo-path> Path of fifo to communicate with graftcp-local. Default: /tmp/graftcplocal.fifo -b --blackip-file=<black-ip-file-path> The IP in black-ip-file will connect direct -w --whiteip-file=<white-ip-file-path> Only redirect the connect that destination ip in the white-ip-file to SOCKS5 -n --not-ignore-local Connecting to local is not changed by default, this option will redirect it to SOCKS5 -V --version Show version -h --help Display this help and exit
mgraftcp can be used to replace
graftcp without running
Usage: mgraftcp [-hn] [-b value] [--enable-debug-log] [--http_proxy value] [--select_proxy_mode value] \ [--socks5 value] [--socks5_password value] [--socks5_username value] [--version] [-w value] prog [prog-args] -b, --blackip-file=value The IP in black-ip-file will connect direct --enable-debug-log enable debug log -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 [127.0.0.1:1080] --socks5_password=value SOCKS5 password --socks5_username=value SOCKS5 username --version Print the mgraftcp version information -w, --whiteip-file=value Only redirect the connect that destination ip in the white-ip-file to SOCKS5
mgraftcp look for config file in following order:
- File provided as a
$(the path of the executeable)/graftcp-local.conf
Assume you are running the SOCKS5 proxy with the default IP address: "localhost:1080". Start the
Install the Go package from golang.org (now is blocked by the GFW) via
./graftcp go get -v golang.org/x/net/proxy
Firefox browser via
graftcp, then all the requests from this browser will redirect to the SOCKS5 proxy:
graftcp, then all the TCP traffic generated by the command in this shell will redirect to the SOCKS5 proxy:
% ./graftcp bash $ wget https://www.google.com
How does it work?
To achieve the goal of redirecting the TCP connection of a app to another destination address and the app itself is not aware of it, these conditions are probably required:
fork(2)a new process and trace it using
execve(2)to run the app. Every
connect(2)syscall will be intercepted, then get the destination address argument and send it to
- Modify the destination address argument of
graftcp-local's address, and restart the stopped syscall. After the syscall returns successfully, the app thought it has connected the original destination address, but in face it connected the
graftcp-local, so we named it "graft".
graftcp-localestablish a SOCKS5 connection based on the information of app's original destination address, then redirect the requests from the app to the SOCKS5 proxy.
Someone may have a question here: since we can modify the arguments of a syscall, modify the app's
send(2) buf argument, attach the original destination information to the
write buffer, isn't it simpler? The answer is that cannot be done. Because attach data to the buffer of the tracked child process, it may case a buffer overflow, causing crash or overwrite other data.
In addition, as the
execve(2) will detach and unmap all shared memory, we also cannot add extra data to the
write buffer of traced app by sharing memory, so we send the original destination address via
The simple sketch is as follows:
+---------------+ +---------+ +--------+ +------+ | graftcp | dest host | | | | | | | (tracer) +---PIPE----->| | | | | | | ^ | info | | | | | | | | ptrace | | | | | | | | v | | | | | | | | +---------+ | | | | | | | | | | | connect | | connect | | connect | | | | +--------------->| graftcp +-------->| SOCKS5 +-------->| dest | | | | | | -local | | or | | host | | | app | | req | | req | HTTP | req | | | |(tracee) +--------------->| +-------->| proxy +-------->| | | | | | | | | | | | | | | | resp | | resp | | resp | | | | |<---------------+ |<--------+ |<--------+ | | +---------+ | | | | | | | +---------------+ +---------+ +--------+ +------+
FAQ and Tips
What are some ways to redirect TCP connections?
The main ones are: global way, environment variables setting way, and programs selection way.
Global way: e.g., use
RedSocks to convert the system's traffic that match certain rules into SOCKS5 traffic. The pros is that it is globally effective; the cons is that all traffic that satisfies the rule is redirected, and the scope of influence is large.
Environment variable setting: some programs will read the proxy-related environment variables to determine whether to convert their own traffic to the corresponding proxy protocol traffic, such as
curl will read
all_proxy Environment variables and decide which proxy traffic to convert based on the request URL scheme. This way is effective only if the program itself implements the traffic conversion function, so it is very limited.
programs selection way: this way can only perform redirection for specified programs, such as
proxychains. As mentioned earlier, they were using the
LD_PRELOAD hijacking dynamic library function, and the default static link compiled program such as
Go is invalid.
graftcp improves this by being able to redirect TCP connections from any program.
graftcp redirect the connection to the SOCKS5 proxy if the target address is localhost?
No. By default,
graftcp ignore the connections to localhost. If you want to redirect all addresses, you can use the
-n option. If you want to ignore more addresses, you can add them to the blacklist IP file; if you want to redirect only certain IP addresses, you can add them to the whitelist IP file. Use
graftcp --help to get more information.
I am suffering a DNS cache poisoning attack, does
graftcp handle DNS requests?
graftcp currently only handles TCP connections.
ChinaDNS may help you.
clone(2)'s argument has a flag
CLONE_UNTRACED to avoid being traced, how does
graftcp do forced tracing?
graftcp will intercept the
clone(2) syscall, and clearing the
CLONE_UNTRACED flag, so the tracked child process could not escape the fate of being tracked. In addition, this
CLONE_UNTRACED flag is intended for the kernel, and user space program should not set it.
Linux provides a way to limit the
ptrace(2): set the value of
ptrace(2) is invalid, check if the default value has been modified.
Does it support macOS?
- ARM/Linux Support
- i386/Linux Support
Acknowledgements and References
Copyright © 2016, 2018-2021 Hmgle [email protected]
Released under the terms of the GNU General Public License, version 3