Skip to content

Commit

Permalink
Update README, Copyright year
Browse files Browse the repository at this point in the history
  • Loading branch information
hmgle committed Jun 1, 2024
1 parent ae28def commit e19dea0
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 12 deletions.
41 changes: 35 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,19 @@
`graftcp` is a proxy tool inspiring by [maybe](https://github.com/p-e-w/maybe) and [proxychains](https://github.com/haad/proxychains).
It hooks `connect(2)` function via `ptrace(2)` and redirects the connection through SOCKS5 proxies.
-->

# graftcp

**English** | [简体中文](./README.zh-CN.md)

## Introduction
## Introduction

`graftcp` can redirect the TCP connection made by the given program \[application, script, shell, etc.\] to SOCKS5 or HTTP proxy.

Compared with [tsocks](https://linux.die.net/man/8/tsocks), [proxychains](http://proxychains.sourceforge.net/) or [proxychains-ng](https://github.com/rofl0r/proxychains-ng), `graftcp` is not using the [LD_PRELOAD trick](https://stackoverflow.com/questions/426230/what-is-the-ld-preload-trick) which only work for dynamically linked programs, e.g., [applications built by Go can not be hook by proxychains-ng](https://github.com/rofl0r/proxychains-ng/issues/199). `graftcp` can trace or modify any
given program's connect by [`ptrace(2)`](https://en.wikipedia.org/wiki/Ptrace), so it is workable for any program. The principle will be explained in this paragraph of [how does it work](#principles).

## Installation
## Installation

### Install from source

Expand All @@ -26,6 +27,7 @@ git clone https://github.com/hmgle/graftcp.git
cd graftcp
make
```

After make finishes, you'll be able to use `local/graftcp-local` and `./graftcp`.
Optionally, you can also install them to system:

Expand Down Expand Up @@ -95,13 +97,15 @@ Options:
-n --not-ignore-local
Connecting to local is not changed by default, this
option will redirect it to SOCKS5
-u --user=<username>
Run command as USERNAME handling setuid and/or setgid
-V --version
Show version
-h --help
Display this help and exit
```

`mgraftcp`: Combined `graftcp-local` and `graftcp` (`mgraftcp` = `graftcp-local` + `graftcp`).
`mgraftcp`: Combined `graftcp-local` and `graftcp` (`mgraftcp` = `graftcp-local` + `graftcp`).
`mgraftcp` can be used to replace `graftcp` without running `graftcp-local`.

```console
Expand All @@ -126,6 +130,8 @@ Usage: mgraftcp [-hn] [-b value] [--enable-debug-log] [--http_proxy value] [--se
SOCKS5 password
--socks5_username=value
SOCKS5 username
-u, --username=value
Run command as USERNAME handling setuid and/or setgid
--version Print the mgraftcp version information
-w, --whiteip-file=value
Only redirect the connect that destination IP/CIDR in the
Expand Down Expand Up @@ -167,9 +173,11 @@ Launch `Bash` / `Zsh` / `Fish` via `graftcp`, then all the TCP traffic generated
% ./graftcp bash
$ wget https://www.google.com
```

![demo](demo.gif)

<a id="principles"></a>

## 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:
Expand All @@ -178,7 +186,7 @@ To achieve the goal of redirecting the TCP connection of a app to another destin
- Modify the destination address argument of `connect(2)` to `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 fact it is connected to the `graftcp-local`, so we named it "graft".
- `graftcp-local` establish 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 `write(2)` / `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.
Someone may have a question here: since we can modify the arguments of a syscall, modify the app's `write(2)` / `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](http://man7.org/linux/man-pages/man2/execve.2.html), we also cannot add extra data to the `write` buffer of traced app by sharing memory, so we send the original destination address via `pipe`.

The simple sketch is as follows:
Expand Down Expand Up @@ -220,10 +228,30 @@ programs selection way: this way can only perform redirection for specified prog

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?
### I am suffering a DNS cache poisoning attack, does `graftcp` handle DNS requests?

No. `graftcp` currently only handles TCP connections. [`dnscrypt-proxy`](https://github.com/jedisct1/dnscrypt-proxy) or `ChinaDNS` may help you.

### Running `[m]graftcp yay` or `graftcp sudo ...` results in an error and exit, how to solve this?

The `yay` command on Arch Linux actually invokes `sudo pacman ...`, which requires the tracer to have root privileges to obtain permissions to trace the child process. You can start `[m]graftcp` with `sudo` and specify the current user to run the subsequent command: `sudo [m]graftcp sudo -u $USER yay`, or `sudo [m]graftcp -u $USER sudo ...`.

If you find the above command too long, you can copy a `[m]graftcp` binary with the setuid bit set and create a wrapper script to simplify the input:

```sh
cp mgraftcp _sumgraftcp
sudo chown root _sumgraftcp
sudo u+s _sumgraftcp
cat << 'EOF' > sumg
#!/bin/sh
./_sumgraftcp -u "$USER" "$@"
EOF
chmod +x sumg
# sumg yay
# sumg sudo ...
```

### The `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.
Expand All @@ -238,6 +266,7 @@ No. macOS's [`ptrace(2)`](http://polarhome.com/service/man/?qf=ptrace&af=0&sf=0&

- [x] ARM/Linux Support
- [x] i386/Linux Support
- [ ] UDP Support

## Acknowledgements and References

Expand All @@ -249,6 +278,6 @@ No. macOS's [`ptrace(2)`](http://polarhome.com/service/man/?qf=ptrace&af=0&sf=0&

## LICENSE

Copyright &copy; 2016, 2018-2021 Hmgle <[email protected]>
Copyright &copy; 2016, 2018-2024 Hmgle <[email protected]>

Released under the terms of the [GNU General Public License, version 3](https://www.gnu.org/licenses/gpl-3.0.html)
31 changes: 28 additions & 3 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ Options:
-n --not-ignore-local
Connecting to local is not changed by default, this
option will redirect it to SOCKS5
-u --user=<username>
Run command as USERNAME handling setuid and/or setgid
-V --version
Show version
-h --help
Expand Down Expand Up @@ -119,6 +121,8 @@ Usage: mgraftcp [-hn] [-b value] [--enable-debug-log] [--http_proxy value] [--se
SOCKS5 password
--socks5_username=value
SOCKS5 username
-u, --username=value
Run command as USERNAME handling setuid and/or setgid
--version Print the mgraftcp version information
-w, --whiteip-file=value
Only redirect the connect that destination IP/CIDR in the
Expand Down Expand Up @@ -164,6 +168,7 @@ $ wget https://www.google.com
![demo](demo.gif)

<a id="principles"></a>

## 工作原理

要达到重定向一个 app 发起的的 TCP 连接到其他目标地址并且该 app 本身对此毫无感知的目的,大概需要这些条件:
Expand All @@ -172,7 +177,7 @@ $ wget https://www.google.com
- 修改这次 `connect(2)` 系统调用的目标地址参数为 `graftcp-local` 的地址,然后恢复执行被中断的系统调用。返回成功后,这个程序以为自己连的是原始的地址,但其实连的是 `graftcp-local` 的地址。这个就叫“移花接木”。
- `graftcp-local` 根据连接信息和目标地址信息,与 SOCKS5 proxy 建立连接,把 app 的请求的数据重定向到 SOCKS5 proxy。

这里可能有个疑问:既然可以修改任何系统调用的参数,那么通过修改 app 的 `write(2)` / `send(2)` 的参数,直接往 `buffer` 里面附加原始目标地址信息给 `graftcp-local` 不是更简单吗?答案是这无法做到。如果直接往运行在子进程的被跟踪程序的 `buffer` 添加信息,可能会造成缓冲区溢出,造成程序崩溃或者覆盖了其他数据。
这里可能有个疑问:既然可以修改任何系统调用的参数,那么通过修改 app 的 `write(2)` / `send(2)` 的参数,直接往 `buffer` 里面附加原始目标地址信息给 `graftcp-local` 不是更简单吗?答案是这无法做到。如果直接往运行在子进程的被跟踪程序的 `buffer` 添加信息,可能会造成缓冲区溢出,造成程序崩溃或者覆盖了其他数据。
另外,[`execve(2)` 会分离所有的共享内存](http://man7.org/linux/man-pages/man2/execve.2.html),所以也不能通过共享内存的方式让被跟踪的 app 的 `write` buffer 携带更多的数据,因此这里采用管道方式给 `graftcp-local` 传递原始的目标地址信息。

简单的流程如下:
Expand Down Expand Up @@ -205,7 +210,7 @@ $ wget https://www.google.com

全局式:比如使用 `iptables` + `RedSocks` 可以把系统符合一定规则的流量转换为 SOCKS5 流量。这种方式的优点是全局有效;缺点是所有满足该规则的流量都被重定向了,影响范围较大。

设置环境变量方式:一些程序启动时会读取 proxy 相关的环境变量来决定是否将自己的数据转换为对应代理协议的流量,比如 `curl`[读取 `http_proxy`, `ftp_proxy`, `all_proxy` 环境变量并根据请求 scheme 来决定转换为哪种代理流量](https://curl.haxx.se/libcurl/c/CURLOPT_PROXY.html)。这种方法只有程序本身实现了转换的功能才有效,局限性较大。
设置环境变量方式:一些程序启动时会读取 proxy 相关的环境变量来决定是否将自己的数据转换为对应代理协议的流量,比如 `curl`[读取 `http_proxy`, `ftp_proxy`, `all_proxy` 环境变量并根据请求 scheme 来决定转换为哪种代理流量](https://curl.haxx.se/libcurl/c/CURLOPT_PROXY.html)。这种方法只有程序本身实现了转换的功能才有效,局限性较大。

仅针对程序方式: 这种方式可以仅针对特定的程序执行重定向,比如 `tsocks``proxychains`。如前面提到,它们之前都是使用 `LD_PRELOAD` 劫持动态库方式实现,对 `Go` 之类默认静态链接编译的程序就无效了。`graftcp` 改进了这一点,能够重定向任何程序的 TCP 连接。

Expand All @@ -217,6 +222,25 @@ $ wget https://www.google.com

不会。`graftcp` 目前仅处理 TCP 连接。建议使用 `dnscrypt-proxy``ChinaDNS` 等方式解决 DNS 污染问题。

### 运行 `[m]graftcp yay` 或者 `graftcp sudo ...` 报错并退出,该如何解决?

Arch Linux 的 `yay` 实际也会调用 `sudo pacman ...`,这需要 tracer 具备 root 特权才能获取到跟踪子进程的权限。可以用 sudo 来启动 `[m]graftcp`,并指定当前用户运行后续命令:`sudo [m]graftcp sudo -u $USER yay`,或者 `sudo [m]graftcp -u $USER sudo ...`
如何觉得上面命令太长,可以复制一个带 setuid 位的 [m]graftcp 副本,并写一个包裹脚本来简化输入:

```sh
cp mgraftcp _sumgraftcp
sudo chown root _sumgraftcp
sudo u+s _sumgraftcp
cat << 'EOF' > sumg
#!/bin/sh
./_sumgraftcp -u "$USER" "$@"
EOF
chmod +x sumg
# sumg yay
# sumg sudo ...
```

### `clone(2)` 参数有个叫 `CLONE_UNTRACED` 的标志位,可以避免让父进程跟踪到自己,`graftcp` 是如何做到强制跟踪的?

`graftcp` 在子进程调用 `clone(2)` 之前会把它拦截,清除这个 `CLONE_UNTRACED` 标志位,所以被跟踪的子进程最终还是难逃被跟踪的命运。另外,这个 `CLONE_UNTRACED` 标志位本意是给内核使用的,普通程序不应该去设置它。
Expand All @@ -231,6 +255,7 @@ Linux 提供了一种限制被 `ptrace(2)` 跟踪的方法:设置 [`/proc/sys/

- [x] ARM/Linux 支持
- [x] i386/Linux 支持
- [ ] UDP 支持

## 感谢及参考

Expand All @@ -242,6 +267,6 @@ Linux 提供了一种限制被 `ptrace(2)` 跟踪的方法:设置 [`/proc/sys/

## License

Copyright &copy; 2016, 2018-2021 Hmgle <[email protected]>
Copyright &copy; 2016, 2018-2024 Hmgle <[email protected]>

根据 [GPLv3 许可](https://www.gnu.org/licenses/gpl-3.0.html)发布。
2 changes: 1 addition & 1 deletion conf.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* graftcp
* Copyright (C) 2021, 2023 Hmgle <[email protected]>
* Copyright (C) 2021, 2023, 2024 Hmgle <[email protected]>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down
2 changes: 1 addition & 1 deletion conf.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* graftcp
* Copyright (C) 2021 Hmgle <[email protected]>
* Copyright (C) 2021, 2024 Hmgle <[email protected]>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down
2 changes: 1 addition & 1 deletion graftcp.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* graftcp
* Copyright (C) 2016, 2018-2023 Hmgle <[email protected]>
* Copyright (C) 2016, 2018-2024 Hmgle <[email protected]>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down

0 comments on commit e19dea0

Please sign in to comment.