В первой статье я разобрал как MAX собирает ваш IP, определяет VPN и какие сайты из вашей сети. Как MAX помогает РКН строить железный занавес: VPN-детект, сбор IP и проверки на Госуслуги
Дальше - про звонки. В MAX встроена система распознавания ключевых слов (KWS - Keyword Spotting). Нейросеть, которая прогоняет аудио с микрофона прямо во время разговора.
Я не утверждаю, что MAX прямо сейчас слушает ваши разговоры в поисках слов вроде «Путин», «митинг» или «VPN». Текущая модель распознавания обучена только на фразу «не слышу» - по задумке это для определения плохой связи. Прямо сейчас функция выключена на сервере.
Но....я разобрал архитектуру, проследил весь код от микрофона до отправки на сервер - и вот что важно:
KWS (Keyword Spotting) - распознавание конкретных слов в аудиопотоке. Вы с этим знакомы: «Окей, Google», «Привет, Алиса», «Hey Siri» - всё это KWS.
Принцип простой: нейросеть берёт звук с микрофона, режет на кусочки по 10 миллисекунд и на каждом решает - это ключевое слово или нет?
VK встроил такую штуку прямо в свой модифицированный WebRTC. Нейросеть крутится локально на устройстве и слушает аудио во время звонка.
Я скачал модель с серверов VK (https://st.okcdn.ru/static/calls_android/1-0-1/kws_270525.zip), разобрал её архитектуру и запустил.
Текущий функционал:
Для звонков это имеет смысл: собеседник говорит «не слышу» - значит со связью проблемы, приложение может это подхватить.
Распознавать «не слышу» - безобидно. А вот как это устроено внутри - уже нет.
Нейронка приходит с сервера. KWS не зашит в код приложения. При запуске приложение получает от сервера конфиг с URL модели, забирает файл и грузит в нейросетевой движок. Сервер VK решает:
VK может подменить модель хоть завтра - на любые другие слова. Обновлять приложение не нужно. Спрашивать пользователя - тоже.
Приложение не проверяет, что именно нейронка распознаёт. Если завтра VK положит на CDN модель, обученную на слово «протест» - приложение скачает её и запустит точно так же.
А что в политике конфиденциальности? Я проверил оба документа - Политику конфиденциальности (legal.max.ru/pp) и Пользовательское соглашение (legal.max.ru/ps). Упоминания KWS или анализа аудио во время звонков - ноль.
При этом в Пользовательском соглашении есть описание того, как голосовые и видеосообщения переводятся в текст.
Когда нейронка считает, что услышала ключевое слово, происходит следующее:
VK видит: в таком-то звонке у такого-то пользователя сработал детектор, уверенность такая-то. Всё привязано к userId как и call_id (vchat.clientStats отправляется в привязке к конкретной VoIP-сессии)
Во время анализа я перехватил реальный ответ сервера VK с конфигурацией KWS. Вот что сервер присылает приложению:
Я забрал модель по этому URL - без авторизации, без cookies. Любой может это сделать прямо сейчас. MD5 совпадает с тем, что прислал сервер.
Ещё я побрутил CDN в поисках других моделей - перебрал 200+ вариантов путей. Нашёл только одну модель в трёх версиях SDK (1-0-1, 1-0-2, 1-0-3) - все три идентичны (одинаковый MD5). На данный момент VK использует только одну модель для фразы «не слышу».
Работает только во время звонков - закрыли приложение, и всё. Кода для фоновой работы KWS я не нашёл.
С голосовыми сообщениями не связан - те переводятся с аудио в текст на серверах VK, это другой процесс, другой код.
Сейчас выключен (use: false). Нейронка уже на устройствах, но не запущена. И ищет она только «не слышу», а не что-то «опасное».
KWS - не единственная интересная вещь в работе звонков MAX:
Все звонки идут через сервер VK. P2P-соединений я не увидел - все медиаданные проходят через TURN-сервер VK. Шифрование DTLS-SRTP есть, но от вас до сервера, не от вас до собеседника. На relay-сервере шифрование заканчивается - ключи у VK.
Флаг записи аудио. В коде есть PMS-ключ calls-sdk-log-audio - если VK его включит, аудио звонка пишется в файл. Управляется с сервера.
Модифицированный WebRTC. VK не использует стандартный WebRTC - они его модифицировали. В модификации добавлены: нативная запись аудио в Opus (nativeAudioStartRecord, nativeAudioWriteFrame), KWS-интеграция, и кастомные параметры.
Ниже - код, конфиги и результаты реверс-инжиниринга. Версия APK 26.12.1 (6679).
KWS встроен в модифицированный WebRTC внутри нативной библиотеки libjingle_peerconnection_so.so. Точка входа - JNI-метод:
Java_org_webrtc_PeerConnectionFactory_nativeSetKeywordSpotterParams
Это вызов из Java в нативный код. Принимает два параметра: isEnabled (включить/выключить) и filePath (путь к модели на устройстве).
Полная цепочка от сервера до нейросети:
Пакет: ru.ok.android.externcalls.sdk.audio - это SDK звонков.
/ru/ok/android/externcalls/sdk/audio/KeywordSpotterManagerImpl.java
Файл: calls_kws.tflite из kws_270525.zip
Результаты запуска модели на тестовых данных:
config.cfg
Вот полная карта: какие серверы участвуют, какие запросы идут и в каком формате.
Шаг 1 - Получение конфига (при подключении к серверу):
"android.wordspotter.config" → {"turn_off_in_ms": 60000}"android.mlfeatures.ws_0" → {"url": "https://st.okcdn.ru/static/calls_android/1-0-1/kws_270525.zi...", "cs": "00320292950aa4896ccc057550442789", "use": false}
Шаг 2 - Скачивание модели (при первом запуске или обновлении URL):
Шаг 3 - Работа KWS (во время звонка, если use: true):
Шаг 4 - Отправка результата (после/во время звонка):
{"metric": "bad_call_detected_by_audio_spotter", "string_value": "не слышу", "double_value": 0.95} + userId, sessionId, call_id
Медиаданные самого звонка:
Файл: defpackage/ConversationKwsStat.java
При срабатывании формируется событие:
Имя "не слышу" зашито в код - но модель определяется URL с сервера. Если VK ее заменит, код по-прежнему будет отправлять "не слышу" как строку, даже если реальная модель будет детектить совершенно другую фразу. Или VK обновит и код в следующей версии.
/ru/ok/android/externcalls/sdk/stat/kws/ConversationKwsStat.java
Проверяйте сами:
curl -O https://st.okcdn.ru/static/calls_android/1-0-1/kws_270525.zip
Содержимое config.cfg:
Я проверил три версии SDK на CDN:
Одна и та же модель во всех версиях. Имя файла kws_270525 предполагает дату создания 27.05.2025. Просканировал 200+ вариантов путей - других моделей на CDN не нашёл.
Цепочка замены модели:
На стороне приложения нет проверки того, что именно модель распознаёт. Нет whitelist допустимых слов. Нет уведомления пользователя. MD5 проверяет только целостность файла - что скачалось без ошибок.
Запись аудио через серверный флаг: PMS-ключ calls-sdk-log-audio (key 129) может включить запись аудио звонка в файл. Плюс JNI-методы nativeStartAecDump / nativeStopAecDump позволяют дампить raw-аудио в файловый дескриптор. Всё управляется сервером.
Все звонки через relay VK: Все медиаданные идут через TURN-сервер VK (155.212.206.115:43210). Шифрование DTLS-SRTP - от вас до сервера, не от вас до собеседника. Сертификат сервера: QRtpServer 1.1.10.
Типы ML-фич в конфиге:
MLFeaturesManagerImpl поддерживает несколько типов моделей. Сейчас WS и NS, но подцепить новый тип - дело пары строк.
/ru/ok/tamtam/android/prefs/PmsKey.java - Коротко и наглядно: вот он, переключатель записи аудио, управляемый с сервера.
/one/video/calls/audio/opus/FileWriter.java - Это нативная запись аудио в файл.
Если вы дочитали до сюда - вы уже поняли суть.
Скажу одно: разница между «детектором плохой связи» и «детектором произвольных слов» - это один URL в JSON-конфиге. Модель, код, процесс отправки на сервер - всё одно и то же. Меняется только файл на CDN.
Я не знаю, планирует ли VK это использовать иначе. Но я знаю, что в политике конфиденциальности об этом ни слова, согласие не спрашивается, а модель можно скачать и проверить прямо сейчас. Ссылка выше.
Код - вот он. Модель - в открытом доступе. Проверяйте.
(ссылка на гитхаб и на готовый продукт - в конце статьи.) OpenAI добавляет водяной знак на все видео из Sora. Типа защита. Только вот оригиналы без водяных знаков лежат на тех же серверах — и забрать их может любой авторизованный пользователь. Я написал расширение, которое это делает в один клик. Рассказываю, как нашёл и почему это вообще работает.
Я не такой частый юзер Sora, но когда увидел, что люди ПРОДАЮТ доступ к видео без водяных знаков — сначала подумал, что они сделали машинное обучение и фотошопом каждый кадр обрабатывают. Но когда я попробовал - результат был готов за 1 секунду. В тот момент я осознал: ЗНАЧИТ ВИДЕО ЛЕЖАТ НА СЕРВЕРАХ. Во мне проснулся инстинкт хакера, и я начал искать ДЫРУ.
Первым делом я полез изучать вкладку Network, пробуя различные методы тыка туда-сюда. Всё что удалось оттуда получить - это подтверждение, что да, оригинал видео без водяных знаков есть на сервере.
Например, при открытии видео мы видим запрос на получение медиафайла: /backend/project_y/post/s_69491ee99e408191addbc9b74004ebca/tree?limit=20&max_depth=1
Который и отдаёт URL для видео с водяным знаком, но в том же ответе присутствуют и другие поля:
"encodings": {
"source": {
"path": "https://videos.openai.com/az/files/00000000-3fc0-7280-a658-0386c8b0d4af/raw?se=2026-01-01T00:00:00Z&sp=r&sv=2024-08-04&sr=b&skoid=cfbc986b-d2bc-4088-8b71-4f962129715b&sktid=a48cca56-e6da-484e-a814-9c849652bcb3&skt=2025-12-27T01:09:39Z&ske=2026-01-03T01:14:39Z&sks=b&skv=2024-08-04&sig=aIewYW3/7HZGVUWwnvYnSUSplfd/WKOpvsaz0yC6wUw=&ac=oaisdsorprsouthcentralus"
},
"source_wm": {
"path": "https://videos.openai.com/az/files/00000000-3fc0-7280-a658-0386c8b0d4af/raw?se=2026-01-01T00:00:00Z&sp=r&sv=2024-08-04&sr=b&skoid=cfbc986b-d2bc-4088-8b71-4f962129715b&sktid=a48cca56-e6da-484e-a814-9c849652bcb3&skt=2025-12-27T01:09:39Z&ske=2026-01-03T01:14:39Z&sks=b&skv=2024-08-04&sig=aIewYW3/7HZGVUWwnvYnSUSplfd/WKOpvsaz0yC6wUw=&ac=oaisdsorprsouthcentralus"
},
"endcard_wm": null,
"thumbnail": {
"path": "https://videos.openai.com/az/files/ec223ed8fe79a36_00000000-3fc0-7280-a658-0386c8b0d4af/drvs/thumbnail/raw?se=2026-01-01T00:00:00Z&sp=r&sv=2024-08-04&sr=b&skoid=cfbc986b-d2bc-4088-8b71-4f962129715b&sktid=a48cca56-e6da-484e-a814-9c849652bcb3&skt=2025-12-27T01:09:39Z&ske=2026-01-03T01:14:39Z&sks=b&skv=2024-08-04&sig=eedTClrNLjxDF7aCW2CbU1TRras38CF+OgmpJ4wAW9U=&ac=oaisdsorprsouthcentralus"
},
"unfurl": {
"path": "https://videos.openai.com/az/files/34780fbcd347644_00000000-3658-7284-9d9f-64947faf826b/drvs/link_thumbnail/raw?se=2026-01-01T00:00:00Z&sp=r&sv=2024-08-04&sr=b&skoid=cfbc986b-d2bc-4088-8b71-4f962129715b&sktid=a48cca56-e6da-484e-a814-9c849652bcb3&skt=2025-12-27T01:10:43Z&ske=2026-01-03T01:15:43Z&sks=b&skv=2024-08-04&sig=3dx5D0VSb8YcK6Pf1VOuoM3s7+BaeGuUTqTnh9nAHQ4=&ac=oaisdsorprcentralus"
},
"md": {
"path": "https://videos.openai.com/az/files/2fccbbc30a166d1_00000000-3fc0-7280-a658-0386c8b0d4af/drvs/md/raw?se=2026-01-01T00:00:00Z&sp=r&sv=2024-08-04&sr=b&skoid=cfbc986b-d2bc-4088-8b71-4f962129715b&sktid=a48cca56-e6da-484e-a814-9c849652bcb3&skt=2025-12-27T01:09:39Z&ske=2026-01-03T01:14:39Z&sks=b&skv=2024-08-04&sig=KfpzXrJ15F3/iti4/NMrWGyQVbKQNmD3LCa0RuAU1Gk=&ac=oaisdsorprsouthcentralus"
},
"gif": {
"path": "https://videos.openai.com/az/files/ec3c5d130062958_00000000-3fc0-7280-a658-0386c8b0d4af/drvs/gif/raw?se=2026-01-01T00:00:00Z&sp=r&sv=2024-08-04&sr=b&skoid=cfbc986b-d2bc-4088-8b71-4f962129715b&sktid=a48cca56-e6da-484e-a814-9c849652bcb3&skt=2025-12-27T01:09:39Z&ske=2026-01-03T01:14:39Z&sks=b&skv=2024-08-04&sig=ZLmnXWBXyptAIBFFjb8oSDZmkJgMIUWH9BUspSk3kvs=&ac=oaisdsorprsouthcentralus"
}
},
Нас интересует поля source и source_wm, URL на видео без водяного знака и с водяным знаком соответственно. Проблема в том, что OpenAI в оба поля вставляют одинаковый URL. Но само наличие поля source указывает на то, что изначально оригинал там был - а значит, вероятно, до сих пор где-то есть.
Первым делом я подумал - ха, так можно просто попробовать подобрать URL, вставляя нужный gen_id/task_id/post_id. OpenAI использует целых три идентификатора для обозначения видео. Но ничего из этого не получилось, потому что в каждом URL присутствует параметр подписи (signature), который генерируется на стороне сервера и который невозможно брутфорсить.
Значит проблема где-то в другом месте...
Спустя пару часов безуспешных попыток я вдруг вспоминаю: стоп, у них же был другой сайт! Не зря этот называется Sora2 - на первом я что-то не припомню водяных знаков, а аккаунт тот же!
И вот я иду на старый сайт Sora, открываю старое видео - запрос уже другой: /backend/generations/gen_01kbk5p1s5f9pb63q81sx9atax
В нем используется gen_id вместо post_id. Я подумал - это оно! Но когда я получил gen_id для видео из новой Sora и попытался выполнить запрос с ним - получил ошибку. Хм, значит снова мимо, едем дальше.
Дальше я начал смотреть - а что еще всплывает во вкладке network?
Я при открытии сайта я увидел странный запрос: /backend/notif?limit=10
Который возвращает всю нашу библиотеку (и Sora1, и Sora2) во всех возможных качествах, включая тот самый source, который Sora2 не хотела отдавать:
"encodings": {"source": {"path": "https://videos.openai.com/az/files/00000000-e9ac-7283-a06d-7e9688f1756f/raw?se=2026-01-01T00:00:00Z&sp=r&sv=2024-08-04&sr=b&skoid=cfbc986b-d2bc-4088-8b71-4f962129715b&sktid=a48cca56-e6da-484e-a814-9c849652bcb3&skt=2025-12-27T01:10:01Z&ske=2026-01-03T01:15:01Z&sks=b&skv=2024-08-04&sig=25ruxWAU/CxniQTJBn6Cvq8GD0sUzkx5MUi2QJouPSc=&ac=oaisdsorprwestus2","size": 5938502,"width": 1280,"height": 704,"duration_secs": 10.1,"ssim": 0.9935621,"az_path": null,"codec": "h264" },"source_c2pa": null,"md": {...},"ld": null,"thumbnail": {...},"link_thumbnail": {...},"spritesheet": {...},"gif": {...},"mp3": {...},"source_wm": {"path": "https://videos.openai.com/az/files/00000000-c904-7284-9ac9-cb42fe88ec64/raw?se=2026-01-01T00:00:00Z&sp=r&sv=2024-08-04&sr=b&skoid=cfbc986b-d2bc-4088-8b71-4f962129715b&sktid=a48cca56-e6da-484e-a814-9c849652bcb3&skt=2025-12-27T01:10:43Z&ske=2026-01-03T01:15:43Z&sks=b&skv=2024-08-04&sig=l8CTaphMR1i/JxTvjR+hvhAt0Oi6RoHoRXnLjJRTs04=&ac=oaisdsorprcentralus","size": 5929815,...},
То есть endpoint для уведомлений отдаёт то, что основной API прячет. Классика.
Открываем видео по ссылке - да, это оно. Видео, сгенерированное на новом сайте Sora2 без водяного знака!
Chrome Web Store: https://chromewebstore.google.com/detail/sora-remove-watermark-ope/femmbfkejicbihcplfopaiokeagdkjgg