1. Теперь за форумную активность начисляются биткоины и другие криптоденьги. Подробнее.
    Скрыть объявление
  2. Появилась архивная версия форума arhiv.xaker.name, где собраны темы с 2007 по 2012 год.
    Скрыть объявление

Самомодификация как средство защиты своих программ - Часть 1

Тема в разделе "Статьи, руководства, видео", создана пользователем Dr. MefistO, 31 янв 2012.

  1. Dr. MefistO
    Dr. MefistO Крывіч Глобальный модератор
    Симпатии:
    123
    Самомодификация как средство защиты своих программ - Часть 1

    Доброго времени суток, друзья!=)
    Наконец-то я созрел и до такого крутого (на первый взгляд) понятия, как "модификация кода в ходе выполнения программы", или "самомодификация". Я решил написать свою статью на эту тему, т.к. имеющиеся статьи на эту тему чем-то меня не устраивали (замудрено/не очевидно/много букаф).

    Данный метод позволяет защитить свои программы от автоматического анализа различного рода декомпиляторами (к примеру: Delphi программы можно анализировать программой IDR).

    Из самомодификации вытекают следующие возможности:
    • Протекторы;
    • Крипторы;
    • Лоадеры для упакованных программ;
    • Инжектирование в процессы.

    Как оказалось, селфмоддинг - это не просто круто, а нереально круто, и полезно!=) Сейчас все и расскажу. Но прежде - необходимый инструментарий:

    Часть I: первое приложение с самомодификацией

    Надеюсь, можно начинать)
    Открываем старушку Дельфу, создаем консольное приложение (чтобы не возиться с созданием KOL-проекта): File->New->Other...->Console Application, и сразу его сохраняем куда-нибудь. Получилась заготовка, в которой нужно заменить SysUtils на KOL и добавим Windows модуль:
    Код:
    program test1;
    
    {$APPTYPE CONSOLE}
    
    uses
      KOL, Windows;
    
    begin
      { TODO -oUser -cConsole Main : Insert code here }
    end.
    Хорошо, теперь давайте напишем процедуру, которая будет что-нибудь выполнять. Например... выводить в окно консоли числа из счетчика (превед Dельфиньему дZену;)).
    Код:
    procedure WriteValues;
    var
      i: Byte;
    begin
      for i:=1 to 10 do
      Writeln(i);
    end;
    Тут все просто: выведутся числа от 1 до 10 в черное окошко консоли.

    Получившийся код:
    Код:
    program test1;
    
    {$APPTYPE CONSOLE}
    
    uses
      KOL, Windows;
    
    procedure WriteValues;
    var
      i: Byte;
    begin
      for i:=1 to 10 do
      Writeln(i);
    end;
    
    begin
      WriteValues;
    
      Readln;
    end.
    Пробуем запустить, видим - все работает.

    Самомодификация

    Теперь сделаем так, чтобы числа выводились от одного до 100, но сделаем это с применением самомодификации.

    Основная процедура для этого: VirtualProtect. Ей можно разрешить/запретить писать в любые участки памяти программы. Но обо всем по порядку.

    Для использования данной процедуры нам понадобятся три переменные:
    Код:
    OldPageProtection: Cardinal; //Она будет хранить режим защиты памяти, который был до нашего вызова этой функции, чтобы не оставить память нашей программы доступной для записи после выполнения всех действий.
    Код:
    base: PByte; //Будет использоваться для хранения адреса (указателя) начала модифицируемой процедуры, т.е. на ее нулевой байт.
    Код:
    offset: byte; //Т.к. изменяемые нами байты будут находиться не в самом начале процедуры, а немного дальше, важно будет узнать расположение относительно начала процедуры нужного нам байта. Его мы посчитаем чуть дальше.
    Объявим их. Теперь поясню как оформляется вызов процедуры VirtualProtect:
    Код:
      base := PByte(Cardinal(@WriteValues) + offset); //тут мы получаем адрес необходимого нам байта (пока будем менять только один)
      VirtualProtect(
      Pointer(base), //передаем указатель на модифицируемый байт
      1,             //количество изменяемых байт
      PAGE_EXECUTE_READWRITE,  //режим чтения-записи в память
      OldPageProtection        //в эту переменную мы сохраняем старый режим
      );
    Вот, собственно, и все хитрости. Хотя вот еще одна) менять байты в памяти мы будем такой вот командой:
    Код:
    base^ := $00; // где $00 нужный нам байт
    Так, теперь нужно узнать, что и на что нам менять. Не спешите открывать Olly Debugger. Сначала нам нужно записать адрес процедуры на бумажку себе. Для этого воспользуемся командой:
    Код:
    MsgOK(Int2Hex(Cardinal(@WriteValues),8));
    Желательно вставить ее сразу после первого begin и не убирать до тех пор, пока все у нас не будет готово, т.к. этот адрес по ходу изменения программы будет меняться.

    Обнаружение цели

    Теперь откроем отладчик Olly Debugger, а в нем нашу программу. Перейдем на полученный адрес (Ctrl+G). У меня получился адрес 00403FEC:
    Самомодификация как средство защиты своих программ - Часть 1

    На картинке я обозначил первый байт и последний у процедуры, а также нужный нам байт. Остается посчитать позицию этого байта относительно начала, и записать значение в offset в коде нашей программы. Столбец, в котором идут хекс-коды, где я рамочками повыделял байты - это машинный код. Считать позицию байта надо с условием, что позиция первого байта процедуры равна 0.

    Итого, вышло, что позиция нужного байта равна = $1E, или 30.
    Т.е. получится так:
    Код:
    offset:=$1e;
    Теперь байт $0B (11 в десятичной) надо программно изменить на 101 ($65 в хексе). Получается что цифра счетчика будет на единицу больше нужного нам значения, т.е. команда присваивания примет вид:
    Код:
    base^ := 101 //$65 в хексе;
    и закрыть память на запись:
    Код:
    VirtualProtect(Pointer(base),1,OldPageProtection,OldPageProtection);
    Что получилось

    Код:
    program test1;
    
    {$APPTYPE CONSOLE}
    
    uses
      KOL, windows;
    
    procedure WriteValues;
    var
      i: Byte;
    begin
      for i:=1 to 10 do
      Writeln(i);
    end;
    
    var
      OldPageProtection: Cardinal;
      base: PByte;
      offset: byte;
    begin
      MsgOK(Int2Hex(Cardinal(@WriteValues),8));
      WriteValues;     //первый раз выводим для просмотра результата
    
      offset:=$1e; //Смещение изменяемого байта относительно начала процедуры
    
      base := PByte(Cardinal(@WriteValues) + offset); //Позиция изменяемого байта
      VirtualProtect(
      Pointer(base), //передаем указатель на модифицируемый байт
      1,             //количество изменяемых байт
      PAGE_EXECUTE_READWRITE,  //режим чтения-записи в память
      OldPageProtection        //в эту переменную мы сохраняем старый режим
      );
    
      base^:=101; //Присваиваем новое значение
      VirtualProtect(Pointer(base),1,OldPageProtection,OldPageProtection);
    
      WriteValues;    //смотрим изменения
    
      Readln;
    end.
    Анализ происходящего

    По F8 выполняя код в Olly Debugger'е постоянно посматривайте за заменяемым байтом, и, в тот момент когда выполнится команда присваивания нашего числа тому адресу памяти, число изменится.
    Важно: изменения происходят только в памяти, и после завершения программы изменения не будут сохранены.

    Исходный код примера из статьи

    ----------------------------------------------------------------​

    Автор: Владимир Мефисто
    Спасибо: Орехов Роман aka tripsin

    В следующий раз я расскажу про шифрование нашей функции и расшифровку ее в памяти
     
    Последнее редактирование: 12 фев 2012
    31 янв 2012
    4 пользователям это понравилось.

Поделиться этой страницей

Загрузка...