一、實例版本
2022.6.8: go version go1.18.3 windows/amd64
高版本適用 1.20
二、還原過程
搜索 go.build


查找交叉引用

go.build 在新版本中一定位于 函數名稱的第一個。

https://go.dev/src/runtime/symtab.go
閱讀源碼,獲取 moduledata 結構
// 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。

適用于下面的結構體:
type functab struct {
entryoff uint32 // relative to runtime.text
funcoff uint32
}

entryoff 為 以代碼段為起始位置 的偏移。表示該函數實際的位置。代碼段在windows為 text。
funcoff 為 以 pclntable 為起始位置的偏移。
//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
通過上圖的信息計算:
hex(0x504320 + 0x2c20) = '0x506f40' hex(0x504320 + 0x2c48) = '0x506f68'
發現剛好可以對得上信息。

第一個函數 go.build

第二個函數 internal_cpu_Initialize

三、輸出腳本
知道了這些就可以編寫簡單的腳本來還原go符號名了。
ida python 腳本
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 需要手動填充。
還原效果

看雪學苑
看雪學苑
看雪學苑
信息安全與通信保密雜志社
信息安全與通信保密雜志社
CNCERT國家工程研究中心
安全牛
黑白之道
E安全
安全圈
一顆小胡椒
一顆小胡椒