😇
牛牛的安全 Odin
  • 个人介绍
  • 数据安全
  • 工控安全
    • 工控概念
  • 车联网安全合规
    • R155
    • CSMS\VTA
    • GDPR认证
  • 车联网安全
    • 漏洞订阅
    • 汽车攻击时间轴
    • 汽车信息安全研究
      • 车厂安全需求 Custom Requirement
      • 安全威胁
      • 参考文章
      • Who’s Behind the Wheel?
      • 安全研究基础
      • 智能网联汽车安全渗透指标
      • 智能网联汽车软件安全测试关键技术研究
      • 基于硬件在环的整车控制器功能安全测试技术研究
      • 智能网联汽车信息安全解决方案
      • 自动驾驶汽车的安全性-识别挑战
    • ECU逆向案例
      • 特斯拉攻击链
      • 汽车动力系统ECU固件逆向工程初探
  • 物联网安全
    • IoT 技术和协议
    • 智能设备常规测试思路总结
    • 各种调试接口(SWD、JTAG、Jlink、Ulink、STlink)的区别
    • QEMU 系统仿真
      • 如何“用 QEMU 模拟它”
      • 处理加密的路由器固件
    • 自动分析Automated Approach
    • IOT渗透测试(一)
    • 物联网安全目录
  • 固件分析案例
    • 智能门锁、手环
      • MCU固件反汇编
      • 云丁鹿客门锁中bootloader和FreeRTOS的分析
      • 云丁鹿客门锁BLE通信的分析(下)
      • 云丁鹿客门锁BLE通信的分析(中)
      • 云丁鹿客门锁BLE通信的分析(上)
      • 华为智联旗下小豚AI摄像头的完整分析(下)
      • 华为智联旗下小豚AI摄像头的完整分析(上)
      • 海康萤石智能门锁的网关分析(4)
      • 海康萤石智能门锁的网关分析(3)
      • 海康萤石智能门锁的网关分析(2)
      • 海康萤石智能门锁的网关分析(1)
      • idapython编写和调试
      • 果加智能门锁的全面分析(下)
      • 果加智能门锁的全面分析(中)
      • 果加智能门锁的全面分析(上)
      • BLE智能手环
      • 耶鲁智能门锁的简单测试(下)
      • 耶鲁智能门锁的简单测试(上)
      • 耶鲁门锁漏洞
      • 对一款BLE灯泡的分析
      • BLE协议栈与Android BLE接口简介
    • 在IoT设备中查找端口对应进程的四种方法
    • 路由器命令执行
    • 对基于Philips TriMedia CPU的网络摄像机进行逆向工程
    • 从Microsoft Band以及 Hello Sense 设备中提取自己的历史数据
    • CVE-2021-22909- 深入研究 UBIQUITI 固件更新错误
    • 复现|摄像头固件重打包
    • Dlink_DWR-932B路由器固件分析
    • 针对小米九号平衡车的无接触式攻击
    • 记一次智能印章设备的漏洞挖掘
  • APP 逆向
    • Go二进制文件逆向分析从基础到进阶——综述
    • Switch APP逆向分析
  • 传统静态代码分析
    • 静态分析案例
      • ELF恶意软件的静态分析原理和方法(上)
      • ELF恶意软件的静态分析原理和方法(下)
    • 静态代码分析工具清单
    • 企业级静态代码分析工具清单
  • 应用安全测试
    • DAST、SAST、IAST
    • IAST 工具初探
  • 芯片架构
    • ARM指令集概念
    • ARM指令集
    • 冯·诺伊曼结构
    • 指令集
    • 处理器架构、指令集和汇编语言,三者有何关系?
  • 病毒分析
    • 熊猫烧香
  • 编程知识
    • REST API 教程
  • 流量分析工具
    • 卡巴斯基开源的智能手机流量劫持工具
    • 利用 Burp Suite 劫持 Android App 的流量(二)
    • 利用 Burp Suite 劫持 Android App 的流量(一)
  • 区块链安全
    • 安全多方计算
    • Chainalysis 团队从区块链的角度分析发现 2020 年最大的 4 起勒索软件攻击实现存在关联
  • 攻击案例
    • 特斯拉Powerwall网关可能受到黑客攻击
  • 移动应用
    • Mac上使用Charles抓包
    • 手机抓包工具汇总
    • APP渗透测试流程和技巧大全
    • 加壳和脱壳
    • 浅谈 Android Dex 文件
    • 移动应用漏洞分析样例分享
    • 移动应用常见漏洞分析
    • 移动应用漏洞分析工具介绍
    • 渗透测试流程详解 及 移动APP安全测试要点
    • Frida Android hook
  • 安全设计
    • 【软件安全设计】安全开发生命周期(SDL)
Powered by GitBook
On this page
  • 1. 概述
  • 2. 典型的恶意程序
  • 3. 已有研究与工具
  • 4. 原理初探
  1. APP 逆向

Go二进制文件逆向分析从基础到进阶——综述

https://www.anquanke.com/post/id/214940

PreviousAPP 逆向NextSwitch APP逆向分析

Last updated 3 years ago

1. 概述

Go 语言是一个比较新的强类型静态语言,2009 年由 Google 发布,在 2012 年才发布首个稳定版。Go 语言靠 Goroutine 和 channel、wait group、select、context 以及 sync 等辅助机制实现的 CSP 并发模型,以简洁高效编写高并发程序为亮点,很快就在全球范围内吸引大量开发者使用其编写各种程序。现在 Go 已成为云原生领域的首选开发语言。

由于 Go 语言上手快,编码效率高,程序运行效率也高,而且很方便跨平台交叉编译,也吸引了恶意软件开发者的注意。渐渐地,安全厂商开始捕获到越来越多的 ,这些恶意软件横跨 Windows、Linux 和 Mac 三大平台。

然而,Go 语言的编译工具链会全静态链接构建二进制文件,把标准库函数和第三方 package 全部做了静态编译,再加上 Go 二进制文件中还打包进去了 runtime 和 GC(Garbage Collection,垃圾回收) 模块代码,所以即使做了 strip 处理( go build -ldflags "-s -w" ),生成的二进制文件体积仍然很大。在反汇编工具中打开 Go 语言二进制文件,可以看到里面包含动辄几千个函数。再加上 Go 语言的独特的函数调用约定、栈结构和多返回值机制,使得对 Go 二进制文件的分析,无论是静态逆向还是动态调式分析,都比分析普通的二进制程序要困难很多。

不过,好消息是安全社区还是找到了针对性的方法,让安全分析人员对 Go 语言二进制文件的逆向变得更加轻松。最开始有人尝试过针对函数库做符号 Signature 来导入反汇编工具中,还原一部分二进制文件中的函数符号。后来有人研究出 Go 语言二进制文件内包含大量的运行时所需的符号和类型信息,以及字符串在 Go 二进制文件中独特的用法,然后开发出了针对性的符号甚至类型信息恢复工具。至此,对 Go 语言二进制文件的逆向分析工作,就变得轻松异常了。

本系列文章将简单介绍 Go 语言二进制文件逆向姿势的发展历史,以及几个典型的恶意程序家族。然后详细介绍基于二进制文件内置的符号、类型信息来逆向分析 Go 语言二进制文件的技术原理和工具,最后以实际的分析案例演示前面介绍的工具和方法。至于还有人研究出的高深技术,来恢复 Go 语言二进制文件的符号和类型信息的姿势,不在本文讨论范围之内。因为鄙人也没研究明白

2. 典型的恶意程序

早在 2012 年,Symantec(现已被博通收购)就曝光了一个 Go 语言编写的 Windows 平台上的恶意软件: ,这是鄙人能查到的最早的 Go 编写的恶意软件。当时,这个恶意软件在业内并没引起多大注意。

到了 2016 年 8 月,Go 编写的两个恶意软件被俄罗斯网络安全公司 Dr.Web 曝光,在业内吸引了很多注意: 和 。前者后来发展成臭名昭著的 ,后者则是史上第一个 Go 编写的 P2P Botnet(基于 )。从公开的信息来看,正是从这时开始,业内的安全研究人员开始对 Go 二进制文件的逆向分析进行初步探索。

2017 年,TrendMicro 曝光了一个 ,他们用到的一个核心的数据窃取工具 DRIGO,即是用 Go 语言编写。2019 年 ,分析了 APT28 组织用到的知名后门工具 Zebrocy ,也有了 Go 语言版本。这也说明 Go 语言编写的木马越来越成熟,Go 语言开始被大型黑客组织纳入编程工具箱。

再往后,Go 语言编写的恶意软件就呈泛滥的态势了。2020 年初,鹅厂还曝光过一个功能比较复杂的跨平台恶意挖矿木马 ,也是由 Go 语言编写。就在最近, Guardicore 刚爆光了一个 。

3. 已有研究与工具

前文说过,Go 语言二进制文件有它自己的特殊性,使得它分析起来跟普通的二进制文件不太一样。主要有以下三个方面:

  1. Go 语言内置一些复杂的数据类型,并支持类型的组合与方法绑定,这些复杂数据类型在汇编层面有独特的表示方式和用法。比如 Go 二进制文件中的 string 数据不是传统的以 0x00 结尾的 C-String,而是用 (StartAddress, Length) 两个元素表示一个 string 数据;比如一个 slice 数据要由 (StartAddress, Length, Capacity) 三个元素表示。这样的话,在汇编代码中看,给一个函数传一个 string 类型的参数,其实要传两个值;给一个函数传一个 slice 类型的参数,其实要传 3 个值。返回值同理;

  2. 独特的调用约定和栈管理机制,使 C/C++ 二进制文件逆向分析的经验在这里力不从心:Go 语言用的是 ,并且 Go 语言函数中 callee 的栈空间由 caller 来维护,callee 的参数、返回值都由 caller 在栈中预留空间,就难以直观看出哪个是参数、哪个是返回值。详见 ;

  3. 全静态链接构建,里面函数的数量动辄大几千,如果没有调试信息和符号,想静态逆向分析其中的特定功能点,如大海捞针,很容易迷失在函数的海洋中;动态调试难度更大,其独特的 Goroutine 调度机制再加上海量的函数,很容易调飞。

由于恶意软件大都是被 strip 处理过,已经去除了二进制文件里的调试信息和函数符号,所以 Go 二进制文件的逆向分析技术的探索,前期主要围绕着函数符号的恢复来展开。

值得一提的是, golang_loader_assist 诞生时,旧版的 IDAPro 对 Go 二进制中的函数识别效果并不是很好,很多函数体识别不全,导致 IDAPro 的自动分析能分析出的函数量有限。所以 golang_loader_assist 实现了一种依靠 Go 语言中 连续栈(Continue Stack) 维护的机制来解析、标注函数体的功能。具体做法是靠汇编代码的特征来找出 runtime_morestack 和 runtime_morestack_noctxt 函数,然后在 IDAPro 种遍历对这两个函数交叉引用的位置来找出函数体。这是一个无奈之举,IDAPro v7.x 的版本对 Go 二进制文件中函数体的自动解析功能加强了很多,绝大部分函数都可以被识别出来,无需自己费劲去识别、解析函数体。

再进一步,有安全研究员发现除了可以从 pclntab 结构中解析、恢复函数符号,Go 二进制文件中还有大量的类型、方法定义的信息,也可以解析出来。这样就可以大大方便对 Go 二进制文件中复杂数据结构的逆向分析。两个代表工具:

IDAGolangHelper 对 Go 的不同版本做了更精细化处理,而且第一次在 Go 二进制文件解析中引入 moduledata 这个数据结构。而且提供一个 GUI 界面给用户提供丰富的操作选项,用户体验更胜一筹。

不过 IDAGolangHelper 的缺点也非常明显:

  1. 支持的 Golang 版本略旧。目前最高支持 Go 1.10,而最新的 Go 1.15 已经发布了。 Go 1.2 之后这些版本之间的差异并不大,所以这也不是个太大的问题;

  2. 太久不更新,目前在 IDAPro v7.x 上已经无法顺利执行,这个问题比较严重;

> PACKAGE: net/http:

    > struct http.Request (5 fields):
        - string Method (offset:0)
        - *url.URL URL (offset:10)
        - string Proto (offset:18)
        - int ProtoMajor (offset:28)
        - int ProtoMinor (offset:30)

> PACKAGE: net/url:

    > struct url.URL (9 fields):
        - string Scheme (offset:0)
        - string Opaque (offset:10)
        - *url.Userinfo User (offset:20)
        - string Host (offset:28)
        - string Path (offset:38)
        - string RawPath (offset:48)
        - bool ForceQuery (offset:58)
        - string RawQuery (offset:60)
        - string Fragment (offset:70)

    > struct url.Userinfo (3 fields):
        - string username (offset:0)
        - string password (offset:10)
        - bool passwordSet (offset:20)

jeb-golang-analyzer 也有一些问题:对 strings 和 string pointers 的解析并不到位,虽然支持多种 CPU 架构类型(x86/ARM/MIPS)的字符串解析,但是 Go 二进制文件中字符串的操作方式有多种,该工具覆盖不全。另外,该工具内部定位 pclntab 的功能实现,基于 Section Name 查找和靠 Magic Number 暴力搜索来结合的方式,还是可能存在误判的可能性,一旦发生误判,找不到 pclntab 结构,至少会导致无法解析函数名的后果。最后,这个工具只能用于 JEB,而对于用惯了 IDAPro 的人来说,JEB 插件的解析功能虽强大,但在 JEB 中展示出来的效果并不是很好,而且 JEB 略卡顿,操作体验不是很好。

$ redress -interface pplauncher
type error interface {
    Error() string
}

type interface {} interface{}

type route.Addr interface {
    Family() int
}

type route.Message interface {
    Sys() []route.Sys
}

type route.Sys interface {
    SysType() int
}

type route.binaryByteOrder interface {
    PutUint16([]uint8, uint16)
    PutUint32([]uint8, uint32)
    Uint16([]uint8) uint16
    Uint32([]uint8) uint32
    Uint64([]uint8) uint64
}

或者打印 Go 二进制文件中的一些 Struct 定义以及绑定的方法定义:

$ redress -struct -method pplauncher
type main.asset struct{
    bytes []uint8
    info os.FileInfo
}

type main.bindataFileInfo struct{
    name string
    size int64
    mode uint32
    modTime time.Time
}
func (main.bindataFileInfo) IsDir() bool
func (main.bindataFileInfo) ModTime() time.Time
func (main.bindataFileInfo) Mode() uint32
func (main.bindataFileInfo) Name() string
func (main.bindataFileInfo) Size() int64
func (main.bindataFileInfo) Sys() interface {}

type main.bintree struct{
    Func func() (*main.asset, error)
    Children map[string]*main.bintree
}

4. 原理初探

前文盘点了关于 Go 二进制文件解析的已有研究,原理层面都是一句带过。可能很多师傅看了会有两点疑惑:

  1. 为什么 Go 二进制文件中会有这么多无法被 strip 掉的符号和类型信息?

  2. 具体有哪些可以解析并辅助逆向分析的信息?

第一个问题,一句话解释就是,Go 二进制文件里打包进去了 runtime 和 GC 模块,还有独特的 Type Reflection(类型反射) 和 Stack Trace 机制,都需要用到这些信息。参考前文 redress 报错的配图,redress 本身也是 Go 语言编写,其报错时打出来的栈回溯信息,除了参数以及参数地址,还包含 pkg 路径、函数信息、类型信息、源码文件路径、以及在源码文件中的行数。

至于内置于 Go 二进制文件中的类型信息,主要为 Go 语言中的 Type Reflection 和类型转换服务。Go 语言内置的数据类型如下:

而这些类型的底层实现,其实都基于一个底层的结构定义扩展而来:

再加上 Go 允许为数据类型绑定方法,这样就可以定义更复杂的类型和数据结构。而这些类型在进行类型断言和反射时,都需要对这些底层结构进行解析。

第二个问题,对于 Go 二进制文件中,可以解析并对逆向分析分析有帮助的信息,我做了个列表,详情如下:

且看下回分解。

最早是有人尝试过为函数符号做 Signature ,然后把 Signature 导入到反汇编工具里的做法。这是一个 Hard Way 的笨办法,比较原始,但挺实用。r2con 2016 上 zl0wram 的议题《》中就演示过这种做法:

进一步,大家发现了隐藏在 Go 二进制文件种 pclntab 结构中的函数名信息,并没有被 strip 掉,而且可以通过辅助脚本在反汇编工具里将其恢复。比如 RedNaga 的 @timstrazz 写了一篇 ,详细讲述了如何从被 strip 的 Go 二进制文件中恢复函数符号以及解析函数中用到的字符串,让 IDAPro 逆向 Go 二进制文件变得更轻松。 还开源了他写的 IDA 脚本 。

用于 radare2 的 ,由上面提到的 zl0wram 在 r2con 2016 上发布;

用于 IDAPro 的 ,以及基于 开发的更强的 。

前文提到的 可能是业内最火的介绍 Go 二进制逆向的文章,但最火的工具可能还是 IDAGolangHelper :

其内部有个独特的做法:把 Go 语言各种数据类型的底层实现,在 IDAPro 中定义成了相应的 ida_struct。这样一来,即使可以顺利在 IDAPro 中解析出各种数据类型信息,展示出来的效果并不是很直观,需要查看相应的 struct 定义才能理解类型信息中各字段的意义,而且不方便跳转操作。窃以为这种体验并不好:

更进一步,2019 年 10 月份,JEB 官方博客发表一篇文章 《》,并发布一个 JEB 专用的 Go 二进制文件解析插件 。这是一个功能比前面几个工具更加完善的 Go 二进制文件解析工具,除了解析前面提到的函数名、字符串和数据类型信息,还会解析 、Source File Path list、GOROOT 以及 Interface Table 等信息。甚至会把每个 pkg 中定义的特定数据类型分门别类地列出来,比如解析某 Go 二进制文件中的部分类型信息:

另外,还有一个非典型的 Go 二进制文件解析工具:基于 的 。 GoRE 是一个 Go 语言编写的 Go 二进制文件解析库,redress 是基于这个库来实现的 Go 二进制文件解析的 命令行工具。redress 的强大之处,可以结构化打印 Go 二进制文件中各种详细信息,比如打印 Go 二进制文件中的一些 Interface 定义:

这些能力,都是上面列举的反汇编工具的插件难以实现的。另外,用 Go 语言来实现这种工具有天然的优势:可以复用 Go 语言开源的底层代码中解析各种基础数据结构的能力。比如可以借鉴 中的代码来解析 pclntab 结构,可以借鉴 中的代码来解析 moduledata 结构,以及借鉴 中的代码来解析各种数据类型的信息。

redress 是一个接近极致的工具,它把逆向分析需要的信息尽可能地都解析到,并以友好的方式展示出来。但是它只是个命令行工具,跟反汇编工具的插件相比并不是很方便。另外,它目前还有个除 jeb-golang-analyzer 之外以上工具都有的缺点:限于内部实现的机制,无法解析 buildmode=pie 模式编译出来的二进制文件。用 redress 解析一个 二进制文件,报错如下:

最后,是鄙人开发的一个 IDAPro 插件: ,该工具除了拥有以上各工具的绝大部分功能(strings 解析暂时只支持 x86 架构的二进制文件,这一点不如 jeb-golang-analyzer 支持的丰富),还支持对 PIE 二进制文件的解析。另外会把解析结果以更友好、更方便进一步操作的方式在 IDAPro 中展示。以 DDG 样本中一个复杂的结构体类型为例,解析结果如下:

后文会以这张图为大纲,以 为例,详细讲解如何查找、解析并有组织地展示出这些信息,尽最大可能提升 Go 二进制文件逆向分析的效率。

Reversing Linux Malware
Reversing GO binaries like a pro
@timstrazz
golang_loader_assist
r2_go_helper
GoUtils
GoUtils 2.0
IDAGolangHelper
Reversing GO binaries like a pro
Analyzing Golang Executables
jeb-golang-analyzer
Duff’s device
GoRE
redress
src/debug/gosym/pclntab.go
src/runtime/symtab.go
src/reflect/type.go
PIE(Position Independent Executable)
go_parser
go_parser
Go 语言编写的恶意软件
基于符号执行和形式化定理证明
Encriyoko
Linux.Lady
Linux.Rex
DDGMiner
DHT
大型的黑客团伙 BlackTech
ESET 发布一篇报告
SysupdataMiner
Go 编写的功能复杂的 P2P Botnet: FritzFrog
continue stack 栈管理机制
The Go low-level calling convention on x86-64