Начинаем: Всем, привет! Это моя первая статья по исследованию программы
с целью ее бесплатной регистрации. Т.е. конечно моей целью, да, думаю,
и вашей не была именно регистрация, но я решил с помощью этого примера
помочь вам в освоении основ исследования (взлома, настройки под себя -
как хотите) программ, ну а ваша цель - получить хоть какой-то начальный
опыт по этому делу. Статья рассчитана на людей, начиная с тех, кто
буквально только-что скачал и установил себе отладчик, но не имеет
опыта работы с ним в этой области, и заканчивая теми, кто может уже и
взломал пару-тройку-пятерку программок, но не более. Хотя конечно эту
статью могут прочитать и те, у кого чуть больше опыта. Однако опытным
крэкерам я сразу предлагаю нажать крестик вверху, справа =) ибо им
будет неинтересно. Нельзя сказать, что программа ломается
очень-очень-очень легко. Скажем так... просто легко. Но я, во-первых,
не захотел использовать для статьи специальные программки для взлома
(crackme), а решил использовать НАСТОЯЩУЮ программу, которая требует
18$ за регистрацию ! Во-вторых, я постараюсь РАЗЖЕВАТЬ вам процесс
исследования программы и нахождения правильного кода, так что думаю
проблем возникнуть не должно.
Этой статьей я хотел исправить
одну большую ошибку многих крэкеров, которые пишут аналогичные статьи.
Они забывают, что пишут для начинающих, которые например представления
не имеют как найти место в программе где происходит сравнение пароля и
что потом делать. Получилось у меня или нет вы можете сказать мне,
написав письмо на MozgCnoSpam@avtograd.ru . Кстати напомню, что
необходимо, чтобы у вас было минимальное знание ассемблера. Ибо без
этого ничего путного не получится, вы просто не разберетесь во всех
этих командах. Я конечно в принципе могу прокомментировать каждую
строчку приводимых здесь отрывков кода программы, но все равно научить
ассемблеру я вас не смогу. Если знания ассемблера у вас на 0, то
предлагаю скачать архив рассылки "Ассемблер? Это просто! Учимся
программировать под MS-DOS", автор - Калашников Олег. Скачать архив
можно здесь. Почитайте как минимум первые 9 выпусков. Там все написано
просто и понятно. Еще могу посоветовать очень маленькую статейку
Ассемблер для крэкеров . Она мне не очень нравится, но возможно там вы
сможете хоть что-то узнать, если уровень вашего знания ассемблера
никакой. Кстати предупрежу, что комментарии ассемблерных строк, а также
большие пояснения в скобках, чтобы не запутаться, я буду выделять
красным цветом. Статья рассчитана на просмотр в разрешении 1024x768. Ну
ладно, хватит для начала, а то больно сильно отвлекся. Вам еще не
надоела моя болтовня? Нет? Ну тогда читайте дальше!
Итак кроме
отладчика (в списке инструментов я написал SoftIce, но в принципе это
может быть любой отладчик, например OllyDbg, TRW2000 и т.д., главное,
чтобы вы имели основы работы с ним. В статье я буду ориентироваться на
то, что вы пользуетесь SoftIce) вам необходим дизассемблер W32Dasm
(вообще-то он является еще и отладчиком, но как отладчик он мне не
нравится, пользуюсь им только для дизассемблирования файлов), ссылку на
который я дал в самом верху страницы. В принципе можно обойтись и без
него, но с ним намного легче! С чего начать? Есть много вариантов. В
статьях обычно пишут так, как будто все получилось сразу легко и
просто. На самом же деле так не бывает. Ну только если с самыми
простыми программами. На деле перебираются все основные способы, пока
какой-то способ не срабатывает. Скажу честно, что тот способ, который я
вам сейчас буду рассказывать был в моем списке под номером 2. Сначала я
попробовал немножко по-другому, но там было запутанно. И я решил
попробовать этим способом. Через 2 минуты я уже увидел, что так легче,
еще через минуту вкратце его проверил и через 5 минут, уже узнав
настоящий пароль, решил написать данную статью. Ну я это к тому говорю,
чтобы вы не отчаивались если у вас не будет получаться с первого раза.
У нас тоже не с первого! Все методом перебора основных вариантов.
Первый способ, который у меня не получился, я рассказывать не буду. Во
всяком случае не в этой статье. Считайте, что мы его проделали, ничего
не вышло и вы спрашиваете у меня другой способ =) У вас возникает
возмущенный вопрос: "Как же мы научимся этому делу, если нам даже
первый способ не рассказывают !?". Отвечу : Просто второй способ мой,
который я вам щас и буду описывать зачастую более результативный. И
пусть в вашем списке он будет под номером 1. Вот и все. Этот другой
способ состоит вот в чем : Зачастую текстовые строки, которые выводятся
во время работы программы так и хранятся полностью внутри программы.
Для того, чтобы например вывести табличку (маленькое окошко, обычно это
стандартное окошко с кнопкой ОК, и называется оно MessageBox,
вызывается с помощью функции MessageBoxA . Это я вам на будущее) с
надписью, нам нужно сначала загрузить адрес этой надписи в стек. Стек -
это специально отведенная область памяти для хранения промежуточных
данных. Перед вызовом функции вывода окошка с каким-либо сообщением, в
стек помещается адрес, по которому в памяти хранится это сообщение. Щас
я вас наведу на мысль. Многие дизассемблеры (в том числе и наш W32Dasm)
анализируют, и сразу показывают если происходит обращение к строке. Это
сделано для того, чтобы облегчить нам поиск. Идем дальше. Например,
когда мы вводим любой пароль (скорее всего неправильный =), то нам
показывается окошко, в котором сообщается, что мы ввели неправильный
пароль. Но как я уже сказал программа сначала загружает в стек адрес
строки-сообщения о неправильном пароле. Если посмотреть этот участок
кода в дизассемблере, то он нам покажет, что там-то там-то происходит
обращение к строке такой-то, а потом вызывается функция, которая
показывает на экране окошко с этим сообщением. Так вот когда мы нашли в
дизассемблере это место (просто сделаем поиск отрывка из сообщения о
выводе неправильного пароля) то рядом если прокрутить несколько экранов
вверх или вниз мы обычно находим обращение к строке, в которой
написано, что пароль правильный, или что мы зарегестрированы. В 80-90%
РЯДОМ сверху от этого места есть проверка правильности пароля и потом
происходит условный переход или на вывод окошка с сообщением о
неправильном пароле, или, если сравнение прошло успешно, т.е если
введенный пароль равен правильному паролю, переход на вывод окошка с
сообщением о правильности введенной информации. И программа становится
зарегестрирована. Наша цель - с помощью отладчика посмотреть правильный
пароль во время сравнения его с введенным паролем. Вот и все! На этом
теория заканчивается. Переходим к практике.
Практика:
Устанавливаем и загружаем нашу программу - BlueFace. Внизу в system
tray появится иконка программы. Кликаем правой кнопкой и выбираем
Register BlueFace... -> Enter your code... Вводим любой пароль и
нажимаем ОК. Видим сообщение "You entered invalid key code!" . Теперь
нам надо найти, где в программе выводится это сообщение, ну а выше,
скорее всего, будет процедура проверки кода. Помните я вам только что
объяснял, что чтобы вывести окошко с сообщением надо сначала адрес
этого сообщения поместить в стек ? Так вот мы щас в W32Dasm'е сделаем
поиск например слова "invalid" (ну просто часть сообщения нашего, можно
например ввести "entered") и попадем на место вывода сообщения о
неправильном номере. Загружаем W32Dasm. Если вы первый раз сейчас
решили воспользоваться W32Dasm'ом, то вам надо настроить шрифты. Для
этого этого идем в меню Disassembler -> Font... -> Select Font и
выбираем шрифт Courier, жирный, размер 8. Теперь делаем Disassembler
-> Font... -> Save Default Font. Все, теперь со шрифтами все
будет в порядке. Сейчас загружаем в W32Dasm'е нашу программу -
BlueFace.exe . Видим на экране дизассемблированный вариант нашей
программы. Т.е. программа строится из последовательных ассемблерных
команд, которые мы сейчас и видим на экране. В меню Search выбираем
Find text, вводим "invalid" и делаем поиск. Попадаем на строку
* Possible StringData Ref from Data Obj ->"You entered invalid key code!" Т.е.
нам дизассемблер говорит, что в следующей команде происходит обращение
к строке "You entered invalid key code!". Смотрим следующую строку
:00407371 6814654100 push 00416514 :00407371 - это адрес по которому в памяти будет находиться данная команда. 6814654100
- это запись команды в шестнадцатеричном виде. (так будет выгляеть эта
команда в exe файле, если просматривать его с помощью
шестнадцатеричного редактора) push 00416514 - это сама команда, которая помещает в стек число - адрес 00416514, по которому хранится наша строка.
Если
подумать логически, то после этого должен идти вызов функции, которая
выводит на экран наше сообщение. Просто так практически всегда :
загружается адрес строки в стек и вызывается процедура вывода окошка.
Это запомните. Как вызывается какая-либо функция ? Да с помощью команды
call . Смотрим ниже и видим
:00407376 E8DD6D0000 Call 0040E158 Точно.
Вызывается функция вывода на экран сообщения о неправильном коде.
Откуда я узнал, что это именно эта функция ? Ну я же уже сказал, что
это просто надо запомнить. Это стандарт. В 80% программ, в которых не
реализовано ничего для затруднения нахождения правильного кода, это
так. После загрузки адреса строки в стек, вызывется ф-ия вывода этой
строки в окошке. Ну не может быть больше ничего другого. Так всегда. А
0040E158 это адрес этой функции в памяти. Но нам он не важен. Теперь,
как я говорил ранее, нам надо найти обращение к строке о правильном
введенном номере или об успешной регистрации, что в принципе одно и то
же. Т.е. нам надо найти строку, где бы было написано что-то о том, что
код правильный или что регистрация успешна, или спасибо за регистрацию
и т.д. Давайте пролистаем пару экранов вниз. Сначала делайте это, а
потом продолжите читать дальше. Не надо волноваться =) Я знаю что там
ничего нет. Давайте теперь возвратимся и пролистаем пару экранов вверх.
Сделайте. Вы наверно сразу обратили внимание на строку:
* Possible StringData Ref from Data Obj ->"Thank you for registration!" Это
ничто иное как вывод строки о правильном пароле и успешной регистрации!
Видите, следующей строкой идет call 0040E158. Эта та же функция вывода
сообщения. (То что это таже фунция вывода сообщения на экран, что и
была перед этим, можно понять из того, что эта функция вызывается и в
прошлом рассмотренном нами отрывке и сейчас по одному и тому же адресу
- 0040E158)
* Possible StringData Ref from Data Obj ->"Thank you for registration!" | :004072A6 6834654100 push 00416534 ; Кладем в стек адрес сообщения об успешной регистрации программы * Reference To: MFC42.MFC42:NoName0040, Ord:0219h | :004072AB E8A86E0000 Call 0040E158 ; Вызывается функция, которая берет из стека адрес строки, потом берет саму строку из памяти по этому адресу и выводит ее в окошке на экран Теперь,
как я говорил в теоретической части статьи, выше должно быть сравнение
двух строк и условный переход на один из любых адресов программы, где и
будет либо выводиться сообщение о правильном коде, либо о неправильном.
На всякий случай скажу, что условный переход - это переход, который
зависит от какого-то условия. Т.е. от какого-то условия зависит
выполнится ли этот переход, или нет. Это на будущее... Что нам теперь
нужно знать. Команды сравнения - cmp и test . CMP <что-то>,
<что-то> (например cmp eax, ebx - сравнивает регистры eax и ebx.
Аналогично cmp eax, 30h - сравнивает регистр eax с шестандцатеричным
числом 30h) устанавливает флаг нуля, если эти <что-то> равны. Т.е
флаг нуля становится равным 1. Команда test ЧАЩЕ ВСЕГО используется для
сравнения значения регистра с 0. Записывается так : test eax, eax . Тут
может быть не eax а любой другой регистр, но чаще всего eax. Если eax
равен 0, то устанавливается флаг нуля, т.е. как я уже сказал он
становится равным 1.Флаг - это регистр, который может принимать
значение либо 0, либо 1 (если флаг равен 1, то говорят что флаг
установлен или выставлен). Флаг нуля например показывает результат
предыдущего сравнения или некоторых других команд. Это я рассказал про
команды, используемые для сравнения. Практически всегда после команды
сравнения идет команда условного перехода, который осуществляется в
зависимости от результата сравнения, ну и соотвественно в зависимости
от значения флага. Основные команды условных переходов это jz, jnz, je,
jne . Отмечу что команды jz и je - абсолютно одинаковые команды,
соотвественно нет разницы между командами jnz и jne. Команда < jz
(je) address > используется для осуществления перехода на адрес -
address, если установлен флаг нуля, т.е. если перед этим либо команда
cmp сравнила ОДИНАКОВЫЕ данные, либо eax при выполнении команды test
eax, eax , был равен 0. Ну вы понимаете, что на самом деле вместо
address в программе будет написан какой-то адрес =). Итак смотрим выше
и ищем команды сравнения и условного перехода :
:00407279 E8621D0000 call 00408FE0 ; Вызываем процедуру проверки кода :0040727E 83C404 add esp, 00000004 :00407281 85C0 test eax, eax ; Проверям, а не ноль ли в eax :00407283 6A00 push 00000000 :00407285 0F84C8000000 je 00407353 ; Если 0, то переходим на место :0040728B 8D4C2410 lea ecx, dword ptr [esp+10] ; вывода ссобщения о :0040728F E89CB8FFFF call 00402B30 ; неправильном коде :00407294 51 push ecx :00407295 C784247801000000000000 mov dword ptr [esp+00000178], 00000000 :004072A0 8BCC mov ecx, esp :004072A2 8964240C mov dword ptr [esp+0C], esp
* Possible StringData Ref from Data Obj ->"Thank you for registration!" Видим
это на экране и сразу замечаем строки c командами test и je. Важные
строки я выделил жирным шрифтом. Хочу заметить, что как видите все
просто. Нам не надо разбираться во всех этих ассемблерных командах.
Просто если мы знаем, что делаем, то действуем по заранее продуманному
плану, и все кажется довольно просто. Это я к тому, чтобы вы поняли и
не пугались : вам не надо быть супер программистом на ассемблере, чтобы
уметь взламывать программы. Основ вполне достаточно. А остальное придет
с опытом исследования программ.
Теперь давайте разберемся с 3
выделенными командами. Начнем с команды test (это я так специально,
чтобы вам понятнее было). Значит команда test eax, eax сравнивает eax с
нулем. Если eax равен нулю, то устанавливается флаг нуля, и в этом
случае сработает переход на адрес 00407353 с помощью команды je
00407353 ! Если eax не равен 0, то переход не сработает ! и мы попадем
на команду lea ecx, dword ptr [esp+10] и пойдем дальше. (Когда в 5-ый
раз перечитывал свою же статью решил на всякий случай еще раз пояснить,
что команды je и jz выполняют переход на указанный после команды адрес,
если флаг нуля выставлен. Если флаг нуля не выставлен, то переход не
осуществляется и просто выполняется следующая после je (jz) команда.
Аналогично команды jne и jnz выполняют переход наоборот только в том
случае если флаг нуля НЕ выставлен (например если команда cmp
сравнивала разные данные или при выполнении команды test eax,eax , eax
не был равен 0) Посмотрим, что у нас по адресу 00407353:
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00407285(C)
|; Это действия, которые нас абсолютно не волнуют :00407353 8D8C24C0000000 lea ecx, dword ptr [esp+000000C0] :0040735A E8D1B7FFFF call 00402B30 :0040735F 51 push ecx :00407360 C784247801000006000000 mov dword ptr [esp+00000178], 00000006 :0040736B 8BCC mov ecx, esp :0040736D 8964240C mov dword ptr [esp+0C], esp
* Possible StringData Ref from Data Obj ->"You entered invalid key code!" | :00407371 6814654100 push 00416514 ; Помещаем адрес этой строки в стек
* Reference To: MFC42.MFC42:NoName0040, Ord:0219h | :00407376 E8DD6D0000 Call 0040E158 ; Выводим сообщение о неверном коде Мы
видим, что это знакомая нам процедура вывода на экран сообщения о
неверном коде ! Самая верхняя строчка означает, что в эту процедуру
можно попасть с помощью перехода с адреса 00407285. Там у нас как раз и
есть наша команда перехода - je. Подведем итоги. Значит осуществляется
переход на процедуру вывода сообщения о неправильном коде. В каком
случае осуществляется этот переход? В случае если регистр eax = 0. Но
причем тут eax и наш введенный код ? Просто обычно для проверки
правильного регистрационного кода сначала вызывается процедура проверки
этого кода, в которой в какой-нить регистр (это обычно eax) помещается
0 или 1 в зависимости от того правилен ли наш введенный код, или нет.
После этой процедуры обычно идет команда test, которая и проверят
регистр на ноль, а дальше идет условный переход. Запомните - это
стандарт. И для начала вам хватит знания этого стандарта. Все это мы
видим на нашем примере ! Давайте еще раз посмотрим на уже знакомый нам
код:
:00407279 E8621D0000 call 00408FE0 ; Вызываем процедуру проверки кода :0040727E 83C404 add esp, 00000004 :00407281 85C0 test eax, eax ; Проверям, а не ноль ли в eax :00407283 6A00 push 00000000 :00407285 0F84C8000000 je 00407353 ; Если 0, то переходим на место :0040728B 8D4C2410 lea ecx, dword ptr [esp+10] ; вывода сообщения о :0040728F E89CB8FFFF call 00402B30 ; неправильном коде :00407294 51 push ecx :00407295 C784247801000000000000 mov dword ptr [esp+00000178], 00000000 :004072A0 8BCC mov ecx, esp :004072A2 8964240C mov dword ptr [esp+0C], esp
* Possible StringData Ref from Data Obj ->"Thank you for registration!" Как
я только что объяснил, перед командой test должен быть вызов процедуры
сравнения кода. Я выделил его первой строчкой. Обычно внутри таких
процедур происходят какие-то преобразования или генерация правильного
кода (код генерируется или по введенному имени, или в нашем случае по
серийному номеру (можете посмотреть в программе, где мы вводим код,
есть поле Your serial number)). Вот по этому серийному номеру и
генерируется код. Это тоже стандарт. Видите, нет ничего сложного,
практически всегда все идет по стандарту. А если не идет, то просто
применяются более сложные стандарты =)) Просто запомните, что если есть
поле серийного номера, который тем более не вы вводите, то код будет
генерироваться по этому серийному номеру. В конце процедуры обычно идет
проверка введнного кода и сгенерированного правильного кода. Но
процедуру генерации кода мы рассматривать не будем. Мы и так вон
сколько разобрали, а то голова треснет! Если вам нравится моя статья и
то, как я объясняю - не поленитесь напишите пару строчек мне на
MozgCnoSpam@avtograd.ru . Там же можете попросить, чтобы я дополнил
статью разбором процедуры генерации кода или может написал еще пару
статей, или можете написать мне типа "Что за лажу ты, чувак, пишешь!",
в общем любые комментарии. Опять отвлеклись. Вообщем что сейчас от нас
требуется. Это поставить в отладчике брейкпоинт ( Брейкпоинт - это
точка останвки программы, т.е. та точка, в которой программа прервет
свое выполнение и мы попадем в окно отладчика. В софтайсе брейкпоинт
ставится с помощью команды: bpx имя_функции . Где имя функции - это
название функции, при выполнении которой программа остановится. Или же
можно поставить брейкпоинт на адрес командой "bpx XXXXXXXX", где
XXXXXXXX - это адрес, дойдя до которого программа прервется, и вылезет
окно СофтАйса, в этот момент мы будем находиться как раз на адресе
XXXXXXXX. Как узнать какая функция нам нужна если мы ставим брейкпоинт
на функцию? Я советую вам где-нить посмотреть список стандартных
функций, которые могут пригодиться при отладке ) на адрес 00407279,
ввести в проге любой код, после чего мы прервемся в софтайсе, т.к.
программа будет выполнять команду по этому адресу и сработает
брейкпоинт. Дальше нам надо зайти в эту процедуру и искать место где
сравнивается наш код со сгенерированным. Такое сравнение будет либо
сделано запутанно, если защита неплохая, либо в конце процедуры будет
просто стоять команда cmp, которая и будет сравнивать наш код. Но
неужели вы думаете, что для нашего первого урока, я подобрал вам
программу с замудренной процедурой сравнения? =) Конечно нет. Так что
давайте зайдем в эту процедуру и будем искать там команду cmp. Сделаем
это. Заходим в окно регистрации программы, вводим любой необычный,
бросающийся в глаза код (я обычно ввожу 1234321) и открываем окно
софтайса (CTRL + D). Теперь нам надо, как я уже сказал, поставить
брейкпоинт на адрес 00407279. Что для этого надо сделать? Сперва надо
сделать наш процесс (BlueFace) текущим. Для этого в окне SoftIce'а
пишем "addr" и видим список загруженных процессов. Где-то в самом конце
списка мы увидим строку такого содержания :
CR3 Addr PID NAME ........ ........ .... ......... xxxxxxxx xxxxxxxx xxxx BlueFace Чтобы
сделать наш процесс текущим, наберем "addr xxxx" , где вместо "хххх"
подставляем соответствующее значение из колонки PID. ( Кстати, во
избежание недоразумений, скажу, что в разных версиях SoftIce'a и как я
понял в разных версиях Windows (98, XP) эта таблица может выглядеть
по-разному. И колонки могут быть подписаны по-другому. Если у вас это
выглядит не так как у меня, т.е. если у вас нет колонки PID, то
наберите "addr name" где вместо name подставьте имя процесса (в нашем
случае BlueFace, т.е наберите "addr blueface"). И никаких проблем
возникнуть не должно. Процесс так же станет текущим ) Напомню, что нам
необходимо сделать процесс текущим для того, чтобы получить возможность
установить брейкпоинт на любой адрес в этом процессе (в нашем случае на
адрес 00407279). Теперь убедимся, что наш процесс стал текущим :
повторно набираем "addr" и видим, что наш BlueFace выделен. Теперь для
установки брейкпоинта на адрес 00407279 ( Помните по этому адресу
происходит вызов процедуры генерации и проверки кода? А нам нужно
попасть в эту процедуру и найти в ней сравнение введенного кода с
правильным ) наберем в СофтАйсе : "bpx 00407279" (enter) . Чтобы
проверить, что наш брейкпоинт действительно установился, наберем
команду "bl" (enter) и увидим такую строку: 00
- это номер брейкпоинта. Когда их будет например несколько, то ниже
будут брейкпоинты, номера которых будут продолжаться ( 01) , 02) и т.д.
). Сама эта строка показывает, что установлен брейкпоинт на адрес
00407279, т.е. мы убедились, что брейкпоинт поставился успешно. Теперь
для продолжения работы программы нажимаем F5. Окно СофтАйса пропадает,
и мы в окне регистрации, где ввели наш код от косяка, нажимаем ОК. И
моментально оказываемся в окне софтайса по адресу 00407279 - это
сработал наш брейкпоинт. Мы стоим прямо на команде вызова процедуры
проверки кода. Нам нужно зайти в эту процедуру. Для пошагового
выполнения программы с заходом внутрь процедур в софтайсе надо нажимать
F8. Вот и нажимаем F8, происходит прыжок на код процедуры проверки
кода, и вот что мы видим на экране:
:00408FE0 6AFF push FFFFFFFF :00408FE2 6883FA4000 push 0040FA83 :00408FE7 64A100000000 mov eax, dword ptr fs:[00000000] :00408FED 50 push eax :00408FEE 64892500000000 mov dword ptr fs:[00000000], esp :00408FF5 81EC08010000 sub esp, 00000108 :00408FFB 56 push esi :00408FFC 8B84241C010000 mov eax, dword ptr [esp+0000011C] :00409003 C784241401000000000000 mov dword ptr [esp+00000114], 00000000 :0040900E 50 push eax :0040900F FF1504144100 Call dword ptr [00411404] :00409015 83C404 add esp, 00000004 :00409018 8D4C2408 lea ecx, dword ptr [esp+08] :0040901C 8D54240C lea edx, dword ptr [esp+0C] :00409020 8BF0 mov esi, eax :00409022 51 push ecx :00409023 52 push edx :00409024 C744241000010000 mov [esp+10], 00000100 :0040902C FF1568104100 Call dword ptr [00411068] :00409032 8D44240C lea eax, dword ptr [esp+0C] :00409036 8D4C2404 lea ecx, dword ptr [esp+04] :0040903A 50 push eax :0040903B E818510000 Call 0040E158 :00409040 8B4C2404 mov ecx, dword ptr [esp+04] :00409044 8B15AC704100 mov edx, dword ptr [004170AC] :0040904A C684241401000000 mov byte ptr [esp+00000114], 00 :00409052 8B41F8 mov eax, dword ptr [ecx-08] :00409055 8D4C0201 lea ecx, dword ptr [edx+eax+01] :00409059 40 inc eax :0040905A 0FAFC8 imul ecx, eax :0040905D 3BF1 cmp esi, ecx :0040905F 5E pop esi :00409060 8D4C2400 lea ecx, dword ptr [esp] :00409064 7536 jne 0040909C :00409066 E893500000 Call 0040E0FE :0040906B 8D8C2418010000 lea ecx, dword ptr [esp+00000118] :00409072 C7842410010000FFFFFFFF mov dword ptr [esp+00000110], FFFFFFFF :0040907D E87C500000 Call 0040E0FE :00409082 B801000000 mov eax, 00000001 Не
пугайтесь такого большого кода. Это часть процедуры проверки. Как я уже
сказал, наша задача на данном этапе - это найти место сравнения
введенного нами кода со сгенерированным правильным кодом. Вспоминаем
какие для этого команды могут использоваться (команды сравнения) и
пытаемся найти в этой процедуре такую команду. Смотрим с начала
процедуры, опускаем глаза и... и видим "cmp esi, ecx" ! Итак, это
команда сравнения, и в данном случае сравниваются регистр esi и ecx .
Нам тут ничего не надо, кроме как найти команду сравнения
сгенерированного кода и введенного. Мы ее уже нашли. Я ее выделил.
Больше команд cmp нету. Так что можете быть уверены, что это она =) Но
давайте проверим. Нажимаем F10 ( F10 - пошаговое выполнение программы
без захода в процедуры. Кстати напоминаю, что это вы должны делать,
находять внутри окна SoftIce'а. Однако в этом случае вы не сможете
читать эту статью т.к. отладчик закроет ее своим окном. Можно конечно
нажать F4 (показать/скрыть окно софтайса во время отладки) и посмотреть
что вы читали. Только вот все будет как бы зависшее. Это сделано
специально, чтобы дать возможность посмотреть, что в этот момент на
экране. но в то же время не дать программе выполняться дальше. Вообщем
это хоть какой-то выход из ситуации. Если во время отладки что-то
забыли, то жмете F4, читаете что надо (естественно прокрутить не
сможете), потом еще раз нажимаете F4 - и вы снова в окне софтайса.
Однако если вам надо будет пролистать страницу, то придется нажать F5,
прочитать что надо и заново попасть отладчиком в нужное место. Как? Да
у вас же остался установлен брейкпоинт на адрес 00407279. Значит если
вы заново введете код в окне регистрации и нажмете ОК, то откроется
окно отладчика и вы будете на этом адресе, ну дальше опять нажимаете
F8, попадается в процедуру проверки и т.д. ) пока не дойдем до этой
строки:
:0040905D 3BF1 cmp esi, ecx Скорее
всего в одном из регистров введенный код, а во втором, аналогично,
правильный код. Так вот в SoftIce чтобы посмотреть содержимое регистра
надо ввести "? регистр", например "? eax". (В принципе вверху окна
софтайса показываются значения регистров, но там они в шестандатеричном
виде, а чтобы посмотреть еще и в десятичном виде и в ASCII-кодах надо
использовать "? регистр") А чтобы посмотреть содержимое памяти, по
адресу хранящемуся в регистре, надо написать "dd регистр". Давайте
посмотрим, что в esi : наберем "? esi" . И что мы видим ! это наш
введенный код (я вводил 1234321): Сначала
показывается содержимое регистра в шестадцатеричном формате ( 0012D591
), а потом в десятичном ( 01234321 ). Нам надо смотреть наш код в
десятичном формате. Еще раз напомню, что у вас числа будут другие, если
вы ввели другой код, а не "123431". Теперь посмотрим, что в ecx. Для
этого наберем команду "? ecx" и обратим внимание на десятичную запись
значения регистра. Ну что там в ecx? =) А в ecx правильный код !!!
Переписываем его на бумажку, удаляем брейкпоинты ("bc *"), нажимаем F5
для возврата. Опять заходим в окно регистрации, вводим наш код и... ...И
ВСЕ !!! Мы зарегестрированы !!! Ну ради галочки, и чтобы вам все
полностью было понятно скажу, что ниже есть команда условного перехода
jne. Если 2 сравниваемых кода не равны, то осуществляется переход на
участок кода, где eax становится равным 0. Если коды при сравнении
равны, то перехода не осуществляется и продолжается выполнение со
следеющей ниже команды. Там в eax запихивается значение 1. Потом
происходит выход из процедуры проверки, где как мы помним с помощью
команды test eax,eax узнается, что в eax и т.д. Я рассказывал, что
будет дальше. Почему мы не остановились и не разобрали это подробно? Да
потому что мы и так узнали пароль, и для вашего блага я не стал
забивать вам голову лишними вещами... Уточню правда, что после например
переустановки программы серийный номер будет уже другой, и
соотвественно будет генерироваться другой код, так что если захотите
переустановить программу или установить ее на другом компе, то узнавать
пароль придется также заново! Ну на этом я думаю все.. А что тут еще
говорить ? Посылайте свои комментарии мне на MozgCnoSpam@avtograd.ru
Заключение: Еще обычно всякие приветы в конце передают... благодарности =) Ну тоже передам :
Спасибо MoonShiner'у, freeExec'у и Fess'у за помощь, за то, что помогали мне разобраться в крэкинге и за все хорошее =)
Всем счастливо! MozgC (c) 2003
|