Теоретические основы крэкинга: Глава 7. Искусство разбивать окна. — Архив WASM.RU
Человек и кошка плачут у окошка,
Серый дождик каплет прямо на стекло.
К человеку с кошкой едет «неотложка» -
Человеку бедному мозг больной свело.Федор Чистяков, «Человек и кошка»
И вот, пройдя долгий и трудный путь исследователя ценных данных, упрятанных в недрах программ, мы, наконец, подошли к вратам «чистого крэкинга». Вспомните, с чего начинаются едва ли не все платные программы: с предложения зарегистрироваться. Это предложение может быть написано аршинными буквами в отдельном окне, появляющемся при запуске, или же в виде маленького MessageBox’а, выскакивающего в самый неподходящий момент во время работы. Оно может быть мерцающим баннером вверху или внизу экрана. Но, как бы оно ни выглядело, цель его существования во всех этих случаях одна: раздражать и давить на психику незарегистрированного пользователя, дабы он, зажав в кулак свои кровные, отправился на почту и перевел их автору программы в знак благодарности за будущее избавление от назойливого окна. Однако далеко не у всех людей, созерцавших эти безыскусные творения, возникали столь теплые чувства к разработчику. И эти раздраженные пользователи дали надоедливым окнам то самое имя, под которым они и известны современному крэкеру. Называются эти окна «nagscreens» (от английского «nag» - «надоедать, приставать, придираться»), и в этой главе речь как раз пойдет о способах борьбы с такими окнами.
Я, увы, лично не застал рождение крэкинга под платформу Win32, но опоздал ненамного, а потому в моей коллекции имеются многие популярные руководства тех времен. Тогда первым словом едва ли не каждого крэкера была классическая команда bpxMessageBoxA – другие варианты были очень, очень редким исключением. Тридцатидвухразрядные Delphiбыли лишь светлой мечтой, монструозный MFC, спроектированный на базе логики пришельцев с Альфы Центавра, не пользовался особой популярностью, и потому большинство программистов тогда обходилось одним лишь Win32 API. А из всех функций WinAPIMessageBoxбыла едва ли не самой популярной – ибо трудно было придумать более простое и универсальное средство вывести какое-нибудь нехитрое сообщение, да так, чтобы пользователь не смог это сообщение проигнорировать. Разумеется, разработчики shareware-программ не могли пройти мимо такой возможности, и первые «надоедливые экраны» были теми самыми MessageBox’ами. Мы же, в свою очередь, нещадно bpx’или эти MessageBox’ы – и выискивали точку ветвления, в которой расходились жизненные пути зарегистрированной и незарегистрированной инкарнации программы. И если когда-нибудь появится мемориал в честь крэкеров Вселенной – на нем, несомненно, огромными золотыми буквами будет выбито bpxMessageBoxA.
Но все-таки я начну рассказ о борьбе с нежелательными визуальными спецэффектами в программах не с MessageBox’ов. Ибо я стараюсь следовать принципу «от простого – к сложному», а стандартные окна с сообщениями – это все-таки не самое простое из того, с чем Вы можете столкнуться. Согласитесь, чтобы удалить из программы лишний MessageBox, все-таки надо приложить некоторые усилия по обнаружению этого MessageBox’а в коде программы при помощи отладчика или дизассемблера – поэтому я начну с рассказа о таких способах удаления nagscreen’ов, которые не предполагают выхода на уровень ассемблера. Вы спросите – возможно ли такое? Конечно, возможно! И, как это ни удивительно, такую возможность подарила нам операционная система Windows.
Прежде чем переходить к рассмотрению методов борьбы с нежелательными окнами и конкретных примеров их применения, рассмотрим проблему баннеров и nagscreen’ов, что называется, в перспективе. Я бы выделил четыре подхода к задаче ликвидации рекламных вставок в программах:
- Изменение свойств объектов (окон, визуальных компонентов) таким образом, чтобы они не отображались на экране. На практике это может быть достигнуто тремя способами: выносом нежелательного объекта за пределы экрана (проще всего использовать отрицательные координаты – при любых размерах монитора объект все равно будет за пределами видимой части экрана); изменением размеров объекта (к примеру, дочерние окна нулевой длины и ширины на экране не видны); модификацией шаблонов диалогов с тем, чтобы придать нежелательным объектам свойства невидимости и неактивности.
- Удаление шаблонов объектов из исполняемого файла.
- Модификация кода с целью предотвратить создание или отображение нежелательного объекта.
- Модификация кода таким образом, чтобы нежелательный объект самоликвидировался сразу после появления.
Вот о первых двух подходах, в основном, и пойдет речь в этой главе.
Ради того, чтобы упростить жизнь программистам, в ОС Windows реализована возможность интегрировать в исполняемый файл ресурсы всевозможных типов – текстовые строки, иконки, изображения в стандартных форматах и, что самое интересное, так называемые «шаблоны» (templates) диалоговых окон. Эти шаблоны описывают внешний вид диалогов: размеры, атрибуты, находящиеся внутри диалога управляющие элементы (меню, кнопки, поля редактирования и т.п.) и надписи на этих элементах. При помощи соответствующих функций WindowsAPIна основе этих шаблонов могут быть созданы реальные окна, внешний вид которых будет в точности соответствовать их описанию внутри шаблона. Открыв такой исполняемый файл при помощи какого-либо редактора ресурсов (наиболее известны ResourceHacker, Restorator и eXeScope), Вы можете попытаться модифицировать ресурсы программы, например, перевести все надписи на русский язык (если быть до конца точным, то у ресурсов имеется еще один атрибут – идентификатор языка, который, возможно, также потребуется модифицировать). Если Вы все сделаете правильно, и редактор ресурсов корректно сохранит изменения, после запуска программы соответствующие надписи будут русифицированы. Таким же образом Вы можете изменять расположение и свойства элементов интерфейса (например, наличие рамки и ее цвет) в диалоговых окнах. Более того, Вы можете заменить не только надписи, но и хранящиеся в ресурсах изображения, своими собственными. Нужно отметить, что ресурсы могут храниться не только внутри исполняемого файла, но и в любом другом файле формата PortableExecutable. Чаще всего это делается для облегчения локализации – реализовать выбор одной из возможных ресурсных DLLгораздо проще, чем выпускать множество локализованных версий одного и того же исполняемого файла. Кроме того, ресурсные DLL – довольно удобный способ хранения «шкурок» (skin’ов) для приложений, использующих эту технологию модификации интерфейса.
Вообще все ресурсы делятся на типы, среди которых есть такие, как иконки (RT_ICON), курсоры (RT_CURSOR), шаблоны диалоговых окон (RT_DIALOG) и многое другое. Для идентификации каждого конкретного ресурса среди других ресурсов того же типа используются числовые либо символьные идентификаторы. Именно на основе идентификаторов программа и различает ресурсы между собой. Что интересно, программе совершенно нет дела до тех данных, которые прячутся за идентификаторами – поэтому, поменяв местами идентификаторы у пары однотипных ресурсов, можно получить весьма занятные эффекты: например, вместо сообщения об успешном завершении той или иной операции будет появляться сообщение об ошибке (и наоборот).
Теперь немного поговорим о том, какие тайны скрываются в недрах шаблонов диалоговых окон. Прежде всего, умение обращаться с шаблонами позволит Вам полностью преобразить интерфейсы очень многих программ, причем сделать это с минимальными затратами сил. Большинство редакторов ресурсов умеют не только декодировать шаблоны в исходный текст, понятный компиляторам ресурсов (при желании Вы можете «одолжить» особенно понравившееся окно из чужой программы), но и непосредственно показывать, как этот диалог будет выглядеть на мониторе. А наиболее продвинутые редакторы позволяют даже править эти шаблоны визуальными средствами. Кто знаком с современными визуальными средствами разработки, тот, наверняка, догадался, о чем идет речь. А те, кто не догадался, могут просто взять Калькулятор из состава Windows 98 (с Калькулятором от WindowsXPтакой номер может не пройти – всяческие MUIмогут отравить Вам радость познания), программку ResourceHackerи вдоволь поэкспериментировать над первой программой при помощи второй: например, поменять местами все кнопки в Калькуляторе. Причем настоятельно рекомендую Вам попытаться проделать эту операцию не только тасканием-бросанием кнопок а-ля VisualBasic, но и через ручную правку ресурсного скрипта (resourcescript).
Но махинации с кнопками – это, в общем-то, мелочи, несмотря на тот могучий эффект, который производит «обработанный напильником» Калькулятор на неподготовленного пользователя. Гораздо более важно другое – а именно то, что управляющие элементы шаблона также имеют свои собственные идентификаторы, по которым программа различает управляющие элементы между собой и «общается» с ними. И эти идентификаторы также можно менять при помощи редактора ресурсов. Попробуйте проделать над несчастным Калькулятором еще один опыт – поменяйте местами значения идентификаторов у двух кнопок, например, у единицы и семерки. Запустив Калькулятор, Вы увидите, что единица и семерка действительно поменялись местами! Кроме идентификатора Вы можете менять и другие атрибуты кнопок (более корректно называть их не атрибутами кнопок, а стилями окна) – видимость, наличие рамки, способ выравнивания текста и многое другое. И из этой возможности следуют кое-какие выводы чисто практического свойства.
Довольно часто авторы sharewareпользуются следующим приемом: сразу после запуска программы выводится nagscreen, кнопка закрытия которого изначально неактивна и активизируется лишь спустя несколько секунд. Внутри программы это может быть реализовано следующим образом: в шаблоне, по которому создается nagscreen, стиль этой кнопки изначально определен как неактивный (он называется WS_DISABLED). После создания окна запускается таймер, который через установленное время активизирует нужную кнопку, и пользователь получает возможность закрыть окно. Однако если мы уберем у кнопки стиль WS_DISABLED, пользователю больше не потребуется ждать несколько томительных секунд, чтобы получить возможность нажать на кнопку. Аналогичным же образом иногда удается включить неактивные функциональные элементы не только на nagscreen’ах, но и непосредственно в самой ломаемой программе. Увы, всвязи с появлением некоторого количества программ, способных принудительно включать неактивные элементы управления, закрывать «лишние» окна и проделывать прочие столь же милые вещицы (ваш покорный слуга со своим проектом Sign 0fMiseryтоже отметился на этом поприще), авторы sharewareвсе чаще вводят различные дополнительные проверки, призванные оградить назязчивую рекламу от священного права пользователя эту рекламу не смотреть.
Однако самое интересное заключено даже не в манипуляциях с идентификаторами и стилями. Куда более важным представляется вопрос – а что если просто взять и удалить из секции ресурсов «лишний» диалог? Будем рассуждать логически: создание диалоговых окон производится при помощи семейства функций CreateDialog*** (которые затем требуют принудительно отобразить окно при помощи ShowWindow) либо при помощи DialogBox*** (это семейство функций все операции по отображению окна берет на себя). Причем для создания модальных окон (т.е. таких, которые пользователь не смог бы проигнорировать) чаще используется именно второе семейство функций.
Предположим, что программа попыталась создать nagscreenна основе шаблона диалога при помощи функции DialogBoxParam (или любой другой функции из семейства DialogBox***), и у нее это не получилось. Большинству разработчиков такой вариант обычно даже в голову не приходит – а потому и проверку на существование шаблона nagscreen’а программа обычно не делает, а сразу переходит созданию и выводу окна на экран. Что бы Вы сделали, если бы Вас попросили создать окно на основе «никакого» шаблона? Правильно, абсолютно ничего! Вот и Windowsпоступает точно так же – а именно, ничего не делает. Хотя на самом деле такое «ничего» все-таки может иметь далеко идущие последствия: ни разу не выполнится, к примеру, оконная процедура выдранного с корнем диалога, что, в свою очередь может повлечь за собой труднопредсказуемые эффекты. Вспомните принцип минимального вмешательства – и ужаснитесь тому, что предлагаю Вам содеять: вырвать из программы особо ценный (с точки зрения разработчика, разумеется) ресурс, отключить «не глядя» оконную процедуру, спровоцировать передачу некорректных данных как минимум в одну функцию WinAPI…
Но «суха теория, мой друг…», а потому все же посмотрим, что там у нас выросло на зеленеющем «древе жизни». В качестве наглядного пособия я возьму программу SkyMapPro 8 Demo, которая обладает одним весьма ценным для нас качеством – наличием nagscreen’а, шаблон которого упрятан глубоко в недрах секции ресурсов и имеет идентификатор 3006. В качестве орудия, при помощи которого производилась ампутация надоедливого диалога, я воспользовался программой ResourceHacker (в принципе, подошел бы и любой другой редактор ресурсов). Сама операция заняла не более 15 секунд, после чего исполняемый файл «похудел» на 4 килобайта. После того, как я произвел пробный запуск исправленной программы, оказалось, что nagscreenкак рукой сняло – и мне для этого не потребовались отладчики, дизассемблеры и прочая крэкерская «тяжелая артиллерия»! Видите: все далеко не так страшно, как может показаться. В принципе, если Вы считаете, что выдирание диалога с корнем – слишком уж варварская операция, шаблон можно оставить на месте, лишь заменив его идентификатор на другой, не используемый в программе. Результат будет аналогичный – программа не сможет найти ресурс с измененным идентификатором и не создаст nagscreen. Этот прием даже предпочтительнее – практика показала, что не всегда и не во всех редакторах ресурсов операция удаления проходит успешно.
А ведь есть еще и третий путь избавиться от диалога путем изменения идентификатора! Ничто не мешает исправить идентификатор не в секции ресурсов, а непосредственно в исполняемом коде, в том месте, где этот идентификатор передается в функции WinAPI. Поскольку наш идентификатор имеет довольно редкое значение 3006, он как нельзя лучше подходит для поиска внутри исполняемого файла. Найдя среди всех подходящих значений то, которое отвечает за создание nagscreen’а – замените его каким-нибудь другим, не указывающим ни на один диалог, например – на 12345. Когда Вы запустите исправленное приложение, оно, конечно, попытается найти и загрузить шаблон с ID=12345 – но у программы это все равно не получится. Впрочем, я считаю такой путь излишне изощренным и рассказал о нем главным образом в познавательных целях – гораздо проще забить вызов функции создания диалога и все, что с ней связано, nop’ами и получить тот же самый эффект.
Успешность такого метода борьбы с «лишними» окнами, вообще, сильно зависит от внутренней логики приложения. Nagscreen, к примеру, может оказаться не «пустышкой», предназначенной исключительно для раздражения пользователя, а исполнять какие-либо неочевидные функции. Например, на обработку сообщения WM_INITDIALOGв оконной процедуре nagscreen’а может быть подвешена инициализация каких-либо критически важных для программы структур. В общем случае такая инициализация может происходить при возникновении практически любого события связанного с этим окном: при нажатии на какую-либо кнопку в окне, при закрытии окна, в момент поступления первого сообщения WM_PAINT – перечислять варианты можно очень долго. И если просто удалить nagscreen, это «волшебное» событие не произойдет – следовательно, не будет произведена инициализация, отчего, в итоге, программа будет работать некорректно, если вообще будет работать.
На практике такая защитная техника в чистом виде встречается достаточно редко – ведь программисту, использовавшему подобный прием, придется учитывать при программировании различия в работе между полной/зарегистрированной (без nagscreen’а) и урезанной/незарегистрированной версиями программы. Тем не менее, если Вы все же столкнетесь с реализацией такого алгоритма, Вам вряд ли станет легче от осознания редкости встреченной проблемы. Поэтому давайте подумаем о том, как эту проблему можно было бы решить. В теории решение довольно простое: Вам нужно найти функции, которые выполняют все нужные действия по инициализации, и вызвать их вручную. На практике Вам скорее всего понадобится добраться до цикла выборки сообщений внутри оконной процедуры и проанализировать его содержимое. Обратите также внимание на следующий факт: если Вам удалась обнаружить, что инициализация программы происходит в ответ на WM_INITDIALOG, из этого отнюдь не следует, что Вам необходимо выполнить все действия, которые программа выполняет при поступлении этого сообщения. Посмотрите на следующий пример:
Код (Text):
.IF uMsg==WM_INITDIALOG invoke GetDlgItem, hWnd, TRIALUSE_BTN invoke SetFocus,eax invoke InitProgramПосле того, как мы умозрительно удалили диалог, которому должно было поступать сообщение, попытка установить фокус на кнопку TRIALUSE_BTNстановится совершенно бессмысленна – поскольку больше нет диалога, нет и кнопки которая в этом диалоговом окне находилась. Поэтому в нашем простейшем примере вместо вызова функций создания диалога было бы достаточно просто написать callInitProgram, а все лишние байты забить nop’ами. Однако представьте себе, что оконная процедура содержит какие-либо локальные переменные (например, вычисленное ранее число дней до истечения триального срока), используемые внутри InitProgramили же проделывает какие-либо операции с созданным диалогом – и Вы поймете, почему простейший путь не является лучшим. Что делать в таком случае? Из каждой безвыходной ситуации есть как минимум два выхода. Можно углубиться в дебри процедуры InitProgram и проверить, не обращается ли она к каким-либо объектам, которые мы так лихо снесли вместе с диалогом и потом долго заниматься художественной штопкой машинного кода. Я сам пару раз применял такую технику и могу сказать определенно: этот процесс мне совершенно не понравился. Поэтому мы поступим хитрее – попробуем поискать другое, более простое решение.
Практически в любом nagscreen’е изначально заложен способ от него избавиться. Этим способом может быть клик по кнопке в диалоге, нажатие «горячей клавиши» или же истечение определенного времени с момента появления окна. Все эти случаи объединяет одно - nagscreenждет некоего события, наступление которого станет для него сигналом к исчезновению. «Приемник» этого сигнала, который и выполняет все действия по убиранию nagscreen’а с экрана, чаще всего прячется во все той же оконной процедуре, о которой уже столько раз говорилось и еще не раз будет сказано. Для тех, кто не особо интересовался программированием с использованием «чистого» WinAPI, дам некоторые пояснения относительно традиционного устройства оконной процедуры.
«Сердцем» оконной процедуры, несомненно, является обработка сообщений, поступающих от окна, которая на ассемблере выглядит приблизительно так:
Код (Text):
.IF uMsg==WM_DESTROY invoke PostQuitMessage,NULL .ELSEIF uMsg==WM_INITDIALOG ... .ELSEIF uMsg==WM_COMMAND mov eax,wParam .IF ax==ID1 ... .ELSEIF ax==ID2 ... .ENDIF .ELSE invoke DefWindowProc,hWnd,uMsg,wParam,lParam ret .ENDIFНе вдаваясь в подробности (которые Вы можете найти в документации и в книгах по программированию под Windows), скажу, что обычно все самое интересное - обработка нажатий на кнопки или реакция на выбор пунктов меню - скрывается в блоке, анализирующем сообщение WM_COMMAND. Внутри этого блока обычно выполняется последовательное сравнение параметров сообщения wParamи lParamс идентификаторами и хэндлами управляющих элементов (кнопок, элементов меню, тулбаров и т.п.). Если значения хэндла и идентификатора указывают, что пользователь нажал на некий элемент - программа выполняет соответствующие действия. Допустим, что мы знаем, где располагается оконная процедура интересующего нас окна. Что мы можем сделать с нашей находкой? Самое простое – это, конечно, возможность менять местами функции управляющих элементов: если в приведенном выше примере поменять местами значения ID1 и ID2, соответственно изменятся и функции элементов с идентификаторами ID1 и ID2. Менее очевиден, но тоже достаточно прост для понимания тот факт, что можно «переключить» реакцию программы с одного сообщения на другое. К примеру, заменив в нашем примере WM_INITDIALOGна WM_PAINT, мы заставим процедуру инициализации выполняться не единожды при создании диалога, а при поступлении каждой команды на перерисовку содержимого окна. Зачем такое может понадобиться? С точки зрения среднего программиста это действо совершенно нелогично, нефункционально, и даже более того – опасно. Но крэкинг – это искусство, существующее по ту сторону обычного программирования. И именно благодаря своей «потусторонней» сущности даже во внешне бессмысленных операциях крэкер способен узреть потаенные возможности и раскрыть их к своей пользе.
Итак, представим также, что кнопка ID1 закрывает nagscreen (на самом деле представлять придется только Вам – у меня код этого примера сейчас перед глазами). Наша задача – убрать nagscreenлюбыми доступными средствами. Как можно решить эту задачу? Да очень просто - базовую идею я описал парой строчек выше. Перво-наперво заменим в оконной процедуре константу WM_COMMANDна WM_PAINT. Следующее, что нам нужно – это чтобы оконная процедура реагировала на сообщение WM_PAINTкак на нажатие кнопки с идентификатором ID1. Добиться этого можно, подправив условие IFax==ID1 таким образом, чтобы оно выполнялось в любом случае, независимо от величины wParam. Думаю, проделать такой фокус не составит никакого труда даже для самого начинающего крэкера, только-только научившегося заменять условный переход на безусловный. Если в оконной процедуре присутствует «настоящий» обработчик WM_PAINT, есть смысл при помощи безусловного перехода выполнить и его – ради соблюдения принципа минимального вмешательства. На этом нашу миссию можно считать завершенной – после внесенных исправлений nagscreenбудет закрываться сам собой сразу же после появления. Главная хитрость заключается в том, что как только nagscreen пытается стать видимым (а Вы когда-нибудь видели невидимые nagscreen’ы?), окно этого screen’а автоматически получит команду на перерисовку содержимого клиентской области - сообщение WM_PAINT. Мы же, движимые возвышенными эстетическими чувствами, протестующими против созерцания рекламных банальностей, преобразовали это безобидное сообщение в приказ немедленно убрать раздражающее окно с экрана.
Все, о чем я говорил выше, относится к классическим средствам работы с окнами и диалогами Windows и к объектно-ориентированным «оберткам» для WinAPI. Однако фирма Borlandвнесла в такую консервативную область, как графические интерфейсы Windows-программ, заметное оживление. Я имею в виду прежде всего фирменную борландовскую разработку: VisualComponentLibrary (она же VCL) – объектно-ориентированную библиотеку, предназначенную для создания графического интерфейса. Впервые эта библиотека появилась в Delphi 1 и с тех пор стала неотъемлемой частью всех версий Delphiи C++ Builder. В VCLшироко используются такие нетрадиционные для Windowsвещи, как «безоконные» управляющие элементы (non-windowedcontrols), по сути представляющие собой картинки на экране, и потому напрямую не подчиняющиеся функциям WinAPI. Другим новшеством, внедренным Borland, является специфический формат хранения шаблонов форм – они хранятся как данные типа RCData, причем в качестве идентификаторов используются названия классов этих форм: к примеру, шаблон формы класса TMyFormхранится в ресурсах под именем TMYFORM. Ну и, наконец, VCLотличается от большинства библиотек аналогичной направленности обширной и довольно сложной иерархией классов.
Давайте возьмем какую-нибудь программу на Delphi (я взял одну из своих разработок) и попробуем повторить эксперимент с удалением шаблона какого-нибудь диалога. Открываем файл программы в ResourceHacker, выбираем любую понравившуюся форму – и видим, что ResourceHackerнещадно глючит, пытаясь перевести шаблон из внутреннего борландовского формата в более удобочитаемое нечто. На самом деле ResourceHackerдовольно успешно понимает формат Borland’овских шаблонов – просто я использовал слишком новую версию Delphi, о которой наш редактор ресурсов, увы, ничего не знает. Кстати, если взглянуть на тот же ресурс в шестнадцатиричном редакторе, Вы увидите перед собой малопонятную кучу байт, в которую вкраплены имена компонентов и их свойств, а также текстовые значения. Но мы пока не будем пытаться дешифровать шаблон, а просто удалим его. Удаляем, запускаем, пытаемся вызвать соответствующее окно – и получаем сообщение Resource <имя удаленного нами ресурса> not found. Да, это определенно не то, о чем мы мечтали – nagscreen, конечно, исчез, но окошко с сообщением об ошибке смотрится ничуть не лучше. Увы, так просто от nagscreen’ов в программах на Delphiне избавиться.
Поскольку ResourceHackerпоказал себя не с лучшей стороны, внесем коррективы в наш инструментарий: возьмем eXeScope 6.41 (последняя версия на момент написания данной главы) – эта программа более-менее успешно переваривает шаблоны Delphi 7 с надписями в кодировке Unicode. Если Вы не хотите нарушать ничьих авторских прав хотя бы в процессе обучения, раздобудьте для экспериментов мою программку InqSoftWindowScanner – лицензионное соглашение дает Вам полный карт-бланш на ее ломание в учебных целях. Распакуйте ее UPX’ом, откройте полученный исполняемый файл в eXeScopeи найдите шаблон формы TAboutForm. Вы увидите что-то вроде:
Код (Text):
object AboutForm: TAboutForm Left = 265 Top = 185 BorderIcons = [biSystemMenu] BorderStyle = bsSingle Caption = #1054' '#1087#1088#1086#1075#1088#1072#1084#1084#1077'...' ClientHeight = 192 ClientWidth = 318 ... end …Если Вы раньше программировали на Delphiили C++ Builder, Вы наверняка уже догадались, что это – текст типичного dfm-файла, в котором описан шаблон формы вместе со всеми ее компонентами, как визуальными, так и не очень. Если же Вы не программировали ни на Delphi, ни на C++ Builder – Вам будет непросто разобраться в ломании программ, написанных при помощи этих средств разработки. Однако в этой главе мы будем разбирать достаточно простые вещи, для понимания которых достаточно знания английского языка, основ ООП и знакомства с любым визуальным средством разработки. В конце-концов, тот, кто умеет помещать управляющие элементы на шаблоны диалогов в RadAsm’е, вряд ли испытает какие-либо трудности в понимании того, как бросать точно такие же компоненты на формы в Delphi. Но в общем случае работает совершенно обратное правило: чтобы эффективно исследовать программу, необходимо иметь представление об инструментах, с использованием которых эта программа была написана. То есть, чтобы взломать программу, написанную на С++ Builder, нужно знать характерные особенности С++ Builder.
Одной из таких характерных особенностей, к примеру, является то, что имена классов окон (понятие «класс окна» здесь используется в том же смысле, что и в документации по WindowsAPI) в Delphi/С++ Builderначинаются с буквы Tи совпадают с именами классов форм. Проще говоря, если программа создает окно как экземпляр класса TMyForm, то имя класса окна, возвращаемое WinAPI’шной функцией GetClassName, тоже будет TMyForm. Собственно, InqSoftWindowScannerв качестве объекта для экспериментов я посоветовал не случайно – эта программа, помимо прочих функций, как раз умеет определять имена классов окон. Как Вы помните, имена классов форм в программе совпадают со строковыми идентификаторами шаблонов этих форм в секции ресурсов, поэтому «подглядев» средствами WinAPIимя класса окна, Вы смело можете шерстить секцию ресурсов на предмет наличия ресурса типа RCDATAс соответствующим идентификатором, и, скорее всего, Вы такой ресурс обнаружите.
Строго говоря, использование механизмов наследования в Delphi/C++ Builder позволяет создавать формы, у которых имя класса окна отличается от символьного идентификатора шаблона, но в большинстве программ эта возможность не используется. Да и распознать форму-наследника по dfm-описанию родителя обычно бывает не так уж сложно, поэтому я не буду заострять внимание на этом достаточно специфическом случае.
Итак, что нам показал eXeScope? А показал он описание формы и свойства всех ее компонентов, причем в самом что ни на есть текстовом виде. Вы наверняка уже поняли, что слово objectначинает описание объекта (или компонента, если быть до конца точным), а слово endэто описание завершает. И что строки вроде Left = 265, BorderIcons = [biSystemMenu] или BorderStyle = bsSingle представляют собой ни что иное, как имена свойств компонентов и значения этих свойств, как они были заданы во время разработки приложения. И, что самое приятное, Вы можете не только смотреть на эти свойства, но и редактировать их. Исправить числовые или текстовые значения – дело нехитрое, стираем старое значение, вписываем новое, сохраняем результат, и можно любоваться эффектом. Но вот если понадобится исправить свойство вроде BorderStyle – скорее всего придется заглянуть в документацию по Delphi, чтобы узнать, что такое bsSingleи какие еще значения может принимать это свойство. Пока что все довольно просто – и чем-то напоминает наши эксперименты по редактированию шаблонов диалогов в Калькуляторе.
Впрочем, сказав, что изменить значение свойства – дело нехитрое, я был не совсем прав. Состояние современного инструментария таково, что если пользоваться специализированными программами (тем же eXeScope, например), то эта операция не всегда выполняется корректно. Поэтому на практике мне нередко приходилось пользоваться следующим приемом – оригинальное значение я читал при помощи eXeScope, но вот новые данные вписывал уже в шестнадцатиричном редакторе; о том, как я определял, какие байты и где надо было менять, я думаю, в очередной раз повторяться не стоит. В самых тяжелых случаях можно даже экспортировать шаблон формы в dfm-файл, а затем попытаться при помощи той же версии Delphi/C++ Builderвпихнуть этот dfm-файл в тестовый проект, откомпилировать и затем перенести шаблон формы из тестового проекта на его «родное» место в подопытной программе. При этом, разумеется, придется обеспечить совпадение версий компилятора и, если на форме имеются нестандартные компоненты, раздобыть и установить точно такие же. Как Вы догадываетесь, весь этот процесс весьма хлопотный, и такая игра вряд ли стоит свеч – я подобную «хирургическую операцию» по пересадке ресурсов проделывал лишь единожды, и то в основном в порядке эксперимента. Но вот изучение тестовых примеров перед тем, как начинать править свойства – вещь очень и очень полезная, поскольку помимо чисто практической пользы это помогает лучше понять внутреннюю логику инструментальных средств, при помощи которых создана программа.
После того, как мы убедились в возможности редактировать отдельные свойства компонентов, было бы нелишне узнать, что произойдет в том случае, если удалить описание какого-либо свойства. Как ни странно, если выполнить удаление корректно – то обычно ничего страшного не случается. Хотя, справедливости ради надо отметить, что в некоторых случаях удаление свойства приводит к катастрофическим последствиям – прежде всего, когда удаляемое свойство ссылается на другой компонент. Но, к счастью, такие специфические компоненты чаще всего не имеют никакого отношения в nagscreen’ам и баннерам.
Причина «нечувствительности» программ к исчезновению свойств следующая: в Delphiи С++ Builderсоздание объекта на основе шаблона производится в два этапа. На первом этапе собственно создается объект, а все его свойства и внутренние структуры инициализируются значениями по умолчанию. И лишь на втором этапе новые значения свойств, которые хранятся в шаблоне диалога, помещаются на место значений по умолчанию. Неудивительно и то, что в eXeScopeВы видите у каждого компонента намного меньше свойств, чем видит разработчик, редактируя форму в IDE: для уменьшения объема исполняемого файла в ресурсах сохраняются не все значения свойств, а только те, которые отличаются от значений по умолчанию. Например, в приведенном выше отрывке описания формы AboutFormВы не увидите свойство AutoSize, несмотря на то, что такое свойство у формы имеется. Причина этого проста: значение свойства AutoSizeпо умолчанию было равно false, и автору (то есть мне) не пришлось его менять, поскольку оно вполне меня устраивало.
Итак, теперь мы знаем, что описания свойств удалять можно. Осталось лишь разобраться, зачем это нам может понадобиться. В этот раз я отступлю от принципа «сначала - теория, потом - практика» и начну с описания небольшого эксперимента. Возьмите все тот же InqSoftWindowScanner, запустите и нажмите в нем кнопку «О программе…». Вы увидите окно с информацией о программе, в левой части которого будет находиться логотип. А теперь представьте себе, что это не безобидная картинка, которая никому не мешает, а ужасный черно-зелено-оранжевый баннер размером в пол-экрана, от которого мы хотели бы избавиться.
Следующим шагом будет определение идентификатора шаблона. Запустите вторую копию WindowScanner’а и «просканируйте» окно «О программе…». В очередной раз убедившись, что класс окна носит имя TAboutForm (да, я знаю, что написал про это несчастное окно уже как минимум пять килобайт – но мы будем действовать так, как будто видим программу впервые), лезем в секцию ресурсов и ищем там шаблон формы, спрятавшийся за идентификатором TABOUTFORM. Теперь нам нужно найти в шаблоне формы описание нашего «баннера». Нетрудно догадаться, что в этой роли выступает объект Image1 (поскольку картинка на форме одна, догадаться, что Image1 и есть наш «баннер», нетрудно). Дешифрованное описание этого объекта выглядит следующим образом:
Код (Text):
object Image1: TImage Left = 0 Top = 0 Width = 57 Height = 192 AutoSize = True Picture.Data = { 0A544A504547496D616765F5260000FFD8FFE000104A46494600010200000100 ............ 3EEBFFD9} endТеперь можно сделать то, ради чего мы и затевали все эти поиски: удалите свойство Picture.Data, запустите программу и посмотрите, что случилось с нашей формой. А случилось именно то, чего мы и хотели добиться - логотип исчез, как будто его и не было. Сам объект, разумеется, никуда не делся – мы лишь удалили связанное с ним изображение, «подчистив» свойство Picture. Вспомните, что я говорил о двух этапах создания форм в Delphi - и Вы легко поймете, каким образом изменилась логика работы программы: стерев значение, которое было назначено свойству Picture.Data программистом, Вы не оставили программе иного выхода, как использовать значение этого свойства по умолчанию. А значением по умолчанию в нашем случае оказалось «никакое» изображение (т.е. отсутствие изображения), которое программа успешно отобразила. Более того, удалять можно не только свойства объектов, но и объекты целиком. Однако такое действо является куда более грубым вмешательством в код программы, да и ограничений в этом случае намного больше.
А теперь давайте посмотрим, как простым редактированием ресурсов можно избавиться от настоящего, вполне реального баннера. Для этого нам потребуется программа GhostInstallerFreeEdition. XML-редактор, входящий в состав этой программы, как раз содержит баннер. И этот баннер, кроме того, что занимает довольно много места внизу экрана и сокращает поле редактирования, еще и постоянно мерцает, раздражая пользователя. Так что если Вы активно пользуетесь бесплатной редакцией GhostInstaller’а, Вам придется либо проявлять чудеса терпеливости, либо спасти огромное количество нервных клеток, избавившись от созерцания зловредного баннера.
Сначала при помощи InqSoftWindowScannerопределим имя класса окна той формы, которую мы собираемся править: TfrmProjectSource. Заодно будет нелишне просканировать и сам баннер – вдруг это позволит нам узнать хотя бы тип компонента, на основе которого этот баннер был создан. А если нам удастся узнать тип компонента, это заметно облегчит задачу поиска самого компонента в дешифрованном шаблоне формы. Пробуем просканировать окно баннера, и узнаем, что на самом деле наш баннер – ни что иное, как кусок InternetExplorer’а, засунутый в программу при помощи технологии ActiveX. Чтож, с первого захода мы получили явно недостаточно полезной информации. Поэтому попробуем копнуть глубже и просканить всю ветку дерева окон, имеющую отношение к нашему баннеру. InqSoft Window Scanner выдал следующий результат:
Код (Text):
+[00050356] {TPanel} *[00010414] {TElSplitter} +[00050370] {TPanel} +[0001040E] {TPanel} +[00010410] Panel1 {TPanel} +[0043036A] {Shell Embedding} +[000103D4] {Shell DocObject View} *[00010412] {Internet Explorer_Server}Что это значит? Всего лишь то, что наш ошметок InternetExplorer’а лежит на компоненте Panel1 типа TPanel (тут программист явно поленился стереть свойство Captionу объекта Panel1, чем сильно облегчил нам задачу поиска нужного объекта). Сам объект Panel1 в свою очередь лежит на другой панели, которая лежит на панели, которая… В общем, всяких панелей там много, и разбираться в них можно долго. Поэтому я предлагаю начать наше расследование с той панели, которую мы идентифицировали однозначно. Откроем файл GIEditor.exeв Restorator 2.52 (да, Вам опять придется обновить инструментарий – практика показала, что eXeScopeс редактированием шаблона в данной программе не справился, а вот Restoratorсработал как нельзя лучше), выберем шаблон нашей формы и в этом шаблоне найдем следующее:
Код (Text):
object panMainBanner: TPanel Left = 0 Top = 287 Width = 553 Height = 126 Align = alBottom BevelOuter = bvNone TabOrder = 1 object Panel1: TPanel Left = 0 Top = 4 Width = 553 Height = 122 Align = alBottom BevelOuter = bvLowered Caption = 'Panel1' TabOrder = 0 object webMainBanner: TEmbeddedWB Left = 1 Top = 1 Width = 551 Height = 120 Align = alClient ... end end endКак видно из приведенного куска текста, это и есть описание нашей Panel1, баннера, который на этой панели находится (как оказалось, он представлен объектом webMainBanner типа TEmbeddedWB) и некой панели panMainBanner, на которой лежит Panel1. Было бы логично предположить, что имя panMainBannerрасшифровывается как panelforMainBanner («панель для главного баннера»), и что если ликвидировать эту панель вместе со всем ее содержимым, то назойливое мерцание баннера более не будет смущать наш взор. Сказано – сделано, переходим в режим редактирования и удаляем вышеприведенный блок текста из ресурса. Сохраняем, запускаем – и получаем GPF. Чтож, как я и предупреждал, безоглядное удаление компонентов – операция отнюдь не безобидная. Поэтому давайте поищем другой путь.
В самом начале главы я выделил четыре подхода к проблеме, и мы только что испробовали тот из них, который стоит под номером два. Это, конечно, было несколько непоследовательно с моей стороны, но иначе Вы бы не смогли поэкспериментировать с удалением объектов и увидеть, к чему это иногда приводит. И вот пришло время пойти правильным путем. Действительно – это ведь всего лишь баннер, он не выпрыгивает в самый неподходящий момент на передний план, не блокирует работу с программой, даже не требует, чтобы по нему кликали. Так что нам нет принципиальной необходимости совершенно изничтожать этот баннер –достаточно просто его не видеть. В нашем случае, кстати, придется убрать не только баннер, но и панели, на которых он расположен, чтобы они не занимали место, и самый простой способ сделать это – уменьшить высоту панелей до нуля. Если быть до конца точным – нам надо спрятать лишь панель panMainBanner, поскольку компоненты webMainBannerи Panel1 находятся внутри этой панели и после «исчезновения» panMainBannerтоже не будут видимы (в общем случае этому могло бы помешать свойство AutoSize=true, но у наших подопытных компонентов это свойство равно false). В Restorator’е исправляем строчку Height=126 на Height=0, сохраняем результат и запускаем многострадальную программу. Баннера больше нет!
Читая эту главу, Вы, наверное, отметили, что я меньше, чем обычно, говорил про общие подходы к проблеме рекламных окон. Действительно, чем более частные вопросы крэкинга мы рассматриваем, тем сильнее приходится «привязываться» к особенностям операционных систем и средств разработки. Но даже в этом случае «за бортом» моего повествования осталось очень многое: визуальные средства для разработки под ДОС (да, были и такие – и некоторые из них, вроде старых версий FoxPro, все еще используются), использование шаблонов диалогов в VisualBasic/VBA и т.д. Вы сами, при желании, сможете узнать об этих вещах через самостоятельные эксперименты ничуть не меньше, чем я мог бы Вам сообщить. А поскольку сама идея хранить шаблоны интерфейсов программ в унифицированном виде давно и прочно вошла в практику программирования, было бы наивным предполагать, что новые средства разработки не принесут с собой новых форматов хранения этих шаблонов. Но, я надеюсь, и в этом случае Вы сможете извлечь пользу из информации, изложенной в этой главе – разумеется, не как из руководства по конкретным инструментам, а как из источника идей.
© CyberManiac
Теоретические основы крэкинга: Глава 7. Искусство разбивать окна.
Дата публикации 10 сен 2004