РУКОВОДСТВО ПО SOFTICE

СОДЕРЖАНИЕ

1. Введение в ломание Windows-программ
2. Обзор SoftICE/Win 2.oo
3. Поиск регистрационных кодов
3.1 Task Lock 3.00 - простая защита на основе серийного номера
3.2 Command Line 95 - простая регситрация "имя-код"
4. Создание генератора ключей для Command Line 95
5. Как работают инструкции PUSH и CALL когда программа вызывает функцию
6. О программах, написанных на Visual Basic

ПРИЛОЖЕНИЯ

A. Как в SoftICE загружать символьные имена (имена функций etc)
B. Синтаксис функций GetWindowText, GetDlgItemText и GetDlgItemInt
C. Где найти программы
D. Как связаться с автором


1. ВВЕДЕНИЕ В ЛОМАНИЕ WINDOWS-ПРОГРАММ

Ломать программы Windows в большинстве случаев даже проще, чем ломать программы Dos. В Windows сложно что-нибудь скрыть от того, кто ищет, особенно если программа использует стандартные функции Windows.Первая (и часто единственная) вещь, которая Вам потребуется - это SoftICE/Win 2.oo, мощный отладчик от фирмы NuMega. Некоторым людям он кажется очень сложным в использовании, но я расскажу Вам, как с ним управляться и, я надеюсь, Вы поверите мне. =) В приложении A я привел некоторую информацию, которую Вам следует прочитать.
URL всех программ, которые Вам понадобятся, приведены в приложении C.

2. ОБЗОР SOFTICE/WIN 2.OO

Ниже приведен очень схематичный рисунок, демонстрирующий окно SoftICE:

qqРегистры 'R' - правка значения регистров
Окно данных 'D' - просмотр памяти, 'E' - правка памяти
qОкно кода 'U' - просмотр кода по адресу, 'A' - вставка кода
Окно команд Здесь Вы набираете команды


Другие важные клавиши (в стандартной настройке):
'H'/F1 - помощь
F5/Ctrl+D - запуск программы (или продолжение прерванной программы)
F8 - пошаговая отладка с заходом в тело функции
F10 - пошаговая отладка без захода в тело функции
F11 - выйти из функции (будет работать только до первого PUSH в функции)


3. ПОИСК РЕГИСТРАЦИОННЫХ КОДОВ

Возможно, наилучший способ попрактиковаться - это найти где-нибудь шареварную (shareware) программку и попытаться зарегистрировать ее.

3.1 Task Lock 3.00 - простая защита на основе серийного номера

Это очень простая защита: номер не зависит ни от каких факторов.

3.1.1 Медицинское обследование

Какой разрядности программа - 16 или 32 бит? Где вводится регистраци- онная информация? Даст ли мне справка какие-нибудь предположения о том, как устроена регистрация? Попробуйте ответить на эти вопросы перед тем, как мы продолжим.
....Сейчас Вы должны быть заняты обследованием....Вы заняты обследо- ванием? ...Ну как, уже все?...
OK, теперь Вы знаете, что это 32-битное приложение, работающее под Windows 95 и что регистрация заключается в заполнении регистрационного номера в диалоговом окошке, которое появляется когда Вы выбираете меню "Register|Register...". Из справки Вам также стало известно, что существует два типа регистрации: для индивидуального использования и для использо- вания в "конторе" (в оригинале - site license). Поэтому очень вероятно, что в программе будет ДВЕ проверки регистрационных кодов.
3.1.2 Прерывание программы

Регистрационные коды чаще всего вводятся в обычных строчках ввода типа Windows Edit. Чтобы проверить код, программа должна прочитать содер- жимое строки ввода при помощи одной из функций:

16-бит

32-бит

GetWindowText
GetDlgItemText
GetWindowTextA, GetWindowTextW
GetDlgItemTextA, GetDlgItemTextW


Последняя буква в названии 32-битных функций говорит о том, какие строки использует эта функция: однобайтовые или двухбайтовые. Двух- байтовые строки используются ОЧЕНЬ редко.Возможно, что Вы уже уловили мою мысль. "Если бы можно было прерваться по вызову GetWindowText..." - и Вы МОЖЕТЕ это сделать!!! Но сперва Вы должны убедиться, что символьные имена (имена функций) загружены SoftICE'ом. Если Вы не знаете, как это сделать - см. приложение A.
Чтобы установить "ловушку" (на самом деле это называется точкой останова или брейкпоинтом) в SoftICE, Вы должны зайти в отладчик нажатием кла- виш Ctrl-D и использовать команду BPX. В качестве параметра команды можно использовать либо имя функции, либо непосредственно адрес. Так как наш "объект изучения" (Task Lock) является 32-битным приложением, мы должны поставить брейкпоинт на функцию GetWindowTextA. Если это не поможет, попробуйте поставить брейкпоинт на другие функции. В командной строке SoftICE наберите следующее:

:bpx getwindowtexta

Если Вы получите сообщение об ошибке (например, "No LDT"), убедитесь, что в фоне у Вас не выполняются никакие другие приложения. Я заметил, что Norton Commander в фоне является причиной подобного поведения SoftICE. Вы можете проверить наличие брейкпоинтов командой:
:bl

В результате Вы увидите что-нибудь типа:

00) BPX USER32!GetWindowTextA C=01

Чтобы выйти из отладчика, нажмите Ctrl-D (или F5) еще раз.

Продолжим... Итак, Вы установили брейкпоинт и теперь SoftICE будет "выскакивать" при каждом вызове функции GetWindowTextA. Попробуем ввести какое-нибудь значение в окне регистрации и нажмем OK. Вы нажимаете OK... ...и получаете дурацкое сообщение о том, что Ваш код был неправильным. Значит, это была не функция GetWindowTextA... Попробуем GetDlgItemTextA. Удалим старый брейкпоинт:

:bc 0

(0 - это номер брейкпоинта в списке брейкпоинтов)

И установим новый:

:bpx getdlgitemtexta

Ну что ж, попробуем еще раз...


3.1.3 В отладчике

Wow! Работает! Теперь вы в SoftICE, в самом начале функции GetDlgItemTextA. Чтобы попасть туда, откуда она была вызвана, нажмите F11. Теперь Вы внутри модуля SGLSET.EXE. Если Вы не уверены - посмот- рите на строчку между окном кода и окном командной строки, она должна выглядеть так:

----------SGLSET!.text+1B13----------

Сейчас Вы уже можете запретить реакцию на вызов функции:

:bd 0

Если Вам вдруг захочется снова разрешить ее, наберите:

:be 0

Первая строка в окне кода выглядит так:

CALL [USER32!GetDlgItemTextA]

Чтобы посмотреть строчки над ней, нажимайте Ctrl+Up ("стрелка вверх") до тех пор, пока не увидите нижеприведенный кусок кода. Если Вы ничего не понимаете в Ассемблере, я добавил комментарии которые могут Вам помочь.
RET йййййййййййййй й й йййй; Конец функции
PUSH EBP йййййййй ййй й ййй; Начало другой функции
MOV EBP, ESP ййййй й йййййй; ...
SUB ESP, 0000009C й й й й й йй; ...
PUSH ESI йййй й й й й й й й й й; ...
> LEA EAX, [EBP-34] й й й йй й ; EAX = EBP-34
PUSH EDI ййййййй й й й й й йй; ...
MOVE ESI, ECX йй й ййй й й йй; ...
PUSH 32 ййййййййййй й ййййй; Макс. длина строки
> PUSH EAX ййййййй й йййййй; Адрес текстового буфера
PUSH 000003F4 ййййй й й й й й ; Идентификатор управления
PUSH DWORD PTR [ESI+1C] йй ; Идентификатор окна диалога
CALL [USER32!GetDlgItemTextA] ; Получить текст

Команды PUSH означают сохранение значений для последующего использования. Я пометил важные строчки символом '>'. Глядя на этот код, мы видим, что адрес текстового буфера хранился в регистре EAX и что EAX был EBP-34h.Поэтому нам стоит взглянуть на EBP-34h:
:d ebp-34

Вы должны были увидеть текст, который вы ввели в диалоговом окне. Теперь мы должны найти место, где Ваш номер сравнивается с реальным серийным номером. Поэтому мы пошагово трассируем программу при помощи F10 до тех пор, пока не встретим что-нибудь о EBP-34. Не про- йдет и нескольких секунд, как Вы наткнетесь на следующий код:
> LEA EAX, [EBP+FFFFFF64] йййййййййй; EAX = EBP-9C
LEA ECX, [EBP-34] ййййййййййййййй й й; ECX = EBP-34
PUSH EAX йййййййййййййййййййййййй; Сохраняет EAX
PUSH ECX йййййййййййййййййййййййй; Сохраняет ECX
> CALL 00403DD0 ййййййййййййййй ййй; Вызывает функцию
ADD ESP, 08 йййййййййййййййййй йй йй; Удаляет сохраненную информацию
TEST EAX, EAX йййййййййййййййййй йй; Проверяет значение функции
JNZ 00402BC0 ййййййййййййййййййй йй; Прыгает, если не "ноль"

Мне кажется, что это выглядит как вызов функции сравнения двух строк. Эта функция работает так: на входе - две строки, на выходе - 0, если они равны и любое другое значание, если не равны.
А зачем программе сравнивать какую-то строчку с той, что Вы ввели в окне диалога? Да затем, чтобы проверить правильность Вашей строки (как Вы, возможно, уже догадались)! Так-так, значит этот номер скрывался по адресу [EBP+FFFFFF64]? SoftICE не совсем корректно работает с отрицательными числами и поэтому настоящий адрес следует посчитать:
100000000 - FFFFFF64 = 9C

Вы можете сделать это вычисление прямо в SoftICE:

:? 0-FFFFFF64

Число 100000000 слишком велико для SoftICE, а вычитание из 0 дает тот же
самый результат.

Наконец пришло время взглянуть, что же скрывается по адресу EBP-9C...

:d ebp-9c

В окне данных SoftICE Вы видите длинную строчку цифр - это серийный номер!Но Вы помните, что я говорил Вам раньше? Два типа регистрации - - два разных серийных номера. Поэтому после того, как Вы записали на бумажечку первый серийный номер, продолжайте трассировать программу при помощи F10. Мы дошли до следующего куска кода:
> LEA EAX, [EBP-68] ййййййййййййййййййй; EAX = EBP-68
LEA ECX, [EBP-34] йййййййййййййййййй йй; ECX = EBP-34
PUSH EAX йййййййййййййййййййййййй й й; Сохраняет EAX
PUSH ECX ййййййййййййййййййййййййййй; Сохраняет ECX
> CALL 00403DD0 ййййййййййййййййййййй; Снова вызывает функцию
ADD ESP, 08 йййййййййййййййййййййй ййй; Удаляет сохраненную информацию
TEST EAX, EAX ййййййййййййййййййййййй; Проверяет значение функции
JNZ 00402BFF йййййййййййййййййййййй йй; Прыгает если не "ноль"

И что Вы видите по адресу EBP-68? Второй серийный номер!

:d ebp-68

Вот и все... Я надеюсь, что у Вас все получилось как доктор прописал? =)

3.2 Command Line 95 - легкая регистрация "имя-код", создание генератора ключей

Это программа - хороший пример, с легким алгоритмом генерации кода.

3.1.1 "Обследование"

Вы осмотрели программу и увидели, что это 32-битное приложение, требующее имя и код в окне регистрации. Поехали!
3.1.2 Прерывание программы

Мы поступаем так же, как и с Task Lock'ом - ставим брейкпоинты. Можно даже поставить сразу два брейкпоинта на наиболее возможные функции: GetWindowTextA и GetDlgItemTextA. Нажмите Ctrl-D, чтобы вызвать отладчик и наберите в окне команд:
:bpx getwindowtexta
:bpx getdlgitemtexta


Теперь возвращайтесь в прерванную программу, идите в окно регистрации и вводите имя и какой-нибудь номер (обыкновенное целое число - это наиболее вероятный код). Я написал примерно следующее:
Name: XXXX
Code: 12345


Программа остановилась на GetDlgItemTextA. Так же, как и в случае с Task Lock'ом, мы нажимаем F11 чтобы вернуться в вызывающюю функцию. Просматриваем окно кода при помощи Ctrl+Up. Вызов функции выглядит так:
MOV ESI, [ESP+0C]
PUSH 1E qqqqqqqqqqqqqqqqqqq; Максимальная длина
PUSH 0040A680 qqqqqqqqqq q q; Адрес буфера
PUSH 000003ED qqqqqqqqqqqqq; Идентификатор управления
PUSH ESI qqqqqqqqqqqqqqqq qq; Идентификатор окна диалога
CALL [User32!GetDlgItemTextA]

Число 40A680 кажется нам интересным, поэтому мы проверяем этот адрес:

:d 40a680

Что же видно в окне данных, как не имя, которое мы ввели? =) А теперь взглянем на кусок кода под вышеприведенным:
PUSH 00 qqqqqqqqqqqqqqqqqqq; (не интересно)
PUSH 00 qqqqqqqqqqqqqqqqqqq; (не интересно)
PUSH 000003F6 qqqqqqqqqqqqq; Идентификатор управления
MOV EDI, 0040A680 qqqqqqqqq; Адрес буфера
PUSH ESI qqqqqqqqqqqqqqqqqq; Идентификатор окна диалога
CALL [User32!GetDlgItemInt]

Функция GetDlgItemInt похожа на GetDlgItemTextA, но возвращает не строку, а целое число. Она возвращает его в регистре EAX, поэтому мы трассируем этот код (F10) и смотрим, что же у нас появилось в окне регистров после вызова функции... В моем случае оно выглядит так:
EAX=00003039

А что такое шестнадцатеричное 3039? Наберем:

:? 3039

И получим следующее:

HEX

DEC

ASCII

00003039

0000012345

09


Как Вы видите (и, возможно, уже догадались) это код, который Вы ввели в диалоговом окне. Ok, что теперь? Посмотрим дальше:
MOV [0040A548], EAX qqqqqqqq ; Сохраняет рег. код
MOV EDX, EAX qq
qqqqqqqqqqqq; А также помещает его в EDX


3.1.3 Подсчитывание регистрационного кода

Мы достигли места, где подсчитывается реальный регистрационный код!

MOV ECX, FFFFFFFF qqqqqqqqqqqqqqqqqq; Эти строчки подсчитывают
SUB EAX, EAX qqqqqqqqqqqqqqqqqqqqqqqq; длину строки
REPNZ SCASB qqqqqqqqqqqqqqqqqqqqqqqq; .
NOT ECX qqqqqqqqqqqqqqqqqqqqqqqqq qqq; .
DEC ECX qqqqqqqqqqqqqqqqqqqqqqqqq q qq; ECX теперь содержит длину
MOVSX EAX, BYTE PTR [0040A680] qqqqqq; Получает байт по адр. 40A680h
IMUL ECX, EAX qqqqqqqqqqqqqqqqqqqq qqq; ECX = ECX * EAX
SHL ECX, 0A qqqqqqqqqqqqqqqqqqqqqqqq qq; Сдвиг влево на 0Ah бит
ADD ECX, 0002F8CC qqqqqqqqqqqqqqqq qqq; Добавляет 2F8CC к результату
MOV [0040A664], ECX

...И где он проверяется

CMP ECX, EDX ; Сравнивает числа
JZ 00402DA6 ; Прыгает, если равны

Когда Вы дотрассировали до сравнения чисел, Вы можете посмотреть, каким должен был быть Ваш РЕАЛЬНЫЙ регистрационный код:
:? ecx

В моем случае это дало:

000DC0CC 0000901324

То есть, правильный код для меня: 901324.

Нажмем F5 или Ctrl-D чтобы вернуться в программу и попробуем еще раз, но на этот раз с правильным кодом (в десятичной форме). Работает!
4. СОЗДАНИЕ ГЕНЕРАТОРА КЛЮЧЕЙ ДЛЯ COMMAND LINE 95

Взглянем на алгоритм генерации кода и попробуем перевести его на язык Си. Вот очень простая формула, по которой подсчитывается ключ:
code = ((uppercase_first_char * length_of_string) << 0x0A) + 0x2f8cc;

Замечание #1: Не следует забывать, что все символы в окне ввода имени были приведены к верхнему регистру, поэтому мы должны сделать то же.
Замечание #2: "<< 0x0A" означает "умножние на 2 в степени 10"

Целиком программа на Си выглядит так:

#include <string.h>
#include <stdio.h>

int main()
{
unsigned long code;
unsigned char buffer[0x1e];

printf("CommandLine95 Keymaker by XXXXXX '2000\n");
printf("Enter name: ");
gets(buffer);

strupr(buffer);
code = ( ((unsigned long)buffer[0] *
(unsigned long)strlen(buffer))
<< 0x0A) + 0x2f8cc;

printf("Your code is: %lu", code);

return 0;
}


Приятных сновидений!


4. КАК РАБОТАЮТ PUSH И CALL КОГДА ПРОГРАММА
ВЫЗЫВАЕТ ФУНКЦИЮ

Снова взглянем на кусок кода из Task Lock'а:

PUSH 32 qqqqqqqqqqqqqqqqqqqqqqqqq; Макс. длина строки
PUSH EAX qqqqqqqqqqqqqqqqqqqqqqq; Адрес текстового буфера
PUSH 000003F4 qqqqqqqqqqqqqqqqqqq; Идентификатор управления
PUSH DWORD PTR [ESI+1C] qqqqq qq; Идентификатор окна диалога
CALL [USER32!GetDlgItemTextA] qq q q; Получает текст

Когда Вы вызываете функцию GetDlgItemTextA из программы на C, вызов выглядит так:

GetDlgItemTextA(hwndDlg, 0x3F4, buffer, 0x32);
^ [ESI+1C] ^ EAX


PUSH сохраняет данные в области памяти, называемой стеком. В результате каждого PUSH'а новый кусок данных помещается в верхушку стека и затем вызываемая функция проверяет, что лежит в стеке и использует эти данные по своему усмотрению.
5. О ПРОГРАММАХ НА VISUAL BASIC

EXE файлы, производимые Visual Basic'ом, не являются настоящими EXE. Они просто содержат код для вызова VBRUNxxx.DLL, который затем читает данные из EXE и выполняет программу. Такое устройство псевдо -EXE файлов является также причиной того, что программы на Visual Basic'е такие медленные. А так как EXE файлы не являются настоящими EXE файлами, Вы не можете трассировать и дизассемблировать их - Вы найдете вызов функции из DLL и кучу мусора. И когда Вы будете трассировать такую программу, Вы "заблудитесь" в DLL. Решением этой проблемы является декомпилятор. Существует декомпилятор для программ, написанных на Visual Basic'е версий 2 и 3, созданный кем-то, называющим себя DoDi. Эта программя является шареварной и ее можно найти в InterNet'е (см. Приложение C). Для программ, написанных на Visual Basic'е версии 4 (VB для Windows 95), не существует декомпилятора, насколько мне известно, хотя я бы хотел, чтобы он существовал. =)
Примечание: Настоящие программисты не пишут на Basic'е. =)


ПРИЛОЖЕНИЯ


A. КАК В SOFTICE ЗАГРУЖАТЬ СИМВОЛЬНЫЕ ИМЕНА

Чтобы проверить, загрузил ли SoftICE символьные имена GetWindowText, Вы должны войти в отладчик нажатием на клавиши Ctrl-D и в окне команд ввести следующее:
:exp getwindowtext

Если Вы не получили списка всех функций GetWindowText, Вам нужно отредактировать файл \SIW95\WINICE.DAT, удалив символ комментария (';') перед одной из строчек 'exp=', которые следуют за текстом: "Examples of export symbols that can be included for chicago" в конце этого файла. Вы можете удалить комментарии из всех строчек 'exp=' или сохранить немножко памяти, раскомментировав только строчки с файлами kernel32.dll, user32.dll и gdi32.dll, которые являются самыми важными. После этого Вы должны перегрузить компьютер.

B. СИНТАКСИС НЕКОТОРЫХ ФУНКЦИЙ

Вам будет легче понять, как вызываются функции, о которых мы говорили, если Вы будете знать их описания (декларации):
int GetWindowText(int windowhandle, char *buffer, int maxlen);
int GetDlgItemText(int dialoghandle, int controlid, char *buffer, int maxlen);
int GetDlgItemInt(int dialoghandle, int controlid, int *flag, int type);

Если Вам нужна более подробная информация, посмотрите в руководстве программиста Windows/Win32.

C. ГДЕ НАЙТИ ПРОГРАММЫ

ПРОГРАММЫ ДЛЯ ВЗЛОМА
SoftICE/Win 2.oo: http://www.geocities.com/SoHo/2680/cracking.html
Декомпилятор VB: ftp://ftp.sn.no/user/balchen/vb/decompiler/

ПРОГРАММЫ, ИСПОЛЬЗОВАННЫЕ В КАЧЕСТВЕ ПРИМЕРА

TaskLock: http://users.aol.com/Sajernigan/sgllck30.zip
CommandLine 95: ftp://ftp.winsite.com/pub/pc/win95/miscutil/cline95.zip

 

 


Avatar (ВроДе КаК РедАктоР) ED!SON (UCF)