go进阶

time包

time.Now()  //当前时间  2023-08-08 11:15:02.0855663 +0800 CST m=+0.001233401
//打印选定时间,年月日时分秒纳秒时区
t2:=time.Date(2004,1,15,8,26,22,0,time.Local)
fmt.Println(t2)
//2023-08-08 11:17:30.3234989 +0800 CST m=+0.001020901
	h, m, s := time.Now().Clock()
	fmt.Println(h, m, s)
//11 28 30

file操作

都用os包

Fileinfo   //返回文件信息和错误值

权限

r w x

读 写 可执行

如果没有的话就是-

-文件 d目录 |连接符

-rwx r-x r-x

-代表文件 rwx代表可读可写可执行 两个r-x分别代表所属组和其它人的权限

八进制表现

r–>004

w–>002

x–>001


chomd +x 777     中的777的7就是4+2+1,可读可写可执行

```go
package main

import (
	"fmt"
	"os"
)

func main() {
	err := os.Mkdir("./testss", 0777)
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println("创建成功")
	file, err := os.Create("123.txt")
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println(file) //&{0xc00011c780}
	//open是打开文件并建立连接的操作,open打开的文件的权限是只读
	file2, err := os.Open("D:/桌面/hack/if_open.txt")
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println(file2) //打开文件地址  &{0xc00011ca00}
	//关闭文件
	err = file2.Close()
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println("关闭成功")
    //删除目录或文件 (不放入回收站,慎用)
	err = os.Remove("D:/桌面/hack/if_open.txt")
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println("删除成功")
}

IO操作

input output

package main

import (
	"fmt"
	"io"
	"os"
)

func main() {
	fn := "D:/桌面/hack/test.txt"
	file, err := os.Open(fn)
	if err != nil {
		fmt.Println(err)
	}
	bs := make([]byte, 10)
	n, err := file.Read(bs)
	fmt.Println("这是err", err)
	fmt.Println(n)
	fmt.Println(bs)
	fmt.Println(string(bs))
	//读取完整文件
	i := -1
	for {
		i, err = file.Read(bs) //n是实际读取的字节数
		if i == 0 || err == io.EOF {
			fmt.Println("文件读取结束")
			break
		}
		fmt.Println("i现在是", i, string(bs[:i]))
	}

}

ioutil包

现在新版本golang里ioutil包已经被os和io包吞并了

package main

import (
	"fmt"
	"os"
)

func main() {
	filename := "D:/桌面/hack/test.txt"
	data, err := os.ReadFile(filename)
    //完整读取文件内容
	fmt.Println(string(data)) //wedfvujabxqwbkdxq64135qpdqwgdq5dqgkuw2541dqw
	fmt.Println(err)  //nil
	fmt.Printf("%T  %T", data, err)//[]uint8  <nil>
    s := "这是测试还是覆盖"
	err = os.WriteFile(filename, []byte(s), os.ModePerm) //这是覆盖函数,不是增加内容
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println("成功写入")
	dirname := "D:/桌面/hack"
	dirinfo, err := os.ReadDir(dirname)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Printf("类型是%T", dirinfo)
	for i := 0; i < len(dirinfo); i++ {
		fmt.Printf("第%d项是%s,是否是目录%t\n", i, dirinfo[i].Name(), dirinfo[i].IsDir())
	}
/*
第1项是.user.ini,是否是目录false
第2项是0.jpg,是否是目录false
第3项是1.bat,是否是目录false
第4项是1.jpg,是否是目录false
第5项是1.php,是否是目录false
第6项是ed8a8619567921e6744dc49adc0c4f75.png,是否是目录false
第7项是info.php,是否是目录false
第8项是pass.jpg,是否是目录false
第9项是test.txt,是否是目录false
第10项是whoami.php,是否是目录false
*/
}

并发并行

进程线程携程

进程是正在运行的程序

操作系统以进程为单位,进程以线程为单位,一个进程可能包含多个线程

比如打开word文档,word文档是正在运行的一个进程

在word里随便敲一行字,会自动触发开头首字母是否大写和整段话是否有拼写错误的检查,这是两个线程

携程是轻线程

goroutine

携程,封装main函数的是主goroutine

go标识要用的函数,函数不能有返回值

package main

import (
	"fmt"
)

func main() {
	go print_num()
	for j := 101; j < 200; j++ {
		fmt.Println(j)
	}
}

func print_num() {
	for i := 0; i < 100; i++ {
		fmt.Printf("这是第%d个子线程,i")
	}
}
//结果会是交替打印,如果没交替就多打印几次
//可能子线程没打印完,但主线程打印完了,那么子线程不会再继续

net


### 扫描器

#### 端口

```go
package main

import (
	"fmt"
	"net"
)

func main() {
	for i := 1; i <= 1024; i++ {
		go func(j int) {
			addr := fmt.Sprintf("127.0.0.1:%d", j)
			con, err := net.Dial("tcp", addr)
			if err != nil {
				return
			}
			con.Close()
			fmt.Println(j, "端口是打开的")
		}(i)
	}
}

稍微进阶一点

就是用了goroutine之后,可能有些携程没加载完,但主进程已经加载完了,这个的作用就是每一次循环就记一次数,循环完成之后记的数就是总的携程数,在用wait()函数等待所有携程完成,在结束进程

package main

import (
	"fmt"
	"net"
	"sync"
)

func main() {
	// 创建一个 sync.WaitGroup 实例,用于等待所有的 goroutines 完成
	var ab sync.WaitGroup

	// 使用循环,遍历从 0 到 1024 的端口号
	for i := 0; i < 1025; i++ {
		// 增加等待计数,表示有一个新的 goroutine 需要等待
		ab.Add(1)

		// 使用 go 关键字开启一个新的 goroutine
		go func(j int) {
			// 在 goroutine 完成时减少等待计数
			defer ab.Done()

			// 构造待扫描的 IP 地址和端口号字符串
			addr := fmt.Sprintf("127.0.0.1:%d", j)

			// 尝试通过 TCP 协议连接到指定地址
			con, err := net.Dial("tcp", addr)
			if err != nil {
				// 如果连接失败,表示端口是关闭的,函数直接返回
				return
			}
			// 连接成功,关闭连接
			con.Close()

			// 输出端口号是开放的信息
			fmt.Printf("%d is open\n", j)
		}(i) // 传递端口号 i 给匿名函数的参数 j
	}

	// 等待所有的 goroutines 完成
	ab.Wait()
}
package main

import (
	"fmt"
	"net"
	"sync"
)
func worker(ports chan int,wg *sync.WaitGroup){
	for p:=range ports{
		fmt.Println(p)
		wg.Done()
	}
}
func main() {
	ports:=make(chan int,100)
	var wg sync.WaitGroup
	for i:=0;i<cap(ports);i++{
		wg.Add(1)
		ports<-i
	}
	wg.Wait()
	close(ports)
}

等待组初步

package main

import (
	"fmt"
	"sync"
	"time"
)

func pro(i int, wg *sync.WaitGroup) {
	fmt.Printf("进程%d开始\n", i)
	time.Sleep(2 * time.Second)
	fmt.Printf("进程%d结束\n", i)
	wg.Done()
}
func main() {
	cou := 3
	var wg sync.WaitGroup
	for i := 0; i < cou; i++ {
		wg.Add(1)
		go pro(i, &wg)
	}
	wg.Wait()
	fmt.Println("main stop")
}

工人池

一个理论上能用的扫描器

package main

import (
	"fmt"
	"net"
	"sort"
)

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 is open\n", port)
	}
}
func worker(ports, results chan int) {
	for p := range ports {
		addr := fmt.Sprintf("127.0.0.1:%d", p)
		//返回值是一个结构体+一个错误,后面的conn.Close()是结构体.方法的类型
		conn, err := net.Dial("tcp", addr)
		if err != nil {
			results <- 0
			continue
		}
		//这里的close是关闭与端口的连接,而不是关闭一个通道
		conn.Close()
		results <- p
	}
}

年轻人的第一款自制扫描器

package main

import (
	"fmt"
	"net"
	"sort"
	"sync"
)

func main() {
	fmt.Print("请输入要扫描的IP地址: ")
	var ip string
	fmt.Scanln(&ip)
	fmt.Println("给你一点加载时间,你才知道是真的扫描而不是面向结果编程")
	var wg sync.WaitGroup
	ops := []int{}
	ports := make(chan int, 5000)
	for i := 1; i <= 5000; i++ {
		wg.Add(1)
		go func(j int) {
			defer wg.Done() //这句一定要放前面,确保被程序看到但是推迟了,如果放最后,return之后会直接看不见done
			addr := fmt.Sprintf("%s:%d", ip, j)
			conn, err := net.Dial("tcp", addr)
			if err != nil {
				return
			}
			ports <- j
			conn.Close()

		}(i)
	}
	fmt.Println("正在检测")
	wg.Wait()
	close(ports)
	for v := range ports {
		ops = append(ops, v)
	}
	sort.Ints(ops)
	for _, v := range ops {
		fmt.Printf("%d port is open\n", v)
	}
	fmt.Println("按下回车键以退出程序...")
	fmt.Scanln()
}
worker pool的实现
package main

import (
	"fmt"
	"net"
	"sort"
)

func worker(ports_chan, results_chan chan int) {
	for port := range ports_chan {
		//这里必须加一个addr来表示端口,下面的net.Dial()需要tcp和ip两个变量,直接 con, err := net.Dial("tcp",":%d",port)会报错
		addr := fmt.Sprintf(":%d", port)
		con, err := net.Dial("tcp", addr)
		if err != nil {
			results_chan <- 0
			continue
		}
		results_chan <- port
		con.Close()
	}
}

func main() {
	ports_chan := make(chan int, 5000)
	results_chan := make(chan int)
	open_ports := make([]int, 0, 30)
	for i := 1; i <= cap(ports_chan); i++ {
		go worker(ports_chan, results_chan)
	}
	go func() {
		for i := 1; i <= 5000; i++ {
			ports_chan <- i
		}
	}()

	/*
		for port := range results_chan {
			if port != 0 {
				open_ports = append(open_ports, port)
			}
		}
	*/

	for i := 1; i <= 5000; i++ {
		port := <-results_chan
		if port != 0 {
			open_ports = append(open_ports, port)
		}
	}
	close(ports_chan)
	close(results_chan)
	sort.Ints(open_ports)
	for _, p := range open_ports {
		fmt.Printf("%d port is open\n", p)
	}
}

tcp代理

package main

import (
	"io"
	"log"
	"net"
)

func main() {
	listener, err := net.Listen("tcp", ":20080")
	if err != nil {
		log.Fatalln("绑不了端口")
	}
	log.Println("监听端口20080")
	for {
		c, err := listener.Accept()
		log.Println("接收连接")
		if err != nil {
			log.Fatalln("连不上")
		}
		go echo(c)
	}
}
func echo(c net.Conn) {
	defer c.Close()
	b := make([]byte, 512)
	for {
		size, err := c.Read(b[0:])
		if err == io.EOF {
			log.Println("未知错误")
			break
		}
		log.Printf("接收到了%d byte的数据,数据是:%s\n", size, string(b))
		log.Println("写数据")
		if _, err := c.Write(b[0:size]); err != nil {
			log.Fatalln("写不了")
		}
	}
}

缓冲

端口转发器

package main

import (
	"io"
	"log"
	"net"
)

func main() {
	//本地端口80监听
	listener, err := net.Listen("tcp", ":80")
	if err != nil {
		log.Fatalln("unable to bind port")
	}
	for {
		conn, err := listener.Accept()
		if err != nil {
			log.Fatal("unable to accept connection")
		}
		go handle(conn)
	}
}
func handle(src net.Conn) {
	dst, err := net.Dial("tcp", "joescatcam.website:80")
	if err != nil {
		log.Fatalln("unable to connect our unreachable host")
	}
	defer dst.Close()
	go func() {
		//将源的输出复制到目标
		if _, err := io.Copy(src, dst); err != nil {
			log.Fatalln(err)
		}
	}()
	//将输出的目标复制回源
	if _, err := io.Copy(src, dst); err != nil {
		log.Fatalln(err)
	}
}

反向shell(不是反弹shell)

package main

import (
	"io"
	"log"
	"net"
	"os/exec"
)

func handle(c net.Conn) {
	//exec.Command()有两个参数和一个返回值,返回值是cmd结构体指针,第一个参数是要打开的终端的路径,这里是本机开放端口允许外部连接,所以是cmd.exe,
	//如果本机是linux,那就是"/bin/sh","-i",-i是以交互模式运行终端,所以第二个参数就是可执行的参数,比如-h,--version
	cmd := exec.Command("cmd.exe")
	//func Pipe() (*PipeReader, *PipeWriter)
	rp, wp := io.Pipe()
	//以下两个cmd操作不是在赋值,而是进行一个重定向操作
	cmd.Stdin = c
	cmd.Stdout = wp
	go io.Copy(c, rp)
	cmd.Run()
	c.Close()
}
func main() {
	listener, err := net.Listen("tcp", ":1234")
	if err != nil {
		log.Fatalln(err)
	}
	conn, err := listener.Accept()
	if err != nil {
		log.Fatalln(err)
	}
	go handle(conn)
}

http请求

package main
import(
	"fmt"
	"io"      //读取操作的包
	"net/http"
	"log"     //如有err直接退出
)
func main(){
    resp,err:=http.Get("https://blog.yblue.top")  //http.Get("")中存入url,返回值是*http.Response的结构体指针,resp下有很多种属性,比如状态(Status),会返回状态码和状态(200 OK),StatusCode会只返回状态码,Body是http报文中的响应主体
    //这里也可以直接进行get请求,就在url里ga
	if err!=nil{
		log.Fatalln(err)
	}
	defer resp.Body.Close()
	body,err:=io.ReadAll(resp.Body)     //这里反回的body是[]byte,是一个字节类型的切片
	if err!=nil{
		log.Fatalln(err)
	}
	fmt.Println(string(body))
	fmt.Println(resp.Status)
	if resp.StatusCode ==200{
		fmt.Println("OK")
	}
}

如果将请求放到内容里而不是直接在url路径里

package main
import(
	"fmt"
    "log"
    "net/http"
    "io"
	"net/url"
)
func main(){
    url_object:=url.Value{}            //创建了一个空的 url.Values 对象,可以用于存储 URL 的查询参数,这里是引用,url.Values{}是个指针
    Url,err:=url.Parse("https://baidu.com")
//func url.Parse(rawURL string) (*url.URL, error)  将baidu.com转化成一个url.URL类型的指针并返回,对这个对象可以直接进行获取信息,此时url_object对应的就是https://baidu.com
    if err!=nil{
        log.Fatalln(err)
	}
    url_object.Set("name","xiaoming")     //set是覆盖,add是添加
    url_object.Set("age","12")
    url_object.Set("name","taosu")
    url_object.Add("age","13")
    Url.RawQuery=url_object.Encode()      //这个函数是一个类似整合的函数,前面set或者add的键值对,在使用过这个函数后,会被编码进url中并用&连接起来,形成查询参数字符串
    urlPath:=Url.String()       //Url现在是指针,  .String()是将url以字符串的形式输出出来
    fmt.Println("请求的路径是:",urlPath)
    resp,err:=http.Get(urlPath)   //对url发起请求
    defer resp.Body.Close()
    if err!=nil{
        log.Fatalln(err)
    }
    body,err:=io.ReadAll(resp.Body)
    if err!=nil{
        log.Fatalln(err)
	}
    fmt.Println(string(body))    //这里必须用string()函数,否则会直接打印ascii码而不是字符串
}
url.Values{} 是 Go 语言中 net/url 包提供的一个类型,用于表示 URL 查询参数的集合。

url.Values 是一个类似于字典的结构,它可以存储一系列的键值对,其中键和值都是字符串类型。它提供了一组方法来设置、获取和操作查询参数。

以下是一些常用的 url.Values 方法:

Set(key, value):设置指定键的查询参数值。如果该键已存在,则会用新的值覆盖旧的值。如果该键不存在,则会将键值对添加到查询参数中。
Add(key, value):向查询参数中添加一个新的键值对,不管该键是否已存在。如果该键已存在,则会将新的值追加到已有值的末尾,用 & 符号分隔。
Get(key):获取指定键的查询参数值。如果该键不存在,则返回空字符串。
Del(key):删除指定键的查询参数。
Encode():将 url.Values 对象编码为查询参数字符串。

//实例
package main

import (
	"fmt"
	"io"
	"log"
	"net/http"
	"net/url"
)

func main() {
	url_object := url.Values{}
	Url, err := url.Parse("https://blog.yblue.top")
	if err != nil {
		log.Fatalln(err)
	}
	url_object.Set("username", "admin")
	Url.RawQuery = url_object.Encode()
	UrlPath := Url.String()
	resp, err := http.Get(UrlPath)
	if err != nil {
		log.Fatalln(err)
	}
	defer resp.Body.Close()
	Body, _ := io.ReadAll(resp.Body)
	fmt.Println(Body)
	fmt.Println("utl的路径是:", UrlPath, "\n", "查询语句是:", Url.RawQuery)
}

http请求且写入请求头内容

package main

import (
	"fmt"
	"io"
	"net/http"
)

func main() {
    client := http.Client{}  //Client是http包中定义的一个结构体,结构体可以通过结构体名字{}来创建一个对象
	req, _ := http.NewRequest("GET", "http://blog.yblue.top", nil)   //http.Get是创造请求并发送,而这里是先创建请求
	req.Header.Add("name", "Paul_Chan")
	req.Header.Add("age", "26")
	resp, _ := client.Do(req)    //这一步是发送请求
	body, _ := io.ReadAll(resp.Body)
	fmt.Println(string(body))
}

打印http请求头

package main

import (
	"fmt"
	"net/http"
	"net/http/httputil"
)

func main() {
	req, _ := http.NewRequest("GET", "http://blog.yblue.top", nil)
	req.Header.Add("name", "Paul_Chan")
	req.Header.Add("age", "26")
	req.Header.Add("x-forwarded-for", "127.0.0.1")
	// 打印所有的请求头
	requestDump, _ := httputil.DumpRequestOut(req, true)
	fmt.Println(string(requestDump))

	// 发起实际的请求
	client := http.DefaultClient
	_,_ = client.Do(req)
}

Category: go | Tags: go | Created: 2024-11-30 17:16:55