From 89db9b6d2ce8cd9dc72ce0f4a70cb0075b1589d9 Mon Sep 17 00:00:00 2001 From: shadowy-pycoder <35629483+shadowy-pycoder@users.noreply.github.com> Date: Fri, 15 Aug 2025 15:46:43 +0300 Subject: [PATCH] added transparent proxy functionality to android, bumped to v1.10.2 --- README.md | 18 ++++++++++++++++-- cmd/gohpts/cli.go | 5 ++--- go.mod | 2 +- go.sum | 4 ++-- gohpts.go | 26 +++++++++++++++----------- tproxy_linux.go | 4 ++-- tproxy_nonlinux.go | 3 ++- tproxy_udp_linux.go | 14 +++++++------- tproxy_udp_nonlinux.go | 3 ++- version.go | 2 +- 10 files changed, 50 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 35833f8..7f64a21 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ - [tproxy (via MANGLE and IP_TRANSPARENT)](#tproxy-via-mangle-and-ip_transparent) - [ARP spoofing](#arp-spoofing) - [UDP support](#udp-support) + - [Android support](#android-support) - [Traffic sniffing](#traffic-sniffing) - [JSON format](#json-format) - [Colored format](#colored-format) @@ -106,7 +107,7 @@ You can download the binary for your platform from [Releases](https://github.com Example: ```shell -GOHPTS_RELEASE=v1.10.1; wget -v https://github.com/shadowy-pycoder/go-http-proxy-to-socks/releases/download/$GOHPTS_RELEASE/gohpts-$GOHPTS_RELEASE-linux-amd64.tar.gz -O gohpts && tar xvzf gohpts && mv -f gohpts-$GOHPTS_RELEASE-linux-amd64 gohpts && ./gohpts -h +GOHPTS_RELEASE=v1.10.2; wget -v https://github.com/shadowy-pycoder/go-http-proxy-to-socks/releases/download/$GOHPTS_RELEASE/gohpts-$GOHPTS_RELEASE-linux-amd64.tar.gz -O gohpts && tar xvzf gohpts && mv -f gohpts-$GOHPTS_RELEASE-linux-amd64 gohpts && ./gohpts -h ``` Alternatively, you can install it using `go install` command (requires Go [1.24](https://go.dev/doc/install) or later): @@ -308,7 +309,7 @@ To learn more about proxy chains visit [Proxychains Github](https://github.com/r > > -- _From [Wiki](https://en.wikipedia.org/wiki/Proxy_server)_ -This functionality available only on Linux systems and requires additional setup (`iptables`, ip route, etc) +This functionality available only on Linux systems and Android (arm64) and requires additional setup (`iptables`, ip route, etc) `-T address` flag specifies the address of transparent proxy server (`GoHPTS` will be running without HTTP server). @@ -560,6 +561,19 @@ sudo ./gohpts -s -T 8888 -Tu :8989 -M tproxy -sniff -body -auto 5. Check connection on your host machine, the traffic should go through Kali machine. +### Android support + +Transparent proxy can be enabled on Android devices (arm64) with root access. You can install [Termux](https://github.com/termux/termux-app) and run `GoHPTS` as a CLI tool there: + +```shell +# you need to root your device first +pkg install tsu iproute2 +# Android support added in v1.10.2 +GOHPTS_RELEASE=v1.10.2; wget -v https://github.com/shadowy-pycoder/go-http-proxy-to-socks/releases/download/$GOHPTS_RELEASE/gohpts-$GOHPTS_RELEASE-android-arm64.tar.gz -O gohpts && tar xvzf gohpts && mv -f gohpts-$GOHPTS_RELEASE-android-arm64 gohpts && ./gohpts -h +# use your phone as router for LAN devices redirecting their traffic to remote socks5 server +sudo ./gohpts -s remote -t 8888 -Tu :8989 -M tproxy -sniff -body -auto -mark 100 -d -arpspoof "fullduplex true;debug false" +``` + ## Traffic sniffing [[Back]](#table-of-contents) diff --git a/cmd/gohpts/cli.go b/cmd/gohpts/cli.go index 49a95f3..c0f2839 100644 --- a/cmd/gohpts/cli.go +++ b/cmd/gohpts/cli.go @@ -16,7 +16,6 @@ const ( app string = "gohpts" addrSOCKS string = "127.0.0.1:1080" addrHTTP string = "127.0.0.1:8080" - tproxyOS string = "linux" ) const usagePrefix string = ` _____ _ _ _____ _______ _____ @@ -105,7 +104,7 @@ func root(args []string) error { return nil }) daemon := flags.Bool("D", false, "Run as a daemon (provide -logfile to see logs)") - if runtime.GOOS == tproxyOS { + if slices.Contains(gohpts.SupportedTProxyOS, runtime.GOOS) { flags.StringVar(&conf.TProxy, "t", "", "Address of transparent proxy server (it starts along with HTTP proxy server)") flags.StringVar(&conf.TProxyOnly, "T", "", "Address of transparent proxy server (no HTTP)") flags.StringVar(&conf.TProxyUDP, "Tu", "", "Address of transparent UDP proxy server") @@ -152,7 +151,7 @@ func root(args []string) error { flags.Usage = func() { fmt.Print(usagePrefix) - if runtime.GOOS == tproxyOS { + if slices.Contains(gohpts.SupportedTProxyOS, runtime.GOOS) { fmt.Print(usageTproxy) } } diff --git a/go.mod b/go.mod index 0d8df3b..009f8bf 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/google/uuid v1.6.0 github.com/rs/zerolog v1.34.0 github.com/shadowy-pycoder/colors v0.0.1 - github.com/shadowy-pycoder/mshark v0.0.14 + github.com/shadowy-pycoder/mshark v0.0.15 github.com/wzshiming/socks5 v0.5.2 golang.org/x/sys v0.33.0 golang.org/x/term v0.32.0 diff --git a/go.sum b/go.sum index 99ec5f7..b7e3593 100644 --- a/go.sum +++ b/go.sum @@ -34,8 +34,8 @@ github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY= github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ= github.com/shadowy-pycoder/colors v0.0.1 h1:weCj/YIOupqy4BSP8KuVzr20fC+cuAv/tArz7bhhkP4= github.com/shadowy-pycoder/colors v0.0.1/go.mod h1:lkrJS1PY2oVigNLTT6pkbF7B/v0YcU2LD5PZnss1Q4U= -github.com/shadowy-pycoder/mshark v0.0.14 h1:Gou+y9tIEjNSztGWzfFSYJJ8G0DDNYmkvKTQUZDYeVQ= -github.com/shadowy-pycoder/mshark v0.0.14/go.mod h1:FqbHFdsx0zMnrZZH0+oPzaFcleP4O+tUWv8i5gxo87k= +github.com/shadowy-pycoder/mshark v0.0.15 h1:n8UrfCkb7fZHg8Ny/eCFEJwv3cfo23P2+md5LztvdME= +github.com/shadowy-pycoder/mshark v0.0.15/go.mod h1:FqbHFdsx0zMnrZZH0+oPzaFcleP4O+tUWv8i5gxo87k= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/wzshiming/socks5 v0.5.2 h1:LtoowVNwAmkIQSkP1r1Wg435xUmC+tfRxorNW30KtnM= diff --git a/gohpts.go b/gohpts.go index 065fccc..cf9e843 100644 --- a/gohpts.go +++ b/gohpts.go @@ -52,6 +52,7 @@ const ( var ( supportedChainTypes = []string{"strict", "dynamic", "random", "round_robin"} SupportedTProxyModes = []string{"redirect", "tproxy"} + SupportedTProxyOS = []string{"linux", "android"} errInvalidWrite = errors.New("invalid write result") ) @@ -258,16 +259,16 @@ func New(conf *Config) *proxyapp { sl := snifflogger.Level(lvl) p.logger = &l p.snifflogger = &sl - if runtime.GOOS == "linux" && conf.TProxy != "" && conf.TProxyOnly != "" { + if slices.Contains(SupportedTProxyOS, runtime.GOOS) && conf.TProxy != "" && conf.TProxyOnly != "" { p.logger.Fatal().Msg("Cannot specify TPRoxy and TProxyOnly at the same time") - } else if runtime.GOOS == "linux" && conf.TProxyMode != "" && !slices.Contains(SupportedTProxyModes, conf.TProxyMode) { + } else if slices.Contains(SupportedTProxyOS, runtime.GOOS) && conf.TProxyMode != "" && !slices.Contains(SupportedTProxyModes, conf.TProxyMode) { p.logger.Fatal().Msg("Incorrect TProxyMode provided") - } else if runtime.GOOS != "linux" && (conf.TProxy != "" || conf.TProxyOnly != "" || conf.TProxyMode != "" || conf.TProxyUDP != "") { + } else if !slices.Contains(SupportedTProxyOS, runtime.GOOS) && (conf.TProxy != "" || conf.TProxyOnly != "" || conf.TProxyMode != "" || conf.TProxyUDP != "") { conf.TProxy = "" conf.TProxyOnly = "" conf.TProxyMode = "" conf.TProxyUDP = "" - p.logger.Warn().Msgf("[%s] functionality only available on linux systems", conf.TProxyMode) + p.logger.Warn().Msgf("[%s] functionality only available on linux or android systems", conf.TProxyMode) } p.tproxyMode = conf.TProxyMode tproxyonly := conf.TProxyOnly != "" @@ -301,12 +302,12 @@ func New(conf *Config) *proxyapp { p.logger.Fatal().Msgf("%s: address already in use", p.tproxyAddrUDP) } p.auto = conf.Auto - if p.auto && runtime.GOOS != "linux" { - p.logger.Fatal().Msg("Auto setup is available only on linux systems") + if p.auto && !slices.Contains(SupportedTProxyOS, runtime.GOOS) { + p.logger.Fatal().Msg("Auto setup is available only on linux/android systems") } p.mark = conf.Mark - if p.mark > 0 && runtime.GOOS != "linux" { - p.logger.Fatal().Msg("SO_MARK is available only on linux systems") + if p.mark > 0 && !slices.Contains(SupportedTProxyOS, runtime.GOOS) { + p.logger.Fatal().Msg("SO_MARK is available only on linux/android systems") } if p.mark > 0xFFFFFFFF { p.logger.Fatal().Msg("SO_MARK is out of range") @@ -469,8 +470,8 @@ func New(conf *Config) *proxyapp { } } if conf.ARPSpoof != "" { - if runtime.GOOS != "linux" { - p.logger.Fatal().Msg("ARP spoof setup is available only on linux systems") + if !slices.Contains(SupportedTProxyOS, runtime.GOOS) { + p.logger.Fatal().Msg("ARP spoof setup is available only on linux/android systems") } if !p.auto { p.logger.Warn().Msg("ARP spoof setup requires iptables configuration") @@ -1508,7 +1509,10 @@ func (p *proxyapp) applyCommonRedirectRules(opts map[string]string) { } else { iface, err = network.GetDefaultInterface() if err != nil { - p.logger.Fatal().Err(err).Msg("failed getting default network interface") + iface, err = network.GetDefaultInterfaceFromRoute() + if err != nil { + p.logger.Fatal().Err(err).Msg("failed getting default network interface") + } } } cmdForwardFilter := exec.Command("bash", "-c", fmt.Sprintf(` diff --git a/tproxy_linux.go b/tproxy_linux.go index 9f7efc6..b045be5 100644 --- a/tproxy_linux.go +++ b/tproxy_linux.go @@ -1,5 +1,5 @@ -//go:build linux -// +build linux +//go:build linux || (android && arm) +// +build linux android,arm package gohpts diff --git a/tproxy_nonlinux.go b/tproxy_nonlinux.go index b203469..74e94d1 100644 --- a/tproxy_nonlinux.go +++ b/tproxy_nonlinux.go @@ -1,5 +1,6 @@ -//go:build !linux +//go:build !linux && !(android && arm) // +build !linux +// +build !android !arm package gohpts diff --git a/tproxy_udp_linux.go b/tproxy_udp_linux.go index 0759943..a734984 100644 --- a/tproxy_udp_linux.go +++ b/tproxy_udp_linux.go @@ -1,5 +1,5 @@ -//go:build linux -// +build linux +//go:build linux || (android && arm) +// +build linux android,arm package gohpts @@ -181,14 +181,14 @@ func newTproxyServerUDP(p *proxyapp) *tproxyServerUDP { } else { tsu.iface, err = network.GetDefaultInterface() if err != nil { - tsu.p.logger.Fatal().Err(err).Msgf("[udp %s] Failed getting default interface", tsu.p.tproxyMode) + tsu.iface, err = network.GetDefaultInterfaceFromRoute() + if err != nil { + tsu.p.logger.Fatal().Err(err).Msgf("[udp %s] Failed getting default interface", tsu.p.tproxyMode) + } } } if tsu.p.arpspoofer != nil { - gw, err := network.GetGatewayIPv4FromInterface(tsu.iface.Name) - if err != nil { - tsu.p.logger.Fatal().Err(err).Msgf("[udp %s] failed getting gateway from %s", tsu.p.tproxyMode, tsu.iface.Name) - } + gw := tsu.p.arpspoofer.GatewayIP() tsu.gwDNS = &net.UDPAddr{IP: net.ParseIP(gw.String()), Port: 53} lc = net.ListenConfig{ Control: func(network, address string, conn syscall.RawConn) error { diff --git a/tproxy_udp_nonlinux.go b/tproxy_udp_nonlinux.go index b472088..ad041cf 100644 --- a/tproxy_udp_nonlinux.go +++ b/tproxy_udp_nonlinux.go @@ -1,5 +1,6 @@ -//go:build !linux +//go:build !linux && !(android && arm) // +build !linux +// +build !android !arm package gohpts diff --git a/version.go b/version.go index 1e84760..135c108 100644 --- a/version.go +++ b/version.go @@ -1,3 +1,3 @@ package gohpts -const Version string = "gohpts v1.10.1" +const Version string = "gohpts v1.10.2"