Шифратор исполняемых файлов
Содержание
. Постановка задачи
. Анализ задачи
.1 Выбор метода защиты исполняемых файлов
.2 Описание системных средств, необходимых для решения
поставленной задачи
. Разработка алгоритмов
.1 Алгоритм работы шифровщика
.2 Алгоритм расшифровки образа защищенного приложения
. Описание программы
.1 Структура программы
.2 Код шифровщика
.3 Код расшифровщика
. Инструкция пользователя
. Описание контрольного примера
Список источников информации
Приложение
1. Постановка задачи
Разработать метод создания защищенных программ. Написать программу,
защищающую выбранный исполняемый .com файл, шифруя его. Защищенный таким образом файл должен быть автономен
относительно шифрующей программы, то есть содержать информацию для
восстановления своего кода. При запуске защищенной программы на выполнение
необходимо запросить пароль. Процедуру дешифрации данных следует запускать
только после загрузки исполняемого файла в оперативную память и проверки
пароля.
2. Анализ задачи
2.1 Выбор
метода защиты исполняемых файлов
В ходе поиска методов решения поставленной задачи было рассмотрено
несколько алгоритмов защиты исполняемых файлов.
Первый вариант заключается в разработке приложения, шифрующего выбранный
.com файл и сохраняющего полученные
данные в файле специального формата. Для запуска защищенного приложения
пользователю необходимо запустить прежде программу-дешифратор, указать путь к
закодированному файлу, ответить на запрос ввода пароля. Зашифрованную
информацию для проверки правильности пароля рассматривалось хранить в файле совместно
с данными защищенного приложения. Программу-дешифратор в данном варианте
допускается компоновать совместно с шифратором.
Второй вариант состоит в написании программы, способной сохранять
преобразованный код защищаемого приложения в самостоятельный исполняемый файл,
автономный от программы-шифратора. Закодированные данные, информация для
проверки правильности пароля и процедура дешифрации в этом случае хранятся в
одном модуле. В начале защищенного .com файла предлагалось хранить код дешифратора, после которого добавлять
данные защищаемого приложения. При запуске на исполнение в данном варианте
программа загружается в оперативную память вместе с образом защищенного
приложения. Управление передается коду дешифратора, который должен декодировать
образ. На этом этапе была выявлена проблема смещения адресов расшифрованного
кода. Заключается она в том, что начало кода любой .com программы должно размещаться в оперативной памяти по
смещению 100h. Это исключает возможность передачи
дешифратором управления на первую команду восстановленного кода образа, поэтому
расшифрованные данные в данном методе защиты планировалось записывать в
исполняемый файл, который можно будет запустить на исполнение, а по завершению
работы удалить.
В третьем варианте было решено оставить образ защищаемой программы по
правильному смещению, а код расшифровщика добавлять в конец выходного файла.
Для перехода к коду дешифратора в данном случае в начале файла должен
находиться соответствующий код безусловного перехода. В связи с этим выявилась
новая проблема - Придется заменить как минимум три байта (именно столько
занимает код команды jmp)
кода защищаемой программы, вследствие чего, программа может оказаться
неработоспособной. Для решения этой проблемы можно записать заменяемый участок
кода в блок расшифровщика и восстанавливать его после передачи управления.
Однако проблема неправильных адресов остается и в этом варианте. Объясняется
она тем, что при получении машинного кода дешифратора компилятор не учитывает
размер образа защищаемой программы. Если бы все .com файлы, которые придется защищать разрабатываемым
приложением, имели одинаковую длину, решить проблему было бы просто: нужно было
бы просто при использовании смещений при написании кода расшифровщика
прибавлять к ним константное число, равное длине исполняемых файлов. Но
разрабатываемая программа должна уметь работать с любыми .com файлами. Настройку смещений
планировалось осуществлять следующим методом:
Листинг 2.1 Настройка смещений методом вычисления размера образа
В листинге 2.1 приведен вариант получения размера зашифрованного образа,
который находится в памяти перед кодом дешифратора. Для этого необходимо
получить содержимое счетчика команд IP, что не позволяет сделать команда mov. В стоке 1 листинга 2.1 с помощью команды call в стек искусственно заносится
содержимое IP и считывается в регистр ax в строке 3. Вычитанием из
полученного результата смещения команды pop ax
можно получить размер образа (строка 4 листинга 2.1).
Однако применение данного метода сильно усложняет код программы, поэтому
было решено использовать способ настройки адресов, предложенный А. Калашниковым
в книге "Ассемблер? Это просто!" [1], который заключается в
копировании настраиваемого кода в свободный участок памяти по правильному
смещению.
Для решения поставленной задачи был выбран третий вариант алгоритма
защиты, т.к. он наиболее универсален, применим к любым COM программам, позволяет создавать защищенные
приложения, автономные относительно шифровщика и обеспечивает наибольшую
безопасность.
2.2 Описание
системных средств, необходимых для решения поставленной задачи
Для написания программы использовался программный пакет TASM (Turbo Assembler) от компании Borland,
предназначенный для разработки программ на языке ассемблера, включающий в себя:
- Turbo Assembler (tasm.exe) - компилятор;
Turbo Linker (tlink.exe) - компоновщик;
Turbo Debugger (TD.exe) - отладчик.
Для удобного написания кода на языке ассемблера использовалось приложение
Notepad++ - свободный текстовый редактор для
Windows со следующими базовыми возможностями:
подсветка синтаксиса;
сворачивание кода;
автодополнение;
закладки;
регулярные выражения для поиска и замены;
запись и воспроизведение макросов;
сравнение файлов;
переопределение любых горячих клавиш;
резервное копирование сохраняемых файлов;
автоматическое обновление содержимого файла при изменении его во внешнем
редакторе.
программа дешифрация пароль приложение
Рисунок 2.1 Вид текстового редактора Notepad++
3. Разработка алгоритмов
3.1 Алгоритм
работы шифровщика
Рисунок 3.1 Блок-схема алгоритма шифровщика
Структура разрабатываемого приложения, загруженного в оперативную память,
показана на рисунке 3.2. Так как код шифровщика не следует записывать в
защищенный исполняемый файл, код расшифровщика помещается для удобства по
младшим адресам (в начале исполняемого файла разрабатываемой программы). Так
как программа односегментная, её выполнение начинается со смещения 100h, следовательно, по этому смещению
необходимо поместить код перехода к процедуре шифратора. Блок-схема алгоритма
шифровщика приведена на рисунке 3.1. Код шифровщика выполняется лишь один раз в
процессе создания защищенной программы и не копируется в выходной файл. Подсчитав
длину исходного .com файла
защищаемого приложения, шифровщик вычисляет адрес размещения процедуры
дешифратора в создаваемом исполняемом файле и записывает его восьмым и девятым
байтом в выходной файл. Первые семь байтов - код для загрузки адреса перехода и
сама команда перехода (см. рис. 3.3).
3.2 Алгоритм
расшифровки образа защищенного приложения
В начале защищенного файла (см. рис. 3.3) находится код перехода к
процедуре расшифровки, который при запуске .com файла на выполнение помещается в памяти по смещению
100h и выполняется. Арес перехода
считывается по смещению 107h.
Получив управление, процедура расшифровки копирует свой код в седьмую
страницу видеопамяти по правильному смещению. Этот метод был опубликован О.
Калашниковым в книге "Ассемблер? Это просто!" [1] и нужен для
настройки адресов расшифровщика, который загружается в памяти после кода образа
защищенного приложения по адресу, зависящему от длины образа. По завершению
копирования кода процедуры расшифровки в седьмую страницу видеопамяти в стек
заносится адрес возврата и управление передается скопированному коду
расшифровщика. Далее программой запрашивается и проверяется пароль,
восстанавливаются первые девять байтов кода защищаемого приложения, которые хранятся
в специальном буфере расшифровщика. После этого декодируется образ, и
управление передается расшифрованному коду.
4. Описание программы
4.1 Структура
программы
В программе выделяются два основных функциональных блока: код шифратора и
код дешифратора. Блок шифрации требуется только на этапе создания нового
защищенного исполняемого файла, поэтому, в отличие от участка кода дешифратора,
не дописывается в конец выходного .com файла. Было логично поместить его после кода расшифровщика (см. листинг
4.1). К тому же, это упрощает впоследствии вычисление размера зашифрованного
образа, операции по настройке адресов на этапе загрузки расшифровщика в
видеопамять, а так же позволяет копировать код дешифратора в седьмую страницу
видеопамяти по смещению 100h, что
является обычным расположением .com программ.
Листинг 4.1 Общая структура программы
4.2 Код
шифровщика
Передача управления на блок шифрации происходит в строке 9 листинга 4.1.
Сначала шифратор запрашивает ввод имени файла, предназначенного для
защиты, имени результирующего файла, пароль. Затем происходит коррекция
содержимого буферов с помощью специальной процедуры FilenameCorrectProc,
описанной в строках 315 - 346 (см. листинг в приложении А.1). Предназначена она
для сдвига строк и добавления расширения .com в конце каждой строки. Далее открывается защищаемый
файл для чтения, создается выходной файл в соответствии с указанным именем,
подсчитывается длина входного файла с помощью процедуры FilenameCorrectProc,
описанной в строках 353 - 357 листинга, на основании полученной длины
подсчитывается точка входа в процедуру дешифратора и сохраняется в переменной
jmpAdress107h (см. листинг 4.2). Участок кода, содержащий эту переменную,
записывается в начало нового исполняемого файла. Впоследствии при запуске файла
на исполнение эта переменная будет загружена по смещению 107h, предоставив возможность определить
расположение дешифратора.
Первые девять байтов файла, открытого для чтения, сохраняются в буфер
FirstBytesOfOriginalProgram, находящийся перед дешифратором.
На следующем этапе шифровщик копирует оставшееся содержимое входного
файла в выходной, предварительно кодируя каждый считанный байт с помощью
процедуры CodingByteProc (см. листинг 4.3). На вход процедуры кодирования
подается байт для кодирования и адрес буфера, хранящего пароль. Рассчитывается
контрольная сумма пароля путем побайтового суммирования элементов буфера с
отбрасыванием старшего байта результата. На выходе процедуры байт результата,
полученный применением операции "исключающее или" к контрольной сумме
пароля и входному байту.
Завершается работа шифратора копированием в создаваемый исполняемый файл
кода дешифратора, находящегося между метками begin и EndDecoderProgram (см.
листинг 4.1). Код по адресам, старшим адреса метки EndDecoderProgram, в
выходной файл не копируется.
Листинг 4.3 Процедура кодирования считанного байта
4.3 Код
расшифровщика
При запуске защищенного файла на исполнение, программа загружается в
оперативную память по смещению 100h. Первые девять байтов по этому смещению занимает код передачи управления
на блок дешифрации (см. листинг 4.2). В строках 304 - 305 листинга код
занесения содержимого ячейки со смещением 107h (точка входа в процедуру дешифрации) в регистр ax. В строке 306 код команды
безусловного перехода по смещению в регистре ax.
Вначале дешифровщик вычисляет размер образа методом, описанным в листинге
2.1. Это необходимо для расчета адреса возврата из седьмой страницы видеопамяти
и получения текущих смещений начала и конца кода программы. Получив смещения
начала и конца своего кода, программа загружает себя в седьмую страницу
видеопамяти по смещению 100h.
Адрес возврата искусственно заносится в стек (см. строки 32 - 35 листинга 4.4).
Так же в стек заносится сегмент седьмой страницы видеопамяти (0BF00h) и
смещение точки входа в нем. Благодаря нестандартному применению команды
возврата retf (см. строку 40 листинга 4.4), процессор извлекает из стека точку
входа блока дешифрации и осуществляет переход по нужному адресу.
Код строк 41 - 95 листинга выполняется в видеопамяти по смещению,
предусмотренному компилятором. Это позволяет использовать адреса меток и
переменных без дополнительной настройки.
Загрузившись в видеопамять, расшифровщик настраивает сегментные регистры,
запрашивает пароль, восстанавливает начальные девять байтов кода защищенной
программы, декодирует образ. Процедура декодирования аналогична процедуре
шифрования, описанной в пункте 4.1.
В строке 95 листинга 4.4 происходит возврат управления из области
видеопамяти в сегмент расшифрованной программы. Адрес возврата был занесен в
стек ранее.
Завершается работа дешифровщика восстановлением сегментных регистров и
передачей управления на первую команду расшифрованного образа (по смещению 100h).
5. Инструкция пользователя
- запустите программу coder.com
Рисунок 5.1 Запуск программы coder.com
Программы выведет запрос на ввод имени защищаемого исполняемого файла,
имени выходного файла и пароля.
ответьте на запрос ввода
Имена файлов следует вводить без расширения.
Если файл находится в одном каталоге с программой coder.com, то полный путь к файлу можно не вводить, указав лишь
имя самого файла.
Если файл находится в подкаталоге папки нахождения программы-шифровщика,
то можно указать частичный путь к файлу, в формате:
\имя_подкаталога1\[имя_подкаталога2]\. . . \[имя_каталогаN]\имя_файла
Пароль может содержать практически любые символы, но рекомендуется
использовать цифры и буквы латинского алфавита. Максимальная длина пароля -
восемь символов.
При попытке ввода девятого символа, программа завершит ввод, сохранив
только первые восемь символом.
Если пароль короче восьми символов, завершите ввод нажатием клавиши
ENTER.
При неудачном открытии файлов, программа выведет соответствующее
сообщение и завешит работу.
Нажмите любую клавишу для завершения работы приложения
Рисунок 5.2 Завершение работы программы
6. Описание контрольного примера
Для тестирования программы был создан пробный исполняемый файл test0.com. Его листинг приведен в приложении А.2.
На рисунке 6.1 приведен начальный фрагмент содержимого программы coder.com, представленный в шестнадцатеричном виде, на рисунке
6.2 - содержимое файла test0.com.
Длина пробного исполняемого файла - 63 байта.
С помощью программы coder.com файл test0.com
был зашифрован и записан в файл test0_c.com, содержимое которого приведено на рисунке 6.3.
Рисунок 6.1 Фрагмент содержимого файла coder.com
Рисунок 6.2 Содержимое исполняемого файла test0.com
Рисунок 6.3 Содержимое файла test0_c.com
Рассмотрев детально рисунок 6.3, можно заметить, что начиная с 64 байта,
в файле расположен код дешифровщика - начало файла coder.com
(см. рисунок 6.1).
Первые семь байтов - код передачи управления на точку входа дешифровщика
(см. рис. 6.3 и 6.4).
Восьмой и девятый байт - смещение для перехода.
С 10 по 63 байты в файле test0_c.com расположен зашифрованный образ программы test0.com, за исключением первых 9 байтов, которые сохранены в
процедуре дешифровщика - байты 67 - 76 в файле test0_c.com.
Рисунок 6.4 Код передачи управления на точку входа дешифровщика
Список источников информации
1 Калашников, О.А. Ассемблер? Это просто! Учимся
программировать. - С.П.: БХВ-Петербург, 2006. - 375 с.
Юров, В.И. ASSEMBLER 2 издание. Учебник для ВУЗов. - С.П.: Питер, 2003. - 636 с.
Калашников О.А. Рассылки. Ассемблер? Это просто!
Учимся программировать [Авторский курс Калашникова Олега Александровича]. URL:
#"602045.files/image016.gif">
А.2 Листинг
приложения test0.com