    6. ИСПОЛНЯЕМЫЙ МОДУЛЬ - ЧТО МОЖНО СДЕЛАТЬ
                            БЕЗ ИСХОДНЫХ ТЕКСТОВ?
         Одной из самых сложных работ в программировании является
    модификация исполняемых модулей при отсутствии исходных текстов. Надо
    признать, что программисту не часто приходится заниматься подобными
    работами, но, однако, никто не застрахован от потери собственных
    исходных текстов программ. В частности, мы обратились к решению
    названной задачи для того, чтобы исправить найденную в нашем пакете
    ошибку. Исходники к тому времени были случайно уничтожены при борьбе
    с вирусом. Бесполезно вспоминать и переписывать весь пакет - за тот
    срок, в течении которого гарантировалось устранение любых замечаний,
    мы в любом случае не успевали этого сделать. Поэтому стали искать
    другие пути. В результате разработали соответствующий метод и даже
    набор инструментальных средств, который с успехом применяем по сей
    день.
         Разумеется, приемы корректировки исполняемых модулей
    предназначены профессиональным разработчикам ПО для IBM PC в среде
    DOS. Поэтому опустим некоторые детали, которые профессионал должен
    знать, а в качестве примера возьмем лишь EXE-файлы - более сложные для
    модификации, поскольку все сказанное может быть отнесено и к
    COM-файлам.
         Напомним, что EXE-файл состоит из заголовка, таблицы перемещения
    и собственно исполняемого кода. Информация заголовка используется
    операционной системой для загрузки модуля в оперативную память.
    Устанавливаются значения основных регистров, обрабатывается таблица
    перемещения, а затем управление передается задаче. В ходе работы все
    основные функции ввода/вывода, захвата или освобождения ОЗУ и т.п.
    обрабатываются операционной системой через прерывание 21h, а значит,
    всегда могут быть изменены с помощью специального драйвера, который
    либо резидентно находится в памяти, либо непосредственно включен в
    модифицируемый 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 мешает хорошо
    себя чувствовать некоторым вирусам, но и, правда, некоторым
    антивирусам тоже.
вверх