HOW TO HACK

By Jon North

Sinclair ZX Spectrum
ISSUE 56
For the last couple of months, I've been showing you how to hack infinite lives out of games. From now on, I'll be concentrating on showing you how to crack the protection systems, so that you'll be writitng Multipokes before you know it.
LOADING THE FIRST BIT
The first program on any game tape is called the loader - because it loads the rest of the program (logical, huh?). To crack any protection system, however simple, you need to keep track of what it's doing at all times.
The *LOAD Routine
This program is a special loading routine. Instead of typing LOAD "" to load the loader, type RANDOMIZE USR 30000. This will load the basic program and stop with the OK message. When it loads, it displays the filename, the start line of the program (this is usually 0,1 or 10) and it's length.

Listing 1

10 REM *Load by Jon North
20 LET t=0
30 FOR f=3e4 to 30083
40 READ a: POKE f,a
50 LET t=t+(f-29990)*a: NEXT f
60 IF t<>544506 THEN STOP
70 PRINT "Data O.K.": STOP
80 DATA 221,33,0,80,17
90 DATA 17,0,175,55,205
100 DATA 86,5,48,240,221
110 DATA 126,239,183,32,236
120 DATA 62,2,205,1,22
130 DATA 33,1,80,6,10
140 DATA 126,215,35,16,251
150 DATA 62,202,215,221,70
160 DATA 253,221,78,252,205
170 DATA 43,45,205,227,45
180 DATA 221,54,253,255,62
190 DATA 32,215,62,177,215
200 DATA 221,70,251,221,78
210 DATA 250,205,43,45,205
220 DATA 227,45,62,13,215
230 DATA 42,83,92,221,46
240 DATA 0,195,115,8
DISGUISING BASIC AND GETTING PAST IT
Unfortunately, what you see and what you get with basic programs are not always the same thing. Type in this one line and RUN it:
10 PRINT 10
Surprise surprise, 10 comes up on the screen. Now type in directly,
LET A=PEEK 23635+256*PEEK 23636: POKE A+5,50
If you now list the program, it will read 10 PRINT 20, but if you run it, it still prints 10. Every time a number is put in a program, two copies of it are stored. The first is what is listed, the second it what is actually used.
The *LIST Program This routine is a special list routine. Use RANDOMIZE USR 30085 instead of LIST to use it. What it does is show you the program as it would be run, that is, it strips away all the disguises and reveals the true program.

Listing 2

10 *List by Jon North
20 LET t=0
30 FOR f=30085 TO 30200
40 READ a: POKE f,a
50 LET t=t+(f-30075)*a: NEXT f
60 IF t<>919527 THEN STOP
70 PRINT "Data O.K.": STOP
80 DATA 62,2,205,1,22
90 DATA 42,83,92,229,237
100 DATA 91,75,92,55,63
110 DATA 237,82,124,181,225
120 DATA 200,70,35,78,35
130 DATA 229,205,43,45,205
140 DATA 227,45,225,78,35
150 DATA 70,35,229,9,34
160 DATA 254,255,225,126,254
170 DATA 13,32,4,35,215
180 DATA 24,212,254,46,40
190 DATA 8,254,58,48,19
200 DATA 254,48,56,15,68
210 DATA 62,14,237,177,205
220 DATA 180,51,229,205,227
230 DATA 45,225,24,220,254
240 DATA 32,56,2,215,126
250 DATA 254,234,32,8,62
260 DATA 13,215,42,254,255
270 DATA 24,167,254,34,32
280 DATA 12,35,126,254,32
290 DATA 56,2,215,126,254
300 DATA 34,32,244,35,24
310 DATA 183
HEADERLESS FILES
This is probably the simplest form of protection you can get. When a block of data loads, it comes in two chunks - a short one, which holds things like the filename and the length of the block, then the block itself. This first short bit is called the header, and the idea of headerless files is, surprise surprise, to be able to do without this header. It does this by holding all the information needed about the block (namely where to load it to and its length) in a machine code program, which uses this information to load the file.
What A Headerless Loader Looks Like
To load a headerless block, a short bit of code is needed:
LD IX, start address
LD DE, length of block
LD A,FF
SCF CALL 0556
RET or JP
That's it! If you look at most of my more complex hacks, you'll see they start off with 221, 33, n, n, 17, n, n, 62, 255, 55, 205, 86, 5, 48, 241; this is the code to load the basic program as a headerless file (because it can't be MERGEd).
Example: Falcon Patrol II
When you *Load the basic, you'll see:
FP2 LINE 99 LEN 800
You now know that the program starts from line 99. When it's loaded, *List it:
99 CLEAR 65367: RANDOMIZE USR (PEEK 23637+256*PEEK 23638)+5
100 REM
To find out the value of the RANDOMIZE USR, find the address of the actual commands (use the TEXT feature of your dissassembler, or search for bytes F9 C0) and change it to PRINT i.e. F5 20. Now RUN the basic and it will display the value of the USR command. Your value may be different to mine, but I got 23825, which is 5D11 hex.
Dissassemble this address:
LD HL,000F
LD DE,F000
ADD HL,BC
LD BC,256
LDIR
JP F000
Taking this line by line:
HL=15,DE=61440.
Whenever you do a USR command from Basic, the BC register holds the value of that USR, so here, BC=23825.
The ADD HL,BC command means LET HL=HL+BC, so HL=23825+15=23840 (5C20). BC then becomes 256 and then you come across a special instruction, LDIR.
What this does is to move BC bytes from HL to DE. For instance, to move a screen from 32768 (the screen is 16384-23295), you'd do LD HL,32768: LD DE,16384: LD BC,6912: LDIR
Here, the LDIR is from 23840 to 61440, for 256 bytes. Then it JP's to 61440. Put a breakpoint over the JP with your dissassembler, RUN the program and dissassemble 61440.
F000:
LD IX,4000
LD DE,1B00
XOR A
CALL F04E
LD IX,6800
LD DE,6000
XOR A
CALL F04E
LD A,0F
LD (5C8D),A
LD (5C48),A
LD A,01
OUT (FE),A
LD A,195
LD (5C37),A
LD BC,1400
XOR A
IN A,(1F)
OR C
LD C,A
DJNZ F02A
AND 192
JR Z,F049
XOR A
LD (9EAE),A
JP B110
We can see that a block is loaded 4000,1B00 and another 6800,6000. Note that the CALL is different (F04E, not 0556) and the value of A is also different (0, not FF). This is simply because the loader is using a different routine to do the actual loading (a turboloader, not the normal speed ROM loader). The JP at F039 starts the game, so put a breakpoint there and load the game (make sure your dissassembler is out of the way, or the game will be loaded over it).
Hacking the game, the LD A,5 (5 lives) is at 45362; the lives store is 40549, which is referenced at 40550,40562,45364 and 45565. The routine at 40550 is:
LD HL,40549
ADD A,(HL)
LD (HL),A
Turning the LD (HL),A into a 0 (at 40554) gives infinite lives.
Coming back to the loading system, the Basic can be MERGEd, and you can work out the address of the JP in the basic program before it is moved to F039. You know that 5C20 goes to F000, so 5C20+39 goes to F039, ie 5C59. The final hack is:
100 MERGE ""
110 POKE 23897,201
120 RANDOMIZE USR 23825
130 POKE 40554,0
140 RANDOMIZE USR 45328
(The 45328 came from the JP B110 at the end of the loader). However, the address of basic programs varies (eg. if Microdrives are connected), so the 23825 and 23897 are expressed in terms of the start of Basic. Also, the original program did a CLEAR 65367 before the USR, so we also need to include that. Hence:
100 CLEAR 65367: MERGE ""
110 LET a=PEEK 23635+256*PEEK 23636
120 POKE a+142,201
130 RANDOMIZE USR (a+70)
The variable a is the start of basic. The rest of the hack would be the same because the game is always loaded to the same place regardless of where Basic is.
The Ballbreaker II hack is in this month's Pokes - see if you can work out how I did it (you shouldn't have too much difficulty, but bear in mind the Basic cannot be MERGEd).
Next month, I'll be discussing decryption, and using it, together with this month's column, to crack Firebird Bleepload. If you hit any problems, drop me a line at Jon North, HTH, YS, 30 Monmouth Street, Bath, Avon. If you don't send an SAE you definately won't get a reply, but you should do if you do. 'Til next month, if it loads - hack it!
Sinclair ZX Spectrum

  Previous Page Back Next Page