Go语言逆向去符号信息还原 – 作者:Mworld

一、实例

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

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

image.png

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

image.png

图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所示:

image.png

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

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

image.png图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)

image.png

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

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

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

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

image.png

图6 ida字符串检索结果

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

image.png

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

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

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

image.png

图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

来源:freebuf.com 2018-08-02 23:00:47 by: Mworld

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享
评论 抢沙发

请登录后发表评论