home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Oakland CPM Archive
/
oakcpm.iso
/
cpm
/
bbs
/
emx-util.lbr
/
PROT.MZC
/
PROT.MAC
Wrap
Text File
|
1988-01-30
|
16KB
|
572 lines
;
;
; ==>> THIS CODE WILL ONLY RUN ON A Z80 CPU
; ==>> IT MUST BE COMPILED WITH M80 AND LINKED WITH L80
;
; PROTECT.MAC by Simon Ewins, Sysop EMX RCPM, Toronto, Ontario
;
; Based on: PASSWORD.ASM v3.0 by Bo McCormick 08/06/81
;
;-----------------------------------------------------------------------
;
; - The command line allows an ASCII value (^A through the upper
; case letter Z) to be passed as the level of protection for the
; .COM file. If a code or level that is less than an ASCII
; 'space' is desired, you may use a caret (^) before the ASCII
; character that is desired to turn it into a 'control' character.
; One character value is reserved as an 'unprotect' switch. In
; the example below, ^Z is such a value. The caret '^' may be
; used as in ^^ but a space or @ may NOT. If you enter a single
; ^ as an argument an error will occur.
;
; - syntax is:
;
; PROTECT FILENAME[.COM] ^Z <-- to 'unprotect' the file
; PROTECT FILENAME[.COM] 5 <-- set level to (ASCII)
; 5 (35 hex)
; PROTECT FILENAME[.COM] Q <-- set level to (ascii)
; Q (51 hex)
; PROTECT FILENAME[.COM] ^I <-- set to (ASCII) CRL-I
; (9 hex)
;
; - The .COM filetype is optional as .COM will be assumed and any
; other filetype will generate an error exit
;
; - This file, when run, relocates itself under the CCP and then
; loads the .COM file requested. The first record is moved to the
; end of the file and replaced with a jump at 100h to the next
; record at the end of the file. There a little routine reads the
; byte in memory that is your 'control' byte and tries to match
; the protection level of the file. If there is no match, a warm
; boot is done. If there is a match, or the level of protection
; is less than the level of the file, then the last record is
; moved back to 100h and the file is executed.
;
; - Alternately you can set EXACT to YES and then the file will run
; only if the levels match (no 'less than' stuff).
;
; - If you update this file, first, be careful since the stack is
; used to pass arguments to the program once it has relocated to
; high memory. If you do update please send a copy to:
;
; EMX RCPM, Toronto, Ontario ... 416 484-9663
;
;-----------------------------------------------------------------------
;
; Equates
;
NO EQU 0
YES EQU NOT NOT
;
RDCHAR EQU 1
MSGOUT EQU 9 ; BDOS functions
INCON EQU 10
OPEN EQU 15
CLOSE EQU 16
DELETE EQU 19
READ EQU 20
BWRITE EQU 21
SETDMA EQU 26
;
CR EQU 0DH ; ASCII values
LF EQU 0AH
EOS EQU '$'
;
BOOT EQU 0 ; 0 for standard CP/M
BDOS EQU 5
FCB EQU 5CH
DEFBUF EQU 80H
TPA EQU 100H
;
;-----------------------------------------------------------------------
;
; This is the address of the start of your CCP. It can be determined by
; either using CPMLOOK, SHOW or TELL or with:
;
;
; Under DDT or ZSID examine location 0001h take the value
; at loation 0002h and subtract 16h from it. Take the re-
; sult and assume it to be the msb of a two byte value the
; LBS of which is 0... This is the value for the next
; equate. Example:
; 00Hh = 0C3H
; 01H = 03H ; Warm boot is 3 above BIOS
; 02H = 0CEH ; Page address is BIOS start
;
; subtract 16H from 0CEH to get 0B8H
; therefore CCP = 0B800H
;
CCP EQU 0A700H ; Address of CCP
;
;-----------------------------------------------------------------------
;
; This is the address of the byte in memory to check against the one
; that the file is locked with..
;
PROBYT EQU 0003FH ; Access level byte location
;
;-----------------------------------------------------------------------
;
; Id EXACT is YES, then the byte in memory and the level of the file
; must match exactly... If it is NO, then the value in memory must be
; equal to or greater than the level of the file
;
EXACT EQU YES ; True if bytes must match exactly
;
;-----------------------------------------------------------------------
;
; This is the character that you wish to use as the 'unprotect' key. It
; cannot be used as a protection level as well. if you select a control
; character, remember that you must use '^' in the command line followed
; by the ASCII character.
;
UKEY EQU 'Z'-40H ; Use CTL-z as unprotect key
; ; (use ^Z on command line)
;
;-----------------------------------------------------------------------
;
MOVLEN EQU (CODEND-CODBEG)+1 ; Length of relocated code
RELLOC EQU CCP-MOVLEN-1 ; Start address of relocated code
OFF EQU RELLOC-CODBEG ; Dif between reloc code and assem code
;
;
.Z80
ASEG
ORG 100H
;
JP START
;
KEYCOD::DB UKEY ; Store unprotect key as a byte at 103h so
; That it may be easily patched
;
; Start of program
;
START:: LD A,(FCB+1) ; See if help needed
CP '?'
JP Z,HELP ; Yes
CP '/'
JP Z,HELP ; Yes
CP ' '
JP Z,HELP ; For sure!
LD A,(FCB+9) ; Get first char of extension
CP ' ' ; If ' ' then change to .COM
JP Z,NOTYPE
CP 'C' ; If there is an extension,
JP NZ,HELP ; Make sure it's .COM
LD A,(FCB+10) ; Check second letter
CP 'O'
JP NZ,HELP
LD A,(FCB+11)
CP 'M' ; Last letter
JP Z,ISCOM0 ; If it is a .COM, then continue
JP HELP ; Wrong type
;
NOTYPE::LD A,'C' ; Force filetype to .COM
LD (FCB+9),A
LD A,'O'
LD (FCB+10),A
LD A,'M'
LD (FCB+11),A
;
ISCOM0::LD A,(FCB+17) ; Get argument, if any
CP ' ' ; None?
JP Z,HELP ; Explain this program
CP '^' ; Is it a control character?
JP NZ,NOTCTL ; No so no conversion needed
LD A,(FCB+18) ; Get next character
CP ' ' ; If space then explain program
JP Z,HELP
CP 'A' ; Less than 'A' not allowed
JP C,HELP
SUB 40H ; Convert to control character
;
NOTCTL::LD HL,KEYCOD ; Point to UKEY
CP (HL) ; Is it 'unprotect' code?
JR Z,UNPSET ; Yes, go set flag
LD (PBYTE),A ; No, so assume we want to protect file
JR REL
;
UNPSET::LD (UBYTE),A ; Store unprotect flag with value
;
; Now relocate the needed code up high and then jump to it...
;
REL:: LD DE,VERSIO
LD C,MSGOUT
CALL BDOS
LD BC,MOVLEN ; Number of bytes to move
LD DE,RELLOC ; Where to and ...
LD HL,CODBEG ; Where from
LDIR ; Move code into place
JP RELLOC ; And go to it
;
VERSIO::DB CR,LF,'PROTECT v1.0 -- (c)1984 Simon Ewins',CR,LF,EOS
;
HELP:: LD DE,HLPMSG
LD C,MSGOUT
CALL BDOS
JP 0
;
HLPMSG::DB CR,LF,LF
DB 'PROTECT v1.0 -- (c)1984 Simon Ewins'
DB CR,LF,LF
DB '- the command line allows an ASCII value '
DB '(^A through the upper case'
DB CR,LF
DB ' letter Z) to be passed as the level '
DB 'of protection for the .COM file.'
DB CR,LF
DB ' If a code or level that is less than '
DB 'an ASCII ''space'' is desired you'
DB CR,LF
DB ' may use a caret (^) before the ASCII '
DB 'character that is desired to'
DB CR,LF
DB ' turn it into a ''control'' character. '
DB 'One character value is reserved'
DB CR,LF
DB ' as an ''unprotect'' switch. in the '
DB 'example below ^Z is such a value.'
DB CR,LF
DB ' the character ''^'' may be used as '
DB 'in ^^ but a space or ''@'' may NOT.'
DB CR,LF
DB ' id est: if you enter a single ^ as '
DB 'an argument an error will occur.'
DB CR,LF,LF
DB '- syntax is:'
DB CR,LF
DB ' PROTECT FILENAME[.COM] ^Z '
DB '<-- to ''unprotect'' the file'
DB CR,LF
DB ' PROTECT FILENAME[.COM] 5 '
DB '<-- set level to (ASCII) 5 (35 hex)'
DB CR,LF
DB ' PROTECT FILENAME[.COM] Q '
DB '<-- set level to (ASCII) Q (51 hex)'
DB CR,LF
DB ' protect filename[.com] ^I '
DB '<-- set to (ASCII) CRT-I (9 hex)'
DB CR,LF,LF
DB '- the .COM filetype is optional as '
DB '.COM will be assumed and any'
DB CR,LF
DB ' other filetype will generate an error exit'
DB CR,LF,EOS
;
; This code gets relocated to high memory where it loads, modifies and
; then saves the .COM file that is being protected...
;
; NOTE: All jumps from here to the end of the program should be rela-
; tive. If the JR label is out of range, use 'stepping-stones' as
; intermediate jump locations. reference to labels for loading
; registers etc., should be of the form: LABEL+off
;
CODBEG EQU $ ; Mark start of code to relocate
;
LD DE,OFF+RDMSG
LD C,MSGOUT
CALL BDOS
LD DE,TPA ; Point to where program goes
LD C,SETDMA ; Set DMA command
PUSH DE ; Save it
CALL BDOS ; And tell CP/M
LD A,0 ; Zero record count
LD (FCB+32),A
LD (FCB+17),A ; And any arguments
LD (FCB+18),A
LD C,OPEN ; Open file command
LD DE,FCB ; Load address of FCB in DE
CALL BDOS ; Open file
INC A ; Successful?
JR NZ,OPENOK ; If so, then continue
LD DE,OFF+ERR0
LD C,MSGOUT
CALL BDOS
JP 0 ; Error
;
OPENOK::POP DE ; Get starting DMA back
;
RLOOP:: LD C,SETDMA ; And set it in loop
PUSH DE ; Save it
CALL BDOS
LD DE,FCB ; Point to FCB
LD C,READ ; Read sector command
CALL BDOS ; Do it
POP DE ; Get DMA address back
AND A ; Eof?
JR NZ,RDDONE ; If so, then go to work
PUSH DE ; Save dma
LD HL,RELLOC ; Check for overload of tpa
DEC H ; Page for safety
INC D ; Rest of page
AND A ; Clear carry
SBC HL,DE ; Top of tpa - current dma
JR NC,DMAOK ; Hl still greater
POP DE ; Unjunk stack
LD DE,OFF+TPAMSG
LD C,MSGOUT
CALL BDOS
;
JP 0000H ; Reboot
;
TPAMSG::DB CR,LF,'++ tpa too small ++',CR,LF,EOS
DMAOK:
POP DE ; Get dma back
LD HL,80H ; Length of sector
ADD HL,DE ; Bump dma
EX DE,HL ; Put new address in de
JR RLOOP ; And read some more
RDDONE:
LD (OFF+ENDADD),DE ; Save end of file
LD A,(OFF+UBYTE) ; Was code valid for unprotecting?
OR A
JR NZ,UNPRO ; Knew the code so go do it
;
; Else falls through to protect routine... Since if UBYTE is 0 then
; PBYTE must have a value and vice versa. Further, the only way that
; UBYTE can have a by the time we reach here is if it was a valid match
; to the unprotect code.
;
; Protect the file
;
PRO:: CALL OFF+PROCHK ; Check if file is protected now
OR A
JR Z,PROT ; 1=yes, 0=no
LD DE,OFF+ERR1
LD C,MSGOUT
CALL BDOS
JP 0
;
PROT:: EX DE,HL ; Got to make sure this file has not been
LD DE,128 ; Protected before, else the file will grow
AND A ; By one sector each time it is protected!
SBC HL,DE ; Hl->start of last sector
LD DE,TPA ; Now we compare it to the first sector
CALL OFF+MATCH
CP 0 ; If 0 then sectors are the same
JR Z,NOMOVE ; So don't adjust pointers
LD DE,(OFF+ENDADD) ; Get back end of file
LD (OFF+OLDEND),DE ; Set pointer for runtime load of sector 1
LD A,(OFF+PBYTE) ; Get protection level requested
LD (OFF+LEVEL),A ; Store protection level for this program
LD HL,TPA ; And save original data at end of file
LD BC,128 ; De already has address of end of program
LDIR ; Move first sector to end of program
LD HL,OFF+PRODAT ; Get protect routine
LD DE,TPA ; And move it to start of file
LD BC,128
LDIR ; Move it
LD HL,(OFF+ENDADD) ; Need to save one more sector than original
LD DE,128 ; So bump marker
ADD HL,DE
LD (OFF+ENDADD),HL ; And store it in write routine
JR WRITE
;
NOMOVE::LD HL,(OFF+ENDADD) ; Set runtime sector load address
LD DE,128 ; Since it was here already we must point
AND A ; To the second last sector
SBC HL,DE
LD (OFF+OLDEND),HL ; Store adjusted pointer
LD A,(OFF+PBYTE) ; Not moving sector but must set new level
LD (OFF+LEVEL),A ; Set level
LD HL,OFF+PRODAT ; Move protect routine to tpa
LD DE,TPA
LD BC,128
LDIR
JR WRITE ; No need to adjust end address
;
; Unprotect the file
;
UNPRO:: CALL OFF+PROCHK ; See if already protected
OR A
JR NZ,UNPROT ; 1=yes, 0=no
LD DE,OFF+ERR2
LD C,MSGOUT
CALL BDOS
;
JP 0000H ; Reboot
;
ERR2:: DB CR,LF,'++ file not protected ++',CR,LF,EOS
;
UNPROT::EX DE,HL ; End of program in hl now
LD DE,128 ; Less the sector for protecting
AND A ; Clear carry
SBC HL,DE ; HL->old last sector of file
LD DE,TPA ; Start of file
LD BC,128 ; Move one sector
LDIR ; Back in place
;
; This writes either the full file, if we just protected it, plus one
; record or the full file less the last record, if we unprotected it...
;
WRITE:: LD DE,OFF+WRMSG
LD C,MSGOUT
CALL BDOS
XOR A ; Zero A
LD (FCB+12),A ; Zero bytes in FCB
LD (FCB+14),A
LD (FCB+32),A
LD C,OPEN ; Open file command
LD DE,FCB ; Point to FCB
CALL BDOS ; Open the file
LD DE,TPA ; Point to program start
PUSH DE
;
WLOOP1:;POP DE ; Get DMA
PUSH DE ; Put it back on stack
LD C,SETDMA ; Set DMA command
CALL BDOS ; Tell CP/M
LD DE,FCB ; Point to FCB
LD C,BWRITE ; Write sector command
CALL BDOS ; Do it
POP HL ; Get dma address from stack
LD DE,80H ; Length of sector
ADD HL,DE ; HL has new DMA
PUSH HL ; Put it on stack
;
;
.8080
;
;
DB (LXI D)
;
;
.Z80
;
;
ENDADD::DW 0000H ; Get ending address
AND A ; Clear carry
SBC HL,DE ; See if current dma is less
JR C,WLOOP1 ; Still more to write
;
LD C,CLOSE ; That's it. Close the file
LD DE,FCB ; Point to FCB
CALL BDOS ; Do it
LD DE,OFF+DNMSG ; Say we done
LD C,MSGOUT
CALL BDOS
JP 0000H ; Reboot
;
; This data gets moved down to the start of the TPA where it is executed
; each time the protected file is run. it is the code that checks the
; values that determine if the file may be run or not... if it can be
; run, then this code replaces itself with the code that was in the
; first reecord of the file originally.... control is then passed back
; to the start of the TPA...
;
TPAOFF EQU $
;
PRODAT::JR PROT0 ; Jump around ff marker stuff
DW 0FFFFH
DW 0FFFFH
DW 0FFFFH
DW 0FFFFH ; To mark protected file
;
PROEXT::LD DE,TPA+PROMSG-TPAOFF
LD C,MSGOUT
CALL BDOS
JP 00000H
;
PROMSG::DB CR,LF,'Restricted access...',EOS
;
;
.8080
;
;
PROT0:: DB (MVI B)
;
;
.Z80
;
;
LEVEL:: DB 0 ; (0 is set to code earlier)
LD HL,PROBYT ; Get current user's level
LD A,(HL)
CP B ; Match to file's level
;
IF EXACT
JR NZ,PROEXT ; No match so drop out
ENDIF ; End EXACT test
;
IF NOT EXACT
JR C,PROEXT ; Current level less than protect level
ENDIF ; End not exact test
;
;
.8080
;
;
DB (LXI H)
;
;
.Z80
;
;
OLDEND::DW 0000H ; Filled with data at protect time
PUSH HL ; Save for after move
LD DE,129 ; Point to end of current program + 1
ADD HL,DE
PUSH HL ; Hl=address to move to
EX DE,HL ; De now=where we are going
LD HL,TPA+START1-TPAOFF ; Where we move from
LD BC,END1-START1 ; Move this much
LDIR
POP HL ; Relocated code
JP (HL) ; Go finish running file
;
START1::POP HL ; Get back address of real program start
LD DE,TPA ; Move to start of tpa
LD BC,128
LDIR
JP TPA ; Go run file
;
END1:: DB 0
;
; End of the control code section
;
; This routine tests to see if the file loaded is protected or not
;
PROCHK::LD HL,TPA+2 ; Point to start of file + 2 for jump relative
LD C,8 ; Testing next 8 bytes in a row for 0ffh
;
TESTIT::LD A,(HL)
CP 0FFH
JR NZ,NPRO ; Not protected
INC HL
DEC C
JR NZ,TESTIT ; Check all 8 bytes
;
YPRO:: LD A,1 ; Indicate protected file
RET
;
NPRO:: LD A,0 ; Indicate unprotected file
RET
;.....
;
; Compare 2 records, return with A=0 if equal
;
MATCH:: LD B,128 ; Number of bytes to check
;
MLOP:: LD A,(DE)
SUB (HL)
RET NZ
INC DE
INC HL
DJNZ MLOP ; Do all 128 bytes
RET ; A=0
;.....
;
ERR0:: DB CR,LF,'++ Can''t open file ++',CR,LF,EOS
ERR1:: DB CR,LF,'++ already protected ++',CR,LF,EOS
RDMSG:: DB CR,LF,'Reading....',EOS
WRMSG:: DB CR,LF,'Writing....',EOS
DNMSG:: DB CR,LF,'Done.......',CR,LF,EOS
;
PBYTE:: DB 0 ; Flags protect wanted
UBYTE:: DB 0 ; Flags unprotect wanted
;
CODEND EQU $
;
END