
🔬 أداة بحث متقدمة لنظام أندرويد لالتقاط صوت VoIP في الوقت الفعلي (Uplink/Downlink) عبر الربط الديناميكي بمكتبة libaudioflinger.so. تم اختبارها وبناؤها على واتساب وسيجنال وتيليجرام على أندرويد 14.
الهدف: توضيح التقاط تدفق VoIP عبر خطاف (hook) في audioserver بنظام أندرويد (وتحديدًا libaudioflinger.so) وتسجيل مخازن PCM للرابط الصاعد (الميكروفون) والرابط الهابط (مكبر الصوت) عندما يكون النظام في وضع الاتصال.
تطبيق التحكم (أندرويد)
واجهة مستخدم تحتوي على 9 أزرار لإدارة إعداد سياسات SELinux، وحقن الحمولة، ونقل البيانات، والتشغيل.
الحمولة (libaudiohook.so)
مكتبة مشتركة بلغة C++ (تُبنى كجزء من مشروع التطبيق) تقوم بعمل خطاف للطرق الداخلية في AudioFlinger باستخدام Dobby وتكتب مخازن PCM الخام إلى مجلد الجلسة.
المعالجة اللاحقة
أداة إضافية، tools/audioparser.py، تحوّل ملفات .ac (رابط هابط) / .bc (رابط صاعد) الخام إلى WAV. ويمكن لمدير الملفات في التطبيق أيضًا التحويل والتشغيل داخل التطبيق.
أدوات بحثية
سكربتات Frida لتتبع الدوال واكتشاف الإزاحات مبدئيًا؛ وملفات تحليل للرموز/الإزاحات ومقاطع أخرى.
يوفّر تطبيق أندرويد هذا واجهة رسومية لإدارة تسجيل VoIP عبر audioserver. ويتكامل مع حمولة أصلية تعمل كخطاف في مكدس الصوت بنظام أندرويد لالتقاط صوت الميكروفون ومكبر الصوت من تطبيقات VoIP.
يعمل التطبيق كمركز تحكم، إذ يتيح لك حقن سياسات SELinux المطلوبة، وبدء/إيقاف المراقبة، وإدارة البيانات المسجلة، وتشغيل ملفات صوت VoIP الملتقطة.
يحتوي النشاط الرئيسي على 9 أزرار تحكم:
حقن السياسات
يحقن سياسات SELinux المطلوبة لكي يعمل audioserver وهذا التطبيق معًا.
بدء المراقبة
يحقن الحمولة (المكتبة المشتركة) في audioserver، ما يتيح التقاط صوت VoIP عبر خطاف في RecordTrack وPlaybackTrack. يستخدم AndKittyInjector لحقن العمليات.
إيقاف المراقبة
يعيد تشغيل عملية audioserver، فيوقف المراقبة ويزيل الخطافات المحقونة.
نسخ البيانات
ينسخ صوت PCM الخام الملتقط من المجلد المؤقت /data/local/tmp/voip إلى /sdcard/voip.
عرض السجلات
يعرض السجلات المتعلقة بالحقن والخطاف وحالة التسجيل.
تفعيل SELinux
يستعيد وضعَ الإلزام (enforcing) في SELinux.
تعطيل SELinux
يضبط SELinux على الوضع المتساهل (permissive). استخدمه عند فشل السياسات المحقونة.
تشغيل الصوتيات
يفتح مدير ملفات VoIP المخصص (/sdcard/voip/):
+----------------------+
| |
| App MainActivity |
| |
| |
|(9 Control Buttons UI)|
| |
| |
+----------+-----------+
|
|
|
+----------v-----------+
| |
| JNI Layer |
| |
| Selinux, Process Inje|
| ction, |
| |
| |
+----------------------+
تُحقن هذه المكتبة المشتركة في audioserver لكشف AUDIO_MODE_IN_COMMUNICATION والتقاط تدفقات صوت VoIP مباشرةً من مكدس الصوت في أندرويد لمجموعة محددة مسبقًا من أسماء الحزم.
يستخدم إطار عمل الخطاف المضمّن Dobby لاعتراض طرق حرجة داخل libaudioflinger.so.
يتم التقاط كل من تدفقي الصوت الرابط الصاعد (الميكروفون) والرابط الهابط (مكبر الصوت) وكتابتهما في ملفات الجلسة لمعالجتها لاحقًا.
تثبّت المكتبة خطافات على الدوال التالية داخل AudioFlinger والطبقات ذات الصلة:
تفلتر المكتبة التدفقات بناءً على تعيين UID إلى الحزمة.
التطبيقات المراقبة حاليًا:
com.whatsappcom.whatsapp.w4b (واتساب للأعمال)org.thoughtcrime.securesms (Signal)org.telegram.messengerorg.telegram.messenger.webيمكنك إضافة تطبيقات أخرى أيضًا داخل audioserver_hook.cpp:
static const char* kTargetPackages[] = {
"com.whatsapp",
"com.whatsapp.w4b",
"org.thoughtcrime.securesms",
"org.telegram.messenger",
"org.telegram.messenger.web"
};
يتم إنشاء مجلد جلسة عند الحقن في audioserver تحت المسار:
/data/local/tmp/voip/audioserver_session_<timestamp>/
تُنشأ الملفات أولاً بامتداد .tmp، ثم يُعاد تسميتها عند إيقاف المسار:
<packageName>_<sampleRate>_<timestamp>.bc<packageName>_<sampleRate>_<timestamp>.acمثال على جلسة:
/data/local/tmp/voip/audioserver_session_1725389200/
├── com.whatsapp_48000_1725389201_123456.ac
├── com.whatsapp_48000_1725389201_123789.bc
بما أن المكتبة المستهدفة مجرّدة (stripped) والطرق غير مُصدَّرة في libaudioflinger.so، نحتاج إلى إزاحات دقيقة للطرق والمكوّنات الأخرى ذات الصلة لنجاح عملية الخطاف.
يمكننا استخراج هذه الإزاحات من التحليل الساكن باستخدام IDA-PRO أو من التحليل الديناميكي باستخدام frida.
وبمجرد معرفة الإزاحات يمكننا استبدالها داخل ملف الترويسة 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)_ZN7android12AudioFlinger14PlaybackThread5Track4stopEvيمكن الحصول عليها من التحليل الساكن.
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) │
└───────────────────────────┘
العملية المستخدمة:
AudioFlinger التشغيل/الالتقاط عبر PlaybackThread وRecordThread.PlaybackThread::Track (الرابط الهابط) وRecordThread::RecordTrack (الرابط الصاعد).app/src/main/cpp/include/offsets.h) لتفادي الرموز المجرّدة.MODE_IN_COMMUNICATIONنقوم بتعليق خطاف على AudioFlinger::setMode(int mode) ونحتفظ بحالة عامة:
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/ في أندرويد ستوديو.libaudiohook.so) كجزء من المشروع.adb install -r app/build/outputs/apk/debug/app-debug.apk
أو ثبّت app-release.apk المرفق:
adb install -r app-release.apk
إذا فصلت المخرجات، يمكنك أيضًا دفع المكتبة المشتركة للاختبار:
adb push app/src/main/jniLibs/arm64-v8a/libaudiohook.so /data/local/tmp/
audioserver في بيئة الاختبار.audioserver.audioserver./data/local/tmp/voip/… إلى /sdcard/voip/ (وينظف المجلد المؤقت). adb pull /sdcard/voip/
python3 tools/audioparser.py voip/
أزرار إضافية:
- "عرض السجلات": للاطلاع على حالة الحقن/الخطاف.
- "تفعيل SELinux" / "تعطيل SELinux": للتبديل بين الإلزام/التساهل في بيئة الاختبار إذا كانت السياسات صارمة للغاية.
- "حذف البيانات": لمسح كل من
/data/local/tmp/voip/و/sdcard/voip/.
dlsym.dlsym المباشر.offsets.h)، مع التحقق منها عبر تفريغات رموز Frida وIDA-PRO.AudioFlinger::setMode(IN_COMMUNICATION)؛ تسجيل التحولات؛ تخزين علامة وضع عامة.audioserver..tmp أولاً؛ وعند stop() إعادة التسمية إلى .ac (رابط هابط) أو (رابط صاعد).frameworks/av/services/audioflinger (AudioFlinger, PlaybackThread, RecordThread)system/media/audio (audio formats, attributes, channel masks)حذف البيانات
يحذف جميع البيانات الصوتية من /data/local/tmp/voip و/sdcard/voip معًا.
ملاحظة: بعد حذف البيانات، نحتاج إلى إعادة الحقن في audioserver حتى يتم إنشاء المجلد لحفظ البيانات.
| Function | Purpose |
|---|
AudioFlinger::setMode | يتتبع الانتقال إلى/من وضع IN_COMMUNICATION (نشاط VoIP). |
RecordTrack::getNextBuffer | يلتقط إطارات صوت الميكروفون (الرابط الصاعد). |
PlaybackThread::Track::getNextBuffer | يلتقط إطارات صوت مكبر الصوت (الرابط الهابط). |
Track::stop | يغلق ملف الرابط الهابط وينهيه. |
RecordTrack::stop | يغلق ملف الرابط الصاعد وينهيه. |
android::AudioFlinger::RecordThread::RecordTrack::stop(void)_ZN7android12AudioFlinger12RecordThread11RecordTrack4stopEv.bclibaudioflinger.so
RecordTrack::getNextBuffer, Track::getNextBuffer, إلخ) غير مُصدَّرة، وlibaudioflinger.so لا يحتوي على مدخلات PLT/GOT لها.
dlsym() القياسية أو الخطاف المعتمد على PLT.target_addr = base_addr(libaudioflinger) + offset
DobbyHook(target_addr, hook_fn, &orig_fn)