Функция подключения и замены в загруженном 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), то, что вы пытаетесь достичь, может быть незаконным. Спросите юриста. Технически, вы нарушаете большинство абстракций, предоставляемых общими библиотеками. Если возможно, попросите автора общей библиотеки оказать помощь и, возможно, внедрить в нее какой-либо механизм плагинов.