![]() |
Главная Случайная страница Контакты | Мы поможем в написании вашей работы! | |
|
Это очень нетривиальный CrackMe и очень приятный. Ломается, правда, просто, но не всеми. Этот опус меня побудил написать один мой знакомый, который уже неделю его копает.
Все-таки математику и хакерам знать не помешает, хотя бы на уровне популярных книжек. В эмуляторе используются только основы булевой алгебры, которые (если я не ошибаюсь) входят и в программу школьного курса информатики.
Solar Designer пишет:
Ø Ну, кто сломает мое творение?;) Кода 80х86 — меньше 100 байт.
Ø Шифрованного кода нет. Защиты от отладки тоже нет.
Ø Пароль сознательно зашифрован так, чтобы можно было разобравшись в коде его
Ø проверки, вычислить подходящий. И тем не
Ø менее сломать будет непросто.;)
Тем не менее сломать просто, и даже очень просто! Ну-с, начнем... Как уже понятно, нам придется иметь дело с эмулятором. Или интерпретатором, кому как больше нравится. Никаких конвейеров, циклов выборки, распараллеленных дешифраторов команд нет, т.е. приятного особо не много. Минимально рабочий интерпретатор а-ля риск с фиксированной длиной и адресацией всех команд.
Но что примечательно, так это необычная архитектура. Не Фон-Неймановская. Исполняемый код и данные перемешаны. Правда, реализация не очень аккуратная и навешано очень много заплаток 186, что портит все удовольствие и сводит сложность взлома к нулю.
Далее, автор думает, что...
Ø В защитах я такого не встречал, а ведь именно в них это
Ø полезно, т.к. не дает возможности полной декомпиляции из-за невозможности
Ø распознать границы команд более высокого уровня (в данном случае они были
Ø на макросах).
Математику знать надо. А+В+С == (А+В)+С во всяком случае. Чем мы ниже с успехом и воспользуемся. Я думаю, что стрелка Пирса — это не самое лучшее для затруднения декомпиляции. А вот скорость замедляет изрядно. И серьезная защита будет сильно "тормозить". А что булевы термы ассоциативны, надо все же иногда иметь в виду.
Для написания декомпилятора или отладчика мы должны сначала разобраться с механизмом самого эмулятора. Поскольку защиты нет, подойдет даже Debug.com.
> 16FO:0101 8В365201 MOV SI, [0152]; Указатель команд
> 16FO:0105 AD LODSW; Читаем 1st операнд
> 16FO:0106 97 XCHG DI.AX; Dl :" [1st]
> 16FO.-0107 8B3D MOV Dl, [Dl]; "
> 16FO:0109 AD LODSW; Читаем 2st операнд
> 16FO:OIOA 93 XCHG BX.AX; BX:= &2st
> 16FO;OIOB OB3F OR Dl, [BX]; tO:= [1st) I [2st]
> 16FO:OIOD AD LODSW; Читаем 3st операнд
> 16FO:OIOE 97 XCHG Dl,AX; AX:= tO; Dl = 3st
> 16FO:OIOF F7DO NOT AX; AX:= NOT tO
> 16FO:0111 89365201 MOV [0152],SI.• Opdate ernIP
> 16FO:0115 AB STOSW; mov [3st],NOT(OR [1st], [2st])
> 16FO:0116 EBE9 JMP 0101; -- цикл выборки команд --"
> 16FO:0152 5А01; ernREG:: ernIP
Обратите внимание на строку 0115—в ней заключена вся логика эмулятора. Именно тут собака зарыта. MOV [3st], NOT(OR [1st),[2st]). Всего одной этой команды достаточно для реализации всех остальных.
Как учит булевская алгебра, есть только две логические операции NOT и OR, и все остальные (AND, XOR) можно выразить через эти две. Причем, учитывая, что OR А,А = А, можно сказать, что данная функция может действовать как NOT (PIRS А,А), а следовательно, и как OR (PIRS(PIRS(A.B), PIRS(A,B)). Поскольку приемник представляет собой непосредственное значение, то можно также создать MOV и JMP (последний — изменяя "регистр" указателя команд).
И все. Защите конец. Мы уже знаем как декодировать числовые последовательности и можем без напряжения вычислить "границы команд более высокого уровня". Те, кто знаком с булем, могут пропустить следующий абзац, а для остальных маленький ликбез. Итак:
AND == NOT[(OR(NOT(A), NOT (В))];
XOR == AND[ OR (A,B), NOT(AND (A,B))];
mov == NOT(NOT(A)) или OR (A,A)
JMP =- MOV ([emlP],[A])
jmp == JMP [t0l\t0 DW offset Label
Таким образом, мы теперь имеем базовый набор команд для создания логики "второго уровня". В отличие от первого уровня, одни и те же определения второго могут быть построены различными комбинациями. Это не позволит применить шаблоны. Но что-нибудь еще придумаем.
Автор сдуру включил фрагменты исходников "чтобы легче было ломать". Я так и не понял глубокий смысл этой акции, но, раз нам дали кусок исходника, давайте проверим на совпадение команд первого уровня.
Ø ernNOT macro Src, Dst
Ø ernNOR Src, Src, Dst
Ø endm
Ø emOR macro Srcl, Src2, Dst
Ø ernNOR Srcl, Src2, emRI
Ø ernNOT emRI, Dst
Ø endm
Эти два фрагмента явно включены ради трафика, поскольку никаких других (кроме очевидно избыточных) реализаций данных команд не существует.
>emAND macro Srcl, Src2, Dst
Ø ernNOT Srcl, emRI
Ø ernNOT Src2, emR2
Ø ernNOR emRI, emR2, Dst
Ø endm
Таким образом, математика торжествует и, как мы видим, для написания декомпилятора исходники абсолютно не нужны.
Переходим к логике. Все пестрое множество реализаций может крутиться только вокруг двух формул:
а ===> В == OR(NOT(A),B)
а <--> В == OR [AND (A,B),AND (NOT(A),NOT(B))]
Например, нам встретится следующая последовательность:
OR [AND (А,В), NOT(OR(A.NOT(C)))]
Тех, кто еще не понял, что это делает, попрошу составить логическую матрицу, в которой перечислить все возможные состояния (благо их всего восемь).
Если то же самое записать на привычном сишном наречии, то получится что-то типа а? Ь: с.
Здравый смысл торжествует еще раз! Воодушевленные успехом, мы садимся за написание декомпилятора или отладчика. По мне лучше отладчик. Впрочем, нетрудно будет декомпилировать это с карандашом и листком бумаги. Тут я вашу свободу не ограничиваю.
Анализ программы не должен представить особых трудностей, и я его опускаю. Не хочу вам портить последнее удовольствие, да и в любом случае вопрос дизассемблирования программ далеко выходит за рамки данной заметки.
Но на механизме проверки подлинности пароля я все же остановлюсь. Алгоритм до ужаса простой, и для нахождения пароля нужно решить одно • простенькое линейное уравнение. Сам алгоритм на 186 диалекте выглядит так:
MOV сх,о
LEA SI.Buffer
Repeat:
MOV DX,CX
XOR CX, [SI]
JMF Repeat
Check:
CMP CX,lstConst
JNZ Nag
CMP DX,2stConst
JNZ Nag
Собственно "двойная" проверка обеспечивает дыру достаточных размеров, чтобы в нее можно было без труда пролезть. В самом деле, последние два символа пароля будут равны:
XOR lstConst,2stConst = XOR 7528h, 784Dh = OD65h = 'e
Пароль едва ли не сам приходит на ум, но мы пойдем другим путем:) Найти подходящий пароль несложно; более того, последовательности в стиле <а хог Ь хог с хог d хог е> описываются едва ли не в каждом букваре! Нетрудно даже скалькулировать необходимое число шагов в наихудшем случае в данной разрядной сетке. Оно слишком мало. Самый криптостойкий пароль вскрывается даже не за 2^ итераций, а гораздо быстрее. Т.е. криптостойкость меньше выбранной разрядной сетки!
Замечу еще раз, что предложенная мной реализация атаки базируется на элементарной алгебре и в принципе должна быть доступна каждому хакеру.
Дата публикования: 2014-12-08; Прочитано: 227 | Нарушение авторского права страницы | Мы поможем в написании вашей работы!