├── README.md ├── cmd └── gnc │ ├── build.sh │ └── gnc.go ├── go.mod └── internal ├── gologger └── gologger.go ├── rc4 └── rc4.go ├── runner ├── options.go └── runner.go └── tcp └── tcp.go /README.md: -------------------------------------------------------------------------------- 1 | # gnc 2 | 3 | -------------------------------------------------------------------------------- /cmd/gnc/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | 4 | PROGRAM_NAME="gnc" 5 | 6 | mkdir -p output/ 7 | rm -rf output/* 8 | 9 | command_exists() { 10 | command -v "$@" > /dev/null 2>&1 11 | } 12 | 13 | build () { 14 | OS=$1 15 | ARCH=$2 16 | TAG=$3 17 | SUFF="" 18 | if [[ $OS == "windows" ]]; then 19 | SUFF=".exe" 20 | fi 21 | echo "Build ${PROGRAM_NAME}_${OS}_${ARCH}${SUFF} ..." 22 | CGO_ENABLED=0 GOOS=${OS} GOARCH=${ARCH} go build -tags "${TAG}" --ldflags="${ldflags}" -o output/${PROGRAM_NAME}_${OS}_${ARCH}${SUFF} 23 | cd output/ 24 | upx -9 ${PROGRAM_NAME}_${OS}_${ARCH}${SUFF} 25 | cd .. 26 | } 27 | 28 | if command_exists upx; then 29 | build linux amd64 "all" 30 | build linux 386 "all" 31 | build windows amd64 "all" 32 | build windows 386 "all" 33 | build darwin amd64 "all" 34 | else 35 | echo "upx not found in PATH" 36 | fi 37 | -------------------------------------------------------------------------------- /cmd/gnc/gnc.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/canc3s/gnc/internal/runner" 5 | ) 6 | 7 | func main() { 8 | options := runner.ParseOptions() 9 | 10 | runner.Process(options) 11 | } -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/canc3s/gnc 2 | 3 | go 1.14 4 | -------------------------------------------------------------------------------- /internal/gologger/gologger.go: -------------------------------------------------------------------------------- 1 | package gologger 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | ) 7 | 8 | // Level defines all the available levels we can log at 9 | type Level int 10 | 11 | // Available logging levels 12 | const ( 13 | Null Level = iota 14 | Fatal 15 | Silent 16 | Misc 17 | Info 18 | ) 19 | 20 | var ( 21 | MaxLevel = Info 22 | ) 23 | 24 | // log logs the actual message to the screen 25 | func logger(level Level, format string, args ...interface{}) { 26 | // Don't log if the level is null 27 | if level == Null { 28 | return 29 | } 30 | 31 | if level <= MaxLevel { 32 | 33 | switch level { 34 | case Fatal: 35 | log.Fatalf(format, args...) 36 | case Misc: 37 | fmt.Printf(format, args...) 38 | default: 39 | log.Printf(format, args...) 40 | } 41 | } 42 | } 43 | 44 | // Infof writes a info message on the screen with the default label 45 | func Infof(format string, args ...interface{}) { 46 | logger(Info, format, args...) 47 | } 48 | 49 | // Printf prints a string on screen without any extra stuff 50 | func Printf(format string, args ...interface{}) { 51 | logger(Misc, format, args...) 52 | } 53 | 54 | // Fatalf exits the program if we encounter a fatal error 55 | func Fatalf(format string, args ...interface{}) { 56 | logger(Fatal, format, args...) 57 | } -------------------------------------------------------------------------------- /internal/rc4/rc4.go: -------------------------------------------------------------------------------- 1 | package rc4 2 | 3 | import ( 4 | "crypto/cipher" 5 | "crypto/rc4" 6 | "crypto/sha256" 7 | "io" 8 | "net" 9 | ) 10 | 11 | type RC4Cipher struct { 12 | *cipher.StreamReader 13 | *cipher.StreamWriter 14 | } 15 | 16 | type CipherConn struct { 17 | net.Conn 18 | Rwc io.ReadWriteCloser 19 | } 20 | 21 | func NewRC4Cipher(rwc io.ReadWriteCloser, key []byte) (*RC4Cipher, error) { 22 | decryptCipher, err := rc4.NewCipher(key) 23 | if err != nil { 24 | return nil, err 25 | } 26 | encryptCipher, err := rc4.NewCipher(key) 27 | if err != nil { 28 | return nil, err 29 | } 30 | return &RC4Cipher{ 31 | StreamReader: &cipher.StreamReader{ 32 | S: decryptCipher, 33 | R: rwc, 34 | }, 35 | StreamWriter: &cipher.StreamWriter{ 36 | S: encryptCipher, 37 | W: rwc, 38 | }, 39 | }, nil 40 | } 41 | 42 | func NewCipherConn(conn net.Conn, password string) (*CipherConn, error) { 43 | key := sha256.Sum256([]byte(password)) 44 | rwc, err := NewRC4Cipher(conn, key[:]) 45 | 46 | if err != nil { 47 | return nil, err 48 | } 49 | 50 | return &CipherConn{ 51 | Conn: conn, 52 | Rwc: rwc, 53 | }, nil 54 | } -------------------------------------------------------------------------------- /internal/runner/options.go: -------------------------------------------------------------------------------- 1 | package runner 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "github.com/canc3s/gnc/internal/gologger" 7 | "os" 8 | ) 9 | 10 | const banner = ` 11 | ▄██████▄ ███▄▄▄▄ ▄████████ 12 | ███ ███ ███▀▀▀██▄ ███ ███ 13 | ███ █▀ ███ ███ ███ █▀ 14 | ▄███ ███ ███ ███ 15 | ▀▀███ ████▄ ███ ███ ███ 16 | ███ ███ ███ ███ ███ █▄ 17 | ███ ███ ███ ███ ███ ███ 18 | ████████▀ ▀█ █▀ ████████▀ v` 19 | 20 | 21 | 22 | // Version is the current version of gnc 23 | const Version = `0.0.1` 24 | 25 | type Options struct { 26 | Listen bool 27 | //Udp bool 28 | Security bool 29 | Version bool 30 | Silent bool 31 | Port int 32 | Addr string 33 | Exec string 34 | Proto string 35 | Pass string 36 | } 37 | 38 | func ParseOptions() *Options { 39 | options := &Options{} 40 | 41 | flag.IntVar(&options.Port, "p", 0, "监听的端口号") 42 | flag.BoolVar(&options.Listen, "l", false, "纯净模式") 43 | //flag.BoolVar(&options.Udp, "u", false, "Silent mode") 44 | flag.BoolVar(&options.Security, "s", false, "开启加密模式") 45 | flag.BoolVar(&options.Silent, "silent", false, "纯净模式") 46 | flag.StringVar(&options.Exec, "e", "", "连接后执行的程序") 47 | flag.StringVar(&options.Pass, "pass", "", "指定加密模式的密码(客服端和服务端需保持一直,通信乱码请检查指定密码是否一致)") 48 | 49 | showBanner() 50 | 51 | flag.Parse() 52 | 53 | if options.Version { 54 | gologger.Infof("Current Version: %s\n", Version) 55 | os.Exit(0) 56 | } 57 | 58 | options.configureOutput() 59 | 60 | options.validateOptions() 61 | 62 | return options 63 | } 64 | 65 | func (options *Options) validateOptions() { 66 | 67 | //if options.Udp { 68 | // options.Proto = "udp" 69 | //} else { 70 | // options.Proto = "tcp" 71 | //} 72 | options.Proto = "tcp" 73 | 74 | 75 | if options.Port < 0 || options.Port > 65535 { 76 | gologger.Fatalf("非法端口,请检查输入") 77 | } 78 | 79 | options.Addr = fmt.Sprintf("%s:%s", flag.Arg(0), flag.Arg(1)) 80 | 81 | //fmt.Println(options.Addr) 82 | 83 | //fmt.Println(flag.Arg(2)) 84 | //if options.CompanyID != "" && len(options.CompanyID) != 10 { 85 | // gologger.Fatalf("公司ID %s 不正确!(10位数字)\n", options.CompanyID) 86 | //} 87 | //if options.InputFile != "" && !fileutil.FileExists(options.InputFile) { 88 | // gologger.Fatalf("文件 %s 不存在!\n", options.InputFile) 89 | //} 90 | //if options.CompanyID == "" && options.InputFile == "" { 91 | // flag.PrintDefaults() 92 | // os.Exit(0) 93 | //} 94 | } 95 | 96 | func showBanner() { 97 | gologger.Printf("%s %s\n", banner,Version) 98 | //gologger.Printf("\t\thttps://github.com/canc3s/xx\n\n") 99 | 100 | //gologger.Labelf("请谨慎使用,您应对自己的行为负责\n") 101 | //gologger.Labelf("开发人员不承担任何责任,也不对任何滥用或损坏负责.\n") 102 | } 103 | 104 | // configureOutput configures the output on the screen 105 | func (options *Options) configureOutput() { 106 | if options.Silent { 107 | gologger.MaxLevel = gologger.Silent 108 | } 109 | } -------------------------------------------------------------------------------- /internal/runner/runner.go: -------------------------------------------------------------------------------- 1 | package runner 2 | 3 | import ( 4 | "github.com/canc3s/gnc/internal/rc4" 5 | "github.com/canc3s/gnc/internal/tcp" 6 | "net" 7 | ) 8 | 9 | func CipWorker (conn net.Conn, options *Options) { 10 | CipherConn,_ := rc4.NewCipherConn(conn, options.Pass) 11 | tcp.CipTransferStreams(CipherConn, options.Exec) 12 | } 13 | 14 | func Process(options *Options) { 15 | if options.Listen { 16 | conn := tcp.StartServer(options.Proto, options.Port) 17 | if options.Security { 18 | CipWorker(conn, options) 19 | return 20 | } 21 | tcp.TransferStreams(conn, options.Exec) 22 | return 23 | } 24 | conn := tcp.StartClient(options.Proto, options.Addr) 25 | if options.Security { 26 | CipWorker(conn, options) 27 | return 28 | } 29 | tcp.TransferStreams(conn,options.Exec) 30 | return 31 | } 32 | -------------------------------------------------------------------------------- /internal/tcp/tcp.go: -------------------------------------------------------------------------------- 1 | package tcp 2 | 3 | import ( 4 | "fmt" 5 | "github.com/canc3s/gnc/internal/gologger" 6 | "github.com/canc3s/gnc/internal/rc4" 7 | "io" 8 | "net" 9 | "os" 10 | "os/exec" 11 | ) 12 | 13 | type ExecResult struct { 14 | Cmd *exec.Cmd 15 | Stdin io.WriteCloser 16 | Stdout io.ReadCloser 17 | Stderr io.ReadCloser 18 | } 19 | 20 | type Progress struct { 21 | bytes uint64 22 | } 23 | 24 | func CipTransferStreams(con *rc4.CipherConn,execs string) { 25 | c := make(chan Progress) 26 | 27 | // Read from Reader and write to Writer until EOF 28 | copy := func(r io.ReadCloser, w io.WriteCloser) { 29 | defer func() { 30 | r.Close() 31 | w.Close() 32 | }() 33 | n, err := io.Copy(w, r) 34 | if err != nil { 35 | gologger.Infof("[%s]: ERROR: %s\n", con.RemoteAddr(), err) 36 | } 37 | c <- Progress{bytes: uint64(n)} 38 | } 39 | 40 | if execs != "" { 41 | var execr ExecResult 42 | Exec(execs, &execr) 43 | go copy(con.Rwc, execr.Stdin) 44 | go copy(execr.Stdout, con.Rwc) 45 | go copy(execr.Stderr, con.Rwc) 46 | } else { 47 | go copy(con.Rwc, os.Stdout) 48 | go copy(os.Stdin, con.Rwc) 49 | } 50 | 51 | p := <-c 52 | gologger.Printf("[%s]: Connection has been closed by remote peer, %d bytes has been received\n", con.RemoteAddr(), p.bytes) 53 | } 54 | 55 | 56 | func TransferStreams(con net.Conn,execs string) { 57 | c := make(chan Progress) 58 | 59 | // Read from Reader and write to Writer until EOF 60 | copy := func(r io.ReadCloser, w io.WriteCloser) { 61 | defer func() { 62 | r.Close() 63 | w.Close() 64 | }() 65 | n, err := io.Copy(w, r) 66 | if err != nil { 67 | gologger.Infof("[%s]: ERROR: %s\n", con.RemoteAddr(), err) 68 | } 69 | c <- Progress{bytes: uint64(n)} 70 | } 71 | 72 | 73 | 74 | if execs != "" { 75 | var execr ExecResult 76 | Exec(execs, &execr) 77 | go copy(con, execr.Stdin) 78 | go copy(execr.Stdout, con) 79 | go copy(execr.Stderr, con) 80 | } else { 81 | go copy(con, os.Stdout) 82 | go copy(os.Stdin, con) 83 | } 84 | 85 | p := <-c 86 | gologger.Printf("[%s]: Connection has been closed by remote peer, %d bytes has been received\n", con.RemoteAddr(), p.bytes) 87 | } 88 | 89 | func StartServer(proto string, port int) net.Conn { 90 | addr := fmt.Sprintf(":%d", port) 91 | ln, err := net.Listen(proto, addr) 92 | if err != nil { 93 | gologger.Fatalf("%s\n", err) 94 | } 95 | gologger.Printf("Listening on %s\n", proto, port) 96 | con, err := ln.Accept() 97 | if err != nil { 98 | gologger.Fatalf("%s\n", err) 99 | } 100 | gologger.Printf("[%s]: Connection has been opened\n", con.RemoteAddr()) 101 | return con 102 | } 103 | 104 | func StartClient(proto string, addr string) net.Conn { 105 | con, err := net.Dial(proto, addr) 106 | if err != nil { 107 | gologger.Fatalf("%s\n", err) 108 | } 109 | gologger.Printf("Connected to %s\n", addr) 110 | return con 111 | } 112 | 113 | func Exec(execs string, execr *ExecResult){ 114 | var err error 115 | execr.Cmd = exec.Command(execs) 116 | execr.Stdin, err = execr.Cmd.StdinPipe() 117 | if err != nil { 118 | gologger.Fatalf("%s\n", err) 119 | } 120 | execr.Stdout, err = execr.Cmd.StdoutPipe() 121 | if err != nil { 122 | gologger.Fatalf("%s\n", err) 123 | } 124 | execr.Stderr, err = execr.Cmd.StderrPipe() 125 | if err != nil { 126 | gologger.Fatalf("%s\n", err) 127 | } 128 | if err := execr.Cmd.Start(); err != nil { 129 | gologger.Fatalf("%s\n", err) 130 | } 131 | } --------------------------------------------------------------------------------