AIGC 加速企业创新,权威指南免费下载,开启你的学习探索之旅吧! 了解详情
写点什么

应用程序热补丁(一):几行代码构造免重启修复补丁

  • 2019-11-12
  • 本文字数:2507 字

    阅读完需:约 8 分钟

应用程序热补丁(一):几行代码构造免重启修复补丁

热补丁是一种在程序运行时动态修复内存中代码 bug 的技术。在 UCloud,我们使用内核热补丁和应用程序热补丁(也就是进程热补丁)来在线修复核心业务的缺陷和安全漏洞。


多年来我们使用内核热补丁技术避免了系统重启导致的业务中断、保证了操作系统的可用性。属于核心业务组件的应用程序,尤其是单点的虚拟化组件和有状态的应用程序,同样面对高可用的挑战,每次重启都会导致服务受损。


然而业界并没有成熟可靠的应用程序热补丁方案可以参考,原因在于应用程序热补丁比内核热补丁更加困难和复杂。比如内核对外提供完整的模块加载功能,可以直接加载内核模块形式的热补丁。


而应用程序需要通过外部程序通过一系列对其内存和寄存器的复杂操作来注入动态链接库形式的热补丁;应用程序包含多线程;内核在编译时会被限定使用特定的编译方式,而应用程序的编译方式则更加宽泛;内核的二进制相对简单,而应用程序二进制因为需要链接到多种的动态链接库,本身的结构会更复杂。


经过大量的研究和实践,我们针对应用程序如何免重启修复 bug,自研了一套应用程序热补丁技术而且在 UCloud 内部已经经过数十万台次修复验证。后面通过一系列文章分享其技术实现。本文先介绍一种简单实用的应用程序热补丁技术,不少场景下采用该方法编写几行代码即可免修复应用程序 bug。

原理

一般来说,应用程序热补丁的流程是:首先通过编译器将热补丁源码制作成可加载的动态链接库,然后通过加载程序将热补丁加载到目标进程的地址空间,最后在进行一致性模型检查确认安全的情况下,把原始代码替换成新的代码,完成在线修复的过程。


下面我们分别介绍热补丁本身和热补丁加载程序,热补丁本身是因 patch 而异的,加载程序是通用的。假设我们有热补丁加载程序 Loader、目标进程 T、热补丁 patch.so,目标程序的 func 函数替换为 func_v2。

热补丁

  • 编写热补丁源码,编译成动态链接库的格式的热补丁 patch.so,patch.so 中包含 func 和 func_v2 的信息。

  • 热补丁 patch.so 在被加载程序 Loader 加载到目标进程 T 地址空间的过程中,通过 dlsym 调用找到 func 的地址,并将 func 的入口指令改为可写,同时改变为跳转到 func_v2。

  • 至此,所有对 func 的调用都会被重定向到 func_v2,func_v2 执行完毕后返回,程序继续运行。

  • 如图所示:


热补丁加载程序

  • 加载程序 Loader 找到目标进程 T 的 dlopen 函数入口地址。

  • Loader 通过 ptrace 依附到目标进程 T,Loader 将热补丁的名字放入放入目标进程 T 的堆栈,将 IP 寄存器设置为 dlopen 函数的地址。

  • Loader 使目标进程 T 继续运行。因为 IP 寄存器已经设置为 dlopen 函数的入口,目标进程 T 会调用 dlopen 把热补丁加载到 T 的地址空间中。

  • 如图所示:



了解原理之后,我们一步步实现一种简单的基于 x86_64 的热补丁。(对于需要制作热补丁的同学,只需自己编写 patch.so,而 Loader 是通用的。patch.so 编写可以参考下面的例子,往往只需几行代码做相应替换。)

实现

热补丁

  1. 目标进程 T 执行 dlopen 的过程中,通过预先在热补丁(动态链接库)中写入的 constructor 函数,在加载过程中函数 func_v1 替换函数 func。


热补丁加载程序

1.Loader 得到目标进程 T 地址空间中 dlopen 入口地址


  • dlopen 函数有 libdl 提供,并不是所有的程序都加载 libdl,幸运的是,libc 中提供了同样功能的函数 __libc_dlopen_mode,并且接受的参数和 dlopen 相同。除非特殊情况,所有程序都会加载 libc。所以我们需要找到 __libc_dlopen_mode 在目标进程 T 地址空间中的函数入口地址。

  • 我们知道,不同进程中 libc 会被加载到不同的基地址,但是 libc 中函数的地址相对基地址的偏移是不变的。

  • 通过 Loader 和目标进程 T 的 /proc/pid/maps,我们可以得到 libc 在 Loader 和目标进程 T 中加载的基地址。通过 Loader 运行 dlsym,我们可以得到 Loader 中的__libc_dlopen_mode 的地址。这样我们可以得到目标进程 T 中__libc_dlopen_mode 的地址(Loader_dlopen - Loader_libc + T_libc)。



2.Loader 对目标进程 T 使用 ptrace attach,并保存 T 此时的寄存器信息。



3.将目标进程 T 的 %RIP 指向 dlopen,热补丁的名字的字符串放入堆栈,字符串的地址写入 %rdi,RTLD_NOW 的值写入 %rsi 作为 dlopen 的 flag。同时把 dlopen 返回地址设置为非法地址 0x0(把 0x0 压入栈中),这样 Loader 可以捕获目标进程 T 产生的 SIGSEGV 信号进而重新获得 T 的控制权。



  1. Loader 使目标进程 T 继续运行。当 T 执行完 dlopen 之后,T 产生的 SIGSEGV 信号被 Loader 捕获,Loader 重新获得 T 进程的控制权。



5.Loader 通过读取目标进程 T 此时的 %rax 寄存器得到 dlopen 的返回值,恢复 T 最开始的执行状态,最后释放对 T 的控制。



至此对目标进程 T 的热补丁就完成了,下面我们看一个例子。

验证

假设我们运行 target 程序,每隔一秒打印 Hello 一次:



target 程序由 target 本身和 libold.so 组成,分别代码如下:



编译如下:



我们想要修改 print 函数,变成打印“Goodbye”。我们需要编写热补丁 new.c,并添加新函数和 constructor:



编译:



然后通过加载程序对 target 进程打入热补丁 libnew.so,最后我们对 target 程序打入这个热补丁,观察变化:



我们发现热补丁确实改变了 print 函数,最后通过 gdb 进一步确认,可以看出 print 函数的入口被修改成 48 b8 dc b6 15 a9 c1 7f 00 00 ff e0,与我们的预期相符:


总结

我们介绍了应用程序热补丁的基本原理,实践了一个应用程序热补丁 demo。此类热补丁适用于动态替换共享链接库中的可见函数,可以修复例如 glibc “GHOST 漏洞”(CVE-2015-0235)等等,在 UCloud 我们利用热补丁修复了若干缺陷,在用户没有感知的情况下把 bug 快速及时的修复。这些热补丁修复程序里,绝大多数代码是通用的,只需少数几行做特殊替换。


本文介绍的热补丁技术对于适用的场景非常理想,简单可靠,但存在几个缺点:


  • 手写热补丁代码门槛较高,特别是被修复函数的依赖函数链较长时手写热补丁很容易出错;

  • 无法修复局部函数和局部变量(只能修复全局可见的函数和变量)。


后面的文章我们会介绍如一种更加先进的应用程序热补丁技术。


本文转载自公众号 UCloud 技术(ID:ucloud_tech)。


原文链接:


https://mp.weixin.qq.com/s/BJhpWihFh3UcxMrvaZaOnw


2019-11-12 16:183084

评论

发布
暂无评论
发现更多内容

视频化全链路智能上云?一文详解什么是阿里云视频云「智能媒体生产」

阿里云视频云

人工智能 媒体 音视频

“整洁架构”和商家前端的重构之路

得物技术

架构 前端 重构

redisson bug分析

wgy

Java redis redisson

三步就能在OpenHarmony中实现车牌识别

OpenHarmony开发者

OpenHarmony

关于静态类型、动态类型、id、instancetype

NewBoy

前端 移动端 iOS 知识体系 7月月更

渲大师携手向日葵,远控赋能云渲染及GPU算力服务

Finovy Cloud

渲染 GPU算力

SAP Fiori 应用索引大全工具和 SAP Fiori Tools 的使用介绍

Jerry Wang

SAP Fiori SAP UI5 ui5 7月月更

C#/VB.NET 给PDF文档添加文本/图像水印

在下毛毛雨

C# .net PDF 添加水印

AddressSanitizer 技术初体验

焱融科技

内存泄露 存储 文件存储 分布式文件存储

beegfs高可用模式探讨

姚华

HPC beegfs 并行文件系统

夏志刚介绍

管理在线

创新管理 企业培训 精益管理体系 企业战略 国际标准体系

Python|数据结构——字典和集合

AXYZdong

Python 7月月更

CRMEB 商城系统如何助力营销?

CRMEB

用Java写学生登陆认证系统

魏铁锤

LSF 集群全面监控!浅析 HPC 基于龙蜥操作系统的迁移替代解决方案

OpenAnolis小助手

开源 解决方案 龙蜥操作系统 常青藤开源科技 HPCinsights

《数字经济全景白皮书》保险数字化篇 重磅发布

易观分析

保险数字化

设计你的安全架构OKR

I

安全架构师 安全架构 企业安全 安全治理

BP神经网络(算法整体思路及原理+手写公式推导)

秃头小苏

7月月更

小程序在产业互联网中的作用

Geek_99967b

小程序 小程序容器

Android 查看签名

沃德

程序员 7月月更

Java多线程案例之阻塞队列

未见花闻

7月月更

提前解锁 2 大直播主题!今天手把手教你如何完成软件包集成?|第 29-30 期

OpenAnolis小助手

云原生 直播 龙蜥大讲堂 SIG 双周会 CSI 与 open-local

【鲲鹏BoostKit】OminiRuntime ——高效统一的大数据分析Runtime底座

Geek_2d6073

终于可以一行代码也不用改了!ShardingSphere 原生驱动问世

SphereEx

数据库 ShardingSphere ShardingSphere-JDBC

企业精益管理体系介绍

管理在线

精益生产 精益管理 精益管理体系

A5000 vGPU显示模式切换

姚华

显卡、gpu

OAI 5G NR+USRP B210安装搭建

柒号华仔

5G 7月月更

ORACLE进阶(四)表连接讲解

No Silver Bullet

oracle 7月月更 表连接

Scala 基础 (五):面向对象(上篇)

百思不得小赵

scala 大数据 oop 7月月更

如何提高网站权重

源字节1号

微信小程序 软件开发 网站开发

案例 ①|主机安全建设:3个层级,11大能力的最佳实践

青藤云安全

网络安全 主机安全 网络安全、攻防演练

  • 扫码添加小助手
    领取最新资料包
应用程序热补丁(一):几行代码构造免重启修复补丁_文化 & 方法_王超_InfoQ精选文章