Исследование Opera 3.50

Объект исследования

Программой, которую мы сегодня будем исследовать будет достаточно популярный в последнее время браузер Opera 3.50. Скопировать программу можно по адресу: http://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-мя способами:

  1. С помощью линейки прокрутки в окне data window.
  2. С помощью известного нам смещения. Нам известно, что в старом блоке памяти Name хранилось по адресу 994AB0, а Organization по адресу 994BDC. Найдем смещение между ними - в SoftICE'e дадим команду:
    ? 994BDC-994AB0
    Возьмем полученный ответ в шестнадцатиричном формате (12С) и приплюсуем его к началу нового блока памяти:
    ? 994F74+12C
    Получили 9950A0. Аналогично вычисляется новый адрес для RN.
  3. С помощью поиска строки. Дадим команду:
    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 под любые настройки (размеры окон, сами окна и т.д.). Например, следующими двумя способами:

  1. отредактировать строку INIT="X" как, например,
    INIT="X; wl; wr; wd 10; width 98; lines 73; wc 40;"

    В этом случае данные установки автоматически выполнятся при первом входе в SoftIСE.

  2. отредактировать значение любой свободной "горячей клавиши". Например, отредактируем значение 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! если:

  1. можно найти рег. номер
  2. у вас есть время
  3. вам нравится, что в окошке About стоит ваше имя

Если же вы все-таки решили патчить программу, то качественно проверьте, все ли работает как надо? Зачастую недостаточно пропатчить в одном месте или убрать например NAG. Opera - тому подтверждение. Убрав NAG, вы избавите себя от этого окна, однако по истечение evaluate time не сможете использовать этот браузер.

Smasher

p.s. главное качество снятия защиты, а не количество "взломанных" программ.

Данная статья написана исключительно в учебных целях. Если вы в дальнейшем собираетесь использовать рассматриваемую в статье программу, купите ее!


Дальше


Образование на Куличках