Реферат Курсовая Конспект
Как победить хэш - раздел Компьютеры, История хакерства Первые Шаги К Усложнению Парольных Защит Прикладные Программисты Сделали Приб...
|
Первые шаги к усложнению парольных защит прикладные программисты сделали приблизительно в самом конце восьмидесятых годов. Посимвольное сравнение пароля было неэффективной и недостаточной защитой от увеличивающейся армии взломщиков, вооруженных новыми по тем временам отладчиками и дизассемблерами.
В главе, посвященной динамической шифровке, я впервые использовал в демонстрационной программе хеш-функцию для сокрытия пароля. Действительно, пусть у нас имеется функция 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 читайте: "История хакерства"
Если Вам нужно дополнительный материал на эту тему, или Вы не нашли то, что искали, рекомендуем воспользоваться поиском по нашей базе работ: Как победить хэш
Если этот материал оказался полезным ля Вас, Вы можете сохранить его на свою страничку в социальных сетях:
Твитнуть |
Новости и инфо для студентов