go语言安全与防范

概述

Go(又称为Golang)是一种开源的编程语言,由Google开发,于2009年首次公开发布。它旨在提供简洁、高效、可靠的软件开发解决方案。Golang是一种静态强类型、编译型语言,Golang具有很强的表达能力,得益于其并发机制,用它编写的程序能够非常有效地利用多核与联网的计算机,其新颖的类型系统则使程序结构变得灵活而模块化。 Go 代码编译成机器码不仅非常迅速,还具有方便的垃圾收集机制和强大的运行时反射机制。

Go 语言的反射机制尤其强大,它使得程序可以在运行时动态地操作对象类型和结构。但正是因为这一特性让 Go 语言编译出的可执行文件本身几乎等同于符号文件,反射提供了丰富的类型和函数信息,而这些信息不能通过简单的 strip 命令或者加壳压缩删除。这一特点使得 Go 语言在逆向分析过程中更加容易被分析。

元数据

在Go语言程序中存在一个叫pcHeader 的结构,下面是它的声明描述:

字段名类型值/示例作用说明
magicuint320xFFFFFFF1魔术字/标识符,用于识别有效的 pclntab 头部
pad1uint80x00填充字节,用于内存对齐和未来扩展
pad2uint80x00填充字节,用于内存对齐和未来扩展
minLCuint81(x86)/4(ARM)最小指令大小(字节),用于计算程序计数器偏移
ptrSizeuint84(32位)/8(64位)指针大小(字节),决定如何解释后续指针字段
nfuncintX模块中函数的数量,用于遍历函数表
nfilesuintX文件表中的条目数,表示引用的源文件数量
textStartuintptrX文本段起始地址,函数入口点偏移计算的基地址
funcnameOffsetuintptrX到函数名表的偏移量,包含所有函数名称信息
cuOffsetuintptrX到编译单元表的偏移量,包含编译单元信息
filetabOffsetuintptrX到文件表的偏移量,包含源文件路径信息
pctabOffsetuintptrX到 pctab 表的偏移量,包含PC与行号的映射关系
pclnOffsetuintptrX到 pclntab 本身的偏移量,用于内部引用或验证

通过这个结构体,可以直接找到程序中的函数信息,模块信息等。

Go 运行时的 moduledata 结构体用于记录模块元数据,描述编译模块的静态特征与运行时属性。pcHeader 提供函数地址与源码位置的映射,支持堆栈跟踪与调试;funcnametabfiletab 保存所有函数名和源文件路径,为反射提供底层信息。它还精确标注代码段(text)、数据段(data/bss)与只读段(rodata)的内存范围,维护类型系统相关的 typelinksitablinks,保存垃圾回收所需的 gcdatamask,并通过 inittasks 记录模块初始化依赖。该集中式元数据设计使 Go 运行时具备精确的 GC、高效的反射、完善的栈跟踪和可靠的模块加载能力,下面是这个结构体的声明。

type moduledata struct {
sys.NotInHeap // Only in static data

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
covctrs, ecovctrs     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

// This slice records the initializing tasks that need to be
// done to start up the program. It is built by the linker.
inittasks []*initTask

modulename   string
modulehashes []modulehash

hasmain uint8 // 1 if module contains the main function, 0 otherwise
bad     bool  // module failed to load and should be ignored

gcdatamask, gcbssmask bitvector

typemap map[typeOff]*_type // offset to *_rtype in previous module

next *moduledata
}

这个结构我们就不继续深入,本文的重点是pcHeader,接下来我们会使用python根据pcHeader结构解析GO程序中的函数信息。

实验

环境

平台:Windows x64

语言:Python

三方库依赖:

  • lief

代码

import sys
import lief
import struct

class PCHeader:
   MAGIC_NUMBER = b'\xF1\xFF\xFF\xFF\x00\x00'
   STRUCT_64_FMT = '<IBBBBqQQQQQQQ'
   
   def __init__(self):
       pass

   @property
   def size64(self):
       return struct.calcsize(PCHeader.STRUCT_64_FMT)

   def from64(self,  data:bytes):
       if len(data) < struct.calcsize(PCHeader.STRUCT_64_FMT):
           return False
       
       self.magic, self.pad1, self.pad2,\
       self.minLC, self.ptrSize, self.nfunc,\
       self.nfiles, self.textStart, self.funcnameOffset,\
       self.cuOffset, self.filetabOffset, self.pctabOffset,\
       self.pclnOffset = struct.unpack_from(PCHeader.STRUCT_64_FMT, data)        
       return True
 
class FunctionTableEntry:
   STRUCT_64_FMT = '<II'
   
   def __init__(self):
       pass

   @property
   def size64(self):
       return struct.calcsize(FunctionTableEntry.STRUCT_64_FMT)

   def from64(self,  data:bytes):
       if len(data) < struct.calcsize(FunctionTableEntry.STRUCT_64_FMT):
           return False
       
       self.pc, self.off_info, = struct.unpack_from(FunctionTableEntry.STRUCT_64_FMT, data)        
       return True
   
class FunctionInfo:
   STRUCT_64_FMT = '<III'
   
   def __init__(self):
       pass

   @property
   def size64(self):
       return struct.calcsize(FunctionInfo.STRUCT_64_FMT)

   def from64(self,  data:bytes):
       if len(data) < struct.calcsize(FunctionInfo.STRUCT_64_FMT):
           return False
       
       self.pc, self.off_name,self.args_size = struct.unpack_from(FunctionInfo.STRUCT_64_FMT, data)        
       return True

class GOFunction:
   def __init__(self, rva = 0, name =''):
       self.rva = rva
       self.name = name


class GOParser(object):
   def __init__(self):
       self.pe = None
       self.go_functions:list[GOFunction] = []
       pass

   def dump_meta_data(self):
       for f in self.go_functions:
           print(f'name:{f.name:<120} pc: {hex(f.rva )}')

   def parse(self, path):
       self.pe = lief.parse(path)
       if not self.pe or self.pe.format != lief.Binary.FORMATS.PE or self.pe.header.machine != lief.PE.Header.MACHINE_TYPES.AMD64:
           print('Invalid PE file or unsupported machine type.')
           return False
       
       if not self._find_pc_header():
           print('PC header not found; this may not be a Go program.')
           return False

       if not self._parse_pc_header():
           print('Failed to parse PC header.')
           return False

       return True

   def _parse_pc_header(self):
       pc_ehader = self.pcheader
       pc_ehader_rva = self.pcheader_rva
       functable_rva = pc_ehader_rva + pc_ehader.pclnOffset
       funcname_rva = pc_ehader_rva + pc_ehader.funcnameOffset
       text_start = pc_ehader.textStart
       
       function_table:list[FunctionTableEntry] = []

       for i in range(pc_ehader.nfunc):
           func_entry = FunctionTableEntry ()
           func_entry_buffer = self._read_bytes(functable_rva + i * 8, func_entry.size64)
           func_entry.from64(func_entry_buffer)
           function_table.append(func_entry)

       for e in function_table:
           func_info = FunctionInfo()
           func_info_buffer = self._read_bytes(functable_rva + e.off_info, func_info.size64)
           func_info.from64(func_info_buffer)

           func_rva = (text_start + func_info.pc) - self.pe.optional_header.imagebase
           name_rva = funcname_rva + func_info.off_name
           name = self._read_c_string(name_rva)

           go_func = GOFunction(func_rva, name)
           self.go_functions.append(go_func)

           pass

       return True

   def _read_qword(self, rva):
       return self._read_bytes(rva, 8)

   def _read_c_string(self, rva):
       string = ''
       while True:
           byte = self._read_bytes(rva, 1)
           if not byte:
               return ''
           ch = byte[0]
           if ch == 0:
               break

           string += chr(ch)
           rva += 1

       return string
       

   def _read_bytes(self, rva, size):
       s = self.pe.section_from_rva(rva)
       if not s :
           return None
       offset = rva - s.virtual_address
       remain_bytes = s.sizeof_raw_data - offset
       if size > remain_bytes:
           return None
       return s.content.tobytes()[offset:offset+size]
   
   def _find_pc_header(self):
       for s  in self.pe.sections:
           offset = s.search(PCHeader.MAGIC_NUMBER)
           if offset:
               self.pcheader_rva = s.virtual_address + offset
               self.pcheader = PCHeader()
               pcheader_buffer = self._read_bytes(self.pcheader_rva, self.pcheader.size64)
               if not pcheader_buffer:
                   return False
               
               if not self.pcheader.from64(pcheader_buffer):
                   return False
               return True
           
       return False
       


def main():

   pe_file = 'hello.exe'
   parser = GOParser()
   if not parser.parse(pe_file):
       print('Failed to parse the program.')
       return 1
   parser.dump_meta_data()


   return 0

if __name__ == '__main__':
   sys.exit(main())

效果

安全防范

Virbox Protector 为 Native 应用提供强有力的安全防护,核心功能是对应用中的函数进行虚拟化保护。这项技术通过将原本的原生代码转换为定制的虚拟机指令,在 Native 层通过虚拟机进行解释执行,从而有效地防止逆向工程和代码还原。

对于 Go(Golang)程序,虽然它是静态编译的原生应用,但依旧存在被逆向分析的风险。与其他原生应用类似,Go 程序的函数、类型信息和堆栈信息在二进制中可能会暴露。通过 Virbox Protector 对 Go 程序进行加密保护、虚拟化函数、反调试和反注入等措施,可以进一步提升 Go 应用的安全性,防止恶意攻击和破解。

此外,Virbox Protector 还提供了多种高级保护机制,包括字符串加密、文件加密、反调试、反注入、签名验证和文件完整性检测等,全面增强了应用的安全性和抗破解能力。同时,它还具备防截屏、模拟器检测、Root 检测和多开检测等功能,从多个维度提升了 Native 应用的防护层级,确保应用在复杂环境中的安全性。

通过这些综合防护手段,Virbox Protector 能够为开发者提供一站式的 Native 应用安全解决方案,尤其适用于 Go 程序,有效保护其免受恶意攻击与破解。

滚动至顶部
售前客服
周末值班
电话

电话

13910187371