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.