; SCRAMBLE.ASM ver 2.1
;
;SCRAMBLE is a program to scramble CP/M files using an 8 byte
;password.
;
;03/14/79 Originally written by Ward Crhistensen
;
;07/13/81 Moved stack init to beginning so default stack not
; used. Added fix to write routine for proper
; operation under CP/M 2.x. Expanded Macros so program
; may be assembled with ASM. By Keith Petersen, W8SDZ
;
;12/30/82 Removed loop called MIXUP and MVI H,0 just before
; it. Comment was "Scramble awhile to mix up the
; seed". Loop occurred before the password was moved
; into location, so loop had no effect on "seed".
; Added CALL ERXIT in FINISH. If an error had
; occurred program would have crashed on the error
; message itself. Added more comments around pseudo-
; random number generator to better understand the
; coding. By Bob Hageman
;
MONTH EQU 12 ;LAST..
DAY EQU 30 ;..MODIFICATION..
YEAR EQU 82 ;..DATE
;
;Scrambling is done in place, i.e. the file is modified on top
;of itself. The same password used to scramble the file is
;used to unscramble it, using the exact same command. This is
;because the scrambling code is exclusive-ORed with the data
;file, and two same exclusive ORs result in the original value
;being returned.
;
;Command format:
;
; SCRAMBLE filename.type PASSWORD
;
;Where PASSWORD is any 8 character string which
;is allowable as a file name (i.e. no '.', etc).
;
ORG 100H
;
;Init local stack
LXI H,0
DAD SP
SHLD STACK
LXI SP,STACK
;
;Print sign-on message
CALL START
DB 'SCRAMBLE.COM as of '
DB '0'+MONTH/10
DB '0'+MONTH MOD 10,'/'
DB '0'+DAY/10
DB '0'+DAY MOD 10,'/'
DB '0'+YEAR/10
DB '0'+YEAR MOD 10
DB 0DH,0AH,'$'
;
START POP D ;GET ID
MVI C,PRINT
CALL BDOS ;PRINT ID
;
;Start of program execution
;
;See that the password is 8 characters
LDA FCB2+8
CPI ' '
JNZ PWIS8
CALL ERXIT
DB '++ PASSWORD NOT 8 BYTES ++$'
;
;Save the password
;
PWIS8 LXI H,FCB2+1 ;POINT TO PASSWORD
LXI D,PASSWD ;OUR PASSWORD AREA
LXI B,8 ;8 CHARS
CALL MOVER ;MOVE IT
;
;Password is 8 bytes, now make sure no character
;is repeated more than 2 times
LXI H,PASSWD
MVI B,8 ;8 CHARS TO TEST
;
DUPTEST CALL CKDUP ;ABORTS IF 3 = CHARS
INX H ;TO NEXT CHAR
DCR B
JNZ DUPTEST
;
;See that the input file exists
PUSH B
PUSH D
PUSH H
MVI C,OPEN
LXI D,FCB
CALL BDOS
POP H
POP D
POP B
INR A ;OK?
JNZ SCRAMLP ;YES, SCRAMBLE IT
CALL ERXIT
DB '++NO SUCH FILE++$'
;
;Read the file, scramble a sector, re-write it.
;
SCRAMLP CALL RDSECT ;READ A SECTOR
JC FINISH ;EXIT LOOP IF EOF
CALL SCRAMBL ;SCRAMBLE IT
CALL BACKUP ;RE-POSITION FOR WRITE
CALL WRSECT ;RE-WRITE THE SECTOR
JMP SCRAMLP ;LOOP UNTIL EOF
;
;All done - on a "normal" CP/M system we wouldn't have to do
;anything because we re-wrote in place. However, for systems
;which use sector deblocking we must explicitly close the file
;in order to flush the memory-resident disk buffers.
;
FINISH PUSH B
PUSH D
PUSH H
MVI C,CLOSE
LXI D,FCB
CALL BDOS
POP H
POP D
POP B
INR A ;THIS BETTER WORK..
JNZ EXIT
CALL ERXIT
DB '++ CLOSE ERROR - FILE LEFT IN '
DB 'UNKNOWN CONDITION ++$'
;
;Sector read routine
;
RDSECT PUSH B
PUSH D
PUSH H
MVI C,READ
LXI D,FCB
CALL BDOS
POP H
POP D
POP B
ORA A
RZ ;ALL OK
;
;Read error or EOF
;
CPI 1 ;EOF?
STC ;CARRY SHOWS EOF
RZ ;RET, CARRY SET
CALL ERXIT
DB '++ READ ERROR - FILE MAY BE '
DB 'DESTROYED ++$'
;
;Scramble the sector
;
SCRAMBL LXI H,80H ;POINT TO SECTOR
;
SCRLP CALL PSEURAN ;GET PSEUDO RANDOM #
XRA M ;SCRAMBLE
MOV M,A
INR L ;MORE IN SECTOR?
JNZ SCRLP
RET
;
;Backup the file pointer for the re-write
;
BACKUP LDA FCBRNO ;GET SECTOR #
DCR A ;BACK UP
STA FCBRNO
RP ;RETURN IF OK
;
;We backed up into previous extent, will have to re-open it
;(this only works for 16k extent size systems).
;
LDA FCBEXT ;GET EXTENT
DCR A ;BACK UP 1
STA FCBEXT
PUSH B
PUSH D
PUSH H
MVI C,OPEN ;RE-OPEN
LXI D,FCB
CALL BDOS
POP H
POP D
POP B
INR A
JNZ OPEN2OK
CALL ERXIT
DB '++ RE-OPENING EXTENT FAILED',0DH,0AH
DB '++ FILE IS CLOBBERED $'
;
OPEN2OK MVI A,7FH ;GET HI SECTOR
STA FCBRNO
RET
;
;Write back the sector
;
WRSECT LDA FCB+14
ANI 1FH ;RESET S2 FLAG FOR CP/M 2.x
STA FCB+14
PUSH B
PUSH D
PUSH H
MVI C,WRITE
LXI D,FCB
CALL BDOS
POP H
POP D
POP B
ORA A
RZ
CALL ERXIT
DB '++ WRITE ERROR - FILE CLOBBERED ++$'
;
;Get a Pseudo-Random 8 bit number using the password as a seed
;
; For speed, this routine does no register
; PUSHes and POPs, however HL aren't used.
;
PSEURAN MVI C,4 ;GRAB EVERY 4TH PSEU. #
;
; The following is done four times for each character in the
;file being scrambled. After four password shifts a value is
;returned in A to the calling routine.
;
PSEULP0 MVI B,8 ;SHIFT THRU 8 BYTES
LXI D,PASSWD
ORA A ;CLEAR INITIAL CARRY
;
; PSEULP1 shifts the 8 byte sequence of the password one bit to
;the right filling the left most bit with 0 (for the first of
;the four passes, after that bit may receive 0 or 1 from carry)
;and ending with the right most bit moved to the carry.
;
PSEULP1 LDAX D ;GET A CHAR
RAR ;SHIFT
STAX D ;Put shifted char back in place
INX D ;Point to next char
DCR B ;Count down
JNZ PSEULP1
;Exclusive-OR the last few bits into the first one
DCX D ;BACK UP TO LAST
RAR ;Shift the 8th byte twice more thru
RAR ; itself and the carry
XCHG
XRA M ;Mix treble shifted 8th byte in A with
; the single shifted 8th byte in M
RRC ;SHIFT LO BIT INTO HI discarding the
; carry bit (4th shift of 8th byte)
ANI 80H ;ISOLATE SINGLE BIT, A will contain
; either 80H or 00H
LXI H,PASSWD ;GET FIRST BYTE
ORA M ;'OR' IN THE BIT
MOV M,A ;MOVE IT BACK, whatever is in A when
; C=0 will be the value to be XORed
; with the next byte in the current
; sector. This value changes for each
; and every byte of the file.
XCHG ;RESTORE HL
DCR C
JNZ PSEULP0 ;LOOP IF MORE PASSES
RET
;
;Routine to check for duplicate chars in password
;
CKDUP MVI C,3 ;DUP CHAR COUNTER
LXI D,PASSWD
MVI A,8 ;CHAR COUNT
;
CKDLP PUSH PSW ;SAVE COUNT
LDAX D ;GET CHAR
CMP M ;DUP?
JNZ CKNDUP
DCR C ;COUNT DUPS
JNZ CKNDUP
STA DUPCHAR ;SAVE FOR PRINT
CALL ERXIT
DB '++ NO CHARACTER MAY APPEAR MORE '
DB 'THAN TWICE IN THE PASSWORD. ',0DH,0AH
DB ''''
DUPCHAR DB $-$,''' DOES IN YOURS ++$'
;
CKNDUP INX D
POP PSW ;GET COUNT
DCR A
JNZ CKDLP
RET ;OK, NOT 3 DUP
;
;Move subroutines
;
MOVER MOV A,M
STAX D
INX H
INX D
DCX B
MOV A,B
ORA C
JNZ MOVER
RET
;
;Exit with error message
;
MSGEXIT EQU $ ;EXIT W/"INFORMATIONAL" MSG
ERXIT POP D ;GET MSG
MVI C,PRINT
CALL BDOS
;
;Exit, restoring stack and return
;
EXIT LHLD STACK
SPHL
RET ;TO CCP
;
PASSWD DS 8 ;PASSWORD KEPT HERE
DS 40H ;STACK AREA
STACK DS 2
;
;BDOS equates
;
RDCON EQU 1
WRCON EQU 2
PRINT EQU 9
CONST EQU 11
OPEN EQU 15
CLOSE EQU 16
SRCHF EQU 17
SRCHN EQU 18
ERASE EQU 19
READ EQU 20
WRITE EQU 21
MAKE EQU 22
REN EQU 23
STDMA EQU 26
BDOS EQU 5
FCB EQU 5CH
FCB2 EQU 6CH
FCBEXT EQU FCB+12
FCBRNO EQU FCB+32
;
END