home *** CD-ROM | disk | FTP | other *** search
- ;***********************************************************************
- ; Listing 1 - ANTIVENM.ASM
- ;
- ; Written by Kevin D. Weeks 10-8-89
- ; Released to the Public Domain.
- ;
- ; int anti_venom(int buf_size,char* buffer)
- ;
- ; Description:
- ; This collection of routines performs a Cyclic Redundancy Check on
- ; the program they are a part of and compares the resulting value
- ; with a value known to be correct. A descrepancy in these two num-
- ; bers indicates the program has been modified and may be infected
- ; by a virus.
- ;
- ; Returns:
- ; -1 Program has been modified and may be infected.
- ; 0 No problems detected.
- ; +1 Execution error (file not found, PSP not found, etc.)
- ;
- .MODEL small
- if @codesize
- bp_ofs equ 6
- else
- bp_ofs equ 4
- endif
-
- FOUND equ 0
- EXECUTION_ERR equ 1
- VIRUS_DETECTED equ -1
-
- .DATA
- state dw ? ; current state of valid_crc search
- cur_crc dw ? ; current crc value
-
- ;* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- .CODE
- public _anti_venom
-
- ;* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- ; The _anti_venom function performs the initial setup (such as ac-
- ; quiring the file name) and then successively calls READ_FILE and
- ; CALC_CRC until the entire file has been processed.
- ;
- if @codesize ; if large code memory model
- _anti_venom proc far
- else ; else, small code memory model
- _anti_venom proc near
- endif
- push bp ; save the caller's stack frame
- mov bp,sp
- push bx
- push cx
- push dx
- push di
- push si
- push es
- push ds
- jmp STEP_1 ; jump over the CRC and signature
-
- ; the signature and validation crc are stored here to guarantee they'll
- ; be contiguous and not subject to compiler startup manipulation.
- signature db 'ANTIV'
- valid_crc dw 0
-
- ; the first step is to get the psp of this process.
- STEP_1:
- call GET_PSP ; returns with BX = PSP
- cmp bx,0 ; check for a 0 return
- je ERROR_OUT
-
- ; from the psp we get the address of the environment block which con-
- ; tains the path and name of this program.
- call SCAN_ENVIRONMENT
-
- ; SCAN_ENVIRONMENT returns with DS:DX pointing to file path and name
- ; ready for a call to DOS to open the file.
- mov ax,3d00h ; Open File function - read only
- int 21h ; call DOS
- jc ERROR_OUT
-
- ; get the buffer address form the stack. NOTE: this is language specific!
- mov bx,ax ; move file handle to bx
- mov cx,[bp + bp_ofs] ; get buffer size
- mov dx,[bp + bp_ofs + 2] ; get offset address of buffer
- if @datasize ; if large data model
- mov ds,[bp + bp_ofs + 4] ; get segment address of buffer
- else ; else small data model
- pop ds ; restore original data segment
- push ds ; keep stack accurate
- endif
- call CLEAR_OUT ; initialize the state
-
- ; this is where the real work gets done. we read to the end of the file
- ; calling CALC_CRC after each read.
- MAIN_LOOP:
- call READ_FILE ; read the file
- jc ERROR_OUT ; bailout if read error
- cmp ax,0 ; check for EOF
- jz COMPARE_CRC ; if so, complete testing
- ; else
- push cx ; save buffer size
- mov cx,ax ; place bytes actually read in cx
- call CALC_CRC ; calculate CRC
- pop cx ; restore buffer size
- jmp MAIN_LOOP ; and repeat
- ; end of MAIN_LOOP
-
- ; test the crc just calculated by subtracting it from the valid crc. if
- ; the result is 0 then we have our return code, otherwise we indicate a
- ; virus was detected
- COMPARE_CRC:
- mov ax,cs:valid_crc ; move the correct crc to ax
- sub ax,cs:cur_crc ; and subtract the new crc
- jz CLOSE ; if the result is 0 they're equal
- ; else
- mov ax,VIRUS_DETECTED ; indicate crc error
- jmp short CLOSE ; and bailout
-
- ERROR_OUT:
- mov ax,EXECUTION_ERR ; indicate execution error
-
- CLOSE:
- push ax ; save the return code
- mov ax,3e00h ; Close File function
- int 21h ; call DOS
- pop ax ; restore the return code
-
- EXIT:
- pop ds ; restore the caller's environment
- pop es
- pop si
- pop di
- pop dx
- pop cx
- pop bx
- mov sp,bp
- pop bp
- ret
- _anti_venom endp
-
- ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- ; GET_PSP
- ; First we issue a call to DOS function 62h, Get PSP. this function only
- ; exists in DOS 3.xx but IS documented. If we get a 0 back in register bx
- ; then we assume we're running under DOS 2.xx and issue a call to the UN-
- ; documented DOS function 51h.
- ;
- GET_PSP proc near
- mov ax,6200h ; DOS Get PSP function
- int 21h ; call DOS
- cmp bx,0 ; check for a 0 return
- jz GET_PSP2
- ret ; if not, return
- GET_PSP2:
- mov ax,5100h ; undocumented Get PSP function
- int 21h ; call DOS
- ret
- GET_PSP endp
-
- ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- ; SCAN_ENVIRONMENT
- ; The DOS environment blocks consist of a series of ASCIIz strings. The
- ; last string is double terminated, followed by the number of additional
- ; strings(?), followed by the ASCIIz path and filename of the current
- ; process, which is what we want.
- ;
- SCAN_ENVIRONMENT proc near
- mov si,002ch ; offset of pointer to environment block
- mov es,bx ; move psp segment into es
- mov ds,es:[si] ; move environment segment into ds
- mov si,0 ; set index to beginning
- mov cx,0 ; use cx to count NUL-terminators
- SCAN_0:
- cmp byte ptr [si],0 ; check for a zero
- jnz SCAN_1 ; no zero so check next character
- ; else
- inc cx ; increment cx
- inc si ; point to next character
- cmp cx,2 ; check to see if we've got two 0's
- je SCAN_2 ; we do so exit loop
- ; else
- jmp SCAN_0 ; loop again
- SCAN_1:
- inc si ; point to next character
- mov cx,0 ; re-initialize cx
- jmp SCAN_0 ; and loop again
-
- SCAN_2: ; we've found the double NUL
- inc si ; skip over the string count
- inc si
- mov dx,si ; ds:dx = pointer to this program's
- ; path and name
- ret ; return
- SCAN_ENVIRONMENT endp
-
- ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- ; READ_FILE
- ;
- READ_FILE proc near
- mov ax,3f00h ; Read File function
- int 21h ; call DOS
- ret ; return immediately
- READ_FILE endp
-
- ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- ; CALC_CRC
- ; This routine consists of two loops. The outer one processes the input
- ; buffer byte-by-byte while the inner loop calculates the crc on each
- ; byte.
- ;
- CALC_CRC proc near
- mov si,dx ; move pointer to buffer to si
- FOR_1:
- push cx ; save buffer count
- mov ah,[si] ; move character to ah
- mov al,0 ; clear low byte of ax
- xor ax,cs:cur_crc ; combine character with crc
- mov cx,8 ; set cx to 8 for 'for' loop
- FOR_2:
- test ax,8000h ; see if MSB is on
- jz CCRC_2 ; if not, perform else
- shl ax,1 ; shift left one
- xor ax,1021h ; and incorporate prime
- loop FOR_2 ; loop back
- jmp short CCRC_3 ; we finished the crc calculation
- CCRC_2: ; else
- shl ax,1 ; shift left one
- loop FOR_2 ; and loop
-
- CCRC_3:
- pop cx ; restore buffer length to cx
- cmp cs:state,FOUND ; see if we've found the signature
- je END_OF_FOR2 ; if so, go on
- call FIND_VALID_CRC ; else, check this character
- END_OF_FOR2:
- inc si ; point to next byte
- mov cs:cur_crc,ax ; save current crc
- loop FOR_1 ; and loop
-
- ret
- CALC_CRC endp
-
- ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- ; FIND_VALID_CRC
- ; This is implemented as a finite state machine (I use these things ALL
- ; the time) where each state is represented by the address of the state
- ; processor. This would of course have to be modified for a different
- ; signature. Alternatively, it could be handled the way SETCRC.C does it
- ; which is more general but less clear. My feeling is that since you
- ; should change the signature for each program you use this in you might
- ; as well change this routine also. It's simple enough to do.
- ;
- FIND_VALID_CRC proc near
- jmp cs:state
- FIND_A:
- cmp byte ptr [si],'A' ; if target value not seen
- jne CLEAR_OUT ; go back to beginning
- mov cs:state,offset FIND_N ; else, change state
- ret
- FIND_N:
- cmp byte ptr [si],'N'
- jne CLEAR_OUT
- mov cs:state,offset FIND_T
- ret
- FIND_T:
- cmp byte ptr [si],'T'
- jne CLEAR_OUT
- mov cs:state,offset FIND_I
- ret
- FIND_I:
- cmp byte ptr [si],'I'
- jne CLEAR_OUT
- mov cs:state,offset FIND_V
- ret
- FIND_V:
- cmp byte ptr [si],'V'
- jne CLEAR_OUT
- mov cs:state,FOUND ; we've found the signature
- dec cx ; so increment the byte counter
- dec cx ; past it
- inc si ; and increment the pointer
- inc si ; past it
- ret
- CLEAR_OUT:
- mov cs:state,offset FIND_A
- ret
- FIND_VALID_CRC endp
-
- END
-