|
|
|||||||||||||||||||||||||||||
|
Практическое применение LD_PRELOAD или замещение функций в LinuxИсточник: habrahabr ValdikSS
В этой статье, мы будем использовать стандартную возможность динамического линкера - переменную окружения LD_PRELOAD, которая может загрузить вашу библиотеку до загрузки остальных.
Как это работает?Да очень просто - линкер загружает вашу библиотеку с вашими "стандартными" функциями первой, а кто первый - того и тапки. А вы из своей библиотеки можете загрузить уже реальную, и "проксировать" вызовы, попутно делая что вам угодно.
Реальный Use-Case #1: Блокируем mimeinfo.cache в OperaМне очень нравится браузер Opera. А еще я использую KDE. Opera не очень уважает приоритеты приложений KDE, и, зачастую, так и норовит открыть скачанный ZIP-архив в mcomix, PDF в imgur-uploader, в общем, вы уловили суть. Однако, если ей запретить читать файл mimeinfo.cache, то она все будет открывать через "kioclient exec", а он-то уж лучше знает, в чем я хочу открыть тот или иной файл. Чем может приложение открывать файл? На ум приходят две функции: fopen и open. В моем случае, opera использовала 64-битный аналог fopen - fopen64. Определить это можно, воспользовавшись утилитой ltrace, или просто посмотрев таблицу импорта утилитой objdump. Что нам нужно для написания библиотеки? Первым делом, нужно составить прототип оригинальной функции.
Реальный Use-Case #2: Превращаем файл в сокетЕсть у меня проприетарное приложение, которое использует прямой доступ к принтеру (файл устройства /dev/usb/lp0). Захотел я написать для него свой сервер в целях отладки. Что возвращает open()? Файловый дескриптор. Что возвращает socket()? Такой же файловый дескриптор, на котором совершенно так же работают read() и write(). Приступаем: #define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <dlfcn.h> #include <strings.h> #include <sys/socket.h> #include <netinet/in.h> static int (*orig_open)(char * filename, int flags) = NULL; int open(char * filename, int flags) { if (orig_open == NULL) orig_open = dlsym(RTLD_NEXT, "open"); if (strcmp(filename, "/dev/usb/lp0") == 0) { //opening tcp socket struct sockaddr_in servaddr, cliaddr; int socketfd = socket(AF_INET, SOCK_STREAM, 0); bzero(&servaddr,sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr=inet_addr("127.0.0.1"); // addr servaddr.sin_port=htons(32000); // port if (connect(socketfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == 0) printf("[Open] TCP Connected\n"); else printf("[Open] TCP Connection failed!\n"); return socketfd; } return orig_open(filename, flags); }
Не совсем реальный Use-Case #3: Перехват C++-методовС C++ все немного иначе. Скажем, есть у нас класс: class Testclass { int var = 0; public: int setvar(int val); int getvar(); }; #include <stdio.h> #include "class.h" void Testclass() { int var = 0; } int Testclass::setvar(int val) { printf("setvar!\n"); this->var = val; return 0; } int Testclass::getvar() { printf("getvar! %d\n", this->var); return this->var; } Но функции не будут называться "Testclass::getvar" и "Testclass::setvar" в результирующем файле. Чтобы узнать названия функций, достаточно посмотреть таблицу экспорта: nm -D libclass.so … 0000000000000770 T _Z9Testclassv 00000000000007b0 T _ZN9Testclass6getvarEv 0000000000000780 T _ZN9Testclass6setvarEi Это называется name mangling.Тут есть два выхода: либо сделать библиотеку-перехватчик на C++, описав класс так же, каким он был в оригинале, но, в этом случае, у вас, с большой вероятностью, будут проблемы с доступом к конкретному инстансу класса, либо же сделать библиотеку на C, назвав функцию так, как она экспортируется, в таком случае, первым параметром вам передастся указатель на инстанс: #define _GNU_SOURCE #include <stdio.h> #include <dlfcn.h> typedef int (*orig_getvar_type)(void* instance); int _ZN9Testclass6getvarEv(void* instance) { printf("Wrapped getvar! %d\n", instance); orig_getvar_type orig_getvar; orig_getvar = (orig_getvar_type)dlsym(RTLD_NEXT, "_ZN9Testclass6getvarEv"); printf("orig getvar %d\n", orig_getvar(instance)); return 0; }
Вот, собственно, и все, о чем хотелось рассказать. Надеюсь, это будет кому-то полезно. Ссылки по теме
|
|