原理
Linux 允许让我们自己的动态库加载在其它动态库之前,甚至是系统库(libc.so.6
),如此我们可以通过自己实现动态库并提前加载来拦截系统调用。
具体例子参看: https://github.com/dingjingmaster/demo/tree/master/syscall/dlopen
编译之后根据Makefile提示设置环境变量,然后在终端内执行任何包含读写操作的命令,都会有相关打印输出。
实现过程
- 通过 dlopen() 打开动态库
- 使用 dlsym() 确对应系统调用的地址
- 自定义系统调用(自定义函数名并保证函数类型与系统调用一致),可以在自定义函数中实现系统调用拦截操作
- 将以上代码打包成动态库并编译为
xxx.so
- 当前终端配置环境变量:
export LD_PRELOAD=/path/to/xxx.so
- 在当前终端执行包含要拦截系统调用的命令
以拦截 open()
read()
系统调用为例
- 动态库源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
|
#include <stdio.h>
#include <dlfcn.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <stdbool.h>
void* libc_handle = NULL;
int (*open_ptr) (const char*, int) = NULL;
int (*close_ptr) (int) = NULL;
ssize_t (*read_ptr) (int, void*, size_t) = NULL;
static bool inited = false;
_Noreturn void die (const char* fmt, ...)
{
va_list va;
va_start (va, fmt);
vprintf (fmt, va);
_exit (0);
}
static void find_original_function ()
{
if (inited) return;
printf ("libc path: %s\n", LIBC);
libc_handle = dlopen (LIBC, RTLD_LAZY);
if (libc_handle == NULL) {
die ("cannot open libc.so\n");
}
open_ptr = dlsym (libc_handle, "open");
if (open_ptr == NULL) {
die ("cannot find open()\n");
}
close_ptr = dlsym (libc_handle, "close");
if (close_ptr == NULL) {
die ("cannot find close()\n");
}
read_ptr = dlsym (libc_handle, "read");
if (read_ptr == NULL) {
die ("cannot find read()\n");
}
inited = true;
}
int open (const char* pathName, int flag)
{
find_original_function();
printf ("start open()\n");
int fd = (*open_ptr) (pathName, flag);
printf ("end open()\n");
return fd;
}
int close (int fd)
{
find_original_function();
printf ("start close()\n");
int ret = (*close_ptr) (fd);
printf ("end close()\n");
return ret;
}
ssize_t read (int fd, void* buf, size_t count)
{
find_original_function();
printf ("start read()\n");
ssize_t ret = (*read_ptr) (fd, buf, count);
printf ("end read()\n");
return ret;
}
|
- 编译成动态库
1
|
gcc -fpic -shared -Wall -o dlopen-shared-lib.so dlopen-shared-lib.c -ldl -DLIBC=\"`find /usr/lib -name "libc.so.6"`\"
|
- 通过uptime命令验证
1
|
LD_PRELOAD=$PWD/dlopen-shared-lib.so && export LD_PRELOAD && uptime
|
uptime 命令会读取 /proc/uptime
文件