浅记Go语言逆向

一、查看版本号

go version xx.exe

二、查看地址以及依赖库

go version -m xxx.exe
 
0
mod 显示出了该程序的依赖库:luago
 

三、pclntab

pclntab 全名是 Program Counter Line Table,直译为 程序计数器行数映射表,也叫Runtime Symbol Table

1、pclntab结构

0

2、pclntab定位

第一种方法是根据二进制文件中的 Section Name 来定位
如,elf文件中方.gopclntab Section 就对应于 pclntab 结构,MachO 文件中 __gopclntab Section 对应于 pclntab 结构
但这种方法不靠谱,如果我们利用ASLR技术在编译的时候加入下面命令就不会产生.gopclntab 节
go build -buildmode=pie -ldflags “-s -w”
第二种方法就是暴力搜索 pclntab 的 Magic Number
ELF 文件中的firstmoduledata 总是在 .noptrdata 这个 Section 里,PE 文件中可能会在 .data 或 .noptrdata Section,而 MachO 文件的 firstmoduledata 在 __noptrdata Section 中,我们可以按照uintptr 为单元遍历section,检查每个uintptr 指向的地址前四个字节是否为pclntab 的 Magic Number 0xFFFFFFFB 或 0xfffffffa
0
 
 

3、pclntab 结构图(针对性的)

0
开头四字节是Magic Number: 一般为 0xFFFFFFFB 或0xFFFFFFFA
Go 1.16之前的MagicNumber是0xffffffb,之后是0xfffffffa 
第五、六字节无意义
第七个字节 instruction size quantum, 1 为 x86, 4 为 ARM;
第八个字节为地址的大小,32bit 的为 4,64 bit 的为 8
这前八个字节就是pclntab的Header
 
第九个字节是函数表(function table)的起始位置
0
uintptr 代表一个指针类型,在 32bit 二进制文件中,等价于 uint32
 
函数表结束后,下面就是 Source file table(源码文件路径列表)
0

4、函数表(function table)

起始地址为pclntab的地址+8,第一个元素代表函数个数
 
0
每两个元素一组,每一组第一个元素代表函数地址,第二个元素代表函数结构体相对于pclntab的偏移
函数定义结构体:
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
};
//name 是函数名的偏移,所以存int类型

5、源码文件表

函数表结束后隔一个uintptr 的位置,就是源码文件表的偏移,长度为4字节(相对于pclntab的起始位置)
 
go_parser的解析如下
0

四、moduledata 

在 Go 语言的体系中,Module 是比 Package 更高层次的概念,具体表现在一个 Module 中可以包含多个不同的 Package,而每个 Package 中可以包含多个目录和很多的源码文件。

1、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
}
 
0
Go语言中定位firstmoduledata  的方法是通过找pclntab的位置来进行定位

2、moduledata 结构的迭代

0
 
 

五、Go语言逆向所注意的小点

1、IDA打开Go程序,会自动跳转到一个入口函数的位置

64bit PE 文件通常会自动跳转到 
_rt0_amd64_windows 
64bit ELF 文件,通常会自动跳转到 
_rt0_amd64_linux 函数
这只不过是 Go runtime 的入口函数,而Go的入口函数是main.main()函数

2、main函数执行之前会执行 init()函数

 
0

3、11.4 runtime.growslice()切片

4、Go语言调试

网上搜 Go debug,绝大部分资料跟 dlv 调试工具和 GDB 的 Go 调试插件有关。但是这两个工具的使用有一个前提:需要有调试符号甚至 Go 项目的源码。它们是给常规的 Go 语言开发者使用的。然而我们要分析的 Go 恶意软件,绝大部分是没有调试符号的,strip 处理的很干净。
 

 

 

六、Go语言恢复符号

1、IDAGolangHelper插件

0

2、待补

 

七、Go语言逆向需要注意的地方

Go语言与C语言用'\0'表示字符串结束不同,Go语言会将所有字符串连接在一起,通过起始指针和字符串长度来表示整个串

posted @ 2022-05-26 23:34  TLSN  阅读(2514)  评论(0)    收藏  举报