#javascript #java #android #frida
Вопрос:
Я пытаюсь обойти механизм обнаружения корня в приложении для Android с помощью Frida, я перепробовал так много разных сценариев (общий доступ к коду frida) и разные подходы (например, скрытие корня), но безуспешно!
Поэтому я попытался найти класс и метод, ответственные за проверку того, укоренено устройство или нет, и изменить его возвращаемое значение.
Это мой сценарий :
setTimeout(function() { // avoid java.lang.ClassNotFoundException
Java.perform(function() {
var hook = Java.use("app.name.RootUtils");
console.log("info: hooking target class");
hook.isRooted.overload().implementation = function() {
console.log("info: entered target method");
return Java.use("java.lang.Boolean").$new(false);
}
});
},0);
Если я введу этот код нормально, он не будет работать, потому что похоже, что метод isRooted будет вызван до него
Если я использую spawn для запуска приложения и изменю возвращаемое значение этого метода, он завершится ошибкой :
фрида,сердцевина.Исключение RPCException: Ошибка: java.lang.Исключение ClassNotFoundException: Не нашел класс …
Я также попытался создать приложение, а затем использовать возражение для запуска «отключить корневой доступ Android», но оно вернет эту ошибку :
фрида,сердцевина.Исключение RPCException: Ошибка типа: не удается прочитать свойство ‘getApplicationContext’ значения null в getApplicationContext (src/android/lib/libjava.ts:21)
Я не уверен, что это проблема с Фридой, или с моей системой, или …
Я думаю, что если бы я смог запустить свой основной код ровно после загрузки класса (например, с помощью цикла для проверки или с помощью крючка), проблема была бы устранена, но я не знаю, как написать такой код в js для фриды.
Я на macOS 11.5.1, использую python 3.9 и установил последнюю версию frida и возражаю
Я протестировал один корневой телефон с Android 10 и эмулятор с Android 6
Ответ №1:
Класс не найден
Как вы узнали, что класс был app.name.RootUtils
декомпилирован в приложение с помощью Jadx или apktool? Как насчет метода, где RootUtils.isRooted()
вызывается? Существует ли какой-либо специальный код, который загружает RootUtils
класс, например, из нестандартного файла dex, включенного в приложение? Если класс загружен из специального файла dex, вы можете подключить этот механизм загрузки dex и сначала выполнить его, а затем установить свой крюк RootUtils.isRooted()
.
В качестве альтернативы предполагается RootUtils.isRooted()
, что вызывается только из одного другого метода и не использует специальный код для загрузки RootUtils
класса, вы можете подключить этот метод и использовать этот крючок для установки установите свой RootUtils.isRooted()
крючок.
Обработка ошибок
Правильный способ обработки ошибок в JavaScript-использовать try
catch
блок, а не setTimeout
функцию:
Java.perform(() => {
try {
var hook = Java.use("app.name.RootUtils");
...
} catch (e) {
console.log("Failed to hook root detection" e);
}
}
Что касается ваших проблем с подключением класса
Комментарии:
1. Я использовал возражение (поиск/просмотр/список для подключения Android), чтобы найти app.name. RootUtils и метод с корнем, провел несколько тестов и увидел, что он вызывается, так что я знаю, что это метод, который мне нужно подключить. Я не уверен насчет файлов dex, есть 3 файла (classes0.dex.dat , classes1.dex.dat , classes2.dex) о setTimeout, который был предложен где-то, и я попытался использовать его, но, конечно, безуспешно . Также использование try catch не решит основную проблему, оно просто покажет приятную ошибку 🙂
2. @Mahdi Два файла dex.dat очень необычны, и эти файлы не загружаются Android автоматически, следовательно, должен быть какой-то код загрузчика. Вы должны проверить, зашифрованы ли эти файлы dex.dat каким-либо образом или изменены таким образом, чтобы они не подвергались статическому анализу.
Ответ №2:
Я смог решить эту проблему с помощью простого, но не очень технического решения.
Я использовал setInteval, чтобы запускать подключение снова и снова, пока оно не заработает (как упоминал @Robert, мне также нужно было завернуть подключение в ловушку try, чтобы предотвратить остановку кода после первой попытки).
Это может сработать не для всех, но так как это сработало для меня, я опубликую окончательный код, может быть, это поможет кому-то еще в будущем 🙂
Java.perform(function() {
var it = setInterval(function(){
try{
var hook = Java.use("app.name.RootUtils");
console.log("info: hooking target class");
hook.isRooted.overload().implementation = function() {
console.log("info: entered target method");
clearInterval(it);
return Java.use("java.lang.Boolean").$new(false);
}
} catch(e) {
console.log("failed!");
}
},200); // runs every 200milisecods
});
PS : возможно, вам придется изменить интервал времени в соответствии с потребностями вашего приложения, для меня это сработало за 200 миллисекунд.
Комментарии:
1. Хорошо, это тоже возможный способ, он может работать не каждый раз, но что с того. Но я бы изменил позицию
clearInterval(it);
. Вы можете переместить его в строку до} catch(e) {
того, как переменная вам не понадобитсяhooked
.
Ответ №3:
Иногда приложение выполняет шифрование в своем загрузчике классов, возможно, вам потребуется заменить Java.ClassFactory.loader на настроенный загрузчик классов приложения, чтобы правильно использовать функцию Java.use.
Вот как это делается:
Java.perform(function() {
//get real classloader
//from http://www.lixiaopeng.top/article/63.html
var application = Java.use("android.app.Application");
var classloader;
application.attach.overload('android.content.Context')
.implementation = function(context) {
var result = this.attach(context); // run attach as it is
classloader = context.getClassLoader(); // get real classloader
Java.classFactory.loader = classloader;
return resu<
}
//interesting classes
const interestingClassPath = "com.myApp.SometingInteresting";
interestingClass = Java.use(interestingClassPath);
//do whatever you like here
})