Студопедия.Орг Главная | Случайная страница | Контакты | Мы поможем в написании вашей работы!  
 

E call j_??4CString@@QAEABVO@PBD[3Z



Обратим внимание на подчеркнутую строку. Насколько же с первого взгляда неочевидно, куда указывает указатель еах! Попутно замечу, что даже сегодня не каждый компилятор способен генерировать такой код, ценность которого заклю­чается практически в экономии всего одного регистра 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 | Нарушение авторского права страницы | Мы поможем в написании вашей работы!



studopedia.org - Студопедия.Орг - 2014-2024 год. Студопедия не является автором материалов, которые размещены. Но предоставляет возможность бесплатного использования (0.013 с)...