----Mr. Robot and His Robot Factory---- A 4am crack 2017-08-09 --------------------------------------- Name: Mr. Robot and His Robot Factory Genre: arcade Year: 1984 Credits: Ron Rosen, Bob McNally Publisher: Datamost Platform: Apple ][+ or later Media: single-sided 5.25-inch floppy OS: custom ~ Chapter 0 In Which Various Automated Tools Fail In Interesting Ways COPYA no errors, but copy reboots endlessly Locksmith Fast Disk Backup ditto EDD 4 bit copy (no sync, no count) ditto Copy ][+ nibble editor nothing suspicious Disk Fixer bootloader is custom no disk catalog on any track no DOS everything is custom Why didn't any of my copies work? I'm guessing there's a runtime protection check somewhere. Disks do not simply reboot unless someone tells them to. Next steps: 1. Find the check 2. Disable the check 3. Declare victory (*) (*) go to the gym ~ Chapter 1 How Do I Reboot Thee? Let Me Count The Ways As with many protection checks, this disk reboots as soon as it fails. There are many ways to reboot an Apple II, but the simplest is a direct jump to $C600, the slot 6 firmware. (The more "correct" way is to jump to $FAA6, which does a "cold" restart and scans your slots from 7 down to 1 looking for card firmware to run. Since I have a hard drive device in slot 7, I can tell this protection check is not doing that, because it reboots slot 6 when it fails.) Anyway... [Disk Fixer] ["F"ind] ["H"ex] "4C 00 C6" Two matches, both in the same sector. --v-- T09,S06 ----------- DISASSEMBLY MODE ---------- 0000:A2 07 LDX #$07 0002:A0 00 LDY #$00 0004:A9 00 LDA #$00 ; check if slot peripheral space is ; writeable, by writing a #$00 to the ; first byte of each slot and checking ; if it worked 0006:8D 00 C7 STA $C700 0009:AD 00 C7 LDA $C700 000C:D0 06 BNE $0014 000E:88 DEY 000F:D0 F3 BNE $0004 ; if that worked, reboot immediately 0011:4C 00 C6 JMP $C600 ; check other all 7 slots 0014:CE 08 19 DEC $1908 0017:CE 0B 19 DEC $190B 001A:CA DEX 001B:D0 E5 BNE $0002 (Fun fact: this check fails in some modern emulators, because they emulate non-existent cards by filling the slot ROM with #$00 bytes which triggers a false positive here.) (Fun fact #2: I don't know what this check is for. One theory was that it was protection against hardware NMI cards like the Wildcard which allowed users to unconditionally break into the monitor and save the contents of memory despite the program's best efforts to block it. But a friend tested this code on a real Apple II with a Wildcard Plus card installed, and it did not reboot. So either that card specifically works around this check, or the theory was incorrect. Mysteries abound, even 30+ years later!) ~ Chapter 2 How Do I (M)align Thee? Let Me Count The Ways ; Not sure what this is, either. Some ; side effect to be checked later? ; Based on the self-modifying loop ; above, I know this code is loaded at ; $1900, so $1600 is not anywhere near ; the code I'm looking at. 001D:A9 60 LDA #$60 001F:8D 00 16 STA $1600 0022:A2 FF LDX #$FF 0024:86 04 STX $04 0026:E8 INX 0027:86 01 STX $01 0029:86 03 STX $03 ; Looking at it in a sector editor, it ; is pretty clear that there's an RWTS ; parameter table starting at $19EA. ; We're setting up a read (command $01) ; of track $01, sector $00 into $0200. 002B:8E EF 19 STX $19EF 002E:A2 02 LDX #$02 0030:8E F3 19 STX $19F3 0033:A2 01 LDX #$01 0035:8E EE 19 STX $19EE 0038:A9 01 LDA #$01 003A:8D F6 19 STA $19F6 ; Call the RWTS (not shown) to read ; T01,S00. 003D:20 E3 19 JSR $19E3 ; increment the track 0040:EE EE 19 INC $19EE ; new RWTS command = 0 (seek), not read 0043:A9 00 LDA #$00 0045:8D F6 19 STA $19F6 ; seek to track 2 0048:20 E3 19 JSR $19E3 ; wait 004B:A9 44 LDA #$44 004D:20 A8 FC JSR $FCA8 ; turn on slot 6 drive motor manually 0050:A2 60 LDX #$60 0052:9D 89 C0 STA $C089,X ; A is 0 coming out of the WAIT routine ; at $FCA8 0055:85 00 STA $00 ; Death Counter? 0057:E6 00 INC $00 0059:F0 33 BEQ $008E ; find the next address prologue 005B:BD 8C C0 LDA $C08C,X 005E:10 FB BPL $005B 0060:C9 D5 CMP #$D5 ; loop back if we didn't find a $D5 ; nibble (increments Death Counter, so ; kind of important that we find one ; sooner rather than later) 0062:D0 F3 BNE $0057 0064:BD 8C C0 LDA $C08C,X 0067:10 FB BPL $0064 0069:C9 AA CMP #$AA 006B:D0 F3 BNE $0060 006D:A0 02 LDY #$02 006F:BD 8C C0 LDA $C08C,X 0072:10 FB BPL $006F 0074:C9 96 CMP #$96 0076:D0 E8 BNE $0060 ; get the physical sector number (loop ; burns through and discards the first ; two 4-and-4-encoded values in the ; address field, which are the disk ; volume number and the track number, ; then exits after parsing the sector ; number into zp$02) 0078:BD 8C C0 LDA $C08C,X 007B:10 FB BPL $0078 007D:2A ROL 007E:85 02 STA $02 0080:BD 8C C0 LDA $C08C,X 0083:10 FB BPL $0080 0085:25 02 AND $02 0087:88 DEY 0088:10 EE BPL $0078 ; is it sector 7? 008A:C9 07 CMP #$07 ; yes -> branch forward 008C:F0 04 BEQ $0092 ; no -> set a flag 008E:A2 FF LDX #$FF 0090:86 01 STX $01 ; Execution continues here regardless ; of which sector we found. Keep exact ; track of how long to took to find the ; next address prologue. Maximum number ; of nibbles ends up in zp$03; minimum ; number in zp$04. 0092:A5 00 LDA $00 0094:C5 03 CMP $03 0096:90 02 BCC $009A 0098:85 03 STA $03 009A:C5 04 CMP $04 009C:B0 02 BCS $00A0 009E:85 04 STA $04 ; Loop back and do this all again for ; tracks $02-$10. 00A0:AD EE 19 LDA $19EE 00A3:C9 11 CMP #$11 00A5:D0 91 BNE $0038 ; Calculate the difference between the ; minimum and maximum number of nibbles ; before each track's next address ; prologue. 00A7:A5 03 LDA $03 00A9:38 SEC 00AA:E5 04 SBC $04 00AC:C9 20 CMP #$20 ; If the difference is less than #$20, ; branch forward. 00AE:90 04 BCC $00B4 ; Otherwise, set a different flag. 00B0:A2 FE LDX #$FE 00B2:86 01 STX $01 ; Execution continues here regardless ; of the nibble count difference. ; Turn off the slot 6 drive motor. 00B4:A2 60 LDX #$60 00B6:9D 88 C0 STA $C088,X ; Check that flag. 00B9:A6 01 LDX $01 ; If negative, some error occurred. ; Either we found the wrong sector on ; one of the tracks, or the difference ; in nibbles-before-address-prologue ; between tracks was too large. 00BB:10 03 BPL $00C0 ; Either way, just reboot. 00BD:4C 00 C6 JMP $C600 ; Execution continues here only if the ; protection check passes. Decrypt the ; entire game in place ($6000..$BFFF). 00C0:A0 FF LDY #$FF 00C2:B9 FF BE LDA $BEFF,Y 00C5:59 00 BF EOR $BF00,Y 00C8:99 00 BF STA $BF00,Y 00CB:88 DEY 00CC:C0 FF CPY #$FF 00CE:D0 F2 BNE $00C2 00D0:CE C4 19 DEC $19C4 00D3:CE C7 19 DEC $19C7 00D6:CE CA 19 DEC $19CA 00D9:AD C7 19 LDA $19C7 00DC:C9 5F CMP #$5F 00DE:D0 E2 BNE $00C2 ; Jump to game start. 00E0:4C 00 76 JMP $7600 --^-- ~ Chapter 3 How Do I Patch Thee? Let Me Count The Bytes Whew, that's quite a strict protection! It require that tracks $01-$10 be physically aligned, relative to each other, so that "blindly" seeking to the next track always finds sector $07. But that's not all! It literally counts the number of nibbles it takes to find the start of sector $07 (after a "blind" seek onto each track) and demands that it not vary too much between tracks -- no more than $20 nibbles difference between the minimum and maximum across 15 tracks! This would be very difficult to duplicate with a bit copier, even with the "synchronize tracks" option. In fact, I suspect this protection scheme was specifically designed to test the limits of that option. As there is no sync sensor on standard Apple II floppy drives, the "synchronize tracks" option had to rely on seeking back to track 0, finding a known pattern (like the start of T00,S00), then forward to the next track to write. This got less and less precise as the track number increased, because it spent more time seeking the drive head between tracks, with only approximate control over how far the disk would spin before arriving at the destination track. The entire design of DOS (and ProDOS, Pascal, and everything) compensates for the fact that you never quite know where you're going to end up on a track when you get there. This copy protection weaponizes that imprecision. Anyway, it's a one-byte patch to change the "JMP $C600" (which reboots) to "BIT $C600" (which does nothing). After all that work, it'll just fall through to the success path after failing and start the game anyway. T09,S06,$BD: 4C -> 2C Oh, and one more byte so it doesn't reboot if it finds a #$00 in the first byte of a slot ROM. I would welcome any feedback on what that was supposed to protect against. T09,S06,$11: 4C -> 2C Quod erat liberandum. --------------------------------------- A 4am crack No. 1357 ------------------EOF------------------