Go 语言,如何做逆向类型推导?

go 初级逆向分析(一) Go 语言是一个比较新的强类型静态语言,2009 年由 Google 发布,在 2012 年才发 阅读详情

dcb911c0a4ebec5cf79384a2f7afe703.gif

文|朱德江(GitHub ID:doujiang24)

MOSN 项目核心开发者

蚂蚁集团技术专家

a37d071aa3f01586b41e615538515430.png

专注于云原生网关研发的相关工作。

本文 2241 字 阅读 8 分钟

PART. 1

引言

在上回的文章《Go 内存泄漏,pprof 够用了么?》中说到,从一个 core 文件生成内存引用关系火焰图时,虽然可以从 core 文件中读到所有的内存对象,但是并不知道它们的类型信息。

这是因为 Go 作为静态类型语言,在运行时,内存对象的类型是已知的。也就是说,并不需要想动态类型语言那样,为每个内存对象在内存中存储其类型信息(有点例外的是 interface)。

比如这个 Go 语言例子:

type Foo struct {
    a uint64
    b int64
}


func foo(f *Foo) int64 {
    return f.b
}

Foo 函数在使用 f 这个指针时,并不需要判断其类型,直接读一个带偏移量地址就能得到 f.b,也就是一条指令:mov rax, qword ptr [rax + 8],就是这么简单直接。

再看 Lua 语言这个例子:

function foo(f)
    return f.b
end
foo({ b = 1 })

Foo 函数在执行的时候,首先得判断 f 的类型,如果是 table,则按照 key 取 b 的值;如果不是,则抛运行时 error。

能够运行时判断 f 的类型,是因为 Lua 中变量是用 TValue 来表示的,这个 TValue 结构中,就有一个信息用来存储变量类型。

PART. 2

逆向类型推导

逆向类型推导的逻辑是:根据已知内存的类型信息,推导被引用的内存对象的类型信息。

比如这个例子:

type Foo struct {
    a uint64
    b int64
}
type Bar struct {
    f *Foo
}
var b Bar

如果我们知道了 b 的类型是 Bar,那么 b  中第一个 field 指向的内存对象,就是 Foo 类型了(前提是合法的内存对象地址)。

既然存在推导,那我们怎么知道一些初始值呢?

一共有两类来源:

1.全局变量;

2.协程中每一帧函数的局部变量。

PART. 3

全局变量

Go 在编译的时候,默认会生成一些调试信息,按照 DWARF 标准格式,放在 ELF 文件中 .debug_* 这样的段里。

这些调试信息中,我们关注两类关键信息:

  1. 类型信息:包括了源码中定义的类型,比如某个 struct 的名字、大小、以及各个 field 类型信息;

  2. 全局变量:包括变量名、地址、类型,调试信息中的、全局变量的地址、以及其类型信息,也就是构成推导的初始值。

函数局部变量,要复杂一些,不过基本原理是类似的,这里就不细说了~

PART. 4

推导过程

推导过程,跟 GC-Mark 的过程类似,甚至初始值也跟 GC-Root 一样。

所以,全部推导完毕之后,GC-Mark 认为是 alive 的内存对象,其类型信息都会被推导出来。

interface

Go 语言中 interface 比较类似动态类型,如下是空接口的内存结构,每个对象都存储了其类型信息:

type eface struct {
    _type *_type
    data  unsafe.Pointer
}

按照类型推导,我们能知道一个对象是 interface{},但是其中 Data 指向对象,是什么类型,我们则需要读取 _type 中的信息了。

_type 中有两个信息,对我们比较有用:

1.名字

不过比较坑的是,只存了 pkg.Name 并没有存完整的 Include Path 这个也合理的,毕竟 Go 运行时并不需要那么精确,也就是异常时,输出错误信息中用一下。不过在类型推导的时候,就容易踩坑了。

2.指针信息

具体存储形式有点绕,不过也就是表示这个对象中,有哪些偏移量是指针。

有了这两个信息之后,就可以从全量的类型中,筛选出符合上面两个信息的类型。

通常情况下,会选出一个正确的答案,不过有时候选出多个,仅仅根据这两个信息还不能区分出来,一旦一步错了,后面可能就全推导不出来了。

我们给 Go 官方 Debug 贡献了一个补丁,可以进一步的筛选,有兴趣的可以看 CL 419176[1]。

unsafe.pointer

其实,在上面的 interface 示例中,最根源的原因,也就是 data unsafe.pointer,这个指针并没有类型信息,只是 interface 的实现中,有另外的字段来存储类型信息。

不过,在 Go Runtime 中还有其它的 unsafe.pointer,就没有那么幸运了。

比如 map 和 sync.map 的实现都有 unsafe.pointer,这种就没有办法像 interface 那样统一来处理了,只能 case-by-case,根据 map/sync.map 的结构特征来逆向写死了...

我们给 Go 官方 Debug 贡献了 sync.map 的逆向实现,有兴趣的可以看 CL 419177[2]。

PART. 5

隐藏类型

除了源码中显示定义的类型,还有一些隐藏的类型,比如:Method Value、Closure 的实现中,也都是用 struct 来表示的,这些属于不太容易被关注到的“隐藏”类型。

Method Value 在逆向推导中,还是比较容易踩坑的,我们给 Go 官方 Debug 贡献了这块的实现,有兴趣的可以看 CL 419179[3]。

相比 Method Value 这种固定结构的,Closure 这种会更难搞一些,不过幸运的是,我们目前的使用过程中,还没有踩坑的经历。

PART. 6

逆向推导风险

这种逆向推导要做到 100% 完备还是挺难的,根本原因还是 unsafe.pointer。

在 reflect.Value  中也有 unsafe.pointer,据我所知,这个是还没有逆向推导实现的,类似的应该也还有其它未知的。

甚至,如果是标准库中的类型,我们还是可以一个个按需加上,如果是上层应用代码用到的 unsafe.pointer,那就很难搞了。

还有一种可能,推导不出来的原因,就是内存泄漏的来源,我们就碰到这样一个例子,以后有机会再分享~

幸运的是:如果是只是少量的对象没有推导出来,对于全局内存泄漏分析这种场景,通常影响其实也不大。

另外,对于一个对象,只需要有一个路径可以推导出来也就够了。

也就是说,如果一条推导线索因为 unsafe.pointer 断了,如果另外有一个线索可以推导到这个对象,那也是不影响的。因为从 GC root 到一个 GC obj 的引用关系链,可能会不止一条。

PART. 7

小结

Go 虽然是静态类型语言,不过由于提供了 unsafe.pointer,给逆向类型推导带来了很大的麻烦。好在 Go 对于 unsafe.pointer 的使用还是比较克制,把标准库中常用到的 unsafe.pointer 搞定了,基本也够用了。

理论上来说,逆向推导这一套也适用于 C 语言,只不过 C 语言这种指针漫天飞的,动不动就来个强制类型转换,就很难搞了。

|相关链接|

[1]CL 419176:
https://go-review.googlesource.com/c/debug/+/419176

[2]CL 419177:

https://go-review.googlesource.com/c/debug/+/419177

[3]CL 419179:

https://go-review.googlesource.com/c/debug/+/419179

了解更多...

MOSN Star 一下✨:
https://github.com/mosn/mosn

推荐观看...

  本周推荐阅读  

e29ca97bc6e0ea897dbb2285ec8d0d2b.jpeg

MOSN 反向通道详解

4d0619e716676ad5a5c551b5732a2486.png

Go 原生插件使用问题全解析

c8c8548aa54112a9cf8fcd2c510005d0.png

Go 内存泄漏,pprof 够用了么?

2ca45486bc06eba95de09b3d676e233f.png

MOSN 文档使用指南

4ae70240cca649052208cd38e13cd0e5.jpeg

go 初级逆向分析(二) 我们尝试编写一些go的程序并逆向。 阅读详情

相关推荐

企业微信iPad协议联系人模块开发:从抓包分析到Go语言实现

在企业级应用集成中,通过官方API获取组织架构和成员信息是常见需求。然而,当面对高实时性、深度客户端集成或官方API未覆盖的细粒度操作时,开发者可能需要探索更底层的通信协议。协议分析与模拟技术,如网络抓包和二进制序列化解析,为解决这类问题提供了可能。其核心价值在于能够实现更贴近客户端视角的数据同步,满足智能办公平板、定制化CRM等特殊场景的需求。本文聚焦于企业微信iPad客户端联系人模块的协议调用,详细阐述了从环境搭建、抓包分析到使用Go语言模拟请求的完整流程,并深入探讨了Protobuf序列化、会话维持等

weixin_30412167的博客 489

逆向一个Go程序

用IDA、x64dbg逆向一个自己写的Go程序,小白也能看懂。

Go的全部 5442

【Go】Go语言切片(Slice)深度剖析与应用实战

在Go语言中,切片(Slice)是一种非常强大且灵活的数据结构,它基于数组但又提供了动态调整大小的能力。切片在Go语言中非常常用,几乎成为处理序列数据的首选方式。本文将结合实际案例,详细介绍Go语言中切片的声明、初始化、操作、扩容等用法。

景天科技苑 4万+

某颜色软件加解密函数逆向分析【初尝go语言逆向】

加解密函数逆向分析、go语言逆向、脱机执行

头铁逆向的旅程 4003

前言:Go 文档之外,学习 Go 会遇到哪些问题?

我一直从事服务器后端系统开发快 10 年了,高并发分布式是互联网应用的基础,工作中先后使用过C#、Java、Go 等语言。当前工作需要把平台迁移到 Go,从零开始构建基础设施框架,在基础设施框架上开发各种业务系统。在迁移过程中逐步自研了一套功能完善的基础框架,同时也对语言的一些特性有了更深入的了解,所以便有了这个专栏。 这个专栏的目的是如何把一门语言学精通了,什么叫精通,就是该会的东西都会了,让读...

YJingLee's 优快云 Blog 367

go语言 代码整理

go语言代码整理,很好很全,是很好的学习资料,共享给大家!

go语言逆向学习笔记(二)函数

调试过程中,从内存的字符串也能看出,编译时并未把happy函数当做独立的函数来出处理,而是直接将源代码中happy函数的代码写入main函数,作为一个统一的函数来处理。ida的函数识别只识别出来了hello函数,并未识别happy函数,应该是hello函数中用了go关键字,新开了一个协程。此时我们用指针来调用函数,ida就能识别出touful函数了,也就是说,编译时给touful函数单独划分了栈帧。至于指针,如图就是用指针调用函数的过程,可以看出,总的来说,go的指针是比较方便使用的。

m0_75098930的博客 476

代码实现ROS2bag录制数据和回放数据(C++)

ROS2的ROS2bag功能可以将指定Topic中的数据以.db3的格式(不同于ROS)保存到本地文件中。读取文件数据可以实现数据回放。

m0_57124501的博客 3256

编程语言底层之数据结构

课程简介 本课程从开发人员需要了解的基础数据结构出发,带着这些基础知识去学习一门编程语言是怎么实现的(内含视频演示),并且思考为什么这么实现,反过来再系统完善知识体系,并运用到实际工作中。通过这门课的学习将体验思考的过程,以便后面更加深入的了解学习其他开发语言知识。 相关联的系列达人课:《编程语言底层之函数执行》和《编程语言底层之系统和并发》。 作者介绍 李永京,从事互联网后端系统开发,擅长高并发...

GitChat 1824

AD17入门简单教程(一)

AD17入门简单教程 我写这篇文章主要是因为最近刚学习完了AD17,想做一下总结,也希望通过自己心得的帮助一些刚入门AD17的小白。 总结一共分为两大部分,包括AD17的下载安装与汉化(文末有详细链接)、工程的创建、元器件库、PCB、走线中的常用命令介绍,文末还附有完整的PCB工程以及讲解视频。 第一部分工程的创建及元器件库常用命令 1.首先在桌面上创建一个文件夹,然后在AD17点击左上角的文件中...

qq_45433195的博客 2万+

ANC降噪学习

概述 ANC,英文名称:Active Noise Control,主动降噪。其原理是降噪系统电路产生降噪MIC接收的外界环境噪音相等的反相信号,将噪声抵消。核心算法 ANC降噪实现核心算法为:FxLMS(最小均方差算法)。最小均方差算法以均方误差为代价函数,并使误差降到最小的算法。 具体算法推导这里不做具体介绍,这里直接列出表达式: ...

王小壮的博客 2万+

重学 Go 语言:基础篇

专栏亮点 79 章节内容解析 Go 语言基础知识点; 使用反汇编、编程范式推导 Go 核心原理; 从架构思想层面解析 Go 语言每个细节; 与 C 语言对比学习 Go 基础核心; 从 Go 语言背景知识到灵活使用,知其然知其所以然。 专栏介绍 我一直从事服务器后端系统开发快 10 年了,高并发分布式是互联网应用的基础,工作中先后使用过C#、Java、Go 等语言。当前工作需要把平台迁移到 Go...

GitChat 969

VisionPro开发实战:CogRecordDisplay控件显示与保存图片的完整流程(附C#代码示例)

本文详细解析了VisionPro开发中CogRecordDisplay控件的图像处理全流程,包括图像加载、显示优化、动态清屏及结果保存等关键操作。通过C#代码示例,展示了如何高效使用该控件进行工业视觉应用开发,特别适合自动化检测系统的开发者参考。

weixin_42523792的博客 276

Go 语言,你真的学会了吗?

为什么是 Go?传统的编程语言都存在一些普遍的缺点:学习成本高:诸如 C++ 要理解作者思想,Java 要学习大量库;编译速度慢:代码的编写、预处理、编...

mobilehub 319

Java全栈:电影数据检索与推荐系统实战

全文检索个性化推荐数据缓存容器化部署通过这个项目,我们不仅实践了Java全栈开发的各项技术,还接触了推荐系统的核心算法实现。用户历史行为数据物品特征数据上下文信息实时反馈算法的准确性推荐的多样性系统的实时性冷启动问题性能优化效果评估VIEW, // 浏览CLICK, // 点击SEARCH, // 搜索RATE, // 评分COMMENT, // 评论SHARE, // 分享WATCH_TIME, // 观看时长。

exlink2012的专栏 891

044、Edge Impulse的音频分类实战

上周调试一块STM32WBA52开发板,板载PDM麦克风死活采不到有效数据。示波器怼上去,时钟线波形漂亮得像教科书,数据线却像心电图——全是毛刺。折腾半天发现是电源纹波干扰,给麦克风单独加了个LC滤波,世界清净了。这种坑,做音频分类的嵌入式工程师迟早要踩一遍。

2401_88858637的博客 54

bilibili go框架_Go 语言,你真的学会了吗?

为什么是 Go?传统的编程语言都存在一些普遍的缺点:学习成本高:诸如 C++ 要理解作者思想,Java 要学习大量库;编译速度慢:代码的编写、预处理、编译与运行流程花费时间太长;无法使用现代计算机发展:现代计算机发展迅速,C、C++ 生态;项目迭代周期短:996 已经成为常态,任务压的又压,在如何提高团队生产效率,又能保证项目质量两者之前权衡成了一个比较重要的问题。所以急需一门语言改善...

weixin_39759989的博客 484

半导体物理基础:载流子输运理论_(4).载流子浓度与费米能级

费米能级(Fermi Level)是描述半导体中电子和空穴能量分布的基准能级。在绝对零度下,费米能级以下的所有能级都被电子占据,费米能级以上的所有能级都是空的。随着温度的升高,电子会获得足够的能量跃迁到费米能级以上的能级,形成导带中的自由电子和价带中的空穴。费米能级在半导体中的位置取决于掺杂浓度和温度。对于本征半导体,费米能级位于禁带的中心;对于掺杂半导体,费米能级会偏移,具体位置取决于掺杂类型和浓度。载流子浓度(Carrier Concentration)是指半导体中自由电子和空穴的数量密度。

2401_87715305的博客 832

重学 Go 语言:进阶篇

专栏亮点 27 章节内容解析 Go 语言进阶知识点; 使用反汇编、编程范式推导 Go 核心原理; 从架构思想层面解析 Go 语言每个细节; 从 Go 语言背景知识到灵活使用,知其然知其所以然。 专栏介绍 我一直从事服务器后端系统开发快 10 年了,高并发分布式是互联网应用的基础,工作中先后使用过C#、Java、Go 等语言。当前工作需要把平台迁移到 Go,从零开始构建基础设施框架,在基础设施框...

GitChat 819

自然语言处理6 -- 情感分析

1 概述 情感分析是自然语言处理中常见的场景,比如淘宝商品评价,饿了么外卖评价等,对于指导产品更新迭代具有关键性作用。通过情感分析,可以挖掘产品在各个维度的优劣,从而明确如何改进产品。比如对外卖评价,可以分析菜品口味、送达时间、送餐态度、菜品丰富度等多个维度的用户情感指数,从而从各个维度上改进外卖服务。 情感分析可以采用基于情感词典的传统方法,也可以采用基于深度学习的方法,下面详细讲解 ...

谢杨易的博客 5万+

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

Go语言中SHA256哈希计算:从原理到实践的完整指南

本文详细介绍了Go语言中SHA256哈希计算的原理与实践。SHA256作为一种安全哈希算法,具有单向性、定长输出和抗碰撞性等核心特性,广泛应用于数据完整性校验、密码存储和区块链技术等领域。Go语言通过crypto/sha256包提供了简洁高效的SHA256计算方案,开发者可以通过创建哈希对象、写入数据和获取结果的标准流程轻松实现哈希计算。文章还提供了完整的代码示例,并扩展了其他SHA家族哈希(如SHA512)的计算方法。此外,强调了在密码存储和大文件处理中的安全注意事项与最佳实践,如使用盐值、密钥拉伸技术和

Tekin 是深耕技术 20 年的全栈实战派专家,精通 Go/Python/Java 等多语言开发。博客专注技术原理与实战结合,深度解析 Python 高阶编程、Go 语言架构、数据库优化等硬核内容。涵盖并发编程、机器学习、云原生等前沿领域,通过真实案例拆 1695

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

工作中,你有遇到过 Go 的这些问题吗?

我一直从事服务器后端系统开发快 10 年了,工作中先后使用过 C#、Java、Go 等语言。我当前的工作需要把平台迁移到 Go,从零开始构建基础设施框架...

形势所逼,不进则退 284

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

go 通道 返回_工作中,你有遇到过 Go 的这些问题吗?

我一直从事服务器后端系统开发快 10 年了,工作中先后使用过 C#、Java、Go 等语言。我当前的工作需要把平台迁移到 Go,从零开始构建基础设施框架,并在基础设施框架上开发各种业务系统。在迁移过程中,我逐步自研了一套功能完善的基础框架,同时也对语言的一些特性有了更深入的了解。为了把这些宝贵的经验分享给大家,于是我开始在 GitChat 上写专栏。专栏想告诉大家的是:如何把一门语言学精...

weixin_35820423的博客 127

当 Python 遇上 Go:Sponge 如何成为替代 Django/Flask 的理想选择

Pyhton 转型 Go 最难的其实不是语法,而是思维方式的转变。虽然在 Go 里不能写 Python 风格的代码,但可以用 Sponge 享受 Django/Flask 般的开发体验!

zhuyasen的博客 1833

Go 内存泄漏,pprof 够用了吗?

MOSN 是主要使用 Go 语言开发的云原生网络代理平台,在蚂蚁集团有着几十万容器的大规模生产应用。在这种大规模的应用中,经常会遇到各种内存问题,通常情况下 pprof heap profile 可以很好帮助分析问题。不过,有时候 pprof 也不够用,也就需要我们有更合适的工具了。

金融级分布式架构 1054

文档之外,学习 Go 会遇到哪些问题?

我一直从事服务器后端系统开发快 10 年了,工作中先后使用过 C#、Java、Go 等语言。我当前的工作需要把平台迁移到 Go,从零开始构建基础设施框架...

mobilehub 158
上一篇: 直播预告 | SOFAChannel#32《你的开源入门指南——Kata Containers 社区》
下一篇: SOFA Weekly|开源人、本周贡献 & issue 精选
SOFAStack
博客等级 码龄7年 747粉丝 389原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值