<summary><strong>Aprenda hacking no AWS do zero ao herói com</strong><ahref="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
* Se você quer ver sua **empresa anunciada no HackTricks** ou **baixar o HackTricks em PDF**, confira os [**PLANOS DE ASSINATURA**](https://github.com/sponsors/carlospolop)!
* Adquira o [**material oficial PEASS & HackTricks**](https://peass.creator-spring.com)
* Descubra [**A Família PEASS**](https://opensea.io/collection/the-peass-family), nossa coleção de [**NFTs**](https://opensea.io/collection/the-peass-family) exclusivos
* **Junte-se ao grupo** 💬 [**Discord**](https://discord.gg/hRep4RUj7f) ou ao grupo [**telegram**](https://t.me/peass) ou **siga**-me no **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/carlospolopm)**.**
* **Compartilhe suas técnicas de hacking enviando PRs para os repositórios github do** [**HackTricks**](https://github.com/carlospolop/hacktricks) e [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud).
Aplicações Android podem conter bibliotecas nativas compiladas. Bibliotecas nativas são códigos que o desenvolvedor escreveu e depois compilou para uma arquitetura de computador específica. Na maioria das vezes, isso significa código que foi escrito em C ou C++. Os motivos benignos, ou legítimos, para um desenvolvedor fazer isso incluem operações matematicamente intensivas ou sensíveis ao tempo, como bibliotecas gráficas. Desenvolvedores de malware começaram a migrar para código nativo porque a engenharia reversa de binários compilados tende a ser uma habilidade menos comum do que analisar bytecode DEX. Isso se deve em grande parte ao fato de que o bytecode DEX pode ser descompilado para Java, enquanto o código nativo compilado, muitas vezes, deve ser analisado como assembly.
O objetivo desta seção não é ensinar assembly (ASM) ou como fazer engenharia reversa de código compilado de forma mais geral, mas sim como aplicar as habilidades mais gerais de engenharia reversa de binários, especificamente para Android. Como o objetivo deste workshop não é ensinar as arquiteturas ASM, todos os exercícios incluirão uma versão ARM _e_ uma versão x86 da biblioteca a ser analisada, para que cada pessoa possa escolher a arquitetura com a qual se sente mais confortável.
Se você não tem experiência prévia com engenharia reversa de binários/assembly, aqui estão alguns recursos sugeridos. A maioria dos dispositivos Android roda em ARM, mas todos os exercícios deste workshop também incluem uma versão x86 da biblioteca.
Para aprender e/ou revisar assembly ARM, eu sugiro fortemente o [ARM Assembly Basics](https://azeria-labs.com/writing-arm-assembly-part-1/) da [Azeria Labs](https://azeria-labs.com).
A Interface Nativa Java (JNI) permite que desenvolvedores declarem métodos Java que são implementados em código nativo (geralmente C/C++ compilado). A interface JNI não é específica para Android, mas está disponível de forma mais geral para aplicações Java que rodam em diferentes plataformas.
O Android Native Development Kit (NDK) é o conjunto de ferramentas específico para Android em cima do JNI. De acordo com a [documentação](https://developer.android.com/ndk/guides):
> No Android, o Native Development Kit (NDK) é um conjunto de ferramentas que permite aos desenvolvedores escrever código em C e C++ para suas aplicações Android.
Juntos, JNI e NDK permitem que desenvolvedores Android implementem parte da funcionalidade de seus aplicativos em código nativo. O código Java (ou Kotlin) chamará um método nativo declarado em Java que é implementado na biblioteca nativa compilada.
* [Funções JNI](https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html) <– Eu sempre tenho esta aberta e me refiro a ela enquanto faço engenharia reversa de bibliotecas nativas Android
* [Dicas JNI do Android](https://developer.android.com/training/articles/perf-jni) <– Sugiro fortemente a leitura da seção "Bibliotecas Nativas" para começar
* [Introdução ao NDK](https://developer.android.com/ndk/guides/) <– Este é um guia de como os desenvolvedores criam bibliotecas nativas e entender como as coisas são construídas facilita a engenharia reversa.
Para esta seção, estamos focando em como fazer engenharia reversa da funcionalidade de aplicativos que foi implementada em bibliotecas nativas Android. Quando dizemos bibliotecas nativas Android, o que queremos dizer?
Bibliotecas nativas Android são incluídas em APKs como `.so`, bibliotecas de objeto compartilhado, no formato de arquivo ELF. Se você já analisou binários Linux anteriormente, é o mesmo formato.
Por padrão, essas bibliotecas são incluídas no APK no caminho de arquivo `/lib/<cpu>/lib<name>.so`. Este é o caminho padrão, mas os desenvolvedores também podem optar por incluir a biblioteca nativa em `/assets/<custom_name>` se assim desejarem. Mais frequentemente, estamos vendo desenvolvedores de malware optarem por incluir bibliotecas nativas em caminhos diferentes de `/lib` e usando extensões de arquivo diferentes para tentar "esconder" a presença da biblioteca nativa.
Como o código nativo é compilado para CPUs específicas, se um desenvolvedor quer que seu aplicativo rode em mais de um tipo de hardware, ele tem que incluir cada uma dessas versões da biblioteca nativa compilada no aplicativo. O caminho padrão mencionado acima inclui um diretório para cada tipo de CPU oficialmente suportado pelo Android.
Antes de um aplicativo Android poder chamar e executar qualquer código que esteja implementado em uma biblioteca nativa, o aplicativo (código Java) deve carregar a biblioteca na memória. Existem duas chamadas de API diferentes que fazem isso:
A diferença entre as duas chamadas de API é que `loadLibrary` só aceita o nome curto da biblioteca como argumento (ou seja, libcalc.so = "calc" & libinit.so = "init") e o sistema determinará corretamente a arquitetura em que está sendo executado e, portanto, o arquivo correto a ser usado. Por outro lado, `load` requer o caminho completo para a biblioteca. Isso significa que o desenvolvedor do aplicativo precisa determinar a arquitetura e, portanto, o arquivo de biblioteca correto a ser carregado por conta própria.
Quando qualquer uma dessas duas APIs (`loadLibrary` ou `load`) é chamada pelo código Java, a biblioteca nativa que é passada como argumento executa seu `JNI_OnLoad` se ele foi implementado na biblioteca nativa.
Para reiterar, antes de executar quaisquer métodos nativos, a biblioteca nativa deve ser carregada chamando `System.loadLibrary` ou `System.load` no código Java. Quando qualquer uma dessas 2 APIs é executada, a função `JNI_OnLoad` na biblioteca nativa também é executada.
Para executar uma função da biblioteca nativa, deve haver um método nativo declarado em Java que o código Java possa chamar. Quando este método nativo declarado em Java é chamado, a função nativa "pareada" da biblioteca nativa (ELF/.so) é executada.
Um método nativo declarado em Java aparece no código Java como abaixo. Ele parece como qualquer outro método Java, exceto que inclui a palavra-chave `native` e não tem código em sua implementação, porque seu código está na verdade na biblioteca nativa compilada.
Para chamar este método nativo, o código Java o chamaria como qualquer outro método Java. No entanto, nos bastidores, o JNI e o NDK executariam a função correspondente na biblioteca nativa. Para fazer isso, é necessário saber o pareamento entre um método nativo declarado em Java com uma função na biblioteca nativa.
Para vincular, ou parear, o método nativo declarado em Java e a função na biblioteca nativa dinamicamente, o desenvolvedor nomeia o método e a função de acordo com as especificações de forma que o sistema JNI possa fazer a vinculação dinamicamente.
De acordo com a especificação, o desenvolvedor nomearia a função da seguinte forma para que o sistema pudesse vincular dinamicamente o método nativo e a função. Um nome de método nativo é concatenado a partir dos seguintes componentes:
Se o desenvolvedor não quiser ou não puder nomear as funções nativas de acordo com a especificação (Ex. deseja remover símbolos de depuração), então ele deve usar a vinculação estática com a API `RegisterNatives` ([doc](https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html#wp5833)) para fazer o pareamento entre o método nativo declarado em Java e a função na biblioteca nativa. A função `RegisterNatives` é chamada a partir do código nativo, não do código Java e é mais frequentemente chamada na função `JNI_OnLoad`, uma vez que `RegisterNatives` deve ser executada antes de chamar o método nativo declarado em Java.
Ao realizar engenharia reversa, se a aplicação estiver usando o método de ligação estática, nós, como analistas, podemos encontrar a estrutura `JNINativeMethod` que está sendo passada para `RegisterNatives` a fim de determinar qual sub-rotina na biblioteca nativa é executada quando o método nativo declarado em Java é chamado.
A estrutura `JNINativeMethod` requer uma string do nome do método nativo declarado em Java e uma string da assinatura do método, então devemos ser capazes de encontrar essas informações em nossa biblioteca nativa.
A estrutura `JNINativeMethod` requer a assinatura do método. Uma assinatura de método indica os tipos de argumentos que o método aceita e o tipo do que ele retorna. Este link documenta [Assinaturas de Tipo JNI](https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/types.html) na seção "Assinaturas de Tipo".
No Exercício #5, vamos aprender a carregar bibliotecas nativas em um desmontador e identificar a função nativa que é executada quando um método nativo é chamado. Para este exercício específico, o objetivo não é engenharia reversa do método nativo, apenas encontrar a ligação entre a chamada ao método nativo em Java e a função que é executada na biblioteca nativa. Para este exercício, usaremos o exemplo Mediacode.apk. Este exemplo está disponível em `~/samples/Mediacode.apk` na VM. Seu hash SHA256 é a496b36cda66aaf24340941da8034bd53940d1b08d83a97f17a65ae144ebf91a.
1. Abra Mediacode.apk no jadx. Consulte [Exercício #1](https://maddiestone.github.io/AndroidAppRE/reversing_intro.html#exercise-1---beginning-re-with-jadx)
2. Desta vez, se você expandir a aba Recursos, verá que este APK tem um diretório `lib/`. As bibliotecas nativas para este APK estão nos caminhos padrão da CPU.
3. Agora precisamos identificar quaisquer métodos nativos declarados. No jadx, pesquise e liste todos os métodos nativos declarados. Deveria haver dois.
4. Em torno do método nativo declarado, veja se há algum lugar em que uma biblioteca nativa é carregada. Isso fornecerá orientação sobre qual biblioteca nativa procurar para a função a ser implementada.
5. Extraia a biblioteca nativa do APK criando um novo diretório e copiando o APK para essa pasta. Em seguida, execute o comando `unzip Mediacode.APK`. Você verá todos os arquivos extraídos do APK, que inclui o diretório `lib/`.
6. Selecione a arquitetura da biblioteca nativa que deseja analisar.
8. Para abrir a biblioteca nativa para análise, selecione "Novo Projeto", "Projeto Não Compartilhado", selecione um caminho para salvar o projeto e dê um nome. Isso cria um projeto no qual você pode carregar arquivos binários.
9. Depois de criar seu projeto, selecione o ícone do dragão para abrir o Navegador de Código. Vá em "Arquivo" > "Importar Arquivo" para carregar a biblioteca nativa na ferramenta. Você pode deixar todos os padrões.
10. Você verá a seguinte tela. Selecione "Analisar".
11. Usando as informações de vinculação acima, identifique a função na biblioteca nativa que é executada quando o método nativo declarado em Java é chamado.
### Engenharia Reversa de Código de Bibliotecas Nativas Android - JNIEnv <a href="#reversing-android-native-libraries-code---jnienv" id="reversing-android-native-libraries-code---jnienv"></a>
Ao começar a engenharia reversa de bibliotecas nativas Android, uma das coisas que eu não sabia que precisava saber era sobre `JNIEnv`. `JNIEnv` é uma estrutura de ponteiros de função para [Funções JNI](https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html). Toda função JNI em bibliotecas nativas Android, recebe `JNIEnv*` como o primeiro argumento.
Da documentação [Dicas JNI](https://developer.android.com/training/articles/perf-jni) do Android:
> As declarações C de JNIEnv e JavaVM são diferentes das declarações C++. O arquivo de inclusão "jni.h" fornece diferentes typedefs dependendo se está incluído em C ou C++. Por essa razão, é uma má ideia incluir argumentos JNIEnv em arquivos de cabeçalho incluídos por ambas as linguagens. (Dito de outra forma: se o seu arquivo de cabeçalho requer #ifdef \_\_cplusplus, você pode ter que fazer um trabalho extra se algo nesse cabeçalho se referir a JNIEnv.)
Ao analisar bibliotecas nativas Android, a presença de JNIEnv significa que:
1. Para funções nativas JNI, os argumentos serão deslocados por 2. O primeiro argumento é sempre JNIEnv\*. O segundo argumento será o objeto no qual a função deve ser executada. Para métodos nativos estáticos (eles têm a palavra-chave static na declaração Java) isso será NULL.
2. Você frequentemente verá ramificações indiretas no desmonte porque o código está adicionando o deslocamento ao ponteiro JNIEnv\*, desreferenciando para obter o ponteiro de função naquela localização e, em seguida, ramificando para a função.
Aqui está uma [planilha](https://docs.google.com/spreadsheets/d/1yqjFaY7mqyVIDs5jNjGLT-G8pUaRATzHWGFUgpdJRq8/edit?usp=sharing) da implementação em C da estrutura JNIEnv para saber quais ponteiros de função estão nos diferentes deslocamentos.
Na prática, no desmonte, isso aparece como muitas ramificações diferentes para endereços indiretos em vez da chamada de função direta. A imagem abaixo mostra uma dessas chamadas de função indireta. A linha destacada no desmonte mostra um `blx r3`. Como reversores, precisamos descobrir o que é r3. Não é mostrado na captura de tela, mas no início desta função, `r0` foi movido para `r5`. Portanto, `r5` é `JNIEnv*`. Na linha 0x12498 vemos `r3 = [r5]`. Agora `r3` é `JNIEnv` (sem \*).
Na linha 0x1249e, adicionamos 0x18 a `r3` e desreferenciamos. Isso significa que `r3` agora é igual a qualquer ponteiro de função que esteja no deslocamento 0x18 em JNIEnv. Podemos descobrir olhando a planilha. `[JNIEnv + 0x18] = Ponteiro para o método FindClass`
Portanto, `blx r3` na linha 0x124a4 está chamando `FindClass`. Podemos procurar informações sobre `FindClass` (e todas as outras funções em JNIEnv) na documentação JNIFunctions [aqui](https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html).
Felizmente, há uma maneira de obter a função JNI sem fazer tudo isso manualmente! Tanto nos descompiladores Ghidra quanto IDA Pro, você pode redefinir o primeiro argumento em funções JNI para o tipo `JNIEnv *` e ele identificará automaticamente as Funções JNI sendo chamadas. No IDA Pro, isso funciona imediatamente. No Ghidra, você tem que carregar os tipos JNI (ou o arquivo jni.h ou um arquivo de tipos de dados Ghidra do arquivo jni.h) primeiro. Para facilitar, carregaremos os tipos JNI do arquivo de tipos de dados Ghidra (gdt) produzido por Ayrx e disponível [aqui](https://github.com/Ayrx/JNIAnalyzer/blob/master/JNIAnalyzer/data/jni_all.gdt). Para facilitar, este arquivo está disponível na VM em `~/jni_all.gdt`.
Para carregá-lo para uso no Ghidra, na Janela do Gerenciador de Tipos de Dados, clique na seta para baixo no canto direito e selecione "Abrir Arquivo de Arquivo".
Em seguida, selecione o arquivo `jni_all.gdt` para carregar. Uma vez carregado, você deve ver jni_all na Lista do Gerenciador de Tipos de Dados, conforme mostrado abaixo.
![Captura de tela do jni_all Carregado no Gerenciador de Tipos de Dados](https://maddiestone.github.io/AndroidAppRE/images/LoadedInDataTypeManager.png)
Uma vez carregado no Ghidra, você pode então selecionar qualquer tipo de argumento no descompilador e selecionar "Retipar Variável". Defina o novo tipo para JNIEnv \*. Isso fará com que o descompilador agora mostre os nomes das Funções JNIFunctions chamadas em vez dos deslocamentos do ponteiro.
![Captura de tela dos nomes das Funções JNI após o argumento ter sido Retipado para JNIEnv\*](https://maddiestone.github.io/AndroidAppRE/images/RetypedToJNIEnv.png)
Vamos juntar todas as nossas habilidades anteriores: identificando pontos de partida para RE, revertendo DEX e revertendo código nativo para analisar um aplicativo que pode ter movido seus comportamentos prejudiciais para código nativo. O exemplo é `~/samples/HDWallpaper.apk`.
Você é um analista de malware para aplicativos Android. Você está preocupado que este exemplo possa estar fazendo fraude de SMS premium, ou seja, enviando um SMS para um número de telefone premium sem divulgação e consentimento do usuário. Para sinalizar como malware, você precisa determinar se o aplicativo Android está:
**Confira este blog:** [**https://medium.com/@shubhamsonani/how-to-debug-android-native-libraries-using-jeb-decompiler-eec681a22cf3**](https://medium.com/@shubhamsonani/how-to-debug-android-native-libraries-using-jeb-decompiler-eec681a22cf3)
<summary><strong>Aprenda hacking AWS do zero ao herói com</strong><ahref="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
* Se você quiser ver sua **empresa anunciada no HackTricks** ou **baixar o HackTricks em PDF** Confira os [**PLANOS DE ASSINATURA**](https://github.com/sponsors/carlospolop)!
* Obtenha o [**merchandising oficial do PEASS & HackTricks**](https://peass.creator-spring.com)
* Descubra [**A Família PEASS**](https://opensea.io/collection/the-peass-family), nossa coleção de [**NFTs**](https://opensea.io/collection/the-peass-family) exclusivos
* **Junte-se ao** 💬 [**grupo Discord**](https://discord.gg/hRep4RUj7f) ou ao [**grupo telegram**](https://t.me/peass) ou **siga** me no **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/carlospolopm)**.**
* **Compartilhe suas dicas de hacking enviando PRs para os repositórios github** [**HackTricks**](https://github.com/carlospolop/hacktricks) e [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud).