日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

二进制函数_Go二进制文件逆向分析从基础到进阶——MetaInfo、函数符号和源码文件路径列表...

發布時間:2024/7/23 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 二进制函数_Go二进制文件逆向分析从基础到进阶——MetaInfo、函数符号和源码文件路径列表... 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
書接前文,本文主要介紹 Go 二進制文件中 Meta Information 的解析,與函數符號和源碼文件路徑列表的提取。最后詳細介紹一下?Moduledata?這個結構。傳送門:Go二進制文件逆向分析從基礎到進階——綜述

05Meta information>>>>

5.1?Go Build ID

每一個 Go 二進制文件內,都有一個獨一無二的 Build ID,詳情參考?src/cmd/go/internal/work/buildid.go。Go Build ID 可以用以下命令來查看:$ go tool buildid 對于 ELF 文件,也可以用?readelf?命令查看,不過看到的只是 Hex String:轉換一下上圖的 Hex String,就是?$ go buildid tracker_nonstrip?的顯示結果了:另外,如果是用?gccgo?編譯的 ELF 文件,用?file?命令 或者?readelf?命令還能看到?GNU Build ID,注意區分一下:Build ID 是 Go 二進制文件中的元信息之一,但是對逆向分析二進制文件并沒有什么實際幫助,本文只做簡單介紹。>>>>

5.2?Go Version

Go 二進制文件中,還會把構建本文件的 Go 語言版本號打包進去。查看方式有以下 2 種:
  • 通過 IDAPro 中的 strings 檢索:
  • 2. 通過 Go 語言官方命令?$ go version [FILE]?,該命令不指定文件則打印本地安裝的 Go 語言版本,指定文件則打印目標文件對應的 Go 語言版本:>>>>

    5.3?GoROOT

    GOROOT 是 Go 語言的安裝路徑,里面有 Go 的標準庫和自帶的命令行工具。一般來說,Go 的二進制文件中都會把?GOROOT?打包進去,并在?runtime.GOROOT()?函數中會用到這個值。比如某 Go 樣本中打包的?runtime.GOROOT()?函數,可以看到構建該樣本所在主機的?GOROOT?路徑是?/usr/lib/go?:看 Go 標準庫中?runtime.GOROOT()?函數的源碼,會發現這個路徑其實叫?sys.DefaultGoroot?:至于如何提取這個字符串,就需要在 IDAPro 中先把函數名符號恢復出來,然后根據函數名找到?runtime.GOROOT()?這個函數,最后在這個函數中提取出?sys.DefaultGoroot?的值。提取方法可能有多種,go_parser for IDAPro 中的方法是先解析該函數的?FlowChart?,找到最后帶 return 的 FlowChart,然后再找出該值的地址。那么知道了這個信息,對逆向分析有什么用處?后文描述如何提取、過濾 Go 二進制文件中的源碼路徑列表時會用到。06pclntab>>>>

    6.1?pclntab介紹

    pclntab 全名是 Program Counter Line Table,可直譯為?程序計數器行數映射表, 在 Go 中也叫 Runtime Symbol Table, 所以我把它里面包含的信息叫做 RTSI(Runtime Symbol Information)。2013 年由?Russ Cox?(Go 語言創始團隊成員,核心開發者)從?Plan9?移植到?Go 1.2?上,至今沒有太大變化。至于 PC(Program Counter),是給 Go 語言的 runtime 來操作的,可以粗淺理解為 runtime 當前執行的程序代碼(或指令)。按 Russ Cox 在《Go 1.2 Runtime Symbol Information》中的說法,引入?pcnlntab?這個結構的最初動機,是為?Stack Trace?服務的。參考前文《Go二進制文件逆向分析從基礎到進階——綜述》中?redress?工具的報錯信息,當程序運行出錯要?panic?的時候,runtime 需要知道當前的位置,層級關系如 pkg->src file->function or method->line number,每一層的信息 runtime 都要知道。Go 就把這些信息結構化地打包到了編譯出的二進制文件中。除此之外,pcnlntab?中還包含了棧的動態管理用到的棧幀信息、垃圾回收用到的棧變量的生命周期信息以及二進制文件涉及的所有源碼文件路徑信息。pclntab?的概要結構如下(其中的?strings?指的是 Function name string、Source File Path string,并不是整個程序中用到的所有 strings):pclntab:
    • func table
    • _func structs
    • pcsp
    • psfile
    • pcline
    • pcdata
    • strings
    • file table
    而我們最關心的信息,其實主要是 2 個:函數表(func table)?和?源碼文件路徑列表(file table)。針對性的?pclntab?結構如下:簡單解釋一下:
    • pclntab?開頭 4-Bytes 是從 Go1.2 至今不變的?Magic Number:?0xFFFFFFFB?;
    • 第 5、6個字節為 0x00,暫無實際用途;
    • 第 7 個字節代表?instruction size quantum,?1?為 x86, 4 為 ARM;
    • 第 8 個字節為地址的大小,32bit 的為 4,64 bit 的為 8,至此的前 8 個字節可以看作是?pclntab?的 Header;
    • 第 9 個字節開始是?function table?的起始位置,第一個 uintptr 元素為函數(pc, Program Counter) 的個數;
    • 第 2 個 uintptr 元素為第 1 個函數(pc0) 的地址,第 3 個 uintptr 元素為第 1 個函數結構定義相對于?pclntab?的偏移,后面的函數信息就以此類推;
    • 直到 function table 結束,下面就是 Source file table。Source file table 以 4 字節(int32)為單位,前 4 個字節代表 Source File 的數量,后面每一個?int32?都代表一個 Source File Path String 相對于?pclntab?的偏移;
    • uintptr?代表一個指針類型,在 32bit 二進制文件中,等價于?uint32,在 64bit 二進制文件中,等價于?uint64?。
    pclntab?在源碼中是如此定義的,構建二進制文件時,RTSI?也是在二進制文件中按上面順序排列的。所以,在 IDAPro 中解析?pclntab,就按照如上說明來解析即可。go_parser在 IDAPro 中解析好的?pclntab?開頭部分如下:關于如何查找、定位到?pclntab,下文詳述。>>>>

    6.2 函數表

    函數表(func table)的起始地址,為?(pclntab_addr + 8),第一個元素( uintptr N) 代表函數的個數,如上圖中的?0x1C3A。解析來就是每兩個 uintptr 元素為一組,即?(func_addr, func_struct_offset),每組第一個元素為函數的地址,第二個元素為函數結構體定義相對于?pclntab?起始地址的偏移。即:func_struct_addr = pclntab_addr + func_struct_offset# 以上圖第一個函數 internal_cpu_Initialize() 為例,pclntab 的地址為 0x8FBFA0,它的 func_struct_offset 為 0x1C3C0# 那么它的 func struct address 為:0x8FBFA0 + 0x1C3C0 = 0x918360地址 0x918360 處的 Function Struct 是什么模樣?下圖為?go_parser在 IDAPro 中解析好的結果:Function Struct 在 Russ Cox 的《Go 1.2 Runtime Symbol Information》有介紹:struct Func{ uintptr entry; // start pc int32 name; // name (offset to C string) int32 args; // size of arguments passed to function int32 frame; // size of function frame, including saved caller PC int32 pcsp; // pcsp table (offset to pcvalue table) int32 pcfile; // pcfile table (offset to pcvalue table) int32 pcln; // pcln table (offset to pcvalue table) int32 nfuncdata; // number of entries in funcdata list int32 npcdata; // number of entries in pcdata list};對于逆向分析來說,這里最有用的信息,就是函數名了。如上圖所示,函數名是一個以?0x00?結尾的 C-String。在 Function Struct 中,第二個元素只是 int32 類型的偏移值,而函數名字符串的地址,則用以下方式計算得出:func_name_addr = pclntab_addr + Dword(func_stuct_addr + sizeof(uintptr))# 比如上面函數 internal_cpu_Initialize() 的 func_struct_addr 為 0x918360,# 它的 Func Name String Offset 為 0x1C408,那么它的 Func Name String Address 就是0x8FBFA0(pclntab_addr) + 0x1C408 = 0x9183A8如此一來,就從 Go 二進制文件中恢復出了一個函數的名字。用這種方式遍歷 Function Table,便可以恢復所有函數名。有的師傅可能還會問,Function Struct 中第 3 個元素?args?,有沒有對逆向分析有用的信息?如果知道函數的參數、返回值信息,豈不更爽。這個……曾經有,現在,真沒有了。在 Go 標準庫源碼?src/debug/gosym/symtab.go中解析這個 Function Struct 的一個類型定義中,有兩條注釋,說 Go 1.3 之后就沒這種信息了:另外,還有一些函數用上面的方式無法解析,是編譯器做循環展開時自動生成的匿名函數,也叫?Duff’s Device),函數體型如下圖:

    這樣的函數知道它是用來連續操作內存(拷貝、清零等等)的就可以。>>>>

    6.3?源碼文件路徑

    在 pclntab 中,函數表下面隔一個 uintptr 的位置,就是源碼文件表(Srouce File Table) 的?偏移,長度為 4-Bytes:這個偏移,是相對?pclntab?的起始地址來說的。接著上面的例子,pclntab?的地址為 0x8FBFA0,此處偏移量為 0x22FDD0 ,那么源碼文件路徑列表的地址就是:srcfiletab_addr = pclntab_addr + srcfiletab_offset0x8FBFA0 + 0x22FDD0 = 0xB2BD70在 IDAPro 中,0xB2BD70 處?go_parser解析好的效果如下:Source File Table 中的元素全都以 4-Bytes(uint32) 為單位,第一個元素(0x2CF)是本二進制文件涉及的所有源碼文件的個數,包括標準庫的源碼文件、第三方 Pacakge 的源碼文件以及當前項目的源碼文件。后續每個 uint32 元素代表一個相對于?pclntab?的偏移量,該偏移量加上?pclntab?的起始地址,即為相應源碼文件路徑字符串的起始地址。每個源碼文件路徑名都是以?0x00?結尾的 C-String。go_parser對此的處理,是計算出每個偏移量對應的地址,然后創建字符串,并為當前偏移所在的地址創建 data reference,在 IDAPro 中雙擊 data reference 即可跳轉到相應的字符串地址。比如,第一個源碼文件的偏移量為 0x239134,那么它對應的實際地址為:0x8FBFA0(pclntab_addr) + 0x239134 = 0xB350D4查看地址 0xB350D4 處的字符串即可印證:另外,?go_parser如果成功定位到?runtime.GOROOT()?函數,并解析出?sys.DefaultGoroot?的值,還會根據這個值過濾掉所有標準庫源碼文件路徑和第三方 Package 源碼文件路徑,在 IDAPro 的 Console 中只打印本項目相關的所有源碼文件路徑;如果沒能提取出?sys.DefaultGoroot?,則會打印所有源碼文件路徑。DDG 樣本中的源碼文件路徑列表如下所示(IDAPro Console):07moduledata>>>>

    7.1?moduledate 介紹

    在 Go 語言的體系中,Module 是比 Package 更高層次的概念,具體表現在一個 Module 中可以包含多個不同的 Package,而每個 Package 中可以包含多個目錄和很多的源碼文件。“A module is a collection of related Go packages that are versioned together as a single unit.”*
    — Go Module Docs詳情參考:《Go Modules in Real Life》相應地,Moduledata?在 Go 二進制文件中也是一個更高層次的數據結構,它包含很多其他結構的索引信息,可以看作是 Go 二進制文件中 RTSI(Runtime Symbol Information) 和 RTTI(Runtime Type Information) 的?地圖。Moduledata?源碼定義如下(關鍵字段看注釋):type moduledata struct { pclntable []byte // pclntab address ftab []functab // function table address filetab []uint32 // source file table address findfunctab uintptr minpc, maxpc uintptr // minpc: first pc(function) address text, etext uintptr // [.text] section start/end address noptrdata, enoptrdata uintptr data, edata uintptr // [.data] section start/end address bss, ebss uintptr // [.bss] section start/end address noptrbss, enoptrbss uintptr // [.noptrbss] section start/end address end, gcdata, gcbss uintptr types, etypes uintptr // types data start/end address textsectmap []textsect typelinks []int32 // offset table for types itablinks []*itab // interface table ptab []ptabEntry pluginpath string pkghashes []modulehash modulename string modulehashes []modulehash hasmain uint8 // 1 if module contains the main function, 0 otherwise gcdatamask, gcbssmask bitvector typemap map[typeOff]*_type // offset to *_rtype in previous module bad bool // module failed to load and should be ignored next *moduledata}根據 Moduledata 的定義,Moduledata 是可以串成鏈表的形式的,而一個完整的可執行 Go 二進制文件中,只有一個 firstmoduledata 包含如上完整的字段。簡單介紹一下關鍵字段:
    • 第 1 個字段?pclntable,即為?pclntab?的地址;
    • 第 2 個字段?ftab,為?pclntab?中 Function Table 的地址(=pclntab_addr + 8);
    • 第 3 個字段?filetab,為?pclntab?中 Source File Table 的地址;
    • 第 5 個字段?minpc,為?pclntab?中第一個函數的起始地址;
    • 第 7 個字段?text,在普通二進制文件中,對應于 [.text] section 的起始地址;在 PIE 二進制文件中則沒有這個要求;
    • 字段?types,為存放程序中所有數據類型定義信息數據的起始地址,一般對應于 [.rodata] section 的地址;
    • 字段?typelinks,為每個數據類型相對于?types?地址的偏移表,該字段與?types?字段在后文解析 RTTI 時要用到;
    • 字段?itablinks,則是 Interface Table 的起始地址,該字段解析 Interface Table 時要用到。
    go_parser在 IDAPro 中對?firstmoduledata?的解析結果示例如下:需要注意的是,Go 語言中,一個 Slice 類型數據的底層定義要用到 3 個元素:(Data Address, Length, Capacity)。所以在 IDAPro 中看某個 Slice 類型的字段,要一次看 3 個 uintptr 元素。>>>>

    7.2?firstmoduledata 定位

    通過上面的介紹可以知道,如果在 Go 二進制文件中事先找到 firstmoduledata 這個結構,然后在這個結構中就可以按圖索驥找到其他我們感興趣的信息。那么如何在一個 Go 二進制文件中精準定位到?firstmoduledata?呢?很直觀就想到?pclntab,原因有兩點:一是它對應于?firstmoduledata?結構的第一個字段;最主要的是?pclntab?包含的信息豐富,起始位置還有獨特的 4-Bytes 的 Magic Number。一旦找到了?pclntab?,就可以定位到?firstmoduledata。

    7.2.1 定位 pclntab

    定位?pclntab?的方法,公開的相關工具里用的最多的,是根據二進制文件中的 Section Name 來定位。因為平常見到的 Go 二進制文件,PE 文件和 ELF 文件中?.gopclntab?Section 就對應于?pclntab?結構,MachO 文件中?__gopclntab?Section 對應于?pclntab?結構。然而這種方法并不靠譜。最主要的原因,是?PIE?二進制文件。PIE 全稱為?Position Independent Executable,意思是地址無關的可執行文件,這個類型的二進制文件結合 ASLR 技術可以加強 Go 二進制文件自身安全性。這對 Go 自身的內存安全機制來說,是個錦上添花的特性。Go 的?build/install?命令在構建二進制文件時,有幾個?buildmode?選項:? go help buildmodeThe 'go build' and 'go install' commands take a -buildmode argument whichindicates which kind of object file is to be built. Currently supported valuesare: -buildmode=archive Build the listed non-main packages into .a files. Packages named main are ignored. -buildmode=c-archive Build the listed main package, plus all packages it imports, into a C archive file. The only callable symbols will be those functions exported using a cgo //export comment. Requires exactly one main package to be listed. -buildmode=c-shared Build the listed main package, plus all packages it imports, into a C shared library. The only callable symbols will be those functions exported using a cgo //export comment. Requires exactly one main package to be listed. -buildmode=default Listed main packages are built into executables and listed non-main packages are built into .a files (the default behavior). -buildmode=shared Combine all the listed non-main packages into a single shared library that will be used when building with the -linkshared option. Packages named main are ignored. -buildmode=exe Build the listed main packages and everything they import into executables. Packages not named main are ignored. -buildmode=pie Build the listed main packages and everything they import into position independent executables (PIE). Packages not named main are ignored. -buildmode=plugin Build the listed main packages, plus all packages that they import, into a Go plugin. Packages not named main are ignored.Go 在構建可執行 ELF 文件時,目前還是默認使用?-buildmode=exe,而從 2019 年 4 月底 Go 語言官方?Change Log 203606開始,在 Windows 上構建可執行 PE 文件時,則會默認使用?-buildmode=pie:下圖是同一個項目,用 3 種不同的編譯方式生成的可執行 ELF 文件 Section Headers 對比,從左至右編譯方式依次為:
  • go build
  • go build -ldflags “-s -w”
  • go build -buildmode=pie -ldflags “-s -w”
  • 可以看到,第三種編譯方式生成的 PIE 文件,是沒有?.gopclntab?這個 Section 的,而是多了一些重定向相關的 Section。這也是前文《Go二進制文件逆向分析從基礎到進階——綜述》中?redress?工具報錯的原因:它根據 Section Name 查找?pclntabl?的思路,對 PIE 文件無效。既然靠 Section Name 來定位?pclntab?的方式不靠譜,其他能用的方式就只剩暴力搜索?pclntab?的 Magic Number 了。通過研究發現,無論是否 PIE ,ELF 文件中的firstmoduledata?總是在?.noptrdata?這個 Section 里,PE 文件中可能會在?.data?或?.noptrdata?Section,而 MachO 文件中的?firstmoduledata?會在?__noptrdata?Section 中。如此一來,就可以按 uintptr 為單元遍歷目標 Section,檢查每個 uintptr 指向的地址前 4 字節是否為?pclntab?的 Magic Number?0xFFFFFFFB,找到這個位置,就差不多了。偽碼可表示為:Dword(uintptr(possible_firstmoduledata_addr)) & 0xFFFFFFFF == MagicNumber之所以說?差不多,是因為二進制文件中可能還有別的值為?0xFFFFFFFB,并不是所有這個值的位置都是 pclntab。找到這個值,還要精準驗證它是不是?pclntab,以及上級數據結構是不是?firstmoduledata?。

    7.2.2 驗證 firstmoduledata

    按照上述思路,找到一個可能的?firstmoduledata 起始地址,其第一個 uintptr 元素指向的位置,前 4 字節為?pclntab?的 Magic Number。如果是真實的?firstmoduledata,它內部是有幾個字段可以跟?pclntab?中的數據進行交叉驗證的,比如:
    • firstmoduledata.ftab == pclntab_addr + 8
    • firstmoduledata.filetab == firstmoduledata.ftab + pclntab.functab_size + sizeof(uintptr)
    • firstmoduledata.minpc == firstmoduledata.text_addr == uintptr(pclntbl_addr + 8 + ADDR_SZ) == first function of pclntab.functab
    當然,不一定要驗證上面所有條件,驗證其中一部分甚至一個關鍵條件,就可以確認當前地址是否為真正的?firstmoduledata。另外,隨著 Go 的持續迭代,?pclntab?和?moduledata?的結構定義也會發生變化。《Reconstructing Program Semantics from Go Binaries》 中對比過幾個版本的?moduledata?之間的區別:我們對此結構的解析,也要隨著 Go 語言的迭代而進行相應調整,否則可能 Go 語言發了一個新版,里面改動了關鍵數據結構,我們按照舊的結構去解析,就會出錯。

    - End -

    精彩推薦

    通達OA11.7 后臺sql注入到rce漏洞分析

    Windows XP源代碼“正式”泄露

    Yii2框架Gii模塊 RCE 分析

    金九銀十招聘季,安全客全新招聘欄正式上線!!!

    戳“閱讀原文”查看更多內容 創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

    總結

    以上是生活随笔為你收集整理的二进制函数_Go二进制文件逆向分析从基础到进阶——MetaInfo、函数符号和源码文件路径列表...的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。