首页
社区
课程
招聘
[原创]高版本go语言符号还原
2023-6-5 15:05 18093

[原创]高版本go语言符号还原

2023-6-5 15:05
18093

实例版本

2022.6.8: go version go1.18.3 windows/amd64
高版本适用 1.20

还原过程

搜索 go.build
图片描述
图片描述
查找交叉引用
图片描述

 

go.build 在新版本中一定位于 函数名称的第一个。

 

图片描述

 

https://go.dev/src/runtime/symtab.go
阅读源码,获取 moduledata 结构

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// pcHeader holds data used by the pclntab lookups.
type pcHeader struct {
    magic          uint32  // 0xFFFFFFF0
    pad1, pad2     uint8   // 0,0
    minLC          uint8   // min instruction size
    ptrSize        uint8   // size of a ptr in bytes
    nfunc          int     // number of functions in the module
    nfiles         uint    // number of entries in the file tab
    textStart      uintptr // base for function entry PC offsets in this module, equal to moduledata.text
    funcnameOffset uintptr // offset to the funcnametab variable from pcHeader
    cuOffset       uintptr // offset to the cutab variable from pcHeader
    filetabOffset  uintptr // offset to the filetab variable from pcHeader
    pctabOffset    uintptr // offset to the pctab variable from pcHeader
    pclnOffset     uintptr // offset to the pclntab variable from pcHeader
}
 
 
 
type moduledata struct {
    pcHeader     *pcHeader
    funcnametab  []byte
    cutab        []uint32
    filetab      []byte
    pctab        []byte
    pclntable    []byte
    ftab         []functab
    findfunctab  uintptr
    minpc, maxpc uintptr
 
    text, etext           uintptr
    noptrdata, enoptrdata uintptr
    data, edata           uintptr
    bss, ebss             uintptr
    noptrbss, enoptrbss   uintptr
    end, gcdata, gcbss    uintptr
    types, etypes         uintptr
    rodata                uintptr
    gofunc                uintptr // go.func.*
 
    textsectmap []textsect
    typelinks   []int32 // offsets from types
    itablinks   []*itab
 
    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
}

pclntable 一般等于 ftab, 参照上图, ftab 与 pclntable 填充的是 pclntable 的值。

 

funcnametab 填充的是 函数名称
图片描述
filetab 填充的是 文件名称
图片描述

 

与函数名称相关的是 ftab 和 pclntable
图片描述

 

适用于下面的结构体

1
2
3
4
type functab struct {
    entryoff uint32 // relative to runtime.text
    funcoff  uint32
}

图片描述
entryoff 为 以代码段为起始位置 的偏移。表示该函数实际的位置。 代码段在windows为 text

 

funcoff 为 以 pclntable 为起始位置的偏移。

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
//src\runtime\symtab.go
type functab struct {
    entry   uintptr
    funcoff uintptr
}
 
type funcInfo struct {
    *_func
    datap *moduledata
}
 
//src\runtime\runtime2.go
type _func struct {
    entry   uintptr // start pc
    nameoff int32   // function name
 
    args        int32  // in/out args size
    deferreturn uint32 // offset of start of a deferreturn call instruction from entry, if any.
 
    pcsp      uint32
    pcfile    uint32
    pcln      uint32
    npcdata   uint32
    cuOffset  uint32  // runtime.cutab offset of this function's CU
    funcID    funcID  // set for certain special runtime functions
    _         [2]byte // pad
    nfuncdata uint8   // must be last
}

对应着一个 funcInfo 结构体, 里面包含一个 _func 类型,该类型中有我们想要的信息。

 

意思就是: func 的 _func = pclntable + funcOff
通过上图的信息计算:

1
2
hex(0x504320 + 0x2c20) = '0x506f40'
hex(0x504320 + 0x2c48) = '0x506f68'

发现刚好可以对得上信息
图片描述

 

第一个函数 go.build

 

图片描述
第二个函数 internal_cpu_Initialize
图片描述

输出脚本

知道了这些就可以编写简单的脚本来还原go符号名了

 

ida python 脚本

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
import idc
from idc import *
import ida_nalt
 
moduledata_addr = 0x05289C0
pcHeader_addr = idc.get_qword(moduledata_addr)
 
if idc.get_wide_dword(pcHeader_addr) != 0x0FFFFFFF0:
    print(idc.get_wide_dword(pcHeader_addr))
    print("错误,并不是一个正确的go文件")
 
funcnametable_addr = idc.get_qword(moduledata_addr + 8)
filetab_addr = idc.get_qword(moduledata_addr + 8 + ((8*3) * 2))
pclntable_addr = idc.get_qword(moduledata_addr + 8 + ((8*3) * 4))
pclntable_size = idc.get_qword(moduledata_addr + 8 + ((8*3) * 4) + (8 * 4))
set_name(moduledata_addr, "firstmoduledata")
set_name(funcnametable_addr, "funcnametable")
set_name(filetab_addr, "filetab")
set_name(pclntable_addr, "pclntable")
 
print(pclntable_size)
 
def readString(addr):
    ea = addr
 
    res = ''
    cur_ea_db = get_db_byte(ea)
    while  cur_ea_db != 0 and cur_ea_db != 0xff:
        res += chr(cur_ea_db)
        ea += 1
        cur_ea_db = get_db_byte(ea)
    return res
 
def relaxName(name):
    # 将函数名称改成ida 支持的字符串
    #print(name)
    if type(name) != str:
        name = name.decode()
    name = name.replace('.', '_').replace("<-", '_chan_left_').replace('*', '_ptr_').replace('-', '_').replace(';','').replace('"', '').replace('\\', '')
    name = name.replace('(', '').replace(')', '').replace('/', '_').replace(' ', '_').replace(',', 'comma').replace('{','').replace('}', '').replace('[', '').replace(']', '')
    return name
 
cur_addr = 0
for i in range(pclntable_size):
 
    # 获取函数信息表
    cur_addr = pclntable_addr + (i * 8)
 
    # 获取函数入口偏移
    funcentryOff = get_wide_dword(cur_addr)
    funcoff = get_wide_dword(cur_addr + 4)
 
    funcInfo_addr = pclntable_addr + funcoff
 
    funcentry_addr = get_wide_dword(funcInfo_addr)
    funnameoff = get_wide_dword(funcInfo_addr + 4)
    funname_addr = funcnametable_addr + funnameoff
    funname = readString(funname_addr)
 
    # 真实函数地址
    truefuncname = relaxName(funname)
    truefuncentry = ida_nalt.get_imagebase() + 0x1000 + funcentryOff
 
    print(hex(truefuncentry), hex(funcoff), hex(funcInfo_addr),hex(funcentry_addr), hex(funnameoff),hex(funname_addr) ,funname)
    # 改名
    set_name(truefuncentry, truefuncname)
 
 
 
 
#print(hex(cur_addr))

其中 moduledata_addr 需要手动填充。

 

还原效果

 

图片描述

参考链接

https://github.com/Abyss-emmm/goparse
跳跳糖


[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

最后于 2023-6-5 17:07 被mingyuexc编辑 ,原因:
收藏
点赞11
打赏
分享
最新回复 (10)
雪    币: 19244
活跃值: (28872)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
秋狝 2023-6-6 09:18
2
1
mark
雪    币: 184
活跃值: (2041)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
lihaiyanz 2023-6-6 12:37
3
0
mark
雪    币: 1282
活跃值: (2212)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
vay 2023-6-6 14:09
4
0
mark
雪    币: 3350
活跃值: (3372)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
fengyunabc 1 2023-6-6 15:24
5
0
感谢分享。
雪    币: 245
活跃值: (213327)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
shinratensei 1 2023-6-6 15:40
6
0
tql
雪    币: 573
活跃值: (949)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
ChengQing 2023-6-7 09:00
8
0
马卡巴卡
雪    币: 11999
活跃值: (8094)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Genes 2023-6-12 11:50
9
1

感谢分享。 

指出里面有两个小错误,不过没什么大影响: 

”entryoff 为 以代码段为起始位置 的偏移。表示该函数实际的位置。 代码段在windows为 text“

猛一看还以为是pe的text,其实不是。这个算是moduledata.text的字段。


另一个是:


type _func struct {

    entry   uintptr // start pc

    nameoff int32   // function name

这个字段entry 我看了下 uint32 ,始终是32位的。这个untptr,在64位就是64位了。

https://go.dev/src/runtime/runtime2.go#:~:text=883-,entryOff%20uint32,-//%20start%20pc%2C%20as


type _func struct {
    entryOff uint32 // start pc, as offset from moduledata.text/pcHeader.textStart  
    nameOff  int32  // function name, as index into moduledata.funcnametab.

这个长度要是错的话,后面肯定是计算错的,可能是你粘贴的老版本的吧。

雪    币: 589
活跃值: (1195)
能力值: ( LV5,RANK:65 )
在线值:
发帖
回帖
粉丝
mingyuexc 2023-6-12 14:39
10
0
Genes 感谢分享。&nbsp;指出里面有两个小错误,不过没什么大影响:&nbsp;”entryoff 为 以代码段为起始位置 的偏移。表示该函数实际的位置。 代码段在windows为 text ...
感谢师傅指正,看了下源码确实是更新了,但不影响符号还原。第一个算是没考虑到其他平台,只考虑了windows,就随手写了。
雪    币: 3632
活跃值: (623)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
nszy007 2023-6-21 14:01
11
0
从上面截图上看脚本里moduledata_addr的地址应该是:0x5289A0吧?
不知道是不是我理解错了?
游客
登录 | 注册 方可回帖
返回