GO渗透测试笔记(一)

0X01 基本使用

  1. 当在某些极端的情况可以使用go build -ldflags "-w -s"进行编译,从二进制文件中剥离调试信息和字符表,从而减少文件的大小,适合在某些极端的情况进行嵌入(大约可以减少 30%)

  2. 创建使用不同架构上使用的二进制文件—交叉编译

  3. go doc 函数 –>可以查询有关的包,函数的使用方法文档

  4. go get —-> 很多情况下,go程序需要要引入第三方包,可以使用此命令进行导入

    即使你使用了 import 导入了相应的包,如stacktian/ldapauth,也无法访问。必须先下载实际的包,

    go get “github.com/stacktian/ldapauth” 下载实际的包,并且放在 $GOPATH/SRC目录下

  5. go 引入了两个工具dep和mod来锁定清理依赖项,有助于避免正在进行的依赖项不一致

    go mod tidy

    go mod download 是使用mod时经常使用的两个命令

  6. go fmt –>可以自动格式化源代码,强制使用正确的换行符,缩进和大括号来设置代码样式。—>大部分IDE包含的钩子会在保存文件时自动执行 go fmt,因此无需显式执行该命令

  7. golint 和 go vet命令

    go fmt 会更改代码的语法样式,但是golint会报告样式错误,例如缺少注释,不遵循约定的变量命名等,无用的类型等等。

    golint时独立工具,需要 go get -u “golang.org/x/lint/golint”进行下载

    go vet 也会尝试确定编译器中可能忽略的问题,其中一些可能是合法的错误

  8. GO Playground -> 托管的执行环境,为开发人员提供了稳定的web前端,以快速,测试,执行和共享Go代码段,但是会限制使用系统命令和第三方交互

go 的基本语法

1. 结构体和接口

定义如下方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main

import "fmt"

type Person struct {
Name string
Age int
}

func (p * Person) sayHello() {
fmt.Println("hello",p.Name)
}

func main() {
var guy = new(Person)
guy.Name = "Dave"
guy.Age = 12
guy.sayHello()
}

此语言定义Person变量的方法,sayHello(),其他语言将p视为selfthis的引用。

于此同时,结构体缺少作用域修饰符(如 public ,private 等等),

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
package main

import "fmt"

type Person struct {
Name string
Age int
}

func (p * Person) sayHello() {
fmt.Println("hello",p.Name)
}

type Friend interface {
sayHello()
}

func Greet(f Friend) {
f.sayHello()
}

func main() {
var guy = new(Person)
guy.Name = "Dave"
Greet(guy)
}

定义了名为friend的接口,该接口需要实现一个方法sayHello,这意味着任何实现了方法sayHello()类型的都是Friend

注意:Friend实际上并未实现这个函数,他只是说,如果你是Friend,则能够使用sayHello

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
package main

import "fmt"

type Person struct {
Name string
Age int
}
type Dog struct {
Name string
Age int
}

func (d *Dog) sayHello() {
fmt.Println("woof,woof")
}

func (p * Person) sayHello() {
fmt.Println("hello",p.Name)
}

type Friend interface {
sayHello()
}

func Greet(f Friend) {
f.sayHello()
}

func main() {
var guy = new(Person)
guy.Name = "Dave"
Greet(guy)
var dog = new (Dog)
Greet(dog)
}

2 控制结构

Go 包含的控制结构比其他现代语言要少的多。Go主要的语法条件是if/else

用户输入的时候用fmt.Scanln()比较合适

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import "fmt"

func main() {
var x int
fmt.Println("请输入x")
fmt.Scanln(&x)
if x==1 {
fmt.Println("x is equals 1")
}else {
fmt.Println("x is not equals 1")
}
}

当使用switch结构的时候,Go语言必须使用花括号,与其他现代编程语言不同的是,go不需要包含break语句。在其他语言中当 case不包含break的时候,将持续执行,在go中只会执行一种语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main

import "fmt"

func main() {
var x = "foo"
switch x {
case "foo":
fmt.Println("fonud foo");
case "bar":
fmt.Println("fonud bar");
default:
fmt.Println("default")
}
}

Go有一个 type switch的特殊变体,通过switch语句进行类型断言,对判断类型接口很有用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main

import "fmt"

func foo(i interface{}) {
switch i.(type) {
case int:
fmt.Println("int")
case string:
fmt.Println("string")
default:
fmt.Println("unknown error")
}
}

func main() {
foo(int(64))
}

本示例中使用特殊语法(i.type)检查 i 变量的类型,然后进行匹配

最后一个流程控制是for循环,for循环是 Go用于执行重复代码的结构,Go没有诸如do或者while之类的约定

1
2
3
4
5
6
7
8
9
10
package main

import "fmt"

func main() {
for i:=0;i<10;i++ {
fmt.Println(i)
}
}

以下是 for循环的一个轻量级变体,用于遍历集合或者切片/映射

和python 一样,idx用于索引

1
2
3
4
5
6
7
8
9
10
11
package main

import "fmt"

func main() {
nums :=[]int{2,4,6,8,10}
for idx,val := range nums{
fmt.Println(idx+1,val)
}
}

3. 并发

go拥有比其他语言更为简单的并发模型,可以使用gorountine并发执行代码,因其是可以同时运行的函数或者方法,gorountine被称为轻量级线程,于实际线程相比,创建他们的成本极低

可以通过在被调用的方法或者函数之前使用go关键字创建gorountine实现并发

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import (
"fmt"
"time"
)

func f() {
fmt.Println("f function")
}

func main() {
go f()
time.Sleep(1 * time.Second)
fmt.Println("main function")
}

在函数之前使用了go关键字,这将意味着程序同时运行f()和main(),换句话说:main()函数的执行将继续,而无需等待f()完成。当我们使用time.Sleep强制main函数暂停,以便f()可以执行完,如果不暂停main()函数,则该线程很可能在f()执行完毕之前就退出,将看不到其结果。

go 具有一种称为通道(channel)的数据类型,该数据类型提供了一个机制,通过该机制,gorountine可以同步执行函数并且这些函数可以互相通信。下面的例子显示使用一个通道时显示不用字符串的长度及其总和的示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import "fmt"

func strlen(s string, c chan int) {
c <- len(s)
}
func main() {
c := make(chan int)
go strlen("Salutations",c)
go strlen("wolrd",c)
x,y := <-c,<-c
fmt.Println(x,y,x+y)
}

首先,定义并使用 chan int类型的变量 c,你可以定义各种类型的通道,具体取决于通道传递的数据类型,你可以在gorountine中定义各种类型的通道,具体取决于要在通道传递的数据类型,上面的类型是要传递int类型的数据传长度。因此使用int类型的传递参数

请注意一个新的运算符,<-。该运算符只是数据时流入还是流出通道,你可以这样理解他,”将物品放在桶里或者取出一个物品”

结果是5,6,11,可以理解为是一个栈,将最先放进去放到最底部,取出最后放入的,所以才会出现这种情况。

同时,从通道中取出数据则表明要取出足够的数据,否则会阻塞在这一行

4. 错误处理

与大多数现代编程语言不同,Go 没有try/catch/finally错误的处理语法。但是,它使用了一种更为简单的方法,鼓励你多去检查哪些容易出现错误的地方,这样他们就不会聚焦在调用链中。

Go使用以下接口声明定义内置错误的类型

1
2
3
type error interface {
Error() string
}

这意味着可以实现了方法Error()的任何数据类型,该方法返回字符串作为一个错误,例如,可以在代码中自定义错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import "errors"

type myError string

func (e myError)Error() string {
return string(e)
}

func foo() error {
return errors.New("some Error Occurred")
}
func main() {
if err := foo(); err !=nil{
//处理错误的回显
}
}

你会发现函数和方法至少返回一个值是很普遍的,并且这些值中总会又一个返回值是错误的,在Go中如果返回错误值是nil,则表明函数未出现任何问题。

go 语言中没有形成统一的错误记录方法,原因之一是,与其他语言不同,Go的内置错误类型没有隐式包含堆栈跟踪以帮助查明错误的上下文或者位置,

5. 处理结构化数据

安全从业人员会预先编好处理结构挂数据或者具有通用编码的数据,如JSON,xml

最常见的两个包是encoding/xmlencoding.json,这两个包都可以组编和结组任意数据结构,这意味着他们可以将字符串转化为结构体,也可以将结构体序列化为字节切片,然后将字节切片反序列化为结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main

import (
"encoding/json"
"fmt"
)

type Foo02 struct {
Bar string
Baz string
}

func main() {
f := Foo02{"h0ld1rs","hello world"}
b,_ := json.Marshal(f)
fmt.Println(string(b))
err := json.Unmarshal(b, &f)
if err != nil {
return
}

}

json.Marshal 将struct 编码为JSON,返回一个字节切片,随后将其打印到标准输出,显示的是Json形式的字节码形式

json.Unmarshal(b,&f) 将其进行解码,生成一个foo结构体实例。处理xml与此过程大致相同

https://www.jianshu.com/p/da486be83e8a