Эта статья подойдёт для тех, кто пытается взломать/защитить приватные работы или столкнулся с отсутствием нормального апи
Сразу скажу, что вызвать функцию которой нет в секции .publics не получится, т.к. такую функцию можно считать "истинно локальной", вызвать которую можно разве что из-под более низкоуровневого яп чем сп. Функции может не быть там по 2-м причинам:
Итак, допустим следующий код был собран на 1.10:
Запускаем и видим в консоли "test"
Теперь начинается какое-то волшебство:
Единственное что 100% советую делать при использовании этой плюхи - выносить идентификаторы/имена функций в геймдату (см. след. страницу), ибо пересобирать решение после каждого обновления целевого плагина, которое может всё сломать, - не комильфо
А теперь приступим к "защите". У сторонних плагинов нет доступа к .data секции, поэтому если записывать туда данные, передавать в функцию и сверять их, то это можно считать как решение нашей головной боли
Старайтесь не использовать эксплойт с не определённым размером массива, поскольку в этом случае у других плагинов будет доступ к .data секции (пример), либо соберите плагин с "истинно локальными" функциями используя компилятор по ссылке ниже
Модификация последней версии из стабильной ветки (1.11-dev) с "истинно локальными" функциями: Releases · deathscore13/sourcepawn-publics
Сразу скажу, что вызвать функцию которой нет в секции .publics не получится, т.к. такую функцию можно считать "истинно локальной", вызвать которую можно разве что из-под более низкоуровневого яп чем сп. Функции может не быть там по 2-м причинам:
- Плагин собирался на версии компилятора ниже 1.7;
- Кто-то заморочился при сборке, поскольку компилятор явно был модифицирован.
public
всегда находятся в .publicsИтак, допустим следующий код был собран на 1.10:
C-подобный:
#include <sourcemod>
public void OnPluginStart()
{
func("test");
}
void func(const char[] text)
{
PrintToServer(text);
}
Теперь начинается какое-то волшебство:
- Открываем smxviewer (надеюсь, не надо разжёвывать как его собрать или хотя бы где взять уже собранный)
- Ищем имя функции. Если был указан модификатор
public
, то имя останется таким же, если нет, то к нему будет добавлен адрес в формате.адрес.имя
- Набрасываем следующий код, где
1
- идентификатор функции из секции .publics
C-подобный:#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+ (используется эксплойт с не определённым размером массива):
C-подобный:#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(); }
.адрес.имя
Единственное что 100% советую делать при использовании этой плюхи - выносить идентификаторы/имена функций в геймдату (см. след. страницу), ибо пересобирать решение после каждого обновления целевого плагина, которое может всё сломать, - не комильфо
А теперь приступим к "защите". У сторонних плагинов нет доступа к .data секции, поэтому если записывать туда данные, передавать в функцию и сверять их, то это можно считать как решение нашей головной боли
C-подобный:
#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);
}
Грамотная реализация вызова функций
Здесь будет рассмотрен пример грамотной реализации вызова функций используя call_publics.inc (а если точнее, тупо импортирован README)
Имена функций должны начинаться с
test.sp:
Path_SM/gamedata/test.txt:
main.sp:
Имена функций должны начинаться с
@
, как имена сигнатурtest.sp:
C-подобный:
#include <sourcemod>
public void OnPluginStart()
{
func("test");
}
int func(const char[] text)
{
PrintToServer(text);
return 123;
}
INI:
"Games"
{
"*"
{
"Keys"
{
// если в плагине не указан дескриптор целевого плагина, то значение берётся из ключа "plugin"
"plugin" "test.smx"
// имя функции из секции .publics (вместо имени можно использовать идентификатор)
"func" "@.3032.func"
}
}
}
C-подобный:
#include <sourcemod>
// подключение call_publics.inc
#include <call_publics>
// глобальная переменная для возможности вызова функции из любой части кода
PrivateForward hTestFunc;
// глобальная переменная для записи возвращаемого значения функции
int iTestFuncReturn;
// макрос для удобства вызова функции
#define TEST_FUNC(%0) \
Call_StartForward(hTestFunc); \
Call_PushString(%0); \
Call_Finish(iTestFuncReturn)
public void OnAllPluginsLoaded()
{
// создание прототипа функции
hTestFunc = new PrivateForward(ET_Single, Param_String);
// создание объекта CallPublics с данными из Path_SM/gamedata/test.txt
CallPublics hndl = new CallPublics(new GameData("test"));
// добавление функции из CallPublics в hFwd
hndl.AddFunction(hTestFunc, "func");
// больше нигде не используется, удаляем (не используйте CloseHandle()!)
hndl.Close();
// вызов функции
TEST_FUNC("main");
// вывод ответа
PrintToServer("iTestFuncReturn = %d", iTestFuncReturn);
}