HOW TO HACK

By Jon North

Sinclair ZX Spectrum
ISSUE 67
This month sees the long-awaited Search loader, used on games such as Rana Rama, Deflektor, City Slicker, Costa Capers and, more recently, Shadow of the Beast. I'm doing Rana Rama as an example because it's just been re-released by Players.
Getting Started
What you'll need for this one is a disassembler, Multiface with Genie and a stopwatch (you'll find out why in a mo). Firstly, *Load and *List as usual.
Rana Rama LINE 0 LEN 320 0 RANDOMIZE USR 2377930000 32768-4.5078551E+32 RESTORE STOP READ - LLIST .....
Line zero looks impossible, but it's just two numbers next to each other (23779 and 30000) with nothing to seperate them. Therefore the only basic instruction carried out by the loader is that simple RANDOMIZE USR 23779, which is 5CE3 hex.

5CE3 LD BC,0117
5CE6 LD DE,E833
5CE9 LD HL,5CF4
5CEC LD SP,5C00
5CEF LDIR
5CF1 JP E856

This just copies the loading system up to where it should be and runs it from there. Notice that it's left completely unprotected - the only decrypters in this entire system are right at the end, for the game itself.

E856 LD HL,4000
E859 LD DE,4001
E85C LD BC,1AFF
E85F LD (HL),L
E860 LDIR
E862 LD A,08
E864 IN A,(FE)
E866 LD B,03
E868 LD HL,3D00
E86B LD D,EE
E86D LD E,C
E86E LDIR
E870 DI
E871 CALL E886
E874 JP NC,0000
E877 LD H,00
E879 LD IX,E94A
E87D LD DE,00F2
E880 CALL E917
E883 JP EA29

E856-E865 just makes the screen and border black. Then it makes a copy of the ROM's character set in RAM (which will be used to bring up those funny little messages while the game loads) and then loads the leader tone (the short continuous tone at the start of the following headerless block). Then it loads F2 bytes starting at E94A (standard headerless load at E879) and JP's to it. If you try and disassemble it now, you'll see there's nothing there - it hasn't been loaded yet. All you need to do is stick a breakpoint at E883 and return control to E856 (to start the load).

EA29 LD A,08
EA2B LD (E833),A
EA2E LD A,(E838)
EA31 SET 1,A
EA33 LD (E838),A
EA36 LD DE,0072
EA39 CALL E917
This is similar to what you've already cracked. There's a LD DE at EA36, then a CALL to the headerless loader. IX is already EA3C (from the end of the previous load), but there isn't a JP - it just carries on. To retain control, change the CALL E917 to CALL a convenient address, where you have put CALL E917 followed by a breakpoint. Now set control BACK TO E856, because remember that each of these short headerless blocks is just one big block on the tape, so you'll have to write a little routine somewhere and run that instead.
       
  LD HL,LOAD1  
  LD (#E884),HL  
  JP #E856  
LOAD1 PUSH HL  
  LD HL,LOAD2  
  LD (#EA3A),HL  
  POP HL  
  JP #EA29  
LOAD2 CALL #E917  

Notice that I PUSHed and POPed HL each time I used it, because we don't know whether it will be needed by the loading system or not. When control returns to the disassembler, you can see the following code at EA3C.

EA3C LD A,(E838)
EA3F SET 0,A
EA41 LD (E838),A
EA44 LD DE,01CB
EA47 CALL E917
EA4A JP EBE2

This is the same sort of thing as we had last time. In place of the breakpoint in the routine we're using, stick this code:
       
  PUSH HL  
  LD HL,LOAD3  
  LD (#EA4B),HL  
  POP HL  
  RET LOAD3  
When control comes back, examine the code at EBE2.

EBE2 LD A,(E838)
EBE5 SET 1,A
EBE7 LD (E838),A
EBEA LD A,(EC74)
EBED LD (E839),A
EBF0 LD DE,5800
EBF3 LD (E83A),DE
EBF7 XOR A
EBF8 LD (E83C),A
EBFB LD DE,(EC72)
EBFF LD (E83F),DE
EC03 LD IX,EE00
EC07 LD DE,(EC70)
EC0B CALL E917
EC0E LD IX,4000
EC12 LD DE,2000
EC15 CALL E917
EC18 LD IX,5800
EC1C LD DE,0200
EC1F CALL E917
EC22 LD IX,(EC75)
EC26 LD DE,(EC77)
EC2A PUSH IX
EC2C CALL E917
EC2F RET

This does a lot of buggering about, but the outcome is that the screen piccy is loaded (EC0E-EC21) and then the game block (EC22-EC2E) which is RETed to. To keep control, simply POKE that PUSH IX at EC2B with zero, then CALL EBE2 (which is exactly what the game hack is going to do). Make sure though that you check (EC75) and (EC76) because they hold the return address for the game. The values are 5A and 66 respectively, so the game starts at 665A. To get it in, add a few lines of code to that hacking routine so that, instead of a breakpoint after LOAD3 it puts a zero into EC2B, CALLs EBE2 then NEWs to about 6600ish (the NEW routine is DI:XOR A:LD DE,address to new to: JP #11B7). Don't put a breakpoint there instead because it's more than likely your disassembler will be loaded over. When it NEWs, load you disassembler back in and have a gander at the code at 665A.

665A LD HL,666D
665D LD BC,7194
6660 LD DE,8E6B
6663 PUSH DE
6664 ADD HL,BC
6665 EX DE,HL
6666 ADD HL,BC
6667 EX DE,HL
6668 DEC HL
6669 DEC DE
666A LDDR
666C RET
This simply moves a block of code from 666D to 8E6B, it's length being 7194. Change the length to about 300ish (otherwise your disassembler will be moved too, causing a crash), and single-step through it. Now SAVE the code at 8E6B with SAVE "rana1"CODE 36459,768 and verify it. Now reset the machine and load the game normally with LOAD "". Get ready on your stopwatch, because once the counter reaches zero, you need to time how long it takes for the game to start. When you've got a time, reset the machine and load it again, but this time press your Multiface button just before the game tries to start, and disassemble 8E6B.

8E6B DI
8E6C LD DE,0002
8E6F LD HL,5B00
8E72 LD BC,336B
8E75 LD A,D
8E76 LD (HL),A
8E77 ADD A,A
8E78 ADD A,A
8E79 ADD A,A
8E7A ADD A,A
8E7B ADD A,A
8E7C ADD A,D
8E7D ADD A,L
8E7E ADD A,0B
8E80 LD D,A
8E81 INC HL
8E82 DEC BC
8E83 LD A,B
8E84 OR C
8E85 JP NZ,8E75
8E88 LD HL,FFFF
8E8B LD BC,1
8E8D DEC E
8E8E JR NZ,8E75
8E90 LD HL,4000
8E93 LD BC,0001
8E96 LD A,D
8E97 ADD A,(HL)
8E98 ADD HL,BC
8E99 JP NC,8E97
8E9C JR C,8ECA
8ECA LD HL,8EB4
8ECD LD (HL),ED
8ECF LD BC,C10D
8ED2 CALL 8E9E
8E9E POP HL
8E9F PUSH HL
8EA0 LD E,B
8EA1 INC HL
8EA2 LD D,A
8EA3 ADD A,A
8EA4 ADD A,A
8EA5 ADD A,A
8EA6 ADD A,A
8EA7 ADD A,D
8EA8 ADD A,C
8EA9 XOR (HL)
8EAA LD (HL),A
8EAB DJNZ
8EA1
8EAD LD B,E
8EAE POP HL
8EAF ADD A,(HL)
8EB0 LD (HL),ED
8EB2 INC HL
8EB3 JP (HL)
8ED6 CALL 8E9E
8EDA DEC B
8EDB DEC B
8EDC CALL 8E9E
8EE0 CALL 8E9E
8EE4 LD C,22
8EE6 CALL 8E9E
8EEA DEC B
8EEB CALL 8E9E
8EEF DEC B
8EF0 CALL 8E9E
8EF4 JR 8F01
8F01 LD C,05
8F03 CALL 8EF6
8EF6 LD HL,4000
8EF9 LD B,L
8EFA ADD A,(HL)
8EFB ADD HL,BC
8EFC JP NC,8EFA
8EFF JR 8EAE
8F07 INC C
8F08 CALL 8E9E
8F0C CALL 8E9E
8F10 CALL 8EF6
8F14 LD BC,870A
8F17 CALL 8E9E
8F1B CALL 8E9E
8F1F JR 8F30
8F30 INC C
8F31 INC C
8F32 CALL 8F21
8F21 LD HL,4000
8F24 LD B,L
8F25 ADD A,(HL)
8F26 ADD HL,BC
8F27 JP NC,8F25
8F2A POP HL
8F2B ADD A,(HL)
8F2C LD (HL),ED
8F2E INC HL
8F2F JP (HL)
8F36 LD BC,1690
8F39 CALL 8E9E
8F3D CALL 8F21
8F41 INC B
8F42 CALL 8EF6
8F46 CALL 8E9E
8F4A LD D,00
8F4C LD E,A
8F4D XOR A
8F4E LD (8F4B),A
8F51 LD HL,4000
8F54 LD BC,0001
8F57 LD A,E
8F58 ADD A,(HL)
8F59 LD E,A
8F5A INC HL
8F5B LD A,D
8F5C ADC A,(HL)
8F5D LD D,A
8F5E ADD HL,BC
8F5F JP NC,8F57
8F62 EX DE,HL
8F63 LD DE,9009
8F66 LD C,29
8F68 LD B,H
8F69 LD H,L
8F6A ADD HL,BC
8F6B LD A,(DE)
8F6C XOR L
8F6D LD (DE),A
8F6E LD L,A
8F6F INC DE
8F70 LD A,(DE)
8F71 XOR H
8F72 LD (DE),A
8F73 INC DE
8F74 LD B,A
8F75 LD A,E
8F76 CP FF
8F78 JP NZ,8F69
8F7B LD A,D
8F7C CP FF
8F7E JR NZ,8F69
8F80 LD H,B
8F81 LD DE,A06E
8F84 ADD HL,DE
8F85 LD DE,0000
8F88 LD (5C09),DE
8F8C LD SP,5FB3
8F8F PUSH HL
8F90 LD A,R
8F92 SET 7,A
8F94 LD R,A
8F96 LD A,CD
8F98 LD DE,5FB4
8F9B LD (BEE7),A
8F9E LD (BEE8),DE
8FA2 LD HL,8FC9
8FA5 LD BC,000A
8FA8 LDIR
8FAA LD HL,9009
8FAD LD DE,61A9
8FB0 LD BC,1E57
8FB3 LDIR
8FB5 LD DE,A500
8FB8 LD BC,3EA0
8FBB LDIR
8FBD LD HL,8000
8FC0 LD DE,8001
8FC3 LD BC,0FD2
8FC6 LD (HL),L
8FC7 JR 8FD3
8FD3 LDIR
8FD5 RET
9009 DEFB C3,F7,C7,C0,61,60,67,0A
Firstly, all the unused memory (336B bytes starting from 5B00) is filled with garbage, by the routine at 8E6B-8E8F. Then the whole memory is added together, including the screen, the garbage and the encrypted game, and a value retained in A. This value is kept for the whole of the game decryption. Virtually all the code from 8ECA to 8F46 consists of strange things happening to BC, followed by a CALL to 8E9E. Occasionally the memory is all added together again (by the routines at 8EF6 and 8F21) and the new value of A used. Finally, at 8F4A, the memory is added together one last time and a large value is kept for it in DE (8F51-8F61), which is used by the decrypter which follows to decrypt the game. A few blocks of code are moved about (8FA2-8FD4) and finally, the game is RETed to. Get a listing of all the code (from 8E6B right though to about 9010) then crash the game. Now load your disassembler and that small chunk of code you saved earlier - things are going to start getting interesting.
Decrypting The Game
First of all, tap in this bit of basic:

10 FOR F=40000 TO 40023
20 READ A: POKE F,A: NEXT F
30 DATA 62,0,33,213,142
40 DATA 17,0,200,1,0
50 DATA 1,237,176,33,0
60 DATA 200,1,13,193,205
70 DATA 160,142,0,201
80 FOR F=0 TO 255: POKE 40001,F
90 RANDOMIZE USR 40000
100 FOR G=51200 TO 51220
110 IF PEEK G=205 AND PEEK (G+1)=158 AND PEEK (G+2)=142 THEN PRINT F: STOP
120 NEXT G: NEXT F

What this does it to move the code from 8ED5 to a convenient address, then decrypt it using every possible value of A. Once decrypted it looks at it to try and find a CALL 8E9E - if it finds one, it must have used the right value and prints it. RUN it and leave it for a while - after a few minutes it will return with 203 on the screen, which is CB hex. Go into your disassembler, give A the value CB, put a breakpoint at 8EB3 (just after the decrypter) and give control to 8ECA. It returns with A=21 hex, so make a note. Now put another breakpoint at 8EB3, return control to the disassembler and make a note of the new value of A (which is 55 hex). Keep going like that until you get to 8F08, because the whole memory is checked again at 8F02. Make a note of the value in BC (it's 6) and go back to basic. Put the following new values into the DATA statements and re-run that hack:

30 DATA 62,0,33,11,143
60 DATA 200,1,6,0,205

When it returns, the value of A is 10, which is 0A hex. Do as you did before until 8F14, when another adding routine is executed. This time, HL is 8F1A (the address after the CALL 8E9E) and BC is 870A, so lines 30 and 60 should read:

30 DATA 62,0,33,26,143
60 DATA 200,1,10,135,205

The new value of A is 14, which is 0E hex. The next time you use the basic is at 8F36, where HL is 8F3C and BC is 1690. The value returned in A is 168, which is A8 hex. You'll then need to do it one last time, for the CALL 8E9E at 8F46. HL is 8F49 and BC is 0090, but you'll need to change line 110 to check for something else, because there are no more CALL 8E9E's. Make it check for that LD HL,4000 at 8F51 by changing line 110 to read:
110 IF PEEK G=33 AND PEEK (G+1)=0 AND PEEK (G+2)=64 THEN PRINT F: STOP
The value returned in A is 43, which is 2B hex. Patch that in as you have been, and then tackle that big game decrypter at the end.

10 FOR F=30000 TO 30034
20 READ A: POKE F,A: NEXT F
30 DATA 33,184,159,34,9,144
40 DATA 42,0,64,205,99,143
50 DATA 58,9,144,254,195,32,6
60 DATA 58,10,144,254,247,200
70 DATA 42,0,64,35,34,0,64,195,48,117
80 POKE 36731,201
90 RANDOMIZE USR 30000

What this does is to put in the original values of B8 and 9F at 9009 and 900A (where the big decrypter starts from), then decrypt up to 90FF (I put the POKE 36731,201 in to make it RET after 90FF, otherwise it would go through the entire memory). Once it's done, it checks the new values of 9009 and 900A to see if they're what they should be if the right value had been used - if they are, the right value WAS used and it comes back to basic. Run it then PRINT PEEK 16384+256*PEEK 16385 to get the value - you'll see it's 5714. Now we know everything we need to write a hack for the game.
The Rana Rama Hack
This firstly loads the basic as a headerless file, to where it would be after the LDIR at 5CE3. Then it does exactly the same routine as we used earlier to load the game. Once loaded, it POKEs the PUSH DE at 6663 with zero, so that a CALL to 665A will move the code to the right place then return to our hack. When the code is where it should be, it sticks a JP back at 8E9E (the start of the decrypter) and sets IX to the start of a table of values. Then it JPs to 8ECA because we don't need to fill the memory with garbage or add it all up. When control comes back, it's from the start of the decrypter, so POP HL, PUSH HL and LD E,B are there because they were removed by the JP we put there. A is given it's correct value, from the table of values at the end (like the table of decrypter lengths in the Speedlock 2 and 3 hacks), and the value checked. If it's not 2B, it just JPs back to the decrypter and waits for control to be returned by the next time the decrypter is used.
Otherwise, it just CALLs it (so that control will be returned afterwards). The RET to the game, at 8FD5, is replaced with a JP for the pokes, and the correct value of 5714 given to HL for the main decrypter at 8F63 (which is then JP'd to). Finally, in go the infy lives pokes and a RET to the game starts it playing. Note that there is a NOP after the CALL 8EA1, because when it's finished it returns to the address AFTER the one it's supposed to (by the POP HL: INC HL: JP (HL) at the end).
       
  ORG 23296 ;A definite safe place
LOAD LD IX,#E833-#5C4F+#5CCB   
  LD DE,00320  
  LD A,#FF  
  SCF    
  CALL #556  
  JR NC,LOAD  
  LD HL,BACK1  
  LD (#E884),HL  
  LD SP,#5C00  
  JP #E856  
BACK1 PUSH HL  
  LD HL,BACK2  
  LD (#EA3A),HL  
  POP HL  
  JP #EA29  
BACK2 CALL #E917  
  PUSH HL  
  LD HL,BACK3  
  LD (#EA4B),HL  
  POP HL  
  RET    
BACK3 XOR A  
  LD (#EC2B),A  
  CALL #EBE2 ;the game has loaded now
  XOR A  
  LD (#6663),A  
  CALL #665A  
  LD A,#C3  
  LD (8E9E),A  
  LD HL,BACK4  
  LD (#8E9F),HL  
  LD IX,TABLE  
  JP #8ECA  
BACK4 POP HL  
  PUSH HL  
  LD E,B  
  LD A,(IX)  
  INC IX  
  CP #2B  
  JP NZ,#8EA1  
  CALL #8EA1  
  NOP    
  LD A,#C3  
  LD (#8FD5),A  
  LD HL,BACK5  
  LD (#8FD6),HL  
  LD HL,05714  
  JP #8F63  
BACK5     ;STICK YOUR POKES HERE
  RET    
TABLE DEFB #CB,#21,#55,#F7,#BB,#B2,#25,#0A,#10,#F3,#0E,#75,#A8,#2B
Sinclair ZX Spectrum

  Previous Page Back Next Page