Главная Случайная страница Контакты | Мы поможем в написании вашей работы! | ||
|
Обратим внимание на подчеркнутую строку. Насколько же с первого взгляда неочевидно, куда указывает указатель еах! Попутно замечу, что даже сегодня не каждый компилятор способен генерировать такой код, ценность которого заключается практически в экономии всего одного регистра ebp. Но читатель должен быть готов, что со временем этому "научатся" все компиляторы и все операции с локальными переменными придется отслеживать указанным выше образом. К счастью, дизассемблеры не отстают в этом от компиляторов, и уже IDA 3.8b прекрасно справляется с этой задачей. Можно даже не задумываться о том, что, собственно, происходит у него "внутри". В том-то и беда, что новые технологии могут освобождать от глубокого понимания предмета. Между тем такой подход делает человека зависимым от окружающих его инструментов. Без них он становится беззащитен перед дикой природой.
Далеко не всегда под руками оказывается последняя версия мощного дизассемблера или другого необходимого инструмента. Обычный программист в такой ситуации просто мычит и беспомощно разводит руками. Хороший хакер не почувствует в этой ситуации дискомфорта. Глубокие знания и привычка делать все самому, своими руками, не доверяя машинам, дают навыки, которые позволяют совершенно "без ничего", в прямом смысле этого слова, ломать программу используя лишь то, что имеется в распоряжении, даже если для этого приходится дизассемблировать код в уме.
Поэтому, стремясь показать, как происходит обращение к локальным переменным, я начал объяснение с версии 3.6, не "умеющей" автоматически отслеживать изменение esp. Разумеется, это не повод для обязательного ее использования. Впрочем, нет оснований и отказываться от нее. Лично мне старая, проверенная и устойчивая версия ближе, чем неустойчивая и непривычная новая 3.8Ь. В то же время встроенный язык позволяет неограниченно расширять возможности дизассемблера и включать в него все новые технологии и достижения на свой вкус.
Использование последней версии IDA выгодно еще и тем, что позволяет получить символьные имена всех используемых функций в популярных библиотеках по их сигнатурам. Так, в вышеприведенном примере трудно понять, что же делает функция в строке 0х040110Е. IDA 3.8 уверенно распознает эту функцию как CString::operator=(char const *). Следовательно, введенный пользователем пароль заносится в переменную типа CString, хранящуюся по адресу [esp+10h]. Само собой, теперь необходимо ожидать процедуру сравнения. Более того, мы можем предположить, что она будет одним из методов CString!
004010DE mov еЬх, ds: _mbscmp
……………………………………………………………….
0040110Е call j_??^CString@[3QAEABV013PBD@Z
00401113 mov eax, [eax]
00401115 push 403040h
004 0111A push e ах
0040111B call ebx
0040111D add esp, 8
00401120 test eax, eax
00401122 mov eax, ds:?cout@std@@3V?$basic_ostream@DU?..
00401127 push eax
00401128 jz short loc_401144
Функция _mbscrnp, как следует из ее названия, сравнивает строки и имеет очевидный прототип mt _mbscrnp(const char *strmgl, const char *string2). В строке 0х0401113 мы получим указатель на новую переменную CString, но что же тогда представляет собой число 0х403040? Очевидно, что это указатель на эталонную строку. Посмотрим, что находится по указанному смещению:
00403040 aKpnc db 'KPNC',0
Как нетрудно догадаться, это и есть тот пароль, которого от нас ожидает программа! Для того чтобы убедиться в этом, посмотрим, как используется результат сравнения. Нет ничего проще для разработчика защиты, чем подсунуть нам "ложный" пароль, который, вместо того чтобы запустить программу, наоборот, удалит се с диска или, что еще хуже, отформатирует сам диск. Конечно, такие приемы не относятся к числу красивых и не так уж часто встречаются, чтобы заставить нас принимать эту угрозу в расчет, но анализ использования результатов сравнения позволит нам изменить код так, чтобы программа вообще не спрашивала пароль или, на худой конец, просто воспринимала любой как правильный.
Для этого в очередной раз обратимся к MSDN, где узнаем, что функция _mbscrnp возвращает false (ноль), если строки идентичны, и true в противном случае. Если бы над кодом не поработал оптимизатор, то можно было бы ожидать непосредственно после CALL-a примерно следующую конструкцию:
CALL хxхх
TEST ЕАХ,ЕАХ
JZ xxxx [JHZ ххх]
Однако оптимизатор расположил команды в несколько ином порядке — так, чтобы они выполнялись за меньшее число тактов. К сожалению, в ущерб читабельности. Условный переход находится на четыре команды ниже в строке 0х0401128. TEST ЕАХ.ЕАХ устанавливает флаг Zero в том случае, когда ЕАХ == 0. Следовательно, переход JZ выполняется только тогда, когда сравниваемые строки идентичны. Думаю, что читатель сможет с удовольствием удостовериться, что код в ветке 1ос_401144 выводит "Password OK" и в законченном (а не демонстрационном) приложении продолжает нормальное выполнение программы.
Что будет, если мы заменим условный переход JZ на безусловный JMP? Тогда независимо от результатов сравнения (а следовательно, и введенной строки) программа будет воспринимать любой пароль как правильный!
IDA 3.6 не может записывать отпаченный РЕ-файл, поэтому нам придется этим заняться самостоятельно. Для этого нужно найти в файле тот же фрагмент, что мы видим в дизассемблере. HIEW позволяет искать непосредственно ассемблерные инструкции, облегчая взломщикам жизнь, но мы пойдем другим путем. Гораздо надежнее искать hex-последовательность, которую включает интересующий нас фрагмент. Для этого переключим IDA в режим показа опкода инструкций.
Строго говоря, теперь нам предстоит выбрать сигнатуру, т.е. по возможности короткую, но уникальную последовательность, которая повторяется в файле только один раз. Разумеется последовательности jz ххх (0х74 Ох1А) окажется скорее всего недостаточно, поскольку ожидается, что она может встретиться более чем в одном контексте. Практика показывает, что обычно требуется последовательность не менее чем из трех инструкций. Конечно, чем короче файл, тем меньше вероятности ложных срабатываний. Давайте ограничимся всего двумя командами — push eax\jz ххх. Запишем на бумажку (или запомним) их опкод— 50 74 1A.
Теперь запускаем HIEW, переводим его в hex-режим просмотра и пытаемся найти эту последовательность. Если все сделано правильно, то мы обнаружим ее по адресу 0х0401127. Удостоверимся, что это действительно единственное вхождение и больше совпадений нет. Если же в файле присутствует более одной строки, то возвращаемся в IDA и записываем более длинную последовательность. Впрочем иногда (чем больше опыта, тем чаще) можно определить. какие варианты оказались ложными, "на глаз" сравнив код в этом месте с тем фрагментом, что мы видели в дизассемблере.
Я умышленно не предупредил, что нужно сделать резервную копию файла. Собственно, если есть дистрибутив (а он, как правило, есть всегда), то потеря файла в результате простой человеческой ошибки неопасна. В противном случае резервную копию делать просто необходимо.
Так или иначе, пришло время немного "похулиганить" и изменить ту заветную пару байт, которая мешает нелегальным пользователям (а так же всем легальным, но забывшим пароль) получить доступ к программе. Как уже было показано выше, изменение условного перехода на безусловный приведет к тому, что программа будет воспринимать любой пароль как правильный. Опкод команды JMP SHORT — ОхЕВ. Узнать это можно из руководства Intel по микропроцессорам 80х86. Впрочем, HIEW позволяет обойтись и без этого. Достаточно перейти в режим ассемблера и ввести jmps с тем же адресом перехода, что и J2. Сохраняем проделанные изменения и выходим.
Запустим программу и попробуем ввести любое слово (желательно из нормативной лексики), пришедшее нам на ум. Если мы все сделали правильно, то на экране появится "Password OK". Если же программа зависла, значит, мы где-то допустили ошибку. Восстановим программу с резервной копии и повторим все сначала.
Если же взлом прошел успешно, то можно попробовать придумать какую-нибудь шутку. Мне в голову пришло целых две, о которых я и расскажу ниже, надеясь, что бурная фантазия читателей не останется безучастной, а предложит что-нибудь еще, гораздо более оригинальное.
Подумаем, что будет, если заменить JZ на JNZ? Ветви программы поменяются местами! Теперь, если будет введен неправильный пароль, то система воспримет его как истинный, а легальный пользователь, вводя настоящий пароль, с удивлением прочитает сообщение об ошибке.
Часто кракеры любят оставлять во взломанной программе свои лозунги или (с позволения сказать) "копирайты". Модификация подобного рода в откомпилированных исполняемых файлах довольно трудна и требует навыков, которыми вряд ли обладает начинающий. Но ведь оставить свою подпись так хочется! Для подобной операции можно использовать уже не нужный во взломанной программе фрагмент, выводящий сообщение о неверно набранном пароле. Вспомним, как расположены ветки в исполняемом файле-
Ввод и сравнение пароля |
JZ Password ok |
•НЕВЕРНЫЙ ПАРОЛЬ' |
JMP enter&compare |
"ВЕРНЫЙ ПАРОЛЬ' |
Что будет, если мы удалим два перехода (один условный, второй безусловный)? В этом случае последовательно отработают две ветки программы. Чтобы "убить" любую инструкцию, достаточно "забить" ее NOP (опкод которой 0х90, а вовсе не 0, как почему-то думают многие начинающие кодокопатели). Обе команды в нашем примере двухбайтовые, и поэтому каждую придется заменить двумя инструкциями NOP.
Кажется, мы все сделали правильно, однако "программа выполнила недопустимую операцию и будет закрыта". К сожалению, мы забыли об оптимизирующем компиляторе. Это затрудняет модификацию программы. Но ни в коем случае не делает ее невозможной.
Давайте заглянем "под капот" могучей системы Windows и посмотрим, что там творится. Запустим программу еще раз и вместо аварийного закрытия нажмем кнопку "сведения", в результате чего получим следующий результат:
Программа BREAK_X вызвала сбой при обращении к странице памяти в модуле MSVCP60.DLL по адресу 015£:780c278d.
Разочаровывающие малоинформативные сведения! Разумеется, ошибка никак не связана с MSVCP60.DLL, и указанный адрес, лежащий глубоко в недрах последней, нам совершенно ни о чем не говорит. Даже если мы рискнем туда отправиться с отладчиком, то следов причины аварии мы не найдем. В действительности вызываемой функции передали неверные параметры, которые и привели к исключительной ситуации. Конечно, это говорит не в пользу фирмы MircroSoft: что же это за функция такая, если она не проверяет, какие аргументы ей передали! С другой стороны, именно сокращением числа проверок и вызвано некоторое ускорение Windows 98 по сравнению с ее предшественницей.
Однако мы опять отвлеклись. Как же нам проникнуть внутрь Windows и выяснить, что там у нее не в порядке? В этом нам поможет другой продукт фирмы Microsoft — MS VC. Будучи установленным в систему, он делает доступной кнопку "отладка" в окне аварийного завершения. Теперь мы можем не только закрыть некорректно работающее приложение, но и разобраться, в чем причина сбоя.
Дождемся появления этого окошка еще раз и вызовем интегрированный в MS VC отладчик. Пусть не самый мощный, но достаточно удобный во многих случаях. Как уже отмечалось, бессмысленно искать черную кошку там, где ее нет. Ошибка никак не связана с местом ее возникновения. Нам нужно выбраться из глубины вложенных функций "наверх", чтобы выяснить, что явилось причиной передачи некорректных параметров. Это можно сделать, используя адреса, занесенные в стек. В удобочитаемом виде эту информацию может предоставить мастер "Call Stack", результат работы которого показан ниже:
std::basic_ostream<char,std::char_traits<char> >::opfx(std::basic_ostre...
std::basic_ostream<char, std:: char_traits<char> >::put (std::basic_ostrea...
std:: endi (std:;basic_ost re am< char, std::char traits<char> > & {...})
BREAK_X! 0040114a()
CThreadSlotDat:a::SetValuelCThreadSlotData * const 0x00000000, int 4,....
Напомню, что стек растет снизу вверх, а нам, следовательно, нужно спускаться вниз. Первые три вызова можно смело пропустить (так как это библиотечные функции), а четвертый break_x принадлежит пользовательскому приложению (по имени исполняемого файла). Вот это — настоящий источник ошибки. Кликнем по нему мышкой и перейдем непосредственно в окно дизассемблера.
00401142 пор
00401143 пор
00401144 call dword ptr ds:[402033h]
0040114Д push 403020h
Узнаете окружающий код? Да-да, это то самое место, где мы его модифицировали. Но в чем причина ошибки? Обратим внимание, что перед вызовом функции в строке 0х0401144 не были переданы параметры! Куда же они могли подеваться? А... Это хитрый оптимизирующий компилятор расположил их так, чтобы они оказывались в стеке только в том случае, если эта ветка получает управление. Вернемся к оригинальной копии, чтобы подтвердить наше предположение:
401122 mov еах, ds:?cout@std@133V?$basic_ostream...
401127 push еах
401128 jz short loc_401144
Как хитро построен код. Этим искусством машинного творения действительно можно залюбоваться! Начинаешь испытывать глубокое уважение и к самим компиляторам, и к их разработчикам. Оставим занимательную головоломку модификации кода любопытным читателям.
Перва.я необдуманная мысль — переписать целиком этот фрагмент — не выдерживает никакой критики. Это слишком некрасивое решение. Наибольший интерес представляют решения с изменением минимального числа байт. Это действительно захватывающая головоломка, от которой можно получить большое удовольствие.
Красивые и лаконичные решения, полученные ценой бессонных ночей, проведенных за монитором и километрами распечаток, это удел настоящих хакеров. Кракерам они не то что бы неинтересны (ведь кракеры тоже неплохие специалисты с нелинейным мышлением), но для них взлом — это все же большей частью профессия с вытекающей отсюда рыночной системой отношений. Клиенту не нужны красивые решения, клиент хочет видеть быстрый и дешевый взлом. Прямо здесь и прямо сейчас. А вместе с красотой страдает и качество.
Изменив всего один байт 0х74 на ОхЕВ, мы грязно взломали программу. Кракер на этом остановится, но хакер пойдет дальше. Почему "грязно"? Программа по-прежнему спрашивает пароль. И хотя не имеет значения какой, все же это может сильно раздражать, да и просто выглядит неаккуратно. Давайте модифицируем программу так, чтобы она вообще не отвлекала нас запросом пароля.
Одним из решений будет удаление процедуры ввода пароля. Обращу внимание на важный момент: вместе с процедурой необходимо удалить и заносимые в стек параметры, иначе стек окажется несбалансированным и последствия, скорее всего, не заставят себя ждать. Однако компиляторы Си строят функции так, что очистку стека выполняет не сама функция.
Рассмотрим внимательно еще раз процедуру ввода строки:
4010D8 mov edi, ds:??5std@@YAAAV?$basic_istream@DU?
…………………………………………………………………………
4010FA lea ecx, [esp+ICh]
4010FE push ecx
401OFF push edx
401100 call edi
401 102 add esp, 10h
Стек очищается командой ADD ESP,10h. Функция его не изменяет. Поэтому нам ничем не грозит удаление этой функции, и мы без последствий можем "забить" ее командами NOP. Кроме того, можно удалить две команды push (соответственно изменив add esp, IOh на add esp,08h), но это вопрос стиля. Кому-то так может показаться красивее, а другой не захочет выполнять бесполезную работу.
Совсем иначе обстоит дело с Pascal-компиляторами. Стек очищает непосредственно сама функция. Тогда удаление заносимых параметров становится обязательным — иначе несбалансированность стека очень быстро приведет к зависанию.
Что же еще можно улучшить? Надпись Enter password: по-прежнему выводится и выглядит небрежной кляксой на фоне опрятного взлома. Отключим ее? Заметим, что это можно сделать изменив всего один байт, — поставить в начало выводимой строки завершающий символ 0. Это не потребует изменения кода, что безопаснее. А что если мы вместо 'Enter password* запишем свой копирайт? (Должны же пользователи знать, какого доброхота им следует благодарить!). Рассмотрим подробно эту простую операцию, ибо она далеко не так проста, какой кажется на первый взгляд. Было бы неплохо, если бы строка 'Enter password' была раза в два длиннее. А в таком ограниченном объеме мало что можно записать. На деле существующие ограничения легко обойти. Рассмотрим несколько наиболее очевидных вариантов.
0403030: 50 61 73 73-77 6F 72 64-20 4F 4В 21-00 00 00 00 Password OK!
0403030: 50 61 73 73-77 6F 72 64-20 66 61 S9-6C 00 00 00 Password fail
0403040: 4В 50 4E 43-00 00 00 00-45 6E 7^ 65-72 20 70 61 KPHC Enter pa
0403050: 73 73 77 6F-72 64 20 3A-20 00 00 00-43 72 61 63 ssword: Crac
0403060: 6В 4D 65 30-31 20 ЗА 20-54 72 79 20-74 6F 20 70 kMeOI: Try to p
0403070: 61 74 68 20-63 6F 64 65-20 6F 66 20-56 6F 75 6E ath code of foun
0403080: 64 20 76 61-6C 69 64 20-70 61 73 73-77 6F 72 64 d valid password
0403090: 00 00 00 00-46 61 74 61-6C 20 45 72-72 6F 72 ЗА Fatal Error:
04030A0: 20 4D 46 43-20 69 6E 69-74 69 61 6C-69 7й 61 74 MFC initializat
0403080: 69 6F 6E 20-65 61 69 6C-65 64 00 00-00 00 00 00 ion failed
Во взломанной программе строки 'Password fail!' и 'KPNC' уже не нужны. И мы их можем использовать для своих нужд. Для этого нужно изменить указатель на выводимую строку. Как помним, он расположен по адресу
Ох04010ЕС: 004010ЕС push 403048h
Изменим смещение 0х403048 на 0х0403030. Тогда нам будет доступна вся область до 0х403059 (т.е. до начала строки 'CrackMc....'). Только не забудьте конец строки отметить завершающим нулем.
С другой стороны, в сегменте данных еще много свободного места (на этом дампе оно не показано). Если уж мы изменили смещение выводимой строки, почему бы тогда не расположить необходимую нам строку в любой свободной области и не установить на нее указатель?
Но наш взлом еще не подошел к концу. Остается последний немаловажный вопрос — как мы будет распространять свое творение? Ехе-файлы обычно имеют очень большой объем, и на распространение их наложены чисто законодательные ограничения.
Хорошо бы объяснить пользователю, какие именно байтики следует поменять, чтобы программа заработала, но сможет ли он понять нас? Вот для этой цели н были написаны автоматические взломщики.
Для начала нужно установить, какие именно байты были изменены. Для этого нам вновь потребуется оригинальная копия и какой-нибудь сравниватель файлов. Наиболее популярными на сегодняшний день являются c2u by Professor Nimnul и MakeCrk by Doctor Stein's labs. Первый гораздо предпочтительнее, так как он не только более точно придерживается наиболее популярного "стандарта" (если можно так сказать), но и позволяет генерировать расширенный xck-формат.
Для запуска утилиты передадим два файла — оригинал и модифицированную версию. После чего все изменения будут записаны в файл. При некоторых различиях практически все форматы (в особенности xck) поддерживают ряд чисто текстовых информационных полей, которые абсолютно бесполезны, кроме того, что могут нести какую-то информацию. Единого формата полей нет, и форма заполнения произвольна (на вкус взломщика). Поэтому навязывать свою точку зрения я не буду.
Теперь нам потребуется другая утилита, цель которой будет прямо противоположна: используя crk-файл, изменить эти самые байты в оригинальной программе. Таких утилит на сегодняшний день очень много. К сожалению, это не лучшим образом сказывается на их совместимости с различными crk-форматами. Самые известные из них, скорее всего, cra386 by Prolessor и pcracker by Doctor Stein's labs.
Но поиск подходящей программы, поддерживающей наш формат crk, является уже заботой пользователя, решившего взломать программу. Заметим, что распространение crk-файлов не является нарушением и не карается законом, так как это чисто текстовая информация н, кроме того, продукт вашего умственного труда, который автоматически попадает под защиту закона об авторских правах.
Крак можно легально распространять, тиражировать, продавать. Но вот у пользователя, решившего его использовать, проблемы с законом возникнуть уже могут, так как этим он ущемляет авторские права разработчиков программы. Парадоксальный, однако, у нас мир!
Для избежания проблем с совместимостью иногда используют исполняемые файлы (c2u способен генерировать и такие), которые выполняют модификацию программы автоматически. При этом они часто занимают даже меньше места! Но главный недостаток их в том, что исполняемый файл по нашим законам уже является не информацией, а орудием атаки, и следовательно, распространяться не может. Впрочем, конечный выбор я оставляю за читателем и его совестью. Мы проделали большую работу, и вы наверняка узнали немало нового. Это была очень простая защита, а нас ждет еще очень длинный, но интересный путь.
Дата публикования: 2014-11-18; Прочитано: 309 | Нарушение авторского права страницы | Мы поможем в написании вашей работы!