2015-06-30 7 views
6

Я пытаюсь изменить поведение по умолчанию в системном вызове Linux. На данный момент я пытаюсь подключить и добавить простой оператор печати, прежде чем они будут вызваны. Я знаю о стандартной опции «обертывания» GCC-компоновщика и о том, как ее можно использовать для обертки оберток Link to GCC Linker options. Это отлично работает для open(), fstat(), fwrite() и т. Д. (Где я фактически подключаю оболочки libc).Как подключить ВСЕ системные вызовы linux во время двоичного выполнения

UPDATE:

Ограничение, что не все системные вызовы получает зацепили с этим подходом. Чтобы проиллюстрировать это, возьмем простой статически скомпилированный двоичный файл. Когда мы пытаемся добавлять обертки, они получают осуществляется от вызовов, которые мы вводим после основных() (Пожалуйста, смотрите вывод Strace показанный ниже)

> strace ./sample 

execve("./sample", ["./sample"], [/* 72 vars */]) = 0 
uname({sys="Linux", node="kumar", ...}) = 0 
brk(0)         = 0x71f000 
brk(0x7201c0)       = 0x7201c0 
arch_prctl(ARCH_SET_FS, 0x71f880)  = 0 
readlink("/proc/self/exe", "/home/admin/sample"..., 4096) = 41 
brk(0x7411c0)       = 0x7411c0 
brk(0x742000)       = 0x742000 
access("/etc/ld.so.nohwcap", F_OK)  = -1 ENOENT (No such file or directory) 
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 4), ...}) = 0 
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fbcc54d1000 
write(1, "Hello from the wrapped readlink "..., 36Hello from the wrapped readlink :з 
) = 36 
readlink("/usr/bin/gnome-www-browser", "/etc/alternatives/gnome-www-brow"..., 255) = 35 
write(1, "/etc/alternatives/gnome-www-brow"..., 36/etc/alternatives/gnome-www-browser 
) = 36 
exit_group(36)       = ? 
+++ exited with 36 +++ 

Если мы замечаем бинарные тщательно первый «не-перехвачены «call readlink() (системный вызов 89, т.е. 0x59), поступает из этих строк - часть кода, связанная с компоновщиком (то есть _dl_get_origin) выполняет readlink() для ее функционирования. Эти неявные syscall (хотя и присутствующие в двоичном коде) никогда не подхватываются нашим «обертыванием» подхода.

000000000051875c <_dl_get_origin>: 
    51875c:  b8 59 00 00 00   mov $0x59,%eax 
    518761:  55      push %rbp 
    518762:  53      push %rbx 
    518763:  48 81 ec 00 10 00 00 sub $0x1000,%rsp 
    51876a:  48 89 e6    mov %rsp,%rsi 
    51876d:  0f 05     syscall 

Как продлить идею оберточной на системные вызовы, как readlink() (в том числе все имплицитных время вызываются)?

+0

Я делаю статические ссылки для простоты. Поэтому я думаю, что LD_PRELOAD мне не помогает. –

ответ

1

л.д. имеет возможность для упаковки, цитаты from manual:

--wrap символ

Используйте функцию обертки для символа. Любая неопределенная ссылка на символ будет разрешена __wrap_symbol. Любая неопределенная ссылка на __real_symbol будет разрешена к символу. Это можно использовать для предоставления обертки для системной функции. Функцию обертки следует называть __wrap_symbol. Если он хочет вызвать системную функцию, он должен вызывать __real_symbol.

Он отлично работает с системными вызовами. Вот пример с readlink:

#include <stdio.h> 
#include <string.h> 
#include <unistd.h> 

ssize_t __real_readlink(const char *path, char *buf, size_t bufsiz); 

ssize_t __wrap_readlink(const char *path, char *buf, size_t bufsiz) { 
    puts("Hello from the wrapped readlink :з"); 
    __real_readlink(path, buf, bufsiz); 
} 

int main(void) { 
    const char testLink[] = "/usr/bin/gnome-www-browser"; 
    char buf[256]; 
    memset(buf, 0, sizeof(buf)); 
    readlink(testLink, buf, sizeof(buf)-1); 
    puts(buf); 
} 

Чтобы передать опции линкера с использованием компилятора -Wl вариант:

$ gcc test.c -o a -Wl,--wrap=readlink 
$ ./a 
Hello from the wrapped readlink :з 
/etc/alternatives/gnome-www-browser 

Идея заключается в том, что __wrap_func ваша функция упаковщик. Компонент __real_func будет связываться с реальной функцией func. И каждый вызов func в коде будет заменен на __wrap_func.

UPD: Можно заметить, что бинарный файл, составленный статически, вызывает другой readlink, которые не перехватываются.Чтобы понять причину, просто сделать небольшой эксперимент - компиляции кода в файл объекта и список символов, например:

$ gcc test.c -c -o a.o -Wl,--wrap=readlink 
$ nm a.o 
0000000000000037 T main 
       U memset 
       U puts 
       U readlink 
       U __real_readlink 
       U __stack_chk_fail 
0000000000000000 T __wrap_readlink 

Самое интересное здесь то, что вы не будете видеть ссылки на кучу функций это видение с strace перед входом в главную функцию - например uname(), brk(), access() и т. Д. Это потому, что основная функция - это не первый код, вызываемый в вашем двоичном файле. A bit of research with objdump покажет вам, что первая функция называется _start.

Теперь, давайте сделаем еще один пример - переопределить _start функцию:

$ cat test2.c 
#include <stdio.h> 
#include <unistd.h> 

void _start() { 
     puts("Hello"); 
     _exit(0); 
} 
$ gcc test2.c -o a -nostartfiles 
$ strace ./a 
execve("./a", ["./a"], [/* 69 vars */]) = 0 
brk(0)         = 0x150c000 
access("/etc/ld.so.nohwcap", F_OK)  = -1 ENOENT (No such file or directory) 
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f3ece55d000 
access("/etc/ld.so.preload", R_OK)  = -1 ENOENT (No such file or directory) 
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 
fstat(3, {st_mode=S_IFREG|0644, st_size=177964, ...}) = 0 
mmap(NULL, 177964, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f3ece531000 
close(3)        = 0 
access("/etc/ld.so.nohwcap", F_OK)  = -1 ENOENT (No such file or directory) 
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3 
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\320\37\2\0\0\0\0\0"..., 832) = 832 
fstat(3, {st_mode=S_IFREG|0755, st_size=1840928, ...}) = 0 
mmap(NULL, 3949248, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f3ecdf78000 
mprotect(0x7f3ece133000, 2093056, PROT_NONE) = 0 
mmap(0x7f3ece332000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1ba000) = 0x7f3ece332000 
mmap(0x7f3ece338000, 17088, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f3ece338000 
close(3)        = 0 
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f3ece530000 
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f3ece52e000 
arch_prctl(ARCH_SET_FS, 0x7f3ece52e740) = 0 
mprotect(0x7f3ece332000, 16384, PROT_READ) = 0 
mprotect(0x600000, 4096, PROT_READ)  = 0 
mprotect(0x7f3ece55f000, 4096, PROT_READ) = 0 
munmap(0x7f3ece531000, 177964)   = 0 
fstat(1, {st_mode=S_IFCHR|0600, st_rdev=makedev(136, 10), ...}) = 0 
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f3ece55c000 
write(1, "Hello\n", 6Hello 
)     = 6 
exit_group(0)       = ? 
+++ exited with 0 +++ 
$ 

Что это было ?! Мы просто переопределили первую функцию в двоичном файле и все еще видим системные вызовы - почему?

Фактически это происходит из-за того, что вызовы выполняются не вашим приложением, а ядром перед загрузкой приложения в память и разрешены для запуска.

UPD: Как мы видели ранее, функции не вызываются приложением. Честно говоря, я не мог найти, что делается для статических двоичных файлов после того, как оболочка вызывает execve для вашего приложения, но из списка это выглядит как каждый вызов, который вы видите, выполняемый самим ядром - без какого-либо побочного приложения, такого как динамический компоновщик которые не нужны для статических двоичных файлов (и потому, что существуют такие функции, как brk, который работает с сегментами данных).

Независимо от того, что вы, конечно же, не можете легко изменить это поведение, вам понадобится взлом. Поскольку, если вы можете легко переопределить функцию для кода, который выполняется перед вашим двоичным запуском, то есть из другого двоичного файла, - это будет большая черная дыра в безопасности, представьте себе: как только вам понадобятся права root, вы переопределите функцию с одним, чтобы выполнить ваш код, и немного подождите, пока какой-то демон с правами root выполняет сценарий и, таким образом, запускает ваш код в игру.

+0

Thaks для вашего сообщения. Но решение НЕ правильно. Вы подключаете readlink(), который вы добавили, а не по умолчанию readlink() исполняемые вызовы. Если вы используете strace, вы видите две readlinks, а ваш вывод printf - из readlink(), который вы ввели –

+0

readlink («/ proc/self/exe», «/ home/admin/sample» ..., 4096) = 41. Это никогда не зацепило, и это мой фактический вопрос –

+0

@SandhyaKumar что? Как тогда я мог бы получить выход '/ etc/alternatives/gnome-www-browser', если бы я не подключил системный вызов? –

Смежные вопросы