ISSUE 64 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
As promised, this month I'm doing SoftLock. So go dig out an old Firebird game then come back. I'm doing Chimera as an example. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
The Basic Bit First up, *Load and *List as usual... CHIMERA LINE 0 LEN 355 0 BORDER 0: INK 0: PAPER 0: CLS : PRINT AT 21,11;"*LOADING*": POKE 20107,255: RANDOMIZE USR (PEEK 23627+256*PEEK 23628) 1 SAVE "CHIMERA" LINE 0 ...so we see it runs from 23923, which is 5D73 hex. 5D73 LD IYL,A 5D75 DEC SP 5D76 DEC SP 5D77 POP BC 5D78 LD HL,0000 5D7B PUSH HL 5D7C POP IX 5D7E LD A,2E 5D80 LD IXH,40 5D83 SLA A 5D85 LD D,(IX+0) 5D88 LD E,(IX+1) 5D8B INC IX 5D8D INC IX 5D8F ADD HL,DE 5D90 CP IXH 5D92 JR NZ,5D85 This checks the screen (IXH=40 hex, which is the start of the screen area), so put a breakpoint at 5D94, return to Basic then GOTO 0 (because the screen is set up by the basic). When control returns to the disassembler, BC is 5D73 and DE is CA4E. These two values are used by the decrypter which follows. 5D94 EX DE,HL 5D95 LD HL,003D 5D98 ADD HL,BC 5D99 LD IXH,B 5D9B LD IXL,C 5D9D LD C,32 5D9F LD A,(HL) 5DA0 XOR E 5DA1 ADD A,C 5DA2 LD (HL),A 5DA3 LD C,A 5DA4 INC HL 5DA5 INC DE 5DA6 LD A,(HL) 5DA7 XOR D 5DA8 ADD A,C 5DA9 LD (HL),A 5DAA LD C,A 5DAB INC HL 5DAC INC DE 5DAD CP 48 5DAF LD A,A 5DB0 JR NZ,5D9F This decrypts two bytes at a time, starting at 5DB0 (the JR NZ instruction). When it comes to cracking it in a routine, we'll move it to somewhere convenient, stick the JR NZ on the end and run it from there. As it is, firstly single-step through it, then move 5D9F-5DB1 to somewhere, stick a breakpoint on the end and run it from there. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
When finished, you'll see the following code at 5DB2... 5DB2 LD SP,0000 5DB5 LD (5C3D),SP 5DB9 LD HL,0556 5DBC LD DE,FF00 5DBF LD B,H 5DC0 LD C,L 5DC1 LDIR 5DC3 LD H,FF 5DC5 LD DE,007E 5DC8 ADD IX,DE 5DCA PUSH IX 5DCC POP DE 5DCD LD B,05 5DCF LD A,(DE) 5DD0 LD L,A 5DD1 LD A,(HL) 5DD2 SRL (HL) 5DD4 SRL (HL) 5DD6 SUB (HL) 5DD7 LD (HL),A 5DD8 INC DE 5DD9 DJNZ 5DCF 5DDB LD B,17 5DDD LD A,(DE) 5DDE INC DE 5DDF LD L,A 5DE0 LD A,(DE) 5DE1 INC DE 5DE2 LD (HL),A 5DE3 DJNZ 5DDD 5DE5 XOR A 5DE6 LD L,A 5DE7 DEC A 5DE8 LD IX,4000 5DEC LD DE,1C00 5DEF SCF 5DF0 JP (HL) Some of this code will be new to you, but what it does is to make a copy of the ROM loader (at 0556) at FF00, by the LDIR at the start. It then uses a table to change some of the timing constants so that it turboloads (which is what the rest of the code does). Finally, it sets IX and DE to load from 4000-5C00 (the screen and a bit of code) and off it goes. FF00 INC D FF01 EX AF,AF' FF02 DEC D FF03 DI FF04 LD A,0F FF06 OUT (FE),A FF08 LD HL,5B00 FF0B PUSH HL This is the start of the ROM loader, and how it works is unimportant, All you need to know is that the PUSH HL at FF0B PUSHes the return address for when loading finishes, which in this case is 5B00. To find the code at 5B00 (remember it hasn't been loaded yet), change the 5B00 at FF09 to something convenient, where you have placed a breakpoint. Once loaded, the code at 5B00 looks a bit like this... 5B00 DEC SP 5B01 DEC SP 5B02 CALL FF70 5B05 LD A,L 5B06 LD IXL,A 5B08 CALL FF70 5B0B LD A,L 5B0C LD IXH,A 5B0E PUSH IX 5B10 CALL FF70 5B13 LD A,L 5B14 LD IXL,A 5B16 CALL FF70 5B19 LD A,L 5B1A LD IXH,A 5B1C LD A,IXL 5B1E OR IXH 5B20 RET Z 5B21 POP DE 5B22 JP FF70 This code loads four bytes, and treats them as new values of IX and DE. These new values then get loaded as another headerless block (like Powerload). The DEC SP: DEC SP at the start ensures that this routine is always what control is returned to once the block has loaded, unless one of the following happens: 1. The code at 5B00 gets overloaded, in which case control is returned to the new code 2. FFFE and FFFF get overloaded. These two addresses hold the return address, and if overloaded, control will return to the address of the new values 3. The loaded value for IX is zero, in which case the loaded value for DE is RETed to. To find out which of these it is, we are going to write a simple routine which will load those values and store them somewhere, and which will load code at 5B00 but nowhere else. FE00 LD IX,4000 FE04 LD DE,1C00 FE07 SCF FE08 LD HL,FE11 FE0B LD (FF09),HL FE0E JP FF00 FE11 LD A,(5B24) FE14 CP FF FE16 JR Z,FE1B FE18 FE1B LD A,28 FE1D LD (5B23),A FE20 LD A,FE FE22 LD (5B24),A FE25 JP 5B00 FE28 LD (FEF0),SP FE2C LD SP,(FEF2) FE30 PUSH IX FE32 PUSH DE FE33 LD (FEF2),SP FE37 LD SP,(FEF0) FE3B LD A,IXH FE3D CP 5B FE3F JR NZ,FE4E FE41 LD A,DD FE43 LD (FF58),A FE46 LD A,75 FE48 LD (FF59),A FE4B JP FF70 FE4E XOR A FE4F LD (FF58),A FE52 LD (FF59),A FE55 JP FF70 Before using this routine, POKE 65266,254 so that you know where the stack is. To find out where the game loads to: 10 FOR F=65020 TO 0 STEP -4: IF PEEK F THEN PRINT PEEK (F+2)+256*PEEK (F+3);",";PEEK F+256*PEEK (F+1): NEXT F The program will give you the following results: 56320,6232 61000,2000 64900,400 23296,100 65455,48 23324,2 39936,16384 23324,5 23296,256 63000,800 64000,1000 23552,16384 23324,2 23296,92 As you can see, 23296 is loaded over a few times, but loading continues. We can therefore assume that these blocks do not alter the code there in any way, or at least if they do, not sufficiently enough to worry about. Loading finished when that block of 92 bytes was loaded, so this must be different. One disassembly later.... 5B00 XOR A 5B01 OUT (FE),A 5B03 LD HL,F870 5B06 LD DE,F870 5B09 LD BC,9470 5B0C LD IX,5AFF 5B10 LD A,FF 5B12 LD R,A 5B14 LD A,(HL) 5B15 SUB (IX+0) 5B18 XOR IYL 5B1A RLCA 5B1B XOR IYH 5B1D LD (DE),A 5B1E DEC HL 5B1F DEC DE 5B20 DEC BC 5B21 DEC IX 5B23 LD A,IXH 5B25 OR IXL 5B27 JR NZ,5B2C 5B29 LD IXH,5A 5B2C LD A,B 5B2D OR C 5B2E JR NZ,5B14 5B30 LD HL,F8D4 5B33 LD DE,5B01 5B36 LD BC,00FF 5B39 LD SP,5FB4 5B3C PUSH HL 5B3D LD HL,5B00 5B40 LD A,C9 5B42 LD (HL),A 5B43 LDIR This routine firstly decrypts the game, then sets the stack pointer and PUSHes the return address for the game (the PUSH at 5B3C), then fills the printer buffer with RETs. To stick pokes in, simply move them down into 5B3D, then stick a RET at the end to start the game. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
The Chimera Hack This routine loads the basic, then moves the decrypter to a convenient address. Once there, the JR NZ at the end is put in manually, then the entry values are put in and it is CALLed. It then puts a RET at the end of the routine which creates the turboloader and CALLs it, and once in memory the return address is patched and it starts loading. After each short headerless and leaderless block is loaded, it checks a value in the printer buffer to check whether or not the game decrypter is there - if it is then loading must have finished and the infy lives pokes are stuck on the end of the decrypter. Otherwise, control is returned to 5B00 so that the next block can be loaded. The routine is ORGed to 63801, because this is a safe place which never gets loaded over (as can be seen by the table of load addresses). Note that before the game decrypter is run, the hacking routine is deleted, because the game is decrypted through it.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Well, that's another one down. Between now and next month, get hold of a copy of MoonStrike, because I'm going to go through the Movieload on it (be warned though, it's quite a tough nut to crack). Ideas, probs, offers of dates and unwanted +3's should be sent to Jon's Hacking Bit at the usual YS address. See you next month. |