В хакерских кругах довольно широко известен баг, называемый memory-leak. На самом деле это название может означать два типа багов:
1. исчерпание свободной памяти
2. возможность читать фрагменты памяти приложения (или ядра ) В нашем случае, под memory-leak я подразумеваю второй пункт, но в роли приложения выступает сервер.
Начнём с азов программирования. Данные в пакетах передаются в бинарном виде (в том смысле, что не в ascii), поэтому для работы с ними используются сишные функции для работы с блоками памяти:
memcpy, memset, memcmp и memmove
Дело в том, что размеры некоторых пакетов в RFO могут варьироваться (только их мало, кстати). Этот момент и вопрос оптимизации привёл к тому, что разработчики Rising Force Online не проверяют длину пакетов при их обработке. Этот момент на самом деле потенциально опасен и может привести к утечкам памяти и нестабильности работы сервера.
Утверждение об отсутствие проверки длины мы можем легко проверить. Пошлём пакет на передвижение персонажа (как надо, в закодированном виде), но обрежем на, пускай, 4 байта. И сервер нас не отключит, напротив - мы получим ответ, что невозможно двинуться в это место. Это свидетельствует о том, что координата Z, которая идёт в пакете последней и которую мы обрезали, всё равно приняла какое-то значение. А значние это - кусочек памяти RFO-сервера, который находиться за пределами вашего пакета! Рассмотрим пример. Приблизительно так в RFO-сервере может быть реализована обработка пакета на передвижение:
Код:
void ParseCharacterMovePacket(int characterID, char *packet) {
short int x = 0, y = 0, z = 0;
memcpy(&x, packet, 2);
memcpy(&y, packet + 2, 2);
memcpy(&z, packet + 4, 2);
ProcessMoving(characterID, x, y, z);
return;
}
Разумеется, в RFO механизм реализован по-другому, но в этом коде наглядно показано, как из пакета (кстати, в данном случае предполагается, что он попадает в функцию расшифрованным) берутся координаты. А теперь представим, что длина пакета 4 байта, а не 6! В таких условия в переменную "z" попадут два байта, которые находятся после x и y координат в пакете (выделенная функция). Что именно там может оказаться предугадать крайне сложно, с учётом отсутствия сорсов RFO-сервера.
Если же в RFO-сервере где-то вместо memcpy используется memmove, то это однозначно приведёт к падению сервера.
А теперь поговорим о том, как мы можем это использовать. Как вы думаете, как сервер обрабатывает пакет на приват? Очень просто! Для примера мы шлём такой пакет на приват:
Цитата:
0x00 | 20 00 02 03 61 61 61 00 00 00 00 00 00 00 00 00
0x10 | 00 00 00 00 00 09 3a 20 62 00 00 00 00 00 00 00
Синим помечен ник получателя
Зелёным отправляемое сообщение (": b")
Красным - длина сообщения с учётом длины своего ника (ника отправителя)
Остальные данные в контексте данной статьи нас не интересуют
Сервер берёт из него ник адресата, определяет его ID, тупо копирует сообщение в новый пакет и шлёт адресату:
Цитата:
0x00 | 21 00 02 0a 02 FF FF FF FF 62 62 62 00 00 00 00
0x10 | 00 00 00 00 00 00 00 00 00 00 00 00 03 3a 20 62
0x20 | 00
Зелёным отмечено ID автора сообщения (специально изменено на FF..)
Синим - ник отправителя
Красным - длина сообщения (обратите внимание, уже без учёта длины ника)
Коричневым - сообщение
А теперь подумаем, каким же образом он копирует сообщение? Обратите внимание на байт, помеченный красным. Это длина сообщения, которое шлёт клиент. Причём, очень важно отметить, что это целиковая длина - с учётом ника, пробела, двоеточия и сообщения. О чём это говорит? А о том, что сервер берёт это значение, выделяет блок памяти равный длине этого сообщения +1. И копирует туда сообщение через strncpy, memcpy или свою собственную функцию.
А что будет, если мы обрежем этот пакет (уберём из него сообщение), но поставим какую-нибудь большую длину? Правильно, в следствие отсутствия проверок длины, сервер выделит нужный кусок памяти, но скопированы в него будут те данные, которые идут в памяти RFO-сервера после пакета. Ну и отправит его нам Вот пример такого пакета:
Цитата:
0x00 | 16 00 02 03 61 61 61 00 00 00 00 00 00 00 00 00
0x10 | 00 00 00 00 00 FF
Исходя из того, что смещение до данных в пакетах данного типа всегда постоянное, сообщение будет копироваться как-нибудь так:
Цитата:
char *message = (char *)malloc(packet[21] + 1); // 21 - смещение до длины сообщения
memcpy(message, packet + 22, packet[21]); // копируем несуществующее сообщение из пакета
А на месте packet + 22 сообщения нет. Поэтому будут скопированы те данные, которые есть =)
Таким образом мы можем просматривать рандомные кусочки памяти RFO-сервера. В них может быть всё - начиная от Логинов и паролей к чужим аккаунтам, заканчивая приватами (причём вне зависимости от расы ). Вот скриншоты того, как я отлавливал чужие приваты (только зарегистрированные на форуме пользователи могут видеть скриншоты):
Я не приветствую воровство аккаунтов, поэтому развивать тему 4-го скриншота не буду.
Для этих же целей можно использовать любые другие чаты, просто приват можно использовать наиболее беспалевно.
И последнее, развивая тему memory-leak, хочу вернуться к серверному пакету на получение приватного сообщения (02 0a 02). В процессе изучения протокола RFO я стал замечать, что иногда пакет содержит непонятный мусор. Вот пример:
Цитата:
0x00 | 24 00 02 0a 02 FF FF FF FF 61 61 61 61 61 61 61
0x10 | 61 61 00 00 d4 f3 0e 41 01 00 01 00 06 3a 20 62
0x20 | 62 62 62 00
Всё, что в данном пакете отмечено цветом - место под ник. Но реальный ник персонажа короткий (9 байт) и отмечен синим цветом. А что же такое помечено красным? В идеале, это место полностью должно быть забито нулями. Откуда же там беруться посторонние данные? Тут я вижу два варианта:
1. память под пакет выделяется, но не очищается
2. копирование ника в пакет происходит через функцию для работы с блоками памяти. Например:
Цитата:
Код:
char nick[] = "mynickname";
memcpy(packet, nick, 18);
Длина ника всего-лишь 10 байт, а копируем в пакет все 18.
Так или иначе опять в пакет попадают данные из памяти RFO-сервера со всеми вытекающими последствиями...
ЗЫ. не сочтите за рекламу ( )) ), но всё вышеописанное было реализовано через бота - он слал описанный пакет, я через клиент на другом персонаже получал приваты.