Русский
Русский
English
Статистика
Реклама

Как обойти проверку на Рутинг устройства обхитрив библиотеку RootBeer?

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

Дисклеймер

Сразу предупрежу, что люблю писать подобные статьи довольно подробно, не ради объема и многобукав, а ради максимального погружения в проблему и способ ее решения. Обратите внимание, что я работаю на macOS, поэтому все команды в терминале будут ориентированы под данную ОС.

Как до этого дошло?

Мне выслали .apk файл приложения. Где-то внутри приложения есть баг, который мне нужно воспроизвести. Но пройти вглубь приложения у меня не получается. Мое устройство не проходит проверку на рутованность.

Экран, который меня не пропускаетЭкран, который меня не пропускает

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

Что будем делать?

Первое, что пришло мне в голову, так это попробовать выполнить декомпиляцию и поправить кое-где код, а потом собрать все обратно. Основное сомнение по поводу такого подхода вызывало то что, у меня не нативное android-приложение, а flutter-приложение. То есть как такового основного кода приложения в виде байт-кода в .apk найти не получится. Или получится? И нужен ли мне код самого приложения?

Небольшое отступление о том, как во флаттере запускается нативный код, то есть родной код для Android или iOS платформы. Например, перед flutter-разработчиком стоит задача: не давать возможность работать с приложением на рутованном устройстве. Аналогом андроид root-устройства является jailbreak для iOS. Для проверки на рутованность или jailbreak необходимо воспользоваться нативными средствами обеих платформ. Для этого разработчик пишет плагин, который вызывает соответствующие нативные методы в зависимости от платформы на которой запущено приложение.

Таким образом, я сделал вывод, что в имеющемся .apk файле есть как минимум 2 интересующих меня места в виде байт-кода. Первое место - это нативная часть приложения, которая слушает команды из flutter части приложения, чтобы вызвать соответствующие нативные методы. И второе место - это собственно и есть эти самые нативные методы, а в данном случае - библиотека для проверки на рутованность устройства.

Я решил, что буду менять байт-код во втором месте. Честно говоря, сначала я работал с первым местом, то есть с плагином. Все потому, что кода там намного меньше и запутаться было сложнее. Всего лишь метод, который вызывает другой метод - это пара строк кода. Но в таком случае статья была бы еще сильнее завязана под конкретный .apk, и не было б такой абстрактности. Поэтому будем работать напрямую с кодом библиотеки.

Смотрим байт-код .apk

Итак, посмотрим на внутренности .apk. Для этого можно воспользоваться инструментом, предоставляемый Android Studio. В меню выбираем Build -> Analyze APK В окне выбора файла находим интересующий нас .apk.

Весь скомпилированный байт-код хранится в файле classes.dex. Выберем его и взглянем на его содержимое. В первую очередь нас интересуют не файлы java или androidx, а именно файлы приложения. Поэтому выбираем первый package в структуре файлов android проекта, в моем случае это папка com. Из любопытства к содержимому каждой из папок я затригерился на слово rootbeer. Погуглив в гугле оказалось, что это весьма популярная библиотека для проверки устройства на предмет рутованности.

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

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

Судя по исходникам библиотеки, нужный мне файл должен находится рядом с классом RootBeerNative, и как мы видим тут таких, два a и b. Эмпирическим методом я понял, что это файл b. Правый клик по файлу b и в выпадающем меню выбираем Show Bytecode.

К сожалению, имена всех методов обфусцированы. Снова обращаясь к исходному файлу на github становится ясно, что метод isRooted() находится на строке 42. Побегав по файлу с байт-кодом мне удалось найти этот метод. Только теперь он называется a() и почему-то начинает работу с .line 43. Вот этот метод я и буду редактировать. Но не так быстро, К сожалению здесь только Read-only доступ.

Редактируем содержимое classes.dex

Для этого придется декомпилировать весь .apk при помощи популярного инструмента apktool. Работать с ним довольно просто. После его установки нам понадобятся всего 2 команды:

apktool d appname.apk, которая декомпилирует указанный .apk и apktool b directory_with_app, которая соберет .apk обратно (в указанной directory_with_app должен располагаться файл apktool.yml).

Итак, выполняю декомпиляцию при помощи команды в терминале: apktool d app.apk

После выполнения команды появляется папка с файлами приложения. Нас интересует папка smali. Smali - это язык которым описан байт-код (вот классная статья по основам smali). Именно эта папка и есть содержимое файла classes.dex. К только что выполненной команде можно добавить параметр --skip-sources или просто -s тогда вместо папки smali мы увидим тот самый файл classes.dex.

Далее, в папке smali находим интересующий и уже известный нам файл b.smali:

Открываем его любым текстовым редактором и переходим к месту, где располагается метод isRooted():

Содержимое файла b.smali
.method public a()Z    .locals 1    .line 43    invoke-virtual {p0}, Lcom/scottyab/rootbeer/b;->c()Z    move-result v0    if-nez v0, :cond_1    invoke-virtual {p0}, Lcom/scottyab/rootbeer/b;->d()Z    move-result v0    if-nez v0, :cond_1    const-string v0, "su"    invoke-virtual {p0, v0}, Lcom/scottyab/rootbeer/b;->a(Ljava/lang/String;)Z    move-result v0    if-nez v0, :cond_1    const-string v0, "busybox"    .line 44    invoke-virtual {p0, v0}, Lcom/scottyab/rootbeer/b;->a(Ljava/lang/String;)Z    move-result v0    if-nez v0, :cond_1    invoke-virtual {p0}, Lcom/scottyab/rootbeer/b;->f()Z    move-result v0    if-nez v0, :cond_1    invoke-virtual {p0}, Lcom/scottyab/rootbeer/b;->g()Z    move-result v0    if-nez v0, :cond_1    .line 45    invoke-virtual {p0}, Lcom/scottyab/rootbeer/b;->b()Z    move-result v0    if-nez v0, :cond_1    invoke-virtual {p0}, Lcom/scottyab/rootbeer/b;->h()Z    move-result v0    if-nez v0, :cond_1    invoke-virtual {p0}, Lcom/scottyab/rootbeer/b;->j()Z    move-result v0    if-nez v0, :cond_1    invoke-virtual {p0}, Lcom/scottyab/rootbeer/b;->e()Z    move-result v0    if-eqz v0, :cond_0    goto :goto_0    :cond_0    const/4 v0, 0x0    goto :goto_1    :cond_1    :goto_0    const/4 v0, 0x1    :goto_1    return v0.end method

Можем сравнить его с неповторимым оригиналом:

Очень похожи. Итак, нас интересует return, то есть самый конец метод. Видим, что метод возвращает некоторое значение константы v0, значение которой определяется на основе выполнивших в методе условий. Нам это не подходит. Нам всегда нужно возвращать false или же как это будет в smali 0x0. Для этого добавим свою констант v1 со значением 0x0. Сделаем это прямо над return:

:goto_1const/4 v1, 0x0return v1

И конечно же заменим в return v0 на v1. Поднимемся в самое начало метода и изменим значение .locals с 1 на 2 потому, что так надо. Сохраняем изменения в файле и закрываем редактор.

Собираем .apk обратно

Для этого воспользуемся командой apktool b app, где app - папка с приложением, в котором мы редактировали smali файл. После завершения команды, в папке app появиться директория dist. Здесь и расположен наш заново собранный .apk. Однако при попытке установить его мы получим следующую ошибку:

The APK failed to install.Error: INSTALLPARSEFAILEDNOCERTIFICATES: Failed collecting certificates for /data/app/vmdl164530405.tmp/base.apk: Failed to collect certificates from /data/app/vmdl164530405.tmp/base.apk: Attempt to get length of null array

Это из-за того, что после пересборки нужно еще и переподписать приложение. Для этого нам понадобиться любой keystore файл. Или можно создать новый при помощи команды:

keytool -genkey -v -keystore my-release-key.keystore -alias alias_name -keyalg RSA -keysize 2048 -validity 10000

Подписать ключом приложение можно при помощи одного из двух инструментов: apksigner или jarsigner.

Я выбрал jarsigner используя следующую команду:

jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore keystore app.apk cp_key

В этой команде мы указываем путь к keystore, путь к .apk, который хотим подписать и имя ключа (alias) из указанного keystore. После, вводим пароль от keystore и приложение успешно подписано.

Однако и это еще не все, теперь при попытке установить .apk мы будем получать другую ошибку:

The APK failed to install.Error: INSTALL_FAILED_INVALID_APK: Failed to extract native libraries, res=-2

Это потому, что после подписи приложения нужно выполнить оптимизацию .apk файла при помощи инструмента zipalign.

Для выполнения оптимизации нужно ввести следующую команду: ~/Library/Android/sdk/build-tools/30.0.3/zipalign -v -f -p 4 app.apk rdy.apk

30.0.3 - я выбрал самую последнюю версию на момент написания статьи. После завершения команды на выходе получаем файл rdy.apk, который можно успешно установить! Проверим, получилось ли обойти проверку на рутованность устройства:

И да, это успех!

Заключение

Вызов принят - вот, что я подумал получив этот .apk. И считаю это маленькой победой. Данной статьей я стремился описать не просто инструкцию о том, как расковырять приложение или свои способности по типу "смотрите как могу". Этой статьей я хочу сказать (и напомнить тем, кто стал забывать) что большинство проблем решаемы, нужно только копать, копать и еще раз копать, не опускать руки и результат обязательно будет удовлетворительным. Даже если, в конце концов, баг я так и не обнаружил, я очень рад что расширил свой кругозор и в дальнейшем смогу взяться за более сложные задачи! Спасибо за внимание, обязательно буду писать еще про подобные танцы с бубном!

Источник: habr.com
К списку статей
Опубликовано: 25.01.2021 14:08:49
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Разработка мобильных приложений

Разработка под android

Реверс-инжиниринг

Тестирование мобильных приложений

Flutter

Эмулятор

Android

Android studio

Мобильная разработка

Мобильные приложения

Командная строка

Терминал

Тестирование

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru