
🔬 Una herramienta avanzada de investigación para Android que captura audio VoIP en tiempo real (Uplink/Downlink) mediante el hooking dinámico de libaudioflinger.so. Probada y construida sobre WhatsApp, Signal y Telegram en Android 14.
Objetivo: Demostrar la captura de flujos VoIP enganchando el audioserver de Android (específicamente libaudioflinger.so) y grabando los buffers PCM de subida (micrófono) y bajada (altavoz) cuando el sistema está en modo communication.
App de Control (Android)
Interfaz con 9 botones para gestionar la configuración de políticas SELINUX, la inyección del payload, el movimiento de datos y la reproducción.
Payload (libaudiohook.so)
Librería compartida en C++ (compilada como parte del proyecto de la app) que engancha métodos internos de AudioFlinger usando Dobby y escribe buffers PCM crudos en un directorio de sesión.
Post-Procesamiento
Herramienta adicional, tools/audioparser.py, convierte archivos crudos .ac(Downlink)/.bc(Uplink) a WAV. El administrador de archivos de la app también puede convertir y reproducir dentro de la aplicación.
Utilidades de Investigación
Scripts de Frida para el trazado inicial de funciones y el descubrimiento de offsets; archivos de análisis para símbolos/offsets y otros fragmentos.
Esta aplicación Android proporciona una interfaz gráfica para gestionar la grabación VoIP a través del audioserver. Se integra con un payload nativo que se engancha en la pila de audio de Android para capturar el audio del micrófono y del altavoz de las aplicaciones VoIP.
La app actúa como un centro de control, permitiéndote inyectar las políticas SELinux requeridas, iniciar/detener la monitorización, gestionar los datos grabados y reproducir los archivos de audio VoIP capturados.
La actividad principal contiene 9 botones de control:
Inyectar Políticas
Inyecta las políticas SELinux necesarias para que audioserver y esta app funcionen juntos.
Iniciar Monitorización
Inyecta el payload de la librería compartida en audioserver, habilitando la captura de audio VoIP mediante el enganche de RecordTrack y PlaybackTrack. Utiliza AndKittyInjector para la inyección de procesos.
Detener Monitorización
Reinicia el proceso audioserver, deteniendo la monitorización y eliminando los hooks inyectados.
Copiar Datos
Copia el audio PCM crudo capturado del directorio temporal /data/local/tmp/voip a /sdcard/voip.
Ver Logs
Muestra los registros relacionados con la inyección, el enganche y el estado de la grabación.
Activar SELinux
Restaura el modo enforcing de SELinux.
Desactivar SELinux
Establece SELinux en modo permisivo. Úsalo si las políticas inyectadas fallan.
Reproducir Audios
Abre el Administrador de Archivos VoIP personalizado (/sdcard/voip/):
+----------------------+
| |
| App MainActivity |
| |
| |
|(9 Control Buttons UI)|
| |
| |
+----------+-----------+
|
|
|
+----------v-----------+
| |
| JNI Layer |
| |
| Selinux, Process Inje|
| ction, |
| |
| |
+----------------------+
Esta librería compartida se inyecta en audioserver para detectar AUDIO_MODE_IN_COMMUNICATION y capturar los flujos de audio VoIP directamente de la pila de audio de Android para un conjunto predefinido de nombres de paquete.
Utiliza el framework de enganche inline Dobby para interceptar métodos críticos dentro de libaudioflinger.so.
Tanto los flujos de audio uplink (micrófono) como downlink (altavoz) se capturan y escriben en archivos de sesión para su posterior procesamiento.
La librería instala hooks en las siguientes funciones dentro de AudioFlinger y clases relacionadas:
La librería filtra los flujos basándose en el mapeo UID → Paquete.
Aplicaciones actualmente monitorizadas:
com.whatsappcom.whatsapp.w4b (WhatsApp Business)org.thoughtcrime.securesms (Signal)org.telegram.messengerorg.telegram.messenger.webTambién puedes añadir otras aplicaciones dentro de audioserver_hook.cpp:
static const char* kTargetPackages[] = {
"com.whatsapp",
"com.whatsapp.w4b",
"org.thoughtcrime.securesms",
"org.telegram.messenger",
"org.telegram.messenger.web"
};
Se crea un directorio de sesión al inyectar en audioserver bajo:
/data/local/tmp/voip/audioserver_session_<timestamp>/
Los archivos se crean primero con la extensión .tmp y luego se renombran al detener el track:
<packageName>_<sampleRate>_<timestamp>.bc<packageName>_<sampleRate>_<timestamp>.acEjemplo de sesión:
/data/local/tmp/voip/audioserver_session_1725389200/
├── com.whatsapp_48000_1725389201_123456.ac
├── com.whatsapp_48000_1725389201_123789.bc
Dado que la librería objetivo está stripped y los métodos no están exportados en libaudioflinger.so, necesitamos los offsets exactos de los métodos y otros componentes relacionados para un enganche exitoso.
Podemos extraer esos offsets mediante análisis estático con IDA-PRO o mediante análisis dinámico con frida.
Una vez que se conocen los offsets, podemos reemplazarlos dentro del archivo de cabecera 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)_ZN7android12AudioFlinger14PlaybackThread5Track4stopEvEstos pueden obtenerse mediante análisis estático.
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) │
└───────────────────────────┘
Proceso utilizado:
AudioFlinger gestiona la reproducción/captura mediante PlaybackThread y RecordThread.PlaybackThread::Track (downlink) y RecordThread::RecordTrack (uplink).app/src/main/cpp/include/offsets.h) para sobrevivir a los símbolos stripped.MODE_IN_COMMUNICATIONEnganchamos AudioFlinger::setMode(int mode) y mantenemos un 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/ en Android Studio.libaudiohook.so) como parte del proyecto.adb install -r app/build/outputs/apk/debug/app-debug.apk
o instala el app-release.apk proporcionado:
adb install -r app-release.apk
Si separas los artefactos, también puedes enviar la librería compartida para probarla:
adb push app/src/main/jniLibs/arm64-v8a/libaudiohook.so /data/local/tmp/
audioserver en tu entorno de prueba.audioserver.audioserver./data/local/tmp/voip/… a /sdcard/voip/ (y limpia el directorio temporal). adb pull /sdcard/voip/
python3 tools/audioparser.py voip/
Botones adicionales:
- "Ver Logs": Consulta el estado del inyector/hooks.
- "Activar SELinux" / "Desactivar SELinux": Alterna entre enforcing/permisivo en tu laboratorio si las políticas son demasiado estrictas.
- "Eliminar Datos": Limpia tanto
/data/local/tmp/voip/como/sdcard/voip/.
dlsym.dlsym directo puede fallar.offsets.h), validados con volcados de símbolos de Frida e IDA-PRO.AudioFlinger::setMode(IN_COMMUNICATION); se registran las transiciones; se almacena un indicador global de modo.audioserver pueden estar bloqueadas..tmp; en renombrar a (downlink) o (uplink).frameworks/av/services/audioflinger (AudioFlinger, PlaybackThread, RecordThread)system/media/audio (formatos de audio, atributos, máscaras de canal)Eliminar Datos
Elimina todos los datos de audio de /data/local/tmp/voip y /sdcard/voip.
NOTA: Después de eliminar los datos, es necesario reinyectar en audioserver para que se cree el directorio y poder guardar los datos.
| Función | Propósito |
|---|
AudioFlinger::setMode | Rastrea las transiciones hacia/desde el modo IN_COMMUNICATION (VoIP activo). |
RecordTrack::getNextBuffer | Captura los frames de audio del micrófono (uplink). |
PlaybackThread::Track::getNextBuffer | Captura los frames de audio del altavoz (downlink). |
Track::stop | Cierra y finaliza el archivo de downlink. |
RecordTrack::stop | Cierra y finaliza el archivo de uplink. |
android::AudioFlinger::RecordThread::RecordTrack::stop(void)_ZN7android12AudioFlinger12RecordThread11RecordTrack4stopEvstop().ac.bclibaudioflinger.so
RecordTrack::getNextBuffer, Track::getNextBuffer, etc.) no están exportadas y libaudioflinger.so no tiene entradas PLT/GOT para ellas.
dlsym() o PLT.target_addr = base_addr(libaudioflinger) + offset
DobbyHook(target_addr, hook_fn, &orig_fn)