Защита плагина от декомпиляции

JDW

Мы открываем бизнес
Сообщения
376
Реакции
325
Всем, привет. Нашел силы в себе и наконец закончил данную статью.

Сама суть идеи: зашифровываем плагин (бинарник) и расшифровываем его при загрузки в память vm.
Плюсы: Плагин зашифрован и его просто так не прочитаешь (прощайте декомпиляторы)
Плагины, которые не зашифрованы так же будут дальше работать.
Минусы: Для каждого ключа нужна своя версия sourcepawn.jit.x86.so.

1) Начнем с написания программы, которая будет зашифровывать плагин.
Src:
#include <string>
#include <vector>
#include <cstdio>

using std::string;
using std::vector;

const string key = "Your key";
const char* pluginName = "test.smx";

int main()
{
    FILE* file = fopen(pluginName, "rb");

    fseek(file, 0, SEEK_END);
    vector<char> data(ftell(file));
    fseek(file, 0, SEEK_SET);
    char ch;

    for (size_t i = 0; i < data.size(); i++)
        data[i] = fgetc(file);

    fclose(file);
    
    // Здесь идет само шифрование
    for (size_t i = 0; i != data.size(); i++)
        data[i] ^= key[i % key.size()];

    file = fopen("output.smx", "wb");

    for (size_t i = 0; i < data.size(); i++)
        fputc(data[i], file);

    fclose(file);

    return 0;
}

2) Модифицируем код виртуальной машины
Открываем sourcepawn/vm/api.cpp

Добавляем в начале файла следующий заголовок:
src:
#include <vector>
Далее ищем следующую функцию (где-то на 252 строке):
src:
IPluginRuntime*
SourcePawnEngine2::LoadBinaryFromFile
И после:
src:
  FILE* fp = fopen(file, "rb");

  if (!fp) {
    UTIL_Format(error, maxlength, "file not found");
    return nullptr;
  }
Добавляем следующий код:

src:
  const std::string key = "Your key"; //Ваш ключ (Который использовался при шифрование)
  uint32_t magic;
  char* buffer = nullptr;

  if (fread(&magic, sizeof(uint32_t), 1, fp) != 1)
  {
    return nullptr;
  }

  if (magic != SmxConsts::FILE_MAGIC)
  {
    fseek(fp, 0, SEEK_END);
    std::vector<char> data(ftell(fp));
    fseek(fp, 0, SEEK_SET);

    for (size_t i = 0; i < data.size(); i++)
      data[i] = fgetc(fp);

    for (size_t i = 0; i != data.size(); i++)
        data[i] ^= key[i % key.size()];

    fclose(fp);

    char* buffer = (char*)malloc(data.size());
    std::copy(data.begin(), data.end(), buffer);
    fp = fmemopen(buffer, data.size(), "rb");
  }
После следующих строк:
src:
  std::unique_ptr<SmxV1Image> image(new SmxV1Image(fp));
  fclose(fp);
Добавляем:
src:
if (buffer != nullptr) free(buffer);
Снимок экрана от 2020-08-23 00-03-37.png

3) Сохраняем и компилируем
 
Последнее редактирование модератором:

JDW

Мы открываем бизнес
Сообщения
376
Реакции
325
Готовый билд сорцов (Только в учебных целях).
Название плагина, который получит на выходе output.smx -> test.smx (Значит плагин, который шифруем должен иметь название: "test.smx".
Выкладываю по просьбе @nulled
Все вопросы в ЛС форума
 

Вложения

  • example.zip
    1.1 МБ · Просмотры: 102
Последнее редактирование модератором:

Rostu

Добрая душа
Сообщения
986
Реакции
623
Плюсы: Плагин зашифрован и его просто так не прочитаешь (прощайте декомпиляторы)
Здравствуйте, декомпиляторы

Способ - странный, запутанный, ненужный, но прекрасный и забавный
Требование: Другое - [INC] Memory Extended [На форуме 3.0, по этому если что, берите с гитхаба] [Rostu13/Memory-Extended]
Минусы: Постоянный меняющийся offset, но его легко найти
Ну, а вообще - ключ открыт, по этому вы можете просто взять его

Как все это работает, как найти offset и ключ, и как "защищается" плагин и ломается защита
Можно увидеть на видео
C-подобный:
#include <dhooks>
#include <MemoryEx>

static const char outPutDir[] = "/memoryDump";

static const Address g_pOffset = view_as<Address>(0x32516); // offset [https://imgur.com/a/J8cshPy]

public void OnPluginStart()
{
    Address pBase = GetModuleHandle("sourcepawn.jit.x86.so") + g_pOffset;

    Address pNew = g_hMem.GetImportAddress(NULL_STRING, "fmemopen");

    if(pNew == nullptr)
    {
        SetFailState("couldn't find fmemopen");
    }

    Address pOffsetFunc = pNew - (pBase + view_as<Address>(0x04));

    //PrintToServer("pNew 0x%X pBase 0x%X [0x%X] byte 0x%X = 0x%X", pNew, pBase + 0x04, pOffsetFunc, LoadFromAddress(pNew, NumberType_Int8), (pBase + 0x04) + pOffsetFunc);

    StoreToAddress(pBase, pOffsetFunc, NumberType_Int32);

    Handle h = DHookCreateDetour(pNew, CallConv_CDECL, ReturnType_Int, ThisPointer_Ignore);
    DHookAddParam(h, HookParamType_Int);
    DHookAddParam(h, HookParamType_Int);
    DHookAddParam(h, HookParamType_Int);

    if(!DHookEnableDetour(h, false, fmemopen_Hooked))
    {
        SetFailState("Couldn't hook ListPluginsToClient");
    }
}
public  MRESReturn fmemopen_Hooked (Handle hReturn, Handle hParams)
{
    Address pAddr = DHookGetParam(hParams, 1);
    int iSize = DHookGetParam(hParams, 2);

    if(iSize < 0x04)
    {
        return MRES_Ignored;
    }

    if(LoadFromAddress(pAddr, NumberType_Int32) != 0x53504646)
    {
        return MRES_Ignored;
    }


    char sOutPut[32];
    FormatEx(sOutPut, sizeof sOutPut, "0x%X_0x%X.smx", pAddr, pAddr + view_as<Address>(iSize));

    /*PrintToServer("addr 0x%X|0x%X |%c%c%c%c", pAddr, iSize,
    LoadFromAddress(pAddr, NumberType_Int8),
    LoadFromAddress(pAddr + 1, NumberType_Int8),
    LoadFromAddress(pAddr + 2, NumberType_Int8),
    LoadFromAddress(pAddr + 3, NumberType_Int8));*/


    CreateMemoryDump(sOutPut, pAddr, iSize);
    return MRES_Ignored;
}

void CreateMemoryDump(const char[] sFileName, Address pBase, any iSize)
{
    char sNewPath[PLATFORM_MAX_PATH];

    if(!DirExists(outPutDir))
    {
        CreateDirectory(outPutDir, 511);
    }

    FormatEx(sNewPath, sizeof sNewPath, "%s/%s", outPutDir, sFileName);

    if(FileExists(sNewPath))
    {
        if(!DeleteFile(sNewPath))
        {
            SetFailState("CreateMemoryDump couldn't delete file %s", sNewPath);
        }
    }

    File outPut = OpenFile(sNewPath, "ab");

    if(outPut == null)
    {
        SetFailState("file == null");
    }

    /*// Исправление smx header
    outPut.WriteString(g_sSMXMagic, false);
    outPut.WriteInt16(LoadFromAddress(pBase + view_as<Address>(0x04), NumberType_Int16));
    outPut.WriteInt8(0x00);
    outPut.WriteInt32(iSize);

    pBase += view_as<Address>(0x0B);*/

    Address end = pBase + view_as<Address>(iSize);

    while(pBase < end) // Очень плохая реализация
    {
        outPut.WriteInt8(LoadFromAddress(pBase, NumberType_Int8));
        pBase++;
    }
    
    delete outPut;
}
 

Вложения

  • SPHack_Dumper.zip
    1.1 МБ · Просмотры: 56
Последнее редактирование:
Сверху Снизу