-------------Good Thinking!------------ -------Cause and Effect Level Two------ A 4am crack 2019-04-05 --------------------------------------- Name: Good Thinking! Cause and Effect Level Two Genre: educational Year: 1986 Publisher: Hoffman Educational Systems Platform: Apple ][+ or later Media: 5.25-inch disk Sides: 1 OS: DOS 3.3 Previous cracks: none ~ Chapter 0 In Which Various Automated Tools Fail In Interesting Ways COPYA no errors, but copy boots to title screen then reboots Locksmith Fast Disk Backup ditto EDD 4 bit copy (no sync, no count) ditto Copy ][+ nibble editor nothing suspicious, looks like a standard 16-sector DOS 3.3 disk Disk Fixer T00 is a bit odd because the sector order is non-standard. T11 is also odd -- there's definitely a DOS 3.3 style disk catalog there, but it starts on sector 7 instead of sector $0F, so third-party utilities don't find it. At any rate, no specific clues about why it would reboot so late in the process. Disks do not spontaneously reboot unless someone tells them to. Why didn't any of my copies work? unclear -- runtime protection check? Next steps: 1. Split up 2. Search for clues 3. Wait no, that's Scooby Doo ~ Chapter 1 Unlucky In Cards Since my copy goes down a different code path than the original, I'm guessing there is a runtime protection check somewhere. One thing that all protection checks have in common is they need to turn on the drive motor by accessing a specific address in the $C0xx range. For slot 6, it's $C0E9, but to allow disks to boot from any slot, developers usually use code like this: LDX LDA $C089,X There's nothing that says where the slot number has to be, although the disk controller ROM routine uses zero page $2B and lots of disks just reuse that. There's also nothing that says you have to use the X-register as the index, or that you must use the accumulator as the load register. But most RWTS code does, out of convention I suppose (or possibly fear of messing up such low-level code in subtle ways). Also, since developers don't actually want people finding their protection- related code, they may try to encrypt it or obfuscate it to prevent people from finding it. But eventually, the code must exist and the code must run, and it must run on my machine, and I have the final say on what my machine does or does not do. But sometimes you get lucky. Turning to my trusty Disk Fixer sector editor, I search the non-working copy for "BD 89 C0", which is the opcode sequence for "LDA $C089,X". [Disk Fixer] ["F"ind] ["H"ex] ["BD 89 C0"] --v-- ------------- DISK SEARCH ------------- $00/$0C-$16 $00/$0E-$4F --^-- Again, track 0 is out of order, so it turns out that the match on sector $0E is just the regular part of the DOS 3.3 RWTS. But the match on sector $0C is much more suspicious. --v-- T00,S0C ----------- DISASSEMBLY MODE ---------- ; set up the RWTS parameter table with ; track 7 0000:A9 07 LDA #$07 0002:8D EC B7 STA $B7EC ; 0 = "seek" command 0005:A9 00 LDA #$00 0007:8D F4 B7 STA $B7F4 000A:8D F2 B7 STA $B7F2 ; seek to track 7 000D:20 E3 03 JSR $03E3 0010:20 D9 03 JSR $03D9 ; turn on drive motor manually (this is ; how I found this routine) 0013:AE E9 B7 LDX $B7E9 0016:BD 89 C0 LDA $C089,X ; counters of some sort 0019:A9 00 LDA #$00 001B:85 3C STA $3C 001D:A9 10 LDA #$10 001F:85 3E STA $3E 0021:C6 3E DEC $3E 0023:D0 06 BNE $002B 0025:A9 10 LDA #$10 0027:85 3E STA $3E 0029:D0 7E BNE $00A9 ; get and parse the next available ; address field 002B:20 44 B9 JSR $B944 002E:B0 F1 BCS $0021 ; until we find a specific sector given ; in zero page $3C (currently #$00) 0030:A5 2D LDA $2D 0032:C5 3C CMP $3C 0034:D0 EB BNE $0021 ; look for third nibble of address ; epilogue (the routine at $B944 ; verifies the $DE and $AA only) 0036:BD 8C C0 LDA $C08C,X 0039:10 FB BPL $0036 003B:C9 EB CMP #$EB 003D:D0 6A BNE $00A9 ; burn cycles by calling an "RTS" a ; few times, and doing nothing in a ; variety of ways 003F:20 C3 BC JSR $BCC3 0042:20 C3 BC JSR $BCC3 0045:EA NOP 0046:EA NOP 0047:EA NOP 0048:EA NOP 0049:EA NOP ; next nibble needs to be #$F2 004A:BD 8C C0 LDA $C08C,X 004D:10 FB BPL $004A 004F:C9 F2 CMP #$F2 0051:D0 56 BNE $00A9 ; do this for every sector 0053:E6 3C INC $3C 0055:A5 3C LDA $3C 0057:C9 10 CMP #$10 0059:D0 D0 BNE $002B ; do this for every track (down to 0) 005B:CE EC B7 DEC $B7EC 005E:10 AD BPL $000D ; another counter 0060:A0 40 LDY #$40 0062:8C F2 B7 STY $B7F2 0065:88 DEY 0066:D0 05 BNE $006D 0068:CE F2 B7 DEC $B7F2 006B:F0 49 BEQ $00B6 ; look for specific nibble sequence ; $CA $D2 $A9 006D:BD 8C C0 LDA $C08C,X 0070:10 FB BPL $006D 0072:C9 CA CMP #$CA 0074:D0 EF BNE $0065 0076:EA NOP 0077:BD 8C C0 LDA $C08C,X 007A:10 FB BPL $0077 007C:C9 D2 CMP #$D2 007E:D0 F2 BNE $0072 0080:EA NOP 0081:BD 8C C0 LDA $C08C,X 0084:10 FB BPL $0081 0086:C9 A9 CMP #$A9 0088:D0 E8 BNE $0072 ; read a 4-and-4-encoded value after ; the custom nibble sequence 008A:A0 01 LDY #$01 008C:BD 8C C0 LDA $C08C,X 008F:10 FB BPL $008C 0091:2A ROL 0092:85 2D STA $2D 0094:BD 8C C0 LDA $C08C,X 0097:10 FB BPL $0094 0099:25 2D AND $2D ; this will actually overwrite the ; zero page addresses we were using ; earlier 009B:99 3C 00 STA $003C,Y 009E:88 DEY 009F:10 EB BPL $008C ; turn off drive motor and reset DOS ; zero page addresses 00A1:BD 88 C0 LDA $C088,X 00A4:A9 00 LDA #$00 00A6:85 48 STA $48 00A8:60 RTS ; failure path is here -- if anything ; fails, increment the death counter ; and retry up to 16 times 00A9:EE F2 B7 INC $B7F2 00AC:AD F2 B7 LDA $B7F2 00AF:C9 10 CMP #$10 00B1:D0 A0 BNE $0053 ; give up, turn off drive motor, and ; jump to $B6B3 00B3:BD 88 C0 LDA $C088,X 00B6:4C B3 B6 JMP $B6B3 --^-- Just for giggles, here is the routine at $B6B3: --v-- T00,S00 ----------- DISASSEMBLY MODE ---------- ; copy to zero page 00B3:A0 00 LDY #$00 00B5:B9 B2 B6 LDA $B6B2,Y 00B8:99 00 00 STA $0000,Y 00BB:88 DEY 00BC:D0 F7 BNE $00B5 ; continue from there 00BE:4C 0F 00 JMP $000F ; wipe all of main memory 00C1:99 00 BF STA $BF00,Y 00C4:88 DEY 00C5:D0 FA BNE $00C1 00C7:C6 11 DEC $11 00C9:D0 F6 BNE $00C1 ; and reboot 00CB:6C FC FF JMP ($FFFC) --^-- ...which is exactly what happened on my non-working copy. ~ Chapter 2 The Space Between The heart of this protection routine is burning CPU cycles after the $EB nibble and requiring that the next nibble we read is $F2. This is only true because there are two hidden timing bits after the $EB nibble, which most disks don't have and bit copiers can't detect. Here is track 7 in the Copy II Plus nibble editor. Timing bits are usually indicated with inverse characters, but I have converted them to "+" signs: --v-- COPY ][ PLUS BIT COPY PROGRAM 8.4 (C) 1982-9 CENTRAL POINT SOFTWARE, INC. --------------------------------------- TRACK: 07 START: 1F14 LENGTH: 180D 1EF0: FF+FF+FF+FF+FF+FF+FF+CA VIEW 1EF8: D2 A9 AA AA AA AA AA AA 1F00: AA AA AA AA AA AA FC FF+ 1F08: FF+FF+FF+CF F3 FC FF+FF+ 1F10: FF+FF+FF+FF+D5 AA 96 FF <-1F14 1F18: FE AB AF AA AA FE FB DE 1F20: AA EB+F2 FF F9 FE FF+FF+ 1F28: FF+FF+D5 AA AD DF FC FC 1F30: FC FE FF FF FF FF FF FF --^-- At offset $1F21, the $EB has a timing bit after it. OK, fine, bit copiers can detect and reproduce that. But wait! It's actually TWO timing bits. Bit copiers can't tell the difference between 1 and 2 timing bits -- there literally isn't enough time at 1 MHz. All the self-sync nibbles ($FF values between the address field and the data field) usually have 2 timing bits after them, so the bit copiers will fake it and assume 2 bits there. But if there happen to be 2 timing bits after any other nibble, bit copiers will only detect 1 and only reproduce 1, and this protection check is designed to tell the difference. ~ Chapter 2 Automate All The Things Despite the fact that the second part of the protection routine reads values and stores them in zero page, the caller does not appear to check these. (I checked; they're zeroes.) I put an "RTS" at the beginning of the routine, and the program finished booting without complaint. As I have several other disks from the same publisher that share the same protection, I applied the same patch to all of them, and they all worked. So we're going to call this a side-effect- free protection check. FURTHERMORE since I have more than one disk that shares the same protection, it's a perfect candidate for Passport. Because why crack one thing when you can crack all the things? Here is what the Passport log will look like: --v-- Reading from S6,D1 Using built-in RWTS Writing to RAM disk T00,S0C Found Hoffman protection check T00,S0C,$00: A9 -> 60 Writing to S6,D2 Crack complete. Press any key --^-- Quod erat liberandum. --------------------------------------- A 4am crack No. 1981 ------------------EOF------------------