1. 程式人生 > >Go語言探索第一天,學習筆記

Go語言探索第一天,學習筆記

        已經在大數字做了三年的windows C++開發了,坦白的講,自己現在還是個小碼農。深深的感到自己是個loser。如今,大家回家後鮮有開啟電腦玩電腦的人,與09年我剛

上大學那會兒乃至13年剛入職的時候相比,堪稱滄海桑田~網際網路節奏太快了,windows終端目測已經淪陷,轉瞬間就變成了移動端的天下。想起之前學習彙編,windows PE

檔案,hook,執行緒注入等各種windows技能,對新技術的嗤之以鼻,如今對自己感到了呵呵。期間自己學過了PHP,JS,甚至安卓SDK NDK。學了不實踐,兩天忘光光。作為一個小碼農,如今感到一絲彷徨迷茫,竟不知未來何去何從。

       經過自己兩天輾轉反折的思考,感覺自己應該學習點服務端的知識,這個方面好友早就提醒過我多次,之前的我竟然執迷不悟。

我們部門的服務端開發語言百花齊放,lua,PHP,python,GO,四大陣營,這個也歸結於領導的包容並收。聽人說GO效率要比C\C++高,我聽到了差點沒笑掉大牙。但我深

深的知道,GO是個很牛逼的東西。決定在業餘時間探索一下這個東西。   GO大道至簡,開發環境搭建也就是解壓縮,配置環境變數兩個步驟而已。對了據說他吸收了各種語言的優點,摒棄了各種語言的缺點。到底是不是這麼回事呢?我們來一點點探索吧~

系統變數名:

GOROOT

值:

Go的安裝位置

系統變數名:

Path

追加值:

;%GOROOT%\bin



開發環境安裝包在此 https://yunpan.cn/cRzWuKqjn9MIV  訪問密碼 a41e。

cmd 輸入go   打印出如下一大堆資訊,那麼恭喜你,環境搭建成功了。然後讓我們開始寫自己的第一個GO程式吧~


package main

import "fmt"

func main() {

fmt.Printf("HelloWorld!")

}

程式碼注意:

func main(){

不可以寫成

func main()

{

這個不僅僅是程式碼規範,Go為了統一編碼風格的語法。

儲存檔名為 gofirst.go

編譯 go build gofirst.go

這個時候你會在gofirst.go目錄下發現多了一個檔案


拖入cmd黑框框執行,

唔?生成的檔案竟然有1942KB這麼大!!!為啥子檔案會這麼大?難道Go會不再沿用windows PE結構?答案是否定的。

UE看了下Go交叉編譯器生成的二進位制檔案,標準的win PE檔案,很顯然,程式碼段和資源段增加了不少內容。瑕不掩瑜,這可能是谷歌為了解決某種問題不得不這麼做的吧。



IDA看了看,顯然,為了輸出一個hello world   Go在初始化的時候做了不少工作。


沒錯,至少在輸出Hello World方面。GO語言應該會比C語言慢個幾十倍。  但它不是生來輸出Hello World的。另外他是一種編譯型語言,而且生成windows的標準WinPE檔案。同理 *nix的標準執行檔案。我們有理由相信,在大型專案上,拋開記憶體垃圾回收機制不講,(語言自動管理垃圾回收機制,勢必會對速度有一丁點影響)Go完全有理由媲美C、C++,還會大大降低開發的難度,我們有好多理由去好好了解下這麼語言。

為了給大家展示一下Go語言媲美C語言的能力,我們寫一個Go語言的   Windows MessBox程式。程式碼如下,唔~第一天寫go程式,這段程式碼是copy自網路哦。為的是證明C能做到的,GO同樣能做到,效率還不會太低哦。

package main
import (
       "syscall"
       "unsafe"
       "fmt"
)
func abort(funcname string, err int) {
       panic(funcname + " failed: " + syscall.Errno(err).Error())
}
var (
       kernel32, _ = syscall.LoadLibrary("kernel32.dll")
       getModuleHandle, _ = syscall.GetProcAddress(kernel32, "GetModuleHandleW")
       user32, _ = syscall.LoadLibrary("user32.dll")
       messageBox, _ = syscall.GetProcAddress(user32, "MessageBoxW")
)
const (
       MB_OK                      = 0x00000000
       MB_OKCANCEL                = 0x00000001
       MB_ABORTRETRYIGNORE        = 0x00000002
       MB_YESNOCANCEL             = 0x00000003
       MB_YESNO                   = 0x00000004
       MB_RETRYCANCEL             = 0x00000005
       MB_CANCELTRYCONTINUE       = 0x00000006
       MB_ICONHAND                = 0x00000010
       MB_ICONQUESTION            = 0x00000020
       MB_ICONEXCLAMATION         = 0x00000030
       MB_ICONASTERISK            = 0x00000040
       MB_USERICON                = 0x00000080
       MB_ICONWARNING             = MB_ICONEXCLAMATION
       MB_ICONERROR               = MB_ICONHAND
       MB_ICONINFORMATION         = MB_ICONASTERISK
       MB_ICONSTOP                = MB_ICONHAND
       MB_DEFBUTTON1              = 0x00000000
       MB_DEFBUTTON2              = 0x00000100
       MB_DEFBUTTON3              = 0x00000200
       MB_DEFBUTTON4              = 0x00000300
)
func MessageBox(caption, text string, style uintptr) (result int) {
       // var hwnd HWND
       ret, _, callErr := syscall.Syscall6(uintptr(messageBox), 4,
               0, // HWND
               uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(text))), // Text
               uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(caption))), // Caption
               style, // type
               0,
               0)
       if callErr != 0 {
               abort("Call MessageBox", int(callErr))
       }
       result = int(ret)
       return
}
func main() {
       defer syscall.FreeLibrary(kernel32)
       defer syscall.FreeLibrary(user32)
       fmt.Printf("Retern: %d\n", MessageBox("Done Title", "This test is Done.", MB_YESNOCANCEL))
}
func init() {
       fmt.Print("Starting Up\n")
}

來來來,儲存為messagebox.go

執行命令go build messagebox.go


同樣檔案不小,也許是包含了除錯資訊?  這個我們之後再進行探索。執行結果如下


注意,我們呼叫了

 kernel32, _ = syscall.LoadLibrary("kernel32.dll")
       getModuleHandle, _ = syscall.GetProcAddress(kernel32, "GetModuleHandleW")
       user32, _ = syscall.LoadLibrary("user32.dll")
       messageBox, _ = syscall.GetProcAddress(user32, "MessageBoxW")

看出來沒,這和C\C++呼叫一個微軟的Api如出一轍。都是獲取了MessageBox在記憶體的地址,然後

func MessageBox(caption, text string, style uintptr) (result int) {
       // var hwnd HWND
       ret, _, callErr := syscall.Syscall6(uintptr(messageBox), 4,
               0, // HWND
               uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(text))), // Text
               uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(caption))), // Caption
               style, // type
               0,
               0)
       if callErr != 0 {
               abort("Call MessageBox", int(callErr))
       }
       result = int(ret)
       return
}

關看程式碼就可以知道GO呼叫MessageBox,和C、c++進行了完全一樣的步驟。syscall.Syscall6到底做了點什麼~  想必也就是簡單的獲取syscall.Syscall6的第一個引數 messbox在windows記憶體中位置(二進位制程式碼段的位置),獲取第二個引數,這個表明了被呼叫的MessageBox有四個引數,也就是push四次,然後分別取出後面的四個引數進行壓杖,最後call

push style

push caption

push text

push 0

CALL messageBox

不過我好奇的是,我簡單的彈一個messagebox,但是這個程序卻啟動了6個執行緒



拋開搜狗輸入法注入可能啟動的執行緒不說,起碼在·GO編譯出來的程式碼中,也就是

記憶體偏移 messagebox.exe+0X4af80和messagebox.exe+0X4b240這個地方的執行緒,理論上講是Go語言生成的。

莫非這幾個執行緒就是為了Go語言高併發設計的執行緒池?

我們以後慢慢來深入學習,看看Go語言葫蘆裡到底有什麼藥。