MASTG-TECH-0044: Exploração de Processos
Ao testar um aplicativo, a exploração de processos pode fornecer ao testador insights profundos sobre a memória do processo do aplicativo. Isso pode ser alcançado por meio de instrumentação em tempo de execução e permite realizar tarefas como:
- Recuperar o mapa de memória e bibliotecas carregadas.
- Buscar ocorrências de determinados dados.
- Após realizar uma busca, obter a localização de um determinado offset no mapa de memória.
- Realizar um dump de memória e inspecionar ou realizar engenharia reversa dos dados binários offline.
- Fazer engenharia reversa de uma biblioteca nativa enquanto ela está em execução.
Como você pode ver, essas tarefas passivas nos ajudam a coletar informações. Essas informações são frequentemente usadas para outras técnicas, como method hooking.
Nas seções a seguir, você usará r2frida para recuperar informações diretamente do runtime do aplicativo. Consulte as instruções oficiais de instalação do r2frida. Comece abrindo uma sessão do r2frida para o aplicativo alvo (por exemplo, o APK HelloWorld JNI) que deve estar em execução no seu telefone Android (conectado via USB). Use o seguinte comando:
r2 frida://usb//sg.vantagepoint.helloworldjni
Veja todas as opções com
r2 frida://?.
Uma vez na sessão do r2frida, todos os comandos começam com :. Por exemplo, no radare2 você executaria i para exibir as informações binárias, mas no r2frida você usaria :i.
Mapas de Memória e Inspeção¶
Você pode recuperar os mapas de memória do aplicativo executando :dm. A saída no Android pode ficar muito longa (por exemplo, entre 1500 e 2000 linhas). Para restringir sua busca e ver apenas o que pertence diretamente ao aplicativo, aplique um grep (~) pelo nome do pacote :dm~<nome_do_pacote>:
[0x00000000]> :dm~sg.vantagepoint.helloworldjni
0x000000009b2dc000 - 0x000000009b361000 rw- /dev/ashmem/dalvik-/data/app/sg.vantagepoint.helloworldjni-1/oat/arm64/base.art (deleted)
0x000000009b361000 - 0x000000009b36e000 --- /dev/ashmem/dalvik-/data/app/sg.vantagepoint.helloworldjni-1/oat/arm64/base.art (deleted)
0x000000009b36e000 - 0x000000009b371000 rw- /dev/ashmem/dalvik-/data/app/sg.vantagepoint.helloworldjni-1/oat/arm64/base.art (deleted)
0x0000007d103be000 - 0x0000007d10686000 r-- /data/app/sg.vantagepoint.helloworldjni-1/oat/arm64/base.vdex
0x0000007d10dd0000 - 0x0000007d10dee000 r-- /data/app/sg.vantagepoint.helloworldjni-1/oat/arm64/base.odex
0x0000007d10dee000 - 0x0000007d10e2b000 r-x /data/app/sg.vantagepoint.helloworldjni-1/oat/arm64/base.odex
0x0000007d10e3a000 - 0x0000007d10e3b000 r-- /data/app/sg.vantagepoint.helloworldjni-1/oat/arm64/base.odex
0x0000007d10e3b000 - 0x0000007d10e3c000 rw- /data/app/sg.vantagepoint.helloworldjni-1/oat/arm64/base.odex
0x0000007d1c499000 - 0x0000007d1c49a000 r-x /data/app/sg.vantagepoint.helloworldjni-1/lib/arm64/libnative-lib.so
0x0000007d1c4a9000 - 0x0000007d1c4aa000 r-- /data/app/sg.vantagepoint.helloworldjni-1/lib/arm64/libnative-lib.so
0x0000007d1c4aa000 - 0x0000007d1c4ab000 rw- /data/app/sg.vantagepoint.helloworldjni-1/lib/arm64/libnative-lib.so
0x0000007d1c516000 - 0x0000007d1c54d000 r-- /data/app/sg.vantagepoint.helloworldjni-1/base.apk
0x0000007dbd23c000 - 0x0000007dbd247000 r-- /data/app/sg.vantagepoint.helloworldjni-1/base.apk
0x0000007dc05db000 - 0x0000007dc05dc000 r-- /data/app/sg.vantagepoint.helloworldjni-1/oat/arm64/base.art
Enquanto você está pesquisando ou explorando a memória do aplicativo, pode sempre verificar onde está localizado a cada momento (onde seu offset atual está localizado) no mapa de memória. Em vez de anotar e procurar o endereço de memória nesta lista, você pode simplesmente executar :dm.. Você encontrará um exemplo na próxima seção "Busca em Memória".
Se você estiver interessado apenas nos módulos (binários e bibliotecas) que o aplicativo carregou, pode usar o comando :il para listá-los todos:
[0x00000000]> :il
0x000000558b1fd000 app_process64
0x0000007dbc859000 libandroid_runtime.so
0x0000007dbf5d7000 libbinder.so
0x0000007dbff4d000 libcutils.so
0x0000007dbfd13000 libhwbinder.so
0x0000007dbea00000 liblog.so
0x0000007dbcf17000 libnativeloader.so
0x0000007dbf21c000 libutils.so
0x0000007dbde4b000 libc++.so
0x0000007dbe09b000 libc.so
...
0x0000007d10dd0000 base.odex
0x0000007d1c499000 libnative-lib.so
0x0000007d2354e000 frida-agent-64.so
0x0000007dc065d000 linux-vdso.so.1
0x0000007dc065f000 linker64
Como você pode esperar, pode correlacionar os endereços das bibliotecas com os mapas de memória: por exemplo, a biblioteca nativa do aplicativo está localizada em 0x0000007d1c499000 e o dex otimizado (base.odex) em 0x0000007d10dd0000.
Você também pode usar o objection para exibir as mesmas informações.
$ objection --gadget sg.vantagepoint.helloworldjni explore
sg.vantagepoint.helloworldjni on (google: 8.1.0) [usb] # memory list modules
Save the output by adding `--json modules.json` to this command
Name Base Size Path
----------------------------------------------- ------------ -------------------- --------------------------------------------------------------------
app_process64 0x558b1fd000 32768 (32.0 KiB) /system/bin/app_process64
libandroid_runtime.so 0x7dbc859000 1982464 (1.9 MiB) /system/lib64/libandroid_runtime.so
libbinder.so 0x7dbf5d7000 557056 (544.0 KiB) /system/lib64/libbinder.so
libcutils.so 0x7dbff4d000 77824 (76.0 KiB) /system/lib64/libcutils.so
libhwbinder.so 0x7dbfd13000 163840 (160.0 KiB) /system/lib64/libhwbinder.so
base.odex 0x7d10dd0000 442368 (432.0 KiB) /data/app/sg.vantagepoint.helloworldjni-1/oat/arm64/base.odex
libnative-lib.so 0x7d1c499000 73728 (72.0 KiB) /data/app/sg.vantagepoint.helloworldjni-1/lib/arm64/libnative-lib.so
Você pode até ver diretamente o tamanho e o caminho para aquele binário no sistema de arquivos do Android.
Busca em Memória¶
A busca em memória é uma técnica muito útil para testar dados sensíveis que possam estar presentes na memória do aplicativo.
Consulte a ajuda do r2frida sobre o comando de busca (:/?) para aprender sobre o comando de busca e obter uma lista de opções. A seguir, é mostrado apenas um subconjunto delas:
[0x00000000]> :/?
/ search
/j search json
/w search wide
/wj search wide json
/x search hex
/xj search hex json
...
Você pode ajustar sua busca usando as configurações de busca :e~search. Por exemplo, :e search.quiet=true; imprimirá apenas os resultados e ocultará o progresso da busca:
[0x00000000]> :e~search
e search.in=perm:r--
e search.quiet=false
Por enquanto, continuaremos com os padrões e nos concentraremos na busca de strings. Este aplicativo é realmente muito simples: ele carrega a string "Hello from C++" de sua biblioteca nativa e a exibe para nós. Você pode começar procurando por "Hello" e ver o que o r2frida encontra:
[0x00000000]> :/ Hello
Searching 5 bytes: 48 65 6c 6c 6f
...
hits: 11
0x13125398 hit0_0 HelloWorldJNI
0x13126b90 hit0_1 Hello World!
0x1312e220 hit0_2 Hello from C++
0x70654ec5 hit0_3 Hello
0x7d1c499560 hit0_4 Hello from C++
0x7d1c4a9560 hit0_5 Hello from C++
0x7d1c51cef9 hit0_6 HelloWorldJNI
0x7d30ba11bc hit0_7 Hello World!
0x7d39cd796b hit0_8 Hello.java
0x7d39d2024d hit0_9 Hello;
0x7d3aa4d274 hit0_10 Hello
Agora você gostaria de saber onde esses endereços estão realmente localizados. Você pode fazer isso executando o comando :dm. para todos os hits @@ correspondentes ao glob hit0_*:
[0x00000000]> :dm.@@ hit0_*
0x0000000013100000 - 0x0000000013140000 rw- /dev/ashmem/dalvik-main space (region space) (deleted)
0x0000000013100000 - 0x0000000013140000 rw- /dev/ashmem/dalvik-main space (region space) (deleted)
0x0000000013100000 - 0x0000000013140000 rw- /dev/ashmem/dalvik-main space (region space) (deleted)
0x00000000703c2000 - 0x00000000709b5000 rw- /data/dalvik-cache/arm64/system@framework@boot-framework.art
0x0000007d1c499000 - 0x0000007d1c49a000 r-x /data/app/sg.vantagepoint.helloworldjni-1/lib/arm64/libnative-lib.so
0x0000007d1c4a9000 - 0x0000007d1c4aa000 r-- /data/app/sg.vantagepoint.helloworldjni-1/lib/arm64/libnative-lib.so
0x0000007d1c516000 - 0x0000007d1c54d000 r-- /data/app/sg.vantagepoint.helloworldjni-1/base.apk
0x0000007d30a00000 - 0x0000007d30c00000 rw-
0x0000007d396bc000 - 0x0000007d3a998000 r-- /system/framework/arm64/boot-framework.vdex
0x0000007d396bc000 - 0x0000007d3a998000 r-- /system/framework/arm64/boot-framework.vdex
0x0000007d3a998000 - 0x0000007d3aa9c000 r-- /system/framework/arm64/boot-ext.vdex
Além disso, você pode procurar ocorrências da versão wide da string (:/w) e, novamente, verificar suas regiões de memória:
[0x00000000]> :/w Hello
Searching 10 bytes: 48 00 65 00 6c 00 6c 00 6f 00
hits: 6
0x13102acc hit1_0 480065006c006c006f00
0x13102b9c hit1_1 480065006c006c006f00
0x7d30a53aa0 hit1_2 480065006c006c006f00
0x7d30a872b0 hit1_3 480065006c006c006f00
0x7d30bb9568 hit1_4 480065006c006c006f00
0x7d30bb9a68 hit1_5 480065006c006c006f00
[0x00000000]> :dm.@@ hit1_*
0x0000000013100000 - 0x0000000013140000 rw- /dev/ashmem/dalvik-main space (region space) (deleted)
0x0000000013100000 - 0x0000000013140000 rw- /dev/ashmem/dalvik-main space (region space) (deleted)
0x0000007d30a00000 - 0x0000007d30c00000 rw-
0x0000007d30a00000 - 0x0000007d30c00000 rw-
0x0000007d30a00000 - 0x0000007d30c00000 rw-
0x0000007d30a00000 - 0x0000007d30c00000 rw-
Eles estão na mesma região rw- de uma das strings anteriores (0x0000007d30a00000). Observe que procurar pelas versões wide das strings às vezes é a única maneira de encontrá-las, como você verá na próxima seção.
A busca em memória pode ser muito útil para saber rapidamente se determinados dados estão localizados no binário principal do aplicativo, dentro de uma biblioteca compartilhada ou em outra região. Você também pode usá-la para testar o comportamento do aplicativo em relação à forma como os dados são mantidos na memória. Por exemplo, você pode analisar um aplicativo que realiza um login e procurar ocorrências da senha do usuário. Além disso, você pode verificar se ainda consegue encontrar a senha na memória após o login ser concluído para verificar se esses dados sensíveis são apagados da memória após o uso.
Dump de Memória¶
Você pode fazer o dump da memória do processo do aplicativo com objection e Fridump. Para aproveitar essas ferramentas em um dispositivo não root, o aplicativo Android deve ser reempacotado com frida-gadget.so e reassinado. Uma explicação detalhada desse processo pode ser encontrada em Análise Dinâmica em Dispositivos Non-Rooted. Para usar essas ferramentas em um dispositivo root, basta ter o frida-server instalado e em execução.
Nota: Ao usar essas ferramentas, você pode encontrar vários erros de violação de acesso à memória que normalmente podem ser ignorados. Essas ferramentas injetam um agente Frida e tentam despejar toda a memória mapeada do aplicativo, independentemente das permissões de acesso (leitura/escrita/execução). Portanto, quando o agente Frida injetado tenta ler uma região que não é legível, ele retornará os correspondentes erros de violação de acesso à memória. Consulte a seção anterior "Mapas de Memória e Inspeção" para obter mais detalhes.
Com o objection, é possível despejar toda a memória do processo em execução no dispositivo usando o comando memory dump all.
$ objection --gadget sg.vantagepoint.helloworldjni explore
sg.vantagepoint.helloworldjni on (google: 8.1.0) [usb] # memory dump all /Users/foo/memory_Android/memory
Will dump 719 rw- images, totalling 1.6 GiB
Dumping 1002.8 MiB from base: 0x14140000 [------------------------------------] 0% 00:11:03(session detach message) process-terminated
Dumping 8.0 MiB from base: 0x7fc753e000 [####################################] 100%
Memory dumped to file: /Users/foo/memory_Android/memory
Neste caso, houve um erro, provavelmente devido a violações de acesso à memória, como já antecipamos. Esse erro pode ser ignorado com segurança, desde que possamos ver o dump extraído no sistema de arquivos. Se você tiver algum problema, um primeiro passo seria habilitar a flag de depuração
-dao executar o objection ou, se isso não ajudar, registrar um problema no GitHub do objection.
Em seguida, somos capazes de encontrar as strings "Hello from C++" com o radare2:
$ r2 /Users/foo/memory_Android/memory
[0x00000000]> izz~Hello from
1136 0x00065270 0x00065270 14 15 () ascii Hello from C++
Alternativamente, você pode usar o Fridump. Desta vez, vamos inserir uma string e ver se conseguimos encontrá-la no dump de memória. Para isso, abra o aplicativo Playground de Hacking do MASTG (Java), navegue até "OMTG_DATAST_002_LOGGING" e insira "owasp-mstg" no campo de senha. Em seguida, execute o Fridump:
python3 fridump.py -U sg.vp.owasp_mobile.omtg_android -s
Current Directory: /Users/foo/git/fridump
Output directory is set to: /Users/foo/git/fridump/dump
Starting Memory dump...
Oops, memory access violation!-------------------------------] 0.28% Complete
Progress: [##################################################] 99.58% Complete
Running strings on all files:
Progress: [##################################################] 100.0% Complete
Finished!
Dica: Habilite a verbosidade incluindo a flag
-vse quiser ver mais detalhes, por exemplo, as regiões que provocam violações de acesso à memória.
Levará um tempo até que seja concluído e você obterá uma coleção de arquivos *.data dentro da pasta dump. Quando você adiciona a flag -s, todas as strings são extraídas dos arquivos de memória raw despejados e adicionadas ao arquivo strings.txt, que também é armazenado no diretório dump.
ls dump/
dump/1007943680_dump.data dump/357826560_dump.data dump/630456320_dump.data ... strings.txt
Finalmente, procure a string inserida no diretório dump:
$ grep -nri owasp-mstg dump/
Binary file dump//316669952_dump.data matches
Binary file dump//strings.txt matches
A string "owasp-mstg" pode ser encontrada em um dos arquivos de dump, bem como no arquivo de strings processado.