Исследование Opera 3.50
Объект исследования
Программой, которую мы сегодня будем исследовать будет достаточно популярный в последнее время браузер Opera 3.50. Скопировать программу можно по адресу: www.operasoftware.com. Защита у программы слабая и статья ориентирована в основном на новичков.
Инструменты
Отладчик SoftICE 3.23
Дизассемблер IDA Pro 3.8x
Введение
Впервые я
услышал об браузере Opera, когда
находился на одном из англоязычных
cracker’ских сайтов. На этом
сайте (как и на всех остальных) люто
ненавидели все, что каким-либо
образом связано с Microsoft, в
результате чего каждые 30 секунд мой
браузер MS IE 4.0 умолял меня
заменить его на некий браузер Opera, находящийся по такому-то адресу.
Позже, прочитав в журнале
КомпьютерПресс статью, посвященную
этой замечательной программе, я
сразу же отправился на сайт
компании-разработчика и скачал себе trial-версию
Opera 3.50.
Программа
действительно очень хорошая –
быстрая, небольшая, удобная. Это
единственный браузер, способный
составить какую-либо конкуренцию MS IE и NN. Интересно
то, что время, которое вам
предоставляет фирма-разработчик не
является непрерывным промежутком
времени между некоторыми числами, а
складывается из количества дней, в
течение которых вы пользовались
этой программой. Т.е. 30
day trial по
существу здесь означает 30 запусков
программы в разные дни, поэтому для
проверки expired time просто перевести
время на 30 дней вперед
недостаточно.
При
написании статьи я старался, чтобы
она была понятна любому новичку,
однако если вам необходимы фразы
типа “теперь N-раз нажмем F12”,”а
теперь нажмем Ctrl+D и поставим точку
прерывания с помощью команды bpx” и
объяснения, что такое точка
прерывания, ассемблерные
инструкции и т.п., то лучше
повремените с прочтением этой
статьи, т.к. для ее понимания
необходимо знание этого "микроминимума".
Несколько слов о
защите
Как уже было
сказано выше, программа
“защищена” (от
абсолютно бесплатного ее
использования) временным
ограничением и Name/Serial. Несколько слов об
этих защитах. Снять trial – защиту можно
“обманув” программу, сказав ей,
что она зарегистрирована. Этот
способ требует патча. Я не буду его рассматривать,
потому что являюсь ярым
противником патчей (везде,
где можно обойтись без них) и сторонником
отыскания правильных рег. кодов
(хотя это дольше, сложнее и не
всегда возможно).
Несколько слов об “обмане” программы я все же скажу
в конце статьи. Итак, перейдем
непосредственно к рассмотрению
защиты Name/Serial.
Как известно,
защиты Name/Serial, как правило,
очень легки для взлома (и написания keygen’a), поэтому, учтя это,
разработчики постарались
максимально запутать возможного
“исследователя” (хотя оставили
слабые места для патча). Как пример
“работы” разработчиков в этой
части, я посоветую вам
попробовать найти алгоритм
создания верного ключа следующими
способами (анализируя код в SoftICE + dead-listing’и IDA Pro и Wdasm):
Найти
вначале программы cmp
is_this_prog_registered ?
Найти в
коде вызов сообщения о
неправильном рег. коде:
с
помощью самого сообщения
с
помощью размеров окна
с
помощью заголовка окна
Найти
алгоритм, трассируя код,
следующий за вызовом hmemcpy()
Использовать
дополнительные интсрументы
такие как Borland Resource Workshop, Customizer,
Regmon, Filemon
Найти
вхождение в dead-listing какого-либо
объекта
Использовать любой
другой способ
От всего
этого код программы достаточно
хорошо защищен. Кстати последний
способ с использованием в качестве
объекта надпись “(unregistered)”
(помните
самое начало запуска браузера ?)
самая большая, на мой взгляд,
“дыра” в защите программы
(идеально подходит для патча).
Если вы
самостоятельно нашли требуемый
алгоритм и вычислили
правильный рег.номер, то можете не
читать дальше, и по
праву считать себя “не совсем”
новичком.
Исследование
Итак, начнем.
Как обычно вводим какую-либо рег.
информацию в NAG’е или с помощью меню Help -> Register Opera.
Примечание: NAG, nag screen - это
мешающее и раздражающее окно с
сообщением о различных
ограничениях. Эти окна можно
увидеть, как правило, в любой
незарегестрированной программе.
После регистрации, окна NAG больше не
появляются. Само NAG-окно может
появляться практически везде: при
запуске программы или/и при выходе
из нее, а также в процессе работы
программы. (тип. пример - ACDSee32).
Практически всегда окна NAG
встречаются в программах,
использующих Trial период, в течение
которого пользователь может
пользоваться программой бесплатно
(незарегестрировавшись) |
Например следующую: Smasher, No
organization, 123-4567A. Затем ставим точку
прерывания bpx hmemcpy. Возвращаемся (F12) в
вызывающую программу (Opera)
и получаем SoftICE следующий
код:
0048DD38 mov edi, ds:GetDlgItemTextA
0048DD3E lea eax, [esi+138h]
0048DD45 push eax ; Name
0048DD46 push 2BBAh
0048DD4B push ebp
0048DD4C call edi ; GetDlgItemTextA->hmemcpy
0048DD4E lea eax, [esi+264h]
0048DD54 push ebx
0048DD55 push eax ; Organization
0048DD56 push 2BBBh
0048DD5B push ebp
0048DD5C call edi ; GetDlgItemTextA->hmemcpy
0048DD5E lea eax, [esi+390h]
0048DD64 push ebx
0048DD65 push eax ; Reg. Number
0048DD66 push 2BBCh
0048DD6B push ebp
0048DD6C call edi ; GetDlgItemTextA->hmemcpy
0048DD6E pop edi
0048DD6F pop ebp
0048DD70 pop ebx
По адресу 0048DD45 на
стек помещается адрес буфера, куда
будет скопировано Name (копирование
идет в строке 0048DD4С).
Аналогично на стек помещаются
адреса для копирования Organization и Reg.
Number в строках 0048DD55 и 0048DD65 (и
соответственно копирование
происходит в строках 0048DD5С и 0048DD6С).
Примечание: Хотя из приведенного
выше фрагмента видно, что для
копирования введеных нами рег.
данных вызывается функция GetDlgItemTextA,
использовать в качестве точки
останова все же лучше вызов функции
hmemcpy. Аргументация следующая:
функции GetDlgItemTextA, GetWindowTextA при
копировании всегда используют
вызов функции hmemcpy. Более того,
многие нестандартные 16-битные
функции (например в программах,
написанных на VisualBasic'e, Delphi и др.)
также используют вызов функции
hmemcpy(). |
Примечание: Функция hmemcpy (Dest_buf,
Sour_buf, Num_bytes) копирует Num_bytes байт из
адреса Sour_buf в адрес Dest_buf.
Рассмотрим приведенный выше
фрагмент кода на примере
копирования введенного в диалоге
имени:
0048DD45 - Dest_Buf,
0048DD46 - Sour_Buf,
0048DD4B - Num_Bytes,
0048DD4С - Call ...hmemcpy()
При этом наш Sour_Buf равен 2BBAh. А
что такое 2BBAh? 2BBAh - это указатель на буфер,
который содержит введенное нами Name.
А почему имеенно 2BBAh? Посмотрите в
WDasm'e в разделе Refs->Dialog References на
диалог SERNO_REGISTER. |
До этого места вы,
наверное, добрались
самостоятельно, так как ничего
сложного в этом нет. Попробуйте
дальше протрассировать код в SoftICE -
через некоторое время вы увидите,
что сообщение о неверном рег. коде
уже появилось, а где идет его вызов
вы так и не нашли. Действия
следующие: нам необходимо знать, по
каким адресам идет копирование
введенных нами регистрационных
данных. Для этого мы помещаем точку
прерывания по адресу 0048DD45 (двойным щелчком мыши сделать
это наиболее быстро и удобно) и,
прервавшись по этой точке
прерывания (точка прерывания по
hmemcpy, естесственно уже давно
отключена - она нам больше не
понадобится), фиксируем нужные нам
адреса. Как выяснилось, это 994AB0, 994BDC,
994D08 для соответсвенно Name, Org-n, RN.
Отключаем все существующие точки
прерывания (bd *) и ставим новые с
помощью команды bpr (breakpoint on memory range):
bpr 994AB0 994AB0+7 RW; "Smasher"= 7 букв;
bpr 994BDC 994BDC+F RW; "No organization" = не важно сколько букв;
bpr 994D08 994D08+9 RW; "123-4567А" = 9 букв;
На примере bpr 994AB0 994AB0+7 RW
поясню, что мы прервемся сразу, как
только будут выполнены какие-либо
Read или Write (RW) действия с адресным
диапазоном 994АВ0-994АВ0+7. Что нам и
нужно!
После установки этих
трех точек прерывания продолжим
выполнение нашей программы (F5).
Итак, мы прервались по адресу 4DC993 в
модуле Opera (Если вы прервались
где-то в другом месте в любом
системном модуле, то просто с
помощью F12 "дойдите" до адреса
4DC993).
004DC993 repe movsd
004DC995 jmp ds:off_4DCAA8[edx*4]
Как нам известно, команда
REPZ MOVSD копирует "n"-е количество
байт (равное значению регистра CX) из
адреса, взятого в регистре ESI по
адресу, взятому из регистра EDI, при
этом проверяет Z-флаг. Фиксируем эти
адреса. Получается, что в этом месте
происходит копирование Name из
адреса 994AB0 в адрес 994F74. Нажмем F8 и
подержим немного эту клавишу :) Ну
вот, регистр СХ стал равен нулю.
Копирование выполнено. В
скопированном блоке памяти Org-n
имеет новый адрес 9950A0, а RN - 9951CC.
Примечание: Вычислить новые
адреса, где хранятся рег. данные
можно следующими 3-мя способами:
С помощью
линейки прокрутки в окне data window.
С помощью известного нам смещения.
Нам известно, что в старом блоке
памяти Name хранилось по адресу 994AB0, а
Organization по адресу 994BDC. Найдем
смещение между ними - в SoftICE'e дадим
команду: ? 994BDC-994AB0Возьмем
полученный ответ в
шестнадцатиричном формате (12С) и
приплюсуем его к началу нового
блока памяти: ? 994F74+12CПолучили 9950A0.
Аналогично вычисляется новый адрес
для RN.
С помощью
поиска строки. Дадим команду:s 0 l ffffffff "Smasher"Получим следующий
результат: pattern found at Но этот адрес
нам уже известен. Еще раз даем
команду поиска, но с другим
начальным адресом: s 994F75 l ffffffff "Smasher"
Получим следующий
результат: pattern found atАналогично
находятся адреса для Org-n и RN.
|
А раз у нас появились
новые буфера хранения рег.
информации, то ставим
соответственно еще точки
прерывания
bpr 994F74 994F74+7 RW
bpr 9950A0 9950A0+F RW
bpr 9951CC 9951CC+9 RW
Теперь нажимаем F10,
пока не достигнем приведенного
ниже кода (пока можно не обращать
внимание на прерывания по memory range,
так как это, в основном, различные
проверки).
004859A5 push eax
004859A6 call sub_4AC886
004859AB lea eax, [esi+390h]
004859B1 push offset a__1
004859B6 push eax
004859B7 call sub_424F5D ; RN modification
004859BC add esp, 10h
004859BF cmp byte ptr [ebx], 0
004859C2 jz short loc_485A3E
004859C4 cmp byte ptr [esi+390h], 0
004859CB jz short loc_485A3E
004859CD lea ebx, [esi+390h]
004859D3 push ebx
004859D4 call sub_4DC390 ; _strlen
004859D9 cmp eax, 0Ch ; cmp len_RN, 12
004859DC pop ecx
004859DD jnz short loc_4859FD ; if len<>12 jump
004859DF push edi
004859E0 call sub_4DC341
Процедура, которая
вызывается в строке 004859B7 преобразует
наш регистрационный номер. Не будем
вдаваться в подробности ее работы,
нам это ни к чему, однако для
визуализации работы этой
подпрограммы, дадим SoftICE'у команду d 9951CC.
Заметим, что наш рег. номер
"123-4567A" сократился до
"1234567А". В строке 004859D4
вызывается функция длины строки
(рег.номера). Далее, мы видим
интересную проверку длины нашего
рег. номера. Если эта длина равна не
равна 12, то мы перескакиваем
определенный кусок кода. Чтобы
выяснить, что бы это могло значить,
запретим все точки прерывания (bd *),
продолжим выполнение программы (F5),
и вновь введем рег. данные, но
теперь в поле Reg. Number укажем любой
12-символьный номер (с учетом того,
что "неверные" символы будут
вырезаны в строке 004859B7). Нажмем ОК -
все понятно, следовательно наш
"верный" рег. номер не может
быть 12-символьным. И в этом случае
мы путем перехода в строке 004859DD оказываемся
в следующем очень интересном
фрагменте:
004859FD push 0Ch ; будет скопировано 12 байт
004859FF lea eax, [ebp-6Ch]
00485A02 push ebx ; из адреса 75FBBC
00485A03 push eax ; в адрес 75FBFC
00485A04 call sub_4DD3F0 ; _strncpy - копирование
00485A09 and byte ptr [ebp-60h], 0
00485A0D lea eax, [ebp-6Ch] ; из адреса 75FBBC
00485A10 push eax
00485A11 lea eax, [ebp-2Ch] ; в адрес 75FBFC
00485A14 push eax
00485A15 call sub_4DC5A0 ; копирование
00485A1A lea eax, [ebp-2Ch]
00485A1D push eax
00485A1E call sub_48E0DD ; !изменение RN по адресу 75FBFC
00485A23 lea eax, [ebp-2Ch]
00485A26 push eax ; будем сравнивать 75FBFC
00485A27 lea eax, [ebp-6Ch]
00485A2A push eax ; с 75FBBC
00485A2B call sub_4DC410 ; _strcmp - сравнение
00485A30 add esp, 20h
00485A33 test eax, eax ; сравниваемые строки одинаковы ?
00485A35 jnz short loc_485A3E ; если нет, то переход
00485A37 mov dword ptr [ebp+0Ch], 1 ; выставляем 1, если одинаковы
Рассмотрим, что делается
в этом фрагменте. С адреса 004859FD по
адрес 00485A04 выполняется копирование
нашего рег. номера из буфера по
адресу 9951СС в буфер по адресу 75FBBC.
Так, это интересно!
Переустанавливаем data window: d 75FBBC. С
адреса 00485A09 по адрес 00485A15 выполняется еще одно
копирование нашего рег. номера в
буфер по адресу 75BFFC. Если ваше окно
data window вмещает хотя бы 5 строк, то
этот адрес также будет виден в нем.
Примечание: Вы можете
отредактировать файл WINICE.DAT под
любые настройки (размеры окон, сами
окна и т.д.). Например, следующими
двумя способами:
отредактировать строку INIT="X"
как, например,
INIT="X; wl; wr; wd 10; width 98; lines 73; wc 40;"
В этом случае данные
установки автоматически
выполнятся при первом входе в SoftIСE.
отредактировать значение любой
свободной "горячей клавиши".
Например, отредактируем значение
Ctrl+F3: CF3="^wl; ^wr; ^wd 10; ^width 98; ^lines 73; ^wc 40;"
В этом случае данные
установки выполнятся при нажатии на Ctrl+F3.
|
А сейчас посмотрим, что
произойдет после выполнения
функции 485A1E (нажимаем F10). Мы видим,
что наш рег. номер по адресу 75BFFC
изменился! Теперь он имеет
следующий вид "1234B3KY2pb4". Все
хорошо, но... это 12-символьный номер!
Т.е. до этого места с таким номером
мы не дойдем! И еще: у полученного
номера первые 4 цифры остались без
изменений, следовательно, можно
предположить, что остальные 8 цифр
получаются путем манипуляций с
первыми 4-мя. Это действительно так
(сомневающимся предлагаю
проверить). Исходя из этих двух
важных замечаний, мы берем себе
новый рег. номер, длина которого
больше 12. Например:
"1234B3KY2pb4Smasher". Проходим все
сказанное раннее с новым рег.
номером и трассируем программу до
следующего кода:
0048DDBC call sub_4DC390 ; _strlen (RN)
0048DDC1 cmp eax, 40h
0048DDC4 pop ecx
0048DDC5 jnb loc_48DEA2 ; if len(RN) >= 40h then jump "...bad RN"
0048DDCB lea eax, [ebp+var_40]
0048DDCE push esi
0048DDCF push eax
0048DDD0 call sub_4DC5A0 ; copy RN из 994D08 в 75FBD4
0048DDD5 lea eax, [ebp+var_40]
0048DDD8 push offset unk_4F5D60
0048DDDD push eax
0048DDDE call sub_424F5D ; возможное "урезание" RN
0048DDE3 lea eax, [ebp+var_40]
0048DDE6 push eax
0048DDE7 call sub_4DC390 ; _strlen (RN по адресу 75FBD4)
0048DDEC add esp, 14h
0048DDEF cmp eax, 0Ch
0048DDF2 jle loc_48DEA2 ; if len (RN[75FBD4]) <=12 then jmp "...bad RN"
0048DDF8 lea eax, [ebp+var_40]
0048DDFB push 0Ch ; !копироваться будут 12 байт
0048DDFD push eax
0048DDFE lea eax, [ebp+var_A0]
0048DE04 push eax
0048DE05 call sub_4DD3F0 ; _strncpy RN из 75FBD4 в 75FB74
0048DE0A lea eax, [ebp+var_34]
0048DE0D mov [ebp+var_94], bl
0048DE13 push eax
0048DE14 lea eax, [ebp+var_E0]
0048DE1A push eax
0048DE1B call sub_4DC5A0 ; !копирование оставшейся части RN в 75FB34
0048DE20 lea eax, [ebp+var_A0]
0048DE26 push eax
0048DE27 lea eax, [ebp+var_60]
0048DE2A push eax
0048DE2B call sub_4DC5A0 ; копирование RN из 75FB74 в 75FBB4 (12 байт)
0048DE30 lea eax, [ebp+var_60]
0048DE33 push eax
0048DE34 call sub_48E0DD ; "изменение" RN из первых 4-х цифр
0048DE39 lea eax, [ebp+var_60]
0048DE3C push eax
0048DE3D lea eax, [ebp+var_A0]
0048DE43 push eax
0048DE44 call sub_4DC410 ; _strcmp - сравнение
0048DE49 add esp, 28h
0048DE4C test eax, eax
0048DE4E jnz short loc_48DEA2 ; if NZ then jmp "...bad RN"
0048DE50 lea eax, [ebp+var_E0]
0048DE56 push eax
0048DE57 call sub_4DC390 ; _strlen части RN[75FB34]
0048DE5C xor esi, esi
0048DE5E cmp eax, ebx
0048DE60 pop ecx
0048DE61 jle short loc_48DE87
0048DE63 movsx edx, [ebp+esi+var_E0] ; цикл - начало
0048DE6B lea ecx, [ebp+esi+var_E0]
0048DE72 sub edx, 61h
0048DE75 jz short loc_48DE7F
0048DE77 dec edx
0048DE78 jnz short loc_48DE82
0048DE7A mov byte ptr [ecx], 31h
0048DE7D jmp short loc_48DE82
0048DE7F mov byte ptr [ecx], 30h
0048DE82 inc esi
0048DE83 cmp esi, eax
0048DE85 jl short loc_48DE63 ; цикл - конец
0048DE87 lea eax, [ebp+var_E0]
0048DE8D push eax
0048DE8E call sub_4DC690 ; !_atol (75FB34)
0048DE93 pop ecx
0048DE94 xor edx, edx
0048DE96 push 7
0048DE98 pop ecx
0048DE99 div ecx ; делим EAX на 7 (EAX-целое,EDX - остаток)
0048DE9B test edx, edx ; если остаток=0, тогда ОК
0048DE9D jnz short loc_48DEA2 ; если нет, тогда "...bad RN"
0048DE9F push 1
0048DEA1 pop ebx
0048DEA2 pop edi
0048DEA3 mov eax, ebx
0048DEA5 pop esi
0048DEA6 pop ebx
0048DEA7 leave
0048DEA8 retn
Я думаю моих комментариев
достаточно и нет необходимости
подробно описывать, что делается в
этом фрагменте. Рассмотрим
наиболее важные моменты
приведенного фрагмента. В строке 0048DE1B происходит
копирование оставшейся (после
первых 12 байт) части рег. номера. А в
цикле 0048DE63 -
0048DE85 происходит
изменение этой части (идет поиск
букв "a" и "b", которые
заменяются на соответственно цифры
0 и 1). В нашем случае берем из
"1234B3KY2bp4Smasher" часть "Smasher".
Она изменяется до "Sm0sher". В
строке 0048DE8E вызывается функция atol, которая
должна преобразовать часть рег.
номера "Sm0sher" в hex-вид. В
описании этой функции сказано, что
если строка не может быть
преобразована к требуемому виду, то
функция возвращает 0. Попробовав
вызвать эту функцию с новым рег.
номером "1234B3KY2bp4Smasher7", мы опять
получим 0. Следовательно, скорее
всего с 13-символа нашего рег. номера
должны быть одни цифры. Поэтому
опять меняем наш рег. номер,
например на следующий:
"1234B3KY2bp1234562" ( 1234562 делится на 7
без остатка - см. далее). Далее, как
видно из листинга, преобразованная
часть рег. номера делится на 7. Нам
нужно, чтобы деление прошло без
остатка, в противном случае мы рано
или поздно придем к сообщению о
неверном рег. номере.
Ну что же, еще
один кусок кода позади, идем дальше.
Если вы попробуете трассировать
далее код программы, то опять
попадетесь в ловушку программистов
и останетесь ни с чем. Поэтому
проверяем, в боевой ли готовности
наши точки прерывания и смело (но
зажмурив глаза) жмем F5. Мы
прерываемся по адресу 004DС3B0. Если
вы раньше самостоятельно изучали
разные программы, то вы без труда по
виду дизассемблированного кода
определите, что перед вами
подпрограмма определения длины
строки _strlen. Проходим ее с помощью F10
(или нажимаем F12) и возвращаемся к
адресу 004B3557. Вот этот код:
004B3552 call sub_4DC390 ; _strlen RN
004B3557 cmp eax, 0Ch
004B355A pop ecx
004B355B jle short loc_4B3593 ; if len (RN) <=12 then jmp "...bad RN"
004B355D cmp eax, 40h
004B3560 jge short loc_4B3593 ;if len (RN) >=40 then jmp "...bad RN"
004B3562 lea eax, [ebp+var_4]
004B3565 push eax
004B3566 push edi
004B3567 call sub_48DEA9 ; !интересно
004B356C mov ebx, eax
004B356E pop ecx
004B356F test ebx, ebx
004B3571 pop ecx
004B3572 jz short loc_4B3593 ; if [ebx] = 0 then jmp "...bad RN"
004B3574 mov eax, dword_505F58
004B3579 test eax, eax
004B357B jz short loc_4B3593 ; if [dword_505F58] = 0 then jmp "...bad RN"
004B357D lea ecx, [esi+264h]
Обратим внимание, что
сначала проверяется длина нашего
рег. номера: не меньше ли она 13 (т.е.
не меньше или не равна 12) и не больше
ли она 3Fh. Если мы не проходим эту
проверку, то перескакиваем к адресу
4B3593 и понимаем, что ни к чему
хорошему это не приведет. А сейчас
рассмотри оставшийся кусок кода. В
строке 004B3567 происходит вызов какой-то
подпрограммы, затем в строке 004B356F проверяется
значение регистра ebx (которое
берется из регистра eax), и, если оно
равно 0, то переходим... как нам уже
известно, к сообщению о неверном
рег. номере. Следовательно,
рассматриваем вызываемую в строке 004B3567 подпрограмму,
для чего нажимаем F8, находясь
(курсором) на этой строке. Вот что мы
видим:
0048DEA9 push ebp
0048DEAA mov ebp, esp
0048DEAC sub esp, 80h
0048DEB2 push esi
0048DEB3 mov esi, [ebp+arg_0]
0048DEB6 push edi
0048DEB7 push esi ; RN (994D08)
0048DEB8 xor edi, edi
0048DEBA call sub_48DF07 ; !интересно
0048DEBF pop ecx
0048DEC0 mov ecx, [ebp+arg_4]
0048DEC3 test eax, eax ; !
0048DEC5 mov [ecx], eax
0048DEC7 jnz short loc_48DF01 ; !
0048DEC9 lea eax, [esi+0Ch]
0048DECC push eax
0048DECD lea eax, [ebp+var_80]
0048DED0 push eax
0048DED1 call sub_4DC5A0
0048DED6 lea eax, [ebp+var_40]
0048DED9 push esi
0048DEDA push eax
0048DEDB call sub_4DC5A0
0048DEE0 and [ebp+var_34], 0
0048DEE4 lea eax, [ebp+var_40]
0048DEE7 push eax
0048DEE8 call sub_48DF85
0048DEED lea eax, [ebp+var_40] ; будем сравнивать
0048DEF0 push esi ; RN (994D08)
0048DEF1 push eax ; и correct RN
0048DEF2 call sub_4DC410 ; _strcmp - сравнение
0048DEF7 add esp, 1Ch
0048DEFA test eax, eax
0048DEFC jnz short loc_48DF01 ; if не равны, то EAX остается = 0
0048DEFE push 1 ; если равны, то EAX = 1
0048DF00 pop edi
0048DF01 mov eax, edi
0048DF03 pop edi
0048DF04 pop esi
0048DF05 leave
0048DF06 retn
Просмотрев весь этот
кусок кода, мы видим, что в его конце
сравниваются 2 строки (что бы это
могло означать? быть может наш рег.
номер сравнивается с верным рег.
номером?). И если строки равны, то мы
возвращаемся в вызывающую
подпрограмму со значением в
регистре eax = 1 (что нам и нужно). В
противном случае, в регистре eax
будет не 0 (а -01 или FFFFFFFFh). Посмотрим
вверх кода. В строке 0048DEBA вызывается
подпрограмма, и в зависимости от
возвращаемого ей значения в
регистре eax (если оно не равно 0), мы
можем перепрыгнуть через столь
нужное нам сравнение. Значит,
рассматриваем эту подпрограмму.
Вот ее код:
0048DF07 push ebp
0048DF08 mov ebp, esp
0048DF0A sub esp, 40h
0048DF0D push ebx
0048DF0E mov ebx, [ebp+arg_0]
0048DF11 push esi
0048DF12 push edi
0048DF13 movzx eax, byte ptr [ebx]
0048DF16 push eax
0048DF17 call sub_4DCE3A ; _isupper
0048DF1C test eax, eax
0048DF1E pop ecx
0048DF1F jz short loc_48DF80 ; !
0048DF21 mov edi, offset off_4FC9EC
0048DF26 push dword ptr [edi]
0048DF28 lea eax, [ebp+var_40]
0048DF2B push eax
0048DF2C call sub_4DC5A0
0048DF31 lea eax, [ebp+var_40]
0048DF34 push offset a__
0048DF39 push eax
0048DF3A call sub_424F5D
0048DF3F lea eax, [ebp+var_40]
0048DF42 push eax
0048DF43 call sub_4DC390
0048DF48 push eax
0048DF49 lea eax, [ebp+var_40]
0048DF4C push eax
0048DF4D push ebx
0048DF4E call sub_4DC350
0048DF53 mov esi, eax
0048DF55 push 0
0048DF57 neg esi
0048DF59 lea eax, [ebp+var_40]
0048DF5C push 40h
0048DF5E sbb esi, esi
0048DF60 push eax
0048DF61 inc esi
0048DF62 call sub_4DCCA0
0048DF67 add esp, 2Ch
0048DF6A test esi, esi
0048DF6C jnz short loc_48DF80
0048DF6E add edi, 4
0048DF71 cmp edi, offset aUJ
0048DF77 jl short loc_48DF26
0048DF79 xor eax, eax ; !
0048DF7B pop edi
0048DF7C pop esi
0048DF7D pop ebx
0048DF7E leave
0048DF7F retn
0048DF80 push 1
0048DF82 pop eax
0048DF83 jmp short loc_48DF7B
Вкратце поясню главные
моменты. Сразу обратим внимание,
что в строке 0048DF79 регистр eax обнуляется (что нам
и нужно). В сторке 0048DF17 вызывается
функция _isupper, которая проверяет
регистр (верхний или нижний) буквы.
С помощью SoftICE'a устанавливаем, что
проверяется первая буква нашего
рег. номера. Но это цифра, а у цифр
нет различия в регистре. Поэтому
делаем вывод, что в верном рег.
номере первой должна стоять буква.
Какой же нам необходим регистр,
верхний или нижний? С помощью
перехода в строке 0048DF1F делаем вывод,
что если это буква нижнего
регистра, то тогда мы перескочим к
строке 48DF80, где регистр eax принимает
значение 1. Следовательно, нам нужна
буква верхнего регистра. Изменяем
(в который раз) наш рег. номер
(например на "S234B3KY2bp1234562"), но
помним, что символы 5-12 генерируются
из первых 4. Вычисляем символы 5-12.
Теперь наш рег. номер имеет
следующий вид: "S234UAZHscUF1234562".
Ставим точку прерывания на строку 0048DEF1 ,
прервавшись по ней, смотрим, что в
буфере с верным рег. номером (d eax), и
записываем его куда нибудь (кто
может, пусть просто запомнит).
Запрещаем все точки прерывания (bd *).
Опять вводим наши рег. данные, но с
известным нам правильным (ли?)
номером. Нажимаем ОК и... принимаем
поздравляем себя с успешной
регистрацией замечательного
браузера Opera 3.50.
Заключение
Адреса буферов в
силу понятных причин могут не
совпадать (это естественно).
Я старался
объяснять практически все, через
что мы проходим, но ближе к концу
многое пропустил, объяснив лишь
главные моменты. Так будет понятней
новичкам (да и устал я немного к
концу :)
Более легкий
способ: патч - "обман" (см.
начало). Не буду тут писать еще одно
микроисследование, но при запуске
Opera 3.50 мелькает окно с надписью:
Registered to: (unregistered). Это вас ни на что
не наводит ? Поищите в dead-listing'e
вхождение строки (unregistered). А дальше
делайте все сами. :) Это не займет у
вас больше 5-10 минут (в зависимости
от уровня знаний).
К вопросу: to patch or
not to patch. Мой ответ not to patch! если:
можно найти рег. номер
у вас есть время
вам нравится, что в окошке About стоит ваше имя
Если же вы все-таки
решили патчить программу, то
качественно проверьте, все ли
работает как надо? Зачастую
недостаточно пропатчить в одном
месте или убрать например NAG. Opera -
тому подтверждение. Убрав NAG, вы
избавите себя от этого окна, однако
по истечение evaluate time не сможете
использовать этот браузер.
Smasher
p.s. главное качество
снятия защиты, а не количество
"взломанных" программ.
Данная
статья написана исключительно
в учебных целях. Если вы в
дальнейшем собираетесь
использовать рассматриваемую в статье
программу, купите ее!
|
|
|