--------------Crypto Cube-------------- A 4am crack 2014-04-02 --------------------------------------- Crypto Cube is an 1983 educational game distributed by DesignWare, Inc. The original disk uses a standard 6-2 nibble encoding and standard address and data prologue and epilogue sequences. COPYA barfs with a disk read error on track $0E. Locksmith 7.0 Fast Disk Backup also shows a read error on sector 3 of track $0E but finishes the copy anyway. EDD 4 bit copy shows no errors. In both cases (Locksmith and EDD), the duplicate disk boots, loads several tracks, then fails with an animated graphical message: "ERROR! Press RETURN to restart CRYPTO CUBE." There is no VTOC on track $11 (so no CATALOG). Listening to the original disk boot, there doesn't seem to be a standard DOS loaded from tracks 2, 1, then 0. It's time for boot tracing. [S6D1=Crypto Cube original disk] [S5D1=my work disk] ]PR#5 ]CALL -151 *9600 zero page $03 BB06- 85 03 STA $03 BB08- 18 CLC BB09- 69 04 ADC #$04 ; $BB --> zero page $05 BB0B- 85 05 STA $05 BB0D- A0 00 LDY #$00 BB0F- 84 02 STY $02 BB11- A0 40 LDY #$40 BB13- 84 04 STY $04 BB15- A0 39 LDY #$39 BB17- A9 93 LDA #$93 ; $93 --> $B739 BB19- 91 02 STA ($02),Y BB1B- C8 INY BB1C- A5 03 LDA $03 ; $B7 --> $B73A BB1E- 91 02 STA ($02),Y ; $BB40..$BBBF --> $0800..$087F BB20- A0 7F LDY #$7F BB22- B1 04 LDA ($04),Y BB24- 99 00 08 STA $0800,Y BB27- 88 DEY BB28- 10 F8 BPL $BB22 ; restore X register and jump to ; relocated code BB2A- A6 1B LDX $1B BB2C- 4C 00 08 JMP $0800 Did you catch that? This subroutine erases any evidence that it was ever called, by changing the JSR instruction that called it from JSR $BB00 to JSR $B793. Then it relocates some other code to $0800 and jumps there. This is not innocent code. Innocent code does not cover its tracks to make it seem like it was never called. This code does not have my best interests at heart. Let's see what ends up at $0800. *BB2C:4C 59 FF *B738G 0800- A9 00 LDA #$00 0802- A0 EC LDY #$EC ; ($02) points to $B700 at this point, ; so this looks like it's setting up ; the RWTS parameter table again. ; $B7EC holds the track number (0). 0804- 91 02 STA ($02),Y 0806- C8 INY ; $B7ED holds the sector number (0). 0807- 91 02 STA ($02),Y 0809- A9 09 LDA #$09 080B- A0 F1 LDY #$F1 ; $B7F1 holds the destination address, ; so it looks like we're going to read ; a bunch of sectors from track 0 ; into $0900 and up. 080D- 91 02 STA ($02),Y 080F- A9 08 LDA #$08 0811- A0 E1 LDY #$E1 ; $B7E1 holds the number of sectors ; to read (8). 0813- 91 02 STA ($02),Y ; set up a JMP instruction at $01 (WTF) 0815- A9 4C LDA #$4C 0817- 85 01 STA $01 0819- A9 93 LDA #$93 081B- 85 02 STA $02 ; call $B793 to read multiple sectors 081D- 20 01 00 JSR $0001 Despite the odd indirection, this part isn't suspicious at all. It's using the standard multi-read routine at $B793 to read the rest of track 0 into memory. I'm still waiting for the other shoe to drop... 0820- A0 00 LDY #$00 0822- 84 02 STY $02 0824- A5 1E LDA $1E 0826- 85 08 STA $08 0828- 0A ASL 0829- 90 01 BCC $082C 082B- C8 INY 082C- 48 PHA 082D- 68 PLA 082E- D0 F8 BNE $0828 0830- C0 02 CPY #$02 0832- 69 FF ADC #$FF 0834- 85 1F STA $1F Wait a minute. Wait wait wait wait wait. The standard DOS 3.3 RWTS doesn't use zero page $1E, so at this point it should be uninitialized and essentially random. But this loop seems to be doing some bit math on it (specifically, counting how many bits are 0 and storing that in the Y register), so either something is setting it or I'm being punked. Am I being punked? That's always a distinct possibility. This code isn't innocent; I'm sure of it. Rebooting into my trusty Copy ][+ sector editor, I found a few instances of the hex value $1E in track 0. The only one that looked like actual RWTS code was T00,S0F,$3F. After some manual cross-referencing, it appears that this sector is loaded at $B900 by the RWTS loader bootstrap in sector 0. Here is the relevant code: B925- BC 8C C0 LDY $C08C,X B928- 10 FB BPL $B925 B92A- D9 00 BA CMP $BA00,Y B92D- D0 13 BNE $B942 B92F- BD 8C C0 LDA $C08C,X B932- 10 FB BPL $B92F B934- B8 CLV B935- BD 8C C0 LDA $C08C,X B938- 10 FB BPL $B935 B93A- B8 CLV B93B- 1E 8C C0 ASL $C08C,X B93E- 26 1E ROL $1E B940- 50 5C BVC $B99E B942- 38 SEC B943- 60 RTS Normally, this code would be checking for the nibble sequence "DE AA", the standard epilogue after the data field. But instead, Crypto Cube accepts any two nibbles, then does some bit math to twiddle zero page $1E based on the timing bit immediately following. Since that relocated "definitely not innocent" routine at $0800 reads 8 sectors, zero page $1E gets twiddled 8 times, giving it a fully defined value despite never being initialized. Obviously the value in zero page $1E is important. Let's capture it. ]PR#5 ]BLOAD TRACE1 ]CALL -151 970A- A9 4C LDA #$4C 970C- 8D 60 BB STA $BB60 970F- A9 1C LDA #$1C 9711- 8D 61 BB STA $BB61 9714- A9 97 LDA #$97 9716- 8D 62 BB STA $BB62 9719- 6C FD 08 JMP ($08FD) 971C- AD E8 C0 LDA $C0E8 971F- A5 1E LDA $1E 9721- 20 DA FD JSR $FDDA 9724- 4C 59 FF JMP $FF59 *BSAVE TRACE2,A$9600,L$127 *9600G ... F7 * This is repeatable; the original disk always ends up with $F7 in zero page $1E. If my theory is correct (that this is part of the copy protection), then running the same boot trace on my non-working copy should yield a different result. [S5D1=my work disk] [S6D1=Crypto Cube non-working copy] ]PR#5 ]BRUN TRACE2 ... 00 * This is also repeatable. Copies (even the bit copy I made with EDD 4) end up with $00 in zero page $1E. Now I'm wondering if I can simply fake this data by ignoring the value of $1E and hard-coding the expected value. I suppose there's only one way to find out. Using my trusty Copy ][+ sector editor, I found the $BB00 routine in sector 3 of track $00. (Why are all these sectors out of order? Sector $0F got loaded into $B900. The answer is interesting. If you look closely at the boot0 routine in sector 0 that loads the RWTS from disk, you'll see that the lookup table that maps logical sectors to physical sectors is in a non-standard order. It didn't affect the boot tracing in any way, because I just let the disk load its own RWTS and interrupt it later. But going in with a sector editor, I have to account for the fact that the RWTS is not stored sequentially in logical sectors. OK, maybe that wasn't so interesting. I don't have an answer to why the original developers did it this way. Just for obfuscation? To load faster? I'm pretty sure it's actually slower. The whole thing is just weird.) Continuing with my hunch, I made one small change after the disk reading but before the bit counting. It just ignores the value in zero page $1E and always uses $F7 instead. T00,S03,$64 change "A5 1E" to "A9 F7" Success! The game boots and no longer displays an error message. The game does more disk reads as you progress, but none of them give me any error message. There doesn't seem to be any further copy protection. Quod erat liberandum. --------------------------------------- A 4am crack 2014-04-02 ------------------EOF------------------