26

Go语言逆向去符号信息还原

 5 years ago
source link: http://www.freebuf.com/articles/others-articles/176803.html?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

一、实例

在国内很多朋友都觉得golang的逆向分析很难,其实不然,和其他编程语言相比,go语言的函数太多。但是如果go语言不去符号话,则觉得和.net差不多难度(还是未混淆的)。本文就是要解决golang逆向过程中所遇到去符号化问题。

1 是没有去掉符号信息的反汇编,可以看到 GOLANG 运行时的各函数名 , 第二张图是去掉符号信息的反汇编,GOLANG运行时的函数名都没了(样本准备,最好是在linux下自己编译一个helloworld用于分析对比)。

3qMnUvi.jpg!web

图1 go语言 没有去掉符号信息的反汇编

Y3uaaiE.jpg!web

图2 go语言 去掉符号信息的反汇编

二、分析

这里先感谢 https://rednaga.io/2016/09/21/reversing_go_binaries_like_a_pro/ 链接的作者所提供的的脚本和资料(Reversing GO binaries like a pro)。其提供了golang_loader_assist.py脚本可用于还原符号信息。不过该脚本并不能直接用于windows环境下编译的go程序。在golang_loader_assist.py代码中使用了.gopclntab, .gopclntab 是一个非常重要的 segments 在windows环境下去符号化编译go程序逆向分析时不包含该segments。如图3所示:

VvueeaV.jpg!web

图3 windows环境下去符号化编译go程序 无.gopclntab

图4 linux下没有去除符号信息的go。

7z2AFnY.jpg!web 图4  含有.gopclntab

因此在他人的肩膀上,对于windows 环境go 语言的去符号化还原,核心就是要确认.gopclntab 的位置

1 、首先旅一下 golang_loader_assist.py 代码的流程,之后再说 windows 环境下的 go 符号还原。

先将golang_loader_assist.py中核心代码贴出,如代码1所示:

Line 1: def renamer_init():

Line 2:     renamed = 0

Line 3:     gopclntab =idaapi.get_segm_by_name(‘.gopclntab’)

Line 4:     ifgopclntab is not None:

Line 5:         # Skip unimportant header and gotosection size

Line 6:         addr = gopclntab.startEA + 16

Line 7:         size, addr_size = create_pointer(addr)

Line 8:         addr += addr_size

Line 9:         # Unsure if this end is correct

Line 10:         early_end = addr + (size * addr_size *2)

Line 11:         while addr < early_end:

Line 12:             func_offset, addr_size =create_pointer(addr)

Line 13: name_offset,addr_size = create_pointer(addr + addr_size)

Line 14:             addr += addr_size * 2

Line 15: func_name_addr = Dword(name_offset +gopclntab.startEA + addr_size)                                                                                       +gopclntab.startEA

Line 16:             func_name =GetString(func_name_addr)

Line 17:             MakeStr(func_name_addr,func_name_addr + len(func_name))

Line 18:             appended = clean_func_name =clean_function_name(func_name)

Line 19:             debug(‘Going to remap function at0x%x with %s – cleaned up as %s’ % (func_offset, func_name, clean_func_name))

Line 20:             ifidaapi.get_func_name(func_offset) is not None:

Line 21:                 if MakeName(func_offset,clean_func_name):

Line 22:                     renamed += 1

Line 23:                 else:

Line 24:                     error(‘clean_func_nameerror %s’ % clean_func_name)

Line 25:     return renamed

代码 1 golang_loader_assist.py中核心代码

如图4所示:.gopclntab的start地址为0813a140,下面为程序单步计算过程。

由line 6可以看到 addr = gopclntab.startEA + 16 的位置的值是 3fcc;

由line 13可知 name_offset =3fcc   

由line 15可知 name_offset + gopclntab.startEA+ addr_size = 0813a140+3fcc+4=0813e110

Dword(0813e110)=3ffc

func_name_addr=0813e13c

func_name ==(main.main)

YZ3euiB.jpg!web

图5代码1单步执行结果地址展示

那么如果已知字符串位置,反推.gopclntab是不是也合理呢,答案是一定的。

2 window 环境 go 语言环境去符号化还原:

IDA使用快捷键shif+f12调出字符串窗口搜索runtime或 main一类的关键字,如图6所示:

VzuQjur.jpg!web

图6 ida字符串检索结果

随意点开一个字符串,如runtime.memhash_varlen,如图7 所示:

raQ3IfN.jpg!web

图 7 runtime.memhash_varlen字符串所在地址

runtime.memhash_varlen 的地址是004c1d8c  ,图7中鼠标选择部分是该函数所在的地址,其下方20f6c 为该函数名(runtime.memhash_varlen字符串)相对于.gopclntab的偏移地址。

那么由反推.gopclntab的地址为:004c1d8c-20f6c=004a0e20 如图8 所示:

bAJVbef.jpg!web

图8 .gopclntab的地址

心细的朋友可能早就发现, .gopclntab位置是包含特征码的啊!!!!!! FB FF FF FF 其实就是这么简单。

三、其它提示

a) 修改IDA的segments 新添加的一定要叫.gopclntab,之后便可以使用idapython(ida的插件)

segment name  .goclntab

start address    (search FB FF FF FF)

end address    (.text segment 的尾部)

b)之后IDA加载golang_loader_assist.py,

i. gopclntab =ida_segment.get_segm_by_name(‘.gopclntab’)

if gopclntab is notNone:

ii.             # Skip unimportant header andgoto section size

iii.             addr = gopclntab.startEA + 8

iv.             size, addr_size =create_pointer(addr)

v.              addr += addr_size 这里 addr = gopclntab.startEA + 8  (也有可能是 +16

c) IDA处理go语言的逆向是f5会失效,原因在于每个函数后面都跟随了runtime_morestack或者runtime_morestack_noctxt函数,因此对这两个函数直接字符串化,就可以使用f5了。

d)  runtime_newproc,启动了一个协程,别跟丢。

e) runtime_makechan,go语言比较特殊的channel。

f) golang_loader_assist.py中涉及到IDApython的版本,如果不能直接使用,可将原python文件中的ida_segment .get_segm_by_name替换成idaapi.get_segm_by_name就可以了。其它地方类似将idat_segment,ida_search,ida_funcs,ida_xref换成idaapi)。

四、总结

在很多论坛里很少看到关于go去符号化还原的文章,也经常听周边的人说go语言逆向有多难,可能是被硕大的golang吓到了吧,其实国外的朋友已经解决了该问题,但是由于使用环境差异造成无法复用。

*本文作者Mworld,转载请注明来自FreeBuf.COM


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK