Русское сообщество по скриптингу

Orpheu: Перехват сообщений в чат от других плагинов

Статьи или фрагменты кода для новичков и уже опытных скриптеров по AMXX.

Модератор: Chuvi

Правила форума
1. Запрещено материться и оскорблять других участников форума.
2. Запрещен флуд, оффтоп, дабл постинг во всех разделах форума, кроме раздела "Болтовня".
3. Запрещено взламывать сайт/форум или наносить любой вред проекту.
4. Запрещено рекламировать другие ресурсы.
5. Запрещено создавать темы без информативного названия. Название темы должно отображать ее смысл.

В данном разделе форума разрешено создавать темы, касающие только обучающему материалу по AMX Mod X.

Orpheu: Перехват сообщений в чат от других плагинов

Сообщение DJ_WEST » 29 авг 2010, 03:57

Автор: DJ_WEST

В данной статье речь пойдет о перехвате сообщений в чат, которые посылаются другими плагинами AMXX. Заметил, что многие об этом спрашивали на форуме, как это сделать. Собственно, перехватывать будем функцию client_print. Зачем это нужно? К примеру, у вас нет исходника плагина, а вам нужно или изменить сообщение, или русифицировать его, а может даже убрать вшитую в плагин рекламу. Для перехвата нам понадобится модуль Вы должны зарегистрироваться, чтобы видеть ссылки..

Итак, начнем с простого. Функция для вывода сообщений имеет следующий синтаксис:
[pawn]client_print(index, type, const message[], ...) [/pawn]
index - id игрока или 0, если нужно отправить сообщение всем игрокам
type - тип сообщения:
  • print_chat - сообщение в чат
  • print_console - сообщение в консоль
  • print_notify - сообщение в консоль при режиме разработчика (developer)
  • print_center - сообщение по центру
message - текст сообщения

Теперь попробуем обратиться к исходникам мода AMX Mod X. В файле amxmodx.cpp находим нашу функцию:
[pawn]
static cell AMX_NATIVE_CALL client_print
(AMX *amx, cell *params) /* 3 param */
{
    int len = 0;
    char *msg;
    
    if 
(params[1] == 0)
    {
        for (int i = 1; i <= gpGlobals->maxClients; ++i)
        {
            CPlayer *pPlayer = GET_PLAYER_POINTER_I(i);
            
            if 
(pPlayer->ingame)
            {
                g_langMngr.SetDefLang(i);
                msg = format_amxstring(amx, params, 3, len);
                msg[len++] = '\n';
                msg[len] = 0;
                UTIL_ClientPrint(pPlayer->pEdict, params[2], msg);
            }
        }
    } else {
        int index = params[1];
        
        if 
(index < 1 || index > gpGlobals->maxClients)
        {
            LogError(amx, AMX_ERR_NATIVE, "Invalid player id %d", index);
            return 0;
        }
        
        CPlayer
* pPlayer = GET_PLAYER_POINTER_I(index);
        g_langMngr.SetDefLang(index);
        
        msg 
= format_amxstring(amx, params, 3, len);
        msg[len++] = '\n';
        msg[len] = 0;
        
        if 
(pPlayer->ingame)
            UTIL_ClientPrint(pPlayer->pEdict, params[2], msg);        //format_amxstring(amx, params, 3, len));
    }
    
    return len
;
}
 
 
[/pawn]
Внимательно изучив код, можно заметить, что в нем вызывается функция UTIL_ClientPrint. Открываем util.cpp и находим ее:
[pawn]void UTIL_ClientPrint(edict_t *pEntity, int msg_dest, char *msg)
{
    if (!gmsgTextMsg)
        return;                // :TODO: Maybe output a warning log?

    char c = msg[190];
    msg[190] = 0;            // truncate without checking with strlen()
    
    if 
(pEntity)
        MESSAGE_BEGIN(MSG_ONE, gmsgTextMsg, NULL, pEntity);
    else
        MESSAGE_BEGIN
(MSG_BROADCAST, gmsgTextMsg);
    
    WRITE_BYTE
(msg_dest);
    WRITE_STRING(msg);
    MESSAGE_END();
    msg[190] = c;
}
 [/pawn]
Данную функцию (UTIL_ClientPrint) мы и будем перехватывать. Теперь нам нужно найти сигнатуру этой функции для Windows и Linux версий библиотеки. А затем создать конфиг к Orpheu и использовать ее для перехвата сообщений. Сигнатуры находим с помощью программы IDA Pro.

[align=center]
client_print_linux.jpg

client_print_win.jpg
[/align]

Так как мы будем искать функцию в библиотеке AMXX, то нужно добавить в Orpheu его поддержку. Для этого в директории ..\addons\amxmodx\configs\orpheu\libraries создадим файл amxmodx. Добавим в него:
Код: Выделить всё

[
   "amxmodx", "amx_mode"
]

Теперь создадим конфигурационный файл для Orpheu в директории ..\addons\amxmodx\configs\orpheu\functions с именем UTIL_ClientPrint. Содержание файла должно быть таким:
Код: Выделить всё
{
   "name" : "UTIL_ClientPrint",
   "library" : "amxmodx",
   "arguments" :
   [
        {
            "type" : "pointer"
        },
        {
            "type" : "int"
        },
        {
            "type" : "char *"
        }
   ],
   "identifiers":
   [
      {
         "os" : "windows",
         "value" : [0x83,"*","*","*","*","*","*",0x74,"*",0x8B,"*","*","*",0x85,0xC0,0x53,0x56,0x8B,"*","*","*",0x8A,"*","*","*","*","*",0xC6,"*","*","*","*","*","*",0x74,"*",0x50,0xA1,"*","*","*","*",0x6A,0x00,0x50,0x6A,0x01,0xEB,"*"]
      },
      {
         "os" : "linux",
         "value" : "_Z16UTIL_ClientPrintP7edict_siPc"
      }
   ]
}

name - имя нашей функции
library - библиотека, где нужно искать функцию, ставим amxmodx, так как мы добавили поддержку
arguments - аргументы функции UTIL_ClientPrint, если вернуться к исходникам AMXX, то функция имеет данный вид:
[pawn]UTIL_ClientPrint(edict_t *pEntity, int msg_dest, char *msg) [/pawn]
Так как Orpheu не поддерживает тип edict_t, вместо этого указываем pointer. Второй тип - int, третий - char *.
identifiers - идентификаторы функции, в данном случае это сигнатуры
os - операционная система (windows/linux)
value - значение, в данном случае сигнатура (на скриншотах выше выделено красным цветом)


Все, наш конфиг готов. Еще один важный момент, как видно функция использует edict_t *pEntity, что нам не подходит, потому что это указатель на объект, а не его id. А для того, чтобы правильно перехватить id игрока, которому отправляется сообщение, нам нужно будет использовать еще одну функцию из engine модуля игры под названием IndexOfEdict.

[align=center]
indexofedict_linux.jpg

indexofedict_windows.jpg
[/align]

Теперь создадим конфигурационный файл для Orpheu в директории ..\addons\amxmodx\configs\orpheu\functions с именем IndexOfEdict. Содержание файла должно быть таким:
Код: Выделить всё
{
   "name" : "IndexOfEdict",
   "library" : "engine",
   "return" :
   {
      "type" : "int"
   },
   "arguments" :
   [
      {
            "type" : "pointer"
      }
   ],
   "identifiers":
   [
      {
         "os" : "windows",
         "value" : [0x55,0x8B,0xEC,0x8B,"*","*",0x85,0xC0,0x75,"*",0x5D,0xC3,0x8B,"*","*","*","*","*",0x56,0x2B,0xC1,0x8B,0xC8,0xB8,"*","*","*","*"]
      },
      {
         "os" : "linux",
         "value" : "IndexOfEdict"
      }
   ]
}

return - тип возвращаемого значения


Осталось применить полученные знания и конфиги на практике, рассмотрим примерный плагин:
[pawn]#include <amxmodx>
#include <orpheu>

#define PLUGIN "Hook client_print"
#define VERSION "1.0"
#define AUTHOR "DJ_WEST"

// Используем для проверки, что объект - это игрок, если входит значение в промежуток между 1 и 32
#define IsPlayer(%1) (1 <= (%1) <= 32)

// Создаем переменную для хранения указателя на функцию IndexOfEdict
new OrpheuFunction:o_IndexOfEdict

public plugin_init
()
{
    register_plugin(PLUGIN, VERSION, AUTHOR)
        
    
// Регистрируем перехват на функцию UTIL_ClientPrint
    // On_ClientPrint_Pre - функция, которая будет вызываться при вызове функции UTIL_ClientPrint
    // OrpheuHookPre - означает, что перехватываем функцию перед ее вызовом
    OrpheuRegisterHook(OrpheuGetFunction("UTIL_ClientPrint"), "On_ClientPrint_Pre", OrpheuHookPre)
    
    
// Помещаем в переменную o_IndexOfEdict указатель на функцию IndexOfEdict
    o_IndexOfEdict = OrpheuGetFunction("IndexOfEdict")
}

// Указываем аргументы для функции относительно тех, что прописаны в конфиге
// p_Edict - указатель на объект
// i_Type - тип сообщения
// s_Message - текст сообщения
public OrpheuHookReturn:On_ClientPrint_Pre(p_Edict, i_Type, s_Message[190])
{
    // Удаляем пробелы с начала и конца s_Message
    trim(s_Message)

    // Если полученное сообщение s_Message равняется указанному
    // и тип сообщения равен print_chat (вывод в чат)
    if (equal(s_Message, "Type 'amx_help' in the console to see available commands") && i_Type == print_chat)
    {
        //  Создаем переменную для хранения id игрока
        static id
        
        
// Вызываем функцию IndexOfEdict, передавая ей указатель p_Edict
        // Полученное значение сохраняем в переменную, как id игрока
        id = OrpheuCall(o_IndexOfEdict, p_Edict)
    
        if 
(IsPlayer(id))
        {
            // Если мы хотим заменить сообщение своим, то посылаем новое
            // Если хотим только блокировать, то не посылаем
            client_print(id, i_Type, "Visit http://amx-x.ru", id)
        
            
// Используем OrpheuSupercede для блокировки выполнения функции UTIL_ClientPrint
            // Следовательно оригинальное сообщение, которое мы заменяем, игрок не увидит
            return OrpheuSupercede
        
}
    }

    // Если сообщение не подходит нашему условию
    // то просто продолжаем выполнение функции UTIL_ClientPrint
    return OrpheuIgnored
}  [/pawn]
Не пишите мне в ЛС: если вам нужна помощь на бесплатной основе. Любые вопросы на форум.
Аватара пользователя
DJ_WEST
Администратор
 
Сообщения: 3641
Зарегистрирован: 22 авг 2009, 00:38
Благодарил (а): 48 раз.
Поблагодарили: 2209 раз.
Опыт программирования: Больше трех лет
Языки программирования: Counter-Strike 1.6
Counter-Strike: Source
Left 4 Dead
Left 4 Dead 2

Re: Перехват сообщений в чат от других плагинов

Сообщение Makzz » 29 авг 2010, 15:23

У меня только два вопроса по программе IDA:
1) Как сделать/настроить чтобы идентификаторы отображались? (а то у меня там везде пустое белое поле)
2) Как сделать/настроить чтобы отображались имена функций, ибо вместо них в большинстве случаев написано sub_1D62XXX :dash:
Аватара пользователя
Makzz
 
Сообщения: 82
Зарегистрирован: 28 авг 2010, 09:50
Благодарил (а): 2 раз.
Поблагодарили: 24 раз.
Опыт программирования: Больше трех лет
Языки программирования: Counter-Strike 1.6

Re: Перехват сообщений в чат от других плагинов

Сообщение DJ_WEST » 29 авг 2010, 15:43

1) Как сделать/настроить чтобы идентификаторы отображались? (а то у меня там везде пустое белое поле)

Options - General - Number of opcode bytes - 10
[align=center]
opcodes.jpg
[/align]
2) Как сделать/настроить чтобы отображались имена функций, ибо вместо них в большинстве случаев написано sub_1D62XXX

Для этого нужен .pdb файл, который можно получить при компиляции исходников AMX Mod X в Visual Studio (в данном случае будет amxmodx_mm.pdb). Затем при открытии .dll библиотеки AMXX в IDA Pro и ее декомпиляции он спросит:
[align=center]
pdb.jpg
[/align]
Нажимаем Yes и получаем нормальные названия функций, .pdb лучше держать в одной директории с .dll.
Не пишите мне в ЛС: если вам нужна помощь на бесплатной основе. Любые вопросы на форум.
Аватара пользователя
DJ_WEST
Администратор
 
Сообщения: 3641
Зарегистрирован: 22 авг 2009, 00:38
Благодарил (а): 48 раз.
Поблагодарили: 2209 раз.
Опыт программирования: Больше трех лет
Языки программирования: Counter-Strike 1.6
Counter-Strike: Source
Left 4 Dead
Left 4 Dead 2

Re: Перехват сообщений в чат от других плагинов

Сообщение Makzz » 29 авг 2010, 16:55

Затем при открытии .dll библиотеки AMXX в IDA Pro и ее декомпиляции он спросит:

Да таки спрашивал меня деасамблер об этом :-D
А если требуется открыть библиотеку не имея исхода (я имею ввиду двиг и мод-либ)?? КОНТР+Ф5 и включать логику??
Аватара пользователя
Makzz
 
Сообщения: 82
Зарегистрирован: 28 авг 2010, 09:50
Благодарил (а): 2 раз.
Поблагодарили: 24 раз.
Опыт программирования: Больше трех лет
Языки программирования: Counter-Strike 1.6

Re: Перехват сообщений в чат от других плагинов

Сообщение DJ_WEST » 29 авг 2010, 17:44

А если требуется открыть библиотеку не имея исхода (я имею ввиду двиг и мод-либ)?? КОНТР+Ф5 и включать логику??

Да.
Не пишите мне в ЛС: если вам нужна помощь на бесплатной основе. Любые вопросы на форум.
Аватара пользователя
DJ_WEST
Администратор
 
Сообщения: 3641
Зарегистрирован: 22 авг 2009, 00:38
Благодарил (а): 48 раз.
Поблагодарили: 2209 раз.
Опыт программирования: Больше трех лет
Языки программирования: Counter-Strike 1.6
Counter-Strike: Source
Left 4 Dead
Left 4 Dead 2

Re: Перехват сообщений в чат от других плагинов

Сообщение Lt.RAT » 29 авг 2010, 17:52

Ой, требуемый плагин выводит рекламное сообщение через Вы должны зарегистрироваться, чтобы видеть ссылки. - и о странно оно не перехватывается, наверное там нет вызовов client_print...

Если уж и перехыватывать, то не UTIL_ClientPrint...

Да и иногда требуется перехватывать не только чат, но и консоль, а туда можно писать и через engclient_print (кстати, если требуется выводить бльшой объем информации (>192) с разными символами типа % - то весьма полезная функа)

ЗЫ гж за туториал, но вот полезность его :)
ЗЗЫ хотя думаю кому надо - напишут для себя нормальный перехват.
Аватара пользователя
Lt.RAT
 
Сообщения: 301
Зарегистрирован: 30 сен 2009, 01:44
Благодарил (а): 4 раз.
Поблагодарили: 151 раз.
Языки программирования: Counter-Strike 1.6

Re: Перехват сообщений в чат от других плагинов

Сообщение DJ_WEST » 29 авг 2010, 18:01

Ой, требуемый плагин выводит рекламное сообщение через viewtopic.php?f=9&t=85 - и о странно оно не перехватывается, наверное там нет вызовов client_print...

Потому что там сообщение отправляется через event, а не функцию client_print.
Если уж и перехыватывать, то не UTIL_ClientPrint...

Можно перехватывать в движке, но я рассмотрел именно данный способ, при желании можно найти сигнатуры и для них.
Да и иногда требуется перехватывать не только чат, но и консоль, а туда можно писать и через engclient_print (кстати, если требуется выводить бльшой объем информации (>192) с разными символами типа % - то весьма полезная функа)

В чем проблема? Хукаем ClientPrint из engine и делаем точно также.
ЗЫ гж за туториал, но вот полезность его
ЗЗЫ хотя думаю кому надо - напишут для себя нормальный перехват.

Потому что ты это и сам умеешь делать (надеюсь). А для других это еще одна возможность понять работу Orpheu и его достоинства.
Не пишите мне в ЛС: если вам нужна помощь на бесплатной основе. Любые вопросы на форум.
Аватара пользователя
DJ_WEST
Администратор
 
Сообщения: 3641
Зарегистрирован: 22 авг 2009, 00:38
Благодарил (а): 48 раз.
Поблагодарили: 2209 раз.
Опыт программирования: Больше трех лет
Языки программирования: Counter-Strike 1.6
Counter-Strike: Source
Left 4 Dead
Left 4 Dead 2

Re: Перехват сообщений в чат от других плагинов

Сообщение Lt.RAT » 29 авг 2010, 18:21

DJ_WEST писал(а):Потому что там сообщение отправляется через event, а не функцию client_print.

Да, просто в плагин встраиваешь любое ручное отправление мессаги и все идет лесом :)

DJ_WEST писал(а):Потому что ты это и сам умеешь делать (надеюсь). А для других это еще одна возможность понять работу Orpheu и его достоинства.

Я так и понял :) Да и заодно в твоих плагинах (под заказ) рекламу перебивать не будут :-X

ЗЫ допиши в название статьи про Орфей, кто будет специально искать информацию по его использованию - быстрей найдут и опробуют :)
Аватара пользователя
Lt.RAT
 
Сообщения: 301
Зарегистрирован: 30 сен 2009, 01:44
Благодарил (а): 4 раз.
Поблагодарили: 151 раз.
Языки программирования: Counter-Strike 1.6

Re: Orpheu: Перехват сообщений в чат от других плагинов

Сообщение navigator » 22 апр 2011, 03:54

Подскажите как найти сигнатуру функции SV_GetIDString для винды?
Раньше стояла такая
Код: Выделить всё
{
    "name" : "SV_GetIDString",
    "library" : "engine",
   "arguments" :
    [
      {
         "type" : "pointer"
      }
   ],
    "return" :
    {
        "type" : "char *"
    },
    "identifiers":
    [
        {
            "os" : "windows",
            "value" : [0x55,0x8B,0xEC,0x83,0xEC,0x30,0x8B,0x4D,0x08,0xC6,"*","*","*","*","*","*",0x85,0xC9,0x0F,"*","*","*","*","*",0x8B,0x01,0x48,0x0F]
        },
        {
            "os" : "linux",
            "value" : "SV_GetIDString"
        }      
    ]
}


и всё работало но когда подключаешь DPROTO она куда то исчезает и Orpheu
Код: Выделить всё
Orpheu functions search started.

        Parsing functions started.
                Parsing file "SV_GetIDString" started
                                Argument type "pointer" validated
                                Return type "char *" validated
                                Searching for signature "[0x55][0x8b][0xec][0x83][0xec](...)" ... NOT FOUND
                Parsing file "SV_GetIDString" ended
        Parsing functions ended.

не может её найти!
Сори если не туда написал, я подумал что это самая подходящая тема!
Аватара пользователя
navigator
 
Сообщения: 65
Зарегистрирован: 03 фев 2011, 21:35
Откуда: Уфа.
Благодарил (а): 18 раз.
Поблагодарили: 3 раз.
Опыт программирования: Меньше месяца
Языки программирования: Counter-Strike 1.6

Re: Orpheu: Перехват сообщений в чат от других плагинов

Сообщение 6a6kin » 22 апр 2011, 08:46

Это значит, что дпрото патчит эту функцию и меняется сигнатура. Лучше всего обратиться к Кроку за помощью, т.к. дпрото патчит движок в памяти(динамически).
На заказ не пишу.
Аватара пользователя
6a6kin
Скриптер
 
Сообщения: 332
Зарегистрирован: 09 мар 2010, 16:40
Благодарил (а): 38 раз.
Поблагодарили: 278 раз.

След.

Вернуться в Статьи / фрагменты кода

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 11