Tartalmi kivonat
Segédlet a Vírusvédelem c. tantárgyhoz Alapismeretek Számítógépes vírusok Készítette: Dr. Leitold Ferenc 1 Tartalomjegyzék 1. Alapismeretek 1.1 Háttértárolók felépítése 1.11 A diszkek fizikai felépítése 1.12 Diszkek logikai felépítése 1.13 Partíciók felépítése 1.14 Directory- és fileszerkezet 1.15 Diszkhozzáférési lehetőségek DOS cluster-szám <--> DOS sector-szám Abszolút sector-szám <--> DOS sector-szám 1.2 Assembly alapismeretek 1.21 Assembly – gépi kód, alapfogalmak 1.22 Regiszterek 1.23 Utasításkészlet 1.231 Aritmetikai utasítások 1.232 Adatmozgató utasítások 1.233 Vezérlésátadó utasítások 1.234 Processzorvezérlő utasítások 1.24 Címkék, változók definiálása 1.3 Kapcsolat a perifériákkal 1.31 Fontosabb DOS funkciók 1.32 Fontosabb lemezkezelő BIOS funkciók 1.4 Az interrupt lánc Programok futtatása 2. Számítógépes vírusok 2.1 Mi is az a számítógépes vírus? 2.2 Hogyan keletkeznek a
vírusok? 2.3 A vírusok fejlődése 2.31 Első generációs vírusok 2.32 Második generációs vírusok 2.33 Harmadik generációs vírusok 2.34 Negyedik generációs vírusok 2.4 A vírusfertőzési módszerek 2.41 Boot vírusok 2.42 File indításakor aktivizálódó vírusok 2.5 Védekezés a vírusok ellen 2 3 3 3 3 7 8 10 10 10 12 12 13 18 18 19 20 21 22 24 24 26 27 28 29 29 30 30 31 31 31 32 33 33 33 35 1. Alapismeretek 1.1 Háttértárolók felépítése A számítógépes vírusok fertőzésük során a számítógép háttértárán valamilyen program– területet fertőznek meg. A vírusok működésének a megértéséhez alapvető szükség van a háttértárolók belső felépítésének a megismerésére. 1.11 A diszkek fizikai felépítése A diszkek kétdimenziós felületű lemezekből épülnek fel. A floppy diszkek egy lemezt, a merevlemezek általában több lemezt tartalmaznak. A lemezeknek mind a két oldalát használják, mind a két oldalon
találhatók író/olvasó fejek (head). Előfordulhat azonban, hogy a merevlemez egy lemezoldalát szinkronizálási célokra használják. Egy-egy lemezoldal kétdimenziós felépítésű: sávoknak (track, vagy cylinder) nevezett körgyűrűkre, minden körgyűrű szektorokra van osztva. Egy szektor mérete a diszk formázásának a függvénye, MSDOS operációs rendszer alatt általában 512 byte Megjegyzés: Elképzelhető, hogy egyes oprációs rendszerek ettől eltérő szektorméretet használnak. Például a TandomDOS 1024 byte-os szektorokat kezel, ami néhány vírus esetén problémákat okoz. A Cluster Buster (vagy DIR2/FAT) vírus feltételezi, hogy egy szektor mérete 512 byte, így a TandomDOS rendszerű gépeken a fertőzés révén a fertőzött programok használhatatlanná válnak. Mind az egyes lemezoldalak, azaz az író/olvasó fejek (head), mind a sávok (cylinder), mind pedig a sávokon belüli szektorok (sector) sorszámozottak. A sector-szám 1-től, a
cylinder- és a head-szám pedig 0-tól kezdődik. Így egy szektort a (cylinder, head, sector) hármassal azonosíthatunk. A merevlemez szektorainak ezt az azonosítási módját fizikai címzésnek nevezzük. A merevlemez szektorai logikailag egy meghatározott sorrendet alkotnak Ebben a sorrendben az első helyen a 0. cylinder 0 head-jének szektorai állnak (1-től kezdődően) Ezt követik a 0. cylinder 1 head-jének szektorai, majd a 0 cylinder utolsó head-jének szektorai után az 1. cylinder 0 head-jének szektorai következnek Így a merevlemez egy szektorára a szektor sorszámával is hivatkozhatunk. A szektoroknak ezt az azonosítási módját abszolút címzésnek nevezzük, ahol tehát az 1-es abszolút című szektor fizikai paraméterei: 0. cylinder, 0. head, 1 sector A merevlemezek paramétereit (cylinder-szám, head-szám, sector-szám) a CMOS memória tartalmazza. 1.12 Diszkek logikai felépítése A diszkeket kezelő operációs rendszerek a merevlemezeket logikai
egységekre partíciókra osztják. A partíciókra osztás operációs rendszertől, hálózati rendszertől független Az egyes partíciók információit (kezdet, vég, boot-olható vagy sem) a partíciós táblázat tartalmazza, amely a partíciós táblában vagy más néven Master Boot Recordban (MBR) található. Az MBR a partíciók információin, a partíciós táblázaton túlmenően egy programrészletet is tartalmaz, amely a gép bekapcsolásakor, illetve minden boot-oláskor hajtódik végre. Megjegyzés: Az MBR és a partíciós tábla egyaránt ugyanazt, a lemezegység fizikailag első szektorát (0.head, 0.cylinder, 1sector) jelenti A partíciós táblázat viszont ennek a szektornak csupán a partíciós információkat tartalmazó részét (1BEh címtől kezdődően). 3 1. ábra: Merevlemezek partíciókra osztása Mint az az 1.ábrán látható, a 0 cylinder 0head 1 szektora az MBR, a 0 cylinder 0headjének többi szektora pedig nem tartozik egyetlen
partícióhoz sem Általában igaz ugyanis, hogy az első partíció nem az MBR-t követő szektorral kezdődik, hanem a 0.cylinder 1head 1.sector-ral Megjegyzés: Boot-oláskor a ROM-ban lévő BIOS program indul el, amely elvégzi a szükséges inicializálásokat, teszteléseket, majd betölti a boot-lemez fizikailag első szektorát (0.head, 0cylinder, 1sector), és a betöltött szektor elejére adja a vezérlést. Floppy lemezek esetén (ahol egy partíció található), a partíció első szektora hajtódik végre. Merevlemezek esetén az MBR töltődik be, melynek programja megkeresi partíciós táblázatban az első boot-olható partíciót, betölti annak első szektorát és annak az elejére adja a vezérlést. A partíciós táblázat maximum négy partíció információit tartalmazza. A DOS operációs rendszer ennél több logikai partíciót is képes kezelni. Ehhez úgynevezett Extended partíciót hoz létre, amelyet logikailag további egységekre oszt. A MBR
(partíciós tábla) felépítése az alábbi: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Master Boot Record (MBR) ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Programrészlet, amely betölti az első ; boot-olható partíció első szektorát és ; annak első byte-jára adja a vezérlést. 7C00 7C01 7C03 7C05 FA 33C0 8ED0 BC007C CLI XOR MOV MOV 7C08 7C0A 7C0B 7C0C 7C0D 7C0E 7C0F 7C10 7C13 7C16 8BF4 50 07 50 1F FB FC BF0006 B90001 F2A5 MOV PUSH POP PUSH POP STI CLD MOV MOV REPNZ AX,AX SS,AX SP,7C00 ; Interrupt tiltás ; Stack állítása közvetlenül e program ; alá ; A programterület másolása ; a DS:7C00-ás címről SI,SP AX ES AX DS ; ES = 0 ; DS = 0 DI,0600 CX,0100 MOVSW ; ; ; ES:0600-as címre 0100 db word MÁSOLÁS 4 ; Ugrás a másolt terület következő byte; jára 7C18 EA1D060000 JMP 0000:061D 061D BEBE07 0620 B304 MOV MOV SI,07BE BL,04 ; SI = Partíciós táblázat
kezdőcíme ; Lehetséges partíciók száma 0622 803C80 0625 740E CMP JZ [SI],80 0635 ; Boot-olható partíció ? ; Ugrás, ha igen 0627 803C00 CMP [SI],00 062A 751C JNZ 0648 ; Ha nem boot-olható, akkor 00-nak kell ; lenni ; Ugrás, ha nem 00 062C 83C610 ADD SI,0010 062F FECB 0631 75EF DEC JNZ BL 0622 0633 CD18 INT 18 0635 8B14 0637 8B4C02 063A 8BEE MOV MOV MOV DX,[SI] ; A partíció paramétereinek töltése a CX,[SI+02] ; BX és DX regiszterekbe BP,SI ; BP = boot partíció táblázatának címe 063C 83C610 063F FECB 0641 741A ADD DEC JZ SI,0010 BL 065D ; A táblázat további részének ellenőrzése 0643 803C00 0646 74F4 CMP JZ [SI],00 063C 0648 BE8B06 MOV SI,068B ; ; ; ; ; ; 064B AC 064C 3C00 064E 740B LODSB CMP JZ AL,00 065B 0650 0651 0654 0656 0658 0659 065B PUSH MOV MOV INT POP JMP JMP SI BX,0007 AH,0E 10 SI 064B 065B 065D 0660 0663 0666 0667 0669 56 BB0700 B40E CD10 5E EBF0 EBFE BF0500 BB007C B80102 57 CD13 5F MOV MOV MOV PUSH INT
POP ; SI = következő partíció címe a partíciós ; táblázatban ; Ugrás a következő bejegyzés vizsgálatára, ; ha van még ; ROM BASIC töltése ; Ugrás, ha vége a táblázatnak Ellenőrzés Ugrás a következő elem vizsgálatára, ha rendben volt az ellenőrzés Az Invalid partition table" szöveg kiírása Szöveg kiíása ; Következő byte olvasása DI,0005 BX,7C00 AX,0201 DI 13 DI ; Ugrás, ha vége a szövegnek ; SI mentése ; Egy karakter kiírása ; BIOS szolgáltatás segítségével ; SI visszaállítása ; Végtelen ciklus ; ; ; ; ; A boot partíció első szektorának a beolvasása 5-ször próbálkozunk 7C00-ás címre 1 szektor olvasása ; BIOS szolgáltatás meghívása 5 066A 066C 066E 0670 0671 730C 33C0 CD13 4F 75ED JNC XOR INT DEC JNZ 0678 AX,AX 13 DI 0660 0673 BEA306 MOV SI,06A3 0676 EBD3 JMP 064B 0678 BEC206 067B BFFE7D 067E 813D55AA MOV MOV CMP 0682 75C7 JNZ SI,06C2 DI,7DFE ; Az utólsó 2 byte-nak AA55-nek kell
[DI],AA55 ; kell lennie ; Ha nincs rendben az ellenőrzés, úgy 064B ; a "Missing operating system" szöveg ; kiírása 0684 8BF5 MOV SI,BP ; ; ; ; ; Ugrás, ha sikerült beolvasni RESET drive, ha nem sikerült a beolvasás Beolvasások számának aktualizálása Ugrás a következő olvasáshoz ; ; ; ; ; 5 próbálkozás után Az Error loading operating system" szöveg kiírása Ugrás a szöveg kiírására A betöltött szektor ellenőrzése ; SI = a boot partíció táblázatának címe 0000:7C00 ; Ugrás a betöltött szektor első byte-jára 0686 EA007C0000 JMP ; A programterületen használt szövegek 068B: 0690: 06A0: 06B0: 06C2: 06D0: 06E0: 69 6c 20 6d 69 00 64 65 6f 00 6e 00 20 00 70 4d 67 00 70 45 65 69 20 00 61 72 72 73 73 00 72 72 61 73 79 00 74 6f 74 69 73 00 69 72 69 6e 74 00 74 20 6e 67 65 00 69 6c 67 20 6d 00 6f 6f 20 6f 00 00 49 6e 61 73 70 00 00 6e 20 64 79 65 00 00 76 74 69 73 72 00 00 61 61 6e 74 61 00 00 6c 62 67 65 74 00
00 " Inval" "id partition tab" "le.Error loading" " operating syste" "m.Missing operat" "ing system." "." . 07A0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 07B0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 07BE: 07C0: 07CE: 07D0: 07DE: 07E0: 07EE: 07F0: 07FE: 01 00 06 0b e3 db 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; Partícíós táblázat ; Minden bejegyzés 10h hosszúságú 80 01 ; 23 00 00 00 cd 54 06 00 00 00 ; 00 00 00 00 00 00 00 00 00 00 ; 00 00 00 00 00 00 00 00 00 00 ; 00 00 00 00 00 00 00 00 55 aa ; 6 1. partíció 2. partíció 3. partíció 4. partíció Boot azonosító Eltolás Méret +0 1 Tartalom Jelentés BootFlag 80h = boot-olható, egyébként 00h +1 1 BHead Kezdő head-szám +2 2 BSecCyl Kezdő sector és cylinder-szám +4 1 SysCode Rendszer kód: 0: ismeretlen 1: DOS 12 bit FAT 4: DOS 16 bit FAT 5: Extended DOS 6: BIGDOS +5 1 EHead Végső
head-szám +6 2 ESecCyl Végső sector és cylinder-szám +8 4 RelSec A partíció első szektorának abszo-lút címe +0Ch 4 PartSize A partíció mérete (szektorokban) 1.13 Partíciók felépítése A DOS operációs rendszer a partíciókat, mint logikai egységeket további részekre osztja. Egy partíció DOS-szektorokra osztódik. Megjegyzés: A szakirodalomban a fizikailag elérhető szektorok, és a DOS-on keresztül elérhető szektorok esetén is a szektor elnevezést használja. A könnyebb áttekinthetőség kedvéért a DOS-on keresztül kezelhető szektorokat DOS-szektoroknak nevezzük. A DOS-szektorok szintén sorszámozottak, 0-tól kezdődően. A 0 szektor tartalmazza a partíció Boot-sector-át. Ezt követi a File Allocation Table (FAT, File Allokációs Tábla) két példányban, majd a Root Directory (Főkönyvtár), és végül a Data Area (Adatterület), amely a partíción elhelyezett alkönyvtárakat, illetve file-okat tartalmazza. Ezt a
címzési módszert DOS szektor címzésnek nevezzük. A 0 szektor, a boot-sector felépítése az MS-DOS operációs rendszer esetén részletesen [1]-ben a 199 oldalon található. A lényegesebb változók az alábbiak: 7 Eltolás Méret Tartalom Jelentés +3 8 Name A formázó program, illetve operációs rendszer neve. +0Bh 2 SectSize Egy szektor mérete byte-okban. +0Dh 1 ClustSize Egy cluster mérete szektorokban. +0Eh 2 ResSecs Foglalt szektorok száma az 1. FAT előtt. +10h 1 FatCnt A FAT-ek száma. +11h 2 RootSize A főkönyvtár bejegyzéseinek a száma. +13h 2 TotSecs A partíció mérete szektorokban. +15h 1 Media A lemez médialeíró byte-ja: 0F8h: merevlemez 0F9h-0FFh: floppy lemez +16h 2 FatSize Egy FAT mérete szektorokban. +18h 2 CylSecs Egy sáv mérete szektorokban. +1Ah 2 HeadCnt A head-ek száma. +1Ch 2 HidnSecs Rejtett szektorok. 1.14 Directory- és fileszerkezet A lemezegységen lévő file-ok és
alkönyvtárak a partíció adatterületén találhatók. Az adatterület cluster-ekre osztódik, melyek 2-től kezdődően sorszámozottak. Egy-egy cluster a szektorméret egész számú többszöröse, általában 4096 byte. A cluster-ek tartalmazzák az alkönyvtárak és a file-ok információit. Minden alkönyvtárhoz és minden file-hoz (ha az nem 0 byte hosszúságú) egy cluster-sorozat tartozik. Alkönyvtárak esetén ezek a clusterek tartalmazzák az alkönyvtárban elhelyezett további alkönyvtárak, illetve file-ok információit. Minden file-hoz és alkönyvtárhoz tartozik egy könyvtár-bejegyzés (directory entry), amely tehát a "tartalmazó" alkönyvtár cluster-sorozatában, míg a főkönyvtár esetén a főkönyvtárnak fenntartott helyen található. Ez utóbbi független a clusterektől, egy fix hosszúságú szekvenciális DOS-sector-sorozatot jelent. A file-okhoz, illetve alkönyvtárakhoz tarozó könyvtár-bejegyzés felépítése MS-DOS 5.00
esetén az alábbi: 8 Eltolás Méret Tartalom Jelentés +0 8 FileName A file neve balra igazítva, végét a jelzi, ha nem tölti ki az összes byte-ot. +8 3 Ext A file kiterjesztése balra igazítva, végét a jelzi, ha nem tölti ki az összes byteot. +0Bh 1 Attr A file attributuma. +0Ch 0Ah Reserved DOS számára fenntartott. +16h 2 Time A file létrehozásának, illetve utolsó módosításának ideje. +18h 2 Date A file létrehozásának, illetve utolsó módosításának ideje. +1Ah 2 FirstCuls A file tartalmának első cluster-e. +1Ch 4 Size A file mérete. A könyvtár-bejegyzés tartalmazza a file-ok, illetve alkönytárak cluster-sorozatának első elemét (FirstClus). A cluster-lánc további tagjait a FAT írja le (2 ábra) A FAT tábla minden egyes clusterhez tartalmaz ugyanis egy értéket, ami a következő cluster-számot jelenti. A FAT bejegyzések a partíció cluster-számának függvényében 12 vagy 16 bitesek lehetnek.
A FAT bejegyzések 0-tól sorszámozottak. Az első két FAT bejegyzés speciális jelentéssel bír Ennek első byte-ja a lemezegység Media leíró byte-ja, melynek meg kell egyeznie a bootsector Media leíró byte-jával. A többi byte (12 bites FAT esetén 2 byte, 16 bites FAT esetén 3 byte) értéke FFh. A FAT-bejegyzések a láncoláson túlmenően speciális tartalommal is rendelkezhetnek. Ezek a következők: (0)000h (F)FF0h - (F)FF6h (F)FF7h (F)FF8h - (F)FFFh üres cluster. fenntartott cluster. hibás cluster. a cluster-lánc vége. 2. ábra: Clusterek láncolása Az ábrán szereplő példában az egyszerűség kedvéért a FAT 8 bit, azaz egy byte méretű. Egy file kezelése során tehát a könyvtárbejegyzésből kiindulva clusterenként kell olvasni a file tartalmat. Ezt a kezelési módszert DOS cluster címzésnek nevezzük 9 1.15 Diszkhozzáférési lehetőségek A lemezegységek tartalma többféleképpen érhető el. Hagyományos működést feltételezve a
felhasználói program a DOS operációs rendszerhez fordul, amely a ROM-ban lévő BIOS programját használja a diszk eléréséhez. A BIOS pedig vagy közvetlenül fordul a hardwarehez I/O utasítások segítségével vagy előfordulhat, hogy a lemezkezelő kontroller kártya külön eprom-ot, saját ROM programot tartalmaz, amely elvégzi a lemezegység kezelését. Ezen ROM program is végső soron I/O utasításokat használ. Szerencsére nem létezik még olyan számítógépes vírus, amely I/O utasítások segítségével terjedne. Ez a megoldás ugyanis csak ugyanazon a lemezkezelő kontroller kártyán működne csak megbízhatóan. A legalacsonyabb szintű általánosan használható lemezkezelési módszer a BIOS programjának használata. A lemezkezelés a 13h-as interrupt meghívásával valósítható meg, amely fizikailag kezeli a lemezegységet, azaz hívási paraméterként az elérni kívánt szektor fizikai címét (head, cylinder, sector) kell megadni. A DOS
operációs rendszer többfajta lemezkezelési eljárást is biztosít. A lemezegységeken elhelyezett file-ok, alkönyvtárak kezelésére a 21h-es interrupt több alfunkciója is lehetőséget nyújt. A DOS partíciók DOS-szektoronkénti kezelése a 25h-ös, illetve a 26h-os interrupton keresztül lehetséges. Itt hívási paraméterként az elérni kívánt szektor DOS szektor címét kell megadni. Az operációs rendszer a lemezkezelésre device driver-eken keresztül is lehetőséget biztosít. A különböző lemezkezelési műveletek az elérni kívánt szektor paramétereit más és más címzés szerint igénylik. Szükség van tehát ezen címzési módok közötti konverzióra, átváltásra. A képletekben szereplő valamennyi dőlt betűs változó a boot sector változója, a vastag betűs változók pedig a partíciós táblázatban találhatók. DOS cluster-szám <--> DOS sector-szám A főkönyvtár által elfoglalt szektorok száma: RootDirSects = (RootSize *
32) / SectSize A FAT-ek által elfoglalt szektorok száma: FATSecs = FatCnt * FatSize Az első adatszektor DOS szektor címe: DataStart = ResSecs + RootDirSecs + FATSecs Mindezek után megadhatjuk a CLUSTER sorszámú cluster első szektorának DOS szektor címét: DOSSect = DataStart + (CLUSTER-2) * ClustSize Hasonlóan a SECTOR DOS szektor-számú szektort tartalmazó cluster sorszáma: CLUSTER = ( DOSSect - DataStart ) / ClustSize + 2, feltéve, ha CLUSTER > 2. Abszolút sector-szám <--> DOS sector-szám 10 A DOSSect DOS szektorszámú szektor abszolút szektorcíme: AbsSect = DOSSect + RelSecs Az AbsSect abszolút szektorcímzésű szektor DOS szektorszáma: DOSSect = AbsSect - RelSecs Az utóbbi képlet esetén természetesen feltételezzük, hogy 0 ≤ DOSSect < PartSize . Abszolút sector-szám <--> Fizikai paraméterek Az AbsSect abszolút szektorszámú szektor fizikai paraméterei: SECT = ( AbsSect -1 ) % SectNum + 1 HEAD = ( ( AbsSect -1 ) / SectNum
) % HeadCnt CYL = ( ( AbsSect -1 ) / SectNum ) / HeadCnt A (CYL, HEAD, SECT) fizikai paraméterű szektor abszolút szektorszáma: AbsSect = CYL * HeadCnt SectNum + + HEAD * SectNum + SECT Az egy sáv egy oldalán lévő szektorok számát a CMOS tartalmazza, de a boot szektor ismeretében is számítható: SectNum = CylSecs / HeadCnt 11 1.2 Assembly alapismeretek E rövid ismertető célja azon assembly alapismeretek összefoglalása, amelyek a vírusok működésének megértéséhez elengedhetetlenek. Nem célja a teljes utasításkészlet és a programfejlesztő eszközök bemutatása. Bár ezek ismeretes erősen ajánlott, de nem feltétlenül szükséges az itt leírtak megértéséhez. Nagyobb hangsúlyt az operációs rendszer és a BIOS azon funkciói kapnak, amelyeket a vírusok gyakran használnak. Azokra a trükkökre térünk ki részletesebben, amelyek a vírusok által megtehető lehetőségeket értetik meg. 1.21 Assembly – gépi kód, alapfogalmak A Neumann
elvű számítógépek az adatokat és az utasításokat is ugyanabban a memóriában tárolják. Így gyakorlatilag csak futás közben dől el, hogy egy a memóriában tárolt byte utasítás éppen vagy adat. A processzor rendelkezik egy belső utasítás pointerrel ( IP – Instruction Pointer ) amely arra a byte – ra mutat a memóriában amelyik a következő utasítás lesz. Egy művelet végrehajtása a következő logikai lépésekből áll (értelemszerűen a 3 És 5 Lépés egyes utasításoknál elmaradhat): 1. Utasítás kódjának beolvasása a memóriából 1 2. a kód értelmezése 3. esetleges további operandusok beolvasása a memóriából 4. a művelet végrehajtása 5. az eredmények kiírása a memóriába Azon kódok halmazát, amelyeket egy processzor ártelmes utasításnak értelmez hívjuk gépi kódnak. Előfordulhat, hogy egy utasítás kódja nem csak egy, hanem több byte hosszú Az ember számára azonban rendkívül nehéz lenne ilyen kódokban
gondolkodni, ezért az utasításoknak a jelentésükhöz kapcsolódó rövidített nevet szokás adni. Ezeket a neveket MNEMONIK – oknak hívják. Például a Ne tégy semmit ( nincs utasítás ) mnemonikja : NOP a No Operand rövidítése. Kódja : 144 (90 h) 2 Ezeknek a mneminikoknak a halmazát, kiegészítve néhány nyelvi elemmel ( konstans, változó, eljárás, címke definíciója ) hívják az adott processzor ASSEMBLY nyelvének. A fordító programot, amely az ilyen assembly nyelvén írt forrásprogramot fordítja le gépi kódra hívják ASSEMBLER-nek. Mivel majdnem egyértelmű a mnemonikok és a gépi kód közötti kapcsolat ezért lehetőség van egy már lefordított program visszafordítására is. ( Ez a többi magas szintű nyelvben, pl: Cben nem lehetséges! ) A visszafordító programot DISASSEMBLER-nek hívják Mivel egy a memóriában lévő byte-ról csak futás közben dől el, hogy az adat vagy kód ezért a visszafordítás eredménye lehet, hogy
hibás. Elképzelhető, hogy a program által adatként kezelt t területet is utasításként akarunk értelmezni vagy egy több byte hosszú utasítást nem az első byte-jánál kezdünk el értelmezni . 1 a valóságban a folyamat nem ennyire egyszerű : a 80 x 86-os processzorok egyszerre több byte-ot olvasnak be a memóriából, azokat egy előolvasási bufferbe – instruction queue-ba rakják és itt értelmezik őket. Amennyiben nincs ugrás és az egyik utasítás eredménye a következő utasítás végrehajtásának egy részét nem befolyásolja, akkor ezek az elemi műveletek párhuzamosan hajtódnak végre. Például amíg az egyik utasítás a 4 Pontnál jár, addig a következő utasítás már olvashatja a memóriából az operandusait. 2egy szám után tett „h” azt jelenti, hogy a szám 16-os számrendszerben ( hexában ) értendő. Mivel a 16-os számrendszerhez 16 számjegyre lenne szükség, ezért a 9-nél nagyobb
számjegyeket az ABC betűivel szokás jelölni : A=10, B=11, C=12. D=13, E=14, F=15 Amennyiben a szám betűvel kezdődne a félreértések elkerülése végett bevezető 0-t szokás eléjük írni. Például 0DEFh = 13*162 + 1416 + 15 = 3567. 12 Az ilyen egyszerűbb hibák kiküszöbölésére az egész programot elemezni kell – nem elég egy táblázatból kikeresni a gépi kódokhoz tartozó mnemonikokat. A legismertebb – ha nem egyedüli – ilyen program a Sourcer. Ő még az eljárások, változók, operációsrendszer-hívások visszafejtésére, dokumentálására is képes. A processzorok tervezésénél alapvető szempont az utasításkészlet és a gépi kód kialakítása. Ha túl sok, bonyolult utasítást ismer a processzor, akkor az utasítások gépi kódja hosszú lesz, lassabb lesz az utasítás kiértékelése. Ha viszont kevés féle utasítás van, akkor egy egyszerűbb művelet is több utasítást igényel így nő a programok hossza. Érdekes módon az
optimum nem valahol középen, hanem szélsőséges esetekben van. Ennek megfelelően két fő irányzat fejlődött ki : a RISC ( Reduced Instructon Set - csökkentett utasításkészletű ) és a CISC ( Complete Instructon Set – teljes utasításkészletű ) processzorok . Az egyes megvalósítások előnyei: CISC: • Egyszerűbb a programozása. • Rövidebb programok. • A gépi kódok növelésével exponenciális arányban nő az utasítások száma. • Processzorcsaládok fejlődésénél a bonyolultabb műveletek gyorsítására több lehetőség van ( biztosítható a folyamatos fejlődés ), az irányzat megsértése nélkül bővíthető az utasításkészlet. • Gyakran feleslegesen bonyolultak az utasítások, nincs minden eredményük kihasználva. RISC: • Jobban optimalizálható kód : kevesebb a felesleges elemi művelet. • Mivel az utasítások egyszerűbbek az egymást követő műveleteknek kevesebb hatásuk van egymásra ezért a végrehajtásuk könnyen
párhuzamosítható. • Egyszerűbb utasításokhoz egyszerűbb architektúra kell, az egyszerűbb architektúra nagyobb működési sebességet tesz lehetővé ( nagyobb órajelet ). A 80x86 család tipikusan CISC felépítésű processzorokból áll. Bonyolultabb ciklusszervező, string kezelő, szorzó, osztó utasításaik is vannak. Azért a CISC processzorok sem valósítanak meg mindenféle műveletet, így gyakorlatilag azt mondhatjuk, hogy az assembly-ben való programozáshoz nem azt kell tudni, hogy a processzornak milyen lehetőségei, utasításai vannak, hanem azt, hogy milyenek nincsenek és azok használata hogyan kerülhető el. 1.22 Regiszterek A processzorok műveletek végrehajtására csak a belső változóikban – regisztereikben – képesek. A programozó szempontjából a regiszterek előre definiált rendkívül gyors elérésű változóknak látszanak. Van pár speciális célú belső regiszter, amelyeknek működése teljesen automatikus. Ilyen
például a korábban látott utasítás pointer IP is Minden utasítás lehívás után értéke automatikusan eggyel nő: tovább lép a következő utasításra. Ennek a regiszternek a programozó mindössze új értéket adhat (abszolút ugrás) vagy hozzáadhat valamennyit (relatív ugrás). A regiszterek többsége azonban általános célú, azaz adatot tárolhat, műveleteket (összeadást, kivonást, összehasonlítást, logikai „vagy”, „és”, „kizáró vagy”, műveleteket) lehet velük végrehajtani. Processzorra jellemző, hogy a regiszterei mekkorák A 8088, 80286 processzorok regiszterei 16 bitesek a 80386, 80486, Pentium regiszterei 32 bitesek (de egyes speciális regiszterek már 48 bitesek). Az általános regiszterek nevei: AX, BX, CX, DX A kibővített 32 bites regiszterekre EAX, EBX, ECX, EDX néven lehet hivatkozni 13 (kompatibilitási okok miatt ritkán használják). Annak ellenére, hogy az egyes regiszterek hány bitesek lehetőség van
bitcsoportonként is hivatkozni rájuk. Például az EAX 32 bites regiszter alsó 16 bitje azonos az AX regiszterrel. Az AX alsó 8 bitjére az AL, felső 8 bitjére az AH névvel lehet hivatkozni. Ugyanígy „van” BL, BH, CL, CH, DL, DH regiszter is: 31 EAX 31 16 15 8 7 0 AX AH 16 15 AL 8 7 0 Álljon itt pár példa a regiszterekkel végezhető műveletekre ( természetesen művelet csak az azonos hosszúságú operandusok között végezhető : nem lehet egy 8 bites regisztert hozzáadni egy 16 biteshez ) : Értékadás regiszternek: MOV MOV MOV MOV AX, 0 AL, 45h AH, 1dh EDX, 12345678h :Ezután az AX regiszter tartalma 0000h lesz.3 : AX = 0045h : AX = 1d45h : EDX = 12345678h, DX = 5678h Regiszter tartalmának növelése, csökkentése: INC DEC INC DX CX AL : DX = DX + 1 : CX = CX –1 : AL = AL + 1 Összeadás, kivonás, összehasonlítás: ADD ADD ADD SUB CMP AX, 1234h BH,0f2h CX, DX DX,BX AX, CX :AX = AX + 1234h :BH = BH + 0f2h :CX = CX + DX :DX = DX – BX :AX
– CX az eredmény nem tárolódik, de a :művelet eredményére vonatkozó információk : (túlcsordulás, zéró eredmény ) a flagekben : megőrződik ( lásd később ). Bitenkénti logikai műveletek: AND OR XOR AH, 3fh DL, 0d4h BH, BL :AH = AH and 3fh :DL = DL or 0d4h BH = BH bitenkénti kizáró vagy BL 3 Az assembly nyelven a ; után álló szöveg megjegyzésnek minősül. Egy sorba csak egy utasítás lehet 14 Néhány utasítás kivételével a mnemonik a műveletet azonosítja, az első operandus adja a művelet egyi operandusát, valamint a végeredmény helyét, a második operandus csak a művelet végrehajtásában vesz részt. ( Kivétel például a MUL, DIV – szorzás osztás ahol az első operandus csak az AX lehet, az eredmény is csak az AX-ben képződhet, ezért azt nem is írják ki ). Az általános regisztereknek is vannak speciális funkci, amik egyedien csak az egyik regiszterel végezhetőek el. Ritkábban van ezekre az
utasítsokra szükség és ilyenkor a regiszterek megfeleő használatával- esetelg cseréjével - különösebb gond nélkül áthidalható a hiány. Igy nem kell a szűkös kódkészletből ezekre az utasítűsokra regiszterenként kódot pazarolni. Ilyen funkciók például: csklusszámlálónak mindig a CX-et használják, csak az AXet lehet szorozni, osztani, csak a BX-et lehet memóriacímzésre használni, csak a DX-el lehet I/O portot címezni. További majdnem általánosindexregiszterek: SI, DI ( ESI, EDI ). Szinte minden olyan művelet elvégezhatő velük, mint az általános regiszterekkel, de 8 bites részeikre nem lehet hivatkozni ( nincs SL, SH.) A memória címzésében játszanak nagy szerepet Egy regiszter által mutatott memóriatartalomra vagyunk kíváncsiak, akkor -ek közé tesszük a regisztert, regiszterek összegét, vagy közvetlenül a memóriacímet : MOV MOV ;Az SI által mutatott memóriacímről veszi ;AX leendő értékét. [4000h], AX;A 4000h címre
teszi AX értékét. AX, [ SI] Az Intel processzoroknál érdekes módon 16 bites ( 2 byte-os ´) értékek tárolása a memóriában fordított sorrendben történik : az első byte az alacsonyabb a második a magasabb helyi értékű. Így az előző példában a 4000h címre kerül AL, 4001h címre pedig az AH értéke. ADD [SI+BX+034dh], CX ; Az SI+BX+034dh cím által ;mutatott helyen lévő értékhez ; hozzáadja CX tartalmát. Néha nem deíthető ki, hogy az utasítás 8 vagy 16 bitre hivatkozik-e. Ilyenkor a C-hez hasonlóan cast-olni kell: MOV WORD PTR [4000H], 0 ; 4000h-ra és 4401h-ra is 0 tölt MOV BYTE PTR [4000h], 0 ; Csak 4000h-ra tötl 0-t Bázispointer : BP. Hasolnó az indexregiszterekhez, de a stack címzésre van specializálva ( lásd később ). Stackpointer : SP. Speciális regiszter A memüria egy adott területét címzi Ebbe a memóriába, mint egy verembe lehet értékeket tenni és onnan később kivenni. Mindig az utoljára berakott értéket lehet
csak kivenni. ( LIFO – Last In First Out ) Azt a memóriaterületet a regiszterek gyors elmentésére, eljáráshíváskor a hívó címének valamint az átadott paraméterek tárolására használják. A stackbe regisztert elmenteni a PUSH <regiszternév> kiolvasni a POP <regiszternév> utasítással lehet. A PUSH utasításnál SP értéke 2-vel csökken majd a megadott regiszter SP címére másolódik ( PUSH <reg> = DEC SP ;DEC SP ; MOV [SP], <REG> ), a POP utasítás pontosan fordítva működik : az SP által mutatott memória tartamát bemásolja a megadott 15 regiszterbe, majd SP-t 2-vel növeli így felszabadítja az előzőleg lefoglalt memóriát ( POP <reg> = MOV <reg>, [SP] ; INC SP ; INC SP ). Figyelem ! Látható, hogy a stack csak az értéket jegyzi meg, azt hogy milyen regiszter mentettünk el nem tartja nyilván. Így arra a programozónk kell vigyáznia, hogy visszaállításkor a helyes – fordított – sorrendben vegye
elő az adatokat. Egy PUSH AX ; PUSH DX után a POP DX ; POP AX utasításokkal lehet AX és DX tartalmát visszaállítani. Eljárást hívni a CALL <cím>, eljárásból visszatérni a RET utasítással lehetséges. A CALL <cím>hatására az SP értéke 2-vel csökken, az IP CALL utáni címe az SP által mutatott címre másolódik, majd IP – ben a cím kerül, így a végrehajtsá ott folytatódil. RET-nél fordítva . IP az [SP] értékével töltődik fel, majd SP 2-vel nő Így a végrehajtás a CALL utáni utasítások folytatódik tovább. Szegmensregiszterek : CS, DS, ES, SS. Mivel a 8086 regiszterei cak 16 bitesek ez mindössze 216 = 65536 byte = 64 KB megcímzését tenné lehetőveé, ezért a címtartomány kibővítésére úgynevezett szegmensregisztereket vezettek be. Így egy teljes memóriacímet a szegmensregiszterből és az úgynevezett offset 16 bites eltolásból lehet megkapni ( eddig ezt az offset-et hívtuk memóriacímnek ). A számítás
nagyon egyszerű A szegmensregiszter értékét 16-tal megszorozzuk és hozzáadjuk az eredményhez az offset értékét. Így egy 20 bites címet kapunk, amely már 1MB címzését teszi lehetővé. Ez az az átkos határ ami miatt még mindig csak 640 KB elérését teszi lehetővé a DOS. 4 Vegyük észre, hogy így egy memóriacímre több különböző módon is lehet hivatkozni. Például a 0000: 0413 = 0040: 0013. (A szegmens és az offset közé ”:”-ot szokás tenni Mivel a címmegadásnál szinte kizárólag 16-os számrendszert használunk a ”h”-t el szokás hagyni a számok mögül. ) Mivel egy szegmensregiszterrel 64 KB-os ablak nyílik a teljes 1Mb-os címtartományban ezt a szegmensregiszter által meghatározott 64 KB-os ablakot gyakran hívják szegmensnek. Alapértelmezésben az IP a CS:-t, BP, SP az SS: szegmensregisztert használja címképzésnél, az összes többi regiszter pedig a DS: szegmensregiszterrel alakítja ki a fizikai címet. AZ IP és SS
címzés kivételével ez felülbírálható a CS:, DS:, ES:, SS: prefixek használatával: MOV MOV MOV MOV [SI],AX ;DS:SI által meghatározott címre kerül AX ES:[SI],AX ;ES:SI által meghatározott címre kerül AX [BP],AX ;SS:BP által meghatározott címre kerül AX CS:[DI], AX;CS:DI által meghatározott címre kerül AX Státuszregiszter, flagek: FLAG. Az assembly programozás megértéséhez rendkívül fontos megérteni a flagek működését. A FLAG gyakorlatilag bitek (flagek) halmaza, amely bitek az utolsó elvégzett művelet eredményére vonatkozó információkat hordoznak. 4 A 80286-os processzornál a szegmensregiszterek mellé bevezettek még 4 bites úgynevezett lapleírókat, amikkel már 16 MB lett a címezhető memória ( 1 byte eléréséhez fel kell tölteni alapleírót, a szegmensregisztert és az offsettet is ). A 80286-os processzorban volt egy apró hiba, miszerint ha a szegmens*16+offset nagyobb volt, mint 1Mb, akkor a
túlcsordulást nem nyelte le, mint ahogy a 8086 teszi, hanem 1 MB feletti címet generált. Ennek köszönheti ma a HMA-ba töltődő DOS a létezését. A 16Mb-os határ és a kényelmetlen címzés miatt a 80386-os sorozattól kezdve a 32 bites regiszterekkel címezhető a memória , ami már 4 GB címzési tartományt jelent, amihez még egy 48 bit hosszú szegmensregiszter is tartozik így logikailag 4 TB memória címezhető. 16 Feltételes ugró utasítás csak a flagek állapotától függhet ( a JCXZ kivételével, ahol a feltétel a CX=0 ), így az ugrási feltétel csak közvetetten adható meg. A legtöbb aritmetikai utasítás hatással van a flagek állapotára. Így ezek segítségével kell a flageket úgy beállítani, hogy az ugró utasítás már a megadott feltétel szerint ugorjon. Például a SUB AX, BX utasítás eredménye csak akkor lesz 0, ha AX=BX volt a művelet előtt. Zérus eredmény esetén a ZF ( zero flag ) 1-be állítódik. Így a JZ feltételes
ugró utasítás csak akkor ugrik , ha a ZR=1, vagyis az előző művelet eredménye 0. Kivonásnál ez azt jelenti, hogy a kisebbítendő és a kivonandó egyenlő volt. Amennyiben BX>AX volt a SUB AX, BX előtt , akkor negatív eredménynek kellene keletkeznie. Ugyanúgy ahogy papíron való kivonásnál ilyenkor a legfelső helyi értékről egy túlcsordulást kell továbbvinnünk, amit a CF (carry flag) jelez. Erre a JC (jump if carry flag=1) feltételes ugró utasítással tudunk hivatkozni. A 80x86 processzorok a ZF és CF flagekből többféle feltételt is tudnak alakítani (az egyes csoportok belüli utasítások ugyanazt jelentik, ezért gépi kódjuk is azonos): CF = 1 JC JB JL JNAE JNGE ( jump if carry – ugrás ha túlcsordulás van ), (jump if bellow – ugrás ha kisebb ), ( jump if less – ugrás ha kisebb ), ( jump if not above or equal – ugrás, ha nem nagyobb vagy egyenlő ), (jump if not greater or equal – ugrás , ha nem nagyobb vagy egyenlő ), CF = 0
JNC JNB JNL JAE JGE ( jump if not carry – ugrás ha nincs túlcsordulás ), ( jump if not bellow – ugrás ha nem kisebb ), ( jump if not less - ugrás ha nem kisebb ), ( jump if above or equal – ugrás , ha nagyobb vagy egyenlő ), ( jump if greater or equal – ugrás , ha nagyobb vagy egyenlő ) JZ JE ( jump if zero – ugrás, ha zéró ), ( jump if equal – ugrás , ha egyenlő ) JNZ JNE ( jump if nin zero – ugrás , ha nem zéró ), ( jump if not equal – ugrás, ha nem egyenlő ) ZF = 1 ZF = 0 CF = 1 vagy ZF = 1 JBE JLE JNA JNG ( jump if bellow or equal – ugrás ha kisebb vagy egyenlő ), ( jump if less or equal – ugrás ha kisebb vagy egyenlő ), ( jump if not above – ugrás, ha nem nagyobb ), (jump if not greater – ugrás , ha nem nagyobb ), CF = 0 és ZF <> 0 JNBE ( jump if not bellow or equal – ugrás ha nem kisebb vagy egyenlő), JNLE ( jump if not less or equal – ugrás ha nem kisebb vagy egyenlő ), JA ( jump if above – ugrás, ha
nagyobb ), JG ( jump if greater – ugrás, ha nagyobb ) A többféle elnevezés könnyebbé teszi a feltételes ugró utasítások használatát. Mindig azt a nevet használhatjuk , ami a legjobban megfelel a környezetének. 17 A FLAG-nek a CF és ZF biteken kívül még jóval több állapotjelzője van, de ezek megértése bonyolultabb és számunkra most feleslegesek. 1.23 Utasításkészlet A legfontosabb utasítások már szinte mind előfordulnak az előző részben. Itt egy kicsit rendszerezettebben, csoportosítva vesszük sorra a különböző utasításokat. 1.231 Aritmetikai utasítások ADD dest, src függően állítódnak. Összeadás dest = dest + src A flagek a művelet eredményétől ADC dest. src Összeadás az átvitel beleszámításával dest = dest + src + CF A flagek a művelet eredményétől függően állítódnak. SUB dest. src Kivonás dest = dest – src A flagek a művelet eredményétől függően állítódnak. SBB dest, src Kivonás az
átvitel beleszámításával dest = dest – src –CF A flagek a művelet eredményétől függően állítódnak. CMP dest, src Összehasonlítás ( látszólagos kivonás ) dest – src eredményétől függően állítódnak a flagek. Az eredmény nem tárolódik sehol sem INC dest Növelés dest = dest + 1 A CF flag nem változik, eredményétől függően változik. a többi DEC dest Csökkentés dest = dest – 1 A CF flag nem változik, a többi a művelet Eredményének megfelelően állítódik be. MUL src Szorzás Ha src 8 bites operandus : AX = AL * src8 Ha src 16 bites operandus : DX:AX = AX * src 16 A flagek a művelet eredményének állítódnak. 18 a művelet megfelelően DIV src OR dest, src Osztás Ha src 8 bites operandus : AL = AX div src8, AH = AX mod src8 Ha src 16 bites operandus : AX = DX:AX div src16, DX = DX:AX mod src16 A flagek a művelet eredményének állítódnak. Bitenkénti logikai VAGY dest = dest OR src A flagek a művelet
állítódnak. XOR dest, src AND dest, src megfelelően eredményétől függően Bitenkénti logikai KIZÁRÓ VAGY dest = dest XOR src A flagek a művelet eredményétől állítódnak. függően Bitenkénti logikai ÉS Dest = dest AND src A flagek a művelet állítódnak. függően eredményétől TEST dest, src Bitenkénti logikai tesztelés ( látszólagos ÉS ) Dest AND src eredményétől függően állítódnak be a flagek. Az eredmény nem tárolódik sehol sem. NOT dest Bitenkénti negálás Dest = NOT dest A flagek a művelet állítódnak. NEG dest 2-es komplemensű negálás Dest = 0 – dest A flagek a művelet állítódnak. 1.232 Adatmozgató utasítások MOV dest, src Adatmozgatás dest = src A flagek állapota nem változik. XCHG dest, src Csere dest = src, src = dest A flagek állapota nem változik. 19 eredményétől függően eredményétől függően IN port8 vagy DX Olvasás I/o portról AL vagy AX = [port8 vagy DX] A flagek
állapota nem változik. OUT port8 vagy DX Írás I/o portra AL vagy AX = [port8 vagy DX] A flagek állapota nem változik. PUSH src Elmentés stackbe SP = SP – 2, SS:[SP] = src A flagek állapota nem változik. PUSHF A flagek elmentése stackbe SP = SP – 2, SS:[SP] = FLAG A flagek állapota nem változik. POP Visszaolvasás stackből SP = SP – 2, SS:[SP] = FLAG A flagek állapota nem változik. dest A flagek visszaolvasása stackből FLAG = SS: [SP], SP = SP + 2, A flagek állapota a művelet eredménye. POPF REP MOVSB utasítások ) határozza meg, REP MOVSW Blokk másolása DS:SI címtől kezdődően CX byte másolása ES:DI címre. A direction flag ( lásd CLD, STD hogy SI, DI-től pozitív vagy negatív irányba. A flagek állapota nem változik. Blokk másolása DS: SI címtől kezdődően CX word ( két byte ) másolása ES: DI címre. A direction flag ( lásd CLD,STD utasítások ) határozza meg, hogy SI, DI-től pozitív vagy negatív irányba. A
flagek állapota nem változik. 1.233 Vezérlésátadó utasítások JMP target Feltétel nélküli ugrás target-ra IP = target A flagek állapota nem változik. JCXZ target Ha CX = 0 ugrás target-ra If CX=0 then IP = target A flagek állapota nem változik. Jccc target Fektételes ugrás If ccc then IP = target A flagek állapota nem változik. 20 LOOP target Ciklusszervezés CX = CX – 1, if CX <> 0 then IP = target A flagek állapota nem változik. CALL target Eljáráshívás SP = SP – 2, SS:[SP] = IP, IP = target A flagek állapota nem változik. RET disp Visszatérés eljárásból IP = SS:[SP], SP = SP + 2 +disp A flagek állapota nem változik INT intno Szoftver interrupt generálás SP = SP – 2, SS:[SP] = FLAG, SP = SP – 2, SS:[SP] =CS, SP = SP – 2, SS:[SP] =IP, CS:IP = 0000:[intno * 4] A flagek állapota nem változik IRET Visszatérés interruptól IP = SS:[SP], SP = SP + 2 CS = SS:[SP], SP = SP + 2 FLAG= SS:[SP], SP = SP + 2 A flagek
állapota a művelet eredménye. 1.234 Processzorvezérlő utasítások HLT CMC STC CLC STD CLD STI A processzor megállítása Csak hardver interrup indíthatja tovább a processzort. Ha az interruptok elfogadása tiltott a processzor „ meghal”. CF invertálása CF = NOT CF A többi flag változatlan. CF beállítása CF = 1 A többi flag változatlan CF törlése Cf = 0 A többi flag változatlan Direction flag beállítása DF = 1, a REP utasítások csökkenő irányban hajtódnak végre. A többi flag változatlan Direction flag törlése DF = 0, a REP utasítások növekvő irányban hajtódnak végre. A többi flag változatlan Interruptok elfogadásának engedélezése 21 IF = 1 A többi flag változatlan Interruptok elfogadásának tiltása IF = 0 A többi flag változatlan CLI 1.24 Címkék, változók definiálása Az ugró utasítások operandusának azt kell megadni, hogy az ugrás hova történik. Gépi kódban ez a valóságban egy relatív eltolásra
fog lefordulni, vagyis valamennyit előre illetve valamennyit visszafelé ugorhatunk. Az assembly nyelvben az egyszerűség kedvéért azonban a cél címét kell megadni és a fordító program ( assembler ) fogja kiszámolni, hogy az valójában mekkora ugrást jelent. Az assembly programban nem címekre, hanem címkékre kell hivatkozni. Egy címkét az utána tett : azonosít Például: MOV CX,100 ;CX-be 100-at tölt DEC JNZ CX CÍMKE ;Csökkenti CX tartalmát ;Ha CX még nem 0, ; akkor visszaugrik a ; CÍMKÉ-re . CÍMKE Eljárást definiálni pedig a PROC – ENDP párossal lehet. Figyelem az ENDP nem jelenti az eljárás végét , a RET utasítás kiadásáról a programozónak kell gondoskodnia. PELDA PELDA PROC MOV MOV MOV MOV RET ENDP AX,0 BX,0 CX,0 dx,0 ;Az általános regiszterek ;feltöltése 0-val ;Visszatérés az eljárásból ;Az ENDP előtt is meg ;kell ismételni az eljárás ; nevét. A PELDA eljárást a következő módon lehet meghívni: CALL PELDA
;Feltölti az ált reg-el 0-val ;Itt folytatódik az eljárás ; után a végrehajtás. Az assembly nyelvben nincsenek valódi változók. Mindössze egy memóriaterületet foglalhatunk le és mondhatjuk az , hogy mi most ezt egy változóként fogjuk használni. Ezután erre a változóra már csak a címe szerint kell hivatkoznunk. A címet pedig helyettesíthetjük egy címkével. Így végül is nevén lehet nevezni a változókat is EGYBYTE DB 0 KETBYTE DW ? ;Egy byte nagyságú változó ,definiálása. ;Alapértelmezett értéke 0 ;lesz ;Két byte nagyságú változó ;Nincs alapértelmezett értéke 22 NEGYBYTE DD SOKBYTE DB SZOVEG DB ? ; Négy byte nagyságú ; terület foglalása. 10 DUP (?) ; 10 byte-nyi hely foglalása byte; ok tömbje „ Ez egy szöveg definiálása”,0 Hivatkozni a változókra a nevük leírásával lehet. Címüket az OFFSET előszóval lehet megtudni. MOV AH,EGYBYTE MOV CX,KETBYTE MOV SI.offset NEGYBYTE INC KETBYTE MOV
SOKBYTE + 3, DL ;EGYBYTE tartalmát tölti ;AH-ba. ;KETBYTE tartalmát tölti ;CX-be. ;NEGYBYTE címét tölti ;SI-be. ;KETBYTE tartalmát , növeli. ;SOKBYTE 4. Elemét ;feltölti DL tartalmával. 23 1.3 Kapcsolat a perifériákkal A gépi kódok hatása alapvetően a processzor belső állapotára és a memória tartalmára korlátozódik. A perifériákkal a memória egyes kijelölt területein 8például a képernyőmemóriában) valamint a portokon keresztül kommunikálhat a processzor Az egyes perifériák kezelése jelentősen eltér egymásról, programozásuk nem egyszerű feladat. Ezért a perifériákat kezelő főbb funkciókat az operációs rendszer valamint a gépbe égetett BIOS (Basic Input Output System) tartalmazza. Így a perifériákat a programozó a DOS és a BIOS megfelelő funkcióinak meghívásával érheti el .A DOS-t és a BIOS-t meghívni az úgynevezett szoftver megszakítások generálásával lehet : a regiszterek megfelelő feltöltése után
(általában AH tartalmazza a meghívandó funkció kódját, a többi regiszter a paramétereket) egy INT 21h utasítás kiadásával lehet a DOS-nak valamint egy INT 13h-mal lehet a BIOS-nak átadni a vezérlést. (Legalább is mi most csak ezt a két belépési pontot fogjuk használni) 1.31 Fontosabb DOS funkciók AH = 09h DS:DX String kiíratása A kiírandó string kezdőcímet tartalmazza. A string végét $ jelzi AH = 3Dh DS:DX File megnyitása A megnyitandó file neve 0-val lezárva. Visszatérési értéke: AXban egy handle, amivel ezek után a megnyitott file-ra hivatkozhatunk. A file pointer megnyitása után a file elejére mutat. Ha hiba volt, akkor CF = 1 és AX tartalmazza a hibakódot ( a hibakódok felsorolására itt nem térek ki ). AH = 3Eh BX File lezárása File handle : a file megnyitásakor kapott hanle. Lezárja a file-t. a handle érvényét veszíti Hibánál CF = 1, AX tartalmazza a hibakódot. AH = 3 Fh BX DS:DX CX AL Olvasás file-ból File handle
Milyen memóriapozícióra olvasson. Hány byte-ot olvasson. Megnyitás módja ( 0 = csak olvasásra, 1 = csak írásra , 2 = olvasás / írásra ).A file pointer aktuális értéke szerint meghatározott pozíciótól olvas CX byte-ot a DS:DX memóriapozícióra. A művelet után a file pointer értéke az olvasott byte-ok számával megnő (szekvenciális olvasás). Hiba esetén CF=1, AX tartalmazza a hibakódot Ha nincs hiba AX a beolvasott byte-ok számát adja vissza. Ha a file rövidebb, mint amennyit olvasni akarunk belőle, akkor ez kevesebb, mint a CX. AH = 40h BX DS:DX CX Írás file-ba File handle Milyen memóriapozícióról írjon ki. Hány byte-ot írjon ki. A DS:DX memóriapozíciótól kezdve CX byte-ot kiír a file-ba a file pointer aktuális értéke szrint meghatározott pozíciótól. A művelet után a file pointer értéke a kiírt byte-ok számával megnő (szekvenciális írás). Ha hiba van, akkor CF = 1 és AX 24 tartalmazza a hibakódot. Ha nincs hiba
AX a kiírt byte-ok számát tartalmazza. AH = 42h BX CX:DX AL File pointer mozgatása File handle file pointer mozgatásának mértéke. Ez szabja meg, hogy a file pointert honnan mozgassuk: AL=0 : A file elejétől AL=1 : A jelenlegi helyétől AL=2 . A file végétől Hiba esetén CF=1, AX-ben a hibakód. AH = 4Bh AL = 0 DS:DX ES:BX Program futtatása Ha AL=3, akkor csak betölti a fiel-t, nem futtatja. A futtatandó file neve 0-val lezárva. Futtatási paraméterek listájára mutat (environment, command line opciók, stb.) Hiba esetén CF=1, AX tartalmazza a hibakódot AH = 4Ch AL Kilépés a programból Kilépési hibakód. Az ERRORLEVEL DOS változó tartalmazza majd az értékét. Hibátlan programlefutás esetén értéke 0 szokott lenni Példa egy file megnyitására és olvasására: MOV MOV MOV INT JC MOV MOV MOV MOV INT JC MOV INT JC MOV MOV INT HIBA: MOV MOV INT AH,3Dh ;File megnyitása DX, offset FILENEV ; Feltételezzük ,hogy DS jó AL, 0 ;Csak olvasunk 21h ;DOS
meghívása HIBA ;Ha CF=1, akkor hiba AX-ben a hibakód BX,AX ;Ezután már BX tartalmazza a ;handle-t DX,offset BUFFER ;A BUFFER-be olvasunk CX,2000 ;Most csak maximum 2000 ;byte-ot olvasunk AH,3Fh ;Olvasás file-ból kódja 21h ;DOS meghívása HIBA ;Ha hiba volt olvasás közben AH, 3 Eh ;File lezárása ( BX-ben még mindig megvan ;handle 21h ;DOS meghívása HIBA AH, 4Ch ;Kilépés a programból AL,0 ;Nem volt hiba jelzés 21h ;A vezérlés visszaadása a DOS-nak (Ez már nem ;tér vissza). ;Hiba esetén ide ugrik AH,4Dh ;Kilépés a programból AL, 3 ;Hibajelzés (ERRORLEVEL 3 lesz) 21h ;A vezérlés visszaadása a ;DOS-nak (Ez már nem tér vissza). 25 FILENEV BUFFER DB DB „C:AUTOEXEC.BAT”,0 2000 DUP (?) ;A file neve, amit olvasni fogunk ;2000byte-nyi hely lefoglalása ;Ide fogjuk beolvasni a file ;tartalmát. 1.32 Fontosabb lemezkezelő BIOS funkciók AH = 00h DL Drive inicializálása Drive kódja ( 0 = A: floppy, 1 = B: floppy, 80h = C: hard disk, 81 h = D:hard
disk ). Első disk kezelés előtt , vagy hiba után szokás kiadni AH = 02h Szektorok olvasása DL Drive kódja ( u.a mint fent ) DH Fej száma ( 0-tól számozva ). CH és CL felső két bitje: Sáv száma ( 0-tól számolva ). CL alsó 6 bitje Szektor száma a sávon ( 1-től számolva ). AL Olvasandó szektorok száma. ES:BX Hova olvasson. Hiba esetén CF = 1, AH-ban a hibakód. AH = 03h Szektorok írása DL Drive kódja ( u.a mint fent ) DH Fej száma ( 0-tól számozva ). CH és Cl felső két bitje : Sáv száma ( 0-tól számolva ). CL alsó 6 bitje Szektor száma a sávon ( 1-től számolva ). AL kiírandó szektorok száma. ES:BX Honnan írjon. Hiba esetén CF = 1, AH-ban a hibakód. Példa a hard diszk MBOOT-jának beolvasására: MOV MOV MOV MOV MOV MOV MOV INT JC AH,02h DL,80h DH,0 CH,0 CL,1 AL,1 BX,offset BUFFER 13h HIBA ;Szektor olvasása ;C:drive ;0-ik fej ;0-ik sáv ;Első szektor Csak egy szektort olvasunk ;Feltételezzük , hogy ES jó ; BIOS meghívása ;Hiba
esetén ugrás MOV MOV INT AH,4Ch AL,0 21h ; Kilépés a programból ; Nem volt hiba ; Visszatérés a DOS-hoz MOV MOV INT AH,4Dh AL,3 21h ; Kilépés a programból ;Hiba volt HIBA: BUFFER DB 512 DUP (?) ;512 byte egy szektor 26 1.4 Az interrupt lánc A rezidens programok és így a vírusok megértéséhez is fontos a PC-k interrupt láncainak megértése. A 80 x 86 processzorok 256 hardver illetve szoftver megszakítás különálló kezelését teszik lehetővé. Ezeket IT 00, IT 01, IT FF-nek nevezik Minden egyes IT-hez tartozik egy kezelő eljárás. Hardver megszakítás esetén, ha a megszakítások elfogadása engedélyezve van , akkor a processzor megszakítja a működését, a stackre elmenti a FLAGet, a CS és IP regisztereket, majd a megfelelő IT kiszolgáló rutinjára ugrik. Például egy billentyű leütése IRQ1 megszakítást generál, amit a processzoron kívül lévő interrupt kezelő IT 09-é fordít le ( 8-cal eltolja a számát ). Ekkor a
processzor megszakítja a működését és meghívja az IT 09-hez tartozó kiszolgáló rutint, ami lekérdezi a billentyűzettől a lenyomott gomb kódját és eltárolja azt a billentyűzet pufferbe. Miután végzett visszaállítja a regiszterek eredeti tartalmát és visszatér arra a pontra, ahol a megszakítás érkezett. Így a processzor folytathatja működését úgy mintha nem történt volna semmi sem. Szoftver megszakítást az INT utasítással lehet generálni. Ekkor is elmentődnek a stackre a FLAG, a CS és az IP regiszterek, majd a megfelelő IT kiszolgáló rutinjára kerül a vezérlés. Ez gyakorlatilag egy eljáráshívásnak fogható fel. A hívó program ekkor a reigszterekben átadhat paramétereket, amiknek megfelelő utasítást az IT kiszolgáló rutinja – ami általában a DOS vagy a BIOS – végrehajtja. Általában az AH regiszterben valamiféle funkciókód, ALben alfunkció kód szokott lenni Az egyes IT-khez tartozó kiszolgáló rutinok címét a
processzor a memória legelejéről 0000:0000-tól tárolja. Minden kiszolgáló rutinhoz tartozik egy szegmens : offset cím Ezt hívják interrupt vektornak. Ennek mérete 4 byte IT-nkét Így a 0000 : ( IT száma * 4 ) pozíciótól helyezkedik el a kiszolgáló rutin címe. Az Intel processzorok sajátossága, hogy fordított sorrendben tárolnak mindent. Az első byte az offset alacsonyabb helyi értékű byte-ja, a második a magasabb, a harmadik a szegmens alacsonyabb a negyedik a szegmens magasabb helyi értékű byte-ja. Például a DOS belépési pontjául szolgáló IT 21h kiszolgáló rutinjának címe : ( 21 h * 4 = 84 h ) 0000:0084. Ezen a memóriaterületen található byte-ok sorban: 9E 10 16 01.Így a kiszolgáló rutin címe : 0116:109E Egy rezidens program és így egy vírus is úgy tudja megőrizni aktivitását – úgy tudja elérni, hogy néha hozzá kerüljön a vezérlés – hogy egy-egy fontosabb interrupt vektort magára irányít. Például a DOS-t
meghívó IT 21 megülésével minden DOS funkció híváskor a vírus kódjára kerül a vezérlés. Annak érdekében, hogy a hívó által kért funkciót végre lehessen hajtani, a vírusnak meg kell őriznie az interrupt vektor által mutatott eredeti címet. Így oda át tudja adni a funkció kiszolgálását. A kiszolgálás előtt vagy után azt csinálhat, amit akar, fertőzhet, rombolhat, stb. Több olyan rezidens program is elképzelhető , ami magára irányítja az adott interrupt vektort, valamilyen funkciót megvalósít egyéb esetekben pedig hívja az eredeti kiszolgáló rutint, így ezek mintegy láncra fűződnek. Ezért szokás ezt interrupt láncnak hívni. Például a file-futtatáskor fertőző vírusok a következőképpen működnek: magukra irányítják az IT 21-et, a kiszolgáló rutinban ellenőrzik, hogy AH egyenlő-e 4Bh-val ( file futtatása DOS funkció ). Ha nem akkor hívják az eredeti DOS kiszolgáló rutint, ha igen , akkor DS:DX mutat a
futtatandó file nevére, így a vírus azt meg tudja fertőzni. Fertőzés után pedig ugyanúgy meghívhatja a vírus a DOS eredeti kiszolgáló rutinját, futtatva ezzel a már megfertőzött file-t. Azért ez a legegyszerűbb módja a vírusfertőzésnek, mert nem kell „gondolkodnia„ a vírusnak azon, hogy melyik file-t érdemes megfertőzni – tálcán kapja a nevét. Amit egyszer futtatnak, azt máskor is futtatják, így célszerű azt megfertőzni 27 IT21 PROC CMP JNZ CALL FAR AH,4Bh EREDETI IT21 FERTOZES EREDETI IT21: JMP DWORD PTR CS:ERE IT21:CIME ERE IT21 CIME DD IT21 ENDP ;Ide mutat most a 21-es interrupt vektor ;Ellenőrzi, hogy 4Bh-e a ;funkciókód ;A file megfertőzése ;DS:DX mutat a ;futtatandó – most már ;fertőzendő file nevére. ;Az eredeti kiszolgáló ;rutin hívása ? ;Itt tároljuk a 21-es ;interrupt vektor eredeti ; értékét ;A kiszolgáló rutin vége Programok futtatása A vírusok fertőzési mechanizmusainak megértéséhez,
már csak annak megismerése maradt vissza, hogy a DOS milyen módon futtat gépi kódú programokat. Ugyanis egy elég komoly problémát okoz az, hogy a gépi kódban több helyen szerepelnek címek. A programokat viszont nem egy adott előre ismert helyen kell futtatni, hanem a hely függ a memória mindenkori állapotától. Ezért a gépi kódú programot kell áthelyezni, relokálni az adott memóriapozíciótól kezdődően: ,minden olyan helyen , ahol hivatkozás van címkiigazítást kell végrehajtani. Itt játszik rendkívül nagy szerepet a PC-k szegmens:offset címzési technikája Ugyanis a szegmensek 16-os szorzója lehetővé teszi, hogy bármely 16-tal osztható memóriapozíción kezdődjön egy szegmens, ami nem túl nagy pazarlással bármikor megtehető. A szegmensen belül így már nem kell címkorrekciót végrehajtani, mindössze a szegmensregiszter beállításánál illetve a szegmensek közötti ugró utasításoknál van rá szükség. A legegyszerűbb
esetben , amikor egy program 64 KB-nál kisebb, akkor elfér egyetlen szegmensben is. Ekkor a kódot egyáltalán nem kell relokálni Az indítás előtt a DOS feltölti a szegmensregisztereket a 28 2. Számítógépes vírusok 2.1 Mi is az a számítógépes vírus? "A számítógépes vírus intelligencia, erkölcs és értelem nélkül." Intelligens, mert létrehozásához mély számítástechnikai ismeret szükséges. Erkölcstelen, mert alattomosan kihasználja a számítógépek sebezhetőségét. Értelmetlen, mert egy vírus terjedése, pusztítása mindössze öncélú erőfitogtatás. A számítógépes vírus valójában egy olyan program ( vagy programrészlet ), amely képes arra, hogy reprodukálja önmagát vagyis önmagát másolva szaporodjon. Nem minden számítógépes vírus okoz károkat, némelyikük csak észrevétlenül terjed. Minden számítógépes rendszerben, bármely operációs rendszer alatt, ahol lehetőség van arra, hogy egy program
egy másik programot módosítson, azaz lehetőség van az adatok és a futtatható kód konvertációjára, létrehozható vírus. A világon talán a legjobban elterjedt DOS operációs rendszer pedig messzemenőleg alkalmas vírusok létrehozására és azok terjedésére. Mit is jelent valójában az, hogy a futtatható kód és az adatok között áttranszformálhatóság van ? A legegyszerűbb példa erre egy egyszerű .COM kiterjesztésű futtatható file ( legyen ez a FORMAT.COM ) adattá alakítása A DOS alapkoncepciója szerint minden file - néhány osztálytól eltekintve - egyforma. Ennek megfelelően ezt az amúgy futtatás céljából létező filet akár ki is nyomtathatjuk a PRINT FORMAT.COM paranccsal. Ekkor a PRINT parancs, a FORMATCOM file-t, mint adatfile-t fogja kinyomtatni. Természetesen az eredmény olvashatatlan lesz Ám ez a fajta kód adat átalakítás még nem adna lehetőséget vírusok írására, de sajnos a DOS ennek a transzformációnak az
ellenkezőjét is lehetővé teszi. Lehetőség van akár egy szövegfile ( példaképpen legyen ez a README.TXT ) futtatására is Ez a következő parancsokkal érhető el : RENAME README.TXT READMECOM README Ekkor a számítógép megpróbálja a most már README.COM-má átnevezett file-t futtatni Mivel egy szövegfile-nak a kódjai teljesen értelmetlenek a számítógép számára, többnyire értelmezhetetlen lesz a program és úgynevezett rendszerösszeomlás következik be. Ám elképzelhető, hogy az ilyen átalakítással nem értelmetlen kódot, hanem nagyon is értelmes, futtatható programot kapunk. A vírusoknak mégsincs ilyen egyszerű dolguk, ugyanis attól, hogy saját magukat először adattá konvertálják, majd ezt az adatot lemásolják, és ismét futtatható kóddá visszaváltoztatják, még csak az biztosított, hogy az operációs rendszer futtatni tudja ezt a duplikálódott kódot, az még egyáltalán nincs biztosítva, hogy valamikor végre is hajtja
azt. 29 2.2 Hogyan keletkeznek a vírusok? Felvetődik a kérdés, vajon kik azok az emberek, akik vírusokat készítenek, miért akarnak rombolni, milyen céljaik vannak. A köztudatban az a tévhit él, hogy a vírusokat nagy tudású ( majdhogynem zseni ) szakemberek írják, romboló céljaik mögött pedig bűnöző, esetleg terrorista szervezetek állnak. A valóság azonban egy kicsit más Az első vírusok létrehozása még valóban ötletes szakemberekhez köthető, azonban ma már nem kell olyan nagy szaktudás egy vírus létrehozásához, illetve ez a tudás már sokkal könnyebben megszerezhető, mint régebben. Manapság bárki hozzájuthat - ha akar, ha nem - egy-két vírushoz, és működésük megértéséhez is minden információ rendelkezésére áll ( ma már nem misztikum a számítógépek programozása, rengeteg oktatókönyv kapható bármelyik könyvesboltban ). Ha pedig már adott egy példa, sokkal könnyebb újabb vírusokat készíteni. Ezt
bizonyítja az is, hogy egy eredeti vírusnak több változata, úgynevezett mutánsa létezik. Ezek a mutánsok nem ultraibolya sugárzásra jönnek létre, mint a valódiak, hanem úgy keletkeznek, hogy egy vírust valaki visszafejt ( visszafordít érthető formába ), majd kis változtatásokkal a saját elképzeléseihez illeszti és újra lefordítja a számítógép számára érthető gépi kódra. Néhány ismertebb vírusnak több ilyen leszármazottja is létezik ( például a Péntek 13-nak van szombat 14-én és csütörtök 12-én aktivizálódó változata is ). Előfordul, hogy más vírusokból vett ötletekkel, megoldásokkal bővítenek tovább egy vírust, illetve egy mutánst fejlesztenek tovább. Így egy-egy mutánsnak egész nagy leszármazási fája is lehet A mutánsok keletkezésétől megkülönböztetjük azt, amikor a vírust készítő saját maga fejleszti tovább vírusát. Általában ilyenkor maga a szerző ad valamilyen verziószámot az egyes
változatoknak. Ezek a fejlesztés során létrejövő különböző változatok úgynevezett víruscsaládot alkotnak. Általában az egy víruscsaládba tartozó egyedek támogatják egymást Gyakori, hogy az újabb verziók a korábbi testvérüket képesek kiirtani, helyüket átvenni, így biztosítva a fejlettebb változatok elterjedését. Visszatérve az eredeti kérdésre, megállapíthatjuk, hogy a gyakorlatban a vírusok többségét nem nagy tudású szakemberek, hanem programozni tanuló diákok, egyetemisták készítik. Ők azok, akik akár otthonukban programozva rengeteg szabadidővel, ötlettel rendelkeznek. A vírusokon keresztűl mutathatják meg a nagyvilágnak, hogy már mire képesek. Általában a legtöbbjük terméke nem tartalmaz romboló funkciókat, kártevésük legtöbbször abból adódik, hogy a készítő még csak most tanult meg programozni és így a vírus tele van hibákkal. Szaporodásuk közben ezek a hibás vírusok jóval több gondot
okozhatnak, mint akár egy romboló egyed. ( Egy hibás fertőzést jóval nehezebb és legtöbbször már lehetetlen is visszaállítani. ) Természetesen azért időnként egészen profi, új elveken szaporodó vírusokkal is találkozhatunk. Sajnálatos tény, hogy már megszülettek az első vírusfejlesztő programcsomagok is. Segítségükkel már gyerekjáték vírusokat készíteni. A fejlesztő programnak egyszerűen szabhatjuk meg, hogy a készülő vírusban milyen funkciók legyenek, mikor aktivizálódjon, milyen károkat okozzon. A tervezés után a vírusfejlesztő programcsomag elkészíti a kívánt vírust, akár több változatban is. Az ilyen programok megjelenése és a számítógépek egyre szélesebb körű terjedése miatt sajnos nem várható, hogy a vírusok számának rohamos növekedése a jövőben jelentősen lelassul. 2.3 A vírusok fejlődése Az első vírus, mint ötlet megjelenése óta a vírusprogramozók egyre újabb és újabb módszereket
találták ki arra, hogy hogyan lehet a DOS-t és a folyamatosan fejlődő vírusvédelemeket kicselezni, minél jobban elbujtatni a vírusprogramot. A hagyományos vírusok ellen ugyanis már olyan vírusvédelemi szoftvereket fejlesztettek ki, melyeknél ezek a vírusok még lappangási idejükben lebuktak, nem tudták alkotójuk szándékát megvalósítani. 30 Ezért a vírusok szerzői újabb és újabb vírusterjedési, vírusálcázási technikákat dolgoztak ki. Ezt a fejlődést (jelenleg) négy szakaszra - úgynevezett generációkra - oszthatjuk. A vírusok generációkba osztása teljesen önkényes, az egyes szakaszok sem időben sem felépítésben nem különülnek el élesen egymástól. A vírusokat sem lehet egyértelműen besorolni egy-egy generációba. A felvázolt generációkba sorolás csupán a leglényegesebb vírusterjedési és álcázási elveket és azok fejlődését tükrözi. 2.31 Első generációs vírusok Az első generációs vírusok
tulajdonképpen nem tartalmaznak különösebb álcázási technikákat, egyszerűen csak fertőznek. Általában az eredeti hordozó programot sem teszik tönkre, az visszaállítható marad. Előfordulhat, hogy a vírus rezidensen a memóriába kerül, de az is, hogy a szaporodását a memóriában maradás nélkül biztosítja. Az előbbi esetben a vírus lényegesen gyorsabban képes terjedni, de könnyebben felfedezhető. Ezzel szemben az utóbbi esetben a vírus ugyan lassabban terjed, de a felfedezése körülményesebb. 2.32 Második generációs vírusok A következő generációba tartozó vírusokat két fő osztályba sorolhatjuk be. Egyik osztályukra az jellemző, hogy a lappangási idejük alatt nehezen felfedezhetőek, mivel ezen vírusok már lopakodó (stealth), illetve polimorf jellegűek. A második generációs vírusok harmadik lényeges osztálya a felülíró (overwrite), gyorsan pusztító vírusok. Ezek már a hordozó programot is tönkreteszik, így
jelenlétük azonnal felfedezhető, de ekkor már többnyire a vírus az állományok nagy részét megfertőzte, azaz tönkre is tette. • • • Lopakodó vírusok: Jellemzőjük, hogy megpróbálnak úgy terjedni, hogy a felhasználó csak nagyon nehezen vehesse észre a vírus jelenlétét. Ennek érdekében például a file végéhez fűződő vírusok esetén, ha azok már bekerültek a memóriába akkor a file-ok eredeti hosszát mutatják, néha még a file eredeti tartalmát is szimulálják. A lopakodó boot-vírusok pedig az eredeti boot-szektort mutatják meg, elfedve vele jelenlétüket. Polimorf vírusok: Ezek a vírusok nem úgy próbálnak elbújni, hogy szimulálják a gép fertőzésmentes állapotát, hanem önmagukat titkosítják, változtatják megnehezítve ezzel felismerésüket. Felülíró, gyorsan pusztító vírusok: A felülíró vírusok általában azzal okoznak adatveszteséget, hogy a fertőzés előtti állapotot nem tárolják el, hanem egy az
egyben felülírják a megfertőzendő programterületet. Ebbe a kategóriába tartoznak a legrövidebb vírusok, hiszen nekik nem kell az eredeti állapotot szimulálniuk. 2.33 Harmadik generációs vírusok A vírusok újabb generációja azt használja ki, hogy a DOS-nak a fentebb említett módokon túl még elég sok kiskapuja van a vírusok számára. Így egy-egy új vírus úgy tud a legkönnyebben megélni, ha olyan módszerrel szaporodik, amit az eddigi vírusvédelmek nem ismernek. • CEB vírusok: A harmadik generációs vírusokra legszemléletesebb példa a CEB (companion) vírusok megjelenése volt. A CEB vírusok működése azon az elven alapszik, hogy a DOS a futtatható állományokat prioritási sorrendben kezeli. Amennyiben a felhasználó egy program indításánál a kiterjesztést nem adja meg, úgy a DOS a prioritási sorrendben az első létező programot indítja. Ez a prioritási sorrend a kiterjesztések alapján: .COM, EXE, BAT Így egy CEB vírusnak
semmi mást nem kell tennie, mint keresni egy .EXE vagy BAT kiterjesztésű file-t és ugyanolyan néven, 31 • • de .COM (vagy EXE) kiterjesztéssel lemásolnia magát Ha ezek után az újonnan létrehozott .COM állományt Hidden (rejtett) jelzővel látja el, akkor az a DIR parancsra nem jelenik meg, vagyis a vírus láthatatlan ! Mivel a vírus terjedése egy egyszerű COPY parancsnak fogható fel, nem keltette fel a vírusvédelmek gyanúját sem. Device vírusok: A device vírusok a device driver-ek működésébe avatkoznak be, a DOS legalsó szintjén dolgoznak, így a vírusvédelmek többsége alá kerülnek. Mivel az operációs rendszer magjába ágyazzák be magukat szinte tökéletesen tudnak lopakodni. ANSI bombák: Ezek a vírusok azt használják ki, hogy a DOS által a képernyőre kiírt szöveg tartalmazhat olyan vezérlő kódokat amelyek a billentyűzetet átdefiniálhatják, így egy egyszerű TYPE parancs kiadása után egy billentyűlenyomásra
elindulhat egy, akár vírusos program is. Ez a módszer csak az ANSISYS használatánál lehetséges 2.34 Negyedik generációs vírusok Ma a makróvírusok okozzák a legtöbb fertőzést. Számuk ’98 februárjában már meghaladta a 2000-et. Ezek között már megjelentek olyanok is, amelyek képesek Word dokumentumból végrehajtható állományokat, valamint ezekből a végrehajtható állományokból Word dokumentumokat fertőzni. A makróvírusok fogalma nem új, 1989-ben Harold Highland volt az első aki megjósolta őket. Ekkor születtek az első tanulmányok a makróvírusok írásának lehetőségéről. Joel McNamara 1994-ben egy tanulmányt is írt ezekről a vírusokról, sőt ő maga is készített egy ilyen vírust DMV néven. Ezt azonban titokban tartotta az első igazi makróvírus a Concept megjelenéséig, ami a megfelelő eszközök és ismeretek hiányában gyorsan elterjedt. A Concept nagyarányú elterjedése már újabb és újabb makróvírusok
megjelenését eredményezte. A makróvírusok megírásához ugyanis nem szükséges a gép mélyebb ismerete A DOS vírusoktól eltérően a makróvírus nem a gép Assembly utasításkészletéből építkezik, hanem valamely makrónyelv lehetőségeit használja ki, mely a magasszintü programozási nyelvek eszközeivel teszi egyszerűbbé a vírusok készítését. Elterjedségének másik oka, hogy a Word alatt terjedő vírusok a WordBasic makrónyelv parancskészletét használják. Ily módon függetlenek magától az operációs rendszertől, akár különböző platformokon is terjedhetnek, feltéve ha azokon léteznek kompatibilis WordBasic értelmezők. Például a Word-nek van Macintosh gépeken futó változata is, így ezek a vírusok PC-ről Macintosh-ra, illetve visszafelé is terjedhetnek! A Concept megjelenése után a vírusirtó cégek is elkezdtek komolyan foglalkozni a makróvírusok kérdésével. A fokozott figyelem ellenére egy másik vírusnak a CAP-nak
sikerült ismét széles körben elterjedni. Ez a vírus elsőként valósította meg a teljesen nyelvfüggetlen terjedést. A vírus elterjedéséhez hozzájárulhatott az is, hogy makróit a fertőzött dokumentumban, külön segédeszköz nélkül nem lehet megnézni. Ezzel párhuzamosan a népszerűbb alkalmazásokra is megjelentek az első makróvírusok, illetve Trójai Falovak (Excel, AmiPro, Lotus, stb.) Ezek száma azonban az Excel vírusok kivételével azóta sem emelkedett. A következő lökést a “vírusvédelem”-mel ellátott WinWord 8 (WinWord/Office97) jelentette. Ebben a programban kétféle vírusvédelem is jelen van. Az egyik jelez minden olyan dokumentumnál ami makrót tartalmaz, a másik képes néhány Word6/7-es makróvírust a konvertálás során felismerni. Ezek a védelmek azonban nem érnek túl sokat, a programban megjelent új lehetőségekkel szemben. Ezek közül a legfontosabb, és a legtöbb bajt okozó a makrók konvertálása Az Office 97-ben
mindegyik alkalmazás “egységes” makrónyelvet kapott, azonban a WinWord a kompatibilitás megőrzése miatt képes a régebbi WordBasic-ben írt makrókat átkonvertálni az 32 új Visual Basic környezetbe. Mivel ez automatikusan történik a betöltött vírusból automatikusan keletkezik egy új variáns, ami már képes az új környezetben működni. 2.4 A vírusfertőzési módszerek Egyetlen olyan feltétel van, amely minden vírus esetében fenn kell, hogy álljon: valamilyen úton-módon el kell indulnia a vírusprogramnak. Ellenkező esetben ugyanis nem lenne működőképes. A vírusok futóképessége két fő módon biztosítható az IBM PC-k DOS operációs rendszerén. 2.41 Boot vírusok Ez a vírusfajta a bootolási folyamatban aktivizálódik, megfertőzve a boot-lemez partíciós tábláját (ha van) vagy a boot szektorát. Előfordulhat továbbá az is, hogy a vírus a bootolás során beolvasásra kerülő rejtett file-ok betöltésekor aktivizálódik. A
boot vírusok tehát módosíthatják: • a merevlemez partíciós táblájának programját, • a boot szektort, • valamelyik rejtett file-t, • valamelyik rejtett file könyvtárbejegyzését, • a DOS FAT-bejegyzéseit. A boot vírusok létéért valójában nem is a DOS, hanem a BIOS (Basic Input Output System) a felelős, ugyanis ez a gépekbe beégetett - a kezdeti memória és gép ellenőrzését, valamint a lemezegységek és a főbb ki-, és bemeneti egységek kezelését végző - rendszer futtatja őket. Ezek a vírusok azt használják ki, hogy a BIOS induláskor betölti és elindítja az operációs rendszert. A hívatlan betolakodó elhiteti a BIOS-sal, hogy ő az operációs rendszer, majd miután a BIOS-tól megkapta a vezérlést gyakorlatilag azt csinál amit akar. Lefutása után általában a vírus betölti az eredeti operációs rendszert, így leplezi jelenlétét. Az eredeti operációs rendszer betöltését a vírus két módon teheti meg: • A
felülírt szektorba kerülő víruskód elvégzi az eredeti szektor feladatát. • Az eredeti szektort a vírus elmenti, majd aktivizálódása után ezt a szektort tölti be és futtatja. Ebben az esetben az operációs rendszer meggátolhatja a vírus terjedését, hiszen elképzelhető, hogy egyes operációs rendszerekben az elmentett eredeti szektor megbénítja az operációs rendszer elindulását vagy működését. Más operációs rendszerek viszont az elmentett eredeti szektortól függetlenül képesek tevékenykedni. Ezt természetesen az elmentett szektor helye határozza meg. 2.42 File indításakor aktivizálódó vírusok A vírus valamilyen végrehajtható program futtatásakor aktivizálódik. Ekkor a vírus módosíthatja: • magát az eredeti programot, • a program könyvtárbejegyzését, • a DOS FAT-bejegyzéseit. Ezeket a vírusokat file-vírusoknak is szokás nevezni. Jellemzőjük, hogy a futtatható programokhoz kapcsolódnak oly módon, hogy az
eredeti file futtatásakor a program helyett a vírusprogram indul el. Elindulása után általában a vírus gondoskodik róla, hogy a hordozó 33 program is végrehajtódjon, így a fertőzésből általában semmi sem vehető észre. Léteznek azonban olyan romboló vírusok is, melyek a fertőzéssel egyidejűleg a hordozó programot tönkreteszik. Ekkor az eredeti program végrehajtására már nincs lehetőség A DOS operációs rendszer alatt a .COM file-ok fertőzése lehetséges oly módon, hogy a vírus az eredeti program után másolja magát, majd az eredeti program első néhány byte-ját módosítja úgy, hogy a vírusra kerüljön a vezérlés. A vírus a lefutása során gondoskodik róla, hogy visszaírja az első néhány byte-ot, majd a lefutása végén az első utasításra adja a vezérlést. A .COM file-ok fertőzésének egy másik lehetséges módja, hogy a vírus az eredeti program elé kerül. Ezzel már biztosította, hogy a fertőzött program
futtatásakor rá adódjon a vezérlés A vírus lefutása során már csak arról kell gondoskodnia, hogy a memóriában az eredeti program és a vírusprogram helyet cseréljen, a lefutás után pedig az eredeti program első utasítására adja a vezérlést. A két alapeset átmenete is előfordulhat. Ekkor a vírus az eredeti program közepébe kerül Ekkor a vírusnak módosítania kell az eredeti program első néhány byte-ját. Ezzel biztosítja, hogy a program futtatása esetén a vezérlés először a vírusprogramra kerüljön. Természetesen az eredeti program vírus utáni részét a vírusnak az eredeti helyére kell másolnia, mielőtt az eredeti programot futtatná. Az .EXE file-ok esetén a vírus a fertőzendő program bármely részére kerülhet (természetesen beszúrással). Azonban, ha a vírus nem a file végére kerül, a vírusnak módosítania kell az .EXE file header-ében lévő relokációs bejegyzéseket, a relokációról pedig lefutása során
gondoskodnia kell. Amennyiben viszont a vírus a file végére kerül, úgy betöltéskor a DOS helyesen végzi el az eredeti program relokálását. A fertőzés során a vírusnak az EXE header-t kell módosítania úgy, hogy végrehajtáskor először a vírusra adódjon a vezérlés, amely lefutása után indítja az eredeti programot. Előfordulhat az az eset is, hogy a vírus megváltoztatja a fertőzendő program típusát. A COM file-ból .EXE-t készíthet, a 64 Kbyte-nál kisebb EXE-kből pedig COM file-t Amennyiben EXE-ből készít COM-ot, úgy a vírusnak az eredeti program relokációját is el kell végezni. Sokkal egyszerűbb a DOS alatt a .BAT file-ok fertőzése A vírus ugyanis a szöveges batch file elejére szúrhat be sorokat, melyeknek a lefutása után automatikusan az eredeti sorokra kerül a vezérlés. A vírusfertőzés az eredeti program módosulása nélkül is létrejöhet. Egyes operációs rendszerek, mint például a DOS ugyanis az azonos nevű, de
különböző típusú és kiterjesztésű (COM, EXE, BAT) programok futtatását prioritási sorrendben végzi. A legnagyobb prioritással a COM, a legkisebbel a BAT bír. Így, egy vírus a fertőzendő program mellett létrehozhat egy nagyobb prioritású programfile-t (COM vagy EXE), amely csak a vírusprogramot tartalmazza. Így, ha a felhasználó az eredeti programot úgy szeretné futtatni, hogy az indításnál nem adja meg a futtatandó program kiterjesztését, akkor a DOS azonnal a vírust indítja el. A vírusnak ezután már csak arról kell gondoskodnia, hogy az alacsonyabb prioritású eredeti programot futtassa. Egy program indításakor a felhasználó megadja, hogy milyen programot szeretne elindítani. Ekkor az operációs rendszer az aktuális, illetve néhány előre definiált alkönyvtárban megkeresi az futtatandó program nevét. Az operációs rendszer a file könyvtárbejegyzéséből kiolvassa, hogy fizikailag hol található a file első blokkja a
háttértáron. További táblázat(ok) írja(k) le, hogy a következő blokkok hol találhatók. Egy vírus az operációs rendszerek filestrúktúrájába többféleképpen avatkozhat be: 34 Módosíthatja a fertőzendő file könyvtárbejegyzését úgy, hogy az operációs rendszer ne az eredeti file-tartalmat töltse be, hanem a víruskódot. • Természetesen módosíthatja a blokkok sorrendjét leíró táblázatot is. • A DOS operációs rendszerben a háttértár cluster-ekre van osztva. A file-okhoz tartozó könyvtárbejegyzések tartalmazzák a file-ok első cluster-ének helyét a háttértáron. A file következő cluster-ének helyét a FAT (File Allocation Table) tartalmazza. Így a DOS esetén a vírus módosíthatja a file könyvtárbejegyzését (nevét, vagy az első cluster mutatóját), illetve a FAT bejegyzéseket. A vírusfertőzések egy eddig még (szerencsére) nem elterjedt módja, hogy a vírus az eddigiektől eltérően nem közvetlenül a
végrehajtható programokba épül be, hanem olyan fileokba, amelyekből végrehajtható programok készíthetők (compiling, linking). A módszer a vírustól jóval bonyolultabb terjedési eljárást követel meg, hiszen ismernie kell a fertőzendő file felépítését, szintaktikáját. Ezen fertőzés végső megjelenési formája a futtatható file-okban nehezen azonosítható, hiszen a víruskód elhelyezkedése függ a fordító (compiler) és a szerkesztő (linker) opcióitól, valamint az egész (eredeti) program felépítésétől. Az opcióktól függően a víruskód is más és más lehet. Felmerül a kérdés, hogy hogyan valósítható meg, hogy egy futó vírusprogram a saját forráskódját állítsa elő. Erre nincs is szükség, ugyanis feltehetjük, hogy a fertőzéskor a forráskódú vírusprogram két példányban kerül a fertőzendő file-ba. Egyszer programként, és egyszer adatként A fordítás és szerkesztés hatására csak a programrészből
képződik kód, az adatterület változatlan marad. A fertőzéskor tehát az adatterületen rendelkezésre áll a teljes forráskódú vírusprogram, amit persze két példányban kell a fertőzendő forrás file-ba másolni. Amennyiben két fordító kompatibilis, úgy egy vírus fertőzheti mindkét fordítóhoz tartozó forrásfile-okat, legyenek azok különböző operációs rendszerek alatt, vagy különböző géptípusokon implementálva. Előfordulhat továbbá az is, hogy a vírus nem igényli a két fordító teljes kompatibilitását, csak ennek néhány részét. Például egy tetszőleges operációs rendszer alatti C fordítótól egy vírus a következőket várhatja el: • • • A program végrehajtása a main() függvénnyel történik. A forrásfile-ok a .C kiterjesztésű file-ok Egy file megnyitására, lezárására, olvasásra, írásra az fopen(), fclose(), fread(), fwrite() parancsok szolgálnak. Ezek általában mind szabványos követelmények,
amelyekkel minden C fordító rendelkezik. 2.5 Védekezés a vírusok ellen A vírusok elleni védekezési módszereket a vírus létezésének feltétele felől közelíthetjük meg. Tökéletes megoldás csak úgy születhet, ha az alkalmazott rendszerekben a futtatható kód és az adatok között semmiféle transzformációt sem engedünk meg. Ez sajnos egy teljesen új operációs rendszert, új rendszerszemléletet kíván meg. A Neumann-elvvel ellentétben, a futtatható számítógépes kódot és az adatokat teljesen elkülönítve kellene tárolni. Ez az út a közeljövőben - a jelenlegi operációs rendszerek nagy számú elterjedtsége miatt - még nem lesz járható. A másik lehetőség, hogy a DOS-t megtartva az adatok és a futtatható kódok közötti transzformációt megakadályozzuk vagy legalábbis felügyeljük. Sajnálatos módon a DOS felépítése miatt ezt a transzformációt tökéletesen megakadályozni lehetetlen. Ha általánosságban egy vírus
fertőzését megakadályozni nem lehet, akkor legalább azt biztosítani kell, hogy az esetleges fertőzés minél előbb felismerhető és eltávolítható legyen. Ehhez a rendszer veszélyeztetett területeiről mintát kell venni, s azt rendszeresen ellenőrizni 35 kell. Fertőzés észlelése esetén a behatolót vírusirtó program segítségével el lehet távolítani A víruskereső és eltávolító programok általában a legegyszerűbb módszert használják: a vírusokból vett rövid minták, vagy más néven szekvenciák keresését végzik. A szekvenciakeresési módszer azonban nem használható a polimorf vírusok, illetve a vírusok átiratai esetén. Ebben az esetben a szekvenciakeresési módszer mellett statisztikai, illetve heurisztikus algoritmusok használhatók. Léteznek azonban olyan destruktív jellegű vírusok is, amiknek fertőzése után az adathordozókat már nem lehet az eredeti állapotukba visszaállítani. Így ez a vírusirtásos módszer nem
nyújthat teljes biztonságot. A vírusfertőzés esélyeit a szoftveres vírusvédelmen túl állandóan betartott óvatossági rendszabályokkal is jelentősen lehet csökkenteni. A rendszerbe kerülő idegen lemezeket alaposan meg kell vizsgálni, forgalmukat nem árt korlátozni. Szoftvertelepítésnél különösen oda kell figyelni a vírusos fertőzés lehetőségeire. Az esetleges fertőzés hatásainak csökkentése érdekében célszerű legalább az újonnan keletkező adatokat rendszeresen menteni. Rendszeres mentéseknél bármiféle rendszerhiba, vírusfertőzés sem okozhat majd katasztrofális károkat, míg rendszeres mentés nélkül a keletkező károk nagyon nagyok is lehetnek. 36