рефераты конспекты курсовые дипломные лекции шпоры

Реферат Курсовая Конспект

Как победить хэш

Как победить хэш - раздел Компьютеры, История хакерства Первые Шаги К Усложнению Парольных Защит Прикладные Программисты Сделали Приб...

Первые шаги к усложнению парольных защит прикладные программисты сделали приблизительно в самом конце восьмидесятых годов. Посимвольное сравнение пароля было неэффективной и недостаточной защитой от увеличиваю­щейся армии взломщиков, вооруженных новыми по тем временам отладчиками и дизассемблерами.

В главе, посвященной динамической шифровке, я впервые использовал в демонстрационной программе хеш-функцию для сокрытия пароля. Действительно, пусть у нас имеется функция f(password) == hashe, которая в первом приближении необратима. Тогда значение hashe не дает никакой информации о самом пароле!

Однако на самом деле ничего не изменилось. Вместо того чтобы сравнивать пароли, теперь защита сравнивает хеши. Чтобы убедиться, что пароль введен правильно и получена верная хеш-сумма, программа должна сравнить полученное значение хеша с эталонным! В зависимости от результата сравнения выполняется та или иная ветка программы. Сравните:

if ((sO"ch)!="KPNC") cout « "Password fail" « endi;

if (hashe(&pasw[0])!=0x771 cout « "Password fail" « endi;

По сути ничего не изменилось. И в том и в другом случае достаточно изменения одного условного перехода. Ниже будет показано, как исправить ситуацию и улучшить реализацию защиты. Но сперва попробуем вскрыть этот вариант, чтобы действительно понять всю глубину ошибки разработчика защиты.

Парадоксально, но факт: многие приложения "защищены" именно таким наивным способом. Для меня остается загадочным упорное нежелание програм­мистов внять советам хакеров и исправить допущенные ошибки. Особенно это характерно для прикладных программистов, все попытки которых затруднить взлом обычно сводятся к запутыванию алгоритма, но никак не к использованию хорошо спроектированных и продуманных защит. Что естественно — качествен^ ная защита требует значительного времени на реализацию, а это в конечном счете сказывается на стоимости проекта.

Все еще открытым остается вопрос о необходимости защит. Как бы ни изощрялся автор и какие бы хитрые алгоритмы ни применял, все равно это взломают. И чем популярнее программа, тем быстрее. Усилия, затраченные на разработку защиты, окажутся выкинутыми на ветер.

Убедимся в этом очередной раз, удалив типовую защиту из предлагаемого примера. Попробуем для начала найти с помощью filter.corn все текстовые строки, входящие в исполняемый файл. С удивлением рассмотрев протокол, мы не обнаружим там ничего похожего на то, что программа выводит на экран. Неужели файл зашифрован? Не будем спешить с выводами. Используем HIEW для более подробного изучения файла. Отсутствие строк в сегменте данных наводит нас на мысль, что они могут находиться в ресурсах. Длл подтверждения этой гипотезы просмотрим содержимое ресурсов файла.

Как в этом случае найти код. выводящий эти строки? Заглянув в SUK. мы узнаем, что загрузить строку из ресурса можно функцией LoadStriiigA (и LoadSt-ringW для уникода). Чтобы понять, какая же из двух используется и нашей программе, изучим таблицу импорта функций исполняемого файла. Для зтой цели подойдет утилита сЬппрЬт или любая другая.

Странно, но приложение не импортирует функции LoadSl-rin^. более того, не импортирует ни одной функции из USER32! Как же оно при этом может работать? Рассмотрим иерархию импорта в 'Dependency Walker'.

На самом деле вызывается функция LoadStriiig — меч-од класса CSiring, которая уже в свою очередь вызывает LoadStringA Win32 API. Посмотрим, какие функции MFC42.DLL импортирует наша "подопытная" программа.

MFC42.DLL

40200C Import Address Table

402140 Import Name Table

0 time date stamp

0 Index of first forwarder reference

 

Original 815

Original 561

Original 800

Original 4160

Original 540

Original 1575

 

Какая жалость! Символьная информация недоступна и известен только орди­нал. Можно ли как-то определить, какая из этих функций — LoadString? Возможно, нам будет доступна символьная информация непосредственно в MFC42.DLL? Увы, там тоже не содержится ничего интересного кроме ординалов. Но как-то линкер справляется с этой ситуацией? Заглянем в MFC42.map и попробуем найти строку по контексту 'LoadString'

 

0001:00003042 ?LoadStringA@CString@@QAEHI@Z 5f40402 f

 

Замечательно! Но как же нам теперь получить ее ординал? Для этого уточним, что означает '0001:00003042'.

 

Preferred load address is 5f400000

 

Start Length Name Class

0001:00000000 0009925OH .text CODE

 

Следовательно, 0х3042 — это смещение относительно секции .text. Восполь­зуемся утилитой HIEW и посмотрим, что там находится. Для этого сначала вызовем таблицу объектов, выберем '.text' и к полученному смещению прибавим 0х3042. Переведем HIEW в режим дизассемблера.

5F04042: 55 push ebp

5F4 04043: 8ВЕС mov ebp,esp

5F4 04045: 81EC0401.0000 sub esp,

000000104 5F40404B: 56 push esi

То, что мы сейчас видим до боли напоминает пролог функции. Но почему, собственно, напоминает?

Это и есть точка входа в функцию LoadStrmgA@CString@@QAEHI@! Разу­меется, теперь нетрудно в списке ординалов найти тот, чей адрес совпадает с полученным. Однако прежде чем приступить к поиску, уточним, что мы, собст­венно, намереваемся искать. Вычтем из полученного адреса рекомендуемый (Ox5F404042 - 0x5f400000 == 0х4042): именно это значение будет присутствовать в таблице экспорта, а не Ox5F404042, как можно было подумать с первого взгляда.

Используем ранее полученный список экспорта функций MFC42.dll. Просмат­ривать вручную двухмегабайтовый файл рапорта было бы чрезвычайно утомитель­но, поэтому воспользуемся контекстным поиском.

 

4160 00004042 [NONAME]

 

Оказывается, ординал этой функции 4160 (0х1040). Не кажется ли вам, что это число мы уже где-то видели? Вспомним таблицу импорта crack02.exe.

MFC 4 2 . DLL

40200C Import Address Table

402140 Import Name Table 0 time date stamp 0 index of first forwarder reference

Ordinal 815

OriJinal 561

Ordinal 800

Ordinal 4160

Ordinal 540

Ordinal 1575

Круг замкнулся. Мы выполнили поставленную задачу. Но можно ли было поступить как-то иначе? Неужели это никто не догадался автоматизировать?

Разумеется: с этим справляется любой хороший дизассемблер. Например, IDA. Но последняя надежно скрывает все эти операции под "капотом". Используя инструментарий подобного класса, можно даже не задумываться как это все происходит.

Но даже возможности IDA в той или иной степени ограничены. Иногда она не может получить символьную информацию (или получает ее неправильно). В этом случае приходится забывать про удобства прогресса и заниматься анализом самостоятельно. Кроме того, такой подход неизбежен, когда под рукой пет IDA, а используемый дизассемблер не поддерживает map-файлы (что чаще всего и случается).

К счастью, у нас IDA под рукой, и мы избежим кропотливой и трудоемкой работы. Убедимся, что она правильно извлекла символьную информацию о импортируемых функциях. Для этого перейдем в сегмент "_rdata". .;

Imports from MFC42.DLL

 

?7lCWinappeeoAEUXZ

?OCWinApp@@QAE@PBD@Z

?ICString@@QAE@XZ

?LoadStringA@CStringA@Cstring@@QAEHI@Z

??OCString@@QAE@XZ

?AfxWinlnit@@YGHPAUHIHSTANCE__@@SOPADH@Z

 

Переместим курсор на LoadStringA@String, чтобы узнать, какой код ее вызывает. После недолгих путешествий мы должны увидеть приблизительно следующее:

0040119F push 1

004011А1 lea ecx, [esp+OCh]

004011A5 ltlov [esp+2Ch], edi

004011A9 call j_?LoadStringA@CString@@QAEHI@Z

Похоже, что функция принимает всего один параметр. Уточним это с помощью MSDN — BOOL LoadString( UINT nID ). Передаваемый параметр представляет собой идентификатор ресурса. Используем любой редактор ресурсов (например, интегрированный в MS VC), чтобы узнать связанную с ним строку. Нетрудно убедиться, что эта та самая строка, которую программа первой выводит на экран. Следовательно, мы находимся в самом начале защитного механизма.

Продолжим наши исследования дальше — до тех пор, пока не обнаружим уже знакомую нам функцию ввода.

004011ЕА lea edx, [esp+14h]

004011ЕЕ push еdх

0040UEF push eax

004011FO call ds:??5std@@YAAAV?$basic_istream@DU...

Вычислим новое значение указателя после засылки двух аргументов в стек. Оно будет равно 14h+2*4 = Ох1С.

Теперь нам становится понятен смысл следующего фрагмента:

04011F6 lea ecx, (esp+ICh] ; указатель на введеный пароль

04011 FA push ecx

04011FB call sub_4010EO ; (1}

0401200 add esp, l4h ; OxIC - 0х14 +4 = OxC

0401203 lea edx, [esp+OCh] ; указатель на введенный пароль

0401207 push eax ; аргумент для функции (3)

0401208 push edx

0401209 call sub_4010E0 ; (2) 040120E add esp, 4

0401211 push eax

0401212 call sub_4010C0 ; (3} 0401217 add esp, 8

040121A cmp ax, 1F8h ; Сравниваем ответ

040121E jz short loc_401224 ; !BOT ИСКОМЫЙ ПЕРЕХОД!

 

Довольно витиеватый алгоритм, задействовавший три процедуры. Но даже не интересуясь, что делает каждая из них, мы с уверенностью можем сказать, что тот условный переход в строку Ох040121Е и является "высшей инстанцией", которая выносит окончательный вердикт. Замена его на безусловный приводит к тому, что независимо от результата работы хеш-функции будет получать управ­ление "правая" ветка программы.

Но это же полная катастрофа для разработчика защиты! Несмотря на все его усилия, программа по-прежнему ломается заменой всего одного байта, на поиск которого уходит всего несколько минут.

Попробуем усилить реализацию. Для этого нам нужно непосредственно использовать введенный пароль в программе. Лучше всего подойдет для этой цели шифровка. Поскольку исполняемый код шифровать средствами языков высокого уровня очень затруднительно, то мы зашифруем данные. Однако сделать это непосредственно не позволяет ни один популярный компилятор! Поэтому всю работу (какой бы она ни показалась трудоемкой, нам предстоит выполнить вручную). Существует по крайней мере два диаметрально противоположных подхода к решению этой задачи. Можно зашифровать данные любой утилитой в исполняемом файле, а в самой программе их расшифровывать. Это может выглядеть, например, так:

while (true)

{

if USecretStringIpTxt]} break;

if (Password[pPsw]<Ox20) pPsw = 0;

SecretString[pTxt++]= SecretString[pTxt] ^ Passwordl:pPsw++];

}

При этом строка SecretString нормально выглядит в исходном тексте и зашифрована только после компиляции непосредственно в исполняемом файле. Однако для этого требуется написание утилиты автоматической зашифровки, что может быть утомительно, а кроме того, требует знания формата исполняемого файла и проведения этой операции после каждой компиляции. Однако можно включать зашифрованные данные прямо в исходный текст программы приблизи­тельно таким образом:

char SecretString[]= "х1Fx38х2ВхбЗх49х4Ех38x24х6Ех2Aх58хОВ"

Чтобы получить подобную строку, нужно воспользоваться специально напи­санным для этой цели шифровальщиком. Например, таким: (полный исходный текст file://CD:SOURCEVCCRACK03Crypt.asm).

MOV DI, offset Text-1

Reset:

LEA SI, Password

Crypt:

LODSB

OR AL, AL

JZ Reset

INC DI

XOR [DI],AL

JMP Crypt

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

Конечно, это утомительная работа, заметно удлиняющая и удорожающая разработку. Оправдает ли этот подход затраченное время и усилия? Попытаемся его вскрыть, чтобы ответить утвердительно: "Да, оправдает". Поскольку crack03 отличается от crack02 только одной новой процедурой, мы можем продолжить с того самого места, на котором закончили изучение последней. Остальной код полностью идентичен и мы быстро найдем следующий ключевой фрагмент:

401276 call sub__401120

40127В add esp, 8

40127E cmp ax, IFSh

401282 jz short loc_401291

Попробуем заменить условный переход на jmp short 1ос_401291, предполагая, что независимо от введенного пароля программа будет корректно работать. Посмотрим, что из этого получилось:

Crack Me 03

Enter password : crack

|JJ

Какое разочарование... Программа действительно воспринимает любой па­роль, но работает некорректно. Мы лишь нейтрализовали механизм проверки, но отнюдь не сняли защиту. Основная (и самая тяжелая работа) еще впереди!

Теперь заменой одного-двух байт программу никак не взломаешь. Нужно разобраться, как она манипулирует паролем и как его можно найти. На эти вопросы невозможно ответить без глубокого и вдумчивого анализа механизма защиты и процедуры расшифровки. Цель хакера — сократить эти усилия до минимума. Подумаем, с чего начать изучение программы? С механизма проверки пароля? Нет конечно. Точный механизм проверки нас пока не интересует. А вот как используется пароль — это уже интересно. Чтобы это выяснить, продолжим анализ ее строки 0х0401291, на которую ссылается условный переход.

0401291 lос_401291:

00401291 lea еах, [esp+IOh]

00401295 lea ecx, [esp+OCh]

00401299 push eax

0040129A push ecx

00401298 call sub_4010C0

Мы знаем, что [esp+10h] указывает на начало буфера, содержащего введен­ный пароль. Но что тогда [esp+OCh]? Похоже, что это адрес буфера для возвращения результата работы процедуры. Для подтверждения этого предполо­жения заглянем внутрь последней:

004010СО sub esp, 28h

004010СЗ push esi

0040100C4 push edi

00401OC5 mov ecx, 9

00401ОСА mov esi, 403020h

00401OCF lea edi, [esp+OCh]

004010D3 mov dword ptr [esp+8], 0

004010DB rep movsd

004010DD mov edi, [esp+38h]

004010EI xor еаx, eax

004010E3 lea esi, [esp+OCh]

Что такое 0х0403020 — константа или смещение? Посмотрим. Esi использу­ется как указатель командой movsd, следовательно, это смещение. Посмотрим, что расположено по этому адресу:

a8Cin8HX?8Cne_7 db '8+clH3$n*X',OBh,'?8+cNE.-7cDMkS£&',OBh,'L$?*c',0

Нечто абсолютно нечитабельное. Однако завершающий нуль наводит на мысль, что это может быть строкой. Команда "mov dword ptr [esp+8], 0" еще больше укрепляет нашу уверенность. Действительно, ноль в конце строки не случайность, а часть структуры. С другой стороны, зная особенности используе­мого компилятора, нетрудно заметить, что рассматриваемый коддекомпилируется в обычную конструкцию char MyString[]="lt's my string". Но почему же эта строка так странно выглядит? Быть может, она зашифрована? Эту мысль подтверждает установка регистра edi на начало пароля. Наступает самый волнующий момент — мы переходим к изучению криптоалгоритма. Если он окажется недостаточно стойким, то можно будет подобрать подходящий пароль. Обратим внимание на следующий фрагмент кода:

004010F5 mov dl, [eax+edi]

004010F8 xor dl, ci

004010FA mov [esi], dl

004010FC inc esi

004010FD inc eax

004010FE jmp short loc_4010E7

Попробуем записать его в более удобочитаемом виде, чтобы легче было отождествить алгоритм:

SecretString[pTxt++] = SecretString[pTxt] ^ Password[pPsw++];

Это хорошо известный шифр Вернама. Криптостойкость его уже рассматрива­лась в главе, посвященной криптографии (равно как и методы атаки). Однако не зная, что за текст содержался в зашифрованной строке, мы имеем мало шансов быстро подобрать пароль. Быть может, удастся подобрать хеш-сумму или просто перебрать пароль? Последнее, уже внушает некоторую надежду. Если пароль окажется не очень длинным (от шести до восьми символов), то перебор скорее всего завершится гораздо быстрее словарной атаки на шифротекст. Чтобы написать переборщик паролей, необходимо с точностью до реализации знать алгоритм вычисления хеш-суммы. Возвратимся вновь к механизму проверки пароля.

04011F6 iea есх, [esp+ICh] ; указатель на введенный пароль

04011FA push есх

04011FB call sub_4010EO ; (1)

0401200 add esp, i4h ; OxIC – 0x14 + 4 - OxC

0401203 lea cdx, [esp+OCh] ; указатель на введенный пароль

0401207 push eax ; аргумент для функции (3)

0401208 push edx

04012C9 call sub_4010EO ; (21

040120E add esp, 4

0401211 push еая

0401212 call sub_4010CO ; {3)

0401217 add esp, 3

040121A cmp ax, IFBh ; Сравниваем ответ

040121E jz short loc_401224 ; !BOT ИСКОМЫЙ ПЕРЕХОД!

Хеш-сумма на самом деле вычисляется дважды (что затрудняет ее реверсиро­вание). Используемый автором алгоритм можно свести к следующему:

if if(fl(&pasw[0]},fU&pasw[0]))=-OxlFB) ....

Как работает функция f? Изучим следующий фрагмент:

00401120 sub_401120 proc near

004C1120

0040112C mov eax, [6sp+4]

00401124 mov ecx, [esp+8]

00401128 and eax, OFFh

0040112D and ecx, OFFh

004011 33 iroul eax, ecx

00401136 sar еаx, 7

00401139 retn

00401139 sub_401120 endp

Она умножает аргументы друг на друга и берет старшие 9 бит (0х10-0х7). Это хорошая хеш-функция. Для ее обращения потребуется разлагать числа на множители, что нельзя эффективно реализовать. С другой стороны, прямое ее вычисление очень быстрое, что упрощает перебор паролей. Однако обратим внимание на то, что на самом деле ее аргументыравны. Таким образом обращение функции сводится к элементарному вычислению квадратного корня. После чего останется перебрать 2^ = 0х80 (128) вариантов (так как эти биты были отброшены хеш-функцией). Это смехотворное число вселяет в нас уверенность, что и пароль мы сможем найти очень быстро. Но не будем спешить. Необходимо ревсрсировать еще одну хеш-функцию. Посмотрим, что за сюрприз приготовил нам автор на этот раз:

00401152 lос_401152:

00401152 mov al, [esi]

00401154 cmp al, 20h

00401156 jl short loc_40117F

00401158 mov ci, [esi-I]

0040115B mov [esp+14h], al

0040115F mov edx, [esp-U4h]

00401163 mov [esp+OCh], cl

00401167 mov eax, [esp+OCh]
0040116B push edx
0040116C push eax
0040116D call sub_^ 01120
00401172 lea ecx, [edi+esi]
00401175 add esp, 8
00401178 shi eax, cl
0040117A or еbx, eax
0040117c inc esi
0040117d jmp short loc_401152


Чтобы разобраться в алгоритме этого непонятного кода, попробуем его построчно перевести па Си-подобный язык.

00401152 while [true) { // Цикл

00401152 char _AL = String[idx+l]; // Берем один символ

00401154 if (_AL < 0х20) break; // Это конец строки?

00401158 char _CL = String[idx]; // Берем другой сbvdjk

0040115В chat _sl = _AL; // Сохраняем _AL в стеке

0040115F (DWORD) _EDX = _sl; // Расширяем до DWORD

00401163 char s2 " CL; // Сохраняем в стеке

00401167 (DWORD) _ЕДХ = _s2; // Расширяем до DWORD

0040116D __EAX-f(_EAX,_EDX); // Уже знакомая нам функция!

00401172 CHAR CL = idx; // см. объяснения ниже

00401176 _EAX - _EAX << _CL; // сдвигаем влево

0040117A DWORD _EBX = _EBX | _EAX; // накапливаем единичные биты

00401Г7С idx++

0040117D }

Весь код понятен, кроме странной и совершенно непонятной на первый взгляд операции "00401172 lea есх, [edi+esi]". Поскольку ESI — это однозначно указа­тель на текущий символ, то что же тогда edi?

00401141 mov eax, [esp+8]

00401148 or edi, OFFFFFFFFh

0040114B xor ebx, ebx

0040114D lea esi, (eax+l]

00401150 sub edi, eax

Этот код наглядно показывает, какие перлы иногда может выдавать оптими­затор. Попробуем разобраться что же было на этом месте в исходном коде.

edi = (or edi, OFFFFFFFFh) = -1; тогда esi = (lea esi, [eax+l])== &String+l; и edi = (sub edi. eax) == -l-(&String) = -1 - &String.

Поэтому есх = (lea есх, [edi+esi]) == - I - &String + &String+idx + I== idx!

Это хороший пример "магического кода". Т.е. такого, который работает, но с первого взгляда абсолютно нельзя понять, как н почему.

Многие хакеры любят писать программу в похожем стиле, и для ее анализа приходится с боем продираться через каждую строку. До недавнего времени это считалось своего рода искусством. Сегодня существующие компиляторы по "дикости" кода с легкостью обгоняют человека. Поэтому все чаще возникают сомнения: что это за искусство такое, в котором машина превосходит человека?

Как бы то ни было, приблизительный исходный текст программы восстановить не составляет труда. Вероятно, в оригинале он выглядел так:

while (true)

{

if (String[idx+l]<0x20) break;

xl =xl | (f (String[idx],String[idx+l]) << id^++);

}

Используя полученные исходные тексты, можно написать программу, которая для всех возможных паролей вычислит хеш-сумму и распечатает только те, у которых она будет равна IF8h, т.е. искомой.

Я настоятельно рекомендую попытаться решить эту задачу самостоятельно и только в самом крайнем случае прибегать к уже готовому решению (file://CD:SOURCEVCCRACK_3).

Собственно, алгоритм перебора паролей достаточно несложен и в нс самой лучшей реализации может выглядеть так;

while(1)

{

int idx=0;

while ( (Pas sword [idx++] ++) >'?.') Password [idx-I] =' ! ' ;

if (mult (hashe (^Password [0] ) ,hashe ( ^Password [0] ) }==OxlF8)

printf ("Password - %s n",Password);

}

На самом деле для написания переборщиков только самые ленивые использу­ют Си. Лишь тщательно продуманный и хорошо оптимизированный код может обеспечить приемлемую скорость. Языки высокого уровня, увы, ее обеспечить пока не в состоянии.

Однако прежде чем садиться за ассемблер, для начала не мешает написать простую тестовую программку на Си для выяснения всех нюансов к отладки алгоритма. Только после этого выверенный и вычищенный код можно переводить на ассемблер. В противном случае мы рискуем потратить огромное количество времени и усилий и получить неработоспособный код, который исправить уже нe удастся.

Не будет исключением и этот случай. Наш простой переборщик начнет "плеваться" паролями, чья хеш-сумма в точности равна OxlF8, ко настоящими паролями все они, разумеется, не являются. Их много, очень много, очень-очень много... Похоже, дальнейший перебор не имеет никакого смысла и его придется прекратить. Почему? Рассмотрим фрагмент протокола:

Password - yuO

Password - xvO

Password - uwO

Password - wwO

Password - rxO

Password - vxO

Password - qyO

Password - uyO

Password - nzO

Password - pzO

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

Это пик торжества разработчика защиты. Использовавплохую хеш-функцию со слабым рассеянием, он обрубил нам все пути ко взлому своего приложения. Бесполезно даже обращение хеш-функции, ибо оно ничего не даст. Мы получим те же "левые" пароли — может быть, чуть быстрее.

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

Это одна из самых лучших стратегий защиты. Не давать взломщику возмож­ности перебора пароля (за счет большого числа вариантов) и затруднить ревер­сирование хеш-функции. Как мы смогли убедиться на этом примере, злоумышлен­нику ничего не остается делать... Постойте, неужели в самом деле ничего? А как же атака на шифротекст?

Какой непредсказуемый поворот событий! Очередная незаметная лазейка разрушает всю воздвигнутую систему защиты. Строго говоря, даже не требуется атаки на шифротекст. Необходим лишь метод автоматического контроля, позво­ляющий отсеять максимально возможное число "ложных" паролей. Для этого используем тот факт, что оригинальный текст (а в нашем примере это должен быть какой-то лозунг или поздравление) с большой вероятностью содержит 'а', 'of, 'is' и т.д. Если в расшифрованном тексте хоть одно из этих слов присутствует и нет ни одного символа, выходящего за интервал *!' — '?.', то это неплохой кандидат.

Предложенный метод хотя и является крайне медленным, но, похоже единст­венно возможным. Добавим в существующий переборщик еще пару строк:

if (mult(hashe(&PasswordEO]),hashe(&Password[0]))==OxlF8)

{

sO=Protect (&Password[0] );

if (sO. Find (" is ") '.=-l] print f ("Password - %s - %5n", password, sO) ;

}

Приведенная реализация не является образцом для подражания, написана исключительно с целью лучшей демонстрации материала и никак не предназна­чена для конечного использования.

Предложенный алгоритм требует быстрого процессора для получения удовлет­ворительной скорости перебора паролей. Переписав эту программу на ассембле­ре, мы добьемся значительного выигрыша в скорости, однако для получения приемлемого быстродействия необходим эффективный алгоритм. Поскольку это не имеет прямого отношения к обсуждаемой тематике, то здесь не рассматрива­ется. Скажу лишь, что, используя древовидный поиск и упреждающую логику, можно увеличить скорость в десятки раз.

Но даже если алгоритм реализован без ошибок, вскоре будет найден не единственный верный пароль. Например: Password - KkEC++ - TSn besO+is tSn eneVr of Осе goTo

Однако теперь уже совсем нетрудно проанализировать полученный текст и угадать настоящий пароль. С большой вероятностью исходный текст можно просто угадать! Или, по крайней мере, продолжить словарный перебор. Благо теперь это не трудно. Предположим, что 'TSn' это искаженное The', следователь­но, ожидаемый пароль 'KPNC++', а вся фраза читается так: 'The best is the enemy of the good'

Мы действительно смогли найти пароль и взломать далеко не самую простую систему защиты. Большинство популярных приложений защищено гораздо проще и ломается быстрее.

Разработчики защит часто очень наивны и ленивы в этом отношении. Прак­тически все хакеры настоятельно рекомендуют использовать именно шифрование, а не тривиальную проверку пароля. Отметим разницу в трудозатратах на взлом в том и ином случае. Шифровка даже при использовании некриптостойких алгорит­мов и коротких паролей все же требует трудоемкого изучения алгоритма, написания атакующих программ, и часто очень длительного времени на поиск подходящего пароля.

Впрочем, ничто не лишено недостатков. Так, никакая шифровка не поможет, если хотя бы один легальный пользователь сообщит пароль всем остальным. Даже если пароль не запрашивается явно, а читается с ключевой дискеты или элект­ронного ключа, достаточно всего одной работоспособной копии, чтобы практиче­ски без труда "отвязать" приложение.

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

Парадоксально, но этот способ крайне редко применяется разработчиками. Из всех программ, защищенных подобным образом, навскидку можно вспомнить только FDR 2,1, в котором фрагмент кода, отвечающего за регистрацию, расшиф­ровывался "магическим словом" 'Pink Floyd'. Обычно применяют более наивные защитные механизмы, которым посвящена следующая глава.

– Конец работы –

Эта тема принадлежит разделу:

История хакерства

На сайте allrefs.net читайте: "История хакерства"

Если Вам нужно дополнительный материал на эту тему, или Вы не нашли то, что искали, рекомендуем воспользоваться поиском по нашей базе работ: Как победить хэш

Что будем делать с полученным материалом:

Если этот материал оказался полезным ля Вас, Вы можете сохранить его на свою страничку в социальных сетях:

Все темы данного раздела:

О чем эта книга
"... В моем уме не оставалось места для беспокой­ства об успехе или провале книги. Было лишь желание работать над ее созданием." Ф. Херберт. "Еретики Дюны". Эта

Рассвет. Первые лучи
Первые хакеры появились задолго до возникновения компьютеров, более того задолго до зарождения цивилизации и даже появления человечества. Первых открытием хакеров было удивительное свойство палки,

Лаборатория искусственного интеллекта в США и PDP-1
"Нет четкой грани между богами и людьми: одни переходят в других." Ф. Херберт. "Мессия Дюны". Персонал, обслуживавший правительственные компьютеры, отн

Си и UNIX
"Легкие мои вдыхают ветер времени. Дующий над мертвыми песками..." Ф. Херберт. "Дюна". В 1969 г. усилиями двух талантливых программистов была создана си

Конец хакеров шестидесятых
"Я не должна бояться. Страх убивает разум. Страх — малая смерть, которая приносит полное уничтожение. Я смотрю в лицо моему страху..." Ф. Херберт. "Дюна".

RSX-11M
"Подсмотреть будущее — значит украсть мисти­ческий огонь от священного костра." Ф. Херберт. "Дюна". В начале семидесятых еще никто не представлял себе,

Бытовой компьютер восьмидесятых
"Наверняка человеческий мозг, в котором особым способом развиты сверхспособности, делающие его живым компьютером, до сих пор находит применение." Ф. Херберт. "

Рождение современных хакеров, или снова INTEL
"...он был пропозойским творением, рождение и смерть которого по сути одновременны." Ф. Херберт. "Дети Дюны". Однажды руководство IBM предприняло попытк

Психологический анализ. Что движет хакером
"Инструменты управления государством всегда должны быть остро отточены и готовы к упот­реблению. Власть держится на страхе." Ф. Херберт. "Мессия Дюны".

Хеши. Односторонние функции
"Ночь — это туннель, — подумала она. — Это дыра в завтра, если только оно наступит, это завтра." Ф. Херберт. "Дюна". Вся современная криптография основа

F(Ai) —P--->Aj
Разумеется, только для одного-единственного р мы получим исходную после­довательность Aj, а для всех остальных р — "мусор". Каким способом можно удостовериться в том, что полученная Aj и

С—f---Z
Такая операция дает нам неограниченную гибкость. Элементами перечислен­ного множества могут быть литеры, группы литер, а также целые слова. Таким образом, предложенный алгоритм позволяет полностью

Простейшие системы шифрования
— Высказана мысль или нет, она существует и имеет свою власть,— сказал Туск. — Ты можешь обнаружить однажды, что грань между жизнью и смертью у Свободных, слишком тонка." Ф. Х

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

Как атаковать шифр
При атаке на шифр считается, что криптоалгоритм известен с точностью до реализации и требуется найти пароль. В качестве примера рассмотрим программу crackmeO.com (file://CD:SOURCEA5M_CCRACKmeOCrack

Первый шаг. От ЕХЕ до CRK
Бесспорно, среди существующих на сегодняшний день дизассемблеров луч­шим является IDA. Особенно идеально он подходит для взлома и изучения защищенных программ. Очевидно, что BreakOO не является так

E call j_??4CString@@QAEABVO@PBD[3Z
Обратим внимание на подчеркнутую строку. Насколько же с первого взгляда неочевидно, куда указывает указатель еах! Попутно замечу, что даже сегодня не каждый компилятор способен генерировать такой к

Новый рубеж
Мир давно привык к тому, что популярные технологии вовсе не обязательно бывают хорошими. Именно так произошло и в области защиты условно-бесплат­ного программного обеспечения. Наибольшее распростра

F:00401624 E805030000 call 0040192E
  Если мы попытаемся заглянуть в процедуру Ох040192Е, то вероятнее всего утонем в условных переходах и вложенных вызовах. Сложность и витиеватость кода наталкивают на мысль, что это б

Перехват WM_GETTEXT
Довольно часто разработчики защит читают содержимое окна, посылая ему сообщение WM_GETTEXT. Это ставит в тупик неопытных кракеров. Установка точек останова на GetWindowsText и GetDIgItemText ни к ч

Ограничение времени использования
Другим популярным ограничением DEMO-версий является ограниченное вре­мя использования. Бывают по крайней мере два вида ограничений. В первом отсчет времени идет от момента первого запуска, а во вто

C2 jnz loc_4011c0
Найти в листинге дизассемблера его можно двояко — среди перекрестных ссылок на RegCreateKeyExA: 0040200С RegCreateKeyExA dd ? или по ссылке на строку aSoftwareCrackO; 004

Ограничение числа запусков
Ограничение числа запусков имеет много общего с защитой по времени с момента первого запуска. Однако теперь вместо начального времени необходимо где-то сохранить счетчик, инкрементирующийся (декрем

Хотите получать на электронную почту самые свежие новости?
Education Insider Sample
Подпишитесь на Нашу рассылку
Наша политика приватности обеспечивает 100% безопасность и анонимность Ваших E-Mail
Реклама
Соответствующий теме материал
  • Похожее
  • Популярное
  • Облако тегов
  • Здесь
  • Временно
  • Пусто
Теги