
🔬 Uma ferramenta avançada de pesquisa para Android para captura de áudio VoIP em tempo real (Uplink/Downlink) por meio de hooking dinâmico do libaudioflinger.so. Testada e desenvolvida em WhatsApp, Signal e Telegram no Android 14.
Objetivo: Demonstrar a captura de fluxo VoIP ao hookear o audioserver do Android (especificamente libaudioflinger.so) e gravar buffers PCM de uplink (microfone) e downlink (alto-falante) quando o sistema está em modo comunicação.
App Controlador (Android)
Interface com 9 botões para gerenciar a configuração da política SELINUX, injeção de payload, movimentação de dados e reprodução.
Payload (libaudiohook.so)
Biblioteca compartilhada em C++ (compilada como parte do projeto do app) que hookeia métodos internos de AudioFlinger usando Dobby e grava buffers PCM brutos em um diretório de sessão.
Pós-processamento
Ferramenta adicional, tools/audioparser.py converte arquivos brutos .ac (Downlink)/.bc (Uplink) para WAV. O gerenciador de arquivos do app também pode converter e reproduzir no próprio aplicativo.
Utilitários de Pesquisa
Scripts Frida para rastreamento inicial de funções e descoberta de offsets; arquivos de análise para símbolos/offsets e outros trechos.
Este aplicativo Android fornece uma interface gráfica para gerenciar a gravação de VoIP através do audioserver. Ele se integra a um payload nativo que hookeia a pilha de áudio do Android para capturar áudio do microfone e do alto-falante de aplicativos VoIP.
O aplicativo serve como um centro de controle, permitindo injetar as políticas SELinux necessárias, iniciar/parar o monitoramento, gerenciar os dados gravados e reproduzir arquivos de áudio VoIP capturados.
A atividade principal contém 9 botões de controle:
Injetar Políticas
Injeta as políticas SELinux necessárias para que audioserver e este aplicativo funcionem juntos.
Iniciar Monitoramento
Injeta o payload da biblioteca compartilhada no audioserver, habilitando a captura de áudio VoIP ao hookear RecordTrack e PlaybackTrack. Usa AndKittyInjector para injeção de processo.
Parar Monitoramento
Reinicia o processo audioserver, interrompendo o monitoramento e removendo os hooks injetados.
Copiar Dados
Copia o áudio PCM bruto capturado do diretório temporário /data/local/tmp/voip para /sdcard/voip.
Ver Logs
Exibe logs relacionados à injeção, hooking e status da gravação.
Habilitar SELinux
Restaura o modo enforcing do SELinux.
Desabilitar SELinux
Define o SELinux para o modo permissivo. Use quando as políticas injetadas falharem.
Reproduzir Áudios
Abre o Gerenciador de Arquivos VoIP personalizado (/sdcard/voip/):
+----------------------+
| |
| App MainActivity |
| |
| |
|(9 Control Buttons UI)|
| |
| |
+----------+-----------+
|
|
|
+----------v-----------+
| |
| JNI Layer |
| |
| Selinux, Process Inje|
| ction, |
| |
| |
+----------------------+
Esta biblioteca compartilhada é injetada no audioserver para detectar AUDIO_MODE_IN_COMMUNICATION e capturar fluxos de áudio VoIP diretamente da pilha de áudio do Android para um conjunto predefinido de nomes de pacotes.
Ela usa o framework de hooking inline Dobby para interceptar métodos críticos dentro de libaudioflinger.so.
Os fluxos de áudio de uplink (microfone) e downlink (alto-falante) são capturados e gravados em arquivos de sessão para processamento posterior.
A biblioteca instala hooks nas seguintes funções dentro de AudioFlinger e classes relacionadas:
A biblioteca filtra fluxos com base no mapeamento UID → Pacote.
Aplicativos atualmente monitorados:
com.whatsappcom.whatsapp.w4b (WhatsApp Business)org.thoughtcrime.securesms (Signal)org.telegram.messengerorg.telegram.messenger.webVocê também pode adicionar outros aplicativos dentro de audioserver_hook.cpp:
static const char* kTargetPackages[] = {
"com.whatsapp",
"com.whatsapp.w4b",
"org.thoughtcrime.securesms",
"org.telegram.messenger",
"org.telegram.messenger.web"
};
Um diretório de sessão é criado no momento da injeção no audioserver em:
/data/local/tmp/voip/audioserver_session_<timestamp>/
Os arquivos são criados primeiro com a extensão .tmp e depois renomeados na parada do track:
<packageName>_<sampleRate>_<timestamp>.bc<packageName>_<sampleRate>_<timestamp>.acExemplo de sessão:
/data/local/tmp/voip/audioserver_session_1725389200/
├── com.whatsapp_48000_1725389201_123456.ac
├── com.whatsapp_48000_1725389201_123789.bc
Como a biblioteca alvo é stripped (sem símbolos) e os métodos não são exportados em libaudioflinger.so, precisamos de offsets exatos dos métodos e de outros componentes relacionados para um hooking bem-sucedido.
Podemos extrair esses offsets a partir de análise estática usando IDA-PRO ou de análise dinâmica usando frida.
Uma vez que os offsets são conhecidos, podemos substituí-los dentro do arquivo de cabeçalho offsets.h.
android::AudioFlinger::setMode(audio_mode_t)_ZN7android12AudioFlinger7setModeE11audio_mode_tandroid::AudioFlinger::RecordThread::RecordTrack::getNextBuffer(android::AudioBufferProvider::Buffer *)_ZN7android12AudioFlinger12RecordThread11RecordTrack13getNextBufferEPNS_19AudioBufferProvider6BufferEandroid::AudioFlinger::PlaybackThread::Track::getNextBuffer(android::AudioBufferProvider::Buffer *)_ZN7android12AudioFlinger14PlaybackThread5Track13getNextBufferEPNS_19AudioBufferProvider6BufferEandroid::AudioFlinger::PlaybackThread::Track::stop(void)_ZN7android12AudioFlinger14PlaybackThread5Track4stopEvEles podem ser obtidos por análise estática.
libaudioflinger.soandroid::AudioFlinger::ThreadBase::TrackBase::TrackBaseTrackBase::TrackBase(
IAfThreadBase *thread,
const sp<Client>& client,
const audio_attributes_t& attr,
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
size_t frameCount,
void *buffer,
size_t bufferSize,
audio_session_t sessionId,
pid_t creatorPid,
uid_t clientUid,
bool isOut,
const alloc_type alloc,
track_type type,
audio_port_handle_t portId,
std::string metricsId)
┌─────────────────────────────┐
│ VoIP Apps │
│ (WhatsApp / Signal / TG) │
└───────────────┬─────────────┘
│ Audio in/out
▼
┌─────────────────────────────┐
│ audioserver │
│ (libaudioflinger.so) │
└───────┬─────────┬───────────┘
│ │
uplink│ │downlink
(mic) │ │(speaker)
▼ ▼
┌───────────-──┐ ┌────────────-─┐
│ RecordTrack │ │ Track │
│ getNextBuffer│ │ getNextBuffer│
└───────┬────-─┘ └───────┬────-─┘
│ │
┌─────▼─────┐ ┌─────▼─────┐
│ hook_... │ │ hook_... │
│ (uplink) │ │ (downlink)│
└─────┬─────┘ └─────┬─────┘
│ │
└──────┬───┬──────┘
│ │
▼ ▼
┌───────────────────────────┐
│ /data/local/tmp/voip/ │
│ audioserver_session_* │
│ - *.bc (uplink) │
│ - *.ac (downlink) │
└───────────────────────────┘
Processo utilizado:
AudioFlinger gerencia reprodução/captura via PlaybackThread & RecordThread.PlaybackThread::Track (downlink) e RecordThread::RecordTrack (uplink).app/src/main/cpp/include/offsets.h) para sobreviver a símbolos removidos (stripped).MODE_IN_COMMUNICATIONNós hookeamos AudioFlinger::setMode(int mode) e mantemos um estado global:
g_currentAudioMode.g_currentAudioMode == AUDIO_MODE_IN_COMMUNICATION..
├── Android_App/
| ├── app/ # Android Studio project (App + Payload)
| │ ├── src/
| │ │ ├── main/
| │ │ │ ├── java/ # Controller App Java/Kotlin sources
| │ │ │ ├── cpp/ # Payload source (built with the app)
| │ │ │ │ └── include/
| │ │ │ │ └── offsets.h # Function offsets for tested builds
├── tools/
│ ├── audioparser.py # Converts .ac/.bc raw files → WAV
│ ├── frida/ # Frida scripts for initial hook discovery
│ │ ├── host_audioserver.py
│ │ ├── agent_audioserver.js
├── app-release.apk # Signed release apk
└── README.md # Root README (design, build, run instructions)
audioparser.pyapp/ no Android Studio.libaudiohook.so) como parte do projeto.adb install -r app/build/outputs/apk/debug/app-debug.apk
ou instale o app-release.apk fornecido:
adb install -r app-release.apk
Se você separar os artefatos, também pode enviar a biblioteca compartilhada para teste:
adb push app/src/main/jniLibs/arm64-v8a/libaudiohook.so /data/local/tmp/
audioserver em seu ambiente de teste.audioserver.audioserver./data/local/tmp/voip/… -> /sdcard/voip/ (e limpa o diretório temporário). adb pull /sdcard/voip/
python3 tools/audioparser.py voip/
Botões adicionais:
- "Ver Logs": Veja o status do injetor/hook.
- "Habilitar SELinux" / "Desabilitar SELinux": Alterne entre enforcing/permissivo em seu laboratório se as políticas forem muito restritivas.
- "Excluir Dados": Limpe
/data/local/tmp/voip/e/sdcard/voip/.
dlsym.dlsym direto pode falhar.offsets.h), validados com dumps de símbolos do Frida e IDA-PRO.AudioFlinger::setMode(IN_COMMUNICATION); registram transições; armazenam uma flag global de modo.audioserver podem ser bloqueadas..tmp primeiro; no stop(), renomear para (downlink) ou (uplink).frameworks/av/services/audioflinger (AudioFlinger, PlaybackThread, RecordThread)system/media/audio (formatos de áudio, atributos, máscaras de canais)Excluir Dados
Exclui todos os dados de áudio de /data/local/tmp/voip e /sdcard/voip.
NOTA: Após excluir os dados, precisamos reinjetar no audioserver para que o diretório de salvamento seja criado.
| Function | Purpose |
|---|
AudioFlinger::setMode | Rastreia transições de entrada/saída do modo IN_COMMUNICATION (VoIP ativo). |
RecordTrack::getNextBuffer | Captura quadros de áudio do microfone (uplink). |
PlaybackThread::Track::getNextBuffer | Captura quadros de áudio do alto-falante (downlink). |
Track::stop | Fecha e finaliza o arquivo de downlink. |
RecordTrack::stop | Fecha e finaliza o arquivo de uplink. |
android::AudioFlinger::RecordThread::RecordTrack::stop(void)_ZN7android12AudioFlinger12RecordThread11RecordTrack4stopEv.ac.bclibaudioflinger.so
RecordTrack::getNextBuffer, Track::getNextBuffer, etc.) não são exportadas e libaudioflinger.so não possui entradas PLT/GOT para elas.
dlsym() padrão ou mecanismos de hooking baseados em PLT.target_addr = base_addr(libaudioflinger) + offset
DobbyHook(target_addr, hook_fn, &orig_fn)