В папке crack/patcher
находится программа-патчер. Она принимает кофигурационный файл с описанием патча, а также исполняемый файл. Она проводит проверку контрольной суммы (crc32
, совместима с unix cksum
), а также сверяет заменяемые байты с образцом.
3866948396 ; контрольная сумма
0111: 17 -> 41
0112: f4 -> 4f
0113: ff -> fe
;\--/ \/ \/
; || || новый байт
; || ||
; || старый байт (для проверки)
; ||
;адрес изменяемого байта
-h
- полный список опций, а также описание формата концигурационного файла.-s <config file>
- задать путь к конфигурационному файлу.-i <input bin file>
- задать путь к входному исполняемому файлу.-o <output bin file>
- задать путь к выходному файлу (может совпадать с входным).-c
- вывести контрольную сумму входного исполняемого файла.-f
- отключить проверки контрольной суммы и заменяемых байтов.-m <PSP>
- задать базовый адрес загрузки внутри текстового сегмента (PSP) - по умолчанию 0x0100.
Программа запрашивает у пользователя пароль. Далее рассчитывает хэш введённой строки (crc16) и сравнивает с образцом, при совпадении выводит сообщение о вводе верного пароля и наоборот. Установлено ограничение на ввод не более 128 символов (защита от переполнения буфера, но в ней есть уязвимость).
- "Лёгкая" - при вводе пустого пароля программа выводит сообщение о вводе верного пароля. Реализация - простая проверка.
- "Сложная" - переполнение входного буфера. В программе есть защита, однако её можно обойти.
- Счётчик количества введённых символов сбрасывается при вводе байта
8d
. - При переполнении можно заменить некоторые команды на прыжок в нужную часть программы.
- Для усложнения обнаружения этой уязвимости фрагмент памяти, предназначенный для записи вводимых символов, был расположен сразу после вызова функции.
- Этот фрагмент был заполнен фиктивным кодом, который никогда не выполняется.
- Функция возвращается со смещением от места вызова (в её конце пересчитывается адрес возврата).
- Образец ввода, эксплуатирующего данную уязвимость находится в файле
/own/OVERFLOW.TXT
.
- Счётчик количества введённых символов сбрасывается при вводе байта
В начале программа сама себя дешифрует путём применения побитого НЕ.
Для дизассемблирования был написан скрипт на C, который декодировал исходный файл и заполнил код-декодер командами nop
.
Вызывает 2 функции:
- функция ввода и проверки пароля
- функция печати результата
Перед вызовами сдвигает sp
на 2 байта влево. Это требуется для уязвимости переполнения (см. ниже).
Команды
Функция печатает приветсвие, запрашивает ввод от пользователя (максимум 6 - 1 = 5
байт). Затем 3 байта пользовательского ввода копируются в область 0x018b
- 0x018e
. На самом деле программа проверяет только 2 символа пароля, но передача трёх байт сделана для уязвимости переполнения. Затем вызывается функция проверки пароля.
Функция заводит счётчик в регистре cx
. Изначально он равен 2. Счётчик уменьшается при каждом совпадении символа. Если счётчик в конце равен 0, в si
записывается адрес строки с сообщением о вводе верного пароля, иначе наоборот.
Функцией ввода пароля в область 0x018b
- 0x018e
записываются 3 введённых пользователем байта.
Функция выводит сообщение, адрес которого ранее был записан в si
.
При вводе следующих байт программа выдаёт сообщение о верном пароле:
68 14 01 8b 01 0d
\------/ \---/ CR
| |
| ret address 0x018b
|
push 0x0114
Разберём последовательность событий при исполнении программы с данным вводом.
-
Введённые данные попадают в стек. 3 первых байта ввода помещаются в память начиная с адреса
0x018b
. -
Для данных в стеке выделено 3 байта. После них находится адрес возврата из функции ввода пароля. В результате переполнения он заменяется адресом
0x018b
. А туда мы только что поместили первые 3 байта ввода (командуpush 0x0114
). Она поместит в стек адрес, который впоследствии будет интерпретирован как адрес возврата. -
CR
(символ возврата каретки) нужен для правильной работы функции ввода DOS. Он не мешает работе программы при переполнении, так как до вызова функции ввода в главной функцииsp
был сдвинут на 2 байта влево. -
После считывания данных функция ввода пароля стандартно вызовет функцию проверки, которая поместит в
di
адрес сообщения о вводе неверного пароля. Произойдёт стандартный возврат в функцию ввода. С этого момента начинается нестандартное поведение программы:ret
функции ввода считает из стека подменённый ранее адрес0x018b
. Произойдёт прыжок по этому адресу. Эта область так же подменена. В ней теперь находится командаpush 0x0114
. Адрес0x0114
разместится на стеке;- выполнится команда
mov si, 0x01b0
и вsi
запишется адрес сообщения о вводе верного пароля; - после произойдёт возврат по адресу на верхушке стека, который мы ранее записали (
0x0114
); - программа вернётся в главную функцию
-
Произойдёт стандартный вызов процедуры вывода сообщения и выход из программы.
Зелёные стрелки - стандартный порядок выполнения до вмешательства в работу программы; красные стрелки - действия, вызванные переполнением; голубые - стандартные действия после вмешательства. Жёлтым цветом выделены изменения, внесённые переполнением.
Другой способ взлома программы - модификация исполняемого файла (патч). В случае с данной программой необходимо модифицировать всего лишь 3 байта начиная с адреса 0x0111
.
- Вместо вызова функции ввода и проверки пароля нужно записать в регистр
si
адрес строки с сообщением о вводе верного пароля. - Все команды зашифрованы побитовым НЕ, поэтому
e8 0b 00
(call
) иbe b0 01
(mov
) превратились в17 f4 ff
и41 4f fe
соответственно.
Текст файла для программы-патчера:
3866948396 ; chksum
0111: 17 -> 41 ; call 0x011f -> mov si, 0x01b0 (bitwise NOT is applied)
0112: f4 -> 4f
0113: ff -> fe
После применения патча программа не запрашивает у пользователя пароль, а сразу выводит сообщение об успешном входе.
- Ilya Dedinsky aka Ded as prepod
- Aleksei Durnov as mentor
- Vladimir Naumov as partner