LD_PRELOAD can not intercept syscalls, but only libcalls?

最后都变了- 提交于 2019-12-22 10:47:02

问题


My code works well with malloc, but not with mmap. The code is below:

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");
}

to compile it

gcc -Wall -fPIC -DPIC -c preload.c
ld -shared -o preload.so preload.o -ldl
gcc main.c

to run it with LD_PRELOAD

LD_PRELOAD=./preload.so ./a.out

to run it with strace

strace ./a.out 2>&1 | view -

the printout from LD_PRELOAD does not hook calls to mmap, but only calls to malloc. Meanwhile, when running with strace, the printout does show mmap is called multiple times.

This result baffles me; assuming mmap is indeed called by main.c (I guess through malloc), how come preload.c can not intercept mmap?

PS: My platform is Ubuntu 14.04 with Linux kernel 3.13

PS2: By syscall, I mean the syscall wrapper in libc (not sure if this makes a difference to the question though)..


回答1:


mmap is a syscall, malloc is not.

Since syscalls are essential for the functioning of a program, they must work before ld.so actually springs into action, they are reside in a section that gets loaded before everything else; it may be linked dynamically, but that mapping (of that particular "virtual" dynamic object) is done by the kernel itself. Looong before ld.so actually gets to work.




回答2:


The mmap calls printed by strace are glibc-internal. It's impossible to intercept the glibc-internal calls to mmap with LD_PRELOAD:

mmap is not in the .plt-section of /lib64/libc.so.6 but is called directly from glibc and therefore LD_PRELOAD can't intercept glibc's calls to mmap.

$ 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  $0xb
   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  $0xa
   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  $0x0
   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  $0x1
   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  $0x2
   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  $0x3
   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  $0x9
   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  $0x4
   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  $0x5
   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  $0x6
   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  $0x8
   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  $0x7
   1f4cb:   e9 30 ff ff ff          jmpq   1f400 <data.8467+0x1f390>
[m@localhost ~]$ 

Calls to mmap in glibc don't call it via a .plt entry but directly, it's impossible to intercept these calls:

$ 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    $0x9,%eax
   fc3b8:   0f 05                   syscall 
   fc3ba:   48 3d 01 f0 ff ff       cmp    $0xfffffffffffff001,%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     $0xffffffffffffffff,%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



回答3:


The title to your question is actually the answer.

assuming mmap is indeed called by main.c (I guess through malloc)

So your main.c doesn't call the library function mmap()? Of course you can't intercept syscalls this way, how would you do it? Some architectures have a syscall CPU instruction, some use a special interrupt ... there are a lot of ways, but it's in any case completely different from C calling conventions. The kernel is not somehow linked to your binary but takes control (with some hardware assistence) when your userspace process does something ... "special".

If you want to know how to intercept syscalls, this is of course very platform specific, but I would advise you to just take a look in the source of the strace utility. You will never see an malloc() in strace, because this is not a syscall, malloc() uses the mmap syscall.

On the other hand, if you preload your lib to a binary that actually calls the libc mmap() function, it will work as expected.

In a nutshell: libc mmap() is a user-friendly wrapper around the mmap syscall and with the following main:

#include <sys/mman.h>

int main()
{
    void *test = mmap(0, 20, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS,
        -1, 0);
    return 0;
}

The result is:

Loading hack.
Hooked(preload)! mmap:start:(nil), length:20, prot:3, flags:0x22,
fd:0xffffffff, offset:0



来源:https://stackoverflow.com/questions/31438697/ld-preload-can-not-intercept-syscalls-but-only-libcalls

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!