实现一个端口扫描器

端口的开放与否基于如下判断

image.png

第一个端口扫描代码

  1. package main
  2. import (
  3. "fmt"
  4. "net"
  5. )
  6. func main() {
  7. _, err := net.Dial("tcp", "129.211.73.107:80")
  8. if err == nil {
  9. fmt.Println("Connection successful")
  10. }else{
  11. fmt.Println("connect error")
  12. }
  13. }

看一下Dial有文档

  1. p2 go doc net.Dial
  2. warning: pattern "all" matched no module dependencies
  3. package net // import "net"
  4. func Dial(network, address string) (Conn, error) //注意这里第二个参为string类型
  5. Dial connects to the address on the named network.
  6. Known networks are "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only), "udp",
  7. "udp4" (IPv4-only), "udp6" (IPv6-only), "ip", "ip4" (IPv4-only), "ip6"
  8. (IPv6-only), "unix", "unixgram" and "unixpacket".
  9. For TCP and UDP networks, the address has the form "host:port". The host
  10. must be a literal IP address, or a host name that can be resolved to IP
  11. addresses. The port must be a literal port number or a service name. If the
  12. host is a literal IPv6 address it must be enclosed in square brackets, as in
  13. "[2001:db8::1]:80" or "[fe80::1%zone]:80". The zone specifies the scope of
  14. the literal IPv6 address as defined in RFC 4007. The functions JoinHostPort
  15. and SplitHostPort manipulate a pair of host and port in this form. When
  16. using TCP, and the host resolves to multiple IP addresses, Dial will try
  17. each IP address in order until one succeeds.
  18. Examples:
  19. Dial("tcp", "golang.org:http")
  20. Dial("tcp", "192.0.2.1:http")
  21. Dial("tcp", "198.51.100.1:80")
  22. Dial("udp", "[2001:db8::1]:domain")
  23. Dial("udp", "[fe80::1%lo0]:53")
  24. Dial("tcp", ":80")
  25. For IP networks, the network must be "ip", "ip4" or "ip6" followed by a
  26. colon and a literal protocol number or a protocol name, and the address has
  27. the form "host". The host must be a literal IP address or a literal IPv6
  28. address with zone. It depends on each operating system how the operating
  29. system behaves with a non-well known protocol number such as "0" or "255".
  30. Examples:
  31. Dial("ip4:1", "192.0.2.1")
  32. Dial("ip6:ipv6-icmp", "2001:db8::1")
  33. Dial("ip6:58", "fe80::1%lo0")
  34. For TCP, UDP and IP networks, if the host is empty or a literal unspecified
  35. IP address, as in ":80", "0.0.0.0:80" or "[::]:80" for TCP and UDP, "",
  36. "0.0.0.0" or "::" for IP, the local system is assumed.
  37. For Unix networks, the address must be a file system path.

扫描1024端口

  1. package main
  2. import (
  3. "fmt"
  4. "net"
  5. )
  6. func main() {
  7. for i:=1;i<1024;i++{
  8. address := fmt.Sprintf("129.211.73.107:%d",i)
  9. _,err := net.Dial("tcp",address)
  10. if err != nil{
  11. continue
  12. } else {
  13. fmt.Printf("[+] port open -> %d \n",i)
  14. }
  15. }
  16. }

image.png
速度巨慢有没有

goroutine版本

  1. import (
  2. "fmt"
  3. "net"
  4. )
  5. func main() {
  6. for i := 1; i <= 1024; i++ {
  7. go func(j int) {
  8. address := fmt.Sprintf("129.211.73.107:%d", j)
  9. conn, err := net.Dial("tcp", address)
  10. if err != nil {
  11. return
  12. }
  13. conn.Close()
  14. fmt.Printf("%d open\n", j)
  15. }(i)
  16. }
  17. }

但很遗憾这是没有输出的。原因在上一章已经说明,主线程如果这样来写是不会等待goroutine结束的,当循环1024后就直接退出,也就没有goroutine输出了。

waitGroup版本

  1. package main
  2. import (
  3. "fmt"
  4. "net"
  5. "sync"
  6. "time"
  7. )
  8. var wg sync.WaitGroup // 声名waitgroup
  9. func main() {
  10. stime := time.Now()
  11. for i:=1;i<1024;i++{
  12. go func(j int) {
  13. wg.Add(1) // 当前队伍等待数量--
  14. defer wg.Done() // defer是一个关键字,当前函数所有操作完成后再执行
  15. address := fmt.Sprintf("129.211.73.107:%d",j)
  16. _,err :=net.Dial("tcp",address)
  17. if err == nil{
  18. fmt.Printf("[+] port open -> %d\n",j)
  19. }
  20. }(i)
  21. }
  22. wg.Wait() // 主线程等待等待队伍为0再结束
  23. fmt.Println(time.Since(stime)) // 计算程序运行时间
  24. }

image.png

workerpool版本

虽然我也不知道上面的版本有啥问题?没有排序?

  1. package main
  2. import (
  3. "fmt"
  4. "net"
  5. "sort"
  6. )
  7. func worker(ports, results chan int) {
  8. for p := range ports {
  9. address := fmt.Sprintf("129.211.73.107:%d", p)
  10. conn, err := net.Dial("tcp", address)
  11. if err != nil {
  12. results <- 0
  13. continue
  14. }
  15. conn.Close()
  16. results <- p
  17. }
  18. }
  19. func main() {
  20. ports := make(chan int, 100)
  21. results := make(chan int)
  22. var openports []int
  23. for i := 0; i < cap(ports); i++ {
  24. go worker(ports, results)
  25. }
  26. go func() {
  27. for i := 1; i <= 1024; i++ {
  28. ports <- i
  29. }
  30. }()
  31. for i := 0; i < 1024; i++ {
  32. port := <-results
  33. if port != 0 {
  34. openports = append(openports, port)
  35. }
  36. }
  37. close(ports)
  38. close(results)
  39. sort.Ints(openports)
  40. for _, port := range openports {
  41. fmt.Printf("%d open\n", port)
  42. }
  43. }

image.png
使用两个chan来输入和输出结果,好在能够对结果数据进行处理,但并不能实时显示。

课后改进版

  1. package main
  2. import (
  3. "flag"
  4. //"debug/elf"
  5. "fmt"
  6. "net"
  7. "strconv"
  8. "strings"
  9. "sync"
  10. )
  11. var wg sync.WaitGroup
  12. var (
  13. h string
  14. d string
  15. p string
  16. )
  17. func scan(host string,ports []int) {
  18. for _,i := range(ports){
  19. go func(j int) {
  20. wg.Add(1)
  21. defer wg.Done()
  22. address := fmt.Sprintf("%s:%d",host,j)
  23. _,err :=net.Dial("tcp",address)
  24. if err == nil{
  25. fmt.Printf("[+] port open -> %d\n",j)
  26. }
  27. }(i)
  28. }
  29. wg.Wait()
  30. }
  31. func parsePorts(pstring string) []int {
  32. var ports = make([]int,0)
  33. parts := strings.Split(pstring,",")
  34. for _,part := range(parts){
  35. if strings.Index(part,"-") == -1{
  36. port,err := strconv.Atoi(part)
  37. if err == nil{
  38. ports = append(ports, port)
  39. }else {
  40. fmt.Println("psrse error")
  41. return nil
  42. }
  43. }else {
  44. left_right := strings.Split(string(part),"-")
  45. start,err := strconv.Atoi(left_right[0])
  46. end,err2 := strconv.Atoi(left_right[1])
  47. if err == nil && err2 == nil{
  48. for i := start;i<=end;i++{
  49. ports = append(ports,i)
  50. }
  51. }else {}
  52. }
  53. }
  54. //fmt.Println(ports)
  55. return ports
  56. }
  57. func main() {
  58. flag.StringVar(&h,"h","","host")
  59. flag.StringVar(&p,"p","","ports to scan")
  60. flag.Parse()
  61. //stime := time.Now()
  62. //fmt.Println(time.Since(stime))
  63. if h!="" && p != ""{
  64. scan(h,parsePorts(p))
  65. }else {
  66. flag.Usage()
  67. }
  68. }

image.png

TCP代理

stdin和stdout

两个接口

  1. type Reader interface {
  2. Read(p []byte) (n int, err error)
  3. }
  4. type Writer interface {
  5. Write(p []byte) (n int, err error)
  6. }
  1. package main
  2. import (
  3. "fmt"
  4. "log"
  5. "os"
  6. )
  7. // FooReader defines an io.Reader to read from stdin.
  8. type FooReader struct{}
  9. // Read reads data from stdin.
  10. func (fooReader *FooReader) Read(b []byte) (int, error) { // 这里实现了Read接口
  11. fmt.Print("in > ")
  12. return os.Stdin.Read(b) // 这个函数从系统的标准输入len(b)长度内读取字符,返回读取到的字符长度,此时input内已经存入“123"和4093空字符
  13. }
  14. // FooWriter defines an io.Writer to write to Stdout.
  15. type FooWriter struct{}
  16. // Write writes data to Stdout.
  17. func (fooWriter *FooWriter) Write(b []byte) (int, error) { // 这里实现了Write接口
  18. fmt.Print("out> ")
  19. return os.Stdout.Write(b) // 向系统标准输出写入b
  20. }
  21. func main() {
  22. // Instantiate reader and writer.
  23. var (
  24. reader FooReader
  25. writer FooWriter
  26. )
  27. // Create buffer to hold input/output.
  28. input := make([]byte, 4096)
  29. // Use reader to read input.
  30. s, err := reader.Read(input)
  31. if err != nil {
  32. log.Fatalln("Unable to read data")
  33. }
  34. fmt.Printf("Read %d bytes from stdin\n", s)
  35. // Use writer to write output.
  36. s, err = writer.Write(input)
  37. if err != nil {
  38. log.Fatalln("Unable to write data")
  39. }
  40. fmt.Printf("Wrote %d bytes to stdout\n", s)
  41. }

os.Stdin.Read 这个函数从系统的标准输入len(b)长度内读取字符,返回读取到的字符长度,此时input内已经存入”123”和4093空字符所以将input再写入stdout就会全部写出。

io.Copy函数

  1. func main() {
  2. var (
  3. reader FooReader
  4. writer FooWriter
  5. )
  6. if _, err := io.Copy(&writer, &reader)❶; err != nil {
  7. log.Fatalln("Unable to read/write data")
  8. }
  9. }

这个函数比较魔幻,隐式调用Read和Write方法,儿没有繁琐的细节。
image.png

io on tcp

  1. package main
  2. import (
  3. "io"
  4. "log"
  5. "net"
  6. )
  7. // echo is a handler function that simply echoes received data.
  8. func echo(conn net.Conn) {
  9. defer conn.Close() // 函数运行完成关闭Conn
  10. // Create a buffer to store received data.
  11. b := make([]byte, 512)
  12. for {
  13. // Receive data via conn.Read into a buffer.
  14. size, err := conn.Read(b[0:])
  15. if err == io.EOF {
  16. log.Println("Client disconnected")
  17. break
  18. }
  19. if err != nil {
  20. log.Println("Unexpected error")
  21. break
  22. }
  23. log.Printf("Received %d bytes: %s\n", size, string(b))
  24. // Send data via conn.Write.
  25. log.Println("Writing data")
  26. if _, err := conn.Write(b[0:size]); err != nil {
  27. log.Fatalln("Unable to write data")
  28. }
  29. }
  30. }
  31. func main() {
  32. // Bind to TCP port 20080 on all interfaces.
  33. listener, err := net.Listen("tcp", ":20080")
  34. if err != nil {
  35. log.Fatalln("Unable to bind to port")
  36. }
  37. log.Println("Listening on 0.0.0.0:20080")
  38. for {
  39. // Wait for connection. Create net.Conn on connection established.
  40. conn, err := listener.Accept()
  41. log.Println("Received connection")
  42. if err != nil {
  43. log.Fatalln("Unable to accept connection")
  44. }
  45. // Handle the connection. Using goroutine for concurrency.
  46. go echo(conn) // go关键字执行非阻塞
  47. }
  48. }

这里的Conn

  1. type netFD struct {
  2. pfd poll.FD
  3. // immutable until Close
  4. family int
  5. sotype int
  6. isConnected bool // handshake completed or use of association with peer
  7. net string
  8. laddr Addr
  9. raddr Addr
  10. }

Conn也实现了Read和Write函数。
image.png

bufio

  1. func echo(conn net.Conn) {
  2. defer conn.Close()
  3. reader := bufio.NewReader(conn)
  4. s, err := reader.ReadString('\n') // 指定读取到回车停止
  5. if err != nil {
  6. log.Fatalln("Unable to read data")
  7. }
  8. log.Printf("Read %d bytes: %s", len(s), s)
  9. log.Println("Writing data")
  10. writer := bufio.NewWriter(conn)
  11. if _, err := writer.WriteString(s); err != nil {
  12. log.Fatalln("Unable to write data")
  13. }
  14. writer.Flush()
  15. }

注意的是

  • bufio.NewReader
  • reader.ReadString
  • bufio.NewWriter
  • writer.WriteString

    再次简化

    1. func echo(conn net.Conn) {
    2. defer conn.Close()
    3. // Copy data from io.Reader to io.Writer via io.Copy().
    4. if _, err := io.Copy(conn, conn); err != nil {
    5. log.Fatalln("Unable to read/write data")
    6. }
    7. }
    没错使用io.Copy,上面已经说过了,Conn实现了Read和Write方法,那么就可以直接传入io.Copy函数,由io.Copy函数自动调用Read和Write方法。

    实现一个端口转发

    ```go package main

import ( “flag” “fmt” “io” “log” “net” “os” )

var ( lp int r string rp int )

func main() { Init() listener,err := net.Listen(“tcp”,fmt.Sprintf(“:%d”,lp)) if err != nil{ log.Fatal(“bind lport error !”) return } log.Printf(“start listen on 0.0.0.0:%d … “,lp) for { conn,err := listener.Accept() if err !=nil{ log.Fatal(“receive conn error !”) } log.Printf(“receive connection from %s\n”,conn.RemoteAddr()) go handler(conn) } }

func Init() { flag.IntVar(&lp,”l”,4444,”listen localhost port”) flag.StringVar(&r,”r”,””,”remote host”) flag.IntVar(&rp,”p”,80,”remote port”) flag.Parse() if r ==””{ flag.Usage() os.Exit(0) } }

func handler(conn net.Conn) { defer conn.Close() dstconn,err :=net.Dial(“tcp”,fmt.Sprintf(“%s:%d”,r,rp)) if err != nil{ log.Fatal(“bind port error ! “) return } log.Printf(“handling data …”) go func() { if ,err = io.Copy(dstconn,conn);err!=nil{ log.Fatalln(“connect error !”) } log.Printf(“>>> %s:%d\n”,r,rp) }() ,err = io.Copy(conn,dstconn) if err != nil{ log.Fatal(“proxy error ! “) } log.Printf(“<<< %s:%d\n”,r,rp)

}

  1. 这里的精髓就是5762行需要用go关键字进行异步执行,否则数据就有去无回。<br />![image.png](https://cdn.nlark.com/yuque/0/2020/png/436678/1598864464129-f5ef97ee-c0ac-4d5c-9a32-b443caf16285.png#align=left&display=inline&height=166&margin=%5Bobject%20Object%5D&name=image.png&originHeight=166&originWidth=882&size=29565&status=done&style=none&width=882)
  2. <a name="bNWGO"></a>
  3. ### netcatexec
  4. ```go
  5. package main
  6. import (
  7. "fmt"
  8. "io"
  9. "net"
  10. "os"
  11. "os/exec"
  12. )
  13. var (
  14. lp int
  15. r string
  16. rp int
  17. )
  18. func main() {
  19. if len(os.Args) != 2{
  20. fmt.Println("netcatexec 8080")
  21. os.Exit(0)
  22. }
  23. port := os.Args[1]
  24. listener,err := net.Listen("tcp",fmt.Sprintf(":%s",port))
  25. if err!=nil{
  26. fmt.Printf("bind port %d error\n",port)
  27. }
  28. for{
  29. conn,err := listener.Accept()
  30. if err != nil {
  31. fmt.Println("accept error !")
  32. }
  33. handler(conn)
  34. }
  35. }
  36. func handler(conn net.Conn) {
  37. defer conn.Close()
  38. cmd := exec.Command("/bin/sh","-i")
  39. rp,wp := io.Pipe()
  40. cmd.Stdin = conn
  41. cmd.Stdout = wp
  42. go io.Copy(conn,rp)
  43. cmd.Run()
  44. }

image.png