Исследование Add Remove Plus! 2004

Перейти на главную страницу, индексную страницу.


Автор: Haart <evilprogrammer@inbox.ru>

INTRO

Всем, привет! Это моя первая статья, посвящённая такому замечательному ремеслу как Reversing Engineering. В статье будут рассмотрена защита одной замечательной (или очень плохой) программы и соответственно рассмотрен взлом этой защиты. Также мы напишем собственный патч на Delphi. Статья рассчитана на новичков, поэтому здесь всё будет предельно просто и подробно.

Из инструментов нам понадобится следующее:

1. W32Dasm v10
2. Hiew (Hacker's View) v 6.83
3. Delphi

Итак, приступим…

Информация

Add Remove Plus! Берёт на себя решение всех проблем, связанных с установкой и удалением программ. Программа позволяет вам изменять названия, под которыми программа записана в системе, что довольно удобно. Также вы можете увидеть не только название программы, но и её характеристики. Ну и главная задача этой проги удалять программы так, чтобы в реестре и других местах не осталось никаких записей, что программа всё ещё установлена… В общем, довольно таки удобный деинсталлятор, мне в хозяйстве пригодился, вот уже как год его значок маячит у меня на desktop'e. Ну и конечно же в его работе есть некоторые шероховатости, однако, стандартному деинсталлятору от Windows он, бесспорно, даст сто очков вперёд. Скачать можно на www.aurelitec.com

Беглый осмотр

Прога имеет 30-ти дневный Trial период, после истечения, которого она отказывается работать (пишет мол, что Trial Expired... ). При каждом её запуске выскакивает Nag, с напоминанием, о сроке окончания её работоспособности. Сейчас мы с вами сделаем эту работоспособность бесконечной и удалим Nag screen… Let's GO!

Исследование и взлом

Итак, запускаем прогу и начинаем, изучать внезапно выскочивший Nag, т.е смотрим, за что там можно зацепиться. Ага! - Day 1 of 30. Поищем это в SDR (кнопка Strn Ref слева от кнопки принтера). Там эта строка будет выглядеть следующим образом: Day %d of %d. Щелкаем на ней 2 раза и попадаем вот сюда:
* Possible StringData Ref from Code Obj ->"Day %d of %d"
|
:0049D459 B8DCD54900  mov eax, 0049D5DC
:0049D45E E8EDC9F6FF  call 00409E50
Здесь видно, что именно вот эта надпись и появляется на Nag'е, где вместо "%" подставляются конкретные значения. Выводит эту надпись call 00409E50. Теперь посмотрим буквально на несколько строк вверх и увидим нижеследующее:
* Possible StringData Ref from Code Obj ->"Trial Expired"
|
:0049D429 BAC4D54900  mov edx, 0049D5C4
:0049D42E E80D45FCFF  call 00461940
На этот участок кода мы попадем, когда иссякнет наш Trial-период. Здесь вызывается функция call 00461940, которая выводит надпись Trial Expired на Nag screen'е и делает недоступной кнопку Try, при нажатии которой мы и запускали нашу прогу. Значит, где-то должна происходить проверка на исход Trial-периода, если он ещё не вышел, то выводится сообщение на наге о том, сколько ещё дней проге осталось жить (кнопка try в это время доступна), если вышел, то … вы уже сами поняли, что должно произойти. Итак, если подумать логически где-то должна вызываться функция на проверку окончания триальности и стоять условный переход. Условный переход должен находиться перед этими StringRefa'ми, следовательно будем листать вверх пока не найдём какой-либо "прыжок". Хммм… листать даже не пришлось…
:0049D408 3B05D4F24900 cmp eax, dword ptr [0049F2D4]
:0049D40E 7E25         jle 0049D435 ; подозрительный переход
:0049D410 8B45F4       mov eax, dword ptr [ebp-0C]
:0049D413 8B80FC020000 mov eax, dword ptr [eax+000002FC]
:0049D419 33D2         xor edx, edx
:0049D41B 8B08         mov ecx, dword ptr [eax]
:0049D41D FF5164       call [ecx+64]
:0049D420 8B45F4       mov eax, dword ptr [ebp-0C]
:0049D423 8B8008030000 mov eax, dword ptr [eax+00000308]

*Possible StringData Ref from Code Obj ->"Trial Expired"
Теперь нам надо всё проверить. Jle - это jump if less or equal, т.е означает прыгнуть, если меньше или равно . Давайте посмотрим, что произойдёт, если этот jle выполнится. Для этого выделите строку с этим условным переходом (просто щёлкните на ней курсором и она выделится зелёным цветом) и щёлкните на кнопочной панели кнопку Jump To, которая переместит вас куда нужно (чтобы вернуть обратно щёлкните Ret Jmp).
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0049D40E(C)
|
:0049D435 8D45E8      lea eax, dword ptr [ebp-18] ; Вот здесь мы окажемся, если jle выполнится…
:0049D438 50          push eax
:0049D439 A1FC0C4A00  mov eax, dword ptr [004A0CFC]
:0049D43E 8945D8      mov dword ptr [ebp-28], eax
:0049D441 C645DC00    mov [ebp-24], 00
:0049D445 A1D4F24900  mov eax, dword ptr [0049F2D4]
:0049D44A 8945E0      mov dword ptr [ebp-20], eax
:0049D44D C645E400    mov [ebp-1C], 00
:0049D451 8D55D8      lea edx, dword ptr [ebp-28]
:0049D454 B901000000  mov ecx, 00000001

* Possible StringData Ref from Code Obj ->"Day %d of %d"
Т.е этим переходом мы перепрыгнули Trial! А это уже о чём-то говорит. Но давайте ещё проверим. Переводим время на 30 дней вперёд и запускаем прогу. И что же видим мы теперь на нашем наге?- правильно Trial expired. Теперь заменим этот переход на противоположный. У нас стоит jle. Заменим его на противоположный переход jnle (jump if not less or equal). Сначала нужно узнать offset address(смещение в памяти) команды jle. Для этого выделите эту команду и посмотрите строку состояния. В ней вы увидите примерно такую вот картину: @Offset 0009C80E in File. 0009C80E -это и есть то самое смещение. Далее грузите arplus.exe в hiew, когда загрузите, нажмите F4 и выберите Decode mode (режим ассемблерных кодов), затем щёлкните F5 и введите 9C80E (оффсетный адрес инструкции jle) . Теперь вы можете править jle как вам душе угодно. Есть 2 способа. Давайте рассмотрим оба:

1) Побайтовое кодирование. Нажмите на F3, когда стоите на 7E и наберите вместо этого 7F.
2) Ассемблерный код. В hiew'е можно и с ассемблером побаловаться. Нажмите на F3, когда стоите на 7E, затем F2 и замените jle на jnle. Не пугайтесь, когда потом в hiew'e вы увидите jg на месте jnle. Hiew иногда так поступает, но это не на чём не сказывается… После замены байтов нажмите на F9 (обновить файл) и F10 (выход из hiew'а).

Теперь запустим прогу и увидим, что триал не вышел, а прога нормально запускается. Значит, мы были с вами правы, перед переходом осуществляется проверка на исход триальности, но если честно, то это нас не сильно то волнует…
Теперь вы можете изменить этот переход на jmp (код EB), чтобы прыгать в любом случае независимо от проверки на триальность. Таким образом, мы продлили жизненный цикл этой проги чуть ли не до бесконечности, НО NAG ТО ещё ОСТАЛСЯ!!! Значит, надо работать дальше. Первое что приходит на ум мне - крэкеру-новичку - это найти вызов Naga и занопить его. Что ж мы так и сделаем, ведь нам всё надо просто. Это для крэкера-специалиста надо, чтобы взлом был качественен и красив. Нам же новичкам важен только взлом как таковой, но ничего потом мы тоже будем как они… Теперь у нас стоит вопрос, как отловить то место, где происходит вызов Nag.

Вот вам 2 способа как это сделать:

1) Итак, если мы хотим отловить вызов Naga, то нам нужно поставить бряк на адрес в проге перед вызовом нага (например, на уже знакомом нам условном переходе jle) и потом дотрейсить до того момента пока не выскочит наш наг. А затем посмотреть в отладчике, на каком мы адресе, например, это может выглядеть там так:
0015 : 0049D3FE E84162FAFF call 00443644
теперь найдём этот адрес в W32Dasm'e и запатчим его нопами (как это сделать будет описано чуть ниже). Теперь, чтобы поставить бряк на адрес нам нужно как - бы "добраться до программы", т. к бряк на адрес в отличие от бряка на какую-либо функцию сработает, только, если наш процесс всегда активен (чего почти невозможно достичь). Так, что нам нужно как-то затормозить прогу в самом начале, а затем, поставив бряк на адрес, снять тормоз… Для этого перед тем, как запускать программу поставьте в SoftICE прерывание на функцию GetVersion. Запустив, исследуемую программу вы сразу же прервётесь. Теперь поставьте прерывание на адрес и снимите прерывание GetVersion. Затем, нажав F5, вы прервётесь именно там, где и хотели.

Итак:

CTRL+D >>> bpx GetVersion >>> Запускаем ARPlus >>> Мы в SoftICE >>> bpx 49D40E (это бряк на наш адрес, где стоит команда jle) >>> bc0 (сбросим бряк GetVersion.) >>> F5 >>> Мы прервались по 49D40E >>> Трассируем по F10 до тех пор, пока не выскочит знакомый наг >>> Наг выскочил! >>> Запишем адрес функции >>> Занопим…

Кстати этот способ у меня не прокатил, далее вы увидите и поймёте почему.

2) Дизассемблируем W32Dasm'ом наш файл. В меню Debug выбираем Load Process. После этого появится диалоговое окно Load Disassembly Dialog. Здесь ничего вводить не надо - просто нажмите ENTER. Теперь не торопясь, жмите по F8 (Wdasm будет трассировать с самого начала проги) и ждите появления знакомого до безобразия окна (то бишь nag'а), когда он выскочит, запишите адрес функции и вперёд в hiew! Теперь подробно. Итак, первый способ у меня не прокатил, попробуем второй… Делаем всё вышеописанное и начинаем потихоньку трейсить по F8. А вот и Nag нарисовался! Смотрим в окно, где мы трассировали, и видим, что …
:0049D9BE B844DA4900    mov eax, 0049DA44
:0049D9C3 E878F9FFFF    call 0049D340 ; … мы здесь
:0049D9C8 84C0          test al, al
:0049D9CA 7441          je 0049DA0D
Т.е 0049D9C3 это и есть тот адрес функции, где происходит вызов NAG. Запомним этот адрес. Теперь жмём кнопку terminate и попадаем обратно в дизассемблированный листинг. Теперь давайте перейдём на этот адрес, жмём Shift+F12 и вводим 0049D9C3, жмём OK и мы опять…
:0049D9C3 E878F9FFFF    call 0049D340 ; …здесь
:0049D9C8 84C0          test al, al
:0049D9CA 7441          je 0049DA0D
На кнопочной панели вашего WinDasma нажмите Call, чтобы попасть внутрь этой процедуры (чтобы попасть обратно надо нажать Ret). Теперь мы...
* Referenced by a CALL at Address:
|:0049D9C3
|
:0049D340 55 push ebp ; ...здесь
Для удобства и наглядности я приведу для вас некоторые части кода этой процедуры:
* Referenced by a CALL at Address:
|:0049D9C3
|
:0049D340 55           push ebp
:0049D341 8BEC         mov ebp, esp
:0049D343 83C4D0       add esp, FFFFFFD0
:0049D346 53           push ebx
|
---урезано---

:0049D3FE E84162FAFF   call 00443644
:0049D403 A1FC0C4A00   mov eax, dword ptr [004A0CFC]
:0049D408 3B05D4F24900 cmp eax, dword ptr [0049F2D4]
:0049D40E 7E25         jle 0049D435
:0049D410 8B45F4       mov eax, dword ptr [ebp-0C]
:0049D413 8B80FC020000 mov eax, dword ptr [eax+000002FC]
:0049D419 33D2         xor edx, edx
:0049D41B 8B08         mov ecx, dword ptr [eax]
:0049D41D FF5164       call [ecx+64]
:0049D420 8B45F4       mov eax, dword ptr [ebp-0C]
:0049D423 8B8008030000 mov eax, dword ptr [eax+00000308]

* Possible StringData Ref from Code Obj ->"Trial Expired"
|
:0049D429 BAC4D54900   mov edx, 0049D5C4
:0049D42E E80D45FCFF   call 00461940
:0049D433 EB3F         jmp 0049D474

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0049D40E(C)
|
:0049D435 8D45E8       lea eax, dword ptr [ebp-18]
:0049D438 50           push eax
:0049D439 A1FC0C4A00   mov eax, dword ptr [004A0CFC]

---урезано---

* Possible StringData Ref from Code Obj ->"Day %d of %d"
|
:0049D459 B8DCD54900   mov eax, 0049D5DC
:0049D45E E8EDC9F6FF   call 00409E50
:0049D463 8B55E8       mov edx, dword ptr [ebp-18]

---урезано---

:0049D543 8B45F4       mov eax, dword ptr [ebp-0C]
:0049D546 E82965F6FF   call 00403A74
:0049D54B C3           ret  ; выход из функции
Знакомо, не правда ли? Я исследовал поподробнее эту процедуру и обнаружил, что в ней идёт не только проверка на исход Trial-периода. Там также происходит ещё и чтение какой-то лицензионной информации, проверяется множество параметров, связанных с регистрацией и ещё, если мы переведём время вперёд, а потом назад, то из этой процедуры вызовется какое-то окошко с надписью, что мы перевели время назад и типа хрен теперь запустим прогу! Вы не избавитесь от этого проклятого сообщения даже, если переустановите программу. Т.е эта функция как раковая клетка в организме, которая со временем убьёт его, если её вовремя не удалить, так - что будем править… Теперь я думаю, вам стало ясно, почему мы не поймали вызов этой процедуры первым способом. Я совершил ошибку, когда поставил прерывание внутрь процедуры, которую и надо было отловить. Вот почему нефига отловить не получилось. Но ничего это дополнительный опыт на ошибках ведь и учатся. Теперь находясь в подобных ситуациях, мы всегда будем листать на пару экранов вверх. Давайте сразу и пролистаем.
* Referenced by a CALL at Address: ; Эти строки говорят нам о том, что в эту
функцию можно попасть с адреса 0049D9C3
|:0049D9C3 
|
:0049D340 55 push ebp|

---урезано---

:0049D3FE E84162FAFF    call 00443644
:0049D403 A1FC0C4A00    mov eax, dword ptr [004A0CFC]
:0049D408 3B05D4F24900  cmp eax, dword ptr [0049F2D4]
:0049D40E 7E25          jle 0049D435
Перейдя, на этот адрес 0049D9C3 мы и окажемся там, где надо и всё сразу встанет на свои места:
:0049D9C3 E878F9FFFF    call 0049D340 ; мы опять здесь
:0049D9C8 84C0          test al, al
К чему я это всё? - Да вот к чему. Через два дня я ломанул прогу AntiAon 2.0 с одного "пиратского" компакта, советую сломать её и вам. Там примерно такой же случай. Как обычно от сообщения "Trial вышел" мы пролистаем вверх и увидим, что туда можно попасть с 3х мест. Значит, из трёх мест вызывается общая процедура. Находим её и правим, вот и всё! Сразу предупрежу, что не стоит рассматривать всё здесь описанное как эталон. Иногда, при отлове процедуры Naga не обойтись и без нормального отладчика. Бывает, что на вызов процедуры происходит джамп, в общем, смотрите и изучайте - будет полезно в любом случае. Мы отвлеклись. Теперь давайте довершим наш взлом. Сейчас мы должны занопить вот эту процедуру:
:0049D9C3 E878F9FFFF call 0049D340
Т.е нам надо заменить E878F9FFFF на 9090909090. Для этого узнаем offset address данной процедуры. Он равен 9CDC3. Теперь запустим наш hiew и, загрузив в него arplus.exe, нажмём на F4 для выбора режима HEX. Затем нажмите на F5 и настучите 9CDC3. Мы переместились точно на то место, где нужно изменить байты. Жмём F3 и заменяем E878F9FFFF на 9090909090. Ну, вот, пожалуй, и всё. Жмём F9 и F10. Теперь запускаем ARPlus и о ЧУДО! - NAG Screen исчез!!! Мы сломали Add Remove Plus!!!

Ну что ж осталось ещё одно небольшое дельце…

Написание собственного патча

Теперь вам, наверное, не терпится написать для друга крэк, дабы показать ему всю свою крутость (кстати, чем "зеленее" крэкер, тем больше у него мания величия) и сохранить результаты своего труда. В самом деле, не посылать же другу пропатченный EXEшник и хранить оный у себя на диске. В первом случае теряется очень большая часть крутости (ох уж эта новичковая мания величия), а во втором будет чувствоваться какая-то неудовлетворённость взломом, да и места на харде наш файлик будет больше занимать и т.д и т.п. В общем, некрасиво, некруто, НЕПРОФЕССИОНАЛЬНО, не… и т.д и т.п. Вывод: от всего вышеописанного нас может спасти только собственноручный патч. Перед тем как начинать писать, давайте ещё раз глянем вот на это:
:0049D9C3 E878F9FFFF call 0049D340
Если вы уже забыли, то offset здесь был равен 9CDC3, но это offset для одного изменяемого байта (E8), всего же их здесь пять. Теперь, чтобы узнать остальные взгляните в W32Dasme'е на offset адрес следующей строки:
:0049D9C8 84C0 test al, al
Он равен 9CDC8. Из этого становится понятно, что offset адреса для каждого изменяемого байта в строке " :0049D9C3 E878F9FFFF call 0049D340 " равны 9СВС3, 9СВС4, 9СВС5, 9СВС6 и 9СВС7 вот их то мы сейчас и будем править… Писать свой патч мы будем в Delphi, если вы плохо знаете, этот язык или вообще не знаете его, то не стоит отчаиваться. Вы всегда сможете использовать приведённый здесь пример как эталон, особо не врубаясь, что делает каждая команда.

Итак, приступим:

Писать мы будем для консоли, кто хоть немного умеет визуально программировать может оформить всё красиво, как оформляю всегда я. Запустим Delphi. Далее щёлкните по кнопке создания файла. Перед нами появится окно, где мы сможем выбрать то, что именно хотим создать. Нам нужно создать Console Application (приложение для консоли), поэтому нажимайте на него. Теперь введите в появившемся окне вот этот код:
program Patch;
{$APPTYPE CONSOLE}
Uses SysUtils;

Begin

Const
A: Array[1..5] of Record
A : Longint;
B : Byte;
End = ((A:$9CDC3;B:$90),
(A:$9CDC4;B:$90),
(A:$9CDC5;B:$90),
(A:$9CDC6;B:$90),
(A:$9CDC7;B:$90));

Var

Ch:Char;
I:Byte;
F:File;
FN:file of Byte;

Begin

Writeln('Hello!');
Writeln;
Writeln(' Welcome to Crack for Add RemovePlus 2004');
Writeln;
Writeln(' # Cracked by Haart #');
Assign(F,'arplus.EXE');
{$I-}
Reset(F,1);
{$I+}

If IOResult <> 0 then
begin
Writeln;
Writeln(' File not found !!! Crack Aborted...');
Readln;
Halt(1);
end;

For I:=1 to 5 do
Begin
Seek(F,A[I].A);
Ch:=Char(A[I].B);
Blockwrite(F,Ch,1);
End;

Writeln(' File succesfull cracked! Enjoy!!!');

Readln;
End.
Как видите, комментировать весь этот кодец я не стал, т.к подобный патч полностью уже закомментировал Vallkor в одной из своих статей (советую почитать)… Теперь сохраните этот файл и нажмите CTRL+F9, чтобы скомпилировать его. Скомпилированный файл EXE появится там, где вы только что сохранили файл проекта. Крэк готов! - приятного аппетита!!!

Да, чуть не забыл! Маленькая деталь. Если в крэкнутом ARP нажать на кнопку Help и выбрать там About, то мы увидим, что там стоит Trial Version, что же подумает друган, когда увидит это?!...

Что можно предпринять:

1) Если вы взламываете только для себя, то можно отыскать эту строку в hiev'e, в режиме Hex по поиску (F7) и затереть её пробелами.
2) Если вы взламываете для кого-либо другого, то в данном случае можно открыть программу в рестораторе и там открыть раздел "RCDATA", в котором хранятся все строки форм и их параметры. В ресурсе TF About вы и найдёте интересующую нас строку:
...
object lVersionType: TLabel
Left = 24
Top = 37
Width = 96
Height = 19
Caption = 'Trial Version' ; Вот она зараза
Font.Charset = EASTEUROPE_CHARSET
Font.Color = clWindowText
Font.Height = -16
Font.Name = 'MS Sans Serif'
Font.Style = [fsBold]
ParentFont = False
Transparent = True
Теперь меняйте Trial Version на всё что вашей душе только угодно. Я лично изменил на "Cracked by Haart". Для пущей крутости можно сменить ещё и Font.Color = clWindowText на Font.Color = clRed. Теперь создайте RPпатч и посылайте его другу вместе с кряком…

Конечно, этот вариант не очень то хорош, но согласитесь, пусть лучше будет крэк о двух частях, чем Trial Version в About… Запомните, что RCDATA присутствует только в программах, написанных на Delphi, во всех остальных случаях "копайте" dialogs и strings…

Послесловие

Это моя первая статья и в ней, конечно же, присутствуют ошибки, опечатки ну и т.д. К тому же защита сломана не профессионально, а самым "низшим" способом. Но попрошу вас особо не пинать, т.к. я всё-таки пока не настоящий крэкер, а новичок, который занимается reversing' ом всего лишь две недели… Но думаю, что у меня ещё всё впереди…

Еcли у вас что-то не получилось, либо было что-то непонятно(!) пишите и я вам обязательно отвечу…

Best regards, Haart...
EvilProgrammer@Inbox.ru


Материалы находятся на сайте http://cracklab.narod.ru/doc/






Hosted by uCoz