相关推荐recommended
Go黑帽子(第一章)
作者:mmseoamin日期:2024-02-20

Go黑帽子渗透编程之道

文章目录

    • Go黑帽子渗透编程之道
      • 1.0 第一章 GO语言基础
        • 1.1 go build 命令
        • 1.2 交叉编译
        • 1.3 golint和go vet命令
        • 1.4 指针、接口和结构体
        • 1.5 断言
        • 1.6 错误处理
        • 1.7 处理结构化数据

          1.0 第一章 GO语言基础

          这一章我就不详细描述了我就记录一些我觉得比较有用和比较重要的东西,每一步我都会给出详细的解释,因为这不但可以加深我个人的理解而且可以给读者一个较好的阅读体验。

          注意:不懂就找ChatGPT比什么博客解答的都详细

          1.1 go build 命令
          go build [文件名或包路径]
          常用选项:
          -o:指定输出文件的名称。
          -v:输出详细的构建信息。
          -a:强制重新构建所有的包,而不使用缓存的对象。
          -x:打印每个命令及其参数。
          -work:打印临时工作目录的路径。
          -ldflags:传递链接器标志。
          示例用法:
          基本用法:go build main.go(编译当前目录下的 main.go 文件)。
          指定输出文件名:go build -o myprogram main.go(将编译结果保存为 myprogram)。
          输出详细信息:go build -v main.go(显示详细的构建信息)。
          强制重新构建:go build -a main.go(强制重新构建所有包)。
          打印每个命令:go build -x main.go(打印每个构建命令及其参数)。
          打印临时工作目录:go build -work main.go(打印临时工作目录的路径)。
          传递链接器标志:go build -ldflags="-s -w" main.go(传递链接器标志,这里是去除符号和禁用 DWARF 调试信息)。
          其他注意事项:
          如果在目录中执行 go build,它将尝试编译包含 main 函数的文件,生成可执行文件。如果是一个库(没有 main 函数的文件),它将构建库而不是可执行文件。
          在构建过程中,Go 会自动下载并安装缺失的依赖项。
          默认情况下,go build 不会生成输出文件,而是将结果保存在临时目录中。如果需要自定义输出文件名,可以使用 -o 选项。
          

          传递链接器标志:go build -ldflags="-s -w" main.go(传递链接器标志,这里是去除符号和禁用 DWARF 调试信息)。

          这条指令单独提出来的目的是,这条指令可以剥离一些不必要的信息从而减少二进制文件的大小,二进制文件越小,越能使你在一些极端情况下有效地进行传输和镶嵌。//我个人觉得这个后面或者以后的测试中肯定用的上,先埋个伏笔吧,等具体用上我再来补充这部分内容。

          组合命令自己去尝试有助于加深理解。


          1.2 交叉编译

          创建可以在不同架构上可以运行的二进制文件

          前提:设置一个约束限制,目的是为了将有关要为其编译代码的操作系统和架构信息传递给build命令的一种方法。

          三种方式:命令行,代码注释或文件扩展名命名约定。

          命令行:

          windows下的指令
          $env:GOOS="linux"; $env:GOARCH="amd64"; go build hello.go
          $env:GOOS="windows":这一部分使用 PowerShell 的 $env: 语法设置 GOOS 环境变量为 "windows",指示 Go 编译器生成 Windows 平台可执行文件。
          $env:GOARCH="amd64":同样使用 PowerShell 的 $env: 语法设置 GOARCH 环境变量为 "amd64",指示 Go 编译器生成 64 位的可执行文件
          

          这里留下了一个疑问:

          当尝试交叉编译使用本机C环境绑定的应用程序时,会遇到真的“陷阱”,如果有大佬知道可以打在评论区供大家学习使用。


          1.3 golint和go vet命令

          golint:

          golint 是 Go 语言中一个用于检查代码风格的工具,它会检查你的 Go 代码是否符合一些常见的编码规范。以下是 golint 的基本使用方法

          安装:

          go get golang.org/x/lint/golint
          go install golang.org/x/lint/golint
          

          运行 golint:

          在你的 Go 项目目录下,执行以下命令:

          golint ./...
          

          这个命令将会在当前目录下递归检查所有 Go 源代码文件,并输出不符合规范的代码行。

          如果只想检查特定的文件或目录,可以指定相应的路径:

          golint path/to/your/package
          

          go vet:

          go vet 命令用于检查 Go 代码中的常见错误和潜在问题。它能够检查代码中的诸多问题,包括但不限于未使用的变量、导入路径错误、printf 格式字符串错误等。以下是一些关于 go vet 命令的基本使用方法:

          基本语法:

          go vet [包名]
          

          如果没有指定包名,则 go vet 将会检查当前目录及其子目录中的所有 Go 源文件。

          示例:

          go vet
          

          或者,如果你想检查特定的包,可以使用:

          go vet your/package/path
          

          检查所有标准库:

          go vet std
          

          这个命令将检查所有标准库的 Go 源代码。

          忽略特定的检查: 你可以使用 -vet 标志来指定要忽略的一些检查。例如,要忽略检查未导出的名字,你可以运行:

          bashCopy code
          go vet -vettool=$(which vet) -vetflags=-nointerfaceexport
          

          这个命令使用 -vettool 指定 vet 工具,-vetflags 用于传递额外的参数。

          在构建时运行: 你也可以在 go build 或 go install 时一并运行 go vet:

          go build -vet all
          

          go install -vet all
          

          go vet 通常用于静态代码分析,帮助你发现可能的代码问题。注意,它并不是完美的,有些问题可能仍然需要通过其他手段检查。


          1.4 指针、接口和结构体

          指针:

          var count = int(43)
          	ptr := &count
          	fmt.Println(ptr)
          	fmt.Println(*ptr)
          	//*ptr *书上说是取消该地址的引用,我的理解就是,修改所指向地址的值用*
          	*ptr = 100
          	fmt.Println(count)
             //ptr存了count的地址,然后*ptr就是修改count地址的存储的值
          

          接口和结构体

          package main
          import "fmt"
          type Person struct {
          	Name string
          	Age  int
          }
          type Dog struct {
          }
          func (p *Person) SayHello() {
          	fmt.Println("hello", p.Name)
          }
          func (D *Dog) SayHello() {
          	fmt.Println("woof")
          }
          type Friend interface {
          	SayHello()
          }
          func Greet(f Friend) {
          	f.SayHello()
          }
          func main() {
          	var guy = new(Person)
          	guy.Name = "laowang"
          	guy.SayHello()
          	//因为guy实现了Friend里面的所有方法,所以是Friend接口类
          	Greet(guy)
          	var dog = new(Dog)
          	//传递给同一个函数Greet()多种类型
          	Greet(dog)
          }
          

          1.5 断言
          package main
          import "fmt"
          func Foo(i interface{}) {
          	switch v := i.(type) {
          	case int:
          		fmt.Println(v, "is int")
          	case string:
          		fmt.Println(v, "is string")
          	default:
          		fmt.Println("UnKnow")
          	}
          }
          func main() {
          	Foo(42)
          	Foo("laowang")
          }
          

          1.6 错误处理

          Go的内置错误错误类型没有隐式包含堆栈跟踪以帮助查明错误的上下文或位置。

          package main
          import (
          	"errors"
          	"fmt"
          )
          type error interface {
          	Error() string
          }
          type MyError string
          func (e MyError) Error() string {
          	return string(e)
          }
          func foo() error {
          	return errors.New("Some Error Occurred")
              //你把上面的返回值修改之后你会发现,errors.New()方法的的error里面的Error()方法的返回值必须是error
          }
          func main() {
          	if err := foo(); err != nil {
          		//处理错误
          		fmt.Println(err)
          	}
          }
          

          解释:

          //个人理解:这里应该是重写了error错误处理接口,自定义了一个错误处理接口。
          //在Go语言中,fmt.Println 函数在输出时会调用 String() 方法,如果类型实现了 String() string 方法,那么在打印该类型的值时就会调用这个方法。由于 error 接口包含了 Error() string 方法,而在 Go 语言中 error 接口经常用于表示错误,因此当你使用 fmt.Println 打印一个实现了 error 接口的值时,实际上会调用该值的 Error() 方法。
          //代码中,err 是一个实现了 error 接口的值,它是 foo() 函数返回的错误。当你使用 fmt.Println(err) 时,fmt.Println 会调用 Error() 方法来获取错误的字符串表示形式,然后将其输出到标准输出。这就是为什么你可以直接通过 fmt.Println(err) 打印出错误信息的原因。
          

          1.7 处理结构化数据

          json

          package main
          import (
          	"encoding/json"
          	"fmt"
          )
          type Foo struct {
          	Bar string
          	Baz string
          }
          func main() {
          	f := Foo{"Hi", "Hello"}
          	b, _ := json.Marshal(f)
          	//这个b打印出来就是ASCII码
          	fmt.Println(b)
          	fmt.Println(string(b))
          	//使用 json.Unmarshal
          	//函数将 JSON 格式的字节切片 b 解码为 f 对象。
          	//json.Unmarshal 接受两个参数,第一个是 JSON 数据的字节切片,第二个是目标对象的指针。
          	//在这里,&f 表示将解码后的数据存储到 f 对象中。
          	json.Unmarshal(b, &f)
          }
          

          这里序列化和反序列化就不详细介绍了,这里一般会和ORM框架一起使用,ORM 框架可以根据标签来自动生成数据库表或将数据库表映射到Go语言中的结构体。

          总体来说,标签是一种元编程的手段,它允许程序员为代码添加元信息,以便在运行时或通过工具进行更灵活的操作。