Rewrite to be more "Correct"
parent
673a832bf7
commit
210a15d990
|
@ -1,21 +1,16 @@
|
||||||
|
|
||||||
# scramble
|
# scramble
|
||||||
|
|
||||||
A simple tool to perform XOR as a TCP proxy.
|
Rewritten, this is now a basic XOR SOCKS proxy.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
```
|
```
|
||||||
$ ./scramble -help
|
$ ./scramble -help
|
||||||
Usage of ./scramble:
|
Usage of ./scramble:
|
||||||
-connect string
|
|
||||||
forward to ip and port (default ":8080")
|
|
||||||
-key int
|
-key int
|
||||||
key to xor the data (default 170)
|
key to xor the data (default 170)
|
||||||
-listen string
|
-listen string
|
||||||
listen on ip and port (default ":8081")
|
listen on ip and port (default ":8081")
|
||||||
```
|
```
|
||||||
|
|
||||||
## Use with a SOCKS proxy
|
|
||||||
|
|
||||||
This tool may come really useful when trying to bypass filters that perform packet inspection. After starting a SOCKS proxy listening on the server, `scramble` can connect to the proxy and listen on an exposed port to provide "obscured" SOCKS proxying if `scramble` is also run on the client.
|
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
module github.com/serverwentdown/scramble
|
||||||
|
|
||||||
|
go 1.14
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5
|
||||||
|
golang.org/x/net v0.0.0-20200602114024-627f9648deb9 // indirect
|
||||||
|
)
|
|
@ -0,0 +1,8 @@
|
||||||
|
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||||
|
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/net v0.0.0-20200602114024-627f9648deb9 h1:pNX+40auqi2JqRfOP1akLGtYcn15TUbkhwuCO3foqqM=
|
||||||
|
golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
190
main.go
190
main.go
|
@ -2,105 +2,151 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
|
"github.com/armon/go-socks5"
|
||||||
)
|
)
|
||||||
|
|
||||||
var key int
|
var keyInt int
|
||||||
var listen string
|
var listen string
|
||||||
var connect string
|
var connect string
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
flag.IntVar(&key, "key", 170, "key to xor the data")
|
flag.IntVar(&keyInt, "key", 170, "key to xor the data")
|
||||||
flag.StringVar(&listen, "listen", ":8081", "listen on ip and port")
|
flag.StringVar(&listen, "listen", ":8081", "listen on IP and port")
|
||||||
flag.StringVar(&connect, "connect", ":8080", "forward to ip and port")
|
flag.StringVar(&connect, "connect", "socks", "forward to IP and port. 'socks' sets up a SOCKS5 proxy.")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
if key < 0 || key > 255 {
|
if keyInt < 0 || keyInt > 255 {
|
||||||
flag.PrintDefaults()
|
flag.PrintDefaults()
|
||||||
log.Fatal("key is not one byte")
|
log.Fatal(fmt.Errorf("key is not one byte"))
|
||||||
|
}
|
||||||
|
key := byte(keyInt)
|
||||||
|
|
||||||
|
var socks *socks5.Server
|
||||||
|
if connect == "socks" {
|
||||||
|
conf := &socks5.Config{}
|
||||||
|
var err error
|
||||||
|
socks, err = socks5.New(conf)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(fmt.Errorf("unable to create socks server: %w", err))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check and parse address
|
// check and parse address
|
||||||
conn, err := net.ResolveTCPAddr("tcp", connect)
|
connAddr, err := net.ResolveTCPAddr("tcp", connect)
|
||||||
if err != nil {
|
if socks == nil && err != nil {
|
||||||
flag.PrintDefaults()
|
flag.PrintDefaults()
|
||||||
log.Fatal(err)
|
log.Fatal(fmt.Errorf("invalid connect address %s: %w", connect, err))
|
||||||
}
|
}
|
||||||
|
|
||||||
// listen on address
|
// listen on address
|
||||||
ln, err := net.Listen("tcp", listen)
|
ln, err := net.Listen("tcp", listen)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
flag.PrintDefaults()
|
flag.PrintDefaults()
|
||||||
log.Fatal(err)
|
log.Fatal(fmt.Errorf("unable to listen on %s: %w", listen, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
c, err := ln.Accept()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(fmt.Errorf("unable to accept connection: %w", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("connection from %v", c.RemoteAddr())
|
||||||
|
|
||||||
|
scrambleConn := NewScrambleConn(c, key)
|
||||||
|
if socks != nil {
|
||||||
|
go socks.ServeConn(scrambleConn)
|
||||||
|
} else {
|
||||||
|
conn, err := net.DialTCP("tcp", nil, connAddr)
|
||||||
|
if err != nil {
|
||||||
|
c.Close()
|
||||||
|
log.Print(fmt.Errorf("unable to connect to %v: %w", connAddr, err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
result := Pipe(conn, scrambleConn)
|
||||||
|
go func() {
|
||||||
|
pipeResult := <-result
|
||||||
|
log.Printf("in: %d %v", pipeResult.Ingress.N, pipeResult.Ingress.Error)
|
||||||
|
log.Printf("eg: %d %v", pipeResult.Egress.N, pipeResult.Egress.Error)
|
||||||
|
}()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("listening on %v", ln.Addr())
|
log.Printf("listening on %v", ln.Addr())
|
||||||
log.Printf("will connect to %v", conn)
|
|
||||||
|
|
||||||
for i := 0; ; i++ {
|
|
||||||
// accept new connection
|
|
||||||
c, err := ln.Accept()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("connection %v from %v", i, c.RemoteAddr())
|
|
||||||
|
|
||||||
cn, err := net.DialTCP("tcp", nil, conn)
|
|
||||||
if err != nil {
|
|
||||||
c.Close()
|
|
||||||
log.Print(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
go pipe(c, cn, byte(key), i)
|
|
||||||
go pipe(cn, c, byte(key), i)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func pipe(w io.WriteCloser, r io.ReadCloser, key byte, count int) {
|
type CloseIndividual interface {
|
||||||
n, err := copyBufferXor(w, r, key)
|
CloseRead() error
|
||||||
|
CloseWrite() error
|
||||||
r.Close()
|
|
||||||
w.Close()
|
|
||||||
|
|
||||||
log.Printf("connection %v closed, %v bytes", count, n)
|
|
||||||
|
|
||||||
opError, ok := err.(*net.OpError)
|
|
||||||
if err != nil && (!ok || opError.Op != "readfrom") {
|
|
||||||
log.Printf("warning! %v", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func copyBufferXor(dst io.Writer, src io.Reader, key byte) (written int64, err error) {
|
type PipeResult struct {
|
||||||
buf := make([]byte, 32*1024)
|
Ingress CopyResult
|
||||||
for {
|
Egress CopyResult
|
||||||
nr, er := src.Read(buf)
|
}
|
||||||
for i := 0; i < nr; i++ {
|
|
||||||
buf[i] = buf[i] ^ key
|
func Pipe(a, b io.ReadWriteCloser) chan PipeResult {
|
||||||
}
|
// Copy from b to a
|
||||||
if nr > 0 {
|
ingressResult := Copy(a, b)
|
||||||
nw, ew := dst.Write(buf[0:nr])
|
// Copy from a to b
|
||||||
if nw > 0 {
|
egressResult := Copy(b, a)
|
||||||
written += int64(nw)
|
|
||||||
}
|
result := make(chan PipeResult)
|
||||||
if ew != nil {
|
go func() {
|
||||||
err = ew
|
var in CopyResult
|
||||||
break
|
var eg CopyResult
|
||||||
}
|
select {
|
||||||
if nr != nw {
|
case in = <-ingressResult:
|
||||||
err = io.ErrShortWrite
|
// b returned error
|
||||||
break
|
// TODO: Consider error handling
|
||||||
}
|
closeOneSide(a, b)
|
||||||
}
|
eg = <-egressResult
|
||||||
if er != nil {
|
case eg = <-egressResult:
|
||||||
if er != io.EOF {
|
// a returned error
|
||||||
err = er
|
// TODO: Consider error handling
|
||||||
}
|
closeOneSide(b, a)
|
||||||
break
|
in = <-ingressResult
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return written, err
|
result <- PipeResult{
|
||||||
|
Ingress: in,
|
||||||
|
Egress: eg,
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func closeOneSide(a, b io.ReadWriteCloser) (aErr error, bErr error) {
|
||||||
|
if c, ok := a.(CloseIndividual); ok {
|
||||||
|
aErr = c.CloseWrite()
|
||||||
|
} else {
|
||||||
|
aErr = a.Close()
|
||||||
|
}
|
||||||
|
if c, ok := b.(CloseIndividual); ok {
|
||||||
|
bErr = c.CloseRead()
|
||||||
|
} else {
|
||||||
|
bErr = b.Close()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type CopyResult struct {
|
||||||
|
N int64
|
||||||
|
Error error
|
||||||
|
}
|
||||||
|
|
||||||
|
func Copy(w io.Writer, r io.Reader) chan CopyResult {
|
||||||
|
result := make(chan CopyResult)
|
||||||
|
go func() {
|
||||||
|
// Do a copy
|
||||||
|
n, err := io.Copy(w, r)
|
||||||
|
result <- CopyResult{n, err}
|
||||||
|
}()
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ScrambleReadWriter struct {
|
||||||
|
ReadWriter io.ReadWriter
|
||||||
|
Key byte
|
||||||
|
buffer []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ScrambleReadWriter) Read(p []byte) (n int, err error) {
|
||||||
|
n, err = s.ReadWriter.Read(p)
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
p[i] = p[i] ^ s.Key
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write takes buffer p, performs XOR in a copy of the buffer and calls write
|
||||||
|
// on the underlying ReadWriter.
|
||||||
|
func (s *ScrambleReadWriter) Write(p []byte) (n int, err error) {
|
||||||
|
if cap(s.buffer) < cap(p) {
|
||||||
|
s.buffer = make([]byte, 0, cap(p))
|
||||||
|
} else {
|
||||||
|
s.buffer = s.buffer[:0]
|
||||||
|
}
|
||||||
|
for i := range p {
|
||||||
|
s.buffer = append(s.buffer, p[i]^s.Key)
|
||||||
|
}
|
||||||
|
return s.ReadWriter.Write(s.buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ScrambleConn struct {
|
||||||
|
net.Conn
|
||||||
|
*ScrambleReadWriter
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewScrambleConn(c net.Conn, key byte) net.Conn {
|
||||||
|
return &ScrambleConn{
|
||||||
|
c,
|
||||||
|
&ScrambleReadWriter{
|
||||||
|
ReadWriter: c,
|
||||||
|
Key: key,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ScrambleConn) Read(b []byte) (n int, err error) {
|
||||||
|
return s.ScrambleReadWriter.Read(b)
|
||||||
|
}
|
||||||
|
func (s *ScrambleConn) Write(b []byte) (n int, err error) {
|
||||||
|
return s.ScrambleReadWriter.Write(b)
|
||||||
|
}
|
Loading…
Reference in New Issue