Патчинг компонент Delphi, или как снять некачественно сделанную защиту.

Ко мне часто обращаются люди с вопросом: "Как мне самому снять защиту?" или “Научи, как ломать компоненты”. Обычно я оставлял подобные вопросы без внимания.

В начале мне было интересно, как же защищают свои программы солидные фирмы, но с течением времени все больше становится скучно, глядя на то, что около 90% всех защит построены по одному и тому же сценарию...

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

Эта статья покажет на самые примитивные методы, которыми пользуются практически все фирмы производители компонент. Она не даст исчерпывающей информации по взлому защит, так как каждая из них, при должном уделении внимания, уникальна по-своему и к некоторым нужен особый подход. А теперь от слов к делу.

Существует 3 основных типа защиты компонентов для Delphi и С Builder.

1. Проверка на загрузку в памяти IDE. И в случае отсутствия программа завершает свою работу
2. Выполнение части кода только при определенных условиях. Сюда же я отнесу варианты периодического вывода Наг-экранов.
3. С помощью некоторых директив компилятора в компонентах блокируется (исключается), либо изменяется логика работы.

С каждым из типов защиты вы наверняка сталкивались. С первыми двумя можно вполне позаниматься, а с третьим - воевать практически бесполезно. Давайте на конкретных примерах подробно рассмотрим снятие защиты по первым двум вариантам.
 

Завершение программы в случае незапущенной IDE.

Классическим примером этого варианта защиты являются компоненты VclZip. Для испытания создадим тестовый пример. Положим на форму компонент VCLUnzip и
что-нибудь еще, например кнопку. Сохраняем проект, компилируем и запускаем из среды программирования. Все прекрасно работает. Но попробуем запустить только что созданную программу, предварительно закрыв IDE. В результате увидим такое окошко

Нажав на кнопку “OK”, программа завершает свою работу, так фактически и не запустившись. Ну что же, засучив рукава, начнем разбираться.
Существует несколько способов проверки наличия в памяти IDE. Наиболее часто используется метод, основанный на поиске окна с заданным заголовком.
Но как же нам определиться в каком именно модуле (файле с расширением .dcu) находится данный поиск. Да очень просто. Вот вам три самые страшные “дырки” в системах защиты:
1. Cтроки в теле модулей хранятся в незашифрованном виде.
2. При именовании процедур и функций производители не особо напрягаются. Так часто можно увидеть, например: IsDelphiRunning, DRun, DelphiRun, DR, DS и подобные. И совершенно забывают, что скрыть названия функций от любопытного взгляда в Windows приложениях достаточно сложно, а особенно в объектных файлах, каковыми являются .obj и .dcu.
3. Используется стандартная реализация функции поиска IDE в памяти. Отличия проявляются в описании строковых констант и количеству вызовов FindWindow.

function Is_IDE_Running: Boolean;

const

   A1 : array[0..12] of char = 'TApplication'#0;

   A2 : array[0..15] of char = 'TAlignPalette'#0;

   A3 : array[0..18] of char = 'TPropertyInspector'#0;

   A4 : array[0..11] of char = 'TAppBuilder'#0;

   T1 : array[0..15] of char = 'Delphi 6'#0;

begin

   Result := (FindWindow(A1, T1) <> 0) and

      (FindWindow(A2, nil) <> 0) and

      (FindWindow(A3,nil)<>0) and

   (FindWindow(A4, nil) <> 0);

end;

В связи с этими 3 моментами поищем файлы dcu, содержащие строчку, например “Delphi”. В результате вырывается наружу возглас: “О чудо!!!”. Вот мы и локализовали среди кучи файлов всего 2, на которые нужно уделить дальнейшее внимание.

А что дальше? Дальше дизассемблируем эти файлы. Можно использовать любой вами любимый дизассемблер. Но я в своей практике в основном использую HIEW и DeDe. Последний, наверное, вам покажется более удобным, и будете совершенно правы.

Итак, дизассемблируем файл VCLUnzip.dcu. Открываем полученный файл на просмотр и дальнейшее изучение. Опять поищем строчку “Delphi”. И что же первым попадается нам на глаза:

procedure MoveI32 (var Source: void; var Dest: void;

  Count: System.Integer);

function DelphiIsRunning: System.Boolean;

procedure Register;

 Далее становится очевидным. Находим реализацию выделенной мной функции.

begin

   00000000 : 68(00 00 00 00 PUSH T1{0x4D1}

   00000005 : 68(00 00 00 00 PUSH A1{0x4CE}

   0000000A : E8(00 00 00 00               CALL FindWindow{0xF2}

   0000000F : 85 C0                               TEST EAX,EAX

   00000011 : 74 20                                JE +32; (0x33)

   00000013 : 6A 00                              PUSH $00

   00000015 : 68(00 00 00 00 PUSH A2{0x4CF}

   0000001A : E8(00 00 00 00               CALL FindWindow{0xF2}

   0000001F : 85 C0                               TEST EAX,EAX

   00000021 : 74 10                                JE +16; (0x33)

   00000023 : 6A 00                              PUSH $00

   00000025 : 68(00 00 00 00 PUSH A4{0x4D0}

   0000002A : E8(00 00 00 00               CALL FindWindow{0xF2}

   0000002F : 85 C0                               TEST EAX,EAX

   00000031 : 75 03                                JNE +3; (0x36)

   00000033 : 33 C0                               XOR EAX,EAX

   00000035 : C3                                    RET NEAR

   00000036 : B0 01                               MOV AL,$01

   00000038 : C3                                    RET NEAR

end;

Понятно, что в результате функция должна вернуть 1. Следовательно, после первого вызова FindWindow и выполнения проверки ее результата мы должны подправить адрес перехода. Открываем файл VCLUnzip.dcu в Hiew. Жмем клавишу F4, т.е. переходим в режим ассемблерных команд.

Находим последовательность 68000000006800000000E80000000085C07420 и меняем 74 20 на EB 23, либо 85 С0 на EB 25.

Аналогично после анализа файла kpZipObj.dcu правим функцию DRun. Интересно что, реализация этой функции полностью идентична DelphiIsRunning. Так что последовательность байт поиска и вариант замены остается тем же.

Остается проверить результат проделанной работы. Подменяем оригинальные файлы, на нами подправленные, собираем пакеты снова. Компилируем наш тестовый пример, и видим, что мы добились результата. С чем себя и поздравляем.

Ну и в завершении неплохо убить строку c текстом из НАГ-экрана, если таковая есть в теле модуля в незашифрованном виде. Для этого можно, например, просто забить байты этой строки пробелами.

Выполнение части кода только при определенных условиях. Периодический вывод Наг-экранов.

Можно было бы рассмотреть вариант вывода Наг-скрина, но это, как правило, скучно. Выскочил Наг. Ладно. Ищем строку с сообщением из Наг-скрина в модулях. Нашли, изменяем условный переход на безусловный, либо если нет поблизости перехода, то добавляем его.

Рассмотрим снятие защиты с компонентов, где часть кода выполняется только при определенных условиях. Таковым, например, является iSEDQuickPDF.

Данный невизуальный компонент предназначен для создания и работы с .pdf-файлами. Рассмотрим простейший пример работы с этим компонентом, который предложен на сайте разработчиков.
 

uses
  uiSEDQuickPDF
;

procedure TForm1.Button1Click(Sender: TObject);
var
  QP: TiSEDQuickPDF;
begin
  QP := TiSEDQuickPDF.Create;
  try
    QP.UnlockKey('type your license key here');
    QP.DrawText(100, 500, 'Hello World!');
    QP.SaveToFile('c:\test.pdf');
  finally
    QP.Free;
  end;
end;

Мы видим, что используется функция UnlockKey. А также читаем

The next part unlocks the library. You must supply your license key to the UnlockKey function. If you have not purchased iSEDQuickPDF yet, contact us for a free evaluation license key.

QP.UnlockKey('type your license key here');

Из текста следует, если Вы не зарегистрированы - обращайтесь к разработчикам. Они дадут ключик, который ограничен по времени. Ну что же. Давайте попробуем разобраться с этим типом защиты.
Запускаем предложенный вариант программы, и что мы видим. Никаких порочащих программу сообщений нет, но не создается так же файл 'c:\test.pdf'.

Возникает вопрос к чему же прицепиться? Как снять эту защиту? У меня сразу возникло желание посмотреть на функцию UnlockKey. Запускаем поиск файла, содержащего строку с названием этой функции, и определяем, что она находится в модуле uiSEDQuickPDF.dcu. Прекрасно. Дизассемблируем этот файл с помощью DeDe. Находим нашу функцию и спокойно начинаем изучать ее. Авторы молодцы, они используют функции шифрования MD5String, MD5Print для работы с ключиком. Это “Вери гуд” для них. Но что же нам делать? Может благодаря собственному опыту, а может какая муха меня укусила, но я обратил внимание на то, что идет частая работа с ячейкой памяти [EDI+20]. Записывается туда то 0, а то 1. Далее эта ячейка сравнивается с 0 и в результате сравнения в регистр ebx помещается 0 или 1. И значение регистра ebx становится результатом самой функции UnlockKey.

CMP BYTE PTR [EDI+20],$00

JE +7; (0x2BF)

MOV EBX,$00000001

JMP +2; (0x2C1)

XOR EBX,EBX

MOV EAX,EBX

В справке прочтем.

function UnlockKey(RegKey As String) As Long

Parameters

RegKey The registration key.

Returns

0 The library could not be unlocked.

1 The library was unlocked successfully.

Так вот где собака порылась. Значит, нам только и надо, чтобы функция всегда давала 1 и ячейка памяти [EDI+20] должна содержать единичку. Я оставляю исправление функции за вами. Обращаю внимание на то, что эта функция также формирует строчку с типом лицензии.

Эта часть показала еще одну грубую ошибку в создании систем защиты. А именно – постоянное хранение результата проверки в статической переменной.

Заключение.

После того как найдены места исправления в файлах, можно создать программу патчер, которая в автоматическом режиме выполнит данную корректировку. За моим примером подобной программы, выполненной без применения библиотеки VCL, Вы можете обратиться ко мне.
На этом я завершаю данную статью. Прочитав ее внимательно, Вы сможете сами снимать примитивные защиты. А после получения определенного навыка и более серьезные. Опять же повторюсь, цель статьи - заострить внимание на том, как нельзя защищать свой программный продукт. Бесспорно, нет защиты, которую нельзя заблокировать. Но сильно затруднить работу хакеру – это в Ваших силах.

Желаю Вам всем создавать программы с хорошей системой защиты.

А лучшая защита бывает у специалистов из компании Proforce.Ru: разработка программного обеспечения под ключ.

 



Опубликовал admin
1 Мар, Понедельник 2004г.



Программирование для чайников.