Desativando verificações de estado de tempo de execução em um aplicativo Android



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 aplicativos

Apó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á rutovanost

Usando 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 rotina

E 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 enraizado

Depois 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:

  1. Verificação de conexão USB;
  2. 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 telefone

Resultados 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ção

Apó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.smali

Agora resta gerar sua chave de liberação e assinar o aplicativo com ela. Isso é feito da seguinte maneira:

  1. Você precisa instalar dois aplicativos: Keytool e Jarsinger.
  2. Execute o comando para montar o aplicativo:
  3. apktool b C: \ Usuários \ Usuário \ Desktop \ ********
  4. Próximo: cd ******** \ dist \
  5. Próximo: Keytool.exe -genkey -alias key.keystore -keyalg RSA -validity 20000 -keystore key.keystore
  6. Próximo: Jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore key.keystore ******** Apk key.keystore
  7. 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).

Fig. 11. Ligue o modo de desenvolvedor e conecte o USBFig. 12. Iniciando o aplicativo

Conclusões

Em 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".

Source: https://habr.com/ru/post/pt419171/


All Articles