Загрузка...
Авторская статья Полиморфный код / Runtime protection
  1. Infern0
    Infern0 Автор темы 7 мар 2018 Ебашу адовые биржи 54 22 фев 2018
    Всем привет!

    В данной теме речь пойдет еще об одном методе защите вашего ПО. А именно о полиморфном коде (ничего общего с полиморфизмом из ООП тут нет).

    Думаю, что те, кто впервые задаются вопросом защиты своих проектов помимо VMProtector`а, SE и тд не понимают что такое полиморфный код. Поэтому для начала я постараюсь почти на пальцах объяснить вам что це такое.

    Если в двух словах, то это код, которые изменяет сам себя (WHAT?!!!). У большинства возникнут два вопроса, как и зачем? На второй я могу ответить сразу. Чтобы скрыть те методы, которые являются критичными, допустим у вас есть метод проверки лицензии, который вы никому не хотите показывать, даже собственному коту. Для этого можно воспользоваться полиморфным кодом, то бишь вызвать метод а после этого зашифровать его или вовсе затереть в памяти нулями или мусором, очень спасает от дампов. Вторая ситуация, у вас есть метод, который вы также не хотите показывать, но еще вы хотите, чтобы его вообще не было в программе. Тоже хорошо подойдет полиморфный код, мы можем сохранить машинный код метода в файл (добавить шифрование) и при запуске программы расшифровывать файл, считать байты метода и выполнить их. Вот в основном для чего я использую полиморфный код.

    Теперь второй вопрос, а как мы реализуем данную черную магию 8 ступени?

    Для начала расскажу немного теории, она вам пригодится, чтобы осознать насколько все просто. Если вы не прогуливали пары по информатике в школе, то, думаю, учитель вам объяснил, что программа при запуске загружается и выполняется в оперативной памяти, это сделано для быстрой работы программы и для уменьшения нагрузки на HDD. А если вы еще и внимательно слушали своего учителя, то, уверен, он говорил, что у любого значения в оперативной памяти есть адрес, по которому это значение можно считать.

    То есть когда мы запускаем свой лоадер, он выгружается в оперативную память, вместе со всеми ресурсами, исходным кодом и тд.

    Теперь немного вспомним простого программирования на C++. Вот пример кода
    Код

    int a = 1337;
    int* pA = &a;

    У нас есть переменная a, в которой хранится значение. У этой переменной есть адрес в оперативной памяти и язык C++ позволяет получить этот адрес с помощью указателя (в данном случае pA). В pA не будет храниться значения переменной "a", однако там будет находиться адрес этой переменной, и мы можем также изменять эту переменную через указатель
    Код

    *pA = 5;


    Но к чему я пишу все это, ведь я показываю на примере переменных, а что же делать с методами? А кто сказал, что там нельзя поступить также?
    вот пример
    Код


    void someFunc()
    {
    ...
    }

    int main()
    {
    void(*_pFunc)() = someFunc;
    }


    Что я сделал в этом примере? Я создал указатель на метод someFunc. Структура тут следующая, сначала надо указать тип нашего метода (в данном случае это void), дальше указываем, что данная переменная - указатель (звездочка) и после этого пишем имя нашего указателя (_pFunc). После этого надо во вторых скобках указываем типы аргументов (только типы), но в данном примере никаких аргументов у метода нет, поэтому это я пропускаю. Собственно остается присвоить этому указателю адрес (тут можно не ставить значок &) и указатель готов. Кстати с помощью этого указателя можно вызывать наш метод.
    Код

    _pFunc();

    Отлично, теперь вы научились получать адреса методов. Но как теперь это использовать? Для этого вспомним, что в C++ мы можем приводить любые типы к любым типам, это может сыграть вам как на пользу, так и во вред. В данном случае мы можем привести тип указателя на метод к типу указателя на массив байт, почему бы и нет, или даже к статическому типу (DWORD, int и тд) и получить просто адрес в виде числа. То есть такой пример абсолютно легален.
    Код

    void(*_pFunc)() = someFunc;
    byte* funcBytes = reinterpret_cast<byte*>(_pFunc);

    Теперь у нас есть указатель на байты метода. Это уже 50% нашей работы. Теперь возникают вопрос, а как понять размер метода в памяти? Для этого есть два метода (мб больше, но я нашел только два). Первый, через stub - то бишь мы создаем еще один пустой метод сразу же под исходным, получаем его
    адрес и просто высчитываем разницу (в конфигурации Release с отключенной оптимизацией это работает идеально). Однако для тех же классов и тд данная фича не подходит, поэтому можно написать такой костыль
    Код

    //Первый костыль
    DWORD getMemSize(void* func, void* stubFunc)
    {
    return reinterpret_cast<DWORD>(stubFunc) - reinterpret_cast<DWORD>(func);
    }

    //Второй костыль
    while (*funcBytes != 0xCC)
    {
    ++funcBytes;
    count++;
    }
    Мы будем будет идти до конца метода и увеличивать счетчик байтов.

    Ну вот мы почти и закончили первый способ применения (перезапись байтов во время выполнения). Остается, как бы это неожиданно не прозвучало бы, перезаписать байты. Однако для этого нам надо повысить права доступа на нужный нам участок памяти, для этого можно использовать WinAPI функцию VirtualProtect (кстати так можно изменять права доступа и к константам).

    Код

    BOOL rewriteMemory(DWORD* startAdress, DWORD size, byte* src)
    {
    DWORD oldProtect;
    if (!VirtualProtect(startAdress, size, PAGE_EXECUTE_READWRITE, &oldProtect))
    return FALSE;

    for (DWORD i = 0; i < size; i++)
    {
    *startAdress = src[i];
    startAdress++;
    }

    if (!VirtualProtect(startAdress, size, oldProtect, &oldProtect))
    return FALSE;

    return TRUE;
    }

    Теперь вы можете шифровать / расшифровывать / удалять методы из памяти вашей программы.

    Ну и второй способ применения полиморфного кода. Загрузка метода из файла / массива байт и выполнение его на стеке.

    Тут задача тоже довольно простая. Получаем байты метода (как получить я уже показал), записать, зашифровать, посолить, поперчить и сохранить в файл. Дальше можно удалять метод.

    Теперь открываем файл, читаем массив байт, расшифровываем и.... приводим наш указатель на массив байт к указателю на метод.
    ОЧЕНЬ ВАЖНО!!! В качестве адреса надо указывать адрес на первый элемент.

    Код

    void(*_pFunc)() = reinterpret_cast<void(*)()>&_bytes[0];

    ГОТОВО! Можно также выполнять эти байты (и не забываем изменять права доступа через VirtualProtect).

    Теперь немного важной информации по поводу того, что будет хранить байт код и чего не будет.

    1. Байт код не хранит адреса на импортируемые функции, так что вам придется передавать адреса на классы, импортируемые функции и тд самостоятельно (думаю я рассказал, как получить адрес нужной функции).
    2. Байт код не хранит строки, то есть если у вас будет строка в методе, то в байт коде будет лишь адрес на нее (тоже самое относится и к массивам). Однако если вы посимвольно будете добавлять в массив значения, то данные операции будут спокойно сохранены.
    3. Никаких статических массивов, только динамическое выделение памяти.

    Однако все это зависит от компилятора и его настроек. Просто говорю, что мне выдавала VisualStudio.


    А на этом у меня все. Я старался как можно подробнее изложить всю суть полиморфного кода. Надеюсь вы не сильно заскучали при чтении этого очень длинного текста.

    p.s.
    1. Не надо писать то, что я называю функции методами, я от ООП не отошел.
    2. Под байт кодом я подразумеваю машинный код.
    #моястатья.
     

Комментарии

    1. Locomen_
      Locomen_ 10 мар 2018 Заблокирован(а) 30 8 янв 2018
      Неплохо
       
    2. Theziky
      Theziky 11 мар 2018 Заблокирован(а) 13 1 ноя 2017
      Годно
       
    3. Checkerchin
      Checkerchin 14 мар 2018 178 16 апр 2017
      Рабочая тема, жаль так мало лайков.
       
    4. Lakostt
      Lakostt 15 мар 2018 Без цели,без плана,без конечного пункта назначения 217 5 окт 2017
      Офигенная статья, очень подробно объяснил
       
    5. smplhack
      smplhack 30 мар 2018 0 28 мар 2018
      Исходники есть? Можешь скинуть в лс?
       
    6. Infern0
      Infern0 Автор темы 1 апр 2018 Ебашу адовые биржи 54 22 фев 2018
      хм, исходники в статье. Просто тут включена защита от дебилов.
       
    7. sokoloff0
      sokoloff0 7 апр 2018 1 3 фев 2018
      Интересно и годно.
       
    8. Енот272_inactive171964
      Енот272_inactive171964 9 апр 2018 Ушел на покой 372 6 сен 2017
      Хорошая статья! Жаль что такого уровня очень мало найти в интернете, автору однозначно пятюня!)
       
    9. Tea418
      Tea418 25 июн 2019 10 26 май 2018
      Четко, буду ебашить анти-пиратскую система. Защита до вечности, уровень: анти-дебил.
       
Top