Тема: Актуально Обход шифрования пакетов
Показать сообщение отдельно
Старый 18.11.2024, 17:30   #1
Diantro
Проф. продавец
Аватар для Diantro
OFFLINE
Регистрация: 29.10.2023
Сообщений: 1,850
Благодарностей:
269 всего
Мнения: + 460
Репутация: 808

Обход шифрования пакетов


Интро

Продолжаем предыдущую тему: Таб-таргет для любых серверов. Мы уже нашли окружающие персонажа объекты, объекты с умениями и инвентарем, функцию выбора объекта в таргет, и функцию использования скиллов и предметов. Теперь перейдем к следующему этапу — обходу шифрования пакетов на примере сервера cerberus-games. Итоговая тулза будет работать и на других серверах.

На гифке мы перехватываем исходящие пакеты, отправляем сообщение в чат, бафаемся через пакеты, а затем отслеживаем входящие пакеты.



Содержание
  1. Зашифрованный и незашифрованный пакет
  2. Поиск функции шифрования исходящих пакетов
  3. Хукаем функцию шифрования пакетов
  4. Работа с входящими пакетами (спойлер: они не шифруются)
  5. Итоговая тулза

Зашифрованный и незашифрованный пакет

Сначала перехватим незашифрованный пакет, чтобы понять, как он устроен.
  • Запускаем клиент RF Online на сервере без шифрования (я экспериментирую на RF Altruism, также можно запустить свой сервер локально).
  • Запускаем x64dbg, аттачимся и ставим брейкпоинт на функцию send из ws2_32.dll
    Example
  • Пишем в чат "123" — срабатывает брейкпоинт. Функция send принимает в аргументах буфер, который нужно отправить. Находим его — там лежит искомый пакет.
    Example
  • То же самое мы увидим, если перехватим этот пакет в Wireshark. Буфер, который передается в send, соответствует данным в пакете стека протоколов TCP/IP.
    Example

Имеем такой пакет: 0f 00 09 00 02 02 03 31 32 33 00 04 00 00 00
  • 0f - длина пакета
  • 02 02 - тип пакета
  • 03 31 32 33 00 04 00 00 00 - данные. В данном случае 03 — длина сообщения, 31 32 33 — "123".

Забегая вперед, нам будут важны только тип и данные. То, что идет в начале, будет рассчитываться функциями игры самостоятельно.

Теперь проделаем то же самое на Церберусе, где шифрование есть. Ставим брейкпоинт на send и пишем "123" в чат. Видим непонятный набор данных, не похожий на пакет, который мы перехватили выше.
Example

Поиск функции шифрования исходящих пакетов
  • Ставим снова брейкпоинт на send и триггерим его, отправив сообщение в чат. Делаем это несколько раз и следим, как меняется адрес буффера.
    Example
  • Замечаем, что адреса всегда похожи, и иногда новый буфер может перетирать старый. Переходим к одному из этих адресов в памяти, убираем брейкпоинт и начинаем спамить сообщения в чат. В результате видим, что буфер при отправке сообщения всегда лежит в какой-то области из фиксированного куска памяти. На гифке значения в памяти меняются только когда я пишу сообщения в чат.
    Example
  • Ставим брейкпоинт на запись на какой-то из адресов (или на несколько), по которым может произойти запись, и начинаем спамить в чат, пока он не сработает. В итоге мы окажемся внутри RFOClient.vmp.dll
    Example
  • RFOClient.vmp.dll - это DLL с кастомной логикой клиента Церберуса. В оригинальном rf_online.bin хукаются необходимые функции и выполнение перенаправляется в данную DLL. Т.к. при каждом старте она инжектится заново, адреса в ней после рестарта клиента съезжают.
  • Начинаем выполнять возврат из функции, пока не окажемся в rf_online.bin (не забудьте перед этим деактивировать поставленные брейкпоинты, чтобы они снова не сработали). В итоге находим место, где была изменена дефолтная логика.
    Example
  • Ставим брейкпоинт на push eax и проверяем, что лежит по этому адресу. Находим что-то похожее на незашифрованный пакет: кажется, что данные зашифрованы, а тип — нет.
    Example
  • Возобновляем выполнение программы и следим за памятью. Новые пакеты добавляются после предыдущих.
    Example
  • Ставим брейкпоинт на запись где-нибудь после текущего пакета, чтобы определить, где подготавливаются данные (важно при этом попасть именно в зашифрованные данные, а не в тип пакета). После срабатывания опять окажемся в RFOClient.vmp.dll.
    Example
  • Выполняем возврат из функции, пока не окажемся в rf_online.bin. В итоге попадаем в функцию LoadSendMsg (название подсказал Cutter).
    Example
  • Делаем возврат из функции и снова оказываемся в RFOClient.vmp.dll
    Example
  • Снова делаем возврат пока не окажемся в rf_online.bin и наконец находим искомую функцию — SendNetMessage. На скриншоте справа подписаны аргументы, которые передаются в обертку над LoadSendMsg из RFOClient.vmp.dll.
    Example
  • Ставим брейкпоинт на начало функции и убеждаемся, что в функцию передаются незашифрованный тип и данные для пакета.
    Example

В итоге мы нашли функцию SendNetMessage, с помощью которой можно отправлять свои пакеты. Функция имеет такую сигнатуру:
Код на C++

Важное замечание: эта функция используется почти для всех действий в игре, таких как передвижение, использование умений и предметов, передачи предметов, отправки сообщений в чат и др. Раз в несколько секунд клиент также шлет пакет на синхронизацию, и он отправляется через другую функцию. Ее также можно найти, повторив шаги выше, если вам нужно.

Хукаем функцию шифрования пакетов

Хукать функцию нужно для того, чтобы логировать исходящие пакеты и понимать, какие пакеты может отправить клиент. При вызове SendNetMessage в стеке будут лежать аргументы — тип пакета, данные и их длина. Нам нужно перенаправить исполнение программы в наш заинжекченный код с помощью команды jmp, вывести значения аргументов в консоль или интерфейс программы и вернуть исполнение обратно в функцию.

Будем хукаться в этом месте, так как команда jmp имеет длину 5 байт.
Код:
0052EB91 | 8BEC      | mov ebp,esp                                                 
0052EB93 | 83EC 18  | sub esp,18
Example

Для того, чтобы хукнуться я использую такой класс
Код на C++

Первый аргумент в конструкторе - адрес для хука, второй - адрес функции, в которую нужно перенаправить исполнение, третий - количество заменяемых байтов. То есть в нашем случае его нужно вызвать так:
Hook sendHook{ (void*)0x0052EB91, reinterpret_cast<void*>(ourCustomFunction), 5 };
В деструкторе возвращается исходный код, чтобы при закрытии нашей программы игра работала как прежде.

Сама функция, в которую будет перенаправляться выполнение, может выглядеть так:
Код на C++

В этой функции мы сохраняем указатель на this, значения регистров и аргументы в переменные. Затем выполняем с этими данными необходимые действия (printSendBufferToLog()). После этого восстанавливаем значения регистров, выполняем команды, перезаписанные jmp (mov ebp, esp и sub esp, 0x18), и возвращаем исполнение в исходную функцию.

В итоге, после хука пролог функции будет выглядеть так (с добавленным нашим jmp).
Example

Более подробно про перенаправление можно почитать здесь: Code caves и Using code caves

Работа с входящими пакетами

Тут все проще, так как входящие пакеты на Церберусе не шифруются. Чтобы убедиться в этом, можно поставить брейкпоинт на recv из ws2_32.dll и нажать execute till return, чтобы в буфер записался контент из сокета.
Example

Единственная проблема здесь — непонятно, где начинается и заканчивается пакет. Начало мне удалось найти: если поставить access-брейкпоинт на буфер и проанализировать, где происходит чтение из него, то можно найти функцию AnalysisZoneServerPacket.
Example

В нее передаются два аргумента — тип пакета (в данном случае 4 байта) и контент, но нет длины. В итоге в хуке я просто логирую тип и 64 байта из контента.

Итоговая тулза

Сделано на основе такой же тулзы для Теры: https://github.com/Erarnitox/TeraProxy

Поддерживает
  • Логирование исходящих и входящих пакетов
  • Отправку пакетов
  • Отправку последовательности пакетов, предварительно загруженных из файла
  • Выгрузгу логов

Работает на всех популярных серверах (независимо от того, есть ли шифрование или нет).

В архиве два файла: RF Proxy.exe — инжектор (методом Manual Mapping) и rfproxy.dll. Вы можете использовать любой другой инжектор, например, встроенный в Cheat Engine.

Ссылка на скачивание в этом сообщении:
https://www.rf-cheats.ru/forum/showp...5&postcount=28

Последний раз редактировалось Diantro; 18.11.2025 в 23:47.
 
Ответить с цитированием
Сказали спасибо:
mcmanrizky (27.09.2025), kontol (26.07.2025), rule-z (28.05.2025), cold (09.04.2025), DungeonKeeper (19.01.2025), Werizander (19.11.2024), dark (18.11.2024), NanoTech (18.11.2024), Sitanom (18.11.2024), Jlo6aH (18.11.2024)