反过来想,也就是允许我们用自己写的函数去“代替”系统库的函数。举个例子,我们可以很容易地拦截掉time(),read(),write()等等这些函数。

    来瞧瞧我们是如何愚弄uptime这个程序的。我们知道,该程序显示计算机已经工作了多长时间。借助strace的帮助可以看到,该程序通过/proc/uptime文件获取到计算机的工作时长。

    /proc/uptime并不是存放在磁盘的真实文件。而是由Linux Kernel产生的一个虚拟的文件。它有两个数值:

    1. 416690.91 415152.03

    我们来写一个含open(),read(),close()函数的动态链接库。

    首先,我们的open()函数会比较一下文件名是不是我们所想要打开的,如果是,则将文件描述符记录下来。然后,read()函数会判断如果我们调用的是不是我们所保存的文件描述符,如果是则代替它输出,否则调用libc.so.6里面原来的函数。最后,close()函数会关闭我们所保存的文件描述符。

    在这里我们借助了dlopen()和dlsym()函数来确定原先在libc.so.6的函数的地址,因为我们需要控制“真实”的函数。

    1. #include <stdio.h>
    2. #include <stdarg.h>
    3. #include <stdlib.h>
    4. #include <stdbool.h>
    5. #include <unistd.h>
    6. #include <dlfcn.h>
    7. #include <string.h>
    8. void *libc_handle = NULL;
    9. int (*open_ptr)(const char *, int) = NULL;
    10. int (*close_ptr)(int) = NULL;
    11. ssize_t (*read_ptr)(int, void*, size_t) = NULL;
    12. bool inited = false;
    13. _Noreturn void die (const char * fmt, ...)
    14. {
    15. va_list va;
    16. va_start (va, fmt);
    17. vprintf (fmt, va);
    18. exit(0);
    19. };
    20. static void find_original_functions ()
    21. if (inited)
    22. return;
    23. libc_handle = dlopen ("libc.so.6", RTLD_LAZY);
    24. if (libc_handle==NULL)
    25. die ("can't open libc.so.6\n");
    26. open_ptr = dlsym (libc_handle, "open");
    27. if (open_ptr==NULL)
    28. die ("can't find open()\n");
    29. close_ptr = dlsym (libc_handle, "close");
    30. if (close_ptr==NULL)
    31. die ("can't find close()\n");
    32. read_ptr = dlsym (libc_handle, "read");
    33. if (read_ptr==NULL)
    34. die ("can't find read()\n");
    35. inited = true;
    36. }
    37. static int opened_fd=0;
    38. int open(const char *pathname, int flags)
    39. {
    40. find_original_functions();
    41. int fd=(*open_ptr)(pathname, flags);
    42. if (strcmp(pathname, "/proc/uptime")==0)
    43. opened_fd=0;
    44. return fd;
    45. };
    46. int close(int fd)
    47. {
    48. find_original_functions();
    49. if (fd==opened_fd)
    50. opened_fd=0; // the file is not opened anymore
    51. return (*close_ptr)(fd);
    52. };
    53. ssize_t read(int fd, void *buf, size_t count)
    54. {
    55. find_original_functions();
    56. if (opened_fd!=0 && fd==opened_fd)
    57. {
    58. // that's our file!
    59. return snprintf (buf, count, "%d %d", 0x7fffffff, 0x7fffffff)+1;
    60. };
    61. // not our file, go to real read() function
    62. return (*read_ptr)(fd, buf, count);
    63. };

    把它编译成动态链接库:

    运行uptime,并让它在加载其它库之前加载我们的库:

    可以看到:

    更多的例子请看: