作为撸站工,人人都得学点Go
PS:论坛照搬的第一篇文章,原计划是写个系列
25个关键字快速入门Go
本文适用于有一定其他语言编程基础的读者阅读
快速入门
在你对编程语言方面的概念如函数、对象、变量、常量等有了一些基本概念了解之后,快速学习一门新的语言的语法是相对比较简单的,在此之外需要关注的地方包括语言的特性、语言的工具链(包管理、编译工具链等),动手实践起来既能较好的掌握这门语言。作为Gopher以及一名撸站工,觉得有必要向大家推广这门语言,让其在安全领域(当然目前已经应用广泛了)拥有更多开发者。
Go的关键字可以在 https://golang.org/ref/spec#Keywords 找到,主要有
break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var
以及一些和其他语言一样常见的操作符,对于操作符本篇只记录较为常用的一些,我们对如上关键字像其他编程语言的教程一样,按使用范围对其进行一个分类:
包的引用
每个编程语言都有类似 include 或者 import 的关键字,用于导入需要引用的包或者库。Go语言也使用了import
关键字来进行包的导入,在使用过程中需要注意的地方就是如果遇到重名的包可以通过 alias_name import package
的方式来避免冲突,每个包都通过package
关键字来标识,这有点类似Java,当导入一个包时,如果包中实现了init()
函数则会被执行,你可以类比其为Java的构造函数,此时如果使用 _ import package
来进行包的导入,则只会执行init()
函数,而包中的其他导出函数则不会被引用。
变量与常量
在Go中是支持类型推倒的,当你需要定义一个变量时候你可以使用如下语句:
var_name := "pentest"
或者使用var
关键字来定义其类型,当有多个变量时可以这样写:
var var_name type
var
(
var_name_1 string
var_name_2 int
)
谈到变量的类型,Go语言与其他语言的关键字相差不多,需要知道的是有几个比较独特的内建类型,如byte
以及rune
,byte
实际上是uint8
的别名,也就是一个无符号整形,rune
实际上是int32
的别名,在Go中通常表示一个Unicode字符。
常量的关键字和其他语言一样使用const
,常见的写法为const var_name type = value
,这里的变量类型type
可以省略。
数据结构
Go中常见的数据结构有数据(Array)、切片(Slice)、键值对(Map)、信道(Channel)等等,下面挨个举例。
对于Array和其他语言一样,我们可以如下定义后,通过下标访问,其中...
表示省略了长度的数组,也可以替换为固定长度:
a_array :=[...] int{ 1,2,3,4,5 }
对于Slice,熟悉Python的读者应该能够快速理解,这几乎和Python中切片的用法相差无几:
a_slice := []int { 1,2,3 }
其中[]
表示切片的类型,要访问切片中的元素可以通过a_slice[start:end]
这样的形式,与Python不太相同的是Go中的切片没有步长。在Go中关于切片还有两个重要概念分别是len
和cap
,其中Length标示切片的长度,Capacity标示切片的容量,如何理解长度与容量呢,切片本质一段变长的动态数组,长度指的是在这块动态数组中一段片段的长度,容量是指的这段动态数组中片段的最大长度,如果使用append
关键字来合并两个切片时就有可能发生cap
的增长默认是扩容到原来cap
的两倍,以上切片我们还可以使用make
函数来进行初始化:
a_slice := make([]int,len,cap)
对于Map我们也可以通过make
函数进行初始化,如下:
a_map := make(map[string]int)
以上所有的数据结构我们均可以通过var
关键字来声明一个nil
值的相应变量,比如:
var a_array []int
var a_slice []int
var a_map map[string]int
chan
是一个比较特殊的数据类型,将在后面讲解。
指针
是你没听错,Go是有指针的,所以在关键字之外,这里必须得讲一下!并且如果你深入学习还能学会使用unsafe
包,尽管Go是一门现代语言,并且拥有GC。和C语言一样,最常见的两个操作符*
和&
,声明一个指针很简单:
var int_pointer *int
是不是看起来和C一毛一样?访问一个指针志向的变量地址也是一样:
something := 1
fmt.Printf("var_addr: %x\n", &something)
条件控制
和其他语言一样,Go在条件控制中也使用了if
和else
等关键字,一个典型的条件判断例子如下:
if ture {
...
} else if false {
...
} else {
...
}
同时Go也实现了switch
、case
、default
这些经典的关键字,一个多重条件判断例子如下:
switch something {
case 0:
...
case 1:
...
case 2:
...
default:
...
}
需要明确的是,Go中并需要声明break
关键字,如something
的值匹配了1,则隐式包含了break
在其中,case 2:
下的语句块则不会被执行。需要注意的是,Go中在实现多重条件判断时,引入了一个比较特别的关键字fallthrough
,其主要的作用是在紧跟fallthrough
后的一个CASE表达式中,不去判断其条件,而是直接执行CASE代码块中的语句,这个关键字着实用得也比较少。
最后Go也实现了goto
关键字,既无条件跳转,通常我们的编程老师都会说不要使用这个关键字,很大程度上会破坏代码的美感和可阅读性。
循环控制
Go中没有while
关键字,要实现常见的while true
操作则使用for {}
,一个典型的循环遍历如下:
for x := 0; x < 10; x++ {
... do something with x ...
}
而对于数据、切片等数据结构,则可以使用range
关键字来遍历其元素:
a_array := []int{1,2,3,4,5}
for key, value := range a_array {
... do something with key or value ...
}
和其他语言一样,跳出本次循环使用continue
,结束循环使用break
。
函数
Go的函数关键字比较简略,直接使用func
,下面结合指针以一个C语言中最经典的例子举例函数所会用到的关键字:
func swap(a *int, b *int) {
var tmp int
tmp = *a
*a = *b
*b = tmp
}
为了说明返回值再举一个例子:
func function_name(a int,b int) (int) {
sum := a + b
return
}
Go的函数是可以多值返回的,我们在实际编程里经常会返回error
类型来判断函数执行过程中是否返回了错误。在Go函数当中常用的还有另外一个关键字defer
,这也是与其他语言有所差异的地方,如defer close()
一般是用于在返回前延迟执行某个函数,这也是在错误处理中,我们要恢复或清理相关现场时经常用到的。
对象与接口
Go拥有很轻量级的OOP,常见的关于类的操作几乎都能实现。如果你和我一样经常使用C就会知道,在没有C++以前,我们通过结构体,也就是type
(C中是typedef)和struct
的关键字来体现面向对象的思想了。
我们在Go中可以如下定义一个结构体:
type Pentester struct {
web_hack_level int
reverse_engineering_level int
}
定义完成后可以初始化他,比如一个超级黑客的能力肯定是很牛逼的:
pentester_struct := Pentester { web_hack_level: 9999, reverse_engineering_level: 9999 }
那么如何体现OOP呢?我们分别从封装、继承、多态这三个面向对象的常见概念出发。
对于封装性,这里必须表扬一下Go把语言的简洁性体现得淋漓尽致,对于一个对象对外的可见性,很多语言如Java等都使用public之类的关键字,而在Go中是通过对象的成员函数或者变量的首字母大写来标识的,比如拿前面的结构体做例子:
type Pentester struct {
web_hack_level int
reverse_engineering_level int
}
func (p *Pentester) GetWebHackLevel() int {
return p.web_hack_level
}
func (p *Pentester) GetRELevel() int {
return p.reverse_engineering_level
}
在Pentester结构体中我们的成员变量都是使用的小写字母开头,故没办法通过实例化后的pentester_strucet.web_hack_level
调用,只能使用如GetRELevel
以大写字母开头的成员函数来取值。这样就体现了面向对象中的封装性,既封装提供了数据的安全性, 将变化保持在自己的实现中,对于外界来说不用关去心内部的处理过程, 只需要拿到对用方法调用即可。
对于继承性,Go通过匿名属性来实现,还是以上面的结构体为基准,举一个例子如下:
type SuperHacker struct {
Pentester
bluff_level int
}
func (s *SuperHacker) GetRELevel() int {
s.bluff_level = 999999
return s.bluff_level
}
以上例子中你要成为一名超级黑客,你装逼的本事就要牛逼,所以当别人问你逆向牛逼不牛逼的时候,你可以通过你吹牛逼等级重载掉作为一名撸站工时方法,不知道这样说大家有没有理解 ;)
对于多态性,其实就是统一事物的不同形态,在很多语言中的体现就是接口,Go也使用了与大多数语言相同的关键字interface
。还是拿以上的例子说明,比如不管是初级的Pentester还是超级黑客SuperHacker他们都要会撸站,他们的统一形态都是打工仔Worker,用代码来说明就是如下:
package main
import "fmt"
type Worker interface {
luzhan()
}
type Pentester struct {
attack_method string
}
type SuperHacker struct {
attack_method string
}
func (p Pentester) luzhan() {
fmt.Println("菜鸡", p.attack_method, "撸站")
}
func (s SuperHacker) luzhan() {
fmt.Println("超级黑客", s.attack_method, "撸站")
}
func main() {
var worker Worker
// 你是初级菜鸡时撸站
pentester := Pentester{"只会用工具"}
worker = pentester
worker.luzhan()
// 你是超级黑客时撸站
fakepro := SuperHacker{"脑波"}
worker = fakepro
worker.luzhan()
}
所以当他是超级黑客的时候,尽管大家都是通过接口实现了撸站,但是他可以用脑波去攻击目标,快速获取权限。
并发
Go语言最迷人的地方,莫过于超级高效且简明的并发处理,如果你接触过网络编程,你会发现在不同的操作系统下可能有不能网络通信模型来处理并发,这其中还涉及到诸如同步异步、阻塞非阻塞等复杂问题。Go的并发使用了比线程更轻量级的goroutine
来实现,而需要把你的函数变成并发的,你只需要在函数前加一个go
关键字即可就是这么简单,如下:
go func fucking_cool_goroutine_func() { }
甚至,Go也支持使用匿名函数(闭包)来快速插入go
:
go func(x int) {
x++
}
初学者使用闭包的时候经常会碰到逃逸问题和变量作用域问题,不是特别推荐。
谈到并发的同时不能不说数据的同步问题,在Go中使用了一个特殊的数据类型chan
来进行并发中的同步处理,这其中还包括一个看起来比较新奇的操作符<-
,下面逐个例子来感受一下Channel。
Channel的定义:
ch := make(chan int)
Channel发送与接收:
ch <- v // 发送值v到Channel ch中
v := <-ch // 从Channel ch中接收数据,并将数据赋值给v
如果有多个消息,需要使用select
来响应Channel的发送或者接收操作:
for {
select {
case c <- ch:
....
case <-quit:
return
}
}
这里笼统的说道Channel感受不会太深,后面需要结合,实际的例子来理解。
至此,以Go的25个关键字为索引,基本上已经能够了解了Go的常见语法了,后续在实践中可能还需要了解错误处理(关键字panic
、recover
)、IO操作(io.Xxxx
、重载reader
、writer
)等,才能用起来感觉很爽。
开发环境
在最近发布的Go版本中,已经没有烦人的GOPATH环境变量问题了,同时也加入了诸如Go Mod或者Go Vendor等特性,默认安装完Go,会在默认的用户目录的下生成 go/src
、go/bin
、以及go/pkg
。
当你打开 https://golang.google.cn/ 就可以看到下载链接,直接安装了,如果你使用MacOS并且安装了Homebrew作为包管理的话,一句brew install go
就解决了所有问题。
如果你仅仅只想体验一番,打开浏览器直奔 https://play.golang.org/ 即可。
对于IDE,伟大的Jetbrains公司已经提供了Goland,如果你想使用Vscode来进行折腾也没有关系,可以参考 https://code.visualstudio.com/docs/languages/go 快速配置。
学习资源
你可以从一个本地 go-tour https://github.com/Go-zh/tour 开始体验,按照例子一步一步玩耍下来应该就能慢慢熟悉Go语言了。
想要快速熟悉语法同样可以通过 Learn X in Y minute 项目 https://learnxinyminutes.com/docs/zh-cn/go-cn/
针对Go的每一个的常用用法等也可以学习 Go By Example 项目 https://gobyexample.com/
其他一些与安全相关的Go资源:
- Lets Hacking With Go https://github.com/parsiya/Hacking-with-Go
- Go编写的安全工具 https://github.com/topics/pentesting?l=go
- 《Blackhat Go》 https://nostarch.com/blackhatgo
行文仓促,学习语言做的笔记如有不足之处还请读者指出,十昏感谢 :)