LD_PRELOAD 具有可能的静态共享库函数
LD_PRELOAD with possible static shared library functions
我的objective是hook linux上dlopen使用的open函数。出于某种原因,这段代码没有挂钩 dlopen->open,但它确实挂钩了我的 open main.c->open 版本。 dlopen 是否没有以某种方式使用我的符号?
编译过程如下:
gcc main.c -ldl -ggdb
gcc fake-open.c -o libexample.so -fPIC -shared
export LD_PRELOAD="$PWD/libexample.so"
当我运行程序时,一切正常。确保设置 LD_PRELOAD 变量..等等
问题来了,当我尝试挂钩直接或间接调用 dlopen 的 open 函数时,不知何故这个 "version" 的 open 不是 resolved/redirected/hooked 我的版本。
[main.c]
#include <dlfcn.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
puts("calling open");
int fd = open("/tmp/test.so", O_RDONLY|O_CLOEXEC);
puts("calling dlopen");
int *handle = dlopen("/tmp/test.so", RTLD_LAZY);
}
[fake-open.c]
#define _GNU_SOURCE
#include <stdio.h>
#include <dlfcn.h>
#include <sys/types.h>
#include <sys/stat.h>
//#include <fcntl.h>
int open(const char *pathname, int flags)
{
puts("from hooked..");
return 1;
}
控制台输出:
呼唤开放
从迷上..
正在调用 dlopen
我知道 dlopen 由于 strace 而以某种方式调用 open。
write(1, "calling open\n", 13calling open
) = 13
write(1, "from hooked..\n", 14from hooked..
) = 14
write(1, "calling dlopen\n", 15calling dlopen
) = 15
brk(0) = 0x804b000
brk(0x806c000) = 0x806c000
open("/tmp/test.so", O_RDONLY|O_CLOEXEC) = 3
read(3, "7ELF[=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=]`504[=12=][=12=][=12=]"..., 512) = 512
但是,由于某些原因,当 dlopen 调用 open 时,它并没有使用我的 open 版本。这必须是某种 运行 时间符号解析问题的链接,或者也许 dlopen 使用的是 open 的静态版本并且不需要在 运行 或加载时解析任何符号?
首先,与@usr 的回答相反,dlopen
open
库。
我们可以通过 运行在 GDB 下进行一个简单的测试来证实这一点:
// main.c
#include <dlfcn.h>
int main()
{
void *h = dlopen("./foo.so", RTLD_LAZY);
return 0;
}
// foo.c; compile with "gcc -fPIC -shared -o foo.so foo.c"
int foo() { return 0; }
让我们编译并运行这个:
gdb -q ./a.out
(gdb) start
Temporary breakpoint 1 at 0x400605: file main.c, line 4.
Starting program: /tmp/a.out
Temporary breakpoint 1, main () at main.c:4
4 void *h = dlopen("./foo.so", RTLD_LAZY);
(gdb) catch syscall open
Catchpoint 2 (syscall 'open' [2])
(gdb) c
Continuing.
Catchpoint 2 (call to syscall open), 0x00007ffff7df3497 in open64 () at ../sysdeps/unix/syscall-template.S:81
81 ../sysdeps/unix/syscall-template.S: No such file or directory.
(gdb) bt
#0 0x00007ffff7df3497 in open64 () at ../sysdeps/unix/syscall-template.S:81
#1 0x00007ffff7ddf5bd in open_verify (name=0x602010 "./foo.so", fbp=0x7fffffffd568, loader=<optimized out>, whatcode=<optimized out>, found_other_class=0x7fffffffd550, free_name=<optimized out>) at dl-load.c:1930
#2 0x00007ffff7de2d6f in _dl_map_object (loader=loader@entry=0x7ffff7ffe1c8, name=name@entry=0x4006a4 "./foo.so", type=type@entry=2, trace_mode=trace_mode@entry=0, mode=mode@entry=-1879048191, nsid=0) at dl-load.c:2543
#3 0x00007ffff7deea14 in dl_open_worker (a=a@entry=0x7fffffffdae8) at dl-open.c:235
#4 0x00007ffff7de9fc4 in _dl_catch_error (objname=objname@entry=0x7fffffffdad8, errstring=errstring@entry=0x7fffffffdae0, mallocedp=mallocedp@entry=0x7fffffffdad0, operate=operate@entry=0x7ffff7dee960 <dl_open_worker>, args=args@entry=0x7fffffffdae8) at dl-error.c:187
#5 0x00007ffff7dee37b in _dl_open (file=0x4006a4 "./foo.so", mode=-2147483647, caller_dlopen=<optimized out>, nsid=-2, argc=1, argv=0x7fffffffde28, env=0x7fffffffde38) at dl-open.c:661
#6 0x00007ffff7bd702b in dlopen_doit (a=a@entry=0x7fffffffdd00) at dlopen.c:66
#7 0x00007ffff7de9fc4 in _dl_catch_error (objname=0x7ffff7dd9110 <last_result+16>, errstring=0x7ffff7dd9118 <last_result+24>, mallocedp=0x7ffff7dd9108 <last_result+8>, operate=0x7ffff7bd6fd0 <dlopen_doit>, args=0x7fffffffdd00) at dl-error.c:187
#8 0x00007ffff7bd762d in _dlerror_run (operate=operate@entry=0x7ffff7bd6fd0 <dlopen_doit>, args=args@entry=0x7fffffffdd00) at dlerror.c:163
#9 0x00007ffff7bd70c1 in __dlopen (file=<optimized out>, mode=<optimized out>) at dlopen.c:87
#10 0x0000000000400614 in main () at main.c:4
这告诉您在 64 位系统上,dlopen
调用 open64
而不是 open
,因此您的插入器将无法工作(您需要插入 open64
代替)。
但是你在 32 位系统上(由 strace
打印的 0x806c000
等地址证明),堆栈跟踪如下所示:
#0 0xf7ff3774 in open () at ../sysdeps/unix/syscall-template.S:81
#1 0xf7fe1211 in open_verify (name=0x804b008 "./foo.so", fbp=fbp@entry=0xffffc93c, loader=0xf7ffd938, whatcode=whatcode@entry=0, found_other_class=found_other_class@entry=0xffffc933, free_name=free_name@entry=true) at dl-load.c:1930
#2 0xf7fe4114 in _dl_map_object (loader=loader@entry=0xf7ffd938, name=name@entry=0x8048590 "./foo.so", type=type@entry=2, trace_mode=trace_mode@entry=0, mode=mode@entry=-1879048191, nsid=0) at dl-load.c:2543
#3 0xf7feec14 in dl_open_worker (a=0xffffccdc) at dl-open.c:235
#4 0xf7feac06 in _dl_catch_error (objname=objname@entry=0xffffccd4, errstring=errstring@entry=0xffffccd8, mallocedp=mallocedp@entry=0xffffccd3, operate=operate@entry=0xf7feeb50 <dl_open_worker>, args=args@entry=0xffffccdc) at dl-error.c:187
#5 0xf7fee644 in _dl_open (file=0x8048590 "./foo.so", mode=-2147483647, caller_dlopen=0x80484ea <main+29>, nsid=<optimized out>, argc=1, argv=0xffffcf74, env=0xffffcf7c) at dl-open.c:661
#6 0xf7fafcbc in dlopen_doit (a=0xffffce90) at dlopen.c:66
#7 0xf7feac06 in _dl_catch_error (objname=0xf7fb3070 <last_result+12>, errstring=0xf7fb3074 <last_result+16>, mallocedp=0xf7fb306c <last_result+8>, operate=0xf7fafc30 <dlopen_doit>, args=0xffffce90) at dl-error.c:187
#8 0xf7fb037c in _dlerror_run (operate=operate@entry=0xf7fafc30 <dlopen_doit>, args=args@entry=0xffffce90) at dlerror.c:163
#9 0xf7fafd71 in __dlopen (file=0x8048590 "./foo.so", mode=1) at dlopen.c:87
#10 0x080484ea in main () at main.c:4
那么为什么 open_verify
对 open
的调用没有重定向到您的 open
插入器?
首先我们来看第1帧的实际调用指令:
(gdb) fr 1
#1 0xf7fe1211 in open_verify (name=0x804b008 "./foo.so", fbp=fbp@entry=0xffffc93c, loader=0xf7ffd938, whatcode=whatcode@entry=0, found_other_class=found_other_class@entry=0xffffc933, free_name=free_name@entry=true) at dl-load.c:1930
1930 dl-load.c: No such file or directory.
(gdb) x/i $pc-5
0xf7fe120c <open_verify+60>: call 0xf7ff3760 <open>
将此与第 10 帧中的调用指令进行比较:
(gdb) fr 10
#10 0x080484ea in main () at main.c:4
4 void *h = dlopen("./foo.so", RTLD_LAZY);
(gdb) x/i $pc-5
0x80484e5 <main+24>: call 0x80483c0 <dlopen@plt>
注意到有什么不同吗?
没错:来自 main
的调用通过过程链接 table (PLT),动态加载器 (ld-linux.so.2
) 解析为 适当定义。
但是第 1 帧中对 open
的调用没有通过 PLT(因此不可插入)。
这是为什么?因为该调用必须在 之前 起作用,所以 open
有任何其他可用的定义 - 它用于 而 libc.so.6
(通常提供 open
的定义)是 本身 正在加载(由动态加载程序)。
出于这个原因,动态加载器必须是完全独立的(实际上包含一个静态链接的 libc
子集副本)。
My objective is to hook the open function that dlopen on linux uses.
由于上述原因,objective 无法通过 LD_PRELOAD
实现。您需要使用其他一些挂钩机制,例如在 运行 时修补加载程序 executable 代码。
我的objective是hook linux上dlopen使用的open函数。出于某种原因,这段代码没有挂钩 dlopen->open,但它确实挂钩了我的 open main.c->open 版本。 dlopen 是否没有以某种方式使用我的符号?
编译过程如下:
gcc main.c -ldl -ggdb
gcc fake-open.c -o libexample.so -fPIC -shared
export LD_PRELOAD="$PWD/libexample.so"
当我运行程序时,一切正常。确保设置 LD_PRELOAD 变量..等等
问题来了,当我尝试挂钩直接或间接调用 dlopen 的 open 函数时,不知何故这个 "version" 的 open 不是 resolved/redirected/hooked 我的版本。
[main.c]
#include <dlfcn.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
puts("calling open");
int fd = open("/tmp/test.so", O_RDONLY|O_CLOEXEC);
puts("calling dlopen");
int *handle = dlopen("/tmp/test.so", RTLD_LAZY);
}
[fake-open.c]
#define _GNU_SOURCE
#include <stdio.h>
#include <dlfcn.h>
#include <sys/types.h>
#include <sys/stat.h>
//#include <fcntl.h>
int open(const char *pathname, int flags)
{
puts("from hooked..");
return 1;
}
控制台输出:
呼唤开放
从迷上..
正在调用 dlopen
我知道 dlopen 由于 strace 而以某种方式调用 open。
write(1, "calling open\n", 13calling open
) = 13
write(1, "from hooked..\n", 14from hooked..
) = 14
write(1, "calling dlopen\n", 15calling dlopen
) = 15
brk(0) = 0x804b000
brk(0x806c000) = 0x806c000
open("/tmp/test.so", O_RDONLY|O_CLOEXEC) = 3
read(3, "7ELF[=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=]`504[=12=][=12=][=12=]"..., 512) = 512
但是,由于某些原因,当 dlopen 调用 open 时,它并没有使用我的 open 版本。这必须是某种 运行 时间符号解析问题的链接,或者也许 dlopen 使用的是 open 的静态版本并且不需要在 运行 或加载时解析任何符号?
首先,与@usr 的回答相反,dlopen
open
库。
我们可以通过 运行在 GDB 下进行一个简单的测试来证实这一点:
// main.c
#include <dlfcn.h>
int main()
{
void *h = dlopen("./foo.so", RTLD_LAZY);
return 0;
}
// foo.c; compile with "gcc -fPIC -shared -o foo.so foo.c"
int foo() { return 0; }
让我们编译并运行这个:
gdb -q ./a.out
(gdb) start
Temporary breakpoint 1 at 0x400605: file main.c, line 4.
Starting program: /tmp/a.out
Temporary breakpoint 1, main () at main.c:4
4 void *h = dlopen("./foo.so", RTLD_LAZY);
(gdb) catch syscall open
Catchpoint 2 (syscall 'open' [2])
(gdb) c
Continuing.
Catchpoint 2 (call to syscall open), 0x00007ffff7df3497 in open64 () at ../sysdeps/unix/syscall-template.S:81
81 ../sysdeps/unix/syscall-template.S: No such file or directory.
(gdb) bt
#0 0x00007ffff7df3497 in open64 () at ../sysdeps/unix/syscall-template.S:81
#1 0x00007ffff7ddf5bd in open_verify (name=0x602010 "./foo.so", fbp=0x7fffffffd568, loader=<optimized out>, whatcode=<optimized out>, found_other_class=0x7fffffffd550, free_name=<optimized out>) at dl-load.c:1930
#2 0x00007ffff7de2d6f in _dl_map_object (loader=loader@entry=0x7ffff7ffe1c8, name=name@entry=0x4006a4 "./foo.so", type=type@entry=2, trace_mode=trace_mode@entry=0, mode=mode@entry=-1879048191, nsid=0) at dl-load.c:2543
#3 0x00007ffff7deea14 in dl_open_worker (a=a@entry=0x7fffffffdae8) at dl-open.c:235
#4 0x00007ffff7de9fc4 in _dl_catch_error (objname=objname@entry=0x7fffffffdad8, errstring=errstring@entry=0x7fffffffdae0, mallocedp=mallocedp@entry=0x7fffffffdad0, operate=operate@entry=0x7ffff7dee960 <dl_open_worker>, args=args@entry=0x7fffffffdae8) at dl-error.c:187
#5 0x00007ffff7dee37b in _dl_open (file=0x4006a4 "./foo.so", mode=-2147483647, caller_dlopen=<optimized out>, nsid=-2, argc=1, argv=0x7fffffffde28, env=0x7fffffffde38) at dl-open.c:661
#6 0x00007ffff7bd702b in dlopen_doit (a=a@entry=0x7fffffffdd00) at dlopen.c:66
#7 0x00007ffff7de9fc4 in _dl_catch_error (objname=0x7ffff7dd9110 <last_result+16>, errstring=0x7ffff7dd9118 <last_result+24>, mallocedp=0x7ffff7dd9108 <last_result+8>, operate=0x7ffff7bd6fd0 <dlopen_doit>, args=0x7fffffffdd00) at dl-error.c:187
#8 0x00007ffff7bd762d in _dlerror_run (operate=operate@entry=0x7ffff7bd6fd0 <dlopen_doit>, args=args@entry=0x7fffffffdd00) at dlerror.c:163
#9 0x00007ffff7bd70c1 in __dlopen (file=<optimized out>, mode=<optimized out>) at dlopen.c:87
#10 0x0000000000400614 in main () at main.c:4
这告诉您在 64 位系统上,dlopen
调用 open64
而不是 open
,因此您的插入器将无法工作(您需要插入 open64
代替)。
但是你在 32 位系统上(由 strace
打印的 0x806c000
等地址证明),堆栈跟踪如下所示:
#0 0xf7ff3774 in open () at ../sysdeps/unix/syscall-template.S:81
#1 0xf7fe1211 in open_verify (name=0x804b008 "./foo.so", fbp=fbp@entry=0xffffc93c, loader=0xf7ffd938, whatcode=whatcode@entry=0, found_other_class=found_other_class@entry=0xffffc933, free_name=free_name@entry=true) at dl-load.c:1930
#2 0xf7fe4114 in _dl_map_object (loader=loader@entry=0xf7ffd938, name=name@entry=0x8048590 "./foo.so", type=type@entry=2, trace_mode=trace_mode@entry=0, mode=mode@entry=-1879048191, nsid=0) at dl-load.c:2543
#3 0xf7feec14 in dl_open_worker (a=0xffffccdc) at dl-open.c:235
#4 0xf7feac06 in _dl_catch_error (objname=objname@entry=0xffffccd4, errstring=errstring@entry=0xffffccd8, mallocedp=mallocedp@entry=0xffffccd3, operate=operate@entry=0xf7feeb50 <dl_open_worker>, args=args@entry=0xffffccdc) at dl-error.c:187
#5 0xf7fee644 in _dl_open (file=0x8048590 "./foo.so", mode=-2147483647, caller_dlopen=0x80484ea <main+29>, nsid=<optimized out>, argc=1, argv=0xffffcf74, env=0xffffcf7c) at dl-open.c:661
#6 0xf7fafcbc in dlopen_doit (a=0xffffce90) at dlopen.c:66
#7 0xf7feac06 in _dl_catch_error (objname=0xf7fb3070 <last_result+12>, errstring=0xf7fb3074 <last_result+16>, mallocedp=0xf7fb306c <last_result+8>, operate=0xf7fafc30 <dlopen_doit>, args=0xffffce90) at dl-error.c:187
#8 0xf7fb037c in _dlerror_run (operate=operate@entry=0xf7fafc30 <dlopen_doit>, args=args@entry=0xffffce90) at dlerror.c:163
#9 0xf7fafd71 in __dlopen (file=0x8048590 "./foo.so", mode=1) at dlopen.c:87
#10 0x080484ea in main () at main.c:4
那么为什么 open_verify
对 open
的调用没有重定向到您的 open
插入器?
首先我们来看第1帧的实际调用指令:
(gdb) fr 1
#1 0xf7fe1211 in open_verify (name=0x804b008 "./foo.so", fbp=fbp@entry=0xffffc93c, loader=0xf7ffd938, whatcode=whatcode@entry=0, found_other_class=found_other_class@entry=0xffffc933, free_name=free_name@entry=true) at dl-load.c:1930
1930 dl-load.c: No such file or directory.
(gdb) x/i $pc-5
0xf7fe120c <open_verify+60>: call 0xf7ff3760 <open>
将此与第 10 帧中的调用指令进行比较:
(gdb) fr 10
#10 0x080484ea in main () at main.c:4
4 void *h = dlopen("./foo.so", RTLD_LAZY);
(gdb) x/i $pc-5
0x80484e5 <main+24>: call 0x80483c0 <dlopen@plt>
注意到有什么不同吗?
没错:来自 main
的调用通过过程链接 table (PLT),动态加载器 (ld-linux.so.2
) 解析为 适当定义。
但是第 1 帧中对 open
的调用没有通过 PLT(因此不可插入)。
这是为什么?因为该调用必须在 之前 起作用,所以 open
有任何其他可用的定义 - 它用于 而 libc.so.6
(通常提供 open
的定义)是 本身 正在加载(由动态加载程序)。
出于这个原因,动态加载器必须是完全独立的(实际上包含一个静态链接的 libc
子集副本)。
My objective is to hook the open function that dlopen on linux uses.
由于上述原因,objective 无法通过 LD_PRELOAD
实现。您需要使用其他一些挂钩机制,例如在 运行 时修补加载程序 executable 代码。