Delphi World - это проект, являющийся сборником статей и малодокументированных возможностей  по программированию в среде Delphi. Здесь вы найдёте работы по следующим категориям: delphi, delfi, borland, bds, дельфи, делфи, дэльфи, дэлфи, programming, example, программирование, исходные коды, code, исходники, source, sources, сорцы, сорсы, soft, programs, программы, and, how, delphiworld, базы данных, графика, игры, интернет, сети, компоненты, классы, мультимедиа, ос, железо, программа, интерфейс, рабочий стол, синтаксис, технологии, файловая система...
Win-eXpose 2.0 Наглядный пример программы с ленивой защитой
Автор: Smasher

Вышла новая серия книг по программированию:
1-я книга "Java для начинающих"
2-я книга "Java для продолжающих"
3-я книга "Java для кончающих"

Статья ориентирована на новичков и тех, кто вообще в первый раз решил исследовать программу. Если вы уже исследовали раньше программы, то предлагаю вам не читать этот файл, а, взяв программу, самому попробовать изучить ее. Средним, продвинутым и опытным пользователям этот файл не рекомендуетсяне читать вообще (т.к. и написан он в стиле "for Dummies"), что влечет за собой головную боль.

Вступление

Была идея написать статью для тех, кто еще не сделал первый шаг в увлекательный мир Reverse Engeneering. А рассматриваемая программа дополнит ваш "рабочий" комплект Filemon'a, Regmon'a и VxDmon'a.

Необходимые инструменты

  • SoftICE for Win95/98
  • Filemon (необязательно)

Где можно достать эти программы?

Win-eXpose-I/O

Статья

Для начала посмотрим, что представляет из себя эта программа. Запустив ее, мы сразу видим что программа просит зарегистрировать ее по истечении 30 дней. (Сразу отмечаем для себя возможное использование функцииGetLocalTime()). Идем дальше. Выбираем Help->Registration и видим, что программа просит ввести следующие поля:

First,Last Name; Company; Address#1; Address#2; Serial; Password;

(для себя отмечаем, что скорее всего идет проверка Name<->Serial<->Password или Serial<->Password. Но это лишь предположительно и совсем не обязательно).

Итак, первый шаг сделан. Выходим из программы, и запускаем Regmon и Filemon для дальнейшего наблюдения за Win-eXpose. Внимательно изучив операции программы, мы обнаруживаем (с помощью Filemon'a) один интересный файл: C:\Windows\Wxi95.ini . Так, посмотрим, что у нас там такое... Ага! Все понятно. (Вот откуда она знает, что мы unregistered!)

Опять запускаем Win-eXpose и уже знакомой дорогой идем на Registration. Заполняем поля. Я в своем случае заполнил так:


First,Last Name = Smasher
Company = Smashing Bytes
Address#1 = Address1
Address#2 = Address2
Serial = 1234567890
Password = ExploreMe

Сейчас самое время пойти в SoftICE (Ctrl+D) и уcтановить точки прерывания. Хмм... Какие же точки прерывания можно поставить? Естесственно, на вызов функции hmemcpy(), но для начала попробуем старые-добрые GetDlgItemTextA() и GetWindowTextA().

Итак, в SoftICE'е мы ввели: BPX GetDlgItemTextA и BPX GetWindowTextA. Выходим из SoftICE'a (Ctrl+D или F5 или X), нажимаем OK...и мы прервались по GetWindowTextA! Чтобы выйти в вызывающую функцию, нажимаем F12. Мы видим, что попали в модуль MFC40. Это скорее похоже на системную библиотеку, чем на нашу программу, поэтому жмем F12 еще раз. О'кей, итак мы в программе (Wxi95). Одной строкой выше той, что выделена белым цветом, мы видим CALL 004078FE. Это вызов подпрограммы, которая вызывает функцию GetWindowTextA(). Поскольку нам интересно, КУДА функция копирует текст, мы двойным щелчком мышки выделяем эту строку (ставим на ней точку прерывания) - строка CALL 004078FE выделилась цветом. Выходим из SoftICE'a, нажимая F5, и видим, что мы прерываемся по GetWindowTextA еще 6 раз (значит все 6 полей были скопированы в память). Теперь первые две точки прерывания нам уже не нужны (в SoftICE отключаем их с помощью bd 0,1. Вводим bl и видим, что рабочей у нас сейчас осталась только одина точка прерывания).

Еще раз нажимаем OK и мы в программе, но уже ПЕРЕД вызовом нужной нам подпрограммы (которая вызывает GetWindowTextA()). Чтобы войти в эту подпрограмму, нажимаем F8. Мы попадаем в модуль MFC40 и видим следующий код:


(-) PUSH ESI
(-) MOV ESI,ECX
(-) ........
(1) CALL [USER32!GETWINDOWTEXTLENGHTA]
(-) ........
(2) CALL 5F803301
(3) PUSH EAX
(4) PUSH DWORD PTR [ESI+20]
(5) CALL [USER32!GETWINDOWTEXTA]
(-) ........
(-) RET

Открываем наш справочник по API функциям (Я, например, пользуюсь HELP-файлами Windows API Reference и Windows System Class Reference, взятых из пакета BC++ x.0 , но здесь есть одно ограничение - надо знать английский, или иметь переводчик HELP-файлов Stylus или Socrat). Прочитав об этих двух функциях, узнаем, что GetWindowTextLengthA() дает нам длину строки, а GetWindowTextA() - саму строку, причем в строке (3) помещается адрес буфера, куда надо скопировать строку, а в строке (4) количество подлежащих копированию букв. Нас интересует адрес, поэтому с помощью F10 доходим до этой строки, и, когда курсор остановится на ней, даем SoftICE'у команду d EAX. Эта команда выводит в окне данных память в HEX-виде, начиная с адреса, помещенного в EAX. Ничего интересного мы пока не видим. Но это только пока. Нажимаем еще 3 раза F10 и...gotcha!...мы видим в окне данных не что иное как "exploreme" - наш пароль.

С помощью F10 выходим из подпрограммы. Дальше нужно для каждой строки таким же точно образом выяснить их адреса. (Это вы сделайте самостоятельно).

Итак, выяснили. Оказывается, что все они располагаются рядом и это видно в окне данных. Теперь с помощью опять-таки F10 идем до следующего кода ():


(-) mov edi,F422C070
(1) mov eax,dword ptr [ebp-14]
(-) push eax
(2) lea ecx,dword ptr [ebp-00000134]
(-) push ecx
(3) call [KERNEL32!lstrcpy]
(4) mov ecx,dword ptr [ebp-28]
(5) lea edx,dword ptr [ebp-00000234]
(-) push ecx
(-) push edx
(6) call dword ptr [KERNEL32!lstrcpy]
(7) lea ecx,dword ptr [ebp-00000134]
(-) push ecx
(8) call dword ptr [KERNEL32!lstrlen]
(-) mov ebx,eax
(9) test ebx,ebx
(-) jng XXXXXXXX

Нажимаем F10, пока не окажемся за строчкой ниже строки (1). Посмотрим на регистр EAX в окне регистров (если у вас нет окна регистров, то введите wr). Мы видим, что в EAX-содержится... адрес строки с нашим Serial номером! Еще 2 раза нажимаем F10. Запоминаем адрес, находящийся в регистре ECX. Нажимаем F10 - сейчас мы выполнили вызов функции lstrcpy, которая скопировала наш Serial номер по адресу, что был в ECX. Введем d ecx - это действительно так. Далее та же самая процедура копирования происходит и с нашим Password'ом. Новый адрес Password'a опять-таки запомнили. В строке (7) в ECX загружается уже новый адрес строки с нашим Serial номером. Функция lstrlen() возвращает в регистре EAX количество букв (байт), содержащихся в строке (для нашего случая "1234567890" = 10 (десятичн.) = 0Ah (шестнадцатиричн.)). В строке (9) проверяется не равно ли это количество 0.

(jng = "jump if not greater" "переход, если не больше" - это эквивалент jz = "jump if zero" "переход, если ZF-флаг сброшен". Две одинаковых машинных инструкции были введены для большей удобочитабельности программ (в нашем случае - jng использовалась для затруднения понимания). test x,y = and x,y но без помещения результата в x.)

Итак, у нас есть 2 новых адреса, где содержатся наши Serial и Password. Теперь уже ясно, что скорее всего (как мы и предполагали раньше) используется проверка Serial<->Password.

Нажимаем F10 пока не окажемся в следующем фрагменте.


(1) movsx eax,byte ptr [ebp+esi-00000134]
(-) imul eax,esi
(-) mov ecx,eax
(-) lea eax,dword ptr [eax+eax*2]
(-) shl eax,03
(-) lea edx,dword ptr [ecx+eax*8]
(-) lea eax,dword ptr [edx+edx*4]
(-) add eax,ecx
(-) neg eax
(-) add edi,eax
(-) inc esi
(-) cmp esi,ebx
(2) jl 0000552E
(8) push edi
(3) lea eax,dword ptr [ebp-00000134]
(-) push 0040A6A8
(9) push eax
(4) call SER_CODING
(-) add esp,0000000C
(5) lea eax,dword ptr [ebp-00000134]
(6) lea ecx,dword ptr [ebp-00000234]
(-) mov dl,byte ptr [eax]
(7) cmp dl,byte ptr [ecx]

Мы видим, что со строки (1) по строку (2) выполняются различные арифметические операции. Не будем пока вдаваться в их суть. Чтобы побыстрее пройти цикл, который создается в строке (2), поставим двойным щелчком мыши точку прерывания на строке (8) и нажмем F5 (можно также установить курсор на строке (8) и ввести команду here - F.M.). Посмотрим, какой адрес загружается в регистр EAX в строке (3)... это адрес нашего Serial номера!. И в строке (9) он помещается на стек, т.е. для чего-то нужен подпрограмме SER_CODING! Введем в SoftICE'е d eax и с помощью нажатия F10 выполним подпрограмму SER_CODING. Что же мы видим в окне данных??? Это очень похоже на... пароль! Запишем этот пароль и посмотрим дальше на код. В строках (5)-(6) в регистр EAX загружается адрес полученного из Serial-номера пароля, а в регистр EСX - введенный нами Password. И затем сравниваются в строке (7)!!!. Значит наш Password должен совпадать с полученным из Serial'a пароля. Проверим это предположение. Дадим SoftICE'у команду bd * (отключить все точки прерывания) и введем полученный пароль в поле Password. Проверим - работает!

Выйдем из программы, опять запустим ее... заметили разницу? Выберем пункт Help->About... Все О'кей. Сейчас проверим файл C:\Windows\Wxi95.ini. Aга, все понятно.

Все! Наш project is finished!

Примечание

Для данной программы единственное отличие зарегестрированной версии от незарегестрированной состоит в отсутствии Nag screen'a в начале программы и отсутствии раздражающих звуков на каждые 100 I/O failed операций. Поэтому точка прерывания по GetLocalTime здесь не работает, т.к. программа не проверяет сколько дней вы ей пользуетесь.

Легально ли это?

Я даже не хочу напоминать (вы должны знать и помнить это сами), что данный материал публикуется только в образовательных целях. Если вы хотите использовать этот програмный продукт, то вы обязаны купить его. Если же вы не желаете покупать его, то для этого вам достаточно поискать серверах распространяющих пиратские программы. Для этого вовсе не надо понимать механизм работы программы и схему ее защиты. Ответсвенность за ваши действия несете только вы сами.

Проект Delphi World © Выпуск 2002 - 2004
Автор проекта: ___Nikolay