Text;004015CFp

jmp ds: ?EnableWindow@cwnd@@QREHH@z

j_?EnableWindow@cwnd@@QAEHH@z endp

Их всего два. Как раз по числу элементов управления. Пока защита не предвещает ничего необычного и ее код выглядит вполне типично:

.text:0040142A mov еая, [esi+68h]

.text:0040142D lea ecu, [esi+OACh]

.text;00401433 push еак

.text:00401434 call j_?EnableWindow@cwnd88aaEHH@Z ;

и, аналогично, другой фрагмент:

.text:004015C8 mov eax, [esi+6Oh]

.text:004015CB lea ecx, [esi+6Ch]

.text:004015CE push eax

.text:004015CF call j_?EnableWindow@CWnd@@QAEHH@Z ;

Попробуем найти, как уже было показано выше, '46 60', т.е. [esi+60] и '46 68' — (esi+68). Полученный результат должен выглядеть следующим образом

.00401385: С7466001000000 mov d,[esi] [00060],000000000

И

.004012CC: C7466801000000 mov d,[esi] [00068],000000000

Кажется, что защита использует два независимых флага. С первого взгляда их нетрудно изменить на ненулевое значение. Ожидается, что это заставит защиту работать. Ну что ж, попытаемся это сделать.

Как будто бы все работает, не правда ли? Но попробуем нажать на левую

Пустой диалог выглядит странно, не так ли? Похоже, что защита взломана некорректно и приложение работает неверно. И дело не только в том, что сложно найти то место, где код ведет себя неправильно (это по большому счету не так уж и трудно). Главная сложность — убедиться в работоспособности (неработос­пособности) программы. В данном примере это тривиальная задача, но она не будет такой в банковских, научных, инженерных приложениях. Если неправильно работает только одна, редко вызываемая ветка, то тестирование поломанного приложения — дело безнадежное.

Однако разработчики защит часто упускают из виду, что компилятор мог расположить все флаги близко от друг друга, значительно облегчая поиск кракеру. В самом деле, в нашем примере фигурируют две переменные типа DWORD — [esi+60] и [esi+68]. Нетрудно заметить, что между ними образовалась "дырка" размером ровно в двойное слово. Может быть, эта переменная — еще один флаг защиты? Попробуем найти '46 64'; . .004015B3 mov d, [esi] [00064] , 000000000

Что будет, если ноль заменить на единицу? Попробуем, и... сработало! Ранее пустой диалог теперь приветствует нас "Hello, Sailor!". Защита пала! Очевидно, что разработчик использовал по крайней мере три флага и конструкцию типа:

S0.SetAt(0,s0[0]*(!RegFlag_l ^ RegFlag__3));

Но кто может гарантировать, что нет четвертого или пятого флага? На самом деле число переменных класса ограничено, и не так трудно проанализировать их все. Кроме того, обычно флаги регистрации — это глобальные переменные. Последних же в грамотно спроектированной программе на объективно-ориенти­рованном языке очень и очень немного.

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

Блокирование элементов управления не единственно возможный вариант. Многие демонстрационные приложения при попытке выполнения некоторой операции (например записи в файл) выдают диалоговое окно, информирующее об отсутствии данной возможности в ограниченной версии. Иногда эта возмож­ность — вернее, код, ее реализующий, — действительно физически отсутствует, но чаще этот код просто не получит управления.

Варианты блокировки больше рассматриваться не будут, во избежание повто­рений. Они слишком просты и элементарно ломаются. Гораздо интереснее искать пути выхода из ситуации, когда кода, реализующего данную операцию, попросту нет. Конечно, зачастую легче полностью переписать программу заново, чем разобраться в взаимодействии с недостающим кодом и воссоздать его. Это тема настолько сложная, что не может быть исчерпывающие рассмотрена в рамках данной книги.

Рассмотрим достаточно простой пример подобной защиты: file:/ /CD:SOUR-CEVCCRACKIO RELEASE crack 10.exe. Это простой текстовый редактор, который при попытке сохранения отредактированного файла выводит диалоговое окно, информирующее об отсутствии такой возможности в демо-версии.

Найдем этот вызов и дизассемблируем его:

.text: 00401440

.text:00401440 NagScreen ргос near ; DATA xref: .rdata:00403648o

. text: 00401440 push О

.text: 004 01442 push 0

.text:00401444 push offset unk_0_404090

.text:00401449 call j_?AfxMessageBox@@YGHPBDII@z

.text:0040144E xor eax, eax

.text 00401450 retn 4

.text:0040144E NagScreen endp

.text:0040144E

Допустим, можно удалить вызов j_?AfxMessageBox@@YGHPBDII@Z, но чего мы этим добьемся? Нет никаких сомнений в том, что код, обрабатывающий запись файла на диск, отсутствует. Впрочем, есть ненулевая вероятность, что он находится сразу после retn или где-нибудь поблизости. Это бывает при использо­вании следующих конструкций:

{

BOOL CCRACKIODoc::OnSaveDocument(LPCTSTR IpszPathName)

AfxMessageBox ("Это ограниченная версия. Пожалуйста, приобретайте полную"};

return 0;

return CCRACKIODoc::OnSaveDocuJtient(lpszPathHaine);

}

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

Впрочем, в нашей ситуации написать недостающий код легко. Мы можем получить указатель на текстовый буфер и просто сохранить его на диске. Все это укладывается в десяток строк и может быть написано за несколько минут. Передаваемые параметры можно узнать, если установить на эту процедуру точку останова и заглянуть отладчиком на вершину стека. Засланное в стек значение очень похоже на указатель (а чем еще могло быть такое большое число?) и в действительности является указателем на имя файла, что можно легко проверить, взглянув на дамп памяти, расположенный по этом адресу.

Однако гораздо большей проблемой, чем написание своего кода, станет его внедрение в уже откомпилированный ехе-файл. Под MS-DOS эта проблема уже была хорошо изучена, но Windows обесценила большую часть прошлого опыта. Слишком велика оказалась разница между старой и новой платформами. С другой стороны, Windows принесла и новые возможности такой модификации. Например, помещение кода в DLL и простой вызов его оттуда. Подробное рассмотрение таких примеров требует целой отдельной книги, поэтому рассматриваемый здесь прием специально упрощен.

Вернемся к защите. Перейдем по единственной перекрестной ссылке, чтобы узнать, кто вызывает этот код.

.rdata: 00403644 dd offset j ?OnOpenDocument@CDocument

.rdata: 00403648 dd offsat. eub_0_401440

.rdata:0040364C dd offset j_?OnCioseDocument@CDocument

Что представляют собой перечисленные смещения? Программисты, знакомые с MFC, безошибочно узнают в них экземпляр класса CDocument. Это можно подтвердить, если прокрутить экран немного вверх и, перейдя по одной из двух перекрестных ссылок, посмотреть на следующий фрагмент:

401390 sub_0_401390 proc near

401390 push esi

401391 mov esi, ecx

401393 call j_??OCDoeument@eQAE0XZ