• Libco Hook 机制浅析


    Libco Hook 机制浅析

    之前的文章里我们提到过 Libco 有一套 Hook 机制,可以通过协程的让出(yield)原语将系统的阻塞系统调用改造为非阻塞的,这篇文章我们将深入解析 Hook 机制到底是怎么运作的

    Hook 机制的核心有两点

    • 提供自己的实现覆盖标准库(libc.so)的实现
    • 在自己实现的代码里要有办法能够调用标准库的实现

    也就是说,我们提供的实现其实是标准库实现的 wrapper

    为了搞明白 Hook 机制,我们首先要了解 Linux 动态库究竟是怎么运作的

    动态库的加载和符号表

    动态库是在运行时链接的,这个工作是由动态链接器来完成的(Linux 下是 /lib/ld-linux.so.2 ),主要涉及到的步骤有

    • 搜索可执行文件依赖的所有动态库,并将它们加载到进程的虚拟地址空间中
    • 做符号解析和重定位
      • 动态库的符号会被加到全局符号表里
    • 执行共享对象的初始化代码

    如果可执行文件依赖的多个动态库定义了同一个符号时,以先加载的动态库为准,那么如果想要覆盖掉动态库 A 里的符号,最简单的做法就是让我们的库在动态库 A 之前加载,通常使用环境变量 LD_PRELOAD 来实现这点

    • LD_PRELOAD 中列出的动态库会在所有其他动态库之前加载,包括 libc.so
      • 不管程序是否依赖于它们,这些库都会被加载
      • 如果我们想要提供自己的 malloc 实现,只需要在自己的动态库里实现 malloc,然后将它加入 LD_PRELOAD 中,这样就会覆盖掉标准库的 malloc 实现

    命令 LD_DEBUG=files ./a.out 可以查看动态库的加载顺序和初始化顺序,这两个顺序不一定相同

    dlsym

    解决了第一个问题,那么剩下的问题就是如何在我们的实现里调用标准库实现了,直接用函数名调用肯定是不行的,那么我们能想到的办法就是能否给标准库的实现改一个名字呢?

    为了实现这点,我们需要用到 dlsym 函数, 它的函数原型为

    void *dlsym(void *restrict handle, const char *restrict symbol);
    

    dlsym 的原意是用来获得动态加载进来的动态库中的接口(Linux 中的动态库不仅可以在程序启动时加载,还可以在程序运行过程中加载和卸载),其中 handle 是动态库的句柄,symbol 是要搜索的符号

    此外,dlsym 还支持两个伪 handle

    • RTLD_DEFAULT
      • 按默认搜索顺序搜索第一次出现的 symbol,搜索范围包含全局符号表里的所有符号
        • 程序可执行文件本身的符号
        • 动态链接器加载的动态库中的符号
        • 如果用 dlopen 加载动态库时,指定了 RTLD_GLOBAL选项,那么它的符号也会出现在全局符号表里
    • RTLD_NEXT
      • 从当前可执行文件或动态库开始,搜索下一次出现的 symbol
        • 搜索顺序依赖于动态链接库的加载顺序
        • 假设动态链接库的加载顺序是 A -> B -> C -> D,在动态库 B 里调这个接口搜索 symbol1,就会依次去 C 和 D 中搜索 symbol1,返回先找到的 symbol1 地址

    利用 RTLD_NEXT 就可以实现我们想要的功能,假设说我们用 LD_PRELOAD 覆盖了标准库 malloc 实现,就可以通过 dlsym 拿到标准库的 malloc 地址(前提是在 libc.so 之前没有其他库定义了 malloc),给它的函数指针起一个其他的名字,就可以在我们的实现里调用标准库 malloc 了

    可以看到 Libco 里就是用这种方法拿到所有标准库实现的函数指针

    typedef ssize_t (*read_pfn_t)(int fildes, void *buf, size_t nbyte);
    static read_pfn_t g_sys_read_func = (read_pfn_t)dlsym(RTLD_NEXT, "read");
    

    __EOF__

  • 本文作者: 路过的摸鱼侠
  • 本文链接: https://www.cnblogs.com/ljx-null/p/16208502.html
  • 关于博主: 评论和私信会在第一时间回复。或者直接私信我。
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
  • 声援博主: 如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。
  • 相关阅读:
    网络安全(黑客)自学
    JavaEE基础知识
    用梯度下降算法极大化对数似然函数来估计参数
    基于Redis+Cookie实现Session共享
    Javascript尚硅谷笔记
    Python中的贝叶斯推断和马尔科夫链蒙特卡洛抽样调查
    SSM健身房管理系统的设计与实现毕业设计-附源码191656
    我的创作纪念日(两周年)
    ELK框架Logstash配合Filebeats和kafka使用
    【计算机网络 一 概述】
  • 原文地址:https://www.cnblogs.com/ljx-null/p/16208502.html