home *** CD-ROM | disk | FTP | other *** search
- name spyprime
-
- rt equ 0dh ; return
- lf equ 0ah ; linefeed
- eof equ 1ah ; end of file
- ok_seg equ 1 ; flag for segment decode check
- ok_off equ 2 ; flag for offset decode check
- max_size equ 4095 ; maximum size for SPY' search region
- beep_delay equ 4 ; number of clock ticks between beeps
- timer_count equ 597 ; frequency of beep = 1,193,180 / timer_count
-
- ; These constants are used by the newint16h routine in this, the SPY' program.
- ; They are not needed for the SPY program.
- ckey1 equ 20cdh ; value of first key (a "backwords" int 20)
- ckey2 equ 1234h ; value of second key
- ckey3 equ 5678h ; value of third key
-
- code segment
- assume cs:code, ds:code
-
- org 0
- key1 dw ? ; offset of int 20h instruction
- ; used to verify a PSP location
-
- org 100h
-
- begin: jmp start ; jump to program start location
-
- ;-------------------------------------------------------------------------------
- ; This copyright message appears when SPY'.COM is "typed" to the screen.
- ;-------------------------------------------------------------------------------
-
- db 8,8,8,' '
- copyright db rt,lf
- db "SPY' Copyright (C) 1987 by Charles Lazo III, v1.0"
- db eof,8,' ',rt,lf,'$'
-
- key2 dw ckey2 ; keys used to determine prior load of SPY'
- key3 dw ckey3
-
- oldint8 dd ? ; double word storage for old int 8 vector
- oldint16h dd ? ; double word storage for old int 16 vector
- beeps dw 0 ; number of beeps (notices) to send to user
- delay dw 1 ; number of clock ticks 'til next beep
- beep_on db 0 ; beep on/off status
- spy_size dw ? ; number of bytes to spy on
- resident db 0 ; indicates prior load of SPY' (0 = none)
-
- spy_location label dword
- spy_off dw ? ; storage for value of offset for SPYing
- spy_seg dw ? ; storage for value of segment for SPYing
-
- ;-------------------------------------------------------------------------------
- ; Comparisons of the spied region and the spy buffer (set up initially as a copy
- ; of the spied region) are made in this addition to the clock interrupt.
- ; Changes to the spied region are counted and the spy buffer is updated. The
- ; speaker is turned on and off here, but frequency is set during program
- ; initialization.
- ;-------------------------------------------------------------------------------
-
- newint8 proc near
- push ax ; save interrupted program's registers
- push bx
- push cx
- push si
- push di
- push ds
- push es
-
- mov ax,cs ; our data lies here
- mov ds,ax
-
- lea si,spy_buffer ; point to our buffer
- les di,spy_location ; point to spied region
- mov cx,spy_size ; compare whole region
- cld ; forward!
- cmp_more: repz cmpsb ; compare until no match or cx = 0
- jz cmp_done ; if zero, then cx = 0 and we're done
- inc beeps ; account for a change
- mov al,es:[di-1] ; change accounted; update spy_buffer
- mov [si-1],al
- or cx,cx ; set zero flag by cx (avoid inf loop)
- jmp short cmp_more ; continue 'til done
-
- cmp_done: cmp beep_on,0 ; is the beep on?
- jz do_beep? ; no, shall we do a beep?
- dec beep_on ; yes, turn it off
- in al,97 ; get speaker control bits
- and al,0fch ; set them off
- out 97,al ; turn off speaker
- jmp short exit ; job done; get out
- do_beep?: cmp beeps,0 ; are there beeps to be done?
- jz delay1 ; no, get out
- dec delay ; reduce delay 'til next beep
- jnz exit ; not zero, then exit
- in al,97 ; get speaker control bits
- or al,3 ; set them on
- out 97,al ; turn on speaker
- inc beep_on ; signal beep is on
- mov delay,beep_delay; reinitialize delay counter
- dec beeps ; one less beep to do
- jmp short exit ; leave now
- delay1: mov delay,1 ; don't wait for first beep of a series
- exit: pop es ; restore registers
- pop ds
- pop di
- pop si
- pop cx
- pop bx
- pop ax
- jmp dword ptr cs:oldint8 ; continue with interrupt
- newint8 endp
-
- ;-------------------------------------------------------------------------------
- ; Keyboard interrupt, int 16h, is issued with function number ah = 77h in the
- ; set_es routine. If there has been no prior load of SPY', then the interrupt
- ; returns without changing anything. However, if SPY' has been run previously,
- ; then this routine has been chained into the interrupt 16h handler and the
- ; issue of "keyboard" function number 77h causes the returned value of es to be
- ; what cs was for the original load of SPY'.
- ;-------------------------------------------------------------------------------
-
- newint16h proc near
- cmp ah,77h ; our cue?
- jne not_us ; nah, not us
- push ax ; save this
- mov ax,ckey1 ; have we a match for the first key?
- cmp ax,cs:key1
- je test_2nd ; yes, test the 2nd key
- jmp short onward ; no previous load found
- test_2nd: mov ax,ckey2 ; have we a match for the second key?
- cmp ax,cs:key2
- je test_3rd ; yes, test the 3rd key
- jmp short onward ; no previous load found
- test_3rd: mov ax,ckey3 ; have we a match for the third key?
- cmp ax,cs:key3
- jne onward ; no match => no previous load
- pop ax ; unstack saved value
- mov ax,cs ; pass this cs in es
- mov es,ax
- mov ax,ckey1 ; pass these to inform set_es
- mov bx,ckey2
- iret
- onward: pop ax ; recover this
- not_us: jmp dword ptr cs:oldint16h ; continue with interrupt
- newint16h endp
-
- ;-------------------------------------------------------------------------------
- ; Here the region spied upon is copied to an area of the TSR that remains in
- ; memory after termination, the spy_buffer. The es register is equal either to
- ; cs if SPY' has not yet been loaded or to the value of cs when SPY' was loaded
- ; previously (accomplished by a call to the set_es routine).
- ;-------------------------------------------------------------------------------
-
- copy_spied: lea di,spy_buffer ; destination of copy
- mov si,spy_off ; the offset is source of copy
- mov cx,spy_size ; number of bytes to copy
- mov ax,spy_seg ; load segment of SPY' region to ds
- push ds ; SOD (save our data)
- mov ds,ax
- cld ; forward copy
- rep movsb ; copy the SPY' region to spy_buffer
- pop ds ; RestoreOD
-
- cmp resident,0 ; is SPY' currently resident in memory?
- je tsr ; no, make it so
- mov ax,4c00h ; yes, end with error code 0
- int 21h
-
- ;-------------------------------------------------------------------------------
- ; SPY' has not yet been loaded into memory, so we do it now. First the 8253-5
- ; chip that generates the frequency of the beeps is initialized with the value
- ; given by the constant timer_count. Then the new interrupt 8 routine is
- ; spliced in and finally the constant max_size is used to reserve enough memory
- ; for the largest permissible spy buffer.
- ;-------------------------------------------------------------------------------
-
- tsr: mov delay,beep_delay; initialize delay counter
-
- ; set 8253-5 programmable timer to chosen frequency
- mov al,182 ; byte to initialize 8253 timer
- out 67,al ; tell timer next two bytes are count
- mov ax,timer_count ; get timer count
- out 66,al ; output low byte
- mov al,ah
- out 66,al ; output high byte
-
- mov ax,3508h ; get the interrupt 8 (clock) vector
- int 21h ; with DOS function 35h call
-
- ; Retain offset and segment of interrupt 8 vector:
-
- mov word ptr cs:oldint8,bx
- mov word ptr cs:oldint8+2,es
-
- lea dx,newint8 ; place offset in dx
- mov ax,2508h ; set its pointer into interrupt table
- int 21h ; with DOS function 25h call
-
- mov ax,3516h ; get the int 16h (keyboard) vector
- int 21h ; with DOS function 35h call
-
- ; Retain offset and segment of interrupt 16h vector:
-
- mov word ptr cs:oldint16h,bx
- mov word ptr cs:oldint16h+2,es
-
- lea dx,newint16h ; place offset in dx
- mov ax,2516h ; set its pointer into interrupt table
- int 21h ; with DOS function 25h call
-
- lea dx,spy_buffer ; where SPY' buffer begins
- mov cx,max_size ; add the maximum size of the buffer:
- add dx,cx
- mov cl,4 ; compute number of paragraphs to save:
- shr dx,cl ; bytes to paragraphs in dx
- inc dx ; insure sufficient size for buffer
- mov ax,3100h ; terminate but stay resident code = 0
- int 21h
-
- ;-------------------------------------------------------------------------------
- ; The following is initialization code and data that is overwritten by the data
- ; copied to the spy buffer when this buffer is initialized with a copy of the
- ; spied region.
- ;-------------------------------------------------------------------------------
-
- spy_buffer: ; this is where SPY' will store a copy of the SPY' region
- ; to later check for any changes to the region
-
- syntax db rt,lf,"SPY' syntax: SPY' xxxx:yyyy L zzz",rt,lf,rt,lf
- db 'Where xxxx, and yyyy are hexadecimal numbers up to '
- db 'four digits in length and',rt,lf
- db "zzz is a one to three digit hexadecimal number. SPY' "
- db 'will monitor the segment-',rt,lf
- db 'offset region xxxx:yyyy for a length of zzz bytes and '
- db 'report to the user with a',rt,lf
- db 'number of beeps equal to the number of bytes changed '
- db 'in that region if and when',rt,lf
- db 'any are changed.',rt,lf,'$'
-
- progress db 0 ; flags for progress of command line conversion
-
- ;-------------------------------------------------------------------------------
- ; This routine will convert an ASCII hex digit in al (with either upper or lower
- ; case alpha hex) into a 1-of-16 binary value in al.
- ;-------------------------------------------------------------------------------
-
- make_binary proc near ; hex ASCII digit in al to binary in al
- cmp al,'0' ; less than "0", then not hex digit
- jb not_digit
- cmp al,'f' ; greater than "f", then not hex digit
- ja not_digit
- cmp al,'9' ; test for numeric digit
- ja not_numeric ; not a numeric digit
- sub al,'0' ; convert to binary
- ret
- not_numeric: cmp al,'F' ; over "F"?
- ja low_case ; yes, test for lower case
- cmp al,'A' ; less than "A"?
- jb not_digit ; yes, not hex digit
- sub al,37h ; convert to binary
- ret
- low_case: cmp al,'a' ; less than "a"?
- jb not_digit ; yes, not hex digit
- sub al,57h ; convert to binary
- ret
- not_digit: stc ; set carry flag if not hex digit
- ret
- make_binary endp
-
- ;-------------------------------------------------------------------------------
- ; This routine is called to allow the command line parse routines to ignore any
- ; space and tab characters on the command line that do not lie in hex numbers.
- ; If the return character at the end of the command line is encountered, then
- ; the carry flag is set; otherwise it is reset.
- ;-------------------------------------------------------------------------------
-
- skip proc near ; skip over spaces and tabs; signal
- ; a RETURN with the carry flag
- more_skip: cmp al,' ' ; is it a space?
- je another ; if so, get another
- cmp al,9 ; is it a tab?
- je another ; if so, get another
- cmp al,rt ; is it a RETURN?
- je a_rt ; yes, set carry and return
- clc ; not one of these three so return
- ret ; with carry clear
- another: lodsb ; get another character
- jmp short more_skip ; and try again
- a_rt: stc ; return with carry set
- ret
- skip endp
-
- ;-------------------------------------------------------------------------------
- ; Here we parse the command line, which by the conditions of proper syntax has
- ; the three hexadecimal values for segment, offset and byte size for the region
- ; which is to be spied. The routine between cnv_more: and cnv_error: is run
- ; once each to decode segment, offset and size which are stored respectively in
- ; the variables spy_seg, spy_off, and spy_size unless a syntax error is found.
- ; The variable progress is used to keep track of which of these three numbers
- ; has just been decoded. A semicolon can be used to preface comments at the end
- ; of the command line.
- ;-------------------------------------------------------------------------------
-
- convert proc near ; convert hex ASCII to binary in ax
- mov cx,16 ; for hex to binary conversion
- cnv_more: xor bx,bx ; accumulate result here
- xor ah,ah ; need this zero too
- dec si ; move back to last character
- cycle: lodsb ; get possible ASCII hex digit
- call make_binary ; if ASCII hex, then make binary in al
- jc test_seg ; if carry set, then char not hex digit
- xchg ax,bx ; accumulation in ax, last digit in bx
- mul cx ; bump to next power
- or dx,dx ; test for overflow
- jnz cnv_error ; result can't be larger than fff0h
- add ax,bx ; add in most recent digit
- xchg ax,bx ; accumulation in bx, ah = 0
- jmp short cycle ; continue conversion
- cnv_error: pop ax ; remove return location (of call)
- jmp stax ; display syntax and end
- test_seg: test progress,ok_seg ; have we decoded the segment value?
- jnz test_off ; yes, test offset
- call skip ; skip over spaces and tabs
- jc cnv_error ; carry flag set if RETURN was found
- cmp al,':' ; syntax says must have colon here
- jne cnv_error
- lodsb ; get another
- call skip ; skip over spaces and tabs
- jc cnv_error ; carry flag set if RETURN was found
- mov spy_seg,bx ; store value of segment for SPYing
- or progress,ok_seg ; segment value is now decoded
- jmp short cnv_more ; continue to convert offset and size
- test_off: test progress,ok_off ; have we decoded the offset value?
- jnz test_size ; yes, test size
- call skip ; skip over spaces and tabs
- jc cnv_error ; carry flag set if RETURN was found
- and al,0dfh ; convert "L" or "l" to "L"
- cmp al,'L' ; syntax says must have "l" or "L" here
- jne cnv_error
- lodsb ; get another
- call skip ; skip over spaces and tabs
- jc cnv_error ; carry flag set if return was found
- mov spy_off,bx ; store value of offset for SPYing
- or progress,ok_off ; offset value is now decoded
- jmp short cnv_more ; continue to convert size
- test_size: cmp bx,max_size ; size must not be over maximum
- ja cnv_error
- call skip ; skip over spaces and tabs
- jnc comment ; shall allow comments at cmd line end
- cnv_finished: mov spy_size,bx ; store away size
- ret
- comment: cmp al,';' ; test for comment symbol
- jne cnv_error ; not comment symbol, then error
- jmp short cnv_finished ; all done
- convert endp
-
- ;-------------------------------------------------------------------------------
- ; This is the routine that contains the code that this program was designed to
- ; demonstrate. It serves to detect if SPY' has been loaded previously as a TSR.
- ; If it has, then the code segment at the time of the previous load will be put
- ; into es; if not, then es will return with the current value of cs. The method
- ; used attempts to find the set of key values key1, key2, and key3 in the memory
- ; above the current PSP. key1 is word cd20h which is the int 20h instruction
- ; found at offset 0 of every PSP. If key1 matches, then key2 is tried and if it
- ; matches key3 is tried. A triple match gives fair confidence that SPY' has
- ; been run as a TSR once before. It is even possible to gain further confidence
- ; or detect the presence of a different version of the same program by comparing
- ; a string in the returned es to a string in cs, say the copyright notice. Note
- ; that the keys are compared in the newint16h routine that handles the function
- ; 77h if SPY' has been previously loaded.
- ;-------------------------------------------------------------------------------
-
- set_es proc near ; es becomes cs of original SPY' load
- mov ah,77h ; function to find previous load address
- int 16h
- cmp ax,ckey1 ; if ax = ckey1
- jne not_loaded
- cmp bx,ckey2 ; and bx = ckey2
- jne not_loaded
- mov resident,1 ; then there's been a prior load
- ret
- not_loaded: mov ax,cs ; else, return with es = cs
- mov es,ax
- ret
- set_es endp
-
- ;-------------------------------------------------------------------------------
- ; Execution starts here. The command line is decoded, new parameters are passed
- ; to the prior load if it exists, and the beeps variable is set to zero for a
- ; fresh start. Execution is then passed to copy_spied: and other termination
- ; code that can not be overwritten by the copy process.
- ;-------------------------------------------------------------------------------
-
- start: lea dx,copyright ; display copyright
- mov ah,9
- int 21h
-
- mov bx,80h ; get command line byte count
- mov al,[bx]
- cmp al,1
- ja parse ; if command given, parse it
- stax: lea dx,syntax ; display command syntax
- mov ah,9
- int 21h
- mov ax,4c01h ; and exit with error code 1
- int 21h
-
- parse: mov si,82h ; address first usable character
- lodsb
- call skip ; skip over spaces and tabs
- jc stax ; carry is set if RETURN was found
- call convert ; convert and store hex values
- call set_es ; set es to spy_buffer segment
-
- mov ax,spy_seg ; set SPY' region to newly given values
- mov es:spy_seg,ax ; (this isn't necessary for first
- mov ax,spy_off ; time through, but it doesn't
- mov es:spy_off,ax ; hurt either.)
- mov ax,spy_size
- mov es:spy_size,ax
-
- xor ax,ax ; initialize beeps count
- mov es:beeps,ax
-
- jmp copy_spied ; copy that which is to be spied
-
- code ends
- end begin