Итак, мы имеем не зарегистрированную и упакованную программу. Наша
задача распаковать и "честно" зарегистрировать не прибегая к bithack'у.
Статья состоит из двух независимых друг от друга частей:
- Распаковка - требует навыков работы с отладчиком sice и опыта
по распаковке. - Исследование и взлом уже распакованной программы будет
очень полезен начинающим, так что новичкам советую начать со
второй части.
Для скачивания доступны, как запакованная так и
распакованная версия подопытной программы. Защита самой программы очень слабая, но взята
из реальных shareware программ!!
Часть 1. Распаковка.
Посмотрим на программу изнутри с помощью Hiew, код программы полностью
упакован, имея ввиду размер и секции можно предположить, что это
Delphi, также все секции имеют название PEPACK. Кто не имел дел с PEPACK, могут в это
поверить, но лучше запустить программу PeiDentifier и проверить ей, она
скажет точнее. В действительности, никакой это не PEPACK, а
"tElock 0.98b1 -> tE!" шифрующийся под другие упаковщики\протекторы.
С помощью peeditor'а посмотрим некоторые данные, а именно
imagebase=400000 и EP (точка входа)=6bbd6 указывает в код упаковщика.
Чем упаковано мы знаем, приступим к распаковке. Найдем
OEP (настоящая точка входа). Универсальное решение нахождение OEP, для этого протектора,
это использование icedump для sice. icedump - это набор расширений для
sice, он включает много полезных команд, начинающихся с '/', таких как
/dump, /tracex и т.д. Последнюю мы и будем сейчас использовать, она позволяет
без всяких копаний в коде протектора найти OEP. Итак. Поставим bpx на
getversion и запустим программу, мы оказываемся в SIce, уберем поставленный
bpx (bc*) и жмем f12 пока не окажемся в testpr, теперь введем:
/tracex 400000 459000, и нажмем f5, когда всплывет sice мы будем
точно в OEP=4563в4 в уже распакованной программе. Зациклим ее находясь на
OEP, для этого введем "e eip" и в окно данных, куда
переместится курсор, запишем "ebfe", то
есть установим прыжок на себя, выходим из sice по f5.
Загружаем peeditor, входим в tasks и там делаем full dump на нашем процессе. Теперь откроем
ImpREC, программа в помощь по восстановлению импорта, выберем наш процесс и в
поле OEP введем "000563d4" (вычисляется 4563d4(oep)-400000(imagebase)=563d4),
жмем IAT AutoSearch, затем GetImport. Импорт распознался, кроме двух модулей.
Это защита импорта у TELock. Ладно. Сделаем это вручную. Смотрим куда указывают
нераспознанные функции первого модуля. 700000, 700010, 700020... смотрим что происходит
по этому (700000) адресу в памяти, там в eax кладется адрес и помещается в стек,
смотрим по этому адрес 700691 - это и есть наш недостающий импорт. Сохраняем
его в файл unr.imp, введем "/dump 700691 a5 d:\cracking\2\unr.imp". Теперь у нас
есть все функции первого нераспознанного модуля. Ищем импорт второго нераспознанного
модуля. Смотрим куда ссылаются его ф-ии, это 330000.., идем по этому адресу,
снова смотрим на сохраняемое в eax значение(c30a69) - это он, мы его нашли,
сохраняем в файл unr2.imp, "/dump c30a69 a5 d:\cracking\2\unr2.imp". Все, мы
получили весь импорт. Делаем fixdump в ImpRec'е на тот файл, который мы сдампили
в peeditor'е. Теперь осталось поправить 2 байта по OEP и вставить сохраненный в
файлах импорт. Открываем его в hiew, меняем первые 2 байта по OEP, с
ebfe обратно на 558b. И вставляем недостающий импорт на место 5a104 для unr.imp и 5a300 для
unr2.imp. Запускаем. Работает! На этом часть с распаковкой завершена.
Часть 2. Исследование, взлом.
Что надо знать.
NagScreen - своеобразная реклама, обычно напоминает о регистрации в
незарегистрированных shareware (платных) программах, появляющаяся при
каждом запуске. В нашей программе представлен в виде окна с надписью
"NagScreen", которое не позволяет работать с основной формой пока
активно. Для закрытия достаточно кликнуть по нему мышкой.
Навесная защита с программы снята, теперь мы можем перейти к
непосредственно защите самой программы. Итак, для начала рассмотрим,
что она из себя представляет. Запускаем программу
- появляется тот самый NagScreen, наша задача безболезненно для программы этот Nag убрать,
причем будем делать это не bithack'ом, то есть модифицированием байт в коде
программы, т.к. в данном примере (программе) Nag не зависит от регистрации,
сделано это специально, чтобы мы могли поупражняться с отключением
назойливой рекламы. Кликаем по Nag'у и смотри, что происходит дальше,
а дальше нам пишут "Not Registered", при этом никаких полей ввода пароля
для регистрации нам не предоставляю, значит наша задача самим отследить,
что проверяет программа. Начнем исследование. Как мы уже знаем, программа
написана на delphi, значит в исследовании нам очень поможет одна
великолепная программа под названием DEDE by DaFixer - это лучший, специальный
дизассемблер для Delphi. Запускаем его, загружаем testpr.exe в него и жмем
process, немного ждем, все "Dump successfull!", нажимаем OK и теперь у нас
есть полный готовый листинг со всеми процедурами и функциями. Войдем во
вкладку "Procedures", в Class Name выберем TForm1 - это главная форма,
та где нам писали "Not Registered", в правой части у нас появились процедуры
этой формы, среди них видим:
Events:
Button1Click
FormActivate
FormCreate
Unit1.Finalization
Unit1.Initialization
Видим, что здесь присутствует событие для какой-то кнопки - Button1Click,
но на главной форме никаких кнопок мы не видели, очевидно кнопка скрыта и
все для нас впереди. Ладно. Нас интересует процедуры FormActivation и FormCreate,
т.к. FormCreate выполняется в первую очередь смотрим сначала его
(два раза кликаем мышью на FormCreate). Открывается
новое окно с ассемблерным листингом этой процедуры.
Прокрутим немного вниз, видим "Registrationincorrect", "Thanks for registration",
то что надо, видимо в этой процедуре проверяется регистрационная информация.
Смотрим с начала процедуры, что в ней происходит:
* Possible String Reference to: 'reg.chk'
00455D1A B820604500 mov eax, $00456020
* Reference to: Unit_00407554.Proc_004088A4
00455D1F E8802BFBFF call 004088A4 ; проверяем существование файла
'reg.chk'
00455D24 84C0 test al, al
00455D26 0F84B7020000 jz 00455FE3 ; если нет выходим.
* Reference to Form1
00455D2C 8B45FC mov eax, [ebp-$04]
* Reference to control TForm1.Memo1 : TMemo
00455D2F 8B80F4020000 mov eax, [eax+$02F4]
* Reference to field TMemo.Lines : TStrings
00455D35 8B8020020000 mov eax, [eax+$0220]
* Possible String Reference to: 'reg.chk'
00455D3B BA20604500 mov edx, $00456020
00455D40 8B08 mov ecx, [eax]
* Reference to method TStrings.LoadFromFile(string)
00455D42 FF5168 call dword ptr [ecx+$68]
00455D45 8D4DF0 lea ecx, [ebp-$10]
Итак, программы использует Memo для загрузки в него внешнего файла 'reg.chk',
посредством LoadFromFile. Очевидно этот файл и есть файл регистрации.
Смотрим дальше.
* Reference to field TMemo.Lines : TStrings
00455D51 8B8020020000 mov eax, [eax+$0220]
00455D57 33D2 xor edx, edx ; edx - index, т.к. xor
edx, edx=0 берем первую строку. (нулевая строка, для
нас будет первая)
00455D59 8B18 mov ebx, [eax] ; указатель на memo
* Reference to method TStrings.Strings [ Index()
00455D5B FF530C call dword ptr [ebx+$0C]
00455D5E 8B45F0 mov eax, [ebp-$10] ; теперь здесь первая строка из Memo
* Possible String Reference to: '[REG-DATA-TESTPROGRAMM]'
00455D61 BA30604500 mov edx, $00456030 ; вшитая строка=[REG-DATA-TESTPROGRAMM]
* Reference to: System.@LStrCmp;
00455D66 E815E9FAFF call 00404680 ; сравнение двух вышеупомянутых двух строк
00455D6B 0F8572020000 jnz 00455FE3 ; если равны продолжаем дальше, иначе прыгаем на 00455F6D и регистрации нам не видать.
Здесь программа с помощью Lines (index) берет первую строку из
Memo и сравнивает ее со строкой константой [REG-DATA-TESTPROGRAMM], если
строки равны идем дальше, значит первую строку в фале
'reg.chk' мы уже знаем.
Создадим файл 'reg.chk', в него с помощью блокнота запишем первую строку,
которую мы только что узнали. Т.о. файл будет выглядеть так:
--reg.chk------
[REG-DATA-TESTPROGRAMM]
---------------
Смотрим дальше.
00455D83 BA01000000 mov edx,
$00000001 ; edx=index =1
00455D88 8B18 mov ebx, [eax] ; указатель на memo
* Reference to method TStrings.Strings [ Index()
00455D8A FF530C call dword ptr [ebx+$0C]
00455D8D 8B55EC mov edx, [ebp-$14] ; теперь здесь вторая строка из Memo
* Reference to: System.@LStrLen(String):Integer;
00455D9F E898E7FAFF call 0040453C ; получаем длину второй строки в eax
00455DA4 85C0 test eax, eax ; если длина<1, выходим, прыгаем на 00455D16
00455DA6 7E17 jle 00455DBF
00455DA8 BA01000000 mov edx, $00000001
00455DAD 8B0D549C4500 mov ecx, [$00459C54] ; ecx=вторая строка из Memo
00455DB3 0FB64C11FF movzx ecx, byte ptr [ecx+edx-$01] ; теперь в ecx помещаем код n-го символа строки, символы перебираются, по мере
увеличения edx.
00455DB8 014DF8 add [ebp-$08], ecx ; добавляем к [ebp-$08] код n-го символа, т.е. накопление суммы символов
00455DBB 42 inc edx ; увеличивает edx на 1, делает возможным перебор.
00455DBC 48 dec eax ; в eax, длина строки, здесь мы уменьшаем
; ее на 1, создавая условие выхода из цикла, при eax=0 - выход.
00455DBD 75EE jnz 00455DAD ; цикл
Значит здесь программа берет вторую строку из Memo и получает из нее хэш, путем накопления
суммы символов. К концу цикла хэш находится в [ebp-$08].
00455DD1 BA02000000 mov edx, $00000002
00455DD6 8B18 mov ebx, [eax]
* Reference to method TStrings.Strings [ Index()
00455DD8 FF530C call dword ptr [ebx+$0C]
00455DDB 8B55E8 mov edx, [ebp-$18]
00455DDE B8589C4500 mov eax, $00459C58
* Reference to: System.@LStrAsg(void;void;void;void);
00455DE3 E8F0E4FAFF call 004042D8
00455DE8 A1589C4500 mov eax, dword ptr [$00459C58]
* Reference to: System.@LStrLen(String):Integer;
00455DED E84AE7FAFF call 0040453C
00455DF2 85C0 test eax, eax
00455DF4 7E17 jle 00455E0D
00455DF6 BA01000000 mov edx, $00000001
00455DFB 8B0D589C4500 mov ecx, [$00459C58]
00455E01 0FB64C11FF movzx ecx, byte ptr [ecx+edx-$01]
00455E06 014DF4 add [ebp-$0C], ecx
00455E09 42 inc edx
00455E0A 48 dec eax
00455E0B 75EE jnz 00455DFB
Здесь происходит тоже самое, что и в предыдущем цикле, только для третьей строки и накопление
производится в [ebp-$0C].
00455E0D 8B45F8 mov eax, [ebp-$08] ;
умножаем полученные хэши
00455E10 F76DF4 imul dword ptr [ebp-$0C] ; друг на друга,
результат в eax
00455E13 8945F8 mov [ebp-$08], eax
00455E16 8D4DE4 lea ecx, [ebp-$1C]
* Reference to Form1
00455E19 8B45FC mov eax, [ebp-$04]
* Reference to control TForm1.Memo1 : TMemo
00455E1C 8B80F4020000 mov eax, [eax+$02F4]
* Reference to field TMemo.Lines : TStrings
00455E22 8B8020020000 mov eax, [eax+$0220]
00455E28 BA03000000 mov edx, $00000003 ; edx=index =3, берем четвертую строку из Memo
00455E2D 8B18 mov ebx, [eax] ; указатель на memo
* Reference to method TStrings.Strings [ Index()
00455E2F FF530C call dword ptr [ebx+$0C]
00455E32 8B45E4 mov eax, [ebp-$1C] ; теперь здесь четвертая строка из Memo
* Reference to: Unit_00407554.Proc_0040866C
00455E35 E83228FBFF call 0040866C
00455E3A 3B45F8 cmp eax, [ebp-$08] ; сравниваем, четвертую строку с результатом
; перемножения хэшей, если равны идем дальше
00455E3D 0F85A0010000 jnz 00455FE3
Здесь умножаются полученные хэши из второй и третьей строки друг на друга:
[ebp-$08] на [ebp-$0C] и сравниваются с четвертой строкой из Memo. Значит теперь мы легко можем
вычислить четвертую строку из двух предыдущих. Представим, что 2 и 3 строка есть имя и компания,
а из этих данных вычисляется пароль (4 строка).
Допишем имя и компанию в наш файл регистрации.
--reg.chk------
[REG-DATA-TESTPROGRAMM]
YourName
YourCompany
---------------
Теперь вычислим из этих данных пароль. Сделаем это на том же
Делфи. Добавим 3 edit'а на форму и кнопку. Добавим кнопке событие onlick:
procedure TForm1.Button1Click(Sender: TObject);
var
i,sum_name,sum_company:integer;
begin
for i:=1 to length(edit1.text) do
sum_name:=ord(edit1.text[i])+sum_name;
for i:=1 to length(edit2.text) do
sum_company:=ord(edit2.text[i])+sum_company;
edit3.text:=inttostr(sum_name*sum_company);
end;
Введем в edit1 имя, в edit2 компанию и в edit 3 получим
наш код, в случае этих данных код получился 944928, добавим
его в файл:
--reg.chk------
[REG-DATA-TESTPROGRAMM]
YourName
YourCompany
944928
---------------
Это еще не все, впереди очередная проверка.