Как-то раз в одном из телеграм-чатов ребята закинули игру, в которой можно посоревноваться друг с другом.
Играли в Paint Io в боте
@gamee
- это аркада, где нужно закрашивать территорию, оставляя за собой след. Замыкаешь контур — зона твоя. Перережешь чужой след — выбиваешь соперника. Цель — захватить максимум поля.
В чате все люди серьезные, поэтому борьба за первенство началась сразу.

Осознав, что честным трудом выйти в топ-1 лидерборда очень сложно, я решил поисследовать игру. Основной мотивацией было найти какие-нибудь недочеты, которые помогли бы мне победить во что бы то ни стало.
Начало исследования
Так как Telegram Mini App это в первую очередь веб-приложение, высока вероятность что она запустится и с браузера на компьютере.
Приложение немного сопротивлялось, требуя запуск на мобильном устройстве, но эмулировать запуск со смартфона не сложно (см ролик с примерами).

Сыграв короткую тестовую игру с включенными инструментами разработчика (DevTools), на сервер ушел http запрос, в котором содержались такие поля как "id": "game.saveWebGameplay"
и score: 12
Несложно догадаться что это та самая команда, которая отправляет результаты игры на сервер, где 12 - количество набранных очков.

Кажется, что дело за малым, нужно подменить значение в запросе и отправить его снова.
Пробуем!

Вставляем скопированный текст в блокнот, и меняем значение в поле score на 50000.

К сожалению, я не делал скриншот этого этапа, придется поверить на слово, что сервер вернул ошибку checksum incorrect

Ищем загадочное checksum
Для этого открываю DevTools - Sources - Find All, фильтрую контент по слову checksum и вот оно, обнаружена функция, отвечающая за его генерацию.

Обратите внимание на зеленые комментарии, их оставили разработчики игры прямо в скриптах. В переводе с английского они пишут "Намеренно оставляем соль md5 здесь, а не в env (переменные окружения), потому что найти ее здесь будет сложнее"
Ну да, поиск по названию операция не из простых.
И так, мы видимо что наш checksum это md5-хэш с солью, которая прямо захардкожена здесь.
export const getChecksum = (score, playTime, url, gameStateData) =>
md5(`${score}:${playTime}:${url}:${gameStateData}:crmjbjm3lczhlgnek9uaxz2l9svlfjw14npauhen`);
Судя по всему, он хэшируется из полей score, playTime, url, gameStateData.
К счастью, абсолютно все эти поля передаются в нашем запросе, осталось подставить новые значения и захэшировать новый checksum.

Обновляем в запросе значение checksum и попробуем снова.

Ура! Запрос обработан и 50 тысяч очков сохраняется в профиле игрока.
Забираем первое место
Осталось нарисовать красивое число в лидерборде. У парня на первом месте было 39 258 очков, решил обогнать его ровно на 100 тысяч.

Считаю, что первое место в рейтинге чата получено абсолютно заслужено.
Безусловно, существует множество способов проделать аналогичное.
Так, например, в своем телеграм-канале «Деплой в пятницу» я разобрал еще один способ выхода в топ-1 — но уже для другой игры. Туда попадает всё, что не тянет на полноценную статью, и иногда даже что-то полезное.