Теоретические основы крэкинга

Вид материалаДокументы
Создание и уничтожение окон
Чтение и изменение текстов окон
Изменение видимости, позиции и прочих подобных свойств окна
Загрузка ресурсов
Отображение изображений и текстов на экране
Работа с файлами
Операции с реестром
Чтение и запись INI-файлов
Работа с датой и временем
Процессы и потоки: создание и управление
Загрузка и выгрузка DLL
Подобный материал:
1   ...   4   5   6   7   8   9   10   11   ...   14

Упрощенный вывод сообщений

MessageBox, MessageBoxEx, MessageBoxIndirect, MessageBeep (эта функция не выводит сообщение, а только издает соответствующий звуковой сигнал)


Создание и уничтожение окон

CreateWindow (наиболее популярная функция создания окон), CreateWindowEx

CloseWindow (функция закрытия окна), DestroyWindow


Создание и уничтожение диалогов

CreateDialog, CreateDialogIndirect, CreateDialogIndirectParam, CreateDialogParam (эти функции только создают диалог)

DialogBox, DialogBoxIndirect, DialogBoxIndirectParam, DialogBoxParam (эта группа функций позволяет создавать модальные диалоги; управление не возвращается программе, пока диалог не будет закрыт)

EndDialog, DestroyWindow


Чтение и изменение текстов окон

GetWindowText, GetDlgItemText (чтение текста окна или элемента диалога)

GetWindowTextLength (чтение длины текста окна)

GetDlgItemInt (чтение текста элемента диалога как 32-битного числа)

SetWindowText, SetDlgItemText, SetDlgItemInt (установка нового текста окна или элемента диалога)


Изменение видимости, позиции и прочих подобных свойств окна

EnableWindow (активация/деактивация окна)

ShowWindow, ShowWindowAsync (изменение видимости и состояния окна, в частности – позволяет минимизировать или наоборот развернуть окно)

SetWindowPos, MoveWindow (изменение положения окна)

SetWindowWord (устаревшая и практически не используемая функция), SetWindowLong – две функции, позволяющие модифицировать весьма широкий спектр параметров окна. GetWindowWord, GetWindowLong – функции чтения этих параметров.


Загрузка ресурсов

LoadImage (универсальная функция загрузки изображений, иконок и курсоров), LoadBitmap, LoadIcon, LoadCursor

FindResource, FindResourceEx, LoadResource (функции загрузки ресурсов любого типа в память)


Отображение изображений и текстов на экране

BitBlt, StretchBlt, MaskBlt (функции копирования BITMAP’ов на экран)

DrawText, TextOut, TabbedTextOut (обычный вывод текста)

GrayString (редко используемая функция, выводит на экран строку со стилем надписи на неактивном управляющем элементе)


Работа с файлами

OpenFile (устаревшая функция), CreateFile (основная функция открытия файлов; несмотря на свое название способна открывать файлы и даже директории)

ReadFile, ReadFileEx (функции чтения из файлов)

WriteFile, WriteFileEx (функции записи в файл)

SetFilePointer (перемещение по файлу)

GetFileTime, GetFileAttributes, SetFileTime, SetFileAttributes (чтение и модификация времени создания и атрибутов файлов/директорий)

SetEndOfFile (изменение размеров файла)


Операции с реестром

RegOpenKey, RegOpenKeyEx, RegCreateKey, RegCreateKeyEx (открытие и создание ключей реестра)

RegQueryInfoKey (запрос информации о ключе, в частности – для проверки факта существования подключа)

RegQueryValue,RegQueryValueEx (чтение значений из реестра)

RegSetValue, RegSetValueEx (запись ключей в реестр)

RegCloseKey (закрытие ключа реестра)


Чтение и запись INI-файлов

GetProfileSection, WriteProfileSection, GetProfileInt, GetProfileString, WriteProfileString, WriteProfileInt (функции для работы с файлом Win.ini, в настоящее время считаются устаревшими, но иногда используются)

GetPrivateProfileSection, GetPrivateProfileSectionNames, WritePrivateProfileSection, GetPrivateProfileInt, GetPrivateProfileString, GetPrivateProfileStruct, WritePrivateProfileString, WritePrivateProfileInt, WritePrivateProfileStruct (функции работы с областью реестра, отведенной для хранения настроек программ, либо с произвольным INI-файлом; эта группа функций считается устаревшей)


Работа с датой и временем

GetSystemTime, GetLocalTime, GetSystemTimeAsFileTime (чтение текущего времени)

SetSystemTime, SetLocalTime (установка нового времени)

LocalTimeToFileTime, FileTimeToLocalTime, FileTimeToSystemTime, SystemTimeToFileTime (преобразование формата времени)

CompareFileTime (сравнение двух переменных, хранящих время)

GetFileTime, SetFileTime (запись и чтение времени создания, последней модификации и последнего доступа к файлу)


Процессы и потоки: создание и управление

WinExec (устаревшая функция запуска исполняемых файлов), CreateProcess (функция, обычно используемая для запуска исполняемых файлов), ShellExecute, ShellExecuteEx (пара «альтернативных» функций для запуска приложений (применительно к исполняемым файлам) или открытия, печати и т.п. папок и документов).

ExitProcess («стандартное» завершение процесса, эта функция способна завершить только тот процесс, внутри которого она вызвана), TerminateProcess (принудительное завершение процесса; эта функция способна «убить» любой процесс (в NT – при наличии соответствующих привилегий), что иногда используется защитами для подавления крэкерского софта)

CreateThread (штатная функция создания нового потока), CreateRemoteThread (эта функция «живет» только под NT-подобными и на самом деле в защитах практически не используется. Зато очень, очень (я не забыл сказать «очень»?) широко используется самими крэкерами для внедрения в чужой процесс. Так что обойти ее вниманием в этом поминальнике было бы несправедливо)

ExitThread, TerminateThread (штатное завершение и аварийное уничтожение потоков соответственно)


Загрузка и выгрузка DLL

LoadLibrary, LoadLibraryEx (функции загрузки динамических библиотек)

LoadModule (устаревшая функция загрузки DLL)

GetProcAddress (функция, возвращающая адрес функции или переменной, объявленной в экспорте DLL, по имени этой функции/переменной (разумеется, соответствующая DLL должна быть подгружена текущим процессом). Эта функция широко используется как в защитах чтобы вызов какой-либо функции не «светился» в дизассемблированном листинге, а также для приемов типа push <желаемый адрес возврата>; jmp <адрес функции, полученный через GetProcAddress>, используемых для сокрытия точки, откуда была вызвана функция)

FreeLibrary (функция принудительной выгрузки DLL)


Надо сказать, список этот отнюдь не полный: в нем нет, к примеру, функций выделения блоков памяти (извлечь практическую пользу из информации о распределении памяти удается довольно редко, и те, области, где это актуально, отнюдь не относятся к «основам» крэкинга) и функций работы со строками (эти функции почти всегда дублируются в коде программы ради повышения быстродействия). Нет в этом списке и специфических библиотечных функций Microsoft’овской MFC и Borland’овских VCL/RTL – если включить в список и их, «поминальник» стал бы совершенно неподъемным. Все это мы оставим за рамками – тем более, что средства разработки не стоят на месте, и, возможно, через пару лет будут актуальны совершенно другие API. Попробуйте понять, из чего я исходил, составляя этот «поминальник» - и, поняв это, Вам станет заметно легче найти подход к ранее неизвестной системе программных интерфейсов. Я выбрал WinAPI лишь по той причине, что это – «основа основ» программирования под платформу Win32, которая в настоящее время наиболее распространена. Однако даже к изложенному выше стоит относиться с известной долей осторожности – поскольку линейка Windows 9x угасает, в ближайшем будущем вполне могут стать актуальными специфические для линейки NT системные вызовы, а Вы будете искать правды среди стандартных функций Win32 – и не найдете ее. Прецедент уже имел место – в многих крэкерских руководствах пятилетней давности рекомендовалось для гарантированного «отлова» считывания текста из окна устанавливать брейкпойнт на функцию hmemcpy. И это работало – но только под Windows 9x, поскольку в линейке NT вместо вызова этой функции для повышения скорости использовались inline-вставки. До тех пор, пока все семейство NT-подобных ограничивалось доисторической NT 3.51 и неудобоваримой NT 4, проблемы как бы не существовало. Но вот на наши десктопы пришли более симпатичные Windows 2000 и Windows XP – и противоречие между старыми руководствами и наблюдаемой реальностью встало в полный рост.


Как Вы можете видеть, имена функций являются сокращенным описанием тех действий, которые эти функции выполняют. Таким образом, если Вы предполагаете существование некой функции WinAPI и хотите поставить на нее брейкпойнт, но не знаете (или просто забыли), как она называется, Вы можете легко это узнать, если владеете принятой программистами под Win32 терминологией и английским языком (или хотя бы русско-английским словарем). Кроме того, если всем другим отладчикам Вы предпочитаете SoftIce, у Вас есть возможность воспользоваться командой EXP <текстовая_строка>, которая позволяет вывести на консоль отладчика все функции, имена которых начинаются с указанной Вами строки. Например, команда EXP MessageBox покажет Вам все MessageBox’ы, какие только встречаются в символьной информации, импортированной из DLL (список DLL для импортирования информации должен быть заранее прописан в инициализационном файле отладчика). Надо отметить, что команда EXP имеет несколько более широкие возможности, чем простой вывод списка функций, начинающихся с определенных символов – Вы можете также просмотреть список модулей, по которым имеется символьная информация, проверить, присутствует ли нужная функция в некотором модуле и многое другое, о чем можно прочесть в руководстве по этому отладчику.


Вы наверняка заметили, что в «поминальнике» отсутствуют функции MessageBoxA и MessageBoxExA, о которых я упоминал в начале статьи, а вместо них описаны лишь MessageBox, MessageBoxEx. Разумеется, это не опечатка. Если Вы уже попробовали «на вкус» SoftIce’овую команду EXP и приложили ее к нашему измученному брейкпойнтами (то ли еще будет!) MessageBox’у, то наверняка заметили, что в списке функций, экспортируемых из USER32.DLL присутствуют MessageBoxA и MessageBoxW (а вот просто MessageBox’а нет и в помине). Откуда же взялись буквы A и W в именах функций и что они вообще значат?


Расшифровка этих букв проста: A – это ANSI, W – это WIDE. А появились эти буквы после того, как в Microsoft взяли курс на внедрение кодировки UNICODE, и возникла необходимость как-то отличать «старые» варианты функций, работающие с традиционными текстовыми строками, от «новых», использующих кодировку UNICODE. А символы в UNICODE имеют тип WСHAR длиной в 16 бит против обычных восьми – поэтому к именам функций добавили букву W, а не U, как можно было бы предположить. Вот и получается, что если функция принимает или передает строковые параметры, программист должен указать, какой из вариантов функции нужно использовать. Разумеется, если функция WinAPI не получает и не возвращает никаких символьных параметров, буквы A и W ей совершенно ни к чему. Так что единственная причина того, что в моем «поминальнике» начисто отсутствуют упоминания об ANSI- и UNICODE-вариантах одной и той же функции весьма банальна. Если бы я подробно расписывал оба варианта имени каждой функции, мне бы пришлось набирать этот список почти в два раза дольше, а Вам – во столько же раз дольше его читать, и при этом Вы бы не получили никакой новой информации. Поэтому я и решил не упражняться в крючкотворстве, а вместо этого объяснить причины использования окончаний A и W. Надеюсь, что после моих объяснений Вы сможете подставить нужные буквы в имена функций самостоятельно.


Вряд ли Вам пригодится на практике эта информация, однако стремление к истине обязывает меня сообщить Вам великую тайну. Знайте, что все-таки они существуют – я имею ввиду MessageBox’ы без буквы A или W. Ибо давным-давно, когда на Земле жили 16-разрядные динозавры Windows 3.1 и Windows for Workgroups, скрывавшиеся в недрах этих ОС функции ничего не ведали ни об ANSI, ни об UNICODE – а потому не нуждались в буквах A и W. И от тех древних времен в include-файлах все еще сохранились строчки вроде lstrcmp equ - в целях совместимости и упрощения переноса старых исходников под новые компиляторы.


Практическое использование этого списка функций выглядит очень просто. Предположим, что у Вас есть некая программа, и на этапе первоначального сбора информации Вы узнали, что она работает 30 дней, выводит при запуске MessageBox со счетчиком дней до окончания триального срока, и, если триальный срок закончился, после нажатия кнопки OK завершает программу. Дальше Вы можете рассуждать примерно следующим образом:

- Если программа проверяет, сколько дней она работает – весьма вероятно, что она считывает текущую дату. Поэтому смотрим в раздел «Работа с датой и временем» и выбираем оттуда функции, предназначенные для чтения времени.

- Программа показывает MessageBox – следовательно, есть смысл поставить точки останова на функции создания MessageBox’ов.

- При истечении триального срока программа завершается – значит можно попытаться отловить вызов функции ExitProcess.

После расстановки брейкпойнтов на упомянутые функции и запуска программы отладчик скорее всего всплывет где-то внутри той ветки программы, которая ответственна за появление nag screen’а и проверку триала. После этого Вы можете ликвидировать сам nag screen (просто забив nop’ами все, что относится к вызову функции, создающей MessageBox) а затем методом обратной трассировки в уме (о сути этого метода я подробно расскажу чуть позже) добраться от точки вызова ExitProcess до условия, по которому программа определяет, что триальный срок еще не кончился (и продолжает работать) или уже истек (и тогда вызывается ветка с ExitProcess). После этого, внеся исправления в исполняемый файл, Вы сможете организовать себе «вечный триал».


Практика показала, что «отловить» появление nag screen’ов проще всего, отслеживая при помощи точек останова следующие типы событий:
  1. Появление MessageBox’ов любых типов
  2. Создание диалогов на основе ресурсов
  3. Вызовы функций отображения окон (успешно работает только в том случае, если окон сравнительно немного – иначе становится сложно отследить среди всех вызовов соответствующих функций те, которые создают нужное окно)
  4. Считывание текущего времени (в тех случаях, когда программа имеет ограничение по времени использования, и nag screen содержит информацию о том, когда истечет испытательный срок)
  5. Создание таймеров (например, для активации управляющего элемента)
  6. Изменение состояния окон Windows (например, активация кнопки или помещение в заголовок окна какой-либо надписи)
  7. Вызовы вспомогательных функций, использующихся для изменения текста окон (к примеру, если в заголовке nag screen’а присутствует надпись «Осталось N дней пробного периода», можно предположить, что для подстановки числа дней в надпись может использоваться функция wsprintf).

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


Теперь Вам, пожалуй, известно о функциях WinAPI достаточно, чтобы приступить к рассмотрению примеров, демонстрирующих, как эти знания применить для решения практическим задачам крэкинга. Так что в порядке эксперимента давайте немного взломаем какую-нибудь распространенную программу, например, Notepad. Это будет довольно несложный, но весьма поучительный эксперимент, демонстрирующий одну из техник ликвидации nag screen’ов в «настоящих» программах. Итак, возьмем обыкновенный Блокнот (я взял тот, который входит в состав Windows XP), скопируем его в надежное место, чтобы в случае чего можно было восстановить файл из резервной копии (вообще, возьмите себе за правило каждый успешный шаг отмечать резервными копиями файлов, дампами успешно модифицированных кусков и т.д. – очень неприятно бывает терять из-за ошибок результаты длительной и кропотливой работы) и запустим его. Теперь нажмите в Блокноте Ctrl-F (оно же «Правка|Найти…»), легонько стукните по клавиатуре левой пяткой (или просто наберите любой текст) и нажмите кнопку «Найти далее». Поскольку нашего «любого» текста в поле редактирования нет (потому что там вообще ничего нет), Блокнот ругнется сообщением вроде «Не удается найти "340t80543t"». Будем считать, что это и есть nag screen, который нам предстоит ликвидировать.


Очевидно, что этот «nag screen» очень сильно похож на MessageBox – поэтому вполне логичным было бы начать поиск нашего окошка с установки брейкпойнта на функции вывода стандартных окон с сообщениями (MessageBoxA, MessageBoxW, MessageBoxExA – и далее по списку). Грузим подопытную программу в отладчик (я в качестве отладчика буду использовать OllyDebug, но те, кто предпочитает SoftIce, тоже смогут без всяких сложностей повторить этот эксперимент) и устанавливаем точки останова. В SoftIce это делается очень просто: «BPX имя_функции» - и так много-много раз, для всех подходящих функций из «поминальника». В OllyDebug эта операция выполняется несколько сложнее, зато Вы получите весьма неожиданный, но очень приятный сюрприз. Но приятные сюрпризы будут позже, а пока щелкните правой кнопкой мыши в окне кода и вызовите всплывающее меню. В этом меню вызовите пункт «Search for > Name (label) in current module» - этот пункт заставляет отладчик пробежаться по загруженному модулю (в нашем случае – по EXE’шнику Блокнота) и найти в нем все ссылки на «именованные» адреса (в частности, такими адресами являются вызовы WinAPI). Отладчик покажет окно с весьма немаленьким списком функций, на которые удалось найти ссылки в коде программы, и теперь в этом списке нам нужно найти наши МessageBox’ы. А вот и наступило время для обещанного приятного сюрприза: в списке нашлась только одна подходящая функция из «поминальника» - MessageBoxW. Так что в то время, как счастливые обладатели SoftIce сбивали в кровь руки, разбрасывая брейкпойнты на функции, которые вообще не используются в программе, пользователи OllyDebug без всяких усилий получили информацию о том, с какого вызова API лучше всего начать подбираться к нашему nag screen’у. Пользователям же SoftIce чтобы получить список вызываемых программой функций API придется воспользоваться каким-нибудь дополнительным софтом, позволяющим просмотреть список импортируемых функций (благо таких программ немало).


В SoftIce брейкпойнт на функцию ставится элементарно: BPX MessageBoxW и никаких гвоздей; в OllyDebug эта операция немного сложнее: найдя нужную строчку в списке импортированных функций, щелкните правой кнопкой мыши и в всплывшем меню выберите пункт «Follow import in disassembler». После этого отладчик мгновенно перенесет Вас в глубины одной из важнейших системных библиотек операционной системы прямо к первому байту функции MessageBoxW. После этого останется лишь нажать клавишу F2 и увидеть, как начальный адрес этой функции окрасится в красный цвет. На этом подготовительные операции закончены – пора отпустить программу на волю и начать охоту на наш «nag screen».


Попробовав опять заставить Блокнот искать «то, не знаю что», мы с радостью видим, что наша точка останова, о необходимости которой я так долго говорил, наконец-то выполнила свою задачу и «тормознула» программу, как только та попыталась выполнить функцию MessageBoxW. В окне отладчика Вы увидите что-то вроде этого:


77D70956 > 6A 00 push 0

77D70958 . FF7424 14 push dword ptr ss:[esp+14]

77D7095C . FF7424 14 push dword ptr ss:[esp+14] ; |Title = "????"

77D70960 . FF7424 14 push dword ptr ss:[esp+14] ; |Text = "????"

77D70964 . FF7424 14 push dword ptr ss:[esp+14] ; |hOwner = 00010015

77D70968 . E8 03000000 call USER32.MessageBoxExW ; \MessageBoxExW

77D7096D . C2 1000 retn 10


И это есть ни что иное, как код функции MessageBoxW с комментариями, которые в него подставил OllyDebug (поскольку SoftIce никаких комментариев, кроме заранее определенных пользователем, подставлять не умеет, пользователям Айса придется немного напрячь воображение). При помощи кнопки F8 начинаем трассировать этот код («Ура! Мы отлаживаем ядро!») без захода в процедуры, и, дотрассировав до адреса 77D70968 с удивлением видим, что отладчик остановился, зато на экране появился наш MessageBox. А это значит, что наше исходное предположение насчет MessageBox’овой сущности «nag screen’а» было верно. Закроем этот MessageBox и продолжим трассировать до тех пор, пока не выйдем из процедуры по команде retn 10.


Пока Вы продавливаете до пола несчастную клавишу F8, я сделаю небольшое лирическое отступление. Вы наверняка уже прочувствовали, что много раз подряд нажимать F8 довольно скучно. А ведь MessageBoxW - это далеко не самая длинная процедура, нередко встречаются шедевры, в дизассемблированном виде занимающие десятки и сотни строк! И у Вас возник естественный вопрос – «неужели в таком большом и сложном отладчике нет какой-нибудь маленькой кнопки, чтобы сразу добраться до точки выхода из процедуры». Не подумайте о разработчиках плохого - они позаботились о нас, поэтому столь нужная кнопка в обеих отладчиках есть! В OllyDebug эта «кнопка» (она находится не только на панели инструментов, но и продублирована соответствующим пунктом меню) называется «Execute till return» и вызывается комбинацией Ctrl-F9. Заодно обратите внимание и на пункт меню «Execute till user code»: если Вы когда-нибудь заплутаете в недрах чужой DLL и захотите одним махом выполнить весь код до того знаменательного момента, когда управление вернется в основную программу – смело жмите Alt-F9, и OllyDebug Вам поможет. В SoftIce для того, чтобы добраться до точки выхода из функции, служит команда