Как ломать программы Windows C EDSON UCF, перевод Mr.BocoTCP

Как ломать программы Windows (C) ED!SON [UCF], перевод Mr.Boco/TCP

СОДЕРЖАНИЕ

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.

— ED!SON, [email protected]

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

Ниже

приведен очень

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

|—

|

————–

Регистры

-|

| ‘R’ — правка значения регистров

|

Окно данных

| ‘D’ — просмотр памяти, ‘E’ — правка памяти

|

Окно кода

| ‘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 GetWindowTextA, GetWindowTextW

GetDlgItemText 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: ED!SON ’96

Code: 12345

Программа остановилась на GetDlgItemTextA. Так же, как и в случае с Task Lock’ом, мы нажимаем F11 чтобы вернуться в вызывающюю функцию. Просматриваем окно кода при помощи Ctrl+Up. Вызов функции выглядит так:

MOV ESI, [ESP+0C]

PUSH 1E; Максимальная длина

PUSH 0040A680; Адрес буфера

PUSH 000003ED; Идентификатор управления

PUSH ESI; Идентификатор окна диалога

CALL [User32!GetDlgItemTextA]

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

:d 40a680

Что же видно в окне данных, как не имя, которое мы ввели? =) А теперь взглянем на кусок кода под вышеприведенным:

PUSH 00; (не интересно)

PUSH 00; (не интересно)

PUSH 000003F6; Идентификатор управления

MOV EDI, 0040A680; Адрес буфера

PUSH ESI; Идентификатор окна диалога

CALL [User32!GetDlgItemInt]

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

EAX=00003039

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

😕 3039

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

00003039 0000012345 «09»

^ hex ^ dec ^ ascii

Как Вы видите (и, возможно, уже догадались) это код, который Вы ввели в диалоговом окне. Ok, что теперь? Посмотрим дальше:

MOV [0040A548], EAX; Сохраняет рег. код

MOV EDX, EAX; А также помещает его в EDX

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

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

MOV ECX, FFFFFFFF; Эти строчки подсчитывают

SUB EAX, EAX; длину строки

REPNZ SCASB; .

NOT ECX; .

DEC ECX; ECX теперь содержит длину

MOVSX EAX, BYTE PTR [0040A680]; Получает байт по адр. 40A680h

IMUL ECX, EAX; ECX = ECX * EAX

SHL ECX, 0A; Сдвиг влево на 0Ah бит

ADD ECX, 0002F8CC; Добавляет 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)

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

Замечание #2: ”

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

#include

#include

int main()
unsigned long code;

unsigned char buffer[0x1e];

printf(«CommandLine95 Keymaker by ED!SON ’96\n»); printf(«Enter name: „);
–PAGE_BREAK–
gets(buffer);

strupr(buffer);

code = ( ((unsigned long)buffer[0] *

(unsigned long)strlen(buffer))

printf(«Your code is: %lu», code);

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

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

Снова

взглянем

PUSH

PUSH

PUSH

PUSH

CALL

на кусок кода из Task Lock’а:

32

EAX

000003F4

DWORD PTR [ESI+1C] [USER32!GetDlgItemTextA]

;

;

;

;

;

Макс. длина строки

Адрес текстового буфера

Идентификатор управления

Идентификатор окна диалога

Получает текст

Когда Вы вызываете функцию 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: www.geocities.com/SoHo/2680/cracking.html Декомпилятор VB: ftp://ftp.sn.no/user/balchen/vb/decompiler/

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

TaskLock: users.aol.com/Sajernigan/sgllck30.zip

CommandLine 95: ftp://ftp.winsite.com/pub/pc/win95/miscutil/cline95.zip

D. КАК СВЯЗАТЬСЯ С АВТОРОМ

На IRC (EFNet): Каналы #Ucf96, #Cracking

E-mail: [email protected] или [email protected]

На моей WWW-страничке: www.geocities.com/SoHo/2680/cracking.html