Как перехватить доступ к файловой системе внутри dlopen ()? - PullRequest
5 голосов
/ 09 октября 2011

Я хочу перехватить весь доступ к файловой системе, который происходит внутри dlopen ().Сначала может показаться, что LD_PRELOAD или -Wl,-wrap, будут жизнеспособными решениями, но у меня возникли проблемы с их выполнением по ряду технических причин:

  • ld.so ужесопоставил свои собственные символы ко времени обработки LD_PRELOAD.Для меня не критично перехватывать начальную загрузку, но рабочие функции _dl_* в настоящее время разрешены, поэтому будущие вызовы проходят через них.Я думаю LD_PRELOAD слишком поздно.

  • Каким-то образом malloc обходит вышеуказанную проблему, потому что malloc() внутри ld.so не имеет функционала free(), он простовызовы memset().

  • Рабочие функции файловой системы, например __libc_read(), содержащиеся в ld.so, являются статическими, поэтому я не могу перехватить их с помощью -Wl,-wrap,__libc_read.

Это может означать, что мне нужно собрать свой собственный ld.so непосредственно из источника, а не связывать его в оболочку.Проблема заключается в том, что libc и rtld-libc построены из одного источника.Я знаю, что макрос IS_IN_rtld определен при сборке rtld-libc, но как я могу гарантировать, что существует только одна копия статических структур данных при экспорте функции открытого интерфейса?(Это вопрос системы сборки glibc, но я не нашел документации по этим деталям.)

Есть ли лучшие способы проникнуть внутрь dlopen()?

Примечание: я могу 't использовать специфичное для Linux решение, такое как FUSE, потому что оно предназначено для минимальных ядер "вычислительных узлов", которые не поддерживают такие вещи.

1 Ответ

3 голосов
/ 17 октября 2011

может показаться, что LD_PRELOAD или -Wl, -wrap, будут жизнеспособными решениями

Решение --wrap не может быть жизнеспособным: оно работает только во время (статического) соединения, и ваши ld.so и libc.so.6 и libdl.so.2 уже все связаны, поэтому сейчас слишком поздно используйте --wrap.

LD_PRELOAD мог бы сработать, за исключением ... ld.so учитывает тот факт, что dlopen() вызывает open() внутреннюю деталь реализации. Таким образом, он просто вызывает внутреннюю функцию __open, минуя PLT, и вашу способность вставлять open в нее.

Каким-то образом malloc обходит проблему

Это потому, что libc поддерживает пользователей, которые реализуют свои собственные malloc (например, для целей отладки). Таким образом, вызов, например, calloc из dlopen проходит через PLT и вставляется через LD_PRELOAD.

Это может означать, что мне нужно собрать свой собственный ld.so напрямую из исходного кода, а не связывать его в оболочку.

Что будет делать восстановленный ld.so? Я думаю, вы хотите, чтобы он вызывал __llibc_openlibc.so.6), но это не может сработать по очевидной причине: это ld.so, что open s libc.so.6 в первую очередь (при запуске процесса) .

Вы можете перестроить ld.so с вызовом __open, замененным вызовом open. Это заставит ld.so пройти PLT и выставить его на LD_PRELOAD вставку.

Если вы пойдете по этому пути, я предлагаю вам не перезаписывать систему ld.so новой копией (вероятность ошибиться и сделать систему не загружаемой слишком велика). Вместо этого установите его, например, на /usr/local/my-ld.so, а затем свяжите ваши двоичные файлы с -Wl,--dynamic-linker=/usr/local/my-ld.so.

Другая альтернатива: исправление во время выполнения. Это что-то вроде хака, но вы можете (как только вы получите контроль над main) просто сканировать .text из ld.so и искать CALL __open инструкции. Если ld.so не удален, вы можете найти как внутренние __open, так и функции, которые вы хотите исправить (например, open_verify в dl-load.c). Как только вы найдете интересную CALL, mprotect страницу, которая содержит его, для записи, и исправите адрес своего собственного посредника (который, в свою очередь, может вызвать __libc_open, если это необходимо), тогда mprotect it назад. Любое будущее dlopen() теперь будет проходить через вашего вставщика.

...