0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

x86_64运行时动态替换函数的hotpatch机制

Linux阅码场 来源:未知 作者:李倩 2018-11-26 16:32 次阅读

晚上回去有朋友在朋友圈回复了我关于 “函数开头5字节跳转” 的事。今天正好要确认一个与此相关的细节,就顺便又把这问题重新撸了一遍。

其实起初我也很纳闷,以前不都是0x55开头的指令吗?怎么现在这种call self或者lea 0x0(%rsp),%rsp套路却都成了惯例。…

关于5字节跳转,说的是下面的情况:

请注意函数最开头的5个字节:

可见,它实际上call的是紧接着它下面的地址,所以说这个5字节的call指令其实是 没有用 的!

仔细看一下这5个字节,思考一下它到底有什么用。

我们可以任意将它替换成 jmp $4字节相对偏移

这样,代码指令流就会进入我们自己的HOOK函数里了。

当然了,关于 “e8 00 00 00 00 callq …” 这个有很多话可以讲,比如和Link相联系的时候就比较有意思了,它可是作为一个桩指令存在。这个和HOOK无关,也不再说太多。

让我觉得最有意思的是,昨天那位朋友提到了微软的/hotpatch编译选项,我大致看了一下:

/hotpatch (Create Hotpatchable Image):https://docs.microsoft.com/en-us/cpp/build/reference/hotpatch-create-hotpatchable-image?view=vs-2017

When /hotpatch is used in a compilation, the compiler ensures that first instruction of each function is at least two bytes, which is required for hot patching.

这是一个很有意思的选项,其实编译器提供这个机制也是举手之劳吧,虽然简单,但它确实为程序员HOOK运行中的函数提供了很大的方便。

/hotpatch的实质其实就是在函数的开头和结尾填充了一些无关紧要的指令,方便HOOK来用自己的jmp指令覆盖这个无关紧要的指令。比如下面是一个函数的开头:

0x90代表一个nop指令,即 “什么也不做”的意思,如此一来,程序员便非常容易将类似下面的指令插入到函数开头了:

无需任何额外的指令保存动作。

既然微软的编译器有这个功能可用,GCC有没有呢?看了GCC的manual,发现了一个-mhotpatch=x,y的选项,但是在x86平台不能用,还是比较不爽的。

后来发现了在编写函数的时候,可以加上下面的属性,然后编译器就可以将其编译成带有填充的指令了:

那么,简单来用一下,看看效果咯。

由于时间并不是很多,我也没有那么多精力去应对不断的panic,所以这次准备在用户态搞。

由于用户态可以直接使用mprotect函数更改内存的使用权限,所以就不需要那个stub函数了。今天的这个例子,原理图如下:

加上ms_hook_prologue属性修饰的函数,编译好了之后,你会在函数最开头两行找到下面的废指令:

随意替换之就好。所以对于这个例子,上面图示里的n的值就是5.

此外,上图中,我们的一个指令buffer不再是一个stub函数,而真的就是一块分配的内存,所以我们需要给它加上EXEC权限,不然会segment fault。这个在内核模块中是不能直接做的,因为分配带有EXEC权限的module_alloc并没有导出,所以如若想用它,则必须通过kallsyms_lookup_name的内省方式来做。

再一个需要注意的是,由于指令buffer是在堆上分配的,在64位系统上,它的位置和函数代码的位置之差会超过4个字节界定的相对偏移,所以就不能用0xe9+4字节相对偏移来jmp了,而要通过64位绝对地址来跳转了,指令如下:

好了,说了这么多,该上代码了:

结果当然是先调用自己的hook函数,然后再调用原始函数咯:

为什么不用kprobe机制呢?kprobe的原理是 为了灵活性,使用int 3指令替换被hook的指令。 这样就可以任意编写pre/post回调函数了,但是我们也能看出来,通过int方式来hook,对效率的影响是不能忽略,特别是对于那些频繁被调用的函数,kprobe更加不可行。

kprobe非常适合做问题排查,热点分析,但不好在生产环境跑的。

其实,本文所描述的hotpatch原理还可以做的更好些,达到 在任意点插入自己的逻辑的目的,包括在函数的内部。 这样可以达到和kprobe相同的效果。当然,这需要对运行中的二进制指令序列做相对周密详细的分析。

这里还有一篇关于hotpatch的文章,比我这篇好,可以看一下:

Hotpatching a C Function on x86 :https://nullprogram.com/blog/2016/03/31/

补充说明一下,朋友圈有高手最新评论,让我又知道了些新东西:

nop指令的实现方式有很多种,比如mov edi edi。可能很多平台并没有类似独立的0x90指令吧。不过既然有,那还是0x90纯粹些。

kprobe也不全部一来int 3,只有return hook的场景才依赖int 3,其它的也可以做jmp hook。

线程安全,原子化操作也是生产环境必须考虑的,不然就是玩具。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 函数
    +关注

    关注

    3

    文章

    4308

    浏览量

    62443
  • 编译器
    +关注

    关注

    1

    文章

    1618

    浏览量

    49057

原文标题:x86_64运行时动态替换函数的hotpatch机制

文章出处:【微信号:LinuxDev,微信公众号:Linux阅码场】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    如何缩短Vivado的运行时

    在Vivado Implementation阶段,有时是有必要分析一下什么原因导致运行时间(runtime)过长,从而找到一些方法来缩短运行时间。
    的头像 发表于 05-29 14:37 1.4w次阅读
    如何缩短Vivado的<b class='flag-5'>运行时</b>间

    函数运行时异常

    。是否有任何机构知道什么可能导致运行时错误运行时异常@ PC地址0x9D017CA0?函数:在C:/Microchip/Hyrn/V2Y03B/Frrase/GFX/LIBARARI/
    发表于 03-20 13:41

    在arm64x86服务器上运行的耗时来发现Docker在arm64架构下的性能问题

    间整理成表格。通过比较相同测试用例在arm64x86机器上的运行时间,可以发现潜在的性能问题。下图即是数据表格的一部分。“Test case”竖列是测试用例所在文件和名字,“Qualcomm”列是在
    发表于 07-12 15:48

    如何为x86_64目标编译88W9098?

    x86_64 目标编译 88W9098
    发表于 04-21 08:52

    FPGA运行时重构的延迟隐藏机制研究与实现

    FPGA运行时重构的延迟隐藏机制研究与实现_刘伟
    发表于 01-07 19:08 0次下载

    紫金桥组态软件新的功能_运行时组态

    运行时组态是组态软件新近提出的新的概念。运行时组态是在运行环境下对已有工程进行修改,添加新的功能。它不同于在线组态,在线组态是在工程运行的同时,进入组态环境,在组态环境中对工程进行修改
    发表于 10-13 16:17 2次下载
    紫金桥组态软件新的功能_<b class='flag-5'>运行时</b>组态

    x86_64函数调用惯例及其栈帧

    从下图可见,x86_64架构取消了传统的中断形式的系统调用,使用syscall指令实现系统调用。并且存放参数的寄存器也有所变化。execve的系统调用号也从0xb变为了0x3b
    的头像 发表于 05-01 16:41 5029次阅读
    <b class='flag-5'>x86_64</b><b class='flag-5'>函数</b>调用惯例及其栈帧

    基于STM32单片机通过使用宏assert_param来实现运行时间检测

    固件函数库通过检查库函书的输入来实现运行时间错误侦测。通过使用宏assert_param来实现运行时间检测。所有要求输入参数的函数都使用这个宏。它可以检查输入参数是否在允许的范围之内。
    发表于 10-22 15:12 1441次阅读
    基于STM32单片机通过使用宏assert_param来实现<b class='flag-5'>运行时</b>间检测

    Xilinx SDAccel开发环境在X86_64位工作站的运行情况

    本视频演示了SDAccel开发环境在一个标准X86_64位工作站上运行的情况,以展示其为您所带来的生产力的提升;以及该开发环境对OpenCL,C,C ++等等支持情况。该工作站包含一个 Alpha Data的ADM-PCIE-7V3加速卡。
    的头像 发表于 11-27 06:45 2240次阅读

    友善之臂T4成为计算性能与X86_64不分伯仲的ARM

    一直以来,小伙伴们都有这样的观念,X86_64处理器的强项是每个核心的多线程能力;ARM Cortex A系列内核处理器的强项是堆核心,一般是依靠多核而不是单核多线程。
    发表于 04-19 15:33 3949次阅读

    Go运行时:4年之后

    自 2018 年以来,Go GC,以及更广泛的 Go 运行时,一直在稳步改进。近日,Go 社区总结了 4 年来 Go 运行时的一些重要变化。
    的头像 发表于 11-30 16:21 793次阅读

    ch32v307记录程序运行时

    ch32v307记录程序运行时间 在程序开发中,很重要的一项任务就是对程序的运行时间进行评估。对于大型的程序系统来说,它们通常需要处理大量的数据或进行复杂的计算操作。因此,如果程序的运行时间过长
    的头像 发表于 08-22 15:53 861次阅读

    Xilinx运行时(XRT)发行说明

    电子发烧友网站提供《Xilinx运行时(XRT)发行说明.pdf》资料免费下载
    发表于 09-14 10:01 0次下载
    Xilinx<b class='flag-5'>运行时</b>(XRT)发行说明

    如何保证它们容器运行时的安全?

    紧密耦合的容器运行时继承了主机操作系统的安全态势和攻击面。运行时或主机内核中的任何漏洞及其利用都会成为攻击者的潜在切入点。
    的头像 发表于 11-03 15:24 640次阅读

    jvm运行时内存区域划分

    JVM是Java Virtual Machine(Java虚拟机)的缩写,它是Java编程语言的运行环境。JVM的主要功能是将Java源代码转换为机器代码,并且在运行时管理Java程序的内存。JVM
    的头像 发表于 12-05 14:08 509次阅读