Функция подключения и замены в загруженном ELF (совместно используемая библиотека.so)

Я пишу код на C, чтобы перехватить некоторую функцию.so ELF (shared-library), загруженную в память.

Мой C-код должен иметь возможность перенаправлять функцию экспорта другой библиотеки.so, которая была загружена в память приложения / программы.

Вот немного проработки:

В приложении Android будет загружено несколько файлов.so. Мой код C должен просматривать функцию экспорта, которая принадлежит другой общей библиотеке.so (в данном случае называемой target.so)

Это не обычный подход dlsym, потому что я не просто хочу адрес функции, а хочу заменить его своим собственным функционалом; в этом: когда другая библиотека выполняет вызов своей собственной функции, вместо этого вызывается мой hook_func, а затем из моего hook_func я должен вызывать original_func.

Для функций импорта это может работать. Но для функций экспорта я не уверен, как это сделать. Функции импорта имеют записи в таблице символов, которые имеют соответствующую запись в таблице перемещений, которая в конечном итоге дает адрес записи в глобальной таблице смещений (GOT). Но для функций экспорта сам элемент st_value символа имеет адрес процедуры, а не адрес GOT (исправьте меня, если я ошибаюсь).

Как мне выполнить перехват для функции экспорта?

Теоретически, я должен получить место в памяти st_value элемент записи таблицы динамических символов (Elf32_Sym) функции экспорта. Если я получу это местоположение, то смогу заменить значение в этом месте на адрес моего hook_func. Тем не менее, я не могу написать в этом месте до сих пор. Я должен предположить, что память таблицы динамических символов доступна только для чтения. Если это правда, то каков обходной путь в этом случае?

Большое спасибо за чтение и помощь мне.

Обновление: LD_PRELOAD может заменить только оригинальные функции на мои собственные, но тогда я не уверен, есть ли способ вызвать оригиналы. В моем случае, например:

Приложение инициализирует аудио движок, вызывая Audio_System_Create и передает ссылку на AUDIO_SYSTEM Возражать Audio_System_Create(AUDIO_SYSTEM **); AUDIO API распределяет эту структуру / объект и возвращает функцию. Теперь, если бы я мог получить доступ к этому AUDIO_SYSTEM объект, я бы легко прикрепить обратный вызов к этому объекту и начать получать аудиоданные. Следовательно, моя конечная цель - получить ссылку на AUIOD_SYSTEM объект; и, насколько я понимаю, я могу получить это только в том случае, если перехватываю вызов, когда этот объект сначала выделяется Audio_System_Create(AUIOD_SYSTEM **), В настоящее время нет прямого способа получить выходной аудио на Android. (все примеры говорят о записи звука только с микрофона)

Обновление 2: Как посоветовал Бэйзил в своем ответе, я использовал dladdr(), но, как ни странно, он дает мне тот же адрес, что и я.

void *pFunc=procedure_addr;  //procedure address calculated from the st_value of symbol from symbol table in ELF file (not from loaded file)

        int  nRet;

            // Lookup the name of the function given the function pointer
            if ((nRet = dladdr(pFunc, &DlInfo)) != 0)
            {
                LOGE("Symbol Name is: %s", DlInfo.dli_sname);
                if(DlInfo.dli_saddr==NULL)
                    LOGE("Symbol Address is: NULL");
                else
                    LOGE("Symbol Address is: 0x%x", DlInfo.dli_saddr);
            }
            else
                LOGE("dladdr failed");

Вот результат, который я получаю:

entry_addr = 0x75a28cfc

entry_addr_through_dlysm = 0x75a28cfc

Имя символа: AUDIO_System_Create

Адрес символа: 0x75a28cfc

Здесь адрес, полученный с помощью dlysm или рассчитанный с помощью файла ELF, является адресом процедуры; пока мне нужно место, где находится сам этот адрес; чтобы я мог заменить этот адрес своим hook_func адрес. dladdr() не сделал то, что я думал, что будет делать.

1 ответ

Вы должны подробно прочитать статью Дреппера: как писать разделяемые библиотеки, особенно чтобы понять, почему LD_PRELOAD недостаточно. Вы можете изучить исходный код динамического компоновщика (ld-linux.so) внутри вашего libc, Вы можете попробовать изменить с помощью mprotect(2) и / или mmap(2) и / или mremap(2) соответствующие страницы. Вы можете запросить отображение памяти через proc(5), используя /proc/self/maps & /proc/self/smaps, Тогда вы могли бы, в зависимости от архитектуры, заменить начальные байты (возможно, используя asmjit или GNU lightning) кода original_func прыжком к вашему hook_func функция (которая может потребоваться изменить его эпилог, чтобы поместить переписанные инструкции - первоначально в original_func - там...)

Все может быть немного проще, если original_func хорошо известен и всегда один и тот же. Затем вы можете изучить его исходный код и код ассемблера, и написать функцию исправления и ваш hook_func только для этого.

Возможно, использование dladdr(3) также может быть полезным (но, вероятно, нет).

Или взломайте динамический компоновщик, чтобы изменить его для своих нужд. Вы можете изучить исходный код http://musl-libc.org/

Обратите внимание, что вам, вероятно, нужно переписать машинный код по адресу original_func (как указано dlsym на "original_func"). В качестве альтернативы вам нужно будет переместить каждое вхождение вызовов этой функции во все уже загруженные общие объекты (я полагаю, это сложнее; если вы настаиваете, посмотрите dl_iterate_phdr (3)).

Если вы хотите общее решение (для произвольного original_func) вам потребуется реализовать некоторый анализатор двоичного кода (или дизассемблер) для исправления этой функции. Если вы просто хотите взломать конкретный original_func Вы должны разобрать его, исправить его машинный код и hook_func делать часть original_func что вы перезаписали.

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

Конечно, все это не просто. Вам необходимо детально понять, что такое общие объекты ELF, см. Также elf(5) и прочитать книгу Левина: компоновщики и загрузчики.


NB: Осторожно, если вы взламываете проприетарную библиотеку (например, unity3d), то, что вы пытаетесь достичь, может быть незаконным. Спросите юриста. Технически, вы нарушаете большинство абстракций, предоставляемых общими библиотеками. Если возможно, попросите автора общей библиотеки оказать помощь и, возможно, внедрить в нее какой-либо механизм плагинов.

Другие вопросы по тегам