LD_PRELOAD 不能拦截系统调用,只能拦截libcalls?
LD_PRELOAD can not intercept syscalls, but only libcalls?
我的代码适用于 malloc
,但不适用于 mmap
。代码如下:
main.c
#include <stdio.h>
#include <stdlib.h>
int main(){
int * p = (int*) malloc(sizeof(int));
printf("in main(): value p = %d\n", *p);
free(p);
}
preload.c
#define _GNU_SOURCE
#include <time.h>
#include <dlfcn.h>
#include <stdio.h>
#include <sys/types.h>
void *(*orig_malloc)(size_t size);
void *malloc(size_t size){
printf(" Hooked(preload)! malloc:size:%lu\n", size);
return orig_malloc(size);
}
void * (*orig_mmap)(void *start, size_t length, int prot, int flags, int fd, off_t offset);
void * mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset){
printf(" Hooked(preload)! mmap:start:%p, length:%lu, prot:%d, flags:%p, fd:%p, offset:%d\n", start, length, prot, flags, fd, offset);
return orig_mmap(start, length, prot, flags, fd, offset);
}
void
_init(void)
{
printf("Loading hack.\n");
orig_malloc = (void* (*)(size_t)) dlsym(RTLD_NEXT, "malloc");
orig_mmap = (void* (*)(void*, size_t, int, int, int, off_t)) dlsym(RTLD_NEXT, "mmap");
}
编译它
gcc -Wall -fPIC -DPIC -c preload.c
ld -shared -o preload.so preload.o -ldl
gcc main.c
到运行它与LD_PRELOAD
LD_PRELOAD=./preload.so ./a.out
到 运行 它与 strace
strace ./a.out 2>&1 | view -
LD_PRELOAD
的打印输出不会挂钩对 mmap
的调用,而只会挂钩对 malloc
的调用。同时,当 运行 strace
时,打印输出显示 mmap
被多次调用。
这个结果让我很困惑;假设mmap
确实被main.c
调用了(我猜通过malloc
),为什么preload.c
不能拦截mmap
?
PS:我的平台是 Ubuntu 14.04,内核是 Linux 3.13
PS2:通过系统调用,我指的是 libc 中的系统调用包装器(虽然不确定这是否对问题有所影响)..
mmap 是系统调用,malloc 不是。
由于系统调用对于程序的运行至关重要,因此它们必须在 ld.so 实际开始运行之前工作,它们驻留在一个先于其他所有内容加载的部分;它可能是动态链接的,但是映射 (那个特定的 "virtual" 动态对象) 是由内核本身完成的。在 ld.so 真正开始工作之前 Looong。
您问题的标题实际上就是答案。
assuming mmap is indeed called by main.c (I guess through malloc)
所以你的 main.c 没有调用 库函数 mmap()
?当然你不能用这种方式拦截系统调用,你会怎么做?有些体系结构有 syscall
CPU 指令,有些使用特殊的中断......有很多方法,但它在任何情况下都与 C 调用约定完全不同。内核并没有以某种方式链接到你的二进制文件,而是在你的用户空间进程做某事时控制(通过一些硬件帮助)...... "special".
如果你想知道如何拦截系统调用,这当然是非常特定于平台的,但我建议你只看一下 strace
实用程序的源代码。你永远不会在 strace 中看到 malloc()
,因为这不是系统调用,malloc()
使用 mmap
系统调用。
另一方面,如果您将 lib 预加载到实际调用 libc mmap()
函数的二进制文件,它将按预期工作。
简而言之:libc mmap()
是 user-friendly 围绕 mmap
系统调用的包装器,具有以下主要内容:
#include <sys/mman.h>
int main()
{
void *test = mmap(0, 20, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS,
-1, 0);
return 0;
}
结果是:
Loading hack.
Hooked(preload)! mmap:start:(nil), length:20, prot:3, flags:0x22,
fd:0xffffffff, offset:0
strace
打印的 mmap
调用是 glibc 内部调用。
使用 LD_PRELOAD
:
拦截对 mmap
的 glibc 内部调用是不可能的
mmap
不在 /lib64/libc.so.6
的 .plt
部分,而是直接从 glibc 调用,因此 LD_PRELOAD
无法拦截 glibc 对 [=13 的调用=].
$ objdump -j .plt -d /lib64/libc.so.6
/lib64/libc.so.6: file format elf64-x86-64
Disassembly of section .plt:
000000000001f400 <*ABS*+0x8e3fb@plt-0x10>:
1f400: ff 35 02 ac 39 00 pushq 0x39ac02(%rip) # 3ba008 <_GLOBAL_OFFSET_TABLE_+0x8>
1f406: ff 25 04 ac 39 00 jmpq *0x39ac04(%rip) # 3ba010 <_GLOBAL_OFFSET_TABLE_+0x10>
1f40c: 0f 1f 40 00 nopl 0x0(%rax)
000000000001f410 <*ABS*+0x8e3fb@plt>:
1f410: ff 25 02 ac 39 00 jmpq *0x39ac02(%rip) # 3ba018 <_GLOBAL_OFFSET_TABLE_+0x18>
1f416: 68 0b 00 00 00 pushq [=10=]xb
1f41b: e9 e0 ff ff ff jmpq 1f400 <data.8467+0x1f390>
000000000001f420 <*ABS*+0xb8c10@plt>:
1f420: ff 25 fa ab 39 00 jmpq *0x39abfa(%rip) # 3ba020 <_GLOBAL_OFFSET_TABLE_+0x20>
1f426: 68 0a 00 00 00 pushq [=10=]xa
1f42b: e9 d0 ff ff ff jmpq 1f400 <data.8467+0x1f390>
000000000001f430 <realloc@plt>:
1f430: ff 25 f2 ab 39 00 jmpq *0x39abf2(%rip) # 3ba028 <_GLOBAL_OFFSET_TABLE_+0x28>
1f436: 68 00 00 00 00 pushq [=10=]x0
1f43b: e9 c0 ff ff ff jmpq 1f400 <data.8467+0x1f390>
000000000001f440 <malloc@plt>:
1f440: ff 25 ea ab 39 00 jmpq *0x39abea(%rip) # 3ba030 <_GLOBAL_OFFSET_TABLE_+0x30>
1f446: 68 01 00 00 00 pushq [=10=]x1
1f44b: e9 b0 ff ff ff jmpq 1f400 <data.8467+0x1f390>
000000000001f450 <__tls_get_addr@plt>:
1f450: ff 25 e2 ab 39 00 jmpq *0x39abe2(%rip) # 3ba038 <_GLOBAL_OFFSET_TABLE_+0x38>
1f456: 68 02 00 00 00 pushq [=10=]x2
1f45b: e9 a0 ff ff ff jmpq 1f400 <data.8467+0x1f390>
000000000001f460 <memalign@plt>:
1f460: ff 25 da ab 39 00 jmpq *0x39abda(%rip) # 3ba040 <_GLOBAL_OFFSET_TABLE_+0x40>
1f466: 68 03 00 00 00 pushq [=10=]x3
1f46b: e9 90 ff ff ff jmpq 1f400 <data.8467+0x1f390>
000000000001f470 <*ABS*+0x90f60@plt>:
1f470: ff 25 d2 ab 39 00 jmpq *0x39abd2(%rip) # 3ba048 <_GLOBAL_OFFSET_TABLE_+0x48>
1f476: 68 09 00 00 00 pushq [=10=]x9
1f47b: e9 80 ff ff ff jmpq 1f400 <data.8467+0x1f390>
000000000001f480 <_dl_find_dso_for_object@plt>:
1f480: ff 25 ca ab 39 00 jmpq *0x39abca(%rip) # 3ba050 <_GLOBAL_OFFSET_TABLE_+0x50>
1f486: 68 04 00 00 00 pushq [=10=]x4
1f48b: e9 70 ff ff ff jmpq 1f400 <data.8467+0x1f390>
000000000001f490 <calloc@plt>:
1f490: ff 25 c2 ab 39 00 jmpq *0x39abc2(%rip) # 3ba058 <_GLOBAL_OFFSET_TABLE_+0x58>
1f496: 68 05 00 00 00 pushq [=10=]x5
1f49b: e9 60 ff ff ff jmpq 1f400 <data.8467+0x1f390>
000000000001f4a0 <free@plt>:
1f4a0: ff 25 ba ab 39 00 jmpq *0x39abba(%rip) # 3ba060 <_GLOBAL_OFFSET_TABLE_+0x60>
1f4a6: 68 06 00 00 00 pushq [=10=]x6
1f4ab: e9 50 ff ff ff jmpq 1f400 <data.8467+0x1f390>
000000000001f4b0 <*ABS*+0xb8bc0@plt>:
1f4b0: ff 25 b2 ab 39 00 jmpq *0x39abb2(%rip) # 3ba068 <_GLOBAL_OFFSET_TABLE_+0x68>
1f4b6: 68 08 00 00 00 pushq [=10=]x8
1f4bb: e9 40 ff ff ff jmpq 1f400 <data.8467+0x1f390>
000000000001f4c0 <*ABS*+0x8ec70@plt>:
1f4c0: ff 25 aa ab 39 00 jmpq *0x39abaa(%rip) # 3ba070 <_GLOBAL_OFFSET_TABLE_+0x70>
1f4c6: 68 07 00 00 00 pushq [=10=]x7
1f4cb: e9 30 ff ff ff jmpq 1f400 <data.8467+0x1f390>
[m@localhost ~]$
在 glibc 中对 mmap
的调用不会通过 .plt
条目调用它,而是直接调用,不可能拦截这些调用:
$ objdump -d /lib64/libc.so.6 | grep mmap
[...]
81628: e8 83 ad 07 00 callq fc3b0 <mmap>
8177c: e8 2f ac 07 00 callq fc3b0 <mmap>
00000000000fc3b0 <mmap>:
fc3c0: 73 01 jae fc3c3 <mmap+0x13>
13a267: e8 44 21 fc ff callq fc3b0 <mmap>
$
00000000000fc3b0 <mmap>:
fc3b0: 49 89 ca mov %rcx,%r10
fc3b3: b8 09 00 00 00 mov [=12=]x9,%eax
fc3b8: 0f 05 syscall
fc3ba: 48 3d 01 f0 ff ff cmp [=12=]xfffffffffffff001,%rax
fc3c0: 73 01 jae fc3c3 <mmap+0x13>
fc3c2: c3 retq
fc3c3: 48 8b 0d 96 da 2b 00 mov 0x2bda96(%rip),%rcx # 3b9e60 <_DYNAMIC+0x2e0>
fc3ca: f7 d8 neg %eax
fc3cc: 64 89 01 mov %eax,%fs:(%rcx)
fc3cf: 48 83 c8 ff or [=12=]xffffffffffffffff,%rax
fc3d3: c3 retq
fc3d4: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
fc3db: 00 00 00
fc3de: 66 90 xchg %ax,%ax
我的代码适用于 malloc
,但不适用于 mmap
。代码如下:
main.c
#include <stdio.h>
#include <stdlib.h>
int main(){
int * p = (int*) malloc(sizeof(int));
printf("in main(): value p = %d\n", *p);
free(p);
}
preload.c
#define _GNU_SOURCE
#include <time.h>
#include <dlfcn.h>
#include <stdio.h>
#include <sys/types.h>
void *(*orig_malloc)(size_t size);
void *malloc(size_t size){
printf(" Hooked(preload)! malloc:size:%lu\n", size);
return orig_malloc(size);
}
void * (*orig_mmap)(void *start, size_t length, int prot, int flags, int fd, off_t offset);
void * mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset){
printf(" Hooked(preload)! mmap:start:%p, length:%lu, prot:%d, flags:%p, fd:%p, offset:%d\n", start, length, prot, flags, fd, offset);
return orig_mmap(start, length, prot, flags, fd, offset);
}
void
_init(void)
{
printf("Loading hack.\n");
orig_malloc = (void* (*)(size_t)) dlsym(RTLD_NEXT, "malloc");
orig_mmap = (void* (*)(void*, size_t, int, int, int, off_t)) dlsym(RTLD_NEXT, "mmap");
}
编译它
gcc -Wall -fPIC -DPIC -c preload.c
ld -shared -o preload.so preload.o -ldl
gcc main.c
到运行它与LD_PRELOAD
LD_PRELOAD=./preload.so ./a.out
到 运行 它与 strace
strace ./a.out 2>&1 | view -
LD_PRELOAD
的打印输出不会挂钩对 mmap
的调用,而只会挂钩对 malloc
的调用。同时,当 运行 strace
时,打印输出显示 mmap
被多次调用。
这个结果让我很困惑;假设mmap
确实被main.c
调用了(我猜通过malloc
),为什么preload.c
不能拦截mmap
?
PS:我的平台是 Ubuntu 14.04,内核是 Linux 3.13
PS2:通过系统调用,我指的是 libc 中的系统调用包装器(虽然不确定这是否对问题有所影响)..
mmap 是系统调用,malloc 不是。
由于系统调用对于程序的运行至关重要,因此它们必须在 ld.so 实际开始运行之前工作,它们驻留在一个先于其他所有内容加载的部分;它可能是动态链接的,但是映射 (那个特定的 "virtual" 动态对象) 是由内核本身完成的。在 ld.so 真正开始工作之前 Looong。
您问题的标题实际上就是答案。
assuming mmap is indeed called by main.c (I guess through malloc)
所以你的 main.c 没有调用 库函数 mmap()
?当然你不能用这种方式拦截系统调用,你会怎么做?有些体系结构有 syscall
CPU 指令,有些使用特殊的中断......有很多方法,但它在任何情况下都与 C 调用约定完全不同。内核并没有以某种方式链接到你的二进制文件,而是在你的用户空间进程做某事时控制(通过一些硬件帮助)...... "special".
如果你想知道如何拦截系统调用,这当然是非常特定于平台的,但我建议你只看一下 strace
实用程序的源代码。你永远不会在 strace 中看到 malloc()
,因为这不是系统调用,malloc()
使用 mmap
系统调用。
另一方面,如果您将 lib 预加载到实际调用 libc mmap()
函数的二进制文件,它将按预期工作。
简而言之:libc mmap()
是 user-friendly 围绕 mmap
系统调用的包装器,具有以下主要内容:
#include <sys/mman.h>
int main()
{
void *test = mmap(0, 20, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS,
-1, 0);
return 0;
}
结果是:
Loading hack.
Hooked(preload)! mmap:start:(nil), length:20, prot:3, flags:0x22,
fd:0xffffffff, offset:0
strace
打印的 mmap
调用是 glibc 内部调用。
使用 LD_PRELOAD
:
mmap
的 glibc 内部调用是不可能的
mmap
不在 /lib64/libc.so.6
的 .plt
部分,而是直接从 glibc 调用,因此 LD_PRELOAD
无法拦截 glibc 对 [=13 的调用=].
$ objdump -j .plt -d /lib64/libc.so.6
/lib64/libc.so.6: file format elf64-x86-64
Disassembly of section .plt:
000000000001f400 <*ABS*+0x8e3fb@plt-0x10>:
1f400: ff 35 02 ac 39 00 pushq 0x39ac02(%rip) # 3ba008 <_GLOBAL_OFFSET_TABLE_+0x8>
1f406: ff 25 04 ac 39 00 jmpq *0x39ac04(%rip) # 3ba010 <_GLOBAL_OFFSET_TABLE_+0x10>
1f40c: 0f 1f 40 00 nopl 0x0(%rax)
000000000001f410 <*ABS*+0x8e3fb@plt>:
1f410: ff 25 02 ac 39 00 jmpq *0x39ac02(%rip) # 3ba018 <_GLOBAL_OFFSET_TABLE_+0x18>
1f416: 68 0b 00 00 00 pushq [=10=]xb
1f41b: e9 e0 ff ff ff jmpq 1f400 <data.8467+0x1f390>
000000000001f420 <*ABS*+0xb8c10@plt>:
1f420: ff 25 fa ab 39 00 jmpq *0x39abfa(%rip) # 3ba020 <_GLOBAL_OFFSET_TABLE_+0x20>
1f426: 68 0a 00 00 00 pushq [=10=]xa
1f42b: e9 d0 ff ff ff jmpq 1f400 <data.8467+0x1f390>
000000000001f430 <realloc@plt>:
1f430: ff 25 f2 ab 39 00 jmpq *0x39abf2(%rip) # 3ba028 <_GLOBAL_OFFSET_TABLE_+0x28>
1f436: 68 00 00 00 00 pushq [=10=]x0
1f43b: e9 c0 ff ff ff jmpq 1f400 <data.8467+0x1f390>
000000000001f440 <malloc@plt>:
1f440: ff 25 ea ab 39 00 jmpq *0x39abea(%rip) # 3ba030 <_GLOBAL_OFFSET_TABLE_+0x30>
1f446: 68 01 00 00 00 pushq [=10=]x1
1f44b: e9 b0 ff ff ff jmpq 1f400 <data.8467+0x1f390>
000000000001f450 <__tls_get_addr@plt>:
1f450: ff 25 e2 ab 39 00 jmpq *0x39abe2(%rip) # 3ba038 <_GLOBAL_OFFSET_TABLE_+0x38>
1f456: 68 02 00 00 00 pushq [=10=]x2
1f45b: e9 a0 ff ff ff jmpq 1f400 <data.8467+0x1f390>
000000000001f460 <memalign@plt>:
1f460: ff 25 da ab 39 00 jmpq *0x39abda(%rip) # 3ba040 <_GLOBAL_OFFSET_TABLE_+0x40>
1f466: 68 03 00 00 00 pushq [=10=]x3
1f46b: e9 90 ff ff ff jmpq 1f400 <data.8467+0x1f390>
000000000001f470 <*ABS*+0x90f60@plt>:
1f470: ff 25 d2 ab 39 00 jmpq *0x39abd2(%rip) # 3ba048 <_GLOBAL_OFFSET_TABLE_+0x48>
1f476: 68 09 00 00 00 pushq [=10=]x9
1f47b: e9 80 ff ff ff jmpq 1f400 <data.8467+0x1f390>
000000000001f480 <_dl_find_dso_for_object@plt>:
1f480: ff 25 ca ab 39 00 jmpq *0x39abca(%rip) # 3ba050 <_GLOBAL_OFFSET_TABLE_+0x50>
1f486: 68 04 00 00 00 pushq [=10=]x4
1f48b: e9 70 ff ff ff jmpq 1f400 <data.8467+0x1f390>
000000000001f490 <calloc@plt>:
1f490: ff 25 c2 ab 39 00 jmpq *0x39abc2(%rip) # 3ba058 <_GLOBAL_OFFSET_TABLE_+0x58>
1f496: 68 05 00 00 00 pushq [=10=]x5
1f49b: e9 60 ff ff ff jmpq 1f400 <data.8467+0x1f390>
000000000001f4a0 <free@plt>:
1f4a0: ff 25 ba ab 39 00 jmpq *0x39abba(%rip) # 3ba060 <_GLOBAL_OFFSET_TABLE_+0x60>
1f4a6: 68 06 00 00 00 pushq [=10=]x6
1f4ab: e9 50 ff ff ff jmpq 1f400 <data.8467+0x1f390>
000000000001f4b0 <*ABS*+0xb8bc0@plt>:
1f4b0: ff 25 b2 ab 39 00 jmpq *0x39abb2(%rip) # 3ba068 <_GLOBAL_OFFSET_TABLE_+0x68>
1f4b6: 68 08 00 00 00 pushq [=10=]x8
1f4bb: e9 40 ff ff ff jmpq 1f400 <data.8467+0x1f390>
000000000001f4c0 <*ABS*+0x8ec70@plt>:
1f4c0: ff 25 aa ab 39 00 jmpq *0x39abaa(%rip) # 3ba070 <_GLOBAL_OFFSET_TABLE_+0x70>
1f4c6: 68 07 00 00 00 pushq [=10=]x7
1f4cb: e9 30 ff ff ff jmpq 1f400 <data.8467+0x1f390>
[m@localhost ~]$
在 glibc 中对 mmap
的调用不会通过 .plt
条目调用它,而是直接调用,不可能拦截这些调用:
$ objdump -d /lib64/libc.so.6 | grep mmap
[...]
81628: e8 83 ad 07 00 callq fc3b0 <mmap>
8177c: e8 2f ac 07 00 callq fc3b0 <mmap>
00000000000fc3b0 <mmap>:
fc3c0: 73 01 jae fc3c3 <mmap+0x13>
13a267: e8 44 21 fc ff callq fc3b0 <mmap>
$
00000000000fc3b0 <mmap>:
fc3b0: 49 89 ca mov %rcx,%r10
fc3b3: b8 09 00 00 00 mov [=12=]x9,%eax
fc3b8: 0f 05 syscall
fc3ba: 48 3d 01 f0 ff ff cmp [=12=]xfffffffffffff001,%rax
fc3c0: 73 01 jae fc3c3 <mmap+0x13>
fc3c2: c3 retq
fc3c3: 48 8b 0d 96 da 2b 00 mov 0x2bda96(%rip),%rcx # 3b9e60 <_DYNAMIC+0x2e0>
fc3ca: f7 d8 neg %eax
fc3cc: 64 89 01 mov %eax,%fs:(%rcx)
fc3cf: 48 83 c8 ff or [=12=]xffffffffffffffff,%rax
fc3d3: c3 retq
fc3d4: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
fc3db: 00 00 00
fc3de: 66 90 xchg %ax,%ax