Техника хакерских атак Фундаментальные основы хакерства




НазваниеТехника хакерских атак Фундаментальные основы хакерства
страница9/50
Дата публикации06.09.2013
Размер6.7 Mb.
ТипДокументы
shkolnie.ru > Информатика > Документы
1   ...   5   6   7   8   9   10   11   12   ...   50
^

Способ 1. Прямой поиск введенного пароля в памяти



Был бы омут, а черти будут.

народная поговорка
Пароль, хранящийся в теле программы открытым текстом, – скорее из ряда вон выходящее исключение, чем правило. К чему услуги хакера, если пароль и без того виден невооруженным взглядом? Поэтому, разработчики защиты всячески пытаются скрыть его от посторонних глаз (о том, как именно они это делают, мы поговорим позже). Впрочем, учитывая размер современных пакетов, программист может, не особо напрягаясь, поместить пароль в каком-нибудь завалявшемся файле, попутно снабдив его "крякушами" – строками, выглядевшими как пароль, но паролем не являющимися. Попробуй, разберись, где тут липа, а где нет, тем паче, что подходящих на эту роль строк в проекте средней величины может быть несколько сотен, а то и тысяч!

Давайте подойдем к решению проблемы от обратного – будем искать не оригинальный пароль, который нам не известен, а ту строку, которую мы скормили программе в качестве пароля. А, найдя – установим на нее бряк, и дальше все точно так же, как и раньше. Бряк всплывает на обращение по сравнению, мы выходим из сравнивающей процедуры, корректируем JMP, и…

Взглянем еще раз на исходный текст ломаемого нами примера "simple.c"
for(;;)

{

printf("Enter password:");

fgets(&buff[0],PASSWORD_SIZE,stdin);
if (strcmp(&buff[0],PASSWORD))

printf("Wrong password\n");

else break;

if (++count>2) return -1;

}
Обратите внимание – в buff читается введенный пользователем пароль, сравнивается с оригиналом, затем (при неудачном сравнении) запрашивается еще раз, но (!) при этом buff не очищается! Отсюда следует, что если после выдачи ругательства "Wrong password" вызвать отладчик и пройтись по памяти контекстным поиском, можно обнаружить тот заветный buff, а остальное уже – дело техники!

Итак, приступим (мы еще не знаем, во что мы ввязываемся – но, увы – в жизни все сложнее, чем в теории). Запускам SIMPLE.EXE, вводим любой пришедший на ум пароль (например, "KPNC Kaspersky++"), пропускаем возмущенный вопль "Wrong" мимо ушей и нажимаем <Ctrl-D> - "горячую" комбинацию клавиш для вызова Айса. Так, теперь будем искать? Подождите, не надо бежать впереди лошадей: Windows 9x\NT – это не Windows 3.x и, тем более, не MS-DOS с единым адресным пространством для всех процессоров. Теперь, по соображениям безопасности, - дабы один процесс ненароком не залез во владения другого, каждому из них предоставляется собственное адресное пространство. Например, у процесса A по адресу 23:0146660 может быть записано число "0x66", у процесса B по тому же самому адресу 23:0146660 может находиться "0x0", а у процесса C и вовсе третье значение. Причем, процессы А, B и C не будет даже подозревать о существовании друг друга (ну, разве что воспользуются специальными средствами межпроцессорного взаимодействия).

Подробнее обо всем этом читайте у Хелен или Рихтера, здесь же нас больше заботит другое – вызванный по <Ctrl-D> отладчик "всплывает" в произвольном процессе (скорее всего Idle) и контекстный поиск в памяти ничего не даст. Необходимо насильно переключить отладчик в необходимый контекст адресного пространства и лишь затем что-то предпринимать.

Из прилагаемой к Айсу документации можно узнать, что переключение контекстов осуществляется командой ^ ADDR, за которой следует либо имя процесса, урезанное до восьми символов, либо его PID. Узнать и то, и другое можно с помощью другой команды – PROC (В том, случае если имя процесса синтаксически неотличимо от PID, например, "123", приходится использовать PID процесса – вторая колонка цифр слева, в отчете PROC).
:addr simple
Отдаем команду "addr simple" и… ничего не происходит, даже значения регистров остаются неизменными! Не волнуйтесь – все ОК, что и подтверждает надпись 'simple' в правом нижнем углу, идентифицирующая текущий процесс. А регистры… это небольшой глюк Айса. Он них игнорирует, переключая только адреса. В частности поэтому, трассировка переключенной программы невозможна. Вот поиск – другое дело. Это – пожалуйста!
:s 23:0 L -1 "KPNC Kaspersky"
Пояснения: первый слева аргумент после s – адрес, записанный в виде "селектор: смещение". Под Windows 2000 для адресации данных и стека используется селектор номер 23, в других операционных системах он может отличаться (и отличается!). Узнать его можно загрузив любую программу, и списав содержимое регистра DS. Смещение – вообще-то, начинать поиск с нулевого смещения – идея глупая. Судя по карте памяти, здесь расположен служебный код и искомого пароля быть не может. Впрочем, это ничему не вредит, и так гораздо быстрее, чем разбираться: с какого адреса загружена программа, и откуда именно начинать поиск. Третий аргумент – "L –1" – длина региона для поиска. "-1", как нетрудно догадаться, – поиск "до победы". Далее - обратите внимание, что мы ищем не всю строку – а только ее часть ("KPNC Kaspersky++" против "KPNC Kaspersky") . Это позволяет избавиться от ложных срабатываний – Айс любит выдавать ссылки на свои внутренние буфера, содержащие шаблон поиска. Вообще-то они всегда расположены выше 0х80000000. Там – где никакой нормальный пароль "не живет", но все же будет нагляднее если по неполной подстроке находится именно наша строка.
Pattern found at 0023:00016E40 (00016E40)
Так, по крайней мере, одно вхождение уже найдено. Но вдруг в памяти есть еще несколько? Проверим это, последовательно отдавая команды "s" вплоть до выдачи сообщения "Pattern not found" или превышении адреса поиска 0x80000000.
:s

Pattern found at 0023:0013FF18 (0013FF18)

:s

Pattern found at 0023:0024069C (0024069C)

:s

Pattern found at 0023:80B83F18 (80B83F18)
Целых два вхождения, да еще одно "в уме" – итого три! Не много ли для нас, начинающих? Во-первых, неясно – вводимые пароли они, что плоятся ака кролики? Во-вторых, ну не ставить же все три точки останова. В данном случае четырех отладочных регистров процессора хватит, а как быть, если бы мы нашли десяток вхождений? Да и в трех бряках немудрено заблудиться с непривычки!

Итак – начинаем работать головой. Вхождений много, вероятнее всего потому, что при чтении ввода с клавиатуры символы сперва попадают в системные буфера, которые и дают ложные срабатывания. Звучит вполне правдоподобно, но вот как отфильтровать "помехи"?

На помощь приходит карта памяти – зная владельца региона, которому принадлежит буфер, об этом буфере очень многое можно сказать. Наскоро набив команду "map32 simple" мы получим приблизительно следующее.
:map32 simple

Owner Obj Name Obj# Address Size Type

simple .text 0001 001B:00011000 00003F66 CODE RO

simple .rdata 0002 0023:00015000 0000081E IDATA RO

simple .data 0003 0023:00016000 00001E44 IDATA RW
Ура, держи Тигру за хвост, есть одно отождествление! Буфер на 0x16E40 принадлежит сегменту данных и, видимо, это и есть то, что нам нужно. Но не стоит спешить! Все не так просто. Поищем-ка адрес 0x16E40 в самом файле simple.exe (учитывая обратный порядок байт это будет "40 E6 01 00"):
> dumpbin /SECTION:.data /RAWDATA simple.exe

RAW DATA #3

00016030: 45 6E 74 65 72 20 70 61 73 73 77 6F 72 64 3A 00 Enter password:.

00016040: 6D 79 47 4F 4F 44 70 61 73 73 77 6F 72 64 0A 00 myGOODpassword..

00016050: 57 72 6F 6E 67 20 70 61 73 73 77 6F 72 64 0A 00 Wrong password..

00016060: 50 61 73 73 77 6F 72 64 20 4F 4B 0A 00 00 00 00 Password OK.....

00016070: 40 6E 01 00 00 00 00 00 40 6E 01 00 01 01 00 00 @n......@n......

00016080: 00 00 00 00 00 00 00 00 00 10 00 00 00 00 00 00 ................
Есть, да? Даже два раза! Посмотрим теперь, кто на него ссылается – попробуем найти в дизассемблированном тексте подстроку "16070" – адрес первого двойного слова, указывающего на наш буфер.
00011032: 68 70 60 01 00 push 16070h ; <<<

00011037: 6A 64 push 64h ; Макс. длина пароля (== 100 dec)

00011039: 8D 4D 98 lea ecx,[ebp-68h]

;Указатель ^^^^^^ на буфер куда записывать пароль

0001103C: 51 push ecx

0001103D: E8 E2 00 00 00 call 00011124 ; fgets

00011042: 83 C4 0C add esp,0Ch ; Выталкиваем три аргумента
В общем, все ясно, за исключением загадочного указателя на указатель 0x16070. Заглянув в MSDN, где описан прототип этой функции, мы обнаружим, что "таинственный незнакомец" – указатель на структуру FILE (аргументы по Си-соглашению, как мы помним заносятся в стек справа налево). Первый член структуры FILE – указатель на буфер (файловый ввод-вывод в стандартной библиотеке Си буферизован, и размер буфера по умолчанию составляет 4 Кб). Таким образом, адрес 0x16E40 – это указатель на служебный буфер и из списка "кандидатов в мастера" мы его вычеркиваем.

Двигаемся дальше. Претендент номер два – 0x24069C. Легко видеть он выходит за пределы сегмента данных и вообще непонятно чему принадлежит. Почесав затылок, мы вспомним о такой "вкусности" Windows как куча (heap). Посмотрим, что у нас там…
:heap 32 simple

Base Id Cmmt/Psnt/Rsvd Segments Flags Process

00140000 01 0003/0003/00FD 1 00000002 simple

^ 00240000 02 0004/0003/000C 1 00008000 simple

00300000 03 0008/0007/0008 1 00001003 simple
Ну, Тигр, давай на счастье хвост! Есть отождествление! Остается выяснить, кто выделил этот блок памяти – система под какие-то свои нужды или же сам программист. Первое, что бросается в глаза – какой-то подозрительно-странный недокументированный флаг 0x8000. Заглянув в WINNT.H можно даже найти его определение, которое, впрочем, мало чем нам поможет, разве что намекнет на системное происхождение оного.
#define HEAP_PSEUDO_TAG_FLAG 0x8000
А чтобы окончательно укрепить нашу веру, загрузим в отладчик любое подвернувшееся под лапу приложение и тут же отдадим команду "heap 32 proc_name". Смотрите – система автоматически выделяет из кучи три региона! Точь-в-точь такие, как и в нашем случае. ОК, значит, и этот кандидат ушел лесом.

Остается последний адрес – 0x13FF18. Ничего он не напоминает? Постой-ка, постой. Какое было значение ESP при загрузке?! Кажется 0x13FFC4 или около того (внимание, в Windows 9x стек расположен совершенно в другом месте, но все рассуждения справедливы и для нее – необходимо лишь помнить местоположение стека в собственной операционной системе и уметь навскидку его узнавать).

Поскольку, стек растет снизу вверх (т.е. от старших адресов к младшим), адрес 0x13FF18 явно находится в стеке, а потому очень сильно похож на наш буфер. Уверенность подогревает тот факт, что большинство программистов размешают буфера в локальных переменных, ну а локальные переменные, в свою очередь, размешаются компилятором в стеке.

Ну что, попробуем установить сюда бряк?
:bpm 23:13FF18

:x

Break due to BPMB #0023:0013FF18 RW DR3 (ET=369.65 microseconds)

MSR LastBranchFromIp=0001144F

MSR LastBranchToIp=00011156
^ 001B:000110B0 MOV EAX,[EDX]

001B:000110B2 CMP AL,[ECX]     

001B:000110B4 JNZ 000110E4

001B:000110B6 OR AL,AL

001B:000110B8 JZ 000110E0

001B:000110BA CMP AH,[ECX+01]

001B:000110BD JNZ 000110E4

001B:000110BF OR AH,AH
И вот мы в теле уже хорошо нам знакомой (развивайте зрительную память!) процедуры сравнения. На всякий случай, для пущей убежденности, выведем значение указателей EDX и ECX, чтобы узнать, что с чем сравнивается:
:d edx

0023:0013FF18 4B 50 4E 43 2D 2D 0A 00-70 65 72 73 6B 79 2B 2B KPNC Kaspersky++
:d ecx

0023:00016040 6D 79 47 4F 4F 44 70 61-73 73 77 6F 72 64 0A 00 myGOODpassword..
Ну, а остальное мы уже проходили. Выходим из сравнивающей процедуры по P RET, находим условный переход, записываем его адрес (ключевую последовательность для поиска), правим исполняемый файл и все ОК.
Итак, мы познакомились с одним более или менее универсальным способом взлома защит основанных на сравнении пароля (позже мы увидим, что он так же подходит и для защит, основанных на регистрационных номерах). Его основное достоинство – простота. А недостатки… недостатков у него много.
– если программист очистит буфера после сравнения, поиск веденного пароля ничего не даст. Разве что останутся системные буфера, которые так просто не затрешь, но отследить перемещения пароля из системных буферов в локальные не так-то просто!
– ввиду изобилия служебных буферов, очень трудно определить: какой из них "настоящий". Программист же может располагать буфер и в сегменте данных (статический буфер), и в стеке (локальный буфер), и в куче, и даже выделять память низкоуровневыми вызовами типа VirtualAlloc или… да мало ли как разыграется его фантазия. В результате, под час приходится "просеивать" все найденные вхождения тупым перебором.
В качестве тренировки разберем другой пример – "crackme 01". Это то же самое, что simple.exe, только с GUI-рым интерфейсом и ключевая процедура выглядит так:
void CCrackme_01Dlg::OnOK()

{

char buff[PASSWORD_SIZE];
m_password.GetWindowText(&buff[0],PASSWORD_SIZE);

if (strcmp(&buff[0],PASSWORD))

{

MessageBox("Wrong password");

m_password.SetSel(0,-1,0);

return;

}

else

{
MessageBox("Password OK");

}

CDialog::OnOK();

}

^ Листинг 5 Исходный текст ядра защитного механизма crackme 01
Кажется, никаких сюрпризов не предвидится. Что ж, вводим пароль (как обычно "KPNC Kaspersky++"), выслушиваем "ругательство" и, до нажатия ОК, вызываем отладчик, переключаем контекст…
:s 23:0 L -1 'KPNC Kaspersky'

Pattern found at 0023:0012F9FC (0012F9FC)

:s

Pattern found at 0023:00139C78 (00139C78)
Есть два вхождения! И оба лежат в стеке. Подбросим монетку, чтобы определить с какого из них начать? (Правильный ответ – с первого). Устанавливаем точку останова и терпеливо ждем всплытия отладчика. Всплытие ждать себя не заставляет, но показывает какой-то странный, откровенно "левый" код. Ждем "x" для выхода, - следует целый каскад всплытий одно непонятнее другого.

Лихорадочно подергивая бородку (варианты – усики, волосы в разных местах) соображаем: функция "CCrackme_01Dlg::OnOK" вызывается непосредственно в момент нажатия на "ОК" – ей отводится часть стекового пространства под локальные переменные, которая автоматически "экспроприируется" при выходе из функции – переходя во всеобщее пользование. Таким образом, локальный буфер с введенным нами паролем существует только в момент его проверки, а потом автоматически затирается. Единственная зацепка – модальный диалог с ругательством. Пока он на экране – буфер еще содержит пароль и его можно найти в памяти. Но это не сильно помогает в отслеживании когда к этому буферу произведет обращение… Приходится терпеливо ждать, отсеивая ложные всплытия один за другим. Наконец, в окне данных искомая строка, а в окне кода – какой-то осмысленный код:
0023:0012F9FC 4B 50 4E 43 20 4B 61 73-70 65 72 73 6B 79 2B 2B KPNC Kaspersky++

0023:0012FA0C 00 01 00 00 0D 00 00 00-01 00 1C C0 A8 AF 47 00 ..............G.

0023:0012FA1C 10 9B 13 00 78 01 01 00-F0 3E 2F 00 00 00 00 00 ....x....>/.....

0023:0012FA2C 01 01 01 00 83 63 E1 77-F0 AD 47 00 78 01 01 00 .....c.w..G.x...
^ 001B:004013E3 8A10 MOV DL,[EAX]

001B:004013E5 8A1E MOV BL,[ESI] 

001B:004013E7 8ACA MOV CL,DL

001B:004013E9 3AD3 CMP DL,BL

001B:004013EB 751E JNZ 0040140B

001B:004013ED 84C9 TEST CL,CL

001B:004013EF 7416 JZ 00401407

^ 001B:004013F1 8A5001 MOV DL,[EAX+01]
На всякий "пожарный" смотрим, на что указывает ESI:
:d esi

0023:0040303C 4D 79 47 6F 6F 64 50 61-73 73 77 6F 72 64 00 00 MyGoodPassword..
Остается "пропадчить" исполняемый файл, и тут (как и следовало ожидать по закону бутерброда) нас ждут очередные трудности. Во-первых, хитрый компилятор заоптимизировал код, подставив код функции strcmp вместо ее вызова, а во-вторых, условных переходов… да ими все кишит! Попробуй-ка, найди нужный. На этот раз бросать монетку мы не станем, а попытаемся подойти к делу по-научному. Итак, перед нами дизассемблированный код, точнее его ключевой фрагмент, осуществляющий анализ пароля:
>dumpbin /DISASM crackme_01.exe

004013DA: BE 3C 30 40 00 mov esi,40303Ch
0040303C: 4D 79 47 6F 6F 64 50 61 73 73 77 6F 72 64 00 MyGoodPassword

В регистр ESI помещается указатель на оригинальный пароль
004013DF: 8D 44 24 10 lea eax,[esp+10h]

В регистр EAX – указатель на пароль, введенный пользователем
004013E3: 8A 16 mov dl,byte ptr [esi]

004013E5: 8A 1E mov bl,byte ptr [esi]

004013E7: 8A CA mov cl,dl

004013E9: 3A D3 cmp dl,bl

Проверка первого символа на совпадение
004013EB: 75 1E jne 0040140B ---(3) --- (1)

Первый символ уже не совпадает – дальше проверять бессмысленно!
004013ED: 84 C9 test cl,cl

Первый символ первой строки равен нулю?
004013EF: 74 16 je 00401407 -- (2)

Да, достигнут конец строки – значит, строки идентичны
004013F1: 8A 50 01 mov dl,byte ptr [eax+1]

004013F4: 8A 5E 01 mov bl,byte ptr [esi+1]

004013F7: 8A CA mov cl,dl

004013F9: 3A D3 cmp dl,bl

Проверяем следующую пару символов
004013FB: 75 0E jne 0040140B --- (1)

Если не равна – конец проверке
004013FD: 83 C0 02 add eax,2

00401400: 83 C6 02 add esi,2

Перемещаем указатели строк на два символа вперед
00401403: 84 C9 test cl,cl

Достигнут конец строки?
00401405: 75 DC jne 004013E3 - (3)

Нет, еще не конец, сравниваем дальше.
00401407: 33 C0 xor eax,eax --- (2)

00401409: EB 05 jmp 00401410 -- (4)

Обнуляем EAX (strcmp в случае успеха возвращает ноль) и выходим
0040140B: 1B C0 sbb eax,eax --- (3)

0040140D: 83 D8 FF sbb eax,0FFFFFFFFh

Эта ветка получат управление при несовпадении строк. EAX устанавливает равным в ненулевое значение (подумайте почему).
00401410: 85 C0 test eax,eax --- (4)

Проверка значения EAX на равенство нулю
00401412: 6A 00 push 0

00401414: 6A 00 push 0

Что-то заносим в стек…
00401416: 74 38 je 00401450 <<<< ---(5)

Прыгаем куда-то….
00401418: 68 2C 30 40 00 push 40302Ch

0040302C: 57 72 6F 6E 67 20 70 61 73 73 77 6F 72 64 00 .Wrong password

Ага, "Вронг пысворд". Значит, прыгать все-таки надо…. Смотрим, куда указывает je (а код ниже – уже не представляет интереса – и так ясно: это "матюгальщик").
Теперь, когда алгоритм защиты в общих чертах ясен, можно ее и сломать, например, поменяв условный переход в строке 0x401416 на безусловный jump short (код 0xEB).


^

Способ 2. Бряк на функции ввода пароля



Вы боитесь творить, потому что творения ваши отражают вашу истинную суть.

Фрэнк Херберт "Ловец душ"
При всем желании метод прямого поиска пароля в памяти элегантным назвать нельзя, да и практичным тоже. А, собственно, зачем искать сам пароль, спотыкаясь об беспорядочно разбросанные буфера, когда можно поставить бряк непосредственно на функцию, его считывающую? Хм, можно и так… да вот угадать какой именно функцией разработчик вздумал читать пароль, вряд ли будет намного проще.

На самом деле одно и тоже действие может быть выполнено всего лишь несколькими функциями и их перебор не займет много времени. В частности, содержимое окна редактирование обычно добывается либо GetWinodowTextA (что чаще всего и происходит), либо GetDlgItemTextA (а это – значительно реже).

Раз уж речь зашла за окна, запустим наш GUI "крякмис" и установим точку останова на функцию GetWindowTextA ("bpx GetWinodwTextA"). Поскольку, эта функция – системная, точка останова будет глобальной, т.е. затронет все приложения в системе, поэтому, заблаговременно закройте все лишнее от греха подальше. Если установить бряк до запуска "крякмиса", то мы словим несколько ложных всплытий, возникающих вследствие того, что система сама читает содержимое окна в процессе формирования диалога.

Вводим какой-нибудь пароль ("KPNC Kaspersky++" по обыкновению), нажимаем <ENTER> - отладчик незамедливает всплыть:
USER32!GetWindowTextA                                 

^ 001B:77E1A4E2 55 PUSH EBP         

001B:77E1A4E3 8BEC MOV EBP,ESP

001B:77E1A4E5 6AFF PUSH FF

001B:77E1A4E7 6870A5E177 PUSH 77E1A570

001B:77E1A4EC 68491DE677 PUSH 77E61D49

001B:77E1A4F1 64A100000000 MOV EAX,FS:[00000000]

001B:77E1A4F7 50 PUSH EAX
Во многих руководствах по взлому советуется тут же выйти из функции по P RET, мол, что ее анализировать-то, но не стоит спешить! Сейчас самое время выяснить: где расположен буфер вводимой строки и установить на него бряк. Вспомним какие аргументы и в какой последовательности принимает функция (а, если не вспомним, то заглянем в SDK):
int GetWindowText(

HWND hWnd, // handle to window or control with text

^ LPTSTR lpString, // address of buffer for text

int nMaxCount // maximum number of characters to copy

);
Может показаться, раз программа написана на Си, то и аргументы заносятся в стек по Си-соглашению. А вот и нет! Все API функции Windows всегда вызываются по Паскаль- соглашению, на каком бы языке программа ни была написана. Таким образом, аргументы заносятся в стек слева направо, а последним в стек попадает адрес возврата. В 32-разрядной Windows все аргументы и сам адрес возврата занимают двойное слово (4 байта), поэтому, чтобы добраться до указателя на строку, необходимо к регистру указателю вершины стека (ESP) добавить восемь (одно двойное слово на nMaxCount, другое – на сам lpString). Нагляднее это изображено на рис. 3


^ Рисунок 3 0х02 Состояние стека на момент вызова GetWindowsText

Получить содержимое ячейки по заданному адресу в Айсе можно с помощью оператора "звездочка", вызов которого в нашем случае выглядит так (подробнее – см. документацию, прилагаемую к отладчику):
:d *(esp+8)

0023:0012F9FC 1C FA 12 00 3B 5A E1 77-EC 4D E1 77 06 02 05 00 ....;Z.w.M.w....

0023:0012FA0C 01 01 00 00 10 00 00 00-01 00 2A C0 10 A8 48 00 ..........*...H.

0023:0012FA1C 10 9B 13 00 0A 02 04 00-E8 3E 2F 00 00 00 00 00 .........>/.....

0023:0012FA2C 01 02 04 00 83 63 E1 77-08 DE 48 00 0A 02 04 00 .....c.w..H.....
В буфере мусор – так и следовало ожидать, ведь строка еще не считана. Давайте выйдем из функции по p ret и посмотрим что произойдет (только потом уже нельзя будет пользоваться конструкцией d *esp+8, т.к. после выхода из функции аргументы будут вытолкнуты из стека):
: p ret

:d 0012F9FC

0023:0012F9FC 4B 50 4E 43 20 4B 61 73-70 65 72 73 6B 79 2B 2B ^ KPNC Kaspersky++

0023:0012FA0C 00 01 00 00 0D 00 00 00-01 00 1C 80 10 A8 48 00 ..............H.

0023:0012FA1C 10 9B 13 00 0A 02 04 00-E8 3E 2F 00 00 00 00 00 .........>/.....

0023:0012FA2C 01 02 04 00 83 63 E1 77-08 DE 48 00 0A 02 04 00 .....c.w..H.....
ОК, это действительно тот буфер, который нам нужен. Ставим бряк на его начало и дожидаемся всплытия. Смотрите, с первого же раза мы очутились именно так, где и надо (узнаете код сравнивающей процедуры?):
^ 001B:004013E3 8A10 MOV DL,[EAX]

001B:004013E5 8A1E MOV BL,[ESI]

001B:004013E7 8ACA MOV CL,DL

001B:004013E9 3AD3 CMP DL,BL

001B:004013EB 751E JNZ 0040140B

001B:004013ED 84C9 TEST CL,CL

001B:004013EF 7416 JZ 00401407

^ 001B:004013F1 8A5001 MOV DL,[EAX+01]
Замечательно! Вот так, безо всяких ложных срабатываний, элегантно, быстро и красиво мы победили защиту!

Этот способ – универсален и впоследствии мы еще не раз им воспользуемся. Вся соль – определить ключевую функцию защиты и поставить на нее бряк. Под Windows все "поползновения" (будь то обращения к ключевому файлу, реестру и т.д.) сводятся к вызову API-функций, перечень которых хотя и велик, но все же конечен и известен заранее.


^

Способ 3. Бряк на сообщения



Любая завершенная дисциплина имеет свои штампы, свои модели, свое влияние на обучающихся.
Френк Херберт "Дюна"
Если у Вас еще не закружилась голова от количества выпитого во время хака пива, с вашего позволения мы продолжим. Каждый, кто хоть однажды программировал под Windows, наверняка знает, что в Windows все взаимодействие с окнами завязано на сообщениях. Практически все оконные API-функции на самом деле представляют собой высокоуровневые "обертки", посылающие окну сообщения. Не является исключением и GetWindowTextA, – аналог сообщения WM_GETTEXT.

Отсюда следует – чтобы считать текст из окна вовсе не обязательно обращаться к GetWindowTextA, - можно сделать это через ^ SendMessageA(hWnd, WM_GETTEXT, (LPARAM) &buff[0]). Именно так и устроена защита в примере "crack 02". Попробуйте загрузить его и установить бряк на GetWindowTextA (GetDlgItemTextA). Что, не срабатывает? Подобная мера используется разработчиками для запутывания совсем уж желторотых новичков, бегло изучивших пару faq по хаку и тут же бросившихся в бой.

Так может, поставить бряк на SendMessageA? В данном случае в принципе можно, но бряк на сообщение WM_GETTEXT – более универсальное решение, срабатывающее независимо от того, как читают окно.

Для установки бряка на сообщение в Айсе предусмотрена специальная команда – "BMSG", которой мы и пользовались в первом издании этой книги. Но не интереснее ли сделать это своими руками?

Как известно, с каждым окном связана специальная оконная процедура, обслуживающая это окно, т.е. отвечающая за прием и обработку сообщений. Вот если бы узнать ее адрес, да установить на него бряк! И это действительно можно сделать! Специальная команда "HWND" выдает всю информацию об окнах указанного процесса.
<Ctrl-D>

:addr crack02

:hwnd crack02

Handle Class WinProc TID Module

050140 #32770 (Dialog) 6C291B81 2DC crack02

05013E Button 77E18721 2DC crack02

^ 05013C Edit 6C291B81 2DC crack02

05013A Static 77E186D9 2DC crack02
Быстро обнаруживает себя окно редактирования, с адресом оконной процедуры 0x6C291B81. Поставим сюда бряк? Нет, еще не время – ведь оконная процедура вызывается не только при чтении текста, а гораздо чаще. Как бы установить бряк на то, что нам нужно, отсеяв все остальные сообщения? Для начала изучим прототип этой функции:
^ LRESULT CALLBACK WindowProc(

HWND hwnd, // handle to window

UINT uMsg, // message identifier

WPARAM wParam, // first message parameter

LPARAM lParam // second message parameter

);
Как нетрудно подсчитать, в момент вызова функции, аргумент uMsg – идентификатор сообщения будет лежать по смещению 8 относительно указателя вершины стека ESP. Если он равен WM_GETTEXT (непосредственное значение 0xD) – недурно бы всплыть!

Вот и настало время познакомиться с условными бряками. Подробнее об их синтаксисе рассказано в прилагаемой к отладчику документации. А, впрочем, программисты, знакомые Си вряд ли к ней обратится, ибо синтаксис лаконичен и интуитивно - понятен.
:bpx 6C291B81 IF (esp->8)==WM_GETTEXT

:x

Выходим их отладчика, вводим какой-нибудь текст в качесвте пароля, скажем "Hello", нажимаем <ENTER>, отладчик тут же "всплывает"
Break due to BPX #0008:6C291B81 IF ((ESP->8)==0xD) (ET=2.52 seconds)
Вот, он хвост Тигры и уши плюшевого медведя! Остается определить адрес буфера, в который возвращается считанная строка. Начинаем соображать: указатель на буфер передается через аргумент lParam (см. в SDK описание WM_GETTEXT), а сам lParam размещается в стеке по смещению 0x10, относительно ESP:
адрес возврата  ESP

hwnd  ESP + 0x4

uMsg  ESP + 0x8

wParam  ESP + 0xC

lParam  ESP + 0x10
Даем команду вывода этого буфера в окно данных, выходим из оконной процедуры по P RET и… видим только что введенный нами текст "Hello"
:d *(esp+10)

:p ret

0023:0012EB28 48 65 6C 6C 6F 00 05 00-0D 00 00 00 FF 03 00 00 Hello...........

0023:0012EB38 1C ED 12 00 01 00 00 00-0D 00 00 00 FD 86 E1 77 ...............w

0023:0012EB48 70 3C 13 00 00 00 00 00-00 00 00 00 00 00 00 00 p<..............

0023:0012EB58 00 00 00 00 00 00 00 00-98 EB 12 00 1E 87 E1 77 ...............w
:bpm 23:12EB28

Установив точку останова, мы ловим одно откровенно "левое" всплытие отладчика (это видно по явно не "юзерскому" значению селектора CS, равного 8) и, уже тянем руку, чтобы нажать 'x' продолжив отслеживание нашего бряка, как вдруг краем глаза замечаем….
^ 0008:A00B017C 8A0A MOV CL,[EDX]

0008:A00B017E 8808 MOV [EAX],CL

0008:A00B0180 40 INC EAX

0008:A00B0181 42 INC EDX

0008:A00B0182 84C9 TEST CL,CL

0008:A00B0184 7406 JZ A00B018C

0008:A00B0186 FF4C2410 DEC DWORD PTR [ESP+10]

0008:A00B018A 75F0 JNZ A00B017C
Эге, буфер-то не "сквозной", - система не отдает его "народу", а копирует в другой буфер. Это видно потому, как из указателя на "наш" буфер EDX символ копируется в CL (то, что EDX – указатель на "наш" буфер следует из того, что он вызвал всплытие отладчика), а из CL он копируется в [EAX], где EAX – какой-то указатель (о котором пока мы еще не можем сказать ничего определенного). Далее – оба указателя увеличиваются на единицу и CL (последний считанный символ) проверяется на равенство нулю – если конец строки не достигнут, то все повторяется. Что ж, суждено нам следить сразу за двумя буферами – ставим еще один бряк.
:bpm EAX

:x

На втором бряке отладчик вскорости всплывает, и мы узнаем нашу родную процедуру сравнения. Ну, а дальнейшее – дело техники.
^ 001B:004013F2 8A1E MOV BL,[ESI]

001B:004013F4 8ACA MOV CL,DL    

001B:004013F6 3AD3 CMP DL,BL

001B:004013F8 751E JNZ 00401418

001B:004013FA 84C9 TEST CL,CL

001B:004013FC 7416 JZ 00401414

001B:004013FE 8A5001 MOV DL,[EAX+01]

^ 001B:00401401 8A5E01 MOV BL,[ESI+01]
В Windows 9x обработка сообщений реализована несколько иначе, чем в NT. В частности, оконная процедура окна редактирования находится в 16-разрядном коде. А это – сегментная модель памяти (треска хвостом вперед под хвост Тигре) a la сегмент : смещение. Представляется любопытным механизм передачи адреса – в какой же параметр засунут сегмент? Чтобы ответить на это, взглянем на отчет Айса:
Break due to BMSG 0428 WM_GETTEXT (ET=513.11 milliseconds)

hWnd=0428 wParam=0666 lParam=28D70000 msg=000D WM_GETTEXT

^ ^ ^--^^--^

| | сегмент/ \смещение

дескриптор окна |

|

макс. кол-во символов для чтения
Адрес целиком умещается в 32-разрядном аргументе lParam – 16-разрядный сегмент и 16-разрядное смещение. Посему, точка останова должна выглядеть так: "bpm 28D7:0000"


1   ...   5   6   7   8   9   10   11   12   ...   50

Похожие:

Техника хакерских атак Фундаментальные основы хакерства iconXss новичкам. Предназначение xss-атак
Приветствую Вас, уважаемые посетители Портала! Зовут меня DrWeb. Я хочу рассказать Вам о предназначении xss-атак, поскольку xss-уязвимости...
Техника хакерских атак Фундаментальные основы хакерства iconЭлективный курс Фундаментальные эксперименты и фундаментальные константы...
В ходе изучения данного элективного курса учащимся создаются условия для решения следующих образовательных задач
Техника хакерских атак Фундаментальные основы хакерства iconУрока информатики и икт в 9 классе по теме «Кодирование графической и мультимедийной информации»
В нем рассматриваются как фундаментальные основы представления информации в электронном виде и общее понятие композиции, так и конкретные...
Техника хакерских атак Фундаментальные основы хакерства iconЭлективный курс Фундаментальные эксперименты и фундаментальные константы...
В ходе изучения данного элективного курса учащимся создаются условия для решения следующих образовательных задач
Техника хакерских атак Фундаментальные основы хакерства iconУчебное пособие Дунаев И. В. г™ Основы лечебного массажа (техника...
Основы лечебного массажа (техника и методики). Учебное пособие.— М.: Ивц «Маркетинг»; Новосибирск: ООО «Издатель­ство юкэа», 2000.—480...
Техника хакерских атак Фундаментальные основы хакерства iconУчебное пособие Дунаев И. В. г™ Основы лечебного массажа (техника...
Основы лечебного массажа (техника и методики). Учебное пособие.— М.: Ивц «Маркетинг»; Новосибирск: ООО «Издатель­ство юкэа», 2000.—480...
Техника хакерских атак Фундаментальные основы хакерства iconАннотационный отчет Номер и наименование конкурсной темы. Блок "Ориентированные...
Блок "Ориентированные фундаментальные исследования". Раздел «Информационные технологии и электроника». Подраздел " Электроника и...
Техника хакерских атак Фундаментальные основы хакерства iconПрограмма фундаментальные основы человеческого общения. Иллюзия понимания....
Все, кто заинтересован в улучшении своих коммуникативных навыков, умения влиять, убеждать
Техника хакерских атак Фундаментальные основы хакерства iconВыставки
Тематика: сельскохозяйственная техника, в частности, тракторы, уборочная техника, системы орошения и другое
Техника хакерских атак Фундаментальные основы хакерства icon«Детско-юношеская спортивная школа» Опыт работы
В основе игры в баскетбол, как в любой другой спортивной игры лежит техника. Чем совершенней техника игры, тем выше её уровень. Техника...
Вы можете разместить ссылку на наш сайт:
Школьные материалы


При копировании материала укажите ссылку © 2014
shkolnie.ru
Главная страница