BSML — это современный, полностью локальный загрузчик модов для Brawl Stars (и других совместимых игр Supercell) на платформе Android. Проект позволяет динамически накладывать, комбинировать и удалять моды «на лету» без необходимости модификации, декомпиляции или переподписи оригинального APK-файла игры и без использования прав Root.
Архитектура BSML состоит из нескольких взаимосвязанных компонентов, работающих на сетевом и прикладном уровнях Android:
sequenceDiagram
participant CDN as Локальный прокси-сервер
actor Game as Игра (Клиент)
participant Proxy as BSML Прокси
participant Server as Сервер игры
Game->>Proxy: 1. CLIENT_HELLO (Оригинальный хеш ассетов)
Note over Proxy: Меняем хеш на кастомный BSML-Patched
Proxy->>Server: 2. CLIENT_HELLO (Измененный хеш)
Server->>Proxy: 3. LOGIN_FAILED (Сжатый список файлов и CDN URL)
Note over Proxy: 1. Прописываем хеши модов в список файлов<br/>2. Меняем CDN URL на http://127.0.0.1
Proxy->>Game: 4. LOGIN_FAILED (Измененный список и Локальный URL)
Game->>CDN: 5. HTTP GET (Запрос файлов ресурсов)
CDN->>Game: 6. Возврат пропатченных модами файлов
- BSML запускает локальный Android-сервис
LocalVpnService. - Он настраивает виртуальный сетевой интерфейс (TUN) с IP-адресом
10.10.10.2и перехватывает исходящие TCP-пакеты, адресованные на порт игрового сервера (9339).
Взаимодействие между игрой и сервером перехватывается классом TcpProxySession. Прокси-сервер осуществляет глубокий разбор (Deep Packet Inspection) и модификацию пакетов протокола Supercell на лету.
Отправляется клиентом игры при инициализации сетевого соединения. Пакет имеет следующую структуру:
protocolVersion(Int32, смещение 0) — версия сетевого протокола.keyVersion(Int32, смещение 4) — версия публичного ключа шифрования.major(Int32, смещение 8) — мажорная версия игры (например,55).revision(Int32, смещение 12) — ревизия сборки.build(Int32, смещение 16) — номер сборки.contentHash(String, смещение 20) — 40-символьный SHA-1 хеш-отпечаток текущей версии оригинальных ассетов игры (префиксируется 4-байтным Big-Endian размером строки;-1указывает наnull).
Логика модификации:
Если в приложении активированы моды, TcpProxySession перехватывает CLIENT_HELLO и подменяет contentHash на кастомную строку с префиксом PATCH_NAMESPACE (константа BSML-Patched- с суффиксом сигнатуры состояния модов). Игровой сервер сверяет этот хеш, понимает, что у клиента «устаревшие» файлы, и инициирует процедуру обновления ассетов.
Отправляется игровым сервером в ответ на «устаревший» contentHash. Пакет разбит на две логические части:
А. Префикс пакета (LoginFailedPrefix):
reason(Int32) — код ошибки (код7означаетCLIENT_CONTENT_UPDATE— требование обновления файлов, код1— общая ошибка входа).fingerprint(String?) — plain-текст fingerprint JSON (в старых версиях игры).unknownString(String?) — зарезервировано.contentDownloadUrl(String?) — базовый URL CDN, откуда игра должна скачать ресурсы (например,https://event-assets.brawlstars.com).updateUrl(String?) — ссылка на страницу обновления игры в Google Play.reasonText(String?) — текст ошибки, отображаемый пользователю.maintenanceWaitSecs(Int32) — время ожидания при тех. работах.suffix(ByteArray) — хвост пакета, содержащий сжатый фингерпринт и резервные адреса.
Б. Суффикс пакета (LoginFailedTail):
unknownBoolean(Boolean / 1 байт) — служебный флаг.compressedFingerprint(ByteArray?) — фингерпринт-JSON, сжатый с помощью zlib/gzip (префиксирован 4-байтным Big-Endian размером сжатого буфера). Распакованный буфер имеет 4-байтный Little-Endian заголовок исходного размера, после которого идет сам поток zlib.contentDownloadUrls(List) — массив резервных CDN URL-адресов для скачивания файлов, начинающийся с 4-байтного Int32-счетчика количества строк.rawSuffix(ByteArray) — оставшиеся байты пакета.
Логика модификации:
При получении LOGIN_FAILED от игрового сервера, LoginFailedRewriter выполняет следующие шаги:
- Декомпрессия (
inflate): ИзвлекаетcompressedFingerprintиз суффикса, определяет алгоритм сжатия (zlib с Little-Endian префиксом длины, чистый zlib, gzip или deflate) и распаковывает его в исходный JSON-фингерпринт. - Патчинг JSON:
- Подменяет корневой параметр
"sha"фингерпринта на хеш, соответствующий перехваченномуcontentHashизCLIENT_HELLO. - Обходит массив
"files"и для каждого файла, измененного модом (полученного изModFilesRepository), подменяет оригинальный"sha"на SHA-1 патченного файла, а также записывает его реальный размер. - Добавляет в список файлов фиктивный триггер-файл (
PATCH_NAMESPACEсо случайным SHA-1 хешем), гарантируя, что игра обнаружит изменения и запустит загрузку.
- Подменяет корневой параметр
- Компрессия (
deflate): Упаковывает модифицированный JSON обратно в соответствующий формат сжатия с вычислением новых размеров. - Редирект CDN: Переписывает поле
contentDownloadUrlв префиксе и весь списокcontentDownloadUrlsв суффиксе, заменяя оригинальные CDN-адреса на адрес локального прокси-сервера:http://127.0.0.1:<localAssetPort>. - Сборка пакета: Кодирует обновленные структуры префикса и суффикса обратно в бинарный поток, формирует стандартный заголовок пакета Supercell (ID сообщения, длина тела, версия) и отправляет его игровому клиенту.
- BSML запускает собственный фоновый веб-сервер
LocalAssetProxyServerна локальном хосте. - Перехваченные HTTP-запросы от игры на скачивание ресурсов перенаправляются на этот локальный прокси.
- Прокси-сервер анализирует, какие файлы запрашивает игра:
- Если файл модифицирован установленным модом, прокси отдает предварительно скомпилированный патч из рабочей директории.
- Если файл не изменен модом,
OriginalAssetProviderлениво отдает оригинальный файл (из кэша, ресурсов игры или скачивает его из официального CDN, если его нет локально).
- Моды поставляются в виде архивов с расширением
.NullsBrawlAssetsили обычных.zip. - Класс
CsvPatchApplierвыполняет точечное слияние изменений из мода с оригинальными CSV-таблицами игры. - Полученные CSV-файлы сжимаются с помощью оригинального алгоритма разжатия
daniillnull.tools.LZMAна базе SDK SevenZip. Это гарантирует, что игра получит структуру данных в точности того формата (с 5-байтовым заголовком свойств и 4-байтовым заголовком размера), который она ожидает.
- При деактивации мода запускается «режим очистки» (
cleanupMode). - BSML принудительно отправляет оригинальный
contentHashигры в пакетеCLIENT_HELLO. - Игра запрашивает оригинальные ассеты, локальный прокси возвращает оригинальные файлы, и кэш игры полностью восстанавливается до ванильного состояния.
- Сразу после этого VPN-интерфейс автоматически отключается (
autoVpnDisable), снижая нагрузку на процессор и батарею.
- Минимальная версия Android: 7.0 (API 24+)
- Стек технологий: Kotlin (Jetpack Compose для UI), Java, Coroutines, Flow.
- Основные библиотеки:
org.b1.pack:lzma-sdk-4j— декодирование и кодирование LZMA-потоков SevenZip.androidx.compose— современный реактивный интерфейс.
