[UPPERCASE]Achtung[/UPPERCASE] Написано дебилом для дебилов, но если что снизу можно задать вопрос [UPPERCASE]Achtung[/UPPERCASE]
Введение
В данном уроке будет разбираться работа с RakNet хуками через CLEO с помощью SAMPFUNCS.
RakNet является сетевым движком SAMP'а. Все данные, которые взаимодействуют в сети, хранятся в BitStream (битстрим) - структура, которая хранит данные как последовательность битов.
В SAMP имеется 2 вида для обмена информацией между клиентом и сервером - Packet и RPC.
> Packet
Packet (далее - пакет) - сообщение от сервера/клиента, в BitStream'е которого хранится сначала ид, а далее идут различные данные. Есть и исключения - некоторые пакеты не имеют данных, они используются как оповещение (к примеру, при разрыве соединения от сервера).
В данном подразделе мы разберем способы их чтения и отправки.
Перехват
Перехват(с англ. "hook" (хук)) - операция при которой основной адрес функции меняется на собственный => возможность чтения/подмены данных.
Пакет может быть двух видов: входящий, исходящий.
Входящие - отправляемые от сервера (сервер-клиент), исходящие - отправляемые серверу (клиент-сервер).
Алгоритм чтения:
- Ищем необходимый ID (с помощью опкода opcodes:0be5);
- Получаем BitStream (с помощью того же опкода opcodes:0be5);
- Читаем BitStream (первый байт (8 битов) можно опустить опкодом opcodes:0beb, т.к. он является идом пакета);
- Завершаем работу с текущими данными используя опкод opcodes:0be0.
Перехват входящих
Для перехвата входящих пакетов используется опкод opcodes:0be4.
Благодаря данному перехвату мы сможем отредактировать/прочитать данные, которые присылается к нам.
Данный код будет выводить активный трейлер (который закреплен за автомобилем игрока) как маркер на карту:
CLEO:
{$CLEO}
{$INCLUDE SF}
0001: wait 0 ms
while not SAMP.Available()
wait 400
end
7@ = 0 // Обнуляем переменную(нет необходимости, сделал для наглядности)
0BE4: raknet setup_incoming_packet_hook @in_packet // Установка хука
while true
wait 0
// Переменные, используемые в хуке взаимосвязаны, поэтому если в теле хука была изменена переменная, то и в данной области она тоже будет изменена. Имейте это в виду.
if 7@ <> 0 // Если маркер был создан ранее
then
if not 0 = SAMP.GetActorHandleByPlayerID(2@) // Если игрок, приславший нам последний пакет о синхронизации трейлера, неактивен, то удаляем маркер с карты
then
0164: disable_marker 7@ // Уничтожаем маркер
7@ = 0 // Обнуляем переменную
end
end
end
:in_packet
0BE5: raknet 0@ = get_hook_param PARAM_PACKETID // Получаем ИД пакета
if 0@ == PACKET_TRAILER_SYNC // Сравниваем
then
0BE5: raknet 1@ = get_hook_param PARAM_BITSTREAM // Получаем битстрим
0BEB: raknet bit_stream 1@ ignore_bits 8 // packetId // 1 байт - ИД пакета - пропускаем
0BE7: raknet 2@ = bit_stream_read 1@ type BS_TYPE_SHORT // playerId // Читаем ИД игрока, приславшего нам пакет
0BE7: raknet 3@ = bit_stream_read 1@ type BS_TYPE_SHORT // trailerId // ИД трейлера, который он везет
0BE7: raknet 4@ = bit_stream_read 1@ type BS_TYPE_FLOAT // pos[0] // Позиция трейлера по оси X
0BE7: raknet 5@ = bit_stream_read 1@ type BS_TYPE_FLOAT // pos[1] // ... по оси Y
0BE7: raknet 6@ = bit_stream_read 1@ type BS_TYPE_FLOAT // pos[2] // ... по оси Z
// Также можно прочитать данные дальше, но нам они не нужны :)
if 7@ <> 0 // Если маркер был создан ранее
then
0164: disable_marker 7@ // то удаляем его, чтобы карта не заполонилась значками
7@ = 0 // Обнуляем(нет необходимости здесь его обнулять, но всё ж)
end
02A8: 7@ = create_marker 51 at 4@ 5@ 6@ // Создаем
end
0BE0: raknet hook_ret true // Принимаем пакет. Если поставить значение false, то данные от сервера не будут приняты.
Перехват исходящих
Для перехвата исходящих пакетов используется опкод opcodes:0be2.
Благодаря данному перехвату мы сможем отредактировать/прочитать данные, которые отправляются от нас.
В данном коде будет происходить перезапись исходящих от нас данных, а именно - синхронизация пешком. Для всех мы будем находиться глубоко под землей:
CLEO:
{$CLEO}
{$INCLUDE SF}
0001: wait 0 ms
while not SAMP.Available()
wait 400
end
0BE2: raknet setup_outcoming_packet_hook @out_packet // Подготавливаем хук
0BDE: pause_thread 0 // Бесконечный цикл нам не нужен, поэтому просто поставим скрипт на "паузу". Хуки, вызовы команды и т.п., что не требует постоянной обработки, будут работать в это время.
:out_packet
0BE5: raknet 0@ = get_hook_param PARAM_PACKETID // Получаем ИД пакета
if 0@ == PACKET_PLAYER_SYNC // Сравниваем
then
0BE5: raknet 1@ = get_hook_param PARAM_BITSTREAM // Получаем битстрим
0BF3: raknet 2@ = bit_stream 1@ get_data_ptr // Получаем указатель на содержимое битстрима
0C0D: struct 2@ offset 15 size 4 = -90.0 // в данные битстрима установим значение -90.0. Это будет координата Z в синхронизации.
// Ещё один из типов перезаписи
/*
0AC8: alloc 2@ 69 // sizeof(onFootData) + packetId // выделяем память для хранения данных битстрима
0BE8: raknet bit_stream 1@ read_array 2@ size 69 // записываем в выделенную память(первый байт - ид пакета, всё остальное - синхронизация игрока(68 байт))
0C0D: struct 2@ offset 15 size 4 = -90.0 // в данные битстрима установим значение -90.0. Это будет координата Z в синхронизации.
0BEA: raknet bit_stream 1@ reset_write_pointer // сбрасываем указатель записи в битстриме
0B40: raknet bit_stream 1@ write 2@ type BS_TYPE_ARRAY size 69 // записываем новые данные
0AC9: free 2@ // освобождаем, т.к. выделенная память больше не нужна, все данные уже хранятся в перезаписанном битстриме
*/
end
0BE0: raknet hook_ret true // отправляем пакет, false - не отправлять
Эмуляция
Для того, чтобы сэмулировать входящие пакеты, применяется опкод opcodes:0bf7.
> RPC
RPC - то же, что и пакет, но с первого байта сразу записываются данные (у пакетов записывается их ид).
Перехват
RPC так же бывает двух направлений: входящий, исходящий.
Алгоритм чтения такой же, как и у пакетов, но при этом первый байт не нужно пропускать.
Перехват входящих
Для перехвата входящих RPC используется опкод opcodes:0be3.
Данный код будет выводить сообщения о подключившихся/отключившихся игроках:
CLEO:
{$CLEO}
{$INCLUDE SF}
0001: wait 0 ms
while not SAMP.Available()
wait 400
end
0BE3: raknet setup_incoming_rpc_hook @in_rpc // подготавливаем хук
0BDE: pause_thread 0 // Бесконечный цикл нам не нужен, поэтому просто поставим скрипт на "паузу". Хуки, вызовы команды и т.п. будут работать в это время
:in_rpc
0BE5: raknet 0@ = get_hook_param PARAM_PACKETID // Получаем ИД RPC
if or
0@ == RPC_SCRSERVERJOIN // сверяем с необходимым ИДом
0@ == RPC_SCRSERVERQUIT // ...
then
0BE5: raknet 1@ = get_hook_param PARAM_BITSTREAM // Получаем битстрим
if 0@ == RPC_SCRSERVERJOIN
then
// RPC_SCRSERVERJOIN
0BE7: raknet 2@ = bit_stream_read 1@ type BS_TYPE_SHORT // ид подключившегося игрока
0BE7: raknet 3@ = bit_stream_read 1@ type BS_TYPE_INT // цвет никнейма
0BE7: raknet 4@ = bit_stream_read 1@ type BS_TYPE_BYTE // игрок NPC(true - да, false - нет)
0BE7: raknet 5@ = bit_stream_read 1@ type BS_TYPE_BYTE // длина ника
0AC8: 6@ = allocate_memory_size 25 // буфер
0BE8: raknet bit_stream 1@ read_array 6@ size 5@ // копируем никнейм с указанной длиной в буфер
0C1E: array 6@ element 5@ el_size 1 = 0 // обрезаем буфер нулевым символом(т.к. нулевой символ - конец строки), чтобы при чтении не попадало лишних символов
0AF8: samp add_message_to_chat "%s[%d] подключился на сервер." color -1 params 6@ 2@ // выводим сообщение в чат
0AC9: free_allocated_memory 6@
else
// RPC_SCRSERVERQUIT
0BE7: raknet 2@ = bit_stream_read 1@ type BS_TYPE_SHORT // ид отключившегося игрока
0BE7: raknet 3@ = bit_stream_read 1@ type BS_TYPE_BYTE // причина отключения
0AF8: samp add_message_to_chat "Игрок с ИДом %d отключился от сервера. Код причины: %d" color -1 params 2@ 3@ // ...
end
end
0BE0: raknet hook_ret true // принимаем RPC, false - не принимать
Перехват исходящих
Для перехвата исходящих RPC используется опкод opcodes:0be1.
Данный код будет выводить сообщение о введенной и отправленной вами команде/текста:
CLEO:
{$CLEO}
{$INCLUDE SF}
0001: wait 0 ms
while not SAMP.Available()
wait 400
end
0BE1: raknet setup_outcoming_rpc_hook @out_rpc // подготавливаем хук
0BDE: pause_thread 0 // Бесконечный цикл нам не нужен, поэтому просто поставим скрипт на "паузу". Хуки, вызовы команды и т.п. будут работать в это время
:out_rpc
0BE5: raknet 0@ = get_hook_param PARAM_PACKETID // Получаем ИД RPC
if or
0@ == RPC_CHAT // сверяем с необходимым ИДом
0@ == RPC_SERVERCOMMAND // ...
then
0BE5: raknet 1@ = get_hook_param PARAM_BITSTREAM // Получаем битстрим
0AC8: 3@ = allocate_memory_size 145 // подготавливаем буфер для хранения сообщения/команды
if 0@ == RPC_CHAT
then
// RPC_CHAT
0BE7: raknet 2@ = bit_stream_read 1@ type BS_TYPE_BYTE // получаем длину отправленного сообщения
0BE8: raknet bit_stream 1@ read_array 3@ size 2@ // копируем сообщение с указанной длиной из битстрима в буфер
0C1E: array 3@ element 2@ el_size 1 = 0 // завершаем буфер нулевым символом, чтобы при чтении не было ненужных символов в конце строки
0AF8: samp add_message_to_chat "Я вангую, что ты сейчас напишешь: \"%s\"" color -1 3@ // Отправляем сообщение в чат
else
// RPC_SERVERCOMMAND
0BE7: raknet 2@ = bit_stream_read 1@ type BS_TYPE_INT // получаем длину отправленной команды
0BE8: raknet bit_stream 1@ read_array 3@ size 2@ // копируем команду с указанной длиной из битстрима в буфер
0C1E: array 3@ element 2@ el_size 1 = 0 // завершаем буфер нулевым символом, чтобы при чтении не было ненужных символов в конце строки
0AF8: samp add_message_to_chat "Я вангую, что ты сейчас введешь команду: \"%s\"" color -1 3@ // Отправляем сообщение в чат
end
0AC9: free_allocated_memory 3@ // Буфер больше не нужен для текущей операции
end
0BE0: raknet hook_ret true // отправляем RPC, false - не отправляем
Эмуляция
Для того, чтобы сэмулировать входящие RPC, применяется опкод opcodes:0bf6.
Последнее редактирование: