PDA

Просмотр полной версии : [Вопрос] Вызов / перехват public-функций



oukibt
09.07.2021, 16:26
Приветствую, собственно, появился вопрос, как перехватить или хотя бы вызвать public-функцию (коллбэк) из плагина. Это, по идее, должно делаться примерно также, как и с нативными функциями, так как в структуре AMX_HEADER предусмотрена запись оффесета не только нативных функций, но и public-функций. Код в итоге выглядит примерно вот так, но не работает.


int index;
if(amx_FindPublic(amx, "OnPlayerConnect", &index) != AMX_ERR_NONE) return logprintf("Not found"), 0;

AMX_HEADER* hdr = (AMX_HEADER*)amx->base;
AMX_FUNCSTUB* func_stub = GETENTRY(hdr, publics, index);

AMX_CALLBACK Callback = (AMX_CALLBACK)func_stub->address;

cell retval , PARAMS[] = { 4, 5 };

Callback(amx, index, &retval, PARAMS);

Daniel_Cortez
09.07.2021, 19:58
Так это ж не нативный код, а интерпретируемый. Вызывать его следует с помощью функции amx_Exec.

oukibt
09.07.2021, 21:58
Об этом я, к сожалению, вспомнил только после этого ответа. А можно ли как-нибудь хукнуть вызов коллбэка?
Есть, конечно, один вариант, до которого я додумался, но он, как не странно, костыльный.
Напрямую в павн создавать нужный коллбэк, и вызывать в нём нативную функцию. В плагине она, в принципе, будет выступать, как коллбэк, но опять же, способ не очень из-за костыля

oukibt
10.07.2021, 23:36
Проблема решена. В конечном счёте, я разобрался, и вот решение.

Я использовал библиотеку subhook от Zeex (https://github.com/Zeex/subhook)
Пару функций я взял из sampgdk

Пример будет приведён на коллбэке OnPlayerText


cell* amx_GetParamStart(AMX* amx)
{
unsigned char* data = amx->data != NULL
? amx->data
: amx->base + ((AMX_HEADER*)amx->base)->dat;
return (cell*)(data + amx->stk);
}

void amx_GetParamValue(AMX* amx, int index, cell* param)
{
*param = amx_GetParamStart(amx)[index];
}

subhook_t amx_Exec_hook;

void amx_Exec_func(AMX* amx, cell* retval, int index)
{
subhook_remove(amx_Exec_hook);

logprintf("Public-function index: %d", index);
if (index == 10) // У меня 10 = индексу OnPlayerText
{
int playerid;
char text[145];
int len;
cell* addr;

amx_GetParamValue(amx, 0, &playerid);

amx_GetAddr(amx, amx_GetParamStart(amx)[1], &addr); // [1] - это номер параметра, как и 0 на строку выше
amx_StrLen(addr, &len);
amx_GetString(text, addr, NULL, len + 1);

logprintf("%d | %s", playerid, text);
}

amx_Exec(amx, retval, index);

subhook_install(amx_Exec_hook);;
}

PLUGIN_EXPORT bool PLUGIN_CALL Load(void** ppData)
{
#ifndef WIN32_
amx_Exec_hook = subhook_new((void*)0x401C90, (void*)amx_Exec_func, {}); // Адрес для Виндовс
#else
amx_Exec_hook = subhook_new((void*)0x8096780, (void*)amx_Exec_func, {}); // Адрес для Линукс
#endif
subhook_install(amx_Exec_hook);

return true;
}


Эти адреса - адреса функции amx_Exec непосредственно в samp-server.exe и samp03svr

В функцию amx_Exec_func в аргумент index приходит индекс коллбэка. Через amx_FindPublic мы можем сравнять индексы, и узнать, какой именно коллбэк был вызван.

P.S. На линуксе я не тестировал, но уверен, что адрес подойдёт