前言 第一次用go是大二的时候,那个时候招新用到github上的项目,但是万万没想到的是那个项目有个bug,redis里的session没有清除机制,一段时间后就会爆满导致网站服务崩溃,一开始测试的时候没问题,但是上线那一天一上强度就503了。那段时间压力爆炸,改时间已经来不及了已经有部分题目被学弟写了,他那个框架我也没学过修bug也来不及了。也是疯狂查资料,最后用的一个定时清除reids连接的go脚本,打包编译上传到docker里,才解决这个问题,这时我对go的印象是作为脚本可以一点不依靠环境配置,docker里面什么语言环境都没有,但是我用go生成的二进制文件直接就执行成功了
最近想写资产测绘工具当作毕设,并且平时挖src也能用上,就简单分析了一个go写的开源渗透工具,看完这个工具后也是感觉到了go语言的便利,有类似python的弱类型,有类似java的类和包结构,甚至还有类似C的指针和结构体,也正因如此就决定毕设用go写而不是用更熟悉的python写
go语言的基础命令 1 2 3 4 5 go run hello.go go build hello.go ./hello go env -w GOPROXY="https://goproxy.cn,direct" go install example.com/mytool@latest
Go语言环境配置 GOROOT:指向 Go 安装目录,通常是 Go 自带的安装路径,包含了 Go 的标准库、工具链等
GOPATH:指向你的工作空间目录 src:源代码存放目录,包括你自己的项目代码和go install导入的外部包。 pkg:编译后的包文件存放目录,包括使用 go install 命令生成的二进制文件。 bin:可执行文件存放目录,包括使用 go install 命令生成的可执行文件。
GOMODCACHE:但是GOPATH规定编程只能在$GOPATH/src下很是麻烦,所以从 Go 1.11 开始,Go 模块系统简化了依赖管理,并使得 GOPATH 不再是必需的,允许你在任何目录下开发 Go 项目 依赖管理通过go.mod(记录模块的依赖关系、版本等信息)和go.sum(保证了这些依赖的完整性和安全性)文件来完成(类似于python的pip install -r requirements.txt),下载的包在$GOPATH/pkg/mod
1 2 3 4 5 6 7 8 9 10 11 12 13 14 export GO111MODULE=on go mod init <module-name> go get <package>@<version> go mod tidy go mod verify
go v1.5引入vendor模式,如果项目目录下有vendor目录,那么go工具链会优先使用vendor内的包进行编译、测试
go基础语法 变量类型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 var a int = 10 a := 10 var a = 10 a:=interface {} m1 := map [string ]string { "a" : "test1" , "b" : "test2" , } m1 ["c" ] = "test3" var a [10 ] string var b = [...] int {1 ,2 ,3 ,4 ,5 } type conf struct { name string age int } a := conf{name: "d4m1ts" , age: 18 } conf := new (conf) conf := &Conf{} conf.name = "d4m1ts"
指针的使用 1 2 3 4 5 6 7 8 9 10 11 var ip *int ip = &a func appendOne (s *[]int ) { *s = append (*s, 1 ) } func main () { s2 := []int {0 , 0 } appendOne(&s2) }
关于切片 1 2 3 4 5 6 7 8 var c []string var numbers = make ([]int ,3 ,5 ) c := [] string {"aa" , "bb" , "cc" } c = append (c, "ee" ,"ff" ,"gg" ) d1 := [10 ]int {1 ,3 ,5 ,7 ,9 } x := d1[2 :] c = append (c, x...)
本来想着学完go语言之后刷刷ctf题目,结果在一道2019年题目中发现有一个有意思的地方
1 2 3 4 5 6 7 8 9 10 11 12 13 14 a := make ([]int , 0 ) a = append (a, 1 ) a = append (a, 2 ) a = append (a, 3 ) a = append (a, 4 ) a = append (a, 5 ) b := append (a, 6 ) c := append (a, 7 ) fmt.Println(a) fmt.Println(b) fmt.Println(c)
为什么b最后一位是7拿,因为切片存储空间cap是2的倍数,如果是存储空间cap从5->9,它就会深拷贝到一个cap16的地方,在本例子中,是从cap5->6,那么b就是a的引用然后len为6,后面c跟b的情况一样都是更改了a的后一位,导致b也被改变了
很神奇,它这个地方居然是深拷贝和浅拷贝的混用,我第一眼看到那道题的时候也懵了
逻辑语法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 func main () { a := [] int {1 ,2 ,3 ,4 ,5 } for _,i := range a { fmt.Println(i) } } for sum <= 10 { sum += sum } func text () { for i := 0 ; i < 5 ; i++ { fmt.Printf("%d " , i) defer fmt.Printf("%d " , i) } fmt.Printf("%d " , 11 ) } package testfunc TestFunc1 (a int , b int ) int { return a+b } s= test.TestFunc1(1 ,2 ) type testImpl interface { testFunc1(a int , b int ) } type testStruct struct {} func (testMethod testStruct) TestFunc1 (a int , b int ) int { return a+b } var test1 testImpl test1 := new (testStruct) res1 := test1.TestFunc1(10 ,20 ) fmt.Println(res1)
先写个大概,开发时遇到不会写的再往上加
go自带的常用包 并发与管道 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 import ( "fmt" "sync" ) ch1 := make (chan string ) ch2 := make (chan string ) select { case msg1 := <-ch1: fmt.Println(msg1) case msg2 := <-ch2: fmt.Println(msg2) default : fmt.Println("no message received" ) } func loop (res chan int ) { i := 1 for i < 5 { i += 1 res <- i } } res := make (chan int , 5 ) go loop(res) fmt.Println(<-res) fmt.Println(<-res) for r := range res{ fmt.Println(r) } fmt.Println(<-res) var wg sync.WaitGroupfunc worker (id int ) { defer wg.Done() fmt.Printf("Worker %d is working\n" , id) } for i := 1 ; i <= 5 ; i++ { wg.Add(1 ) go worker(i) } wg.Wait() fmt.Println("All workers are done!" ) var mu sync.Mutexvar count int func increment () { mu.Lock() count++ mu.Unlock() } var wg sync.WaitGroup for i := 0 ; i < 10 ; i++ { wg.Add(1 ) go func () { defer wg.Done() increment() }() } wg.Wait() fmt.Println("Final count:" , count)
context context 可以在多个 goroutine 之间共享取消信号或超时信号,很多 Go 的标准库和第三方库(如 net/http、database/sql 等)都与 context 紧密集成。例如,HTTP 请求的超时、数据库查询的取消操作等都依赖于 context
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 import ( "context" "fmt" "time" ) func main () { ctx, cancel := context.WithTimeout(context.Background(), 1 *time.Second) defer cancel() go handle(ctx, 500 *time.Millisecond) select { case <-ctx.Done(): fmt.Println("main" , ctx.Err()) deadline, _ := ctx.Deadline(); } } func handle (ctx context.Context, duration time.Duration) { select { case <-ctx.Done(): fmt.Println("handle" , ctx.Err()) case <-time.After(duration): fmt.Println("process request with" , duration) } }
输入输出 1 2 3 4 5 6 7 8 9 10 11 12 import "fmt" fmt.Scan(&name) fmt.Print("Enter your age: " ) fmt.Scanln(&age) fmt.Println("Hello, World!" ) fmt.Printf("Hello, %v!\n" , name) var sentence = fmt.Sprintf("hello %s %s %d" , keyword, name, age)
基本数据类型处理 strconv类型转化 Go 的类型转换是显式的,你必须告诉编译器要如何转换类型,以避免类型不兼容问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 import ( "fmt" "strconv" ) func main () { str := strconv.Itoa(n) str := strconv.FormatInt(255 , 10 ) fmt.Println(str) num, err := strconv.Atoi(str) var i int = 42 var f float64 = float64 (i) var x float64 = 42.58 var y int = int (x) str := "true" b, err := strconv.ParseBool(str) var flag bool = true s := strconv.FormatBool(flag) }
strings处理字符串 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import "strings" import "fmt" func main () { fmt.Println(strings.Contains("hello world" , "world" )) fmt.Println(strings.Contains("hello world" , "golang" )) fmt.Println(strings.ToLower("HELLO WORLD" )) fmt.Println(strings.ToUpper("hello world" )) result := strings.Split("a,b,c,d" , "," ) fmt.Println(result) result := strings.Join([]string {"a" , "b" , "c" , "d" }, "," ) fmt.Println(result) fmt.Println(strings.Index("hello world" , "world" )) fmt.Println(strings.Index("hello world" , "golang" )) fmt.Println(strings.HasPrefix("hello world" , "hello" )) fmt.Println(strings.HasSuffix("hello world" , "world" )) }
文件操作 1 2 3 4 5 6 7 import ( "io" "io/ioutil" "path/filepath" "os" "bufio" )
获得当前路径 1 2 3 cwd, _ := os.Getwd() exePath:=os.Args[0 ] exePath, err := os.Executable()
路径处理 1 2 3 4 5 6 path := filepath.Join("api" , "mayylu" , "file.txt" ) cleanPath := filepath.Clean(path) dir, file := filepath.Split(path) dir:=filepath.Dir(path) absPath, err := filepath.Abs("file.txt" ) ext := filepath.Ext(path)
展示路径下的文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 f, err := os.Open("./directory" ) if err != nil { fmt.Println("打开目录时出错:" , err) return } defer f.Close() fi, _ := f.Stat() if fi.IsDir() { l, _ := f.Readdir(0 ) } for _, file := range l { fmt.Println(file.Name()) } files, _ := fileutil.ListFileNames("./directory" )
获取文件的信息 1 2 3 4 5 6 7 _, err := os.Stat(filepath) if os.IsNotExist(err) { err := os.Mkdir("new_directory" , 0755 ) } if fileutil.MiMeType(filepath) == "application/zip" {}
读文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 file,_ := os.Open("test.txt" ) defer file.Close()byteRes, _ := ioutil.ReadAll(file) fmt.Println(string (byteRes)) data, _ := ioutil.ReadFile("test.txt" ) fmt.Println(string (data)) datas := []string {} scanner := bufio.NewScanner(file) for scanner.Scan(){ datas = append (datas, scanner.Text()) } import ( _ "embed" "fmt" ) var content string func main () { fmt.Println(content) }
写文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 fileutil.CreateDir(userDir) gobFile, _ := os.Create(userDir + "user.gob" ) file,_ := os.OpenFile("test.txt" , os.O_RDWR | os.O_APPEND | os.O_CREATE, 0666 ) defer file.Close()data := []byte ("hello d4m1ts" ) count, _ := file.Write(data) count, _ = file.WriteString("\nHello D4m1ts" ) file.Sync() data := []byte ("hello d4m1ts" ) ioutil.WriteFile("test.txt" , data, 0666 )
系统信息 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import ( "os/exec" "runtime" "time" "math/rand" ) s:=os.Args[1 :] sysType := runtime.GOOS i:=time.Now().Unix() duration := time.Duration(5 ) * time.Minute duration := 1 *time.Second duration := 500 *time.Millisecond select { case ... ..... case <-time.After(duration): fmt.Println("process request with" , duration) }
命令执行 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import ( "os/exec" "runtime" ) cmd := exec.Command("ls" , "-l" ) output, err :=cmd.Output() output, err := cmd.CombinedOutput() if err != nil { fmt.Println("runcmd err : " + fmt.Sprint(err) + ":" + string (cmdOutput)) } var outb, errs bytes.Buffercmd.Stdout = &outb cmd.Stderr = &errs err := cmd.Run() os.Exit(0 ) os.Exit(1 )
网络请求 域名/ip处理 1 2 3 4 5 6 7 8 9 10 import ( "io/ioutil" "net" ) host := "127.0.0.1" port := "8080" address := net.JoinHostPort(host, port) fmt.Println(address) host, port, err := net.SplitHostPort(address)
HTTP请求 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 import ( "io/ioutil" "net/http" "net/url" ) http.Post(u, "application/x-www-form-urlencoded" , strings.NewReader("aa=bb" )) resp, err := http.Post(url, "application/json" , bytes.NewBuffer(jsonData)) baseURL := "https://www.baidu.com/" Url, _ := baseURL.Parse(u) param.Set("name" , "d4m1ts" ) Url.RawQuery = param.Encode() response,_ := http.Get(Url.String()) defer response.Body.Close() u := "http://httpbin.org/" req, _ := http.NewRequest("GET" , u, nil ) req.Header.Set("User-Agent" , "Test GO" ) transport := &http.Transport{ Proxy: http.ProxyFromEnvironment, DialContext: (&net.Dialer{ Timeout: 60 * time.Second, KeepAlive: 60 * time.Second, }).DialContext, MaxIdleConnsPerHost: 100 , MaxIdleConns: 100 , TLSClientConfig: &tls.Config{ InsecureSkipVerify: true , }, DisableKeepAlives: true , } client := http.Client{ Timeout: 3 *time.Second, Transport: transport, CheckRedirect: func (req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse }, } response, _ := client.Do(req) fmt.Println(response.StatusCode) fmt.Println(response.Header.Get("Server" )) body, _ := ioutil.ReadAll(response.Body) fmt.Println(string (body))
正则 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import ( "fmt" "regexp" ) regex, _ := regexp.Compile("(\\d{1,3})\\d{1,3}" ) fmt.Println(regex.MatchString("123123123123" )) fmt.Println(regex.FindString("123213123123" )) fmt.Println(regex.FindStringIndex("123213123123" )) fmt.Println(regex.FindStringSubmatch("123213123123" )) fmt.Println(regex.FindAllString("123213123123" ,-1 )) fmt.Println(regex.FindAllStringSubmatch("123213123123" ,-1 )) fmt.Println(regex.FindAll([]byte ("123123123123" ), -1 )) fmt.Println(regex.ReplaceAllString("123123123213" ,"a" ))
配置文件解析 yaml 1 2 3 4 5 6 7 8 9 10 cache: enable : false list : - aaa - bbb mysql: user : root password : haha port : 3306 type: Fingerprint
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 import ( "fmt" "gopkg.in/yaml.v3" "io/ioutil" ) func main () { type Yaml struct { Cache struct { Enable bool `yaml:"enable"` List []string `yaml:"list,flow"` } Mysql struct { User string `yaml:"user"` Password string `yaml:"password"` Port int `yaml:"port"` } Type string `yaml:"type,omitempty"` } result := new (Yaml) yamlFile, _ := ioutil.ReadFile("test.yml" ) _ = yaml.Unmarshal(yamlFile, result) result.Cache.Enable:=True yamlFile, _ := os.OpenFile("test.yml" , os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777 ) defer yamlFile.Close() enc := yaml.NewEncoder(yamlFile) err := enc.Encode(result) } import ( "github.com/spf13/viper" ) vp := viper.New() vp.SetConfigName("config" ) vp.AddConfigPath("/var/" ) vp.SetConfigType("yaml" ) err := vp.ReadInConfig() if err != nil { return nil , err } if err := vp.UnmarshalKey("database" , &config.Database); err != nil { fmt.Println("Error unmarshalling key:" , err) return } }
json快速解析 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 import ( "encoding/json" "fmt" "github.com/json-iterator/go" "github.com/tidwall/gjson" "github.com/tidwall/sjson" ) type User struct { Name string Age int } func main () { user := User{ Name: "d4m1ts" , Age: 18 , } s1, _ := json.Marshal(user) fmt.Println(string (s1)) user2 := User{} json.Unmarshal(s1, &user2) fmt.Println(user2) } json := `{"name": "John", "age": 30, "address": {"city": {"country": "china", "name": "henu"}, "zip": "10001"}}` address := gjson.Get(json, "address" ) enList:= address.Get("city" ).Array() json, _ = sjson.Set(json, "name" , "Jane" ) json, _ = sjson.Set(json, "address.city" , "New York" ) json, _ = sjson.Delete(json, "address.city" ) name := jsoniter.Get([]byte (json), "name" ) fmt.Println("Name:" , name.ToString())
go web web系统编译设计原理都是相通的,翻来覆去就那些东西
路由和传参 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 import ( "github.com/fvbock/endless" "github.com/gin-gonic/gin" "github.com/astaxie/beego/validation" ) type auth struct { Username string `valid:"Required; MaxSize(50)"` Password string `valid:"Required; MaxSize(50)"` } func GetAuth (c *gin.Context) { url := c.Request.URL.String() username := c.Query("username" ) username:=c.DefaultQuery("username" , "admin" ) password := c.DefaultPostForm("password" , "0" ) if err := c.ShouldBindJSON(&person); err != nil {} session := sessions.Default(c) session.Set("shallow" , userDir) shallow:=session.Get("shallow" ) session.Save() file, err := c.FormFile("file" ) ext := file.Filename[strings.LastIndex(file.Filename, "." ):] filename := userUploadDir + file.Filename err = c.SaveUploadedFile(file, filename) valid := validation.Validation{} a := auth{Username: username, Password: password} ok, _ := valid.Valid(&a) data := make (map [string ]interface {}) code := 400 if ok { } else { c.Redirect(http.StatusFound, "/" ) } c.JSON(http.StatusOK, gin.H{ "code" : code, "data" : data, }) } r := gin.New() r.Use(gin.Logger()) r.Use(gin.Recovery()) r.Use(Cors()) gin.SetMode(global.ServerSetting.RunMode) r.GET("/api/v1/auth" , GetAuth) apiv1 := r.Group("/api/v1" ) apiv1.GET("/downtasklog/:id" , v1.DownTaskLog) apiv1.Use(Midderware) endless.DefaultReadTimeOut = 60 endless.DefaultWriteTimeOut = 60 endless.DefaultMaxHeaderBytes = 1 << 20 server := endless.NewServer(8081 , r) err := server.ListenAndServe() func Midderware () (c *gin.Context) { token := c.GetHeader("Authorization" ) if token == '' { c.Abort() return } c.Next() }
鉴权 jwt鉴权 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 import ( "github.com/dgrijalva/jwt-go" "time" ) var jwtSecret = []byte ("213123dd1" )type Claims struct { Username string `json:"username"` Password string `json:"password"` jwt.StandardClaims } func GenerateToken (username, password string ) (string , error) { nowTime := time.Now() expireTime := nowTime.Add(3 * time.Hour) claims := Claims{ username, password, jwt.StandardClaims{ ExpiresAt: expireTime.Unix(), Issuer: "linglong" , }, } tokenClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) token, err := tokenClaims.SignedString(jwtSecret) return token, err } func ParseToken (token string ) (*Claims, error) { tokenClaims, err := jwt.ParseWithClaims(token, &Claims{}, func (token *jwt.Token) (interface {}, error) { return jwtSecret, nil }) if tokenClaims != nil { if claims, ok := tokenClaims.Claims.(*Claims); ok && tokenClaims.Valid { return claims, nil } } return nil , err } func JWT () gin .HandlerFunc { return func (c *gin.Context) { token := c.GetHeader("Authorization" ) if token == "" { code = e.INVALID_PARAMS } else { claims, err := utils.ParseToken(token) if claims == nil { c.JSON(http.StatusUnauthorized, gin.H{ "code" : 401 , "data" : data, }) c.Abort() return } if err != nil { code = e.ERROR } else if time.Now().Unix() > claims.ExpiresAt { code =e.ERROR } } c.Next() } }
数据库操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 import ( "fmt" "github.com/jinzhu/gorm" _ "github.com/jinzhu/gorm/dialects/mysql" ) var db *gorm.DBdb, err = gorm.Open("mysql" , "user:password@tcp(localhost:3306)/dbname?charset=utf8&parseTime=True&loc=Local" ) gorm.DefaultTableNameHandler = func (db *gorm.DB, defaultTableName string ) string { return global.DatabaseSetting.TablePrefix + defaultTableName } db.SingularTable(true ) db.LogMode(true ) student := Student{Name: "1" , Age: 18 , Job: "1" } tx := db.Create(&student) studs := []*Student{ &Student{Name: "aa" , Age: 1 , Job: "aa" }, &Student{Name: "bb" , Age: 2 , Job: "bb" }, } tx := db.Create(&studs) tx.Error tx.RowsAffected db.Create(&mainurllist) db.Model(&User{}).Create(map [string ]interface {}{ "Name" : "jinzhu" , "Age" : 18 , }) db.Delete(&user) db.Where("id = " + strconv.Itoa(id)).Delete(&Finger{}) db.Model(&user).Select("Name" , "Age" ).Updates(User{Name: "new_name" , Age: 0 }) db.First(&user, "id = ?" , "1" ) db.Where(maps).Order("created_time" ).Find(&portbruteres) db.Exec("DELETE FROM users WHERE id = ?" , 1 )
其他不重要的包 在终端输出表格
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import ( "github.com/olekukonko/tablewriter" "os" ) table := tablewriter.NewWriter(os.Stdout) table.SetHeader([]string {"Name" , "Age" , "Country" }) table.Append([]string {"John" , "30" , "USA" }) table.Append([]string {"Alice" , "25" , "Canada" }) table.Append([]string {"Bob" , "35" , "UK" }) table.Render() data := [][]string { {"Alice" , "30" , "USA" }, {"Bob" , "25" , "UK" }, {"Charlie" , "35" , "Canada" }, } table := tablewriter.NewWriter(os.Stdout) table.SetAlignment(tablewriter.ALIGN_CENTER) table.AppendBulk(data) table.Render()