- Русские Блоги
- Android Native Hook
- Разные жанры Native Hook
- 1. GOT/PLT Hook
- Формат ELF
- Процесс связывания
- ПОЛУЧЕНО / PLT Крюк практика
- 2. Trap Hook
- ptrace
- Как зацепить
- Тренировка ловушки
- 3. Inline Hook
- Android: исследование IPC Android и хукинг нативных библиотек
- Содержание статьи
- Почитать
- Как устроена система сообщений в Android
- Хукинг нативных библиотек с помощью Frida
- Продолжение доступно только участникам
- Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте
- Вариант 2. Открой один материал
- Евгений Зобнин
Русские Блоги
Android Native Hook
Hook буквально означает «ловушка», что означает перехват вызова определенной функции API процессом, так что поток выполнения API превращается в фрагмент кода, который мы реализуем, чтобы достичь нужной нам функции. Функция здесь может быть мониторингом и исправлением. Системными уязвимостями также могут быть угон или другие вредоносные действия.
Я полагаю, что многие новички почувствуют, что эта технология очень загадочна, когда они впервые вступают в контакт с Hook, и освоить ее могут только несколько экспертов и хакеров. Действительно ли сложно освоить Hook? Я надеюсь, что сегодняшняя статья развеет ваши опасения.
Разные жанры Native Hook
Что касается технологии Native Hook, мы больше знакомы с GOT / PLT Hook, Trap Hook и Inline Hook. Позвольте мне объяснить принципы реализации, преимущества и недостатки этих технологий Hook по одному.
1. GOT/PLT Hook
В Chapter06-plus мы использовали технологию PLT Hook для получения стека, созданного потоком. Давайте сначала рассмотрим весь процесс: мы заменили внешнюю функцию pthread_create в libart.so нашим собственным методом pthread_create_hook.
Вы можете обнаружить, что GOT / PLT Hook в основном используется для замены внешнего вызова некоторого SO путем перехода внешнего вызова функции в нашу целевую функцию. GOT / PLT Hook можно назвать очень классическим методом Hook, он очень стабилен и может достичь стандарта развертывания в производственной среде.
Каков принцип реализации GOT / PLT Hook? Вы должны сначала понять формат файла ELF и процесс динамического связывания файлов библиотеки SO.
Формат ELF
ELF (Исполняемый и связывающий формат) — это исполняемый и связывающий формат, открытый стандарт. Исполняемые файлы различных систем UNIX в основном используют формат ELF. Хотя сам файл ELF поддерживает три разных типа (перемещение, выполнение, совместное использование), формат немного отличается в разных представлениях, но он имеет унифицированную структуру, как показано на следующем рисунке.
В Интернете представлено много статей, посвященных формату ELF, вы можете обратиться к разделу «Анализ формата файлов ELF». Как следует из названия, для GOT / PLT Hook мы в основном заботимся о двух разделах «.plt» и «.got»:
- .plt. В этом разделе сохраняется таблица привязки процедур.
- .получил. Этот раздел содержит таблицу глобальных смещений.
Мы также можем использовать readelf -S для просмотра конкретной информации о файле ELF.
Процесс связывания
Далее, давайте посмотрим на процесс динамического связывания. Когда нам нужно использовать Native library (.so файл), нам нужно вызвать dlopen («libname.so») для загрузки этой библиотеки.
После того, как мы вызовем dlopen («libname.so»), система сначала проверит список файлов ELF, загруженных в кэш. Если не загружен, процесс загрузки выполняется, если он загружен, счетчик увеличивается на единицу, и вызов игнорируется. Затем система будет читать библиотеки, от которых она зависит, из динамического раздела libname.so, и добавлять библиотеки, которые не находятся в кэше, в список загрузки в соответствии с той же логикой загрузки.
Вы можете использовать следующую команду для просмотра зависимостей библиотеки:
Давайте кратко рассмотрим, как файлы ELF загружаются системой.
- Прочитайте таблицу заголовков программы ELF и отобразите все разделы PT_LOAD в память.
- Прочитайте каждый элемент информации из «.dynamic», вычислите и сохраните виртуальные адреса всех разделов, а затем выполните операцию перемещения.
- Наконец, ELF успешно загружен, и счетчик ссылок увеличивается на единицу.
Но здесь есть ключевой момент: в формате файла ELF у нас есть только абсолютный адрес функции. Если вы хотите запустить в системе, его необходимо переместить сюда. На самом деле это более сложная проблема, потому что разные машины имеют разную архитектуру ЦП и порядок загрузки, поэтому мы можем рассчитать это значение только во время выполнения. К счастью, динамический загрузчик (/ system / bin / linker) поможет нам решить эту проблему.
Если вы понимаете процесс динамического связывания, давайте вернемся назад и подумаем о конкретном значении «.got» и «.plt».
- The Global Offset Table (GOT), Проще говоря, это таблица адресов в сегменте данных. Предполагая, что у нас есть некоторые инструкции сегмента кода, которые ссылаются на некоторые переменные адреса, компилятор будет ссылаться на таблицу GOT вместо прямой ссылки на абсолютный адрес, поскольку абсолютный адрес не может быть известен во время компиляции. Он будет получен только после позиционирования, а сам GOT будет содержать абсолютный адрес ссылки на функцию.
- The Procedure Linkage Table (PLT), PLT отличается от GOT. Он расположен в сегменте кода. Каждая внешняя функция динамической библиотеки будет иметь запись в PLT. Каждая запись PLT представляет собой небольшой фрагмент исполняемого кода. Как правило, внешний код вызывает записи в таблице PLT, и соответствующие записи PLT будут отвечать за вызов фактической функции. Мы обычно называем эту настройку«Трамплин»(Trampoline)。
Записи PLT и GOT находятся во взаимно-однозначном соответствии, а таблица GOT будет содержать фактический адрес вызывающей функции после первого анализа. В этом случае, каково значение PLT? PLT дает нам возможность ленивой загрузки в некотором смысле. Когда динамическая библиотека была впервые загружена, все адреса функций не были разрешены. Давайте подробно разберем первый вызов функции вместе с рисунком. Обратите внимание, что черная стрелка на рисунке — это прыжок, а фиолетовый — указатель.
Мы вызываем func в коде, компилятор преобразует это в func @ plt и вставляет запись в таблицу PLT.
Первая (или 0) таблица PLT [0] в таблице PLT является специальной записью, которая используется, чтобы помочь нам определить адрес. Обычно в Linux-подобной системе эта реализация находится в динамическом загрузчике, который является / system / bin / linker, упомянутым в предыдущей статье столбца.
Остальные записи PLT содержат следующую информацию:
— инструкция для перехода к таблице GOT (jmp * GOT [n]).
— Подготовьте параметры для функции разрешения адресов, упомянутой выше.
— вызов PLT [0], где фактический адрес ресовлера хранится в GOT [2].
Перед синтаксическим анализом GOT [n] будет напрямую указывать на следующую инструкцию jmp * GOT [n]. После завершения анализа мы получаем фактический адрес func, динамический загрузчик заполняет этот адрес в GOT [n] и затем вызывает func.
Если у вас есть какие-либо вопросы о вышеупомянутом потоке вызовов, вы можете обратиться к статье «Таблица GOT и таблица PLT», в которой очень четкая картина.
Когда происходит первый вызов, гораздо эффективнее и проще вызвать функцию func позже. Сначала вызовите PLT [n], затем выполните jmp * GOT [n]. GOT [n] указывает непосредственно на func, который эффективно завершает вызов функции.
Подводя итог, можно сказать, что многие функции могут не использоваться после выполнения программы, такие как функции обработки ошибок или некоторые функциональные модули, которые редко используются пользователями и т. д., а затем все На самом деле это пустая трата функций, чтобы хорошо связать. Чтобы повысить производительность динамического связывания, мы можем использовать PLT для реализации функции отложенного связывания.
Для фактического адреса функции нам все еще нужно получить его через таблицу GOT. Весь упрощенный процесс выглядит следующим образом:
Смотрите, я думаю, у вас уже есть предварительное представление о том, как взломать этот процесс. Здесь индустрия обычно различает GOT Hook и PLT Hook в зависимости от модификации записей PLT или GOT, но основные принципы очень близки.
ПОЛУЧЕНО / PLT Крюк практика
GOT / PLT Hook кажется простым, но в реализации есть некоторые подводные камни, и необходимо учитывать совместимость. В целом, рекомендуется использовать следующие зрелые решения в отрасли.
WeChat Matrix библиотека с открытым исходным кодомELF Hook, Он использует GOT Hook, в основном используется для мониторинга производительности.
iQiyi с открытым исходным кодомxHookТакже используется GOT Hook.
FacebookPLT Hook。
Если мы не хотим углубляться в его внутренние принципы, нам просто нужно напрямую использовать эти превосходные решения с открытым исходным кодом. Поскольку этот метод Hook является очень зрелым и стабильным, в дополнение к созданию потоков Hook у нас есть много других примеров использования.
- Используется в «I / O Optimization»matrix-io-canary Hook Файловые операции.
- Операции с сокетами используются в «Оптимизации сети».
Этот метод Hook не является панацеей, поскольку он может заменить только способ импорта функций. Иногда мы не можем найти такие внешние функции вызова. Если вы хотите вызвать функцию Hook изнутри, вам нужно использовать наш Trap Hook или Inline Hook.
2. Trap Hook
Что касается хука внутри функции, вы можете думать об этом с самого начала, вы обнаружите, что отладчик обладает всеми возможностями фреймворка хука, вы можете разбить программу перед целевой функцией, изменить память, сегмент программы и продолжить выполнение. Я верю, что многие студенты будут использовать отладчик, но мало что известно о том, как работает отладчик. Давайте сначала разберемся, как работает программный отладчик.
ptrace
Обычные программные отладчики используют системный вызов ptrace и SIGTRAP для выполнения отладки точек останова. Сначала давайте разберемся, что такое ptrace и как остановить запуск программы, а затем изменим соответствующие шаги выполнения.
Так называемые квалифицированные программисты низкого уровня, первый шаг в понимании неизвестных знаний — это использование команды man для просмотра документации системы.
Буквальный перевод этого отрывка состоит в том, что ptrace предоставляет метод для одной программы (tracer) для наблюдения или управления потоком выполнения другой программы (tracee), а также для изменения памяти и регистров контролируемой программы. Он в основном используется для реализации точек отладки и систем останова. Вызов трассировки.
Давайте кратко рассмотрим, как отладчик (GDB / LLDB) использует ptrace. Сначала отладчик решает, использовать ли ответвление или присоединиться к целевому процессу, в зависимости от того, запущен ли отлаживаемый процесс. Когда отладчик привязан к целевой программе, любой сигнал целевой программы (кроме SIGKILL) будет сначала перехвачен отладчиком, и отладчик будет иметь возможность обработать соответствующий сигнал, а затем передать полномочия на выполнение целевой программе для продолжения выполнения. Как вы можете себе представить, это фактически достигло цели Хука.
Как зацепить
Но подумайте об этом дальше, если нам не нужно изменять память или выполнять сложные взаимодействия, такие как отладчик, мы можем полностью не полагаться на ptrace, а только получать соответствующий сигнал. В этот раз мы подумали о ручке (обработчике сигнала). Верный! Мы можем поднять сигнал добровольно, а затем использовать обработчик сигнала для достижения аналогичного эффекта Хука.
Многие люди в отрасли называют Trap Hook как точку останова Hook.Его принцип заключается в том, чтобы найти способ вызвать точку останова и перехватить исключения, когда Hook необходим. Как правило, мы будем использовать эти два сигнала SIGTRAP или SIGKILL (исключение недопустимой инструкции). Далее в качестве примера используется сигнал SIGTRAP. Конкретные этапы реализации следующие.
- Зарегистрируйте дескриптор приема сигнала (обработчик сигнала), разные архитектуры могут выбирать разные сигналы, здесь мы используем SIGTRAP.
- Вставьте инструкцию Trap в ту часть, где нам нужен Hook.
- Система вызывает инструкцию Trap, входит в режим ядра и вызывает обработчик сигнала, который мы уже зарегистрировали.
- Запустите наш обработчик сигналов, обратите внимание, что весь код, выполняемый в обработчике сигналов, должен обеспечивать безопасность асинхронных сигналов. Здесь мы можем просто использовать дескриптор получения сигнала в качестве батута и использовать logjmp, чтобы выпрыгнуть из среды, которая требует асинхронного безопасного сигнала (как я сказал в «Анализ сбоя», некоторые функции небезопасны для использования в обратных вызовах сигнала), Затем выполните наш код Hook.
- После выполнения функции Hook нам нужно восстановить сцену. Здесь, если мы хотим продолжить вызывать исходную функцию A, то сразу напишем исходную инструкцию функции A и восстановим состояние регистра.
Тренировка ловушки
Совместимость с Trap Hook очень хорошая, ее также можно широко использовать в производственной среде. Но его самая большая проблема заключается в том, что его эффективность относительно низкая, и он не подходит для функции, которую Хук вызывает очень часто.
Для практики Trap Hook вы можете использовать FacebookProfilo, Для достижения контроля заикания путем периодической отправки сигнала SIGPROF.
3. Inline Hook
Как и Trap Hook, Inline Hook также является хуком, вызываемым внутри функции. Он непосредственно заменяет инструкцию в начале функции (Prologue) инструкцией перехода, так что исходная функция непосредственно переходит к целевой функции функции Hook, и сохраняет вызывающий интерфейс исходной функции для завершения цели последующего вызова.
По сравнению с GOT / PLT Hook, Inline Hook не может быть ограничен таблицей GOT / PLT, почти любая функция может быть подключена. Однако его реализация очень сложна. Я не видел реализации, которую можно использовать в производственной среде. А в архитектуре ARM листовые функции и очень короткие функции не могут быть подключены.
Источник
Android: исследование IPC Android и хукинг нативных библиотек
Содержание статьи
Почитать
Как устроена система сообщений в Android
Вопреки расхожему мнению, Android с самых первых версий использовал песочницы для изоляции приложений. И реализованы они были весьма интересным способом. Каждое приложение запускалось от имени отдельного пользователя Linux и, таким образом, имело доступ только к своему каталогу внутри /data/data .
Друг с другом и с операционной системой приложения могли общаться только через IPC-механизм Binder, который требовал авторизации для выполнения того или иного действия. В Android Binder использовался и продолжает использоваться буквально для всего: от запуска приложений до вызова функций операционной системы.
Операционная система спроектирована так, что пользователи и даже программисты не догадываются о существовании какого-то IPC-механизма. Если программист хочет скопировать текст в буфер обмена, он просто получает ссылку на объект-сервис и вызывает один из его методов. Под капотом фреймворк преобразует этот вызов в сообщение Binder и отправляет его в ядро через файл-устройство /dev/binder . Это сообщение перехватывает Service manager, который находит в своем каталоге сервис буфера обмена, проверяет полномочия приложения на отправку ему сообщений и, если оно имеет все необходимые права, передает сообщение сервису. После получения и обработки сообщения сервис буфера обмена отправляет ответ, используя тот же Binder.
Кроме полномочий, Service manager также проверяет, имеет ли приложение право на создание сервиса и может ли оно использовать ряд опасных сервисов. И первое, и второе — проверяя значение UID (идентификатор пользователя) вызывающего процесса. Если UID больше 10 000 — приложение не имеет права регистрировать новый сервис (все сторонние приложения в Android получают UID больше 10 000), а если UID находится в районе 99 000–99 999, приложение получает ограничение на использование «опасных» сервисов. Именно в эту группу попадают вкладки браузера Chrome.
Хукинг нативных библиотек с помощью Frida
How to hook Android Native methods with Frida (Noob Friendly) — статья о перехвате функций нативных библиотек с помощью Frida.
Для начала файл APK следует развернуть. Это обычный архив ZIP, поэтому можно использовать любой архиватор. Каталог lib содержит набор нативных библиотек для различных архитектур. Находим библиотеку для архитектуры своего смартфона (обычно это arm64-v8a или armeabi-v7a ) и анализируем ее содержимое с помощью утилиты nm (она доступна в Linux и macOS):
Как видно, библиотека содержит в том числе функцию Java_com_erev0s_jniapp_MainActivity_Jniint . Судя по имени, она должна быть доступна для вызова из Java (на стороне Java она будет иметь имя com.erev0s.jniapp.MainActivty.Jniint ).
Допустим, наша задача — перехватить вызов функции и вернуть удобное нам значение. Например, если бы это была функция, проверяющая наличие root, мы бы могли перехватить ее и вернуть false, невзирая на реальный результат проверки.
Есть два способа перехватить эту функцию:
- На стороне Java, когда приложение только попытается вызвать нативную функцию.
- На стороне нативного кода, когда управление уже будет передано библиотеке.
Предположим, мы уже установили Frida на ПК и frida-server на смартфон (как это сделать, можно прочитать здесь). Теперь напишем такой скрипт:
Этот скрипт переписывает функцию Jniint класса com.erev0s.jniapp.MainActivity так, чтобы она всегда возвращала значение 80 085.
Сохраняем скрипт в файл myhook.js и запускаем приложение под управлением Frida:
Перехватить нативную функцию еще проще:
Результат работы скрипта
Продолжение доступно только участникам
Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее
Вариант 2. Открой один материал
Заинтересовала статья, но нет возможности стать членом клуба «Xakep.ru»? Тогда этот вариант для тебя! Обрати внимание: этот способ подходит только для статей, опубликованных более двух месяцев назад.
Евгений Зобнин
Редактор рубрики X-Mobile. По совместительству сисадмин. Большой фанат Linux, Plan 9, гаджетов и древних видеоигр.
Источник