
Em um
artigo anterior, fiz uma revisão no OWASP Mobile TOP 10 e, em seguida, não tinha um caso adequado para demonstrar a necessidade de proteger o código-fonte. Um caso interessante para demonstração apareceu apenas recentemente e quem está interessado em examinar nossa experiência de contornar as verificações ambientais, vamos nos aprofundar.
Ao avaliar o desempenho de um dos projetos, nossa equipe percebeu imediatamente que o caso não seria fácil. Os desenvolvedores abordaram bem a questão da segurança da informação no programa e implementaram verificações no estado do ambiente de execução. O aplicativo não foi iniciado sob nenhuma das seguintes condições:
- o dispositivo estava em ruínas;
- um emulador foi usado;
- disponibilidade de conexão via USB;
- usando o modo de desenvolvedor.
Os desenvolvedores não ofuscaram o código-fonte e não havia verificação interna para modificação do código, o que me permitiu analisar os métodos pelos quais as verificações foram realizadas e executar as manipulações necessárias com eles.
Então, vamos começar. De acordo com o OWASP Mobile TOP 10, que usamos como metodologia de teste básica em Hacken, Reverse Engineering - essa vulnerabilidade inclui análise de arquivos binários para determinar o código-fonte, bibliotecas, algoritmos etc. Softwares como IDA Pro, Hopper, otool e outras ferramentas de engenharia reversa podem dar uma idéia da operação interna do aplicativo. Isso pode ser usado para procurar vulnerabilidades de aplicativos, extrair informações críticas, como um servidor back-end, chaves de criptografia ou propriedade intelectual.
Para conduzir a análise estática básica, usei uma ferramenta tão interessante como o MobSF, que executava descompilação e análise estática básica. Após a descompilação, fiquei interessado nos códigos java e smali do programa. O código Java é necessário para análise e faremos alterações no código smali. Mais detalhes sobre como smali e java se relacionam podem ser encontrados
aqui .
Tendo examinado a lista de classes, encontrei um arquivo responsável por verificar a rotina do telefone (veja a Figura 1) - rootingcheck / RootBeerNative.java.
Fig. 1. Lista de classes de aplicativosApós analisar a classe, ficou claro que precisamos procurar mais as chamadas para as funções checkForRoot () e setLogDebugMessage () (veja a Figura 2).
Fig. 2. O código fonte da classe para verificar se há rutovanostUsando o comando grep, obtemos os seguintes resultados, que nos mostram quais arquivos contêm a chamada para os métodos checkForRoot () e setLogDebugMessage ().
Resultados da Pesquisa:root @ kali: ~ / Desktop # grep -nr 'RootBeerNative' ******** _ v_0.9.2 /
********_v_0.9.2/smali/rootingcheck/RootBeerNative.smali:1:.class public Lrootingcheck/RootBeerNative; ********_v_0.9.2/smali/rootingcheck/RootBeerNative.smali:17: sput-boolean v0, Lrootingcheck/RootBeerNative;->?:Z ********_v_0.9.2/smali/rootingcheck/RootBeerNative.smali:28: sput-boolean v0, Lrootingcheck/RootBeerNative;->?:Z ********_v_0.9.2/smali/rootingcheck/RootBeerNative.smali:57: sget-boolean v0, Lrootingcheck/RootBeerNative;->?:Z ********_v_0.9.2/smali/o/CM.smali:591: new-instance v1, Lrootingcheck/RootBeerNative; ********_v_0.9.2/smali/o/CM.smali:593: invoke-direct {v1}, Lrootingcheck/RootBeerNative;-><init>()V ********_v_0.9.2/smali/o/CM.smali:685: new-instance v0, Lrootingcheck/RootBeerNative; ********_v_0.9.2/smali/o/CM.smali:687: invoke-direct {v0}, Lrootingcheck/RootBeerNative;-><init>()V ********_v_0.9.2/smali/o/CM.smali:689: invoke-static {}, Lrootingcheck/RootBeerNative;->?()Z ********_v_0.9.2/smali/o/CM.smali:753: new-instance v4, Lrootingcheck/RootBeerNative; ********_v_0.9.2/smali/o/CM.smali:755: invoke-direct {v4}, Lrootingcheck/RootBeerNative;-><init>()V ********_v_0.9.2/smali/o/CM.smali:764: invoke-virtual {v4, v3}, Lrootingcheck/RootBeerNative;->checkForRoot([Ljava/lang/Object;)I ********_v_0.9.2/smali/o/xZ$5.smali:257: new-instance v1, Lrootingcheck/RootBeerNative; ********_v_0.9.2/smali/o/xZ$5.smali:259: invoke-direct {v1}, Lrootingcheck/RootBeerNative;-><init>()V ********_v_0.9.2/smali/o/xZ$5.smali:261: invoke-static {}, Lrootingcheck/RootBeerNative;->?()Z
root @ kali: ~ / Desktop # grep -nr 'setLogDebugMessages' ******** _ v_0.9.2 / ********_v_0.9.2/smali/o/CM.smali:599: invoke-virtual {v1, v0}, Lrootingcheck/RootBeerNative;->setLogDebugMessages(Z)I ********_v_0.9.2/smali/o/CM.smali:761: invoke-virtual {v4, v0}, Lrootingcheck/RootBeerNative;->setLogDebugMessages(Z)I
Mas estes não foram todos os cheques. Após analisar a classe MainActivity.java, encontramos chamadas de função nas quais as strings “su”, “test-keys” e “what” são transmitidas, com a ajuda da qual a verificação é realizada (consulte a Fig. 3).
Fig. 3. Verificações de rotinaE novamente, com o comando grep, procuramos nos arquivos smali para verificar a rotina:
Resultados da Pesquisa:root @ kali: ~ / Desktop # grep -nr 'su' ******** _ v_0.9.2 / ********_v_0.9.2/smali/o/CM.smali:443: const-string v2, "su" ********_v_0.9.2/smali/o/CM.smali:706: const-string v2, "su" ********_v_0.9.2/smali/o/xZ$5.smali:172: const-string v1, "su" ********_v_0.9.2/smali/o/xZ$5.smali:347: const-string v0, "su"
root @ kali: ~ / Desktop # grep -nr 'test-keys' ******** _ v_0.9.2 / ********_v_0.9.2/smali/o/xZ$5.smali:141: const-string v1, "test-keys" ********_v_0.9.2/smali/o/xZ$5.smali:374: const-string v0, "test-keys"
root @ kali: ~ / Desktop # grep -nr 'que' ******** _ v_0.9.2 / ********_v_0.9.2/smali/o/CM.smali:437: const-string v2, "which"
No artigo, mostrarei apenas uma das modificações encontradas nas verificações de rotina. Após uma pequena manipulação, ou seja, alterando 1 para 0, as verificações de rotina foram removidas.
Fig. 4. O valor da variável é igual a um se o telefone estiver enraizado
Fig. 5. Agora, o valor da variável é zero se o telefone estiver enraizadoDepois disso - o programa pode ser montado, assinado com sua chave de liberação e executado em um telefone móvel. Mas se não for por dois, mas! Ou seja:
- Verificação de conexão USB;
- verificação da inclusão do modo Desenvolvedor - a conexão USB e o modo Desenvolvedor incluído permitem análises dinâmicas.
A verificação do modo Desenvolvedor é desativada da mesma maneira que a verificação de rotina - alterando a unidade para zero nas verificações
Na classe MainActivity.java, encontramos a linha responsável por verificar o modo Desenvolvedor (veja a Figura 6). Depois disso, procure arquivos nos quais a string “development_settings_enabled” está presente e modifique a verificação - altere 1 para 0 (consulte as Fig. 7 e 8).
Fig. 6. Verifique se o modo Desenvolvedor está ativado no telefoneResultados da Pesquisa:grep -nr "development_settings_enabled" ******** _ v_0.9.2 \ Binary file ********_v_0.9.2\/build/apk/classes.dex matches ********_v_0.9.2\/smali/o/xZ$1.smali:49: const-string v1, "development_settings_enabled"
Fig. 7. O local onde você precisa fazer a modificação
Fig. 8. ModificaçãoApós todas as manipulações, você pode executar o programa no modo Desenvolvedor.
Em seguida, desligue a verificação da conexão USB. Essa verificação está na classe MainActivity.java (veja a Figura 9). Sem grep, encontramos a linha em MainActivity.smali, encontramos a linha que contém a linha com a verificação USB - android.hardware.usb.action.USB_STATE. Depois disso, no código smali, modificamos a linha para outras permissões que retornem "false" (veja a Fig. 10).
Fig. 9. Verifique a conexão USB no código MainActivity.java
Fig. 10. A linha de código a ser excluída em MainActivity.smaliAgora resta gerar sua chave de liberação e assinar o aplicativo com ela. Isso é feito da seguinte maneira:
- Você precisa instalar dois aplicativos: Keytool e Jarsinger.
- Execute o comando para montar o aplicativo:
- apktool b C: \ Usuários \ Usuário \ Desktop \ ********
- Próximo: cd ******** \ dist \
- Próximo: Keytool.exe -genkey -alias key.keystore -keyalg RSA -validity 20000 -keystore key.keystore
- Próximo: Jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore key.keystore ******** Apk key.keystore
- Próximo: jarsigner -verify -verbose -certs ********.
Aqui, em princípio, todas as manipulações estão terminadas. Agora instalamos o aplicativo no telefone usando a instalação adb ou a partir do diretório do smartphone e você pode realizar testes dinâmicos de vulnerabilidade.
Após a instalação do aplicativo, execute-o (consulte a Fig. 11 e Fig. 12).
ConclusõesEm um exemplo prático, mostrei como você pode desativar algumas verificações no estado do tempo de execução. Além disso, com a ajuda de outras ferramentas, realizamos uma análise de vulnerabilidade, mas essa é uma história diferente ...
Que atitude negligente em relação à proteção de código pode levar a:
- estas são burlas de certas verificações incorporadas no programa
- implementação de código de terceiros, após o qual o programa pode ser publicado e usado como malware
Como posso me proteger? No
ByteCode, decidimos não reinventar a roda e sugerimos que o cliente ofusque o código fonte e use funções que verifiquem a modificação do código fonte.
PS
Você pode usar um método de análise mais avançado - é a depuração smali. Você pode ler mais sobre isso no
manual .
Um pouco para referência, formulei uma lista de linhas que são usadas para verificar a rotina:
- "Chaves de teste";
- "/system/app/Superuser.apk";
- "/ sbin / su";
- "/ sistema / bin / su";
- "/ sistema / xbin / su";
- "/ data / local / xbin / su";
- "/ data / local / bin / su";
- "/ sistema / sd / xbin / su";
- "/ sistema / bin / à prova de falhas / su";
- "/ data / local / su";
- "/ su / bin / su";
- "/ system / xbin / which";
- "Su";
- "Qual".