либо резидентно находится в памяти, либо непосредственно включен в
модифицируемый EXE-файл. Второй подход более удобен.
Для корректировки функций EXE-файла необходимо: внедрить в него
этот драйвер; осуществить передачу управления на драйвер.
ВКЛЮЧЕНИЕ В ТЕЛО EXE-файла СВОЕГО МОДУЛЯ
Вариантов включения собственного блока в существующий EXE-файл
несколько: можно добавить его в конец или в начало файла; встроить в
свободное место внутри программы или "склеить" им оба EXE-файла (свой
и модифицируемый).
Легче всего добавить дополнительные команды в конец EXE-файла и
исправить в заголовке размер загружаемой части (с учетом добавленных
байт). Однако предварительно необходимо проверить, возможна ли их
загрузка в ОЗУ. Дело в том, что задача может быть оверлейной и DOS
загрузит только ее корневой сегмент. Например, в BORLAND C++ размер
BCC.EXE более 800 кб, а объем ОЗУ всего 640 кб. Ясно, что загрузкой и
выгрузкой отдельных частей гиганта занимается специальный блок в
корневом сегменте программы, который ничего не знает о "прилепленном"
в конце модуле, и поэтому тот никогда не попадет в оперативную память.
А в заголовке имеется информация только о корневом блоке, небольшом по
размеру. Так что если к загружаемой части не относится "хвост", значит
данный метод здесь не годится.
В этом случае можно поискать "пустые" места в загружаемой части
EXE-файла. Например, цепочку любых повторяющихся кодов. Если размер
найденного пространства больше модуля, то все в порядке и следует
вписать туда свои команды.
Если же и этого нет, то пробуем включить свой модуль в начало
программы, сразу за таблицей перемещения, конечно, при наличии там
свободного места. Впрочем, в отличие от предыдущего способа, здесь его
всегда можно создать. Достаточно "раздвинуть" файл и изменить таблицу
перемещения с учетом сдвига, затем подправить заголовок (адрес первой
исполняемой команды и размер загружаемой в память части файла).
Кстати, пример такой программы с комментариями приводится на дискете.
В принципе, можно и не раздвигать файл, а создать модуль в виде
такого же COM или EXE-файла. Затем изменить список распределения
кластеров в FAT-таблице каталога, не трогая их самих.
FAT (File allocation table) - это таблица в начальных секторах
дискеты, которая содержит связный список цепочек кластеров, занимаемых
тем или иным файлом на диске или относящимися к свободному
пространству [2]. Ее дополняет оглавление файлов (называемое также
директорий или каталог). Два байта по смещению 1Ah в элементе
оглавления каждого файла содержат номер начального кластера цепочки, а
каждый элемент цепочки в FAT-таблице указывает на следующее звено или
информирует о конце файла.
Для включения модуля в EXE-файл необходимо найти в FAT-таблице
цепочку, отмечающую занятые модулем кластеры диска и заменить в ней
метку "конец модуля" ссылкой на начальный кластер встраиваемого
исполняемого файла. Затем в элементе оглавления файла исправить номер
начального кластера (вместо него вписать стартовый номер кластера
нашего модуля), а сам модуль пометить как удаленный.
В результате при запуске EXE-файла на выполнение операционная
система загрузит в память содержимое всей цепочки кластеров с диска, в
том числе и кластеров нашего файла, который первым получит управление.
Теперь уже его команды должны отвечать за запуск исполняемой части
EXE-файла, в который он был внедрен.
Основная сложность при "склейке" двух программ заключается в том,
чтобы после первой корректно запустить вторую. Если первая - ваша,
вопрос решается просто: при ее завершении нужно передать управление по
адресам, взятым из заголовка второй задачи. Если же обе из
"склеиваемых" - "чужие", причем завершаются в неизвестном месте и
неизвестным способом, то выход один - необходимо изменить часть DOS,
обрабатывающую функции завершения процессов.
Это можно сделать двумя способами. Первый. Создается специальный
драйвер перехватывающий все функции завершения работы программ
(назовем его монитор). Затем, "склеиваются" вместе все выбранные EXE
или COM-файлы, причем сначала в полученном конгламерате размещается
монитор. При запуске он принимает на себя обработку прерываний, а
получив управление, анализирует - какая из задач уже отработала,
освобождает ресурсы, выбирает и размещает в ОЗУ следующую задачу, и
передает ей управление. После последней задачи монитор восстанавливает
исходную подпрограмму DOS и завершает свою работу.
Второй вариант является модификацией первого. Разница заключается
только в том, что монитор не обладает суверенитетом отдельной задачи,
а прикрепляется к первой из них одним из уже описанных способов. После
этого, к "зараженной" монитором задаче можно командой COPY добавлять
любое количество EXE-файлов.
ПЕРЕДАЧА УПРАВЛЕНИЯ
Управление автоматически перейдет на модуль, внедренный в тело
EXE-файла, если изменены начальные значения регистров CS и IP в
заголовке файла (байты 20 - 23 заголовка) или первый оператор, на
который указывают эти регистры (вместо него ставят команду перехода
JMP либо CALL по адресу своего модуля). Но можно перехватить
управление исправив номер прерывания в двухбайтовой команде INT.
Рассмотрим эти методы подробнее.
Для изменения начальных значений регистров CS и IP в заголовке
EXE-файла после внедрения блока команд в его тело нужно скопировать с
20 по 23-й байт заголовка (CS:IP) в специальный буфер, предусмотренный
в модуле. Затем вписать в заголовок файла адрес первой команды модуля,
а по его завершении передать управление на сохраненный в буфере адрес
начала выполнения EXE-файла.
Впрочем, можно измененить первую команду (указанную регистрами
CS:IP), не меняя сам заголовок. После внедрения блока команд в тело
файла скопируем первые байты в заранее предусмотренный буфер, а вместо
них впишем JMP или CALL с адресом модуля. По завершении его работы
выполним сохраненные в буфере команды EXE-файла и передадим управление
ему самому (на адрес из заголовка файла с учетом уже выполненных
команд).
Если же недалеко от начала EXE-файла встретится собственный JMP
или CALL с атрибутом far , то достаточно изменить адрес перехода у
существующей команды (запомнив первоначальное значение) на адрес
встроенного модуля. Удостовериться в том, что обнаруженный вами байт (
EAh или 9Ah ) действительно является первым байтом команды JMP или
CALL с аттрибутом FAR можно используя таблицу перемещения: один из ее
элементов должен указывать на адрес на 3 больший, чем адрес найденного
байта (это сегментная часть адреса в команде).
Изменение номера прерывания в двухбайтной команде INT является
самым простым из всех перечисленных способов перехвата управления.
Метод не требует внедрения своего модуля в тело "чужого" файла.
Достаточно лишь изготовить резидентный драйвер, отрабатывающий любое
свободное прерывание DOS. А в EXE-файле ищем первое попавшееся
прерывание и заменяем его тем, на котором "висит" наш драйвер.
Естественно, что он должен обрабатывать и функцию исправленной команды
INT в ЕХЕ-файле.
ДИНАМИЧЕСКАЯ КОРРЕКТИРОВКА ПРОГРАММЫ.
Допустим, надо отстранить резидентную задачу от обработки
какого-либо прерывания. В этом случае необходимо выяснить, какая из
загруженных в ОЗУ программ получает, словно эстафетную палочку,
обработку данного прерывания.
Доступ к диску осуществляется, в основном, через 13h и 21h
прерывания. Системы защиты обычно "забирают" их на себя и проверяют,
разрешен ли доступ по ним. Если разрешен, то остальной процесс
возвращается обратно к операционной системе. Порой, чтобы снять
защиту, достаточно восстановить переустановленный вектор прерывания,
направив его на DOS. А найти старый адрес поможет пошаговый режим
(трассировка) отладчика. Но как определить, что "процесс дошел" именно
до защитного механизма? Это можно сделать автоматически. Текст такой
программы (на языке Си) находится на дискете в директории FTRACE.
Заметим, что трассировкой пользуются не только отладчики, но и
вирусы. Метод их работы демонстрируется программой RV (файлы rv.c,
ftrace.asm, ftrace.h). Кстати, "вирусный" подход можно использовать и
для борьбы с вирусами же. Практика показала его эффективность:
например, приведенная на дискете программа protect.asm мешает "хорошо
себя чувствовать" некоторым вирусам, но и, правда, некоторым
антивирусам тоже.
7. КАК ОЧИСТИТЬ ПРОГРАММУ ОТ ВИРУСОВ И
ПРИСТЫКОВАННЫХ ЗАЩИТ
В прерыдущей главе описано, как внедрить защитный механизм в тело
программы. В этой, напротив, расскажем о способе его удаления с
помощью программы EXEB, имеющейся на дискете.
НАЗНАЧЕНИЕ
Программа EXEB.EXE предназначена для анализа исполняемых модулей
и удаления из них пристыковочных частей - вирусов, защит и т.п. Она
корректно работает на ПЭВМ типа IBM PC/XT/AT в среде MS DOS версии 3.1
и выше.
СОДЕРЖИМОЕ ДИСКЕТЫ И ПОРЯДОК УСТАНОВКИ
Поставляемая дискета содержит следующие файлы:
README - описание;
EXEB.EXE - основная программа;
CHSIZE - утилита для уменьшения размера результирующей задачи.
Перед началом работы необходимо скопировать дискету на жесткий диск
в любой директорий.
Формат команды запуска:
EXEB [!]
[]
где:
! - признак "холостого" срабатывания;
::= номер перехватываемого прерывания;
::= номер функции (AH) указанного прерывания;
::= счетчик появлений функции FuncNumber;
::= имя исследуемой задачи;
::= аргументы командной строки исследуемой
задачи.
Примечание:
В командной строке в качестве аргумента FuncNumber разрешен
символ "*".
Числовые аргументы задаются в 16-ричной системе счисления.
Например, "EXEB 21 30 1 a.exe" - означает: перенести код
исследуемой задачи a.exe в результирующий EXE-файл при первом
обращении к 30h функции 21h прерывания после запуска a.exe.
Аргументы желательно выбирать таким образом, чтобы они
соответствовали первому после пристыкованного блока прерыванию в теле
исследуемой задачи.
Если программа была создана компиляторами с языков высокого
уровня (С, PASCAL, BASIC и т.п.), то рекомендуются следующие значения
аргументов:
= 21 = 21
= 30 или = 35
= 1 = 1.
EXEB позволяет исследовать исполняемый EXE-модуль на команды
вызова прерываний (int). Их номера фиксируются программой EXEB, если в
качестве аргумента FuncNumber использован символ "?". Например: при
строке запуска программы "EXEB 10 ? 1 a.exe" в крайнем правом столбце
экрана будут выcвечиваться текущие номера функций 10-го прерывания.
Первый аргумент командной строки "!" - признак "холостого"
срабатывания - необязателен. Его функциональное назначение - выдача
звукового сигнала в момент обнаружения указанной вами в командной
строке ситуации (без реальной ее обработки программой EXEB, т.е. без
создания результирующего EXE-файла).
Итогом работы EXEB является файл с именем TASK.EXE, который можно
запускать на выполнение (в той же среде, в которой он был создан:
версия DOS, конфигурация ПЭВМ).
ПРИНЦИПЫ РАБОТЫ
Будучи запущенной, EXEB захватывает указанное в командной строке
прерывание, запускает исследуемую задачу и начинает отсчитывать
обращения к указанной в командной строке функции захваченного
прерывания. Когда счетчик обращений станет равен 0, EXEB переносит
образ исследуемой задачи из оперативной памяти на диск. При этом
первой исполняемой командой задачи станет вызов указанного в командной