Эта статья подойдёт для тех, кто пытается взломать/защитить приватные работы или столкнулся с отсутствием нормального апи
Сразу скажу, что вызвать функцию которой нет в секции .publics не получится, т.к. такую функцию можно считать "истинно локальной", вызвать которую можно разве что из-под более низкоуровневого яп чем сп. Функции может не быть там по 2-м причинам:
Итак, допустим следующий код был собран на 1.10:
Запускаем и видим в консоли "test"
Теперь начинается какое-то волшебство:
Либо сделайте автоматическое получение идентификатора функции из имени через GetFunctionByName. Локальные имена придётся указывать в формате
Единственное что 100% советую делать при использовании этой плюхи - выносить идентификаторы/имена функций в геймдату (см. след. страницу), ибо пересобирать решение после каждого обновления целевого плагина, которое может всё сломать, - не комильфо
А теперь приступим к "защите". У сторонних плагинов нет доступа к .data секции, поэтому если записывать туда данные, передавать в функцию и сверять их, то это можно считать как решение нашей головной боли
Старайтесь не использовать эксплойт с не определённым размером массива, поскольку в этом случае у других плагинов будет доступ к .data секции (пример), либо соберите плагин с "истинно локальными" функциями используя компилятор по ссылке ниже
Если интересно, вот модификация последней версии из стабильной ветки (1.11-dev) с "истинно локальными" функциями: Releases · sourcepawn-publics
Сразу скажу, что вызвать функцию которой нет в секции .publics не получится, т.к. такую функцию можно считать "истинно локальной", вызвать которую можно разве что из-под более низкоуровневого яп чем сп. Функции может не быть там по 2-м причинам:
- Плагин собирался на версии компилятора ниже 1.7;
- Кто-то заморочился при сборке, поскольку компилятор явно был модифицирован.
Итак, допустим следующий код был собран на 1.10:
Код:
#include <sourcemod>
public void OnPluginStart()
{
func("test");
}
void func(const char[] text)
{
PrintToServer(text);
}
Теперь начинается какое-то волшебство:
- Открываем smxviewer (надеюсь, не надо разжёвывать как его собрать или хотя бы где взять уже собранный)
- Ищем имя функции. Если был указан модификатор
public
, то имя останется таким же, если нет, то к нему будет добавлен адрес в формате.адрес.имя
- Набрасываем следующий код, где
1
- идентификатор функции из секции .publicsКод:#include <sourcemod> public void OnPluginStart() { PrivateForward fwd = new PrivateForward(ET_Ignore, Param_String); // в 1.10 здесь будет предупреждение, которое можно проигнорировать, но в 1.11+ // приведение к типу Function вызовет ошибку и плагин не соберётся fwd.AddFunction(FindPluginByFile("test.smx"), view_as<Function>(1)); Call_StartForward(fwd); Call_PushString("test2"); Call_Finish(); }
Версия для 1.11+ (используется эксплойт с не определённым размером массива):Код:#include <sourcemod> int ptr[1]; Function func; void memset(int[] p, int off, int val) { p[off] = val; } Function getFunc(int f) { memset(ptr, 1, f); return func; } public void OnPluginStart() { PrivateForward fwd = new PrivateForward(ET_Ignore, Param_String); fwd.AddFunction(FindPluginByFile("test.smx"), getFunc(1)); Call_StartForward(fwd); Call_PushString("test2"); Call_Finish(); }
Либо сделайте автоматическое получение идентификатора функции из имени через GetFunctionByName. Локальные имена придётся указывать в формате
.адрес.имя
Единственное что 100% советую делать при использовании этой плюхи - выносить идентификаторы/имена функций в геймдату (см. след. страницу), ибо пересобирать решение после каждого обновления целевого плагина, которое может всё сломать, - не комильфо
А теперь приступим к "защите". У сторонних плагинов нет доступа к .data секции, поэтому если записывать туда данные, передавать в функцию и сверять их, то это можно считать как решение нашей головной боли
Код:
#include <sourcemod>
int protect;
public void OnPluginStart()
{
protect = GetRandomInt(1, 100500);
func(protect, "test")
}
void func(int data, const char[] text)
{
if (data != protect)
SetFailState("Гэндальф неодобрительно покачивает головой");
PrintToServer(text);
}
Если интересно, вот модификация последней версии из стабильной ветки (1.11-dev) с "истинно локальными" функциями: Releases · sourcepawn-publics
Грамотная реализация вызова функций
Здесь будет рассмотрен пример грамотной реализации вызова функций используя call_publics.inc (а если точнее, тупо импортирован README)
test.sp:
Path_SM/gamedata/test.txt:
main.sp:
test.sp:
Код:
#include <sourcemod>
public void OnPluginStart()
{
func("test");
}
int func(const char[] text)
{
PrintToServer(text);
return 123;
}
Код:
"Games"
{
"*"
{
"Keys"
{
// если в плагине не указан дескриптор целевого плагина, то значение берётся из ключа "plugin"
"plugin" "test.smx"
// имя функции из секции .publics (вместо имени можно использовать идентификатор)
"func" ".3032.func"
}
}
}
Код:
#include <sourcemod>
// подключение call_publics.inc
#include <call_publics>
// глобальная переменная для возможности вызова функции из любой части кода
PrivateForward hFwd;
// макрос для удобства вызова функции
#define FUNC(%0,%1) \
Call_StartForward(hFwd); \
Call_PushString(%1); \
Call_Finish(%0)
public void OnPluginStart()
{
// создание прототипа функции
hFwd = new PrivateForward(ET_Single, Param_String);
// создание объекта CallPublics с данными из Path_SM/gamedata/test.txt
CallPublics hndl = new CallPublics(new GameData("test"));
// добавление функции из CallPublics в hFwd
hndl.AddFunction(hFwd, "func");
// больше нигде не используется, удаляем (не используйте CloseHandle()!)
hndl.Close();
// создание переменной под ответ
int ret;
// вызов функции
FUNC(ret, "main");
// вывод ответа
PrintToServer("ret = %d", ret);
}