xkcd

Introdução

No desenvolvimento mobile somos quase que obrigados a usar alguma ferramenta de debug, só rodar o app e ver o comportamento dele muitas vezes não é o suficiente para entender que uma mudança deu bom ou não, ou até mesmo pra entender como que aquele bug sinistro está acontecendo.

Existem várias ferramentas bem estabelecidas no mundo mobile, acho que todos conhecem android studio e xcode. Quem desenvolveu apps usando react native talvez já ouviu falar de flipper ou reactotron.

Nesse post vamos ver uma ferramenta que talvez você nunca tenha ouvido falar, e que podemos usar para abordar o debug de problemas em aplicativos móveis de uma forma completamente diferente do tradicional.

Além de falar sobre ela, vou mostrar um exemplo simples de como usei ela para resolver um problema que tive recentemente.

Mas já adianto que acho improvável ela substituir outras ferramentas… A ideia aqui está longe disso, é muito mais sobre ter mais uma ferramenta no seu cinto de utilidades. Cada uma das ferramentas tem suas especialidades e seus casos de uso, e como engenheiro de software precisamos reconhecer quais ferramentas funcionam melhor para cada objetivo.

debug with breakpoint




Contexto sobre ferramentas usadas em AppSec

No meu tempo livre as vezes eu me pego estudando sobre segurança da informação focado no mobile, pesquisando na internet mas também fazendo meus próprios testes e explorações, e isso contribuiu para ter novas ideias e perspectivas diferentes sobre aplicações android e ios.

A área de segurança da informação tem várias ‘sub áreas’ (AppSec acho que pode se dizer como uma sub área, certo?), mas acho que todos que trabalham com isso devem conhecer a existência de uma ONG que tem como objetivo melhorar a segurança das aplicações através de projetos open source, a famosa OWASP (Open Worldwide Application Security Project)

Um parenteses aqui, devo ressaltar que a ideia dessa ONG é muito legal e talvez até meio comica se puxarmos essa ideia para desenvolvimento de software: imagine por exemplo uma iniciativa dessa só que com o objetivo de acabar com todos os bugs dos softwares? ou outra pra acabar com as breaking changes ao atualizar?? apesar de meio bizarro isso seria simplesmente maravilhoso kkkkk, cade a ong pra acabar com vendor-lock? fica ai a reflexão pra você.


imagination meme


Dentro da iniciativa da OWASP tem um sub-projeto chamado OWASP Mobile Application Security - e por se tratar de mobile vamos olhar com mais atenção.

O OWASP de Mobile tem uma lista com várias ferramentas open source para facilitar o trabalho para quem quer explorar aplicações mobile.

O que eu tenho percebido é que dependendo do caso elas também podem ajudar os desenvolvedores. Vou abordar em especial a ferramenta Frida, mas essas outras que listei aqui também são interessantes e valem a pena conhecer:

  1. MASTG-TOOL-0001 - Frida for Android
  2. MASTG-TOOL-0039 - Frida for iOS
  3. MASTG-TOOL-0029 - objection for Android
  4. MASTG-TOOL-0074 - objection for iOS
  5. MASTG-TOOL-0077 - Burp Suite

Capacidades, vantagens e desvantagens

Okok eu entendi que essas ferramentas podem ser úteis, mas pra que exatamente?

Para definir Frida em uma frase eu vou usar a que tem no site deles: Dynamic instrumentation toolkit for developers, reverse-engineers, and security researchers., dai falando mais tecnicamente: frida por baixo dos panos usa um módulo escrito em C que permite você injetar códigos JS durante período de execução de tal forma que é possível interagir com APIs que esse módulo provem.

Vou explicar melhor como isso pode ser aplicável no dia a dia.

Você talvez já deve ter pego aquele bug cabuloso onde o uso de debug foi útil e talvez até necessário, aquele cenário de concorrência entre duas funções ou senão um erro que só ocorre quando o app está em um estado muito específico.

Já deve estar cansado de saber que debug em ferramentas como android studio e xcode da pra colocar breakpoints para inspecionar e alterar variáveis, mas algo muito bacana e que não vejo muitas pessoas comentando é sobre o watchpoints (android) onde você consegue registrar um ‘hook’ para pausar a execução do app quando uma variável específica for alterada, ou no iOS o thread return (lldb no iOS) que permite você alterar o valor de retorno de uma função nativa.

A Frida tem as mesmas capacidades de debug que falei acima (leitura, escrita e observar uma variável), mas aborda isso de uma maneira completamente diferente, e eu acredito que é ai que podemos tirar proveito dela.


debug matrix morpheus


Um exemplo simples de debug que fiz usando Frida

Nessa semana encontrei um bug muito esquisito que acontecia apenas em dispositivos iOS em um cenário desconhecido, a única evidência ou log que eu tive sobre isso é o código do erro e uma mensagem muito superficial: -25291 (errSecNotAvailable) - que se você ler a doc basicamente diz No trust results are available., no log de erro a seguinte mensagem aparece: No keychain is available..

Bom, depois de muito investigar e achar diversos links de pessoas que passaram pelo mesmo problema e não acharam a causa raiz, ficou muito difícil para reproduzir o erro e corrigir o que causa ele de fato, mas para não sair de mãos vazias eu decidi seguir pela abordagem de assumir que ele vai ocorrer e tratar o erro de forma elegante.

Dai veio um pensamento: Ok mas quando eu fizer essa possível tratativa do erro, como que vou comprovar que minha solução de fato trata o erro se eu nem consegui reproduzir ele? É basicamente uma solução às cegas, um tiro no escuro. Até podemos lançar um throw no meio do código pra fingir que o erro aconteceu, mas com certeza não é a mesma coisa que a API do módulo de Security te retornar esse erro de fato.

Eu poderia usar o modo debug do xcode e tentar colocar um breakpoint e usar o thread return para trocar o valor, mas iria gastar um bom tempo para entender quais comandos usar no lldb para modificar o retorno do método que eu quero… dai veio a ideia de usar o Frida - lendo as docs eu vi que você pode usar APIs em JS para poder modificar os retornos, o que pareceu bem promissor e relativamente simples de fazer, e como no passado eu já tinha configurado e instalado Frida no meu macbook, decidi tentar.

No final eu cheguei nesse código para forçar esse erro a acontecer, escrevi esse script que basicamente faz o hook na função nativa que consulta a keychain e força o retorno do erro que quero.

// keychain_hook_specific_error.js
const secItemCopyMatching = Module.findExportByName("Security", "SecItemCopyMatching");
if (secItemCopyMatching) {
    Interceptor.attach(secItemCopyMatching, {
        onEnter: (args) => {
            console.log("SecItemCopyMatching called");
        },
        onLeave: (retval) => {
            console.log("SecItemCopyMatching returning -25291");
            retval.replace(-25291);
        }
    });
} else {
    console.log("SecItemCopyMatching not found");
}

Lembrando que para usar o frida no iOS você precisa ter o dispositivo jailbroken ou escolher outro método de operação.

Com esse script, toda vez que a função SecItemCopyMatching for chamada, o retorno dela será alterado para o código de erro -25291 para simular o erro que quero testar. Dai depois disso bastou executar o seguinte comando:

ID_DO_SEU_DEVICE="" # serve tanto para simulador quanto device fisico.
# -F pra pegar o app que estiver rodando no momento, portanto abra o app antes de rodar esse comando
frida -D $ID_DO_SEU_DEVICE -F -l ./keychain_hook_specific_error.js

Agora que que esse script está rodando, toda vez que qualquer parte do meu app chamar essa função do módulo de Security, aquele erro vai ser retornado conforme eu configurei, dessa forma eu consigo comprovar que meu código está funcionando pois o módulo literalmente retornou esse erro, só precisei dar um empurrãozinho.

Esse foi um exemplo simples, mas já da pra ter uma ideia do poder do frida para fazer debug em runtime, e o melhor de tudo é que você não precisa modificar o código fonte do app para isso, muito menos saber onde que esse código está, você pode fazer o hook em qualquer função nativa ou gerenciada que quiser.

Além disso vale ressaltar que eu usei apenas 2 módulos do projeto (Interceptor e Module) mas existem outras que podem ser usados.

Tiveram pelo menos mais outras 2 vezes onde eu usei Frida e ferramentas que usam Frida por baixo dos panos (como o objection) para resolver problemas como esse, eu gostei que consegui chegar em uma solução bacana mais rápido do que eu esperava, e ainda por cima com evidências sólidas que minha solução funcionava - o que é muito bom, eu poderia citar elas aqui mas o post vai ficar longo demais, com o exemplo que eu dei já deu para entender por cima como uma ferramenta dessa pode ajudar no fluxo de debug.

Conclusão

Nesse post passamos um pouco sobre ferramentas de que são usadas na área de segurança da informação, dei alguns exemplos e aprofundamos um pouco mais na ferramenta Frida dando um exemplo bem simples.

A principal mensagem que quero que leve desse post é não ficar preso as ferramentas tradicionais por estar acostumado a usá-la.

Explore alternativas, até aquelas que são de outras áreas se realmente tiver interesse. Saber identificar as ““melhores”” ferramentas para cada caso pode te pagar dividendos, uma analogia interessante e meio exagerada para se pensar é: você consegue furar uma parede usando uma parafusadera? Não manjo muito de obras mas acredito que dependendo da potência dela e o material da parede até deve conseguir sim, mas sem dúvidas uma ferramenta própria para isso seria bem mais fácil.