HOW TO HACK

By Jon North

Sinclair ZX Spectrum
ISSUE 62
INTRO? WHAT INTRO? (continuation of issue 61)

SPEEDLOCK 4
This is where identification starts getting difficult. All the following Speedlocks look exactly the same when loading, and you only know what version you are doing when you start getting into it. All I can safely tell you is that the original releases of Firefly, Rastan, Gutz, Star Paws, Arkanoid II and Target Renegade had Speedlock 4 on them (although rereleases may be different). I'll be doing Arkanoid II as an example. First off, *Load and *List as usual.
ARKANOIDII LINE 0 LEN 3452 0 RANDOMIZE USR ((PEEK 23635+256*PEEK 23636)+59) 62241X COPY u GO SUB VAL CODE OPEN .....
The basic loader has only one basic command, a simple RANDOMIZE USR command. The whole of the rest of the basic is taken up by decrypters, and a few hundred bytes for the loader itself. The USR command starts running code from 5D06.

5D06 DI
5D07 LD HL,5800
5D0A LD DE,5801
5D0D LD BC,03FF
5D10 LD (HL),L
5D11 LDIR
5D13 XOR A
5D14 OUT (FE),A
5D16 LD HL,(5C53)
5D19 LD DE,005C
5D1C ADD HL,DE
5D1D LD BC,0D1F
5D20 LD DE,F1C9
5D23 PUSH DE
5D24 LDIR
5D26 RET

Firstly, this disables interrupts (the DI at 5D06) which stops R getting corrupted.
5D07-5D12 makes the screen black, 5D13-5D15 makes the border black, then HL is set to the start of basic, has 5C added to it (so it points to the start of Speedlock), and is moved to F1C9, then RET'd to. It RETs to F1C9...

F1C9 LD A,2B
F1CB LD R,A
F1CD LD DE,F1CF
F1D0 LD HL,F1D0
F1D3 LD BC,0064
F1D6 LDDR
F1D8 LD BC,0CFA
F1DB LD SP,FEE6
F1DE POP DE
F1DF LD A,R
F1E1 XOR D
F1E2 LD D,A
F1E3 PUSH DE
F1E4 DEC BC
F1E5 LD A,C
F1E6 DEC SP
F1E7 OR B
F1E8 JP NZ,F1DE
F1EB JP F1EE

F1C9-F1CC sets R to 2B, so make sure you keep track of R at all times. Move F1C9-F1EA to somewhere convenient, change the F1DE to the address of the POP DE in this new copy, and on the end stick a LD A,R: breakpoint. Now you can execute it. The value returned in A is two more than the value of R (the LD A,R instruction itself increments R by 2) so subtract 2 from it. Now add 1 to it, because we are not going to be executing the JP F1EE at F1EB, because the decrypter at F1EE is going to be moved somewhere convenient and executed from there.

F1EE LD DE,0CE9
F1F1 LD HL,F1FF
F1F4 DEC (HL)
F1F5 DEC DE
F1F6 LD A,D
F1F7 INC HL
F1F8 OR E
F1F9 JP NZ,F1F4
F1FC JP F1FF

You crack this in a similar way to the way you cracked the last decrypter, by moving F1EE-F1FB to somewhere convenient and ending with LD A,R: breakpoint. However, you will need to put in the value of R from the end of the last decrypter, which is 36 hex, so start it with LD A,37: LD R,A and execute it (I added 1 to it because we are not executing the JP F1EE). Afterwards, R will be 31, which is actually 2F after you subtract the 2 from it.
Never-Ending Story
That's what you'll think when you go through this Speedlock (and the ones that follow) - they seem to go on forever. Just carry on as you have been doing, until you get bored with it. By now you should have noticed that there are five different types of decrypter, with one thing in common. The last instruction executed by them is a JP. We can use this fact to write a very compact hack for the game.
The CPIR Command
We are going to use this command in our hack to find that C3 at the end of the decrypter. You use it as follows:

LD HL, first byte to search
LD BC,amount of bytes to search
LD A,number to search for
CPIR Afterwards, a JP Z will JP if the byte is found, and HL points to the address AFTER the address of the required number.
The ARKY II Hack
How does this work? Firstly, it loads the basic loader to where it would be after it has been moved to the top of memory. Then it checks for the LDDR command, and goes past the start of each decrypter to the actual loop itself, the reason being that we are going to look for a C3 (code for the JP command), which could be a part of one of those numbers. Having found one, by using the CPIR command explained above, it changes the address it JPs to so that control is returned to our routine, rather than the next decrypter. Then it does exactly the same thing over and over, until it doesn't find a C3, in which case it's finished and we can patch the loading system in the usual way. Note that once a C3 is found, it goes back three bytes to look for a JP Z. One of the different types JPs to the new decrypter by saying "if finished, JP to the next one" rather than "finish this one then JP"
       
  ORG 50000 ;Anything safe over 32768
LDBAS LD IX,#F1C9-#5C ;So that Speedlock ends up in the right place
  LD DE,3452 ;Basic length from *Load
  LD A,#FF  
  SCF    
  CALL #556 ;Standard headerless load
  JR NC,LDBAS ;Repeat until basic loaded properly
  DI   ;So that R doesn't get corrupted
DCRLP LD HL,#F1CD ;Address of first actual decrypter
  LD BC,10  
  ADD HL,BC ;HL is now the address of the 10th byte of the decrypter
  LD A,(HL) ;A=PEEK HL
  CP #B8 ;Does (HL)=B8, i.e. is it a LDDR?
  JR Z,ISLDR ;Go forward if it is
  SBC HL,BC ;Otherwise go back to the start
ISLDR ADD HL,BC ;Add another 10 bytes so that we are in the loop itself
  LD A,#C3 ;We want to search for a C3
  LD BC,50 ;Only search 50 bytes
  CPIR   ;Find the byte
  JR NZ,DONE ;If not found, we must have finished the decryption
  DEC HL ;HL points to the address of the C3
  LD C,3  
  SBC HL,BC ;Go back 3 bytes
  LD A,(HL) ;A=PEEK HL
  CP #CA ;Is it a CA, i.e. a JP Z?
  JR Z,ISJPZ ;Go forward if it is
  ADD HL,BC ;Otherwise go to the JP instead
ISJPZ INC HL ;HL is now the first byte to patch
  LD E,(HL) ;Take the LSB of next decrypter
  LD (HL),BACK&255 ;Patch in the LSB of "BACK"
  INC HL ;HL is now the next byte to patch
  LD D,(HL) ;DE is now the address of the next decrypter
  LD (HL),BACK&#FF00/256 ;Patch in the MSB of "BACK"
LDGME LD HL,(DCRLP+1) ;HL is now the start of the current decrypter
  LD (DCRLP+1),DE ;Store the address of the next decrypter
KEEPR LD A,2A ;2A=initial value of R-1
  LD R,A  
  JP (HL) ;Do the decrypter. this instruction increments R by one so it will be 2B at F1CD
BACK LD A,R ;Come back here after decrypting. A=new value of R
  SUB 3 ;SUB 2 for the LD A,R and 1 for the JP (HL)
  RES 7,A ;Bit 7 of R is always either set or reset, in this case it is reset
  LD (KEEPR+1),A ;Store R for the next decrypter
  JR DCRLP ;Now do it all again for the next decrypter
DONE LD HL,POKES ;Come here when all decrypting is done
  LD DE,#5BA0 ;A safe place
  LD BC,END-POKES ;BC=length of pokes
  LD (#FE16),DE ;Standard Speedlock patch
  LDIR   ;Copy the pokes to 5BA0
  XOR A  
  LD (#FBB8),A ;Stops blanking out the new copy of the pokes
  JR LDGME ;Start loading the game.
(DCRLP+1) is the start of the loader
POKES EQU $ ;BUNG YOUR INFY LIVES POKES HERE
  JP #FEC2 ;The original value of (FE16)
END EQU $  
Next month I'll be doing Speedlocks 5-7, so stay tuned and, more importantly, keep hacking. The address for any ideas, probs or unwanted +3's is Jon North, The Hacking Bit, YS, 30 Monmouth Street, Bath, Avon BA1 2BW. No SAE, no reply. T'ra for now...
Sinclair ZX Spectrum

  Previous Page Back Next Page