Splitting the Atom |
|
page 59-66 |
CHAPTER 9
METHODS FOR PREVENTION OF COPYING
Programs saved on tape by the Atom can be protected from
copying by several general methods:
- Prevent the program from being LISTed
- When the program runs it alters some part of the
machine's cassette operating system (COS)
- Load the main program using a 'preloader' program
involving some machine code.
Several techniques for each of these methods are given below.
Of course, there is no way to totally protect a program other
than by using a mathematical 'trap door' function, and these are
unsuitable for small machines. Thus any program can be copied if
a pirate has the right hardware and software, plus the skill, to
do the job. As with most things this is a two-edged sword, since
the techniques for preventing copying are the same as for
pirating.
In general, a machine-oriented chip such as DISATOM will allow
any of the protection techniques given to be overcome by a
skilled user. If you do not have one in your machine, see the hex
dump program in this book, which allows you to directly inspect
and modify memory. Although this is more awkward than using a
ROM, it is better than no tools at all.
The examples and techniques below are in order of increasing
complexity. In all cases the following symbols will be used, and
virtually ALL NUMBERS WILL BE HEX!!!
<...> |
= push this key, such as <space>, <CR> or
<CTRL-C>. |
[XX] |
= an actual byte in memory, as a hex number, e.g.
[0D] or [03 CD 9F] |
A) Using the REM statement
1. Start the program with:
10 REM <CTRL-L> <CTRL-C> <CTRL-U> <CR>
This clears the screen, turns off the printer, and turns off
the VDU screen. As you type <CTRL-U>, the screen is
disabled, but carry on typing the line, then type <CTRL-F>
<ESC> and the screen is re-enabled. Now any attempt to list
will send the control characters behind the REM to the print
stream, and they will take effect. However, a RUN is still OK,
since BASIC disregards anything after the REM. It is easier to
insert the control codes directly into memory using the DISATOM,
but this should be done after the program is completely
perfected. For example, type in
10 <space> <space> <space> REM <space> <CR>
Then type
a 2900 <CR> <REPT> <ESC> .
This will give an ASCII DUMP of memory at 2900 as follows:
a 2900 0D 00 0A .R .E .M 20 20
a 2908 20 .C .A .N .' .T 20 0D
Move the cursor up to the a 2900 and <copy> the line
over to the first 20. Change this and the next 20 to 0C 03, then
hit <CR> and <ESC>. In the same way edit the final 20
(before the 0D) to 15 (see the appendix on the DISATOM toolkit
for further details on its use). After editing, an ASCII dump
gives:
a 2900 0D 00 0A .R .E .M 0C 03
a 2908 20 .C .A .N .' .T 15 0D
Now any attempt to LIST will clear the screen, the word CAN'T
will appear in the upper let hand corner, and the printer and the
screen will be turned off.
Unfortunately, since it must be physically the first line in
the program, a pirate can overcome it simply by typing
0 <CR> L. <CR>
and if this has no effect he recovers the screen with:
<CTRL-F> <CR> followed by
1 <CR> L. <CR> and so on
This has the effect of eventually removing the BASIC line with
the offending REM statement in it. Alternatively, if the pirate
has DISATOM, he may do an ASCII dump and replace the 0C, 03 and
15 with 20's.
2. A REM statement can be used after a genuine BASIC
statement. The REM is followed by four backspaces [08], and then
some apparently legitimate BASIC statement such as X=3*A, then a
[15] (screen off). The line is best set up by typing out:
10 DIM XX(12); REM <3 spaces> X=3*A <space> <CR>
The first three spaces [20] are then replaced by [08]
(backspace), and the final [20] by [15] (screen off). When a list
is attempted, the following appears on screen:
10 DIM XX(12); REM X=3*A (screen fails)
The dimensioning of the array is genuine, but the second
statement is a fake. The purpose of all this is to convince the
user that the entire line is real, and leave him baffled as to
why the screen failed. It can of course be overcome by an ASCII
dump, which would reveal the REM, and it can then be removed.
However, if someone attempts to delete the entire line (as in the
last example) the entire program fails. You can see that there
are several possible twists in this technique.
A slight sophistication is to have another REM as the last
line of the program that reads
1000 REM [06]
Now when a LIST is called this results in
>LIST
10 DIM XX(12); REM X=3*A
>
and the program appears to have only one line. The technique
can again be defeated by an ASCII or HEX dump the reveals either
the first or last REM.
3. Machine code in a REM statement
Quite a lot of machine code can be put in a disguised REM
statement. As with the previous example, the first part of the
line is valid BASIC, but buried in the REM is some machine code
to be accessed by a later part of the program. Thus
40 x=6; ?1B=41;rem[7f 7f 7f 15 <m/c code here> 06 0D]
!! M/C CODE MUST NOT CONTAIN 0D !!
The first two instructions are valid and appear on LISTing.
The REM causes three backspacing deletes and turns off the screen
so your machine code is not seen, then turns it on again at the
end. The line can be placed anywhere in the program, but the
deeper the better, since this decreases the likelihood that
someone will stumble on it with an ASCII dump. The machine code
can be anything at all, but for example, it might alter the
SAVVEC of the COS system to disable the tape saving function.
There are two disadvantages to this method.
- you must exactly determine the entry point for the hidden
machine code (then get P exactly equal to that address
and have it assembled there), and
- you must eventually LINK to that address
Someone seeing a LINK into the BASIC text area will of course
be suspicious, and in any event all LINKs can be found with a
DISATOM using FIND "LINK" and FIND "LI.". It
is possible to access this code using another, less suspicious
machine code routine. Using this method without camouflage is a
good way to save short machine code routines within BASIC itself,
instead of having to assemble it each time, or using *SAVE to
ensure machine code outside BASIC test is also saved. To prevent
someone hitting <break> and then copying, site the BASIC
program to start at 2800, and then having a hidden REM that
contains NOP: RTS ([EA 60]) such that the NOP is at 2900 and the
RTS is at 2901. On one occasion in the program proper, LINK to
29000 and the RS as 2901. If a pirate breaks from the program and
copies it, the 2900,2901 machine code is lost, and the program
will crash. An even more effective way is to have 28FF = JMP
28XX, and somewhere in the 2800's is another REM containing an
RTS. Hitting <break> distorts the JMP location and the
program crashes. Indirect jumps can also be used, via an address
stored here. Once the program is running, it is easy to prevent
the <ESC> key operating by intercepting the code from the
keyboard and changing it. This is done by:
LDA @0; STA #B000 ENABLE the keyboard
JSR #FE94 GET a key in accumulator
CMP @#D IF a <CR> jump to DISABLE
BEQ P+8
CMP @32
BCS @32 IF >= <> jump to DISABLE
LDA @32 CHANGE code to a <space>
PHA
LDA @10; STA #B000 DISABLE keyboard again
PLA
RTS
Finally, spanner the vector at 20A, 20B to point at this
routine. The routine also prevents the entry of any other control
codes, some of which re-enable the <ESC> key. Remember to
set B000=10 as the first part of your BASIC program. This can be
beaten by causing an error, which will return the user to the
direct mode. To be safe you should therefore alter the BRK vector
at 202, 203.
4) The long Line
The BASIC interpreter is perfectly happy to work on a line
which is (almost) infinitely long, with the statements being
separated by semicolons. The practical consequences of that are
that
- the LIST command will turn back on itself (recycle from
the start) if the line greater than 258 bytes (two of
which are the line number), and
- if this is the first line in the program then BASIC is
unable to add any new lines or delete any old ones, since
it cannot find the end of the first line.
If the first line consists of something like P.; P.; P.; etc.
etc. for the whole of page 29, then the rest of the program
cannot be LISTed and the program cannot be edited, nor do the
command OLD or END work, since the real size of the program is no
unknown to the operating system. The real program can be
terminated with LINK #C2B2, which accomplishes a NEW, or a GOTO
X, where X is a real line number or label in the program, if you
wish to repeat the program. Below is a procedure for setting up
such a method, and it is given so that those without DISATOM can
also do it, given some extra work. Make sure that you program is
perfect BEFORE you protect it, and note that you have 258 bytes
less space for your real program
- DIRECT COMMAND:
F.I=#29000 to #2A048.4; !I=#3B202E50; N.
- DIRECT COMMAND
?18 = #2A
NEW
Now write and completely debug your program as normal,
but the FIRST LINE MUST BE 1REM <3 spaces> <CR>
- When your program is perfect give DIRECT COMMANDS:
?18 = #29
!#2900 = #5000000D
!#2904 = #3B20203E
!#2A00 = #3B202E50
- *SAVE the program in the usual way, remembering that
the total program does start at 2900.
B) Disabling the SAVE
This can be done by spannering the SAVVEC at 20E, 20F to point
at a different location. However, this is easy to spot. A much
more subtle method is to point the SAVVEC to a machine-code
routine that display the "RECORD TAPE", then waits an
appropriate amount of time, say 2 minutes. Of course, nothing
meaningful goes to the tape, but the pirate won't know this. Some
examples, which will easily fit in a hidden REM are given below.
a. JSR #FC40 print "RECORD TAPE", and wait for key
b. LDY @05
JSR #FB7D each Y is worth 2 seconds, so this
DEY section delays 10 seconds
BNE P-4
RTS TOTAL = 11 BYTES
Another possibility is
JSR #FC40 as before
JSR #FAF8 put a small amount of garbage on tape
then use part (b) from above.
C) Using preloaders
This technique involves using one program to call another from
tape. At the same time the first program should be accessible
only via machine code routes, should alter such things as the
SAVVEC, and then destroy itself.
Both the *SAVE and *LOAD commands can be carried out from in
program, and the usual prompts 'PLAY TAPE' etc can be avoided.
This is done by changing the SAVVEC and/or LODVEC to point at
your short routine (given below). Assume here that your saving
routine will be at 8350, and loading routine at 8300:
Vector Changes Your Program
-------------- ------------
*LOAD 20C = 00 PHP; JMP #F97A
20D = #83
*SAVE 20E = #50 PHP; JMP #FAF8
20F = #83
A BASIC program at 8000 (on the screen) can be used to perform
the actual *LOAD of the main program. Programs on screen are
convenient because P.$12,<BRK> etc. erases them. They are
best saved to tape by using yet another BASIC utility program,
say at 8500, which
- uses the DISATOM "COPY" command to place the
program on the screen, and then
- *SAVE the screen to tape.
The BASIC program on the screen should not be a runnable
program, but should contain the missing parts, deliberate errors
and misinformation. These will be corrected by a machine code
program located at 8200.
Initially the main program should have some title which turns
off the screen (to prevent *CAT), and should make reference to
some part of the machine-code preloader at 8200, so that it will
not run unless the machine-code has been run first. In summary,
what we find is this:
Address Program type
------- ------------
2900 and up Main program
8000 Preloader, BASIC part
8200 Preloader machine code ONE PROGRAM
8300 *LOAD spannering program
8350 *SAVE spannering program
8500 BASIC utility program to save on tape the main and preloader programs.
Given below are example of each program.
Program type: Main program Location: 2900 and up Main program
Program title: "[15 03]MAIN"
10 !#80=#C98046AD; !#84=#AD0FD04A; !#88=#0DC98000; !#8C=#15AD08D0
20 !#90=#D000C902; !#94=#B24C6001; ?#98=#C2
30 LI.#80
40 CLEAR 2; P.$12
50 REST OF PROGRAM FROM HERE ON
Lines 10 and 20 write a machine code program, and 30 links to
it. The purpose is to ensure that the machine-code preloader has
already been run. The code reads as:
80= LDA 8046 ; CMP @4D; BNE 96
LDA 8000 ; CMP @0D; BNE 96
LDA 0215 ; CMP @0D; BNE 96
RTS
96= JMP C2B2
Finally, line 30 erases the entire preloader program.
Program type: Preloader, BASIC part Location: 8000
Program title: "LOAD" 8000 8255 8200
NOTE:
- Deliberate errors are underlined,
- you MUST fill in the values for the TOP vector of
your main program at 13,14
in line 30 of this program (given here as XX XX).
[0D FF 00] !#80=03902000; !#84=#4C81FF8D; !#88=#FBEE; !#214=#FC7C0080
20 *LOAD"<3 spaces> AIN"
30 ?1B=41; ?13=XX; ?13=XX; LI. #FE86
This program should originally be written with the first line
numbered as, say, line 5. You will later change this with DISATOM
so that the area of memory storing this line number will read [0D
00 05]. This is of course creating an error, but the machine code
part of the preloader will correct this to [0D 00 00], which
BASIC interprets as the start of the program, line 0. The effect
of the line when it runs is to set up machine code at #80, and
then spanner the tape byte-getting routine to point at it. This
causes the bytes loading from tape to appear in the lower right
of the screen, thus visually confirming further LOADs, and then
passes the byte to the correct location. Line 20 LOADs the main
program into 2900. The title starts out as "<3
spaces>AIN". The machine code preloader will change the
three spaces so the title reads "[15 03] MAIN". The
title is therefore actually "TURN OFF SCREEN, TURN OFF
PRINTER,MAIN", and so it cannot be *CATed, nor can the title
or the rest of the program be LISTed once it is altered to its
final form. Line 30 changes the BASIC text pointer to 2900, sets
the value of TOP for the main program (you MUST provide this),
then starts the main program at 2900. NOTE - if the main program
contains DIM statements, you must first set the DIM pointer (at
#23, 24) to the value of TOP, somewhere in the main program
before the DIM statement.
The previous BASIC program has several deliberate errors to
hinder copying and relocation. Here is a summary.
ERROR BYTE AT BECOMES COMMENT
---------- -- ------- -------
FF 8001 00 BECOMES LINE NUMBER 0
30 800F 43 ALTERS MACHINE CODE
30 8010 39 FROM 00 TO C9
20 8044 15 CONVERTS PROGRAM LOAD TITLE
20 8045 03 TO
20 8046 4D "[15 03]MAIN"
46 806C 43 ALTERS LINK FROM FE86 TO CE86
PROGRAM TYPE: Preloader, machine code part LOCATION: 8200
PROGRAM TITLE: same as BASIC part, all saved as one program
LOCATION SOURCE OBJECT REM
-------- ------ ------ ---
8200 LDA @08 A9 08 WRITE MACHINE CODE
STA #8300 AD 00 83 TO ENABLE
LDA @#4C A9 4C *LOAD BY BASIC
STA #8301 AD 01 83 PRELOADER
LDA @#7A A9 7A
STA #8302 AD 02 83
LDA @#F9 A9 F9
STA #8303 AD 03 83
8214 LDA @00 A9 00 FIX DELIBERATE
STA #8001 AD 01 80 ERRORS IN BASIC
LDA @#43 A9 43 PRELOADER
STA #800F AD 0F 80
LDA @#39 A9 39
STA #8302 AD 02 83
LDA @#15 A9 15
STA #8303 AD 44 80
LDA @#03 A9 03
STA #8045 AD 45 80
LDA @#4D A9 4D
STA #8046 AD 46 80
LDA @#43 A9 43
STA #806A AD 6A 80
8237 LDA @00 A9 00 SPANNER LODVEC
STA #020C AD 0C 02 TO POINT AT
LDA @#83 A9 83 OUR PROGRAM AT 83
STA #020D AD 0D 02
8241 LDA @40 A9 40 SPANNER SAVVEC
STA #020E AD 0E 02 TO PRINT MESSAGE
LDA @#FC A9 FC THEN FAIL
STA #020F AD 0F 02
824B LDA @80 A9 80 SPANNER TEXT
STA #12 85 12 POINTER AND JUMP
JMP #CE86 4C 86 CE TO BASIC LAST BYTE AT 8251
Now to construct the entire preloader program:
i) Use the DIRECT COMMANDS
F.I=#8200 TO #8600; ?I-32;N.
?18= #82
NEW
ii) Type in the BASIC part of the preloader, as mentioned
above, including the errors. Start with line 5. When finished,
type NEW, then alter the program title in line 20 using either
HEX DUMP or a DISATOM ASCII dump.
iii) Use the DIRECT COMMANDS
?18=#85
NEW
iv) Construct the SOURCE code give for the machine code part
of the part of the preloader here at #8500, with P=#8400.
Needless to say, a program combining ALL the techniques listed
her will be a truly formidable program to pirate.
|