💾 Archived View for mirrors.apple2.org.za › archive › ground.icaen.uiowa.edu › apple8 › Pgms › apple… captured on 2023-01-29 at 10:54:05.

View Raw

More Information

-=-=-=-=-=-=-

Path: news1.icaen!news.uiowa.edu!chi-news.cic.net!mr.net!news.maxwell.syr.edu!cpk-news-hub1.bbnplanet.com!news.bbnplanet.com!cam-news-hub1.bbnplanet.com!news.mathworks.com!uunet!in2.uu.net!165.113.1.21!nntp.crl.com!nexp.crl.com!crl12.crl.com!not-for-mail
From: dastar@crl.com (Sam Ismail)
Newsgroups: comp.sys.apple2.programmer
Subject: Locking Applesoft programs with AppleLock
Date: 5 Jan 1997 21:34:32 -0800
Organization: CRL Dialup Internet Access	(415) 705-6060  [Login: guest]
Lines: 458
Message-ID: <5aq2t8$jc@crl12.crl.com>
NNTP-Posting-Host: crl12.crl.com
X-Newsreader: TIN [version 1.2 PL2]




    A Better Way to Protect Your AppleSoft BASIC Programs with AppleLock

                              by Sam Ismail
                             January 5, 1997


:Introduction:

    I devised this method of protecting AppleSoft BASIC program listings
some years back during my prime Apple programming days.  It involves a
combination of several different techniques for keeping your program
unlistable by the casual to intermediate user.  This system is no match
for advanced users who understand the internal structure of BASIC, for
they could no doubt blast through this protection with little difficulty.
Hence, this program is suitable for protecting programs which you wish to
keep hidden from users who are less than expert Apple ][ hackers.

    The following is a technical explanation of the AppleLock protection
scheme.  If you don't care to read it, you may want to skip to the end of
this document to get the source code to the AppleLock program.  This
discussion will not stop to explain in detail the advanced AppleSoft BASIC
and machine language techniques that are employed in this scheme as there
are plenty of FAQs around to explain the finer details of the tricks
involved.


:AppleSoft BASIC Internal Program Storage:

    AppleSoft BASIC stores your program in memory by tokenizing the
keywords in your program into one byte values.  So for instance, when
you type the follwing line:

10 PRINT "HELLO WORLD!"

the BASIC interpreter converts this into the following bunch of numbers in
memory (shown in hexadecimal):

15 08                                Pointer to program data for next line
0A 00                                Line number (in this case, 10)
BA                                   The token for PRINT
22                                   The opening quotation mark
48 45 4C 4C 4F 20 57 4F 52 4C 44 21  The string HELLO WORLD!
22                                   The closing quotation mark
00                                   The end of line terminator

    If this was the first line of the program, it would be stored starting
at memory location $0801 (or decimal 2049).  As shown above in the first
line of bytes, the next program line data would be stored at memory location
$0815 (addresses are usually stored backwards in memory, which is the way
the 6502 CPU reads addresses, so in this case 15 08 is $0815).


:Some Simple Protection Schemes:

    One of the simplest and most widely known tricks for protecting your
program is to POKE a 1 at memory location $0801 (POKE 2049,1).  This will
trick AppleSoft into thinking that the next line of the program is at
$0801 instead of $0815.  This will have the effect of listing the first
line of your program over and over again, indefinitely, until the user
presses Control-C to stop the display.  Unfortunately, this trick is not
permanent.  If you SAVE your program in this condition and then LOAD it
into memory again, AppleSoft will "fix" the pointer and make it point
to the next line again.  So this trick only keeps your program secret as
long as the user always runs your program before doing anything else with
it (ie. LISTing it) and as long as you perform this POKE as the first
command inside your program.  Not a very secure method.

    Another well known trick for keeping your programs secret is to set
a flag in AppleSoft which has the effect of ignoring all AppleSoft BASIC
commands at the command line and running your program instead, regardless
of what the user types (with the exception of DOS commands which ignore the
RUN-only flag and execute regardless of its status).  This flag is turned
on by POKE-ing any value greater than 127 at memory location 214
(eg. POKE 214,255).  The flag is turned off by poking any value less than
128 at the same location (eg. POKE 214,0).  Again, this method is not
effective because your program, or some other program that runs before
your program, must set this flag before the user has a chance to list
your program.  Otherwise, the user could simply load and list your program
before it has a chance to set this flag.

    So what are we to do?  Well, unfortunately our options are limited
and ineffective unless we know some machine language and a little bit
about the AppleSoft BASIC architecture.  That's where AppleLock comes in.


:Technical Overview of AppleLock:

    The key to this scheme is adding a special line to the beginning of
your program which acts as a gateway to your program listing.  This first
line will block the rest of your program from being viewed.  It seems
benign enough to the naked eye.  Yet it is performing some special
magic to enable your program to run normally and be copied to another
disk, but never listed.

    This scheme uses a machine language subroutine to change the pointers
of your program to where your program really is.  This machine code is
stored right inside the first line, but it is hidden from the user by
taking advantage of some features of AppleSoft BASIC.  AppleSoft allows
you to embed control characters in your program listings, for instance,
in REM statements.  When your program is LISTed, these control characters
are interpreted and output to the display just like in any other
circumstance.  For instance, if you stuff a Control-H (or backspace)
character in a REM statement, it will be interpreted as a backspace when
the line is listed, and the character previous will be over-written by the
character following the backspace.  There are several programs available
to allow you to create fancy listings by embedding backspace characters
into your REM statements in this manner.  The following will demonstrate
the effect an embedded backspace will have when your program is listed:

LIST

10 REM  A CHARACER WILL BE MISSING
                ^
                The backspace occurred here.  The 'T' was printed, but it
                was followed by a backspace character, so the 'E' over-
                wrote the 'T'.

Going back to our protection scheme, we must be able to CALL our machine
language subroutine which unlocks the rest of the program, but we don't
want the user to know what we are doing to unlock the program.  So to
hide the CALL command, we follow it with a REM statement with enough
backspace characters to over-write the CALL command.  As a side benefit,
we can now put whatever message we want as the first line of the program,
such as "THIS PROGRAM CANNOT BE LISTED".  Or, we can get real tricky
by displaying what looks like a program line, but in actuality is some
dummy text which is hiding the real program line underneath, such as
"10 END              " (we must put sufficient spaces to over-write both
the CALL and REM commands).  To illustrate this more effectively, the
following is an example of a raw line before we embed the backspace
characters:

10  CALL 2100: REM *******************[ CAN'T LIST THIS! ]

Now we would replace all the asteriks with a backspace character (ASCII 8).
The easiest way to do this is from the monitor.  In this case, we would
find the first asterik at memory location $080C.  We could then replace
the asteriks with backspace characters by doing the following:

CALL -151
80C: 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8

This now changes the 19 asteriks into backspace characters.  Nineteen
backspace characters is enough to backspace all the way back to the
line number at the beginning of the line.  If our line number was 1, we
would only need 18 backspaces.  If our line number was 100, we would need
20 backspaces.  If we had other commands after the CALL command, we would
need enough backspace characters to backspace over these commands as well,
including the formatting spaces AppleSoft prints when it displays your
program listing.

    We have now completed the first step.  We have created a line that
when listed will only show our message.  Now we must create some room
for our machine language code which unlocks the program when it is CALLed
by the hidden command.  To do this, we will add extra asteriks after our
message so that we can replace these asteriks with machine code and have
the code stored right inside the BASIC program.  We must store the code
right inside the BASIC program so that it automatically gets loaded when
we load our program.  Otherwise, if the code was stored in a separate
file, the user would be able to stop the program before it had a chance
to unlock itself.  This would enable the user to possibly figure out
what was going on, and we don't want that.

    In order to do all this, we must hide our machine code from BASIC.
Otherwise, BASIC will try to interpret our machine code as BASIC tokens
when we list our program and will throw garbage all over the screen.
First, let's create the program line we will need to hide our CALL command
as well as reserve enough bytes to store our machine language unlock
code.  We will also add another program line for demonstration purposes
later in the tutorial.

10  CALL 2100: REM *******************[ CAN'T LIST THIS! ]*********
20  PRINT "HELLO WORLD!"

We have added some asteriks after our message to create the space we will
use to store our machine code.  There are two extra bytes that will be
used to hide this machine code from BASIC as well as to fool BASIC into
thinking our program is shorter than it really is, thus hiding the rest
of the program.  We must now jump back into the monitor to embed our
backspace characters and then insert our machine code:

CALL -151
80C: 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8      Embedded backspaces
834: A9 3D 85 67 4C 65 d5                       Our machine language code

The machine language code disassembles to the following instructions:

834: A9 3D       LDA #$3D         ; Get the true start of our program
836: 85 67       STA $67          ; and tell AppleSoft where it really is
838: 4C 65 D5    JMP $D565        ; Now run the real program

Basically, this code tells BASIC to jump over our first decoy line and
then start running our real program.  As you may have guessed, the CALL
command that is at the beginning of our decoy line calls this code.  Of
course, the user doesn't see this CALL command because it is over-written
by our message.

Now we must hide our machine code from BASIC, or else when we list our
program we will see a bunch of garbage.  The machine code actually started
at the second asterik after our message.  We will replace the first
asterik with a zero byte.  A zero byte indicates to BASIC that it has
reached the end of the program line, and should go on to list the next
line.  When we input a line, BASIC always puts a zero byte at the end of
our line before starting a new line.  So after the last asterik in our
line is a zero byte.  But we will also replace the last asterik in our
line with another zero byte, resulting in two consecutive zero bytes.
Two consecutive zero bytes tells BASIC that it has reached the end of the
program, and it should stop listing.  We are now going to change the
internal pointers of our decoy line to point to these zero bytes so that
we can fool BASIC into thinking our program is only one line long!

833: 0                  Replace the first asterik with a 0 byte
83B: 0                  Replace the last asterik with a 0 byte
                        ($083C has another zero byte already)
801: 3B                 Change the internal pointer to point to the two
                        zero bytes, fooling BASIC into thinking it has
                        reached the end of the program

Now when we list our program, we will see the follwing:

[ CAN'T LIST THIS! ]

But when we run it, we will see the following:

HELLO WORLD!

Cool, eh?  Now, before we end our program, we must make sure we lock it
back up so that we don't defeat our protection scheme by leaving the
door open.  You can add this line to your program to have it change the
pointer to the start of our program back to our decoy line:

30  POKE 103,1

Now this program will run and then re-lock itself when it's done.  And
now hopefully you understand the basic premise of AppleLock.


:Additional Security Requirements:

    As the programmer, you must also take several steps within your
program to ensure nobody can break out of it by hitting Control-C.  And
we also have to contend with that darn reset button.  First though,
let's take care of Control-C.

    To prevent the user from trying to break out of the program and at
the same time trap any unexpected errors in your program, make sure the
FIRST line of your program is an ONERR GOTO command.  ONERR GOTO allows
you to tell BASIC to jump to any line in your program whenever an error
occurs, and Control-C is considered an error (its error code is 255).
The simplest way to handle errors and Control-C attempts is with the
following lines:

10 ONERR GOTO 63999
<your program>
63999 RESUME

These lines will in effect cause the program to ignore any errors,
including attempts to break out of the program by the user.  There is
still a very minor chance that the user could time a quick Control-C
right when the machine language unlock program transfers control to your
unlocked program, but before you issue the ONERR GOTO command or possibly
set the AppleSoft RUN-only flag (POKE 214,255), thus allowing the user to
break into your program.  The AppleLock program at the end of this tutorial
addresses this problem.

    Now let's take care of the reset button.  The simplest way to prevent
a person from using RESET to gain access to your program is by adding a
simple POKE command at the beginning of your program, as follows:

POKE 1010,0

This will cause the computer to re-boot whenever the reset button is
pressed.  We could get fancy and re-hook the reset vector so that when
the user presses RESET we can restore our program lock and then return
them to BASIC.  This is exactly what the AppleLock program at the end
of this tutorial does.

    Before your program terminates and returns to the command prompt,
it must undo the RUN-only flag (if you have set it) and then re-lock your
program.  The following program line should be the last thing your program
does before it exits:

63999 POKE 103,1:POKE 214,0

The first POKE re-locks your program.  The second POKE turns off the
AppleSoft RUN-only flag.  You should also return the reset vector to
its original setting if you have modified it.  Otherwise, the next time
the user presses RESET, your program will run again.  If another program
was loaded and then RESET was pressed, the system will most assuredly
crash.  AppleLock includes a routine which you can call at the end of
your program which will restore the reset vector as well as the AppleSoft
RUN-only flag and then re-lock your program before exiting.


:AppleLock Effectiveness Analysis:

    Unfortunately, the weakest point of this scheme is that it takes only
one well-placed POKE to unlock your program.  As an aside, the user can
find out what you are trying to hide by changing the character output
speed with a SPEED= command.  For instance, by setting SPEED=0, and then
LIST-ing the program, the user would be able to see that there is a CALL
command hidden behind your message.

    The best and most secure way to protect your program from prying
eyes remains the use of a BASIC compiler.  However, this is not always a
feasible solution.  There are a few compilers for DOS 3.3 that will convert
your program to machine language.  And there is the Beagle Compiler for
ProDOS, but this compiler does not create a stand-alone machine language
program, but rather requires an interpreter that replaces BASIC.SYSTEM.


:Conclusion:

    Hopefully this tutorial has given you enough information to help
you protect your AppleSoft BASIC programs for whatever reason you may
have.  Enjoy.


:The AppleLock Program:

    This program is very rudimentary and not very pretty.  A nice
interface can be built around this program to make it more user friendly.
The important thing is that it is functional.  What it does is creates
the machine language unlock code by POKE-ing it into memory.  It then
asks you to enter the name of the program you wish to lock, as well as
the line number you wish to use as your decoy line.  This must be the
first line in your program!  After that, it allows you to enter a message
up to 160 characters in length (the length of the message is fairly
arbitrary, but this length was chosen to keep the program simple).  Once
you get good at this technique, you will want to use other programs to
create your message (or perhaps use the monitor like I do) so that you
can have fancy banners show up when you try to list the program.  It
then builds a text file with all the commands necessary to apply AppleLock
to your program.  This file will then be EXEC-ed so that the AppleLock
procedure will be performed automatically.  Once the process is complete,
your program will be loaded in memory and in a locked state (if you try
to LIST it you will only get the message you typed in).  At this point,
you should SAVE your program to disk to make the process permanent.  The
program will give you instructions on how to lock/unlock your program so
that you, as the author, can list and modify it as you please.

    Keep in mind that you must unlock your program before you make any
changes to it, otherwise you stand the chance of royally screwing your
program and possibly crashing your machine.  Weird things can happen when
you start to mess with AppleSoft program pointers.  If this happens,
simply reboot your computer and re-load your program, and all will be well
again.  If you ever want to remove AppleLock from your program, simply
unlock it and then save it to your disk (be sure to restore the BASIC
start-of-program marker with a POKE 103,1 after you are done saving).
When you load your program again, it will be back to normal.

    The AppleLock program includes an enhanced locking mechanism which
will modify the reset vector and also sets the AppleSoft RUN-only flag
by POKE-ing a 255 at location 214 before it transfers control to your
program.  This will prevent users from listing your program should it
fail and drop to the command prompt at some point, or the user presses
Control-C and you haven't added an ONERR GOTO command to your program.
If the reset button is pressed, the locking mechanism will re-lock the
program, reset the AppleSoft RUN-only flag, restore the reset vector
to its original value and then return to the command prompt.


:Disclaimer:

    This program may be freely modified, distributed, sauteed, fricasseed,
lambasted, spindled, mutilated, desecrated, tormented and forced to cry
"uncle".  It may not, however, be recruited into sado-masochistic rituals
unless it has consented to participate in such activities.  Niether myself,
my immediate family, nor the code (it's just code, it doesn't know any
better) can be held responsible for any havoc it wreaks on your AppleSoft
programs.  This program has been run and tested several times and has been
deemed sound by the author.  If you do somehow get yourself in a bind, I
can be reached via email (dastar@crl.com) and I will try to help you
reclaim your lost code.  On that note, I leave you with these words to
live by: Always Keep a Backup!


------- cut here -------

 0  REM  APPLELOCK SCHEME (C) 1997 BY SAM ISMAIL
 10  TEXT : HOME 
 20 DOS$ =  CHR$ (4)
 30  INPUT "WHAT IS THE NAME OF YOUR PROGRAM? ";P$
 40  INPUT "WHAT IS THE FIRST LINE OF YOUR PROGRAM? ";LINE
 50 AS = 17 +  LEN ( STR$ (LINE))
 60  PRINT : PRINT "ON THE LINE BELOW, TYPE WHAT YOU WOULD"
 70  PRINT "LIKE TO BE DISPLAYED WHEN SOMEONE TRIES"
 80  PRINT "TO LIST YOUR PROGRAM (IT MUST BE AT"
 90  PRINT "LEAST ";AS;" CHARACTERS LONG FOR BEST"
 100  PRINT "RESULTS)"
 110  PRINT : INPUT ":";T$
 120  IF  LEN (T$) < AS THEN  PRINT : PRINT "TOO SHORT, TRY AGAIN...": GOTO 60
 130  IF  LEN (T$) > 160 THEN  PRINT : PRINT "UNFORTUNATELY, THAT IS TOO LONG.": PRINT "TRY AGAIN...": GOTO 60
 140  PRINT : PRINT "HANG ON A SECOND..."
 150  FOR X = 1 TO 60: READ BYTE: POKE 767 + X,BYTE: NEXT X
 160 L =  LEN (T$)
 170 BASE = 2059 + AS + L + 1:S = BASE + 1:E = BASE + 60
 180 L1 = BASE + 5:H1 = BASE + 11
 190 L2 = BASE + 14:H2 = BASE + 19
 200 P = BASE + 31:R = BASE + 37
 210 A1 = R + 1:A2 = R + 6
 220  PRINT DOS$;"OPEN APPLELOCK.CMD"
 230  PRINT DOS$;"CLOSE APPLELOCK.CMD"
 240  PRINT DOS$;"DELETE APPLELOCK.CMD"
 250  PRINT DOS$;"OPEN APPLELOCK.CMD"
 260  PRINT DOS$;"WRITE APPLELOCK.CMD"
 270  PRINT "LOAD ";P$
 280  PRINT  STR$ (LINE);" CALL 0000:REM";
 290  FOR X = 1 TO AS: PRINT "*";: NEXT X
 300  PRINT T$;
 310  FOR X = 1 TO 62: PRINT "*";: NEXT X: PRINT 
 320  PRINT "FOR X=1 TO 4:POKE 2053+X,ASC(MID$("; CHR$ (34);S; CHR$ (34);",X,1)):NEXT X"
 330  PRINT "FOR X=1 TO "; STR$ (AS);":POKE 2059+X,8:NEXT X"
 340  PRINT "POKE "; STR$ (BASE);",0"
 350  PRINT "FOR X=0 TO 59:POKE "; STR$ (S);"+X,PEEK(768+X):NEXT X"
 360  PRINT "POKE "; STR$ (L1);","; STR$ (A1 - 2048)
 370  PRINT "POKE "; STR$ (H1);","; STR$ (A2 - 2048)
 380 H =  INT (R / 256):L = R - (H * 256)
 390  PRINT "POKE "; STR$ (L2);","; STR$ (L)
 400  PRINT "POKE "; STR$ (H2);","; STR$ (H)
 410  PRINT "POKE "; STR$ (P);","; STR$ (E - 2047 + 2)
 420  PRINT "POKE "; STR$ (E + 1);",0"
 430  PRINT "POKE 2049,"; STR$ (E - 2047)
 440  PRINT "?"; CHR$ (34);"YOU ARE NOW READY TO ROCK!"; CHR$ (34)
 450  PRINT DOS$;"CLOSE APPLELOCK.CMD"
 460  HOME 
 470  PRINT "YOUR PROGRAM WILL NOW GO THROUGH"
 480  PRINT "THE PROTECTION PROCESS.  WHEN IT"
 490  PRINT "IS DONE, MAKE SURE YOU SAVE YOUR"
 500  PRINT "PROGRAM BEFORE YOU DO ANYTHING!!"
 510  PRINT : PRINT "TO UNLOCK YOUR PROGRAM, TYPE:"
 520  PRINT : PRINT "POKE 103,"; STR$ (E - 2047 + 2)
 530  PRINT : PRINT "TO LOCK IT AGAIN, TYPE:"
 540  PRINT : PRINT "POKE 103,1"
 550  PRINT : PRINT "ADD THIS COMMAND TO THE END OF"
 560  PRINT "YOUR PROGRAM TO AUTOMATICALLY"
 570  PRINT "RE-LOCK YOUR PROGRAM:"
 580  PRINT : PRINT "CALL "; STR$ (R)
 590  PRINT : PRINT "MAKE SURE YOU SAVE YOUR PROGRAM"
 600  PRINT "ONLY WHEN IT IS LOCKED!"
 610  PRINT : PRINT "NOW PRESS A KEY TO DO IT..."
 620  GET A$: PRINT 
 630  PRINT DOS$;"EXEC APPLELOCK.CMD"
 1000  DATA  173,242,3,141,0,8,173,243
 1010  DATA  3,141,0,8,169,0,141,242
 1020  DATA  3,169,0,141,243,3,32,111
 1030  DATA  251,169,255,133,214,169,0,133
 1040  DATA  103,76,102,213,169,0,141,242
 1050  DATA  3,169,0,141,243,3,32,111
 1060  DATA  251,169,0,133,214,169,1,133
 1070  DATA  103,76,208,3
-- 

Sam
-------------------------------------------------------------------------------
Computer Historian, Programmer, Musician, Philosopher, Athlete, Writer, Jackass