zoukankan      html  css  js  c++  java
  • [转] 微信协程库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某个头文件中的符号时,会把该符号之前的所有符号也导入进来?

  • 相关阅读:
    分析建模中的行为分析
    SOLID总结(未完待续)
    分析建模的五个子过程中为什么有个编码子过程?
    用例建模的五个子过程
    类之间的关系
    python cx_Freeze安装详解、打包exe文件简解
    pyautoit:OSError: [WinError 193] %1 不是有效的 Win32 应用程序
    Python 动态导入类并运行其中的方法
    比较2个字符串,输出看起来一样,但比较起来不同
    pyautogui 文档(五):截图及定位功能
  • 原文地址:https://www.cnblogs.com/encode/p/8124149.html
Copyright © 2011-2022 走看看