Оригинальный DVD-ROM: eXeL@B DVD !
eXeL@B ВИДЕОКУРС !

Курс видеоуроков КРЭКЕРСТВО + ПРОГРАММИРОВАНИЕ 2017
(актуальность: июнь 2017)
Свежие инструменты, новые видеоуроки!

  • 400+ видеоуроков
  • 800 инструментов
  • 100+ свежих книг и статей

УЗНАТЬ БОЛЬШЕ >>
Домой | Статьи | RAR-cтатьи | FAQ | Форум | Скачать | Видеокурс
Новичку | Ссылки | Программирование | Интервью | Архив | Связь

Новый подход к эксплуатации API-функций

Обсудить статью на форуме

Хорошая подборка видеоуроков, инструментов крэкера, книг и статей - здесь.

Автор: ZLOvar <maximys88888@yandex.ru>

Новый подход к эксплуатации API-функций.

Содержание:
1.Вступление
2.Необходимые Инструменты
3.Немного теории
4.Закрепление практикой
5.Заключение.

1. Вступление:
"Всё что создано человеком, можно взломать."
Первая заповедь Хакера.

Доброго тебе времени суток, мой дорогой читатель. Если ты читаешь это, значит модераторы сжалились-таки и опубликовали данный тутор. Хотя если они этого не сделают, они потеряют очень хорошее повествование об API функциях. Потому что я как можно лучше их описал. Статья, которую ты не поленился открыть ориентирована на новичков и рассказывает об новой технике эксплуатации Win32 API-функций. Что ещё за новая техника? И что вообще такое API-функции по сути? На эти и другие вопросы я постараюсь ответить в данной статье. Ну чтож, приступим.

2. Необходимые Интрументы:
OllyDbg v1.10 (брать на кряклабе)
IDR (ссылка: http://cracklab.ru/f/index.php?action=vthread&forum=3&topic=15434 )
OllyDump v=>2.20 (ссылка: http://cracklab.ru/olya/)
GODUP Plugin. (ссылка: http://cracklab.ru/olya/)
PEiD (брать на кряклябе)
2 исследуемые программы в данной статье. (ссылка: http://www.multiupload.com/H24NDD51KP )
Компилятор Masm32 (тоже валяется где-то на cracklab.ru)
Мозги + тётя Клава.
Листок + ручка(карандаш) для записей. (Для ещё большего извращения можно тетрадь для этого дела завести)
Справочник по Win32API (можно найти на этом сайте в разделе "Скачать")

3. Немного теории.
"Возможно всё! На невозможное просто требуется больше времени."
Мудрец из Шангри Ла.


I. Введение.
Здесь я подробно обьясню и покажу на примерах что и как. Но если вы всё уже знаете, то советую пропустить эту часть и сразу перейти к практике. Итак, давайте разберёмся, что же вообще представляют собой API-функции. Я приведу вам своё определение. API функции (от англ. Application Programming Interface) это - просто специальные функции для работы с операционной системой Windows и использования её скрытых возможностей. API функции появились в Windows с самых первых её 32-битных версий. По мере расширения самой ОС, (Операционная Система - здесь и далее прим авт.) стали расширяться и возможности API. Теперь этих функций великое множество. Огромные возможности здесь открываются для людей, которые знают хотя бы один ЯП (Язык Программирования) Даже такой низкоуровневый как Ассемблер. API функции вызываются из программ по соглашению передачи аргументов stdcall (От англ. Standart Call - Стандартный Вызов). Значит аргументы передаются через стек справа-налево, т.е наоборот. Также API функции могут принимать и возвращать аргументы. Что это такое? Сейчас мы разберём всё на конкретном примере. Возьмем самую распрастранённую у крекеров API функцию. Вы уже догадались какую? Значит вы новичек :) Это конечно же MessageBox! Итак, посмотрим по справочнику (Win32.hlp. Вы должны были его скачать) что она из себя представляет. Ага, это функция для вызова сообщения с каким-то повествованием. В программах обычно используется для сообщения юзерукракеру правельно ли введён рег.код. Вот её описание:

The MessageBox function creates, displays, and operates a message box. The message box contains an application-defined message and title, plus any combination of predefined icons and push buttons.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int MessageBox(

    HWND hWnd,	// handle of owner window
    LPCTSTR lpText,	// address of text in message box
    LPCTSTR lpCaption,	// address of title of message box  
    UINT uType 	// style of message box
   ); 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Откроем любую СП, (Среда Программирования) так как для вызова функции нам понадобится программа. Я взял блокнот и транслятор Masm для ассемблера. Откроем блокнот и напишем там такой код:

; CUT HERE
.486 
.model flat, stdcall 
option casemap :none 

include \masm32\include\user32.inc 
include \masm32\include\kernel32.inc 

includelib \masm32\lib\kernel32.lib 
includelib \masm32\lib\user32.lib 
.data 
     ttl db 'Fist project! ',0 ; Третий параметр - Заголовок сообщения
     txt db 'Hello, World!',0 ; Второй параметр - Текст сообщения.
.code 
start: 
     invoke MessageBoxA,0,offset txt,offset ttl,0 ;API функция собственной персоной
     invoke ExitProcess,0 ;Выходим из программы
end start 
; CUT HERE

Если вы хотите побольше узнать о написании и компилировании программ на ассемблере рекомендую рассылку Калашникова "Ассемблер? Это просто!" можно взять на просторах Сети. ОК. Сохраним всё это в текстовый документ и сменим у него расширение на .asm Я сделал так: hello.asm. Итак, чтобы создать исполняемый файл у вас должен быть инсталирован транслятор MASM32. Как скомпилировать:
1. Скачайте установочный файл.
2. Запустите и выберите папку, куда хотите установить. (Рекомендую: X:masm32, где X: ваш локальный диск.)
3. Создайте папку Hello World в директории: \masm32\examples\advanced.
4. После этого переместите туда ваш файл (hello.asm). Теперь путь к файлу должен быть таким: \masm32\examples\advanced\Hello World
Теперь всё готово для компилирования. Осталось только создать bat файл с указаниями линковщику. Откроем блокнот и там напишем:

REM CUT HERE
@echo off
x:\masm32\bin\ml.exe /c /coff <Имя файла>.asm 
x:\masm32\bin\link.exe /subsystem:windows <Имя файла>.obj 
pause 
REM CUT HERE

Где x: также должно быть заменено на ваш локальный диск. И <Имя файла>.asm имя вашего ассемблерного файла(у меня это hello.asm). Сохраним это в файл с расширением .bat У меня получилось: makeit.bat. Запустим его. В результате в этом же каталоге должно появиться два файла. Один экзешник, который мы в последствии будем изучать, а другой obj файл в котором хранится другая, не нужная нам инфа. Итак, запустим наш экзешник и видим, что наша программа работает! Т.е показывает сообщение, где она приветствует мир. Нажимаем ОК и выходим.
Теперь пришло время для отладчика! Стартуем Олли, жмём F3 и выбираем наш ещё тёпленький файл. Вот как он должен выгледеть:

; Assembler
/*401000*/  PUSH 0 ; Последний парамерт (аргумент) - стиль сообщения
/*401002*/  PUSH hello.00403000 ; offset на строку с заголовком
/*401007*/  PUSH hello.0040300F ; offset на строку с текстом
/*40100C*/  PUSH 0 ; первый параметр - хендл окна.
/*40100E*/  CALL  ; собственно, сам вызов ф-и
/*401013*/  PUSH 0 ; первый параметр ф-и ExitProcess, которая и вырубает нашу прогу.
/*401015*/  CALL  ; Вызов ExitProcess
/*40101A*/  JMP DWORD PTR DS:[<&kernel32.ExitProcess>] ; Offset`s на сами
/*401020*/  JMP DWORD PTR DS:[<&user32.MessageBoxA>] ; API функции
; Assembler

Здесь необходимо небольшое, но очень важное лирическое отступление:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Лирическое отступление:
Как вы могли заметить, в ассамбленом коду и в отладчике после названия ф-и присутствует ещё и какой-то непонятный постфикс "А". Что это? Это указание компилятору, с каким форматом строк функция выполняется. Т.е Этот постфикс "А" это сокращение от ASCII. АСКИИ - первый формат строк. Он имеет в своём арсенале 2 алфавита (Rus-Eng), символы (!,#,$,% и т.д), цифры от 0 до 9 и спец. символы (перевод строк, перенос каретки и т.д и т.п). Вес одного символа в ASCII составляет один байт. Чтобы узнать поподробней придется заглянуть в коды ASCII символов. (Это вы сделаете сами.) Есть ещё один формат строк. Это UNICODE. ЮНИКОД, второй и последний. Он включает в себя все символы ВСЕХ алфавитов на земле + цифры, знаки и спец. символы. Строки в ЮНИКОДЕ весят в 2 раза больше чем в АСКИИ! В API функциях этот формат обозначается постфиксом "W" (от англ. Wide - широкий) Пример API функции с ЮНИКОДОМ:

; Assembler
	invoke MessageBoxW,0,offset txt,offset ttl,0
; Assembler

НО! Важное пояснение! Большинство ф-ий в Win32 не используют ни ЮНИКОДА ни АСКИИ формата строк, т.к ВООБЩЕ НЕ РАБОТАЮТ СО СТРОКАМИ! Поэтому их нужно писать без постфиксов. Пример:

; Assembler
	invoke ExitProcess,0
; Assembler

Это нужно учесть, прежде чем ставить на них Точки Останова (в простонародии именуймые бряками). На практике мы разберём поподробней. Уф! Ну кажется с этим разобрались.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Итак, как мы видим, теория о StdCall оказалась верна (посмотрите ещё раз на ассемблерный листинг)! Аргументы (Или параметры. Я думаю вы уже поняли что это такое) действительно передаются справа-налево. Вспомните, как у нас проходил вызов, сначала 0 (хендл окна), затем текст сообщения, потом заголовок сообщения и в конце стиль сообщения. Здесь же всё наоборот. Как видите мы импортировали не одну только MessageBoxA, есть ещё ExitProcess. эта ф-я завершает программу и освобождает память. У неё только один аргумент - Код Выхода (Exit Code). Хорошо, думаю что эта часть понятна. Остались ещё две темы и мы переходим к Практике!

II. Принимаем возвращаемые значения функции.
Оказывается некоторые функции кроме принимания значений, могут их ещё и возвращять! Рассмотрим же конкретный пример. Напишем небольшую антиотладочную программку, которая будет проверять, есть ли запущенный отладчик в системе или нет. Ловить мы собираемся OllyDbg с помощью одной-единственной функции - FindWindowA. Что она из себя представляет мы узнаём в справочнике (Win32.hlp):

The FindWindow function retrieves the handle to the top-level window whose class name and window name match the specified strings. This function does not search child windows. 

HWND FindWindow(

    LPCTSTR lpClassName,	// pointer to class name
    LPCTSTR lpWindowName 	// pointer to window name
   );
Ага, значит у функции два аргумента, это Имя класса и заголовок окна. Последний мы установим в null, т.к искать мы собираемся именно по названию класса. Итак, что конкрето делает данноя функция. Она ищет окно с указанным именем класса иили заголовка и если находит, возвращает его handle. В противном случаем возвратит ноль. Но как же мы с её помощью сможем поймать OllyDbg? А вот как. Искушённый читатель наверное знает, что у этого отладчика своё имя класса! Если посмотрим через любую прогу, которая показывает хендлы окон, то мы увидим, что он называется OllyDBG (это делает отладчик очень уязвимым). Конечно, сейчас есть куча плагинов, с помощью которых можно сделать Олю неприступной крепостью, но мы же изучаем не антиотладочные способы, а API функции. Так что в добрый путь! Открывает блокнот и пишем туда следующее:

; CUT HERE
.486 
.model flat, stdcall 
option casemap :none 

include \masm32\include\user32.inc 
include \masm32\include\kernel32.inc 

includelib \masm32\lib\kernel32.lib 
includelib \masm32\lib\user32.lib 
.data 
         txt db 'Not debugging!',0  
	 ttl db 'AntiDebug v0.1',0
	 ClassName db 'OllyDBG',0
.code 
start: 
     invoke FindWindowA,offset ClassName,0 ; API функция собственной персоной
     cmp eax, 0 ; Сравнить EAX с нулем. Здесь более уместно употребить TEST EAX, EAX.
	 je @not ; если eax = 0 то прыгать
	 invoke ExitProcess,0 ;Выходим из программы если в eax число отличное от нуля
	 @not: invoke MessageBoxA,0,offset txt,offset ttl,0 ; показываем юзеру, что отладчика нет
	 invoke ExitProcess,0 ; Выходим из проги
end start 
; CUT HERE
Сохраняем файл и компилируем в .exe (как компилить см. выше только в .bat файле изменить <Имя файла>.asm на другое);
Готово! У нас в каталоге лежит наша первая антиотладочная прога! Ок, посмотрим в Оле как она работает.

; Assembler
/*401000*/  PUSH 0 ; Второй параметр FindWindow - Текст Окна (stdcall)
/*401002*/  PUSH anti.0040301E ; offset на имя класса. Первый параметр.
/*401007*/  CALL  ; Вызов ф-и
/*40100C*/  CMP EAX,0 ; Сравнить EAX с нулём
/*40100F*/  JE SHORT anti.00401018 ; если равно, то прыгать
/*401011*/  PUSH 0 ; Выходим из проги
/*401013*/  CALL  ; если в EAX хендл окна.
/*401018*/  PUSH 0 ; Вызываем
/*40101A*/  PUSH anti.0040300F ; Сообщение
/*40101F*/  PUSH anti.00403000 ; Чтобы уведомить
/*401024*/  PUSH 0 ; Юзера
/*401026*/  CALL  ; что отладчика в системе нет.
/*40102B*/  PUSH 0 ; Выходим 
/*40102D*/  CALL  ; из программы.
; Assembler
Ещё можно было, при нахождении отладчика, послать ему сообщение с WM_CLOSE, т.е просто закрыть его, но это уже было бы лишним (хотя вы можете попробывать :). Если программу прямо сейчас запустить в пропатченной Olly со всякими разными плагинами, то мы увидим заветное сообщение. Но нам нужно понять, как действует функция FindWindow, поэтому мы берём "чистую" Олю без плагинов. Я её просто заново скачал. Итак, откроем нашу прогу в чистой ольке и попробуем запусить. Ага, мы обломались. Ок, значит наша прога работает как надо :) Теперь перезапустим её и попробуем оттрасировать по F8. Доходим до 40100C. Если взглянем повнимательней на регистры, то можно увидеть, что в EAX содержится число не равное единице. И соответственно прыжок не выполняется и мы выходим. Что же это за число такое? Если вы внимательно читали описание ф-и FindWindow, то вы должны знать, что если окно с указанным классом иили именем существует, то ф-я возвращяет его Handle. Значит, число в EAX и есть его Handle. Жмём F8, меняем флаг Z на 1 и с радостью видим наше сообщение :)
Итак, в данном случае ф-я FindWindow вернула нам значение в регистр EAX, а после этого мы уже использовали его по своему назначению. Ок, надеюсь это понятно. У нас последняя тема, а затем практика!

III. Точки останова на API функции.
Когда мы наконец-то разобрались что такое API функции, то давайте уже перейдём ближе к делу! А именно поговорим, как и на какую ф-ю в каком случае, какую точку останова нужно ставить чтобы выйти на защитный код программы. Для начала, что же такое Точки Останова (в простонародии именуймые бряками)? Я думаю, что уже каждому новичку это известно (а если неизвестно, то может вам рано ещё проги-то ломать?). Бряк позволяет вам приостановить выполнение кода программы в нужном вам месте. Бряки бывают несколько видов. Это обычные точки останова, они же BPX. Точки останова на чтение и запись в память и наконец процессорные (хардварные) точки останова (правда, есть ещё один вид - условные. В общем, если хотите поподробней узнать о бряках читайте Цикл статей "Введение в крекинг с нуля" - Рикардо Нарвахи). В этой же статье мы рассмотрим только один вид брейкпоинтов - обычные (BPX). Как работают BPX? Всё просто! При установке бряка, отладчик ставит специальный опкод (CCh или INT 3) на начало ф-и или байта по такому-то адресу. И когда программа проходит через это место, выполняется этот опкод и отладчик останавливает прогу. Итак, чтобы успешно поставить BPX на API функцию, нам надо знать 3 вещи:
1. Точное название API ф-и.
2. Работает ли она со строками.
3. Если работает, то какой постфикс использовать.
Вот, допустим мы хотим поставить бряк на чтение пароля из Edit. Какая ф-я за это отвечает? Всё очень просто! Вы уже наверное обратили внимание, что API ф-и имеют очень схожее название с их действием. Например: Найти окно - FindWindow, Выйти из процесса - ExitProcess, Показать сообщение - MessageBox. Значит, чтобы узнать имя ф-и нам просто нужно знать, какие действия она должна выполнять. Как всегда, рассмотрим конкретный пример. Возьмем четвёртый крекми от Fantom`a. Допустим мы хотим поставить бряк на ф-ю чтения пароля из Edit. Давайте подумаем, какая ф-я за это отвечает. Так, значит нам нужно получить текст. Переведём на английский, Получить текст - Get Text. Откуда нам нужно его получить? Из Edit`a. Памятуя о том, что все обьекты в Windows (кнопки, строки, скрол-бары и т.д и т.п) являются окнами(Window), значит у нас есть полное название API функции. Получить текст из окна - Get Window Text. без пробелов это получается GetWindowText. Теперь заглянем в справочник, есть ли такая ф-я. Да действительно! Новый подход к API нас не подвёл! Вот её описание:

The GetWindowText function copies the text of the specified window's title bar (if it has one) into a buffer. If the specified window is a control, the text of the control is copied.

int GetWindowText(

    HWND hWnd,	// handle of window or control with text
    LPTSTR lpString,	// address of buffer for text
    int nMaxCount 	// maximum number of characters to copy
   );

Так, из описания мы видим, что один из параметров ф-и - строка. А что это значит? Это значит, что у ф-и есть постфикс формата строк, который она использует. Как мы помним из лирического отступления, этих самых форматов два. ASCII, постфикс "А" и UNICODE, постфикс "W". Как же нам выбрать нужный? Поправочка: Мы не можем поставить бряк на 2 функции сразу т.к это может вызвать тучу ненужных срабатываний. Поэтому нужно выбрать одну. Тут в принципе ничего сложного нет. Нам просто нужно посмотреть в вызовах, какие ф-и программа импортирует. Ок, откроем крекми в Оле и ПКМ (F10) -> Search for -> All intermodular calls. И прям там пишем название ф-и. Такс, ни одной подобной функции не найдено. Возможно наша ошибка в том, что мы выбрали ф-ю указывающую именно на ОКНО! А ведь есть ещё и диалоговые окна и предметы(обьекты)! А значит существует и другая ф-я! Ок, начнем заново. Получить текст - Get Text. Диалоговый предмет - Dialog Item. Ок, у нас есть кое-что: GetDialogItemText. Поищем что-нибудь похожее в справочнике. Не найдено. Ладно, зайдем с другого конца. Допустим, программеры из майкрософта эту функцию как-то сократили. Может быть какое-нибудь слово урезали? Если бы я был на их месте, я бы сократил именно слово Dialog. Почему? Потому что другие три слова итак меньше некуда :) Итак, вбиваем в справку: GetDlgItem... Ура, логика нас снова не подвела! Есть одна! Вот её описание:

The GetDlgItemText function retrieves the title or text associated with a control in a dialog box. 

UINT GetDlgItemText(

    HWND hDlg,	// handle of dialog box
    int nIDDlgItem,	// identifier of control
    LPTSTR lpString,	// address of buffer for text
    int nMaxCount 	// maximum size of string
   );	
Итак, из описания сказано, что ф-я возвращает заголовок или текст ассоциирущийся с диалоговым окном. То что нам нужно! В параметрах ф-ии видим строку. Значит ф-я идет с постфиксом. Ок, теперь рассмотрим применение этой функции. Откроем четвёртый крекми от Fantom`a в OllyDbg и ставим бряк на найденную функцию. bpx GetDlgItemTextA. ("А" - потому что крекми не работает с юникодом). Запускаем прогу, вводим левые данные и нажимаем кнопку "Check". Отладчик незамедлительно всплывает! Ок, смотрим в стек:

0012FC2C   00030288  |hWnd = 00030288 ('FaNt0m's Crackme #4',class='DLGCLASS')
0012FC30   000003E8  |ControlID = 3E8 (1000.)
0012FC34   00403084  |Buffer = CRACKME4.00403084
0012FC38   00000100  Count = 100 (256.)
Внимательно посмотрите на аргументы. Из них нам потребуется только один - буфер для сохранения возвращаемого значения (также посмотрите на hWnd и убедитесь что мы правы. Класс называется Dialog, т.е можно сказать мы угадали :). Кликаем на третьем значении и (F10 -> Follow in Dump) проследуем в дамп. Оттрасируем ф-ю по F8. Отлично! В дампе по адресу 00403084h появилось наше имя! Ок, раз есть имя, значит должен быть и пароль. F9 и ещё одно вхождение. На этот раз пароль должен быть по адресу 00403184h. А вот и он. Отлично, теперь мы знаем, где хранятся наши рег. данные. Ну чтож, я думаю эта часть понятна(и две предыдущие тоже). Конечно, нам не нужно будет каждый раз отгадывать название функции и смотреть его в справочнике. Это нужно делать если только вы не знаете какая точно API ф-я вам нужна. Итак, если вы все ещё живы, то пойдите передохните, попейте зелёного чаю, расслабтесь, посидите в кресле перед ящиком, хлебните пивка... Сделали? Отлично! Тогда нас ждёт практика!

4. Практика
"Не крути две ручки сразу."
Первая заповедь радиотехника.
I. Начало.
Вас уже можно поздравить! Вы только что прошли интенсивный курс обучения API функциям (с чем я вас и поздравляю)! Теперь вы уж точно не потеряетесь в этих гущах ассемблерных инструкций. Ок, приступим. Для начала изучим наших жертв, ссылку на которых я дал вам ещё в начале статьи. Как вы можите видеть, это две программы, а точнее игры на тему извесной телепередачи - "Кто хочет стать миллионером". Я вас заинтриговал? А как же иначе ;) Но что мы там будем ломать, если в нех обеих нет даже никакого намёка на регистрацию? Хе-хе, давайте откроем первую из них. (1999 года. "О, счастливчик") И не забудте включить звук...

II. Зарабатываем первый миллион...
Ну как, дошли до миллиона? Эх вы! Я с первого раза дошел (каюсь - юзал Google :). Ну чтож, попробуем самим до него дойти, но другим путем (надеюсь вы уже поняли каким?). Сделаем так, что даже при неправельном ответе Дмитрий Дибров (он тогда был ведущим) будет говорить что это правельный ответ, и соответственно, мы выйграем МИЛЛИОН! И ещё уберём этот чертов смех при неправельном ответе. Ок, сказано - сделано. Смотрим прогу в PEiD`e: "Borland Delphi 3.0". Отлично! Значит мы можем упростить себе анализ, загрузив прогу в IDR (вы должны были её дополнительно скачать). File -> Load Exe -> Autodetect Version (за одно и посмотрим, прав ли пеид). Да действительно, Delphi 3. Ок, после полной загрузки приложения мы генерируем MAP файл для Оли. Зачем? Да чтоб легче было в листинге разобраться. Ок, Tools -> MAP Generator и сохраняем файл где-нибудь на Рабочем Столе. Теперь загрузим счастливчика в Олю вместе с .map файлом. (Plugins -> GODUP Plugin -> Map loader -> Load labels)
Ok, теперь наш листинг в отладчике заметно преобразился. Двигаемся дальше. Вот теперь-то и пришло время для моего нового подхода к API о котором я писал в самом начале статьи. Суть данного метода в том, чтобы анализировать поисходящие изменения в программе до и после вашего ДЕЙСТВИЯ. Что это может быть за действие? Да что угодно: нажатие кнопки, введение текста в Edit, срабатывание таймера, вариации мышкой или просто ничегонеделание. Главное, это проследить, как программа отреагирует на это действие и поставить бряк на соответствующую API функцию или на событие. Вот таблица:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Ваше ДЕЙСТВИЕ (нажатие кнопки, введение текста и т.п) -> СОБЫТИЕ программы (все сообщения, иили изменения в работе)
далее: Распознование СОБЫТИЯ и определение его вызывающей функции. Т.е, сообщение - MessageBoxASetWindowTextAShowWindow и т.д и т.п
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Ok, давайте ещё раз взглянем на программу. Что происходит когда мы ошибаемся или наоборот - правельно отвечаем? Всплывает окно, которое сообщает нам об этом и играет соответствующая мелодия. Итак, мы используем только одно событие это нажатие на одну из четырёх кнопок (вариантов ответа) и затем программа реагирует. Значит (если в прогу не встроен таймер) она воспроизводит описанные рание действия с нажатием кнопки. Ok, теперь нам стоит определится, будем ли мы ставить бряк на API или всё же поищем код процедура ButtonXClick (где Х - номер кнопки)? Что-то мне подсказывает, что в данном случае нужно ставить бряк именно на API. И я думаю, что все со мной согласятся (я так решил после просмотра юнита Main, где очень много всяких процедур с кликами да ещё и таймер). Ну чтож, раз решили на API значит на API. Осталось только выбрать подходящюю функцию. Для этого посмотрим происходящие события - всплывает окно, которое сообщает нам об проигрышевыйгрыше + играет соответствующая мелодия. Ок, у нас уже как минимум есть 2 бряка. Это бряк на всплытиепоказ окошка с сообщением (как вы уже успели заметить НЕ MessageBox) и бряк на игру мелодии. Хотя можно ещё поставить бряк на загрузку данных из ресурсов (там явно фотка Диброва из ресурсов подгружается), но это не самый лучший вариант, а ставить BPX на вплытие окна не модно :) Я конечно за модой не гонюсь, но когда есть альтернативный вариант - грех не воспользоваться. А после того как заработаем миллион, разберём вариант с всплытием окна. Итак, решено. Будем ставить бряк на мелодию. Хорошо, раз мы новички, и ещё не знаем (заранее) названия этой API, то используем нашу логику и новый подход ;) Ok, событие, воспроизводимое нашей функцией должно играть мелодию. По звучанию, я определил что это .wav формат. Итак имеем в деле словосочетание: Play (Играть) Sound (Мелодия). Ok, посмотрим что есть в справочнике. Хм, там ничего нет. Ладно, последняя попытка. Посмотрим, сможем ли мы установить брейкпоинт на эту псевдофункцию. Вводим в command bar`e "bp PlaySoundA" ("А" - потому что на просто PlaySound бряки не ставятся, а прога как видимо, с ЮНИКОДОМ не работает). Почему не BPX? Потому что bp устанавливает бряк именно на начало API ф-и прямо в библиотеке. Таким образом, мы сможем перехватывать ф-ии если они даже не импортируются! Советую использовать его, когда обычный bpx не помогает. Ok, поставили бряк. Теперь заходим в окно брейкпоинтов (Alt-B) и смотрим, если ли там что-нибудь новенькое. Да, действительно! Точка останова поставлена, теперь жмем F9 и ждём всплытия отладчика. Попутно отметим, что эта псевдо API функция импортируется из winmm.dll (это кое-что проясняет). Ok, прога запустилась. Теперь F2 чтобы начать играть. Ага! Не прошло и секунды, а Оля уже тут как тут! Смотрим в стек:

0012FC04   76B3A980  RETURN to winmm.76B3A980 from winmm.PlaySoundA
0012FC08   004B5500  ASCII "GONG"
Первый параметр - возвращение по RET`y нас совершенно не интересует. А вот второй, это уже кое-что. Тут прослеживается какае-то непонятная строка. Что это? Разве не узнаете? Это же название нашей мелодии! Запустим программу чтобы удостоверится, и действительно слышим ГОНГ! Ок, попробуем после него нажать на какую-нибудь из кнопок и отладчик снова всплывает! Посмотрим в стек и там с радостью обнаружим:

0012FB80   76B3A980  RETURN to winmm.76B3A980 from winmm.PlaySoundA
0012FB84   004B6214  ASCII "CLICK"
Теперь видим во втором параметре название этом мелодии - CLICK (клик). А ведь мы только что нажали на кнопку! Сечёте? Ну конечно же! Это то, что нам нужно! Ok, отпустим прогу на свободу, и т.к мы уже ответели, то скоро будет новое всплытие отладчика. Я ответил неправельно и поэтому мой стек во время всплытия выглядит так:

0012FB80   76B3A980  RETURN to winmm.76B3A980 from winmm.PlaySoundA
0012FB84   004B6268  ASCII "GOODBAY"
Хы-хы, афтар проги походу прощается с нами. Но он-то не знает с кем связался :) Итак, жмакаем Alt+F9 и затем после этой самой мелодии (как она меня достала! одно радует, мы слышим её в последний раз :) несколько раз долбим по F8 и затем ещё раз Alt+F9 и вот мы здесь:

/*4B6FFC*/  MOV EAX,DWORD PTR SS:[EBP-4]
/*4B6FFF*/  CALL 
/*4B7004*/  PUSH EAX ; заносим в стек единственный указатель - название мелодии.
/*4B7005*/  CALL  ; сам вызов
/*4B700A*/  TEST EAX,EAX ; < - мы здесь
/*4B700C*/  JNZ SHORT happy.004B701E
/*4B700E*/  TEST BL,BL
/*4B7010*/  JE SHORT happy.004B701E
/*4B7012*/  PUSH 0
/*4B7014*/  PUSH 7D0
/*4B7019*/  CALL 
Итак, мы нашли функцию, играющую мелодию. Теперь хорошо бы её занопить, но ведь эта функция не только смеётся но ещё и поздравляет нас и в гонг бьёт! Поэтому нам нужно найти именно сам её вызов. Ok, выходим из вложенной процедуры. Теперь мы здесь:

; Заносим в стек параметры процедуры TForm1.Wave.

/*4B5EC5*/  MOV CL,1 ; 3-й параметр.
/*4B5EC7*/  MOV EDX,happy.004B6268 ; ASCII "GOODBAY" - 2-й параметр. (Афтар на знает албанского! По англ. прощай - GOODBYE)
/*4B5ECC*/  MOV EAX,EBX ; 1-й параметр.
/*4B5ECE*/  CALL  ; сам вызов
/*4B5ED3*/  CALL  ; ещё какая-то процедура.
/*4B5ED8*/  MOV DL,1
/*4B5EDA*/  MOV EAX,EBX
/*4B5EDC*/  CALL  ; А это уже интересней!
Я думаю, что из названия call`oв всё понятно. Теперь нам нужно найти тот самый условный переход, который отвечает за правельность ответа. Ok, сразу бросается в глаза джамп по адресу 004B5E93h. Посмотрим, нужен ли он нам. Ставим бряк на этот адрес (предварительно сняв все прошлые бряки) и отпускаем прогу на волю. Отвечаем НЕПРАВЕЛЬНО на вопрос и отладчик всплывает. Т.к мы ответили неправельно, перехода не происходит. Злорадно улыбаемся и меняем флаг Z на один. Дрожащими руками жмем на F9 и... Это срабатывает! УРА! Мы только что нашли и устранили проблему неправельных ответов :) Теперь можно без труда добраться до миллиона! Ах, да и ещё кое-что. Если вам необходимо знать правельный вариант ответа, то я даю вам такой листинг (да вы уж наверно сами догодались):

/*4B5E8D*/  CMP EAX,DWORD PTR DS:[4BDD74] ; Сравнить номер кнопки с правельным ответом с кнопкой, которую нажали мы. 
/*4B5E93*/  JE happy.004B5F38 ; если равно то мы ответили правельно!
Осталось полько превратить условный переход в безусловный, и мы всегда будем отвечать правельно! Ну чтож, нас можно поздравить с зарабатыванием первого миллиона! Хоть там деньги и старые, но всё же несколько тысяч баксов нам перепало :) Я ещё обещал вам рассмотреть вариант с функцией всплытия окна, но пусть это останется вашем домашним заданием! А мы переходим к заработку второго миллиона с Максимом Галкиным!

III. Зарабатываем второй миллион.
Как? Вы так быстро промотали целый миллион, что решили заработать ещё один? Не расстрайвайтесь, денег никогда не бывает много. Ну я надеюсь вы его хоть на что-нибудь стоящие потратили? Ладно, хватит пустой болтовни. Примемся за дело! Сначала посмотрим, что же это за миллионер-то такой. Хм. Прошлый был получше. Ладно, деньги не пахнут ;) Применим наш новый метод. Итак, мы опять же видим четыре кнопки ответа. Поэтому ставить бряк на их нажатие мы не будем (я думаю вы понимаете почему). Значит, у нас остались события, функции которых можно использовать. После нажатия одной из четырех кнопок, программа в зависимости от правильности ответа, играет две разные мелодии и меняет цвет на названии варианта ответа. Ok, у нас есть опять же всё та же PlaySound и функция изменения цвета текста. Прежде чем принять решение, посмотрим в каталог, где расположена программа. Нет ли там чего-нибудь интересного. Есть! В папке Sounds находятся все звуки, проигравающиеся программой. Значит для нас в идеале будет именно функция PlaySoundA. Но не станем же мы повторятся? Ведь это отобьет всякий интерес к взлому программы. Значит решено! Будем использовать второе событие - изменение цвета текста кнопки. Что что? Вы уже замучались напрягать мозги и копаться в справочниках? Тогда поступим по более облегчённому способу. (Но сначала немного помучаемся) Итак, посмотрим в PEiD`e что за компилятор у программы. Ага, это ASPack. ASPack? Постойте, но ведь это не компилятор никакой, а самый что ни на есть настоящий упаковщик! Но не спешите бросать читать статью, ведь мы сможем его распаковать! Что-что? вы не умеете распаковывать? (если б вы умели давно бы уже распаковали :) Не беда! Я вам помогу. (для тех, кто распаковал прогу - можете пропустить несколько строк)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Распаковка:
Итак, открываем Олю и грузим туда наш файл. Теперь нам нужно выбрать метод распаковки. У меня их 2. Первый для простых упаковщиков (к ним относится ASPack старых версий), и второй для сильных (типа ASProtect 2.XX и т.д).
Сейчас будем использовать первый способ (второй я вам расписывать не буду. т.к у нас статья не по распаковке). Для поиска OEP заюзаем небольшой трюк. Трассируем команду PUSHAD, далее в регистрах выбираем ESP -> Follow in Dump (можно также использовать любой другой регистр). В дампе выделяем первый DWORD, т.е четыре байта и ПКМ -> Breakpoint -> Hardware on access -> Dword. F9. Останавливаемся на 005BA3AB (обратили внимание на POPAD командой выше?) затем F8 и видим как в стек ложится наш заветный OEP. Далее переходим по нему по RET`y и сразу же анализируем прогу (Ctrl + A). Хм, странно, почему-то не анализируется, а ладно, я и так по оффсетам вверху понял что прога написана на Delphi (глаз намётан :). Теперь пришло время использовать плуг OllyDump, который сдампит наш процесс и даже, скажу по секрету, восстановит IAT! Сказано - сделано. Plugins -> OllyDump -> Dump debugged process. Там ничего не меняя нажимаем Dump и сохраняем его под именем dumped.exe. Отлично! Осталось только кинуть этот файл в папку с прогой и изменить у него имя на million.exe (предварительно согласившись на замену) и вуаля, у нас теперь готовый распакованный файл.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Теперь, когда у нас в руках "голая" программа, мы можем приступить непосредственно к реверсингу. Итак, как мы раньше договорились, мы будем ставить бряк на функцию изменения цвета текста. Но здесь есть один нюанс. Мы можем заметно ускорить процесс, используя программу под названием IDR (Interactive Delphi Reconstructor - Я описывал её в прошлой главе). Этот продукт является неким подобием DeDe, т.е частично декомпилирует проекты, написанные на Delphi. Но он гораздо лучше своего старшего собрата. Итак, открываем IDR и File -> Load Exe -> Autodetect Version. Немного подождём и перед нами улучшеный листинг программы. Чем он лучше прежнего? Ну сами посмотрите! Одни только распознанные имена всех функций чего стоят! Ведь именно на них мы и будем опираться при исследовании программы. Ok, теперь вспомним как меняется цвет текста в Delphi. А меняется он собственно, очень просто. В шрифте меняется свойство Color на нужное. Пример:
Button.Font.Color := clRed;
Здесь у обьекта Button меняется подсвойство Color (Цвет), свойства Font (Шрифт) на красный. Значит нам нужно именно свойство Font. А конкретнее TFont. Как мы все знаем, TFont находится в модуле Graphics. Ok, лезем через IDR в Graphics. Нам нужно что-то, наподобие Font.Color. Ищем... Есть кое-что! Даже не кое-что, а TFont.SetColor! Отлично! Смотрим его процедуру:

Graphics::TFont.SetColor
 00414A3C     push       ebx
 00414A3D     cmp        edx,dword ptr [eax+18]
>00414A40     je         00414A4E
 00414A42     mov        dword ptr [eax+18],edx
 00414A45     mov        bx,0FFFD
 00414A49     call       @CallDynaInst
 00414A4E     pop        ebx
 00414A4F     ret
 
У нас есть адрес! Это - 00414A3C. Теперь возвращаемся в Olly, не забыв заранее сгенерировать IDP .map файл и загрузить его в отладчик (как это сделать, я рассказывал в предыдущей главе). Переходим по найденому адресу (Ctrl+G). Теперь мы могли бы спокойно установить сюда бряк, но ведь эта функция вызывается неоднократно (т.е не только в одном месте)! Поэтому мы сначала запустим программу, а уже потом установим BPX (я вам ну прям как младенцам всё обьясняю :). Начнем новую игру, введем имя, выберем любой ответ и ОЧЕНЬ БЫСТРО ПЕРЕХОДИМ В ОТЛАДЧИК И ПО 00414A3C УСТАНАВЛИВАЕМ БРЯК (F2)! Получилось? Значит с быстротой реакции у вас всё в порядке. Почему нельзя было просто поставить бряк? Да все по той же причине, что и в начале. Меня наверное сейчас все тёртые кракеры обзывают последними словами, но ведь это работает! (Есть другой способ - поставить бряки на все вызовы этой функции и убирать по отдельности ненужные, но первый способ быстрее). Ладно, давайте посмотрим что же у нас получилось. Покидаем функцию по RETN`y и вот мы здесь:

/*464672*/  MOV EDX,0FF ; Второй параметр функции SetColor - clRed
/*464677*/  MOV EAX,DWORD PTR DS:[ESI+58] ; Первый параметр - hWnd кнопки.
/*46467A*/  CALL  ; Сам вызов
/*46467F*/  PUSH 1 ; мы здесь
Здесь всё понятно, но для пущей уверенности попробуйте изменить число FF на F (т.е поменять цвет с красного на черный). Ага, теперь при неверном ответе цвет текста на кнопке действительно меняется на чёрный! Отлично, значит это то, что нам нужно. Теперь посмотрим окрестности кода. В комментариях мы видим название мелодии - Soundslose.wav и все тот же вызов функции PlaySoundA. Поднимаемся повыше и по адресу 00464305 замечаем в комментариях уже Soundsnext.wav. Это мелодия правельного ответа. Значит условный переход должен быть где-то поблизости! В глаза нам сразу же бросается джамп по адресу 004642A1. А там ещё и StrCmp поблизости! Я могу сказать с достоверностью примерно 90% что это то, что нам нужно! Проверим? Ставим бряк на этот джамп и отвечаем на вопрос в программе. Я ответил неправельно и у меня он выполнился. Хе-хе, меняем флаг Z на противоположный и... ЭТО СРАБАТЫВАЕТ! Всё! Осталось только занопить переход и дело сделано! Я вас поздравляю, вы только что стали миллионером!


5. Заключение.
Конечно, мой метод кому-то не покажется новым, но ведь не в этом дело! А в чем же тогда? Все дело в том, что новички, приступая к взлому конкретной программы не знают с чего начать! В этой статье я описал ещё один подход к практическому использованию Win32 API функций и показал на практике, как его можно применить. Конечно, не нужно каждый раз выдумывать название конкретной функции, а потом искать его в справочнике. Это стоит делать только тогда, когда вы его не знаете. И чтобы облегчить жизнь новичкам приведу краткий список функций, необходимых для работы с программой, файлами и реестром:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
GetWindowTextAGetDlgItemTextA - получить текст из строки ввода именипароля. Также можно юзать SendMessage с параметром WM_GETTEXT.
PlaySoundA - проиграть мелодию.
CreateFileA - создать файл. Используется для чтениязаписи или создания файла. Универсально.
GetLocalTime - получить время компьютера. Для снятия триальности программы.
RegOpenKeyRegOpenKeyEx - открыть ключ из реестра.
RegQueryValueRegQueryValueEx - прочитатьзаписать значение из переменной в ключе из реестра.
ExitProcessTerminateProcess - завершить выполнение программы.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Это лишь самые общие и необходимые функции. Ну чтож, мы завершили-таки нашу экскурсию по миру API. Я думаю, она была интересной. В конце, по старой традиции, как всегда передаю приветы всем крякерам, кого знаю. Это - BoRoV, 3ton, Coderess, Hexxx (тот самый :) и другие (уж извините кого забыл). И естественно спасибо всем читателям, что вы уделили внимание мне и моей статье. Ах да, чуть не забыл, вот здесь( http://www.multiupload.com/K8G1BOHG9X ) я выкладываю небольшой архивчик, где вы можете найти все рассматриваемые в этой статье программы и исходники. До новых встреч!

Статья написана под руковадством ZLOvar. Автор ни за что ответственности не несёт!

Все ругательства слать в null, остальное на maximys8888@yandex.ru
02.06.2010 г.г



Обсуждение статьи: Новый подход к эксплуатации API-функций >>>


Комментарии к статье: Новый подход к эксплуатации API-функций

punxer 07.07.2010 19:50:30
Эксплуатация как то не звучит в этом смысле в названии. Как и новый подход. Но статья как статья хороша

---
int 26h 08.07.2010 03:32:06
довольно не плоха
---

Материалы находятся на сайте https://exelab.ru



Оригинальный DVD-ROM: eXeL@B DVD !


Вы находитесь на EXELAB.rU
Проект ReactOS