• 15 апреля стартует «Курс «SQL-injection Master» ©» от команды The Codeby

    За 3 месяца вы пройдете путь от начальных навыков работы с SQL-запросами к базам данных до продвинутых техник. Научитесь находить уязвимости связанные с базами данных, и внедрять произвольный SQL-код в уязвимые приложения.

    На последнюю неделю приходится экзамен, где нужно будет показать свои навыки, взломав ряд уязвимых учебных сайтов, и добыть флаги. Успешно сдавшие экзамен получат сертификат.

    Запись на курс до 25 апреля. Получить промодоступ ...

Статья Frida Framework vs Android p.1

, это динамический фреймворк для взаимодействия с бинарными приложениями. Сначала инструмент просто показался мне интересным, чуть позже я понял насколько это веселая вещь. Помните режим бога в играх? Frida позволяет делать тоже самое, только уже с нативными приложениями. В этом посте я расскажу о использовании Frida для игры с Android приложениями. И раз уж вы здесь, мы дополнительно решим несложный Android CrackMe во второй части.

Что такое динамическая бинарная инструментация

Динамическая бинарная инструментация (Dynamic binary instrumentation) позволяет внедрять сторонний код в уже запущенное приложение, для того чтобы оно делало то, что не делало раньше. Это не эксплуатация инъекции кода через ранее найденные уязвимости. Это не отладка приложения, ведь на самом деле вы не подключаетесь отладчиком к приложению, но это позволяет делать в общем-то похожие вещи. Какие возможности открывает динамическая бинарная инструментация:
  • Доступ к памяти процесса
  • Переопределение функций во время исполнения приложения
  • Вызов функций из импортированных классов
  • Поиск экземпляров объектов в куче и взаимодействие с ними
  • Перехват и трассировка функций
Конечно, вы можете делать все эти вещи с помощью отладчика, но перед вами, скорее всего, будут возникать разные препятствия. Например, в Android вам потребуется дизассемблировать и перекомпилировать приложение для возможности его отладки. Некоторые приложения препятствуют своей отладке, обнаруживая отладчики, вам нужно будет как-то от этого избавляться. Кончено, все это возможно, но обременительно. С помощью Frida вы можете быстро начинать исследование, не разбираясь в устройстве приложения.

Frida "позволяет внедрять JavaScript сниппеты или вашу собственную библиотеку в нативное приложение на Windows, MacOS, Linux, iOS, Android или QNX". Первоначально инструмент работал на движке Google V8 JavaScript Runtime, но начиная с 9 версии Frida использует , при этом у вас есть возможность перейти обратно к V8, если вам это понадобиться. Frida определяет для взаимодействия с приложениями (включая возможность использования инструментария даже на устройстве без прав супер-пользователя), но сейчас мы рассмотрим самые частые примеры использования, не особо затрагивая методов их работы.

Для начала вам понадобиться:
  • Frida
  • frida-server, исполняемый файл скаченный со страницы релизов (на момент написания это frida-server-9.1.16-android-arm.xz. Версия frida-server должна совпадать с версией Frida.
  • Android Emulator или устройство с root. Frida была разработана для Android 4.4 ARM, но работает и с более поздними версиями. Я успешно использовал Android 7.1.1 ARM для этого руководства. Для CrackMe во второй части статьи вам в любом случае понадобиться, что-то современнее, чем Android 4.4.
Я так же предполагаю использование в качестве хоста операционную систему основанную на Linux, если же вы работает на Windows или MacOS вам, скорее всего, потребуется изменить несколько команд, что бы все точно заработало.

Если вы планируете повторить решение OWASP Unbreakable Crackme Level 1, который решается во второй части статьи, вам так же следует загрузить:
Frida предоставляет несколько APIs и способов для начала анализа. Вы можете использовать командную строку или инструмент наподобие frida-trace для отслеживания низкоуровневых функций (таких как вызов "open" в libc.so) и максимально быстрого начала. Вы можете использовать биндинги на C, NodeJS или Python для нетривиальных задач. Под капотом Frida работает на JavaScript и в большинстве случаев вам потребуется писать код на этом языке. Поэтому, если вы как и я немного недолюбливаете JavaScript (помимо его возможностей XSS), Frida еще одна причина что бы разобраться с ним получше.

Установите Frida, если еще не сделали этого (посмотрите README для других способов установки):
Код:
pip install frida
npm install frida
Запустите ваш эмулятор или присоединитесь к вашему устройству, убедитесь, что устройство доступно в adb:
Код:
michael@sixtyseven:~$ adb devices
List of devices attached
emulator-5556 device
После, установите frida-server. Распакуйте архив и разместите исполняемый файл на устройстве:
Код:
adb push /home/michael/Downloads/frida-server-9.1.16-android-arm /data/local/tmp/frida-server
С помощью adb откройте shell на устройстве, перейдите в root и запустите frida:
Код:
adb shell
su
cd /data/local/tmp
chmod 755 frida-server
./frida-server
(Примечание 1: Если frida-server не стартовал убедитесь, что у вас есть root права и файл находиться на устройстве. Я наблюдал несколько странных ошибок с поврежденным файлом при перемещении. Примечание 2: Если вы хотите запустить frida-server в фоновом режиме выполните: ./frida-server &)

В другом терминале, на хостовой системе проверьте, что Frida запустилась и вы можете посмотреть список процессов на Android:
Код:
frida-ps -U
аргумент -U означает использование USB и позволяет Frida проверить подключенные USB-устройства, но это также работает с эмулятором. Вы должны получить примерно следующий список процессов:
Код:
michael@sixtyseven:~$ frida-ps -U
PID  Name
----  --------------------------------------------------
696  adbd
5828  android.ext.services
6188  android.process.acore
5210  audioserver
5211  cameraserver
8334  com.android.calendar
6685  com.android.chrome
6245  com.android.deskclock
5528  com.android.inputmethod.latin
6120  com.android.phone
6485  com.android.printspooler
8355  com.android.providers.calendar
5844  com.android.systemui
7944  com.google.android.apps.nexuslauncher
6416  com.google.android.gms
[...]
Вы можете увидеть ID процесса (PID) и сам запущенный процесс (Name). С помощью Frida вы можете внедрится в любой из этих процессов и начать взаимодействие.

Например, вы можете отслеживать нужные вам вызовы функций в Chrome (запустите Chrome на эмуляторе, если он еще не запущен):
Код:
frida-trace -i "open" -U com.android.chrome
Вы увидите следующее:
Код:
michael@sixtyseven:~$ frida-trace -i open -U -f com.android.chrome
Instrumenting functions...                                           
open: Loaded handler at "/home/michael/__handlers__/libc.so/open.js"
Started tracing 1 function. Press Ctrl+C to stop.                    
           /* TID 0x2740 */
   282 ms  open(pathname=0xa843ffc9, flags=0x80002)
           /* TID 0x2755 */
   299 ms  open(pathname=0xa80d0c44, flags=0x2)
           /* TID 0x2756 */
   309 ms  open(pathname=0xa80d0c44, flags=0x2)
           /* TID 0x2740 */
   341 ms  open(pathname=0xa80d06f7, flags=0x2)
   592 ms  open(pathname=0xa77dd3bc, flags=0x0)
   596 ms  open(pathname=0xa80d06f7, flags=0x2)
   699 ms  open(pathname=0xa80d105e, flags=0x80000)
   717 ms  open(pathname=0x9aff0d70, flags=0x42)
   742 ms  open(pathname=0x9ceffda0, flags=0x0)
   758 ms  open(pathname=0xa63b04c0, flags=0x0)
Команда frida-trace генерирует небольшой файл JavaScript, который внедряется в процесс и отслеживает вызовы функций. Посмотрим, как устроен скрипт open.js в __handlers__/libc.so/open.js. В нем происходит перехват вызовов функции open из libc.so и вывод ее аргументов. Собственно, сам код для Frida:
Код:
[...]
onEnter: function (log, args, state) {
    log("open(" + "pathname=" + args[0] + ", flags=" + args[1] + ")");
},
[...]
Обратите внимание как Frida предоставляет доступ к аргументам функции open во время ее вызова (args[0], args[1] и т.д.) внутри приложения Chrome. Давайте немного изменим скрипт. Было бы неплохо, если бы мы получали действительный путь до открытого файла, вместо адреса памяти где храниться данный путь. К счастью, мы можем напрямую обращаться к памяти с помощью Frida. Посмотрите на Frida API и объект . Мы можем изменить наш скрипт, что бы он выводил содержимое по адресу памяти, где размещена UTF8 строка и мы получали более понятный вывод. После изменения код будет выглядеть примерно так:
Код:
onEnter: function (log, args, state) {
    log("open(" + "pathname=" + Memory.readUtf8String(args[0])+ ", flags=" + args[1] + ")");
},
(мы просто добавили функцию Memory.readUtf8String) и преобразили вывод:
Код:
michael@sixtyseven:~$ frida-trace -i open -U -f com.android.chrome
Instrumenting functions...                                           
open: Loaded handler at "/home/michael/__handlers__/libc.so/open.js"
Started tracing 1 function. Press Ctrl+C to stop.                    
           /* TID 0x29bf */
   240 ms  open(pathname=/dev/binder, flags=0x80002)
           /* TID 0x29d3 */
   259 ms  open(pathname=/dev/ashmem, flags=0x2)
           /* TID 0x29d4 */
   269 ms  open(pathname=/dev/ashmem, flags=0x2)
           /* TID 0x29bf */
   291 ms  open(pathname=/sys/qemu_trace/process_name, flags=0x2)
   453 ms  open(pathname=/dev/alarm, flags=0x0)
   456 ms  open(pathname=/sys/qemu_trace/process_name, flags=0x2)
   562 ms  open(pathname=/proc/self/cmdline, flags=0x80000)
   576 ms  open(pathname=/data/dalvik-cache/arm/system@app@Chrome@Chrome.apk@classes.dex.flock, flags=0x42)
Frida выводит пути до файлов. Легко, не правда ли?

Еще одна вещь о которой стоит рассказать, вы можете либо запустить приложение перед внедрением Frida, либо передать аргумент -f для Frida, чтобы приложение было запущено автоматически.

Теперь используем интерфейс командной строки Frida, frida-cli:
Код:
frida -U -f com.android.chrome
Этой командой вы запустите приложение Chrome. Однако это не запускает основной процесс приложения. Это нужно для того что бы вы могли внедрить код во Frida, еще перед началом исполнения приложения. К сожалению, в моем случае приложение всегда закрывалось по истечению двух секунд. Это не то, что нам нужно. За эти две секунды вы можете набрать %resume%, как предлагает это cli и приложение сможет запустить свой основной процесс. Или вы можете запускать приложения без его остановки, для этого передайте аргумент --no-pause, в этом случае приложение все еще будет запускается посредством Frida.

В обоих случаях вы получаете shell (который не закрывается автоматически), куда вы можете вводить команды Frida следуя Frida JavaScript API. Нажмите TAB для просмотра доступных команд. Оболочка так же поддерживает автодополнение команд.

Большинство возможностей хорошо документированы. Для Android особенно внимательно посмотрите на секцию (Я буду говорить о "Java API", хотя технически это просто обертка на JavaScript для доступа к Java объектам). Мы сосредоточимся на Java API, поскольку это наиболее удобный способ взаимодействия с Android приложениями. Вместо перехвата функций из libc мы будем работать напрямую с функциями и объектами из Java. (Примечание: Если вы заинтересованы, что вы можете делать с помощью Frida за пределами Java API, перехватывая более низкоуровневые C функции на Android, как мы делали с помощью frida-trace, посмотрите раздел документации посвященный . Я не рассматриваю это здесь.)

Начиная работу с Java API просто получим версию Android на устройстве:
Код:
[USB::Android Emulator 5556::['com.android.chrome']]-> Java.androidVersion
"7.1.1"
Или список загруженных классов (Предупреждение: Команда может иметь довольно длинный вывод. Дальше я объясню выполняемый код.)
Код:
[USB::Android Emulator 5556::['com.android.chrome']]-> Java.perform(function(){Java.enumerateLoadedClasses({"onMatch":function(className){ console.log(className) },"onComplete":function(){}})})

org.apache.http.HttpEntityEnclosingRequest
org.apache.http.ProtocolVersion
org.apache.http.HttpResponse
org.apache.http.impl.cookie.DateParseException
org.apache.http.HeaderIterator
Мы ввели достаточно длинную команду, с некоторой вложенной функцией. Прежде всего обратим внимание, что код обернут в Java.perform(function(){ ... }), что является требованием Java API.

Это тело функции, находящейся внутри обертки Java.perform:
Код:
Java.enumerateLoadedClasses(
  {
  "onMatch": function(className){
        console.log(className)
    },
  "onComplete":function(){}
  }
)
Код достаточно прост: Мы перечисляем все загруженные классы используя Java.enumerateLoadedClasses из Frida API, и выводим каждый элемент в консоль с помощью console.log. Такой тип Callback является шаблоном часто встречающимся во Frida. Это некоторое представление Callback объекта:
Код:
{
  "onMatch":function(arg1, ...){ ... },
  "onComplete":function(){ ... },
}
onMatch вызывается с одним и более аргументом, когда Frida находит совпадение с вашим запросом, а onComlete, когда Frida заканчивает итерирование возможных совпадений.

Сейчас мы углубимся в магию Frida и перезапишем функцию. Кроме того мы будем загрузим код из внешнего скрипта, вместо того что бы набирать его в консоли, это намного удобнее. Сохраните следующий код, например, как chrome.js:
Код:
Java.perform(function () {
    var Activity = Java.use("android.app.Activity");
    Activity.onResume.implementation = function () {
        console.log("[*] onResume() got called!");
        this.onResume();
    };
});
Этот код перезаписывает функцию onResume в классе android.app.Activity. Происходит вызов Java.use для получения доступа к реализации функции и последующего переопределения функции onResume в классе. В новой реализации функция вызывает оригинальную функцию, через this.onResume(), так что приложение продолжит работать нормально.

Открой ваш эмулятор, откройте Chrome и выполните внедрение кода передав -l.
Код:
frida -U -l chrome.js com.android.chrome
Теперь, когда происходит вызов onResume, например, перейдите в другое приложение и вернитесь обратно в Chrome, вы увидите:
Код:
[*] onResume() got called!
Приятно, не так ли? Мы фактически перезаписали функцию из приложения. Это дает нам много возможностей для контроля поведения нашего выбранного приложения. Но мы можем больше: мы также можем искать объекты в куче с помощью Java.choose.

Прежде чем мы продолжим, предостережение: Когда наша эмуляция достаточно медленная Frida может останавливается по тайм-ауту. Для предотвращения этого оберните свои скрипты в функцию setImmediate или . По умолчанию RPC во Frida не имеют тайм-аутов. setImmediateавтоматически перезапускает ваши скрипты во Frida, после того как вы изменили исходный файл, поэтому это еще и удобно. Также это запускает ваши скрипты в фоновом режиме. Это означает, что сразу получите cli, хотя Frida все еще обрабатывает ваши скрипты. Просто подождите и не покидайте cli, пока Frida не показала вам вывод ваших скриптов. Еще раз отредактируем chrome.js:
Код:
setImmediate(function() {
    console.log("[*] Starting script");
    Java.perform(function () {

        Java.choose("android.view.View", {
          
             "onMatch":function(instance){
                  console.log("[*] Instance found");
             },

             "onComplete":function() {
                  console.log("[*] Finished heap search")
             }
        });

    });
});
Запустим это через команду frida -U -l chrome.js com.android.chrome, получив следующий вывод:
Код:
[*] Starting script
[*] Instance found
[*] Instance found
[*] Instance found
[*] Instance found
[*] Finished heap search
Итак, мы обнаружили 4 экземпляра класса android.view.View в куче. Давайте посмотрим, что мы сможем с ними сделать. Может быть мы можем вызвать их методы. Добавим instance.toString() в наш вывод console.log (поскольку мы использовали setImmediate мы можем изменить скрипт и он перезагрузится во Frida автоматически)
Код:
setImmediate(function() {
    console.log("[*] Starting script");
    Java.perform(function () {

        Java.choose("android.view.View", {
          
             "onMatch":function(instance){
                  console.log("[*] Instance found: " + instance.toString());
             },

             "onComplete":function() {
                  console.log("[*] Finished heap search")
             }
        });

    });
});
Это вернет:
Код:
[*] Starting script
[*] Instance found: android.view.View{7ccea78 G.ED..... ......ID 0,0-0,0 #7f0c01fc app:id/action_bar_black_background}
[*] Instance found: android.view.View{2809551 V.ED..... ........ 0,1731-0,1731 #7f0c01ff app:id/menu_anchor_stub}
[*] Instance found: android.view.View{be471b6 G.ED..... ......I. 0,0-0,0 #7f0c01f5 app:id/location_bar_verbose_status_separator}
[*] Instance found: android.view.View{3ae0eb7 V.ED..... ........ 0,0-1080,63 #102002f android:id/statusBarBackground}
[*] Finished heap search
Frida просто вызывает метод toString экземпляра класса android.view.View. Здорово. Таким образом с помощью Frida мы можем читать память процесса, изменять функции, находить инстансы классов в куче и все это в несколько строчек кода.

Предостережения

Когда вы экспериментируете с Frida вы заметите, что есть некоторые нестабильности. Во-первых внедрение стороннего когда в другой процесс может вызвать сбои, потому приложение будет работать не так, как планировалось. Во-вторых, сама Frida является экспериментальной. Она работает, но часто вы должны попробовать несколько способов для получения желаемого результата. Например, Frida всегда крашится, когда я пытаюсь загрузить скрипт и запустить процесс в одну команду. Вместо этого я сначала запускаю процесс, а затем внедряю скрипт. Поэтому я и показывал вам разные способы избежания тайм-аута. Возможно, вам будет нужно какой из них работает в вашем случае.

Python bindings

Если вы хотите автоматизировать вашу работу с Frida еще больше, вы должны посмотреть на биндинги для Python, C и NodeJS, их очень просто использовать как только вы разобрались как работать с Frida. Например, инъекция вашего скрипта chrome.js из Python, вы можете использовать и создать
Код:
chrome.py
#!/usr/bin/python
import frida

# put your javascript-code here
jscode= """
console.log("[*] Starting script");

Java.perform(function() {

   var Activity = Java.use("android.app.Activity");
    Activity.onResume.implementation = function () {
        console.log("[*] onResume() got called!");
        this.onResume();
    };

});

"""

# startup frida and attach to com.android.chrome process on a usb device
session = frida.get_usb_device().attach("com.android.chrome")

# create a script for frida of jsccode
script = session.create_script(jscode)

# and load the script
script.load()
Если вы хотите хотите завершить сессию Frida и скрипты внедренные в этой сессии вызовите session.detach().

Для других примеров, как обычно, .

Продолжение следует!

Frida Framework vs Android p.2
Frida Framework vs Android p.3
 

Сергей Попов

Кодебай
30.12.2015
4 693
6 587
BIT
343
@Hadariel убедительная просьба оформить код соотв.тегами. Кнопка вставки кода находится в панели редактирования сообщений, правее кнопки со смайлами.
 
  • Нравится
Реакции: alexej и Vander
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!