как исправить ошибку: java.lang.Исключение ClassNotFoundException для фриды

#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
})