实现一个端口扫描器
端口的开放与否基于如下判断
第一个端口扫描代码
package main
import (
"fmt"
"net"
)
func main() {
_, err := net.Dial("tcp", "129.211.73.107:80")
if err == nil {
fmt.Println("Connection successful")
}else{
fmt.Println("connect error")
}
}
看一下Dial有文档
➜ p2 go doc net.Dial
warning: pattern "all" matched no module dependencies
package net // import "net"
func Dial(network, address string) (Conn, error) //注意这里第二个参为string类型
Dial connects to the address on the named network.
Known networks are "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only), "udp",
"udp4" (IPv4-only), "udp6" (IPv6-only), "ip", "ip4" (IPv4-only), "ip6"
(IPv6-only), "unix", "unixgram" and "unixpacket".
For TCP and UDP networks, the address has the form "host:port". The host
must be a literal IP address, or a host name that can be resolved to IP
addresses. The port must be a literal port number or a service name. If the
host is a literal IPv6 address it must be enclosed in square brackets, as in
"[2001:db8::1]:80" or "[fe80::1%zone]:80". The zone specifies the scope of
the literal IPv6 address as defined in RFC 4007. The functions JoinHostPort
and SplitHostPort manipulate a pair of host and port in this form. When
using TCP, and the host resolves to multiple IP addresses, Dial will try
each IP address in order until one succeeds.
Examples:
Dial("tcp", "golang.org:http")
Dial("tcp", "192.0.2.1:http")
Dial("tcp", "198.51.100.1:80")
Dial("udp", "[2001:db8::1]:domain")
Dial("udp", "[fe80::1%lo0]:53")
Dial("tcp", ":80")
For IP networks, the network must be "ip", "ip4" or "ip6" followed by a
colon and a literal protocol number or a protocol name, and the address has
the form "host". The host must be a literal IP address or a literal IPv6
address with zone. It depends on each operating system how the operating
system behaves with a non-well known protocol number such as "0" or "255".
Examples:
Dial("ip4:1", "192.0.2.1")
Dial("ip6:ipv6-icmp", "2001:db8::1")
Dial("ip6:58", "fe80::1%lo0")
For TCP, UDP and IP networks, if the host is empty or a literal unspecified
IP address, as in ":80", "0.0.0.0:80" or "[::]:80" for TCP and UDP, "",
"0.0.0.0" or "::" for IP, the local system is assumed.
For Unix networks, the address must be a file system path.
扫描1024端口
package main
import (
"fmt"
"net"
)
func main() {
for i:=1;i<1024;i++{
address := fmt.Sprintf("129.211.73.107:%d",i)
_,err := net.Dial("tcp",address)
if err != nil{
continue
} else {
fmt.Printf("[+] port open -> %d \n",i)
}
}
}
goroutine版本
import (
"fmt"
"net"
)
func main() {
for i := 1; i <= 1024; i++ {
go func(j int) {
address := fmt.Sprintf("129.211.73.107:%d", j)
conn, err := net.Dial("tcp", address)
if err != nil {
return
}
conn.Close()
fmt.Printf("%d open\n", j)
}(i)
}
}
但很遗憾这是没有输出的。原因在上一章已经说明,主线程如果这样来写是不会等待goroutine结束的,当循环1024后就直接退出,也就没有goroutine输出了。
waitGroup版本
package main
import (
"fmt"
"net"
"sync"
"time"
)
var wg sync.WaitGroup // 声名waitgroup
func main() {
stime := time.Now()
for i:=1;i<1024;i++{
go func(j int) {
wg.Add(1) // 当前队伍等待数量--
defer wg.Done() // defer是一个关键字,当前函数所有操作完成后再执行
address := fmt.Sprintf("129.211.73.107:%d",j)
_,err :=net.Dial("tcp",address)
if err == nil{
fmt.Printf("[+] port open -> %d\n",j)
}
}(i)
}
wg.Wait() // 主线程等待等待队伍为0再结束
fmt.Println(time.Since(stime)) // 计算程序运行时间
}
workerpool版本
虽然我也不知道上面的版本有啥问题?没有排序?
package main
import (
"fmt"
"net"
"sort"
)
func worker(ports, results chan int) {
for p := range ports {
address := fmt.Sprintf("129.211.73.107:%d", p)
conn, err := net.Dial("tcp", address)
if err != nil {
results <- 0
continue
}
conn.Close()
results <- p
}
}
func main() {
ports := make(chan int, 100)
results := make(chan int)
var openports []int
for i := 0; i < cap(ports); i++ {
go worker(ports, results)
}
go func() {
for i := 1; i <= 1024; i++ {
ports <- i
}
}()
for i := 0; i < 1024; i++ {
port := <-results
if port != 0 {
openports = append(openports, port)
}
}
close(ports)
close(results)
sort.Ints(openports)
for _, port := range openports {
fmt.Printf("%d open\n", port)
}
}
使用两个chan来输入和输出结果,好在能够对结果数据进行处理,但并不能实时显示。
课后改进版
package main
import (
"flag"
//"debug/elf"
"fmt"
"net"
"strconv"
"strings"
"sync"
)
var wg sync.WaitGroup
var (
h string
d string
p string
)
func scan(host string,ports []int) {
for _,i := range(ports){
go func(j int) {
wg.Add(1)
defer wg.Done()
address := fmt.Sprintf("%s:%d",host,j)
_,err :=net.Dial("tcp",address)
if err == nil{
fmt.Printf("[+] port open -> %d\n",j)
}
}(i)
}
wg.Wait()
}
func parsePorts(pstring string) []int {
var ports = make([]int,0)
parts := strings.Split(pstring,",")
for _,part := range(parts){
if strings.Index(part,"-") == -1{
port,err := strconv.Atoi(part)
if err == nil{
ports = append(ports, port)
}else {
fmt.Println("psrse error")
return nil
}
}else {
left_right := strings.Split(string(part),"-")
start,err := strconv.Atoi(left_right[0])
end,err2 := strconv.Atoi(left_right[1])
if err == nil && err2 == nil{
for i := start;i<=end;i++{
ports = append(ports,i)
}
}else {}
}
}
//fmt.Println(ports)
return ports
}
func main() {
flag.StringVar(&h,"h","","host")
flag.StringVar(&p,"p","","ports to scan")
flag.Parse()
//stime := time.Now()
//fmt.Println(time.Since(stime))
if h!="" && p != ""{
scan(h,parsePorts(p))
}else {
flag.Usage()
}
}
TCP代理
stdin和stdout
两个接口
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
package main
import (
"fmt"
"log"
"os"
)
// FooReader defines an io.Reader to read from stdin.
type FooReader struct{}
// Read reads data from stdin.
func (fooReader *FooReader) Read(b []byte) (int, error) { // 这里实现了Read接口
fmt.Print("in > ")
return os.Stdin.Read(b) // 这个函数从系统的标准输入len(b)长度内读取字符,返回读取到的字符长度,此时input内已经存入“123"和4093空字符
}
// FooWriter defines an io.Writer to write to Stdout.
type FooWriter struct{}
// Write writes data to Stdout.
func (fooWriter *FooWriter) Write(b []byte) (int, error) { // 这里实现了Write接口
fmt.Print("out> ")
return os.Stdout.Write(b) // 向系统标准输出写入b
}
func main() {
// Instantiate reader and writer.
var (
reader FooReader
writer FooWriter
)
// Create buffer to hold input/output.
input := make([]byte, 4096)
// Use reader to read input.
s, err := reader.Read(input)
if err != nil {
log.Fatalln("Unable to read data")
}
fmt.Printf("Read %d bytes from stdin\n", s)
// Use writer to write output.
s, err = writer.Write(input)
if err != nil {
log.Fatalln("Unable to write data")
}
fmt.Printf("Wrote %d bytes to stdout\n", s)
}
os.Stdin.Read 这个函数从系统的标准输入len(b)长度内读取字符,返回读取到的字符长度,此时input内已经存入”123”和4093空字符所以将input再写入stdout就会全部写出。
io.Copy函数
func main() {
var (
reader FooReader
writer FooWriter
)
if _, err := io.Copy(&writer, &reader)❶; err != nil {
log.Fatalln("Unable to read/write data")
}
}
这个函数比较魔幻,隐式调用Read和Write方法,儿没有繁琐的细节。
io on tcp
package main
import (
"io"
"log"
"net"
)
// echo is a handler function that simply echoes received data.
func echo(conn net.Conn) {
defer conn.Close() // 函数运行完成关闭Conn
// Create a buffer to store received data.
b := make([]byte, 512)
for {
// Receive data via conn.Read into a buffer.
size, err := conn.Read(b[0:])
if err == io.EOF {
log.Println("Client disconnected")
break
}
if err != nil {
log.Println("Unexpected error")
break
}
log.Printf("Received %d bytes: %s\n", size, string(b))
// Send data via conn.Write.
log.Println("Writing data")
if _, err := conn.Write(b[0:size]); err != nil {
log.Fatalln("Unable to write data")
}
}
}
func main() {
// Bind to TCP port 20080 on all interfaces.
listener, err := net.Listen("tcp", ":20080")
if err != nil {
log.Fatalln("Unable to bind to port")
}
log.Println("Listening on 0.0.0.0:20080")
for {
// Wait for connection. Create net.Conn on connection established.
conn, err := listener.Accept()
log.Println("Received connection")
if err != nil {
log.Fatalln("Unable to accept connection")
}
// Handle the connection. Using goroutine for concurrency.
go echo(conn) // go关键字执行非阻塞
}
}
这里的Conn
type netFD struct {
pfd poll.FD
// immutable until Close
family int
sotype int
isConnected bool // handshake completed or use of association with peer
net string
laddr Addr
raddr Addr
}
bufio
func echo(conn net.Conn) {
defer conn.Close()
reader := bufio.NewReader(conn)
s, err := reader.ReadString('\n') // 指定读取到回车停止
if err != nil {
log.Fatalln("Unable to read data")
}
log.Printf("Read %d bytes: %s", len(s), s)
log.Println("Writing data")
writer := bufio.NewWriter(conn)
if _, err := writer.WriteString(s); err != nil {
log.Fatalln("Unable to write data")
}
writer.Flush()
}
注意的是
- bufio.NewReader
- reader.ReadString
- bufio.NewWriter
- writer.WriteString
再次简化
没错使用io.Copy,上面已经说过了,Conn实现了Read和Write方法,那么就可以直接传入io.Copy函数,由io.Copy函数自动调用Read和Write方法。func echo(conn net.Conn) {
defer conn.Close()
// Copy data from io.Reader to io.Writer via io.Copy().
if _, err := io.Copy(conn, conn); err != nil {
log.Fatalln("Unable to read/write data")
}
}
实现一个端口转发
```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)
}
这里的精髓就是57到62行需要用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)
<a name="bNWGO"></a>
### netcatexec
```go
package main
import (
"fmt"
"io"
"net"
"os"
"os/exec"
)
var (
lp int
r string
rp int
)
func main() {
if len(os.Args) != 2{
fmt.Println("netcatexec 8080")
os.Exit(0)
}
port := os.Args[1]
listener,err := net.Listen("tcp",fmt.Sprintf(":%s",port))
if err!=nil{
fmt.Printf("bind port %d error\n",port)
}
for{
conn,err := listener.Accept()
if err != nil {
fmt.Println("accept error !")
}
handler(conn)
}
}
func handler(conn net.Conn) {
defer conn.Close()
cmd := exec.Command("/bin/sh","-i")
rp,wp := io.Pipe()
cmd.Stdin = conn
cmd.Stdout = wp
go io.Copy(conn,rp)
cmd.Run()
}