10 éves a vírus
Az előző számban megjelent, nosztalgikus, múltidéző történeti cikk folytatásaként álljon itt egy programozói szemmel nézve szakmaibb, amolyan kedvcsináló írás arról, hogy vajon hogyan is működnek a vírusok. A teljesség igénye nélkül, a file-vírusok példáján keresztül próbálok meg tippeket adni azoknak, akik késztetést éreznek arra, hogy egy-egy netről letöltött konzervvírust, avagy szőke titkárnő gépének rendberakása közben talált szerzeményt alaposabban megvizsgáljanak.
Amennyiben emulátorban dolgozunk (pl. VMware, vagy Linux dosemu), és partíció elérés nélkül, diskimage-be írunk, különösebb veszélye nincs a dolognak. A bátrabbak persze csinálhatják közvetlenül egy DOS ablakból, itt csak arra kell figyelni, hogy debugolás közben nehogy elszabaduljon a vírus (tipikusan popf utasítás vagy INT 1 vektor állítása szokott ilyet eredményezni). Az, hogy debugolás közben rezidenssé válik a vírus, nem gond, mert a debuggerek mindig szorgalmasan lementik/visszatöltik az interrupt vektorokat.
Vegyünk egy egyszerű, com-okat fertőző vírust. Ezek többsége a file végéhez írja be magát, a file elejére pedig lerak egy JMP vagy CALL utasítást, amely a vírustestre mutat. Némi ellenőrzés és inicializálás után a vírus visszamásolja a file elejére az eredeti program byte-jait (amelyeket a JMP felülírt), és futtatja a fertőzött programot, mintha mi sem történt volna. Mivel a hordozó programtól függ, hogy a vírus milyen offszetcímre kerül, ezért a vírus a saját változóihoz általában BP relatív címzéssel fér hozzá. Egy tipikus fertőzött file:
0100 JMP 12E3........ # a hordozó program kódja....12E3 CALL 12E6 # praktikus megoldás az # offszet meghatározásához12E6 POP BP.... # inicializálás12E7 MOV SI,BP12E9 SUB SI,010912ED MOV DI,0100 # a program elejének a # visszaállítása12F0 MOVSW12F1 MOVSB12F2 PUSH 0100 # a hordozó program futtatása12F5 RET
Az inicializáló rész tartalma is meglehetősen kötött, például:
– Itt szokás ellenőrizni a rendszerdátumot. Vajon bekövetkezett-e az az előre beprogramozott időpont, amikor rombolni támad kedve a vírusnak? Ha igen, indul egy ciklus, amely teleírja szeméttel a merevlemez elejét, a BIOS INT 13 megszakítását felhasználva.
MOV AH,2AINT 21 # a DOS dátum szolgáltatásaCMP DX,0208 # február 8-a?
Mókás példa a Nov17 vírus esete (legalábbis az F-Prot nevezi így), amely még véletlenül sem 17-én rombol, mert abban CMP DL,17 van, azaz hexa-decimális 17. :)
– Meg kell vizsgálni, hogy már aktív-e a vírus. Ez egy INT 21 hívással történhet, amely egy nem definiált függvényre hivatkozik. Az aktív vírus persze ezt figyeli, és visszaad egy megfelelő konstanst. Egyébként aktívvá kell tennie magát a vírusnak, bemásolja magát a 640K-s határ alá, és magára állít néhány interrupt vektort (pl. 21-eset, így figyelni tudja a file-ok megnyitását, írását, futtatását). Pici, trükkös vírusok máshol is elbújhatnak a memóriában, például egy nem használt DOS puffer-területben.
Láttam olyat egy vírusban, hogy egy 80 byte-os területet hexa 80 hosszúságúként kezelt. Ilyen okai vannak annak, hogy vírusos környezetben a gép a szokásosnál kissé instabilabb...
– Kikódolás. A vírusok kódoltan is tárolódhatnak. Ez lehet egy szimpla XOR egy konstannsal, de lehet egészen összetett is: véletlenszámtól függő algoritmuskiválasztás, alakváltoztató kódoló ciklus, változó, hogy melyik regiszterrel kódol stb. Így elérhető, hogy két különböző generációs vírus egyetlen byte-ban sem hasonlít, de mégis ugyanúgy fut le.
Miután az inicializálás lefutott, a vírus már figyeli is a file-műveleteket. Általában futtatáskor fertőznek, de van olyan is (pl. Trident), amelyik egy dir parancs hatására végigfertőzi az egész könyvtárat. Persze egy jó vírus nem tesz ilyet, ennél sokkal körültekintőbb. Kevesebbszer fertőz, és akkor is jól megnézi, hogy mit. Például COMMAND.COM-ot, Norton Commandert nem, vírus-keresőket, túl kicsi file-okat sem. Rááll a 24-es interruptra is, hogy némileg átvegye a DOS-tól a hibakezelést. (Az Androméda ezt nem teszi, így egy írásvédett lemezen egy futtatás során kaphatunk write errort is. Abort, Retry, Fail?)
Cseles kódrészlet a Trident vírusból:
0200 MOV AX,2501 # INT 1 vektor beállítása # (trap, utasításonkénti # végrehajtás)0203 INT 210205 PUSHF0206 PUSH CS0207 PUSH 0219 # innen fog folytatódni020A PUSHF020B POP AX020C OR AX,0100 # trap bit beállítása a # flagben020F PUSH AX0210 PUSH word ptr [0300] # INT 21 vektor0214 PUSH word ptr [0302]0218 IRET # INT 21 # végrehajtása utasításonként0219 ...... # itt folytatódik a köv. # IRET után
Ez az akció arra jó, hogy a vírus megkeresse az INT 21 igazi belépési címét a DOS kernelben, és így lecselezzen mindenfajta aktív vírusvédőt, amely figyeli az INT 21-et. Ha megvan a belépési cím, a vírus csak azt hívja meg a fertőzési műveletnél az INT 21 helyett. Persze ez ellen az antivírus is védekezhet, nem engedi magát trap-elni, azaz extrábbnál extrább módokon állítja vissza a flag-et. Hogy melyik győz? Amelyiknek az írója több időt, kódot és trükköt szánt erre a kis momentumra...
Látható, hogy egy jó vírus megírása kreatív dolog, nem véletlen, hogy tízezerszámra gyártják őket a sportemberek (avagy a számítógépes alvilág, amolyan Kókusz Plusssz stílusban fogalmazva). Jó keresőt írni azonban nagyságrendekkel nehezebb feladat. Akik erre nem vállalkoznak, egyszerűen csak érdekli őket a programozás eme változatos színfoltja, azok számára a vírus nem más, mint egy művészi alkotás egy művészien gagyi operációs rendszer környezetében: igazi bitfaragós játék.
bereg