• [转] 微信协程库libco研究:hook系统函数


    系统为我们提供了 dlopen,dlsym工具,用于运行时加载动态库。可执行文件在运行时可以加载不同的动态库,这就为hook系统函数提供了基础。
    下面用一个小小的例子来说明如何利用dlsym工具hook系统函数。

    假设现在我们需要统计程序中malloc的调用次数,但是不能修改原有程序。最简单的思路类似于Java中动态代理Proxy的做法,先找到系统的malloc函数,然后将其替换为自定义的函数,在自定义函数中增加调用次数,并回调系统的原有malloc函数。

    例如我们要统计以下main.c中调用malloc的次数:

    // main.c

    include <stdio.h>

    include <stdlib.h>

    int main() {
    void *p = malloc(4);
    free(p);
    printf("hello world ");
    return 0;
    }
    为了能让自己的malloc函数回调系统的malloc函数,我们需要利用dlsym获取系统的malloc函数。

    // myhook.c

    include <stdlib.h>

    include <dlfcn.h>

    include <stdio.h>

    static int count = 0;

    void *malloc(size_t size) {
    void (myMalloc)(size_t) = dlsym(RTLD_NEXT, "malloc");
    printf("call my malloc ");
    count++;
    return myMalloc(size);
    }
    RTLD_NEXT允许从调用方链接映射列表中的下一个关联目标文件获取符号,即找到glibc.so中的malloc函数。

    下一步则是要让可执行文件main找到自定义的malloc函数。

    在linux操作系统的动态链接库的世界中,LD_PRELOAD就是这样一个环境变量,它可以影响程序的运行时的链接(Runtime linker),它允许你定义在程序运行前优先加载的动态链接库。loader在进行动态链接的时候,会将有相同符号名的符号覆盖成LD_PRELOAD指定的so文件中的符号。换句话说,可以用我们自己的so库中的函数替换原来库里有的函数,从而达到hook的目的。
    编译:

    $ gcc -o main main.c
    $ gcc -o libmymalloc.so -fPIC -shared -D_GNU_SOURCE myhook.c -ld
    运行:

    $ LD_PRELOAD=./libmymalloc.so ./main
    call my malloc
    hello world
    至此,malloc函数的hook已经完成。

    不使用LD_PRELOAD的Hook
    这样就结束了吗?我们看看libco库是如何实现hook的呢,它的makefile中并没有LD_PRELOAD相关的信息。其秘密在于co_hook_sys_call.cpp,其将 co_enable_hook_sys()的定义在该cpp文件内,这样就把该文件的所有函数都导出了(即导出符号表)。

    //co_hook_sys_call.cpp
    ssize_t read(int fd, void* buf, size_t bytes)
    {
    ...
    }

    ...

    void co_enable_hook_sys() //这函数必须在这里,否则本文件会被忽略!!!
    {
    stCoRoutine_t *co = GetCurrThreadCo();
    if( co )
    {
    co->cEnableSysHook = 1;
    }
    }
    我们仍然以上面malloc的例子来说明:

    // main.c

    include <stdio.h>

    include <stdlib.h>

    include "myhook.h"

    int main() {
    enable_hook();
    void *p = malloc(4);
    free(p);
    printf("hello world ");
    return 0;
    }
    // myhook.h
    int enable_hook();
    // myhook.c

    include <stdlib.h>

    include <dlfcn.h>

    include <stdio.h>

    include "myhook.h"

    static int count = 0;

    void *malloc(size_t size) {
    void (myMalloc)(size_t) = dlsym(RTLD_NEXT, "malloc");
    printf("call my malloc ");
    count++;
    return myMalloc(size);
    }

    int enable_hook() {
    return 1;
    }
    编译和运行:

    $ gcc -o libmymalloc.so -fPIC -shared -D_GNU_SOURCE myhook.c -ldl
    $ gcc -o main main.c -L./ -lmymalloc
    $ ./main
    call my malloc
    hello world
    这种方式算是对源代码进行了侵入,必须调用特定的函数(即本例中的enable_hook()),才能将hook的函数导出,并链接到现有的可执行文件的内存空间中。

    总结
    libco库通过非LD_PRELOAD的方法,将网络相关的read,write...等方法进行hook后,将其改造成异步操作,即相关调用阻塞后让出cpu,让其他协程继续处理,从而达到异步化的效果。libco的具体实现后续再介绍。

    注:这种不使用LD_PRELOAD的方式蛮奇怪的,难道说include某个头文件中的符号时,会把该符号之前的所有符号也导入进来?

  • 相关阅读:
    yum安装Mysql
    在Centos上安装xtrabackup,对Mysql做备份
    Linux下使用yum安装Mysql之后,无法登陆mysql
    手动产生replication中的系统SP
    MySQL全面瓦解1:安装部署与准备
    开发笔记:PDF生成文字和图片水印
    调试与优化:一次数据中心看板 T+1 改 T+0 优化过程
    MySQL:SELECT COUNT 小结
    系统架构设计:平滑发布和ABTesting
    linux /dev/mapper/centos-root目录莫名其妙被占满
  • 原文地址:https://www.cnblogs.com/encode/p/8124149.html
Copyright © 2020-2023  润新知