Дезинфицируем Android. Как устроены программы для очистки смартфона от встроенной малвари

Владельцы смартфонов, приобретенных на условном «Алиэкспрессе» за условные пятьдесят долларов, исчисляются десятками тысяч. Безусловно, пользоваться такими поделками можно, только вот их производители вынуждены экономить на каждом этапе, и здесь мы говорим даже не о проблемах с железом и качеством сборки — прошивка таких телефонов может содержать большие сюрпризы: малварь, трояны и абсолютно не оптимизированные приложения, которые беспощадно жрут батарею.  В этой статье мы покажем, как дать решительный бой всему этому дурно пахнущему программному мусору.

INFO: Самый прямой и брутальный способ решения проблемы зараженной прошивки — накатить чистую и проверенную. 🙂

Откуда берутся вредоносы на устройстве?

Раньше основным каналом поставки на устройство вредоносных приложений были форумы и неофициальные магазины. Немногие уже вспомнят, как vk музыка угоняла логины и пароли от нашей самой популярной социальной сети. Довольные пользователи скачивали ее с 4pda.ru, а потом некоторые из них недоумевали, просматривая свои сессии из стран, где они никогда не бывали. Мы уже писали, что сегодня не так сложно опубликовать вредонос в Google Play, тогда вопрос его распространения становится вопросом поисковой оптимизации в маркете (ASO).

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

Как найти вредонос?

Странности в поведении устройства обычно всплывают уже в первую неделю использования. Например, одна кастомная клавиатура время от времени показывала межстраничное объявление в самых неожиданных местах. Другие «быстрые браузеры» могут без спроса устанавливать чужие приложения. Заподозрив аномальную активность, с помощью сторонних программ можно посмотреть манифест устройства, вызывающего сомнения. Например, android.permission.SYSTEM_ALERT_WINDOW разрешает создавать окна поверх всех остальных приложений. Очень удобно для показа рекламы, как ты догадался. А для установки приложений нужен android.permission.REQUEST_INSTALL_PACKAGES.

Серые накрутчики установок так и работают — сначала заражаем устройства своими установщиками, потом принимаем заказы от легальных или не очень разработчиков на установку. С помощью трюков с accessibility service можно даже оставить отзыв и оценку в приложении Google Play. Отзывы, оценки и количество установок — важный фактор для ASO любого приложения, так что услуга еще долго будет востребована на рынке.

Как обезвредить вредонос?

Локализовав злокачественный пакет, можно сразу его удалить и успокоиться. Но простое удаление работает, только когда этот пакет не системный. Если пакет системный, можно его удалить с помощью рут-доступа. Возьми стороннее приложение или напиши свой собственный инструмент. Когда рут-прав нет, но вредонос есть в списке приложений, можно просто удалить его обновления и отключить его.

Если на этом мучения пользователя остановились, его можно поздравить. В самых запущенных случаях (нет рута, и вредонос засел в системе) может помочь лишь полная перепрошивка устройства, но она доступна только с разблокированным загрузчиком, а не на всех устройствах можно это сделать (кстати, и не на всех устройствах можно получить рут-доступ). Если загрузчик нельзя разблокировать, нет рут-прав и вредонос не светится в списке приложений в настройках, то лекарство остается только одно — магазин. 🙂

INFO: Перед покупкой нового устройства обязательно найди его ветку на 4pda.ru. Там может уже быть вся нужная информация о нежелательных приложениях, более чистых прошивках и способах получения рут-прав.

Как прочитать манифест установленного приложения?

Для чтения манифестов не требуется специальных разрешений в системе. Любое приложение может это делать без спроса, используя контекст чужого приложения:

Context context = createPackageContext(packageName, CONTEXT_RESTRICTED);
parser = context.getResources().getAssets().openXmlResourceParser("AndroidManifest.xml");

Если пишешь свое приложение, удобнее всего отобразить манифест в компоненте WebView. Сначала сгенерируй красивый HTML-файл (со стилями CSS и прочим), потом загрузи его в WebView. Есть также и готовые читалки манифестов, в маркете можешь сам поискать по запросу manifest view.

Как получить множество информации об установленном APK при помощи PackageManager?

С первой версии Android SDK у нас есть замечательный инструмент — класс PackageManager. Его метод getInstalledApplications вернет список установленных на устройстве приложений (ApplicationInfo), как системных, так и пользовательских. Зная установленные на устройстве приложения, можно оптимизировать рекламу. Например, не показывать рекламу тех, что уже есть. Или же можно просто собирать статистику по пользователям. Есть мнение, что кто-то за эту статистику платит, но мне таких пока не встречалось.

Класс ApplicationInfo содержит много полезной информации (имя пакета, путь до APK-файла, включено или отключено приложение и так далее). Но если ее оказалось мало, то вызови метод getPackageInfo() для нужного имени пакета приложения, и получишь еще больше данных в классе PackageInfo (версия, время установки, время последнего обновления и прочее).

Как извлечь установленный APK-файл?

Класс PackageManager позволяет нам получить не только список всех зарегистрированных пакетов в системе, но и сами APK-файлы этих пакетов. ApplicationInfo.sourceDirсодержит путь до APK-файла (например, /data/app/имя пакета/base.apk). Мы можем скопировать файл во внутреннюю кеш-папку нашего приложения, а оттуда уже поделиться с другими приложениями с помощью FileProvider.

Использование FileProvider — рекомендованный Google способ обмена файлами между приложениями. Он, кстати, не требует прав доступа к внешнему хранилищу <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>, так что простое приложение будет иметь на один запрос прав меньше. Вот мы копируем APK в папку с кешем:

public static File copyFileToFilesDir(Context context, String fileName, String name) throws IOException {
  File src = new File(fileName);
  File dst = new File(context.getCacheDir(), name + ".apk");
  FileChannel inChannel = new FileInputStream(src).getChannel();
  FileChannel outChannel = new FileOutputStream(dst).getChannel();
  try {
    inChannel.transferTo(0, inChannel.size(), outChannel);
  } catch (IOException e) {
    e.printStackTrace();
  } finally {
    if (inChannel != null) {
      inChannel.close();
    }
    outChannel.close();
  }
  return dst;
}

Вот как поделиться приложением:

public void shareApk(View view) throws IOException {
  Intent shareIntent = new Intent();
  shareIntent.setAction(Intent.ACTION_SEND);
  shareIntent.setType("application/xml");
  Uri uri = FileProvider.getUriForFile(this, "ru.androidtools.greenbro.files",
    Tools.copyFileToFilesDir(this, app.sourceDir, app.packageName));
  shareIntent.putExtra(Intent.EXTRA_STREAM, uri);
  startActivity(Intent.createChooser(shareIntent, getResources().getText(R.string.share_file)));
}
 Как проверить приложение на присутствие в маркетах и что дает эта информация?

Класс PackageManager может сказать нам имя пакета приложения, установившего пакет. Для этого используется метод getPackageManager().getInstallerPackageName(), который вернет строку. Например, Play Маркет имеет com.google.market или com.google.market, Amazon App Store — com.amazon.venezia, маркет от Samsung — com.sec.android.app.samsungapps. А вот приложения с F-Droid указывают не на него, а на com.google.android.packageinstaller, как будто бы их установил сам пользователь.

Зная источник установки и имя пакета самого приложения, можно постучаться в маркеты по конкретным URL. Наличие программы в маркете — уже некоторый повод для самоуспокоения, все-таки их иногда чистят. А вот если приложение было из маркета удалено, то это серьезный сигнал для беспокойства (его, конечно, могли удалить из-за какой-нибудь чепухи, вроде нарушения чужих авторских прав, но, скорее всего, это было что-то реально нехорошее).

Для получения URL приложения в Play Маркет и F-Droid используй строчки

String playStoreUrl = "https://play.google.com/store/apps/details?id=" + packageName;
String fdroidStoreUrl = "https://www.f-droid.org/packages/" + packageName;

Аналогично можно проверять и остальные маркеты. Код простой проверки (запускай только в фоновых потоках, так как используется сеть):

private void checkPlayMarket(String playStoreUrl) {
  try {
    URL url = new URL(playStoreUrl);
    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
    connection.setRequestMethod("GET");
    connection.connect();
    int code = connection.getResponseCode();
    if (code == 200){
      // Есть в маркете
    }
    if (code == 404){
      // Нет в маркете
    }
    connection.disconnect();
  } catch (Exception e) {
    Log.e("CheckStoreTask", e.toString());
  }
}

Читаем системные ресурсы описания разрешений Android

Бывает, что нужно сделать описание разрешений, используемых приложением. Для таких случаев можно заготовить множество строчек, помноженное на количество локалей… плюс к каждой нужна своя картинка. От таких решений APK нашего конечного приложения будет пухнуть на глазах. А как ты знаешь, есть еще люди в 2017 году (за пределами Москвы их несколько миллионов), для которых размер APK-файла приложения очень важен. Места на хранилище устройства наверняка хватает всем, но очень дорогой интернет заставляет людей учитывать этот показатель при выборе приложения.

Можно зайти с другой стороны и взять описание и ресурсы из самой системы (класс PermissionInfo). Они уже сразу будут локализованы, так что переводчики пока отдохнут. Список разрешений конкретного приложения получаем вот так:

PackageInfo packageInfo = getPackageManager().getPackageInfo(app.packageName, PackageManager.GET_PERMISSIONS);
String[] requestedPermissions = packageInfo.requestedPermissions;

Информацию о разрешениях — так:

PermissionInfo pinfo = getPackageManager().getPermissionInfo(requestedPermissions[i], PackageManager.GET_META_DATA);

Как ты знаешь, разрешения в «Андроиде» разделены по группам. Получаем заголовок: getGroupTitle(pinfo.group); :

private String getGroupTitle(String perm) {
  String result = getString(R.string.others_permission);
  try {
    PermissionGroupInfo groupInfo = getPackageManager().getPermissionGroupInfo(perm, 0);
    result = groupInfo.loadLabel(getPackageManager()).toString();
  } catch (PackageManager.NameNotFoundException e) {
    e.printStackTrace();
  }
  return result;
}

Заголовок разрешения и картинку:

private CharSequence loadItemInfoLabel(PermissionInfo pinfo) {
  CharSequence label = pinfo.loadLabel(getPackageManager());
  if (label == null) {
    label = pinfo.name;
  }
  return label;
}

private Drawable loadItemInfoIcon(String perm) {
  Drawable icon;
  icon = Tools.getPermissionDrawable(getPackageManager(), perm);
  if (icon == null) icon = ContextCompat.getDrawable(this, R.drawable.ic_perm_device_info);
  return icon;
}

Будь осторожен: в любой момент может прийти null, так что имей заглушки на этот случай. Пока!

Источник — xakep.ru

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

*