------------Decimal Dungeon------------ A 4am crack 2018-02-18 --------------------------------------- Name: Decimal Dungeon Genre: educational Year: 1985 Credits: David Howard Flatman, June Selena Stark Publisher: Unicorn Software Platform: Apple ][+ or later Media: 5.25-inch disk Sides: 1 OS: DOS 3.3 Previous cracks: none ~ Chapter 0 In Which We Get Right To It COPYA gives no errors, but the copy reboots after loading DOS. I made a bit copy onto a .nib disk image with EDD 4 and a CFFA 3000 card. That copy also reboots. In my experience, disks do not reboot unless someone tells them to. Inspecting track 0 with a sector editor shows a normal DOS 3.3 bootloader, but things are on the "wrong" sectors because the physical-to-logical sector mapping is non-standard. On T00,S08 is the second-stage boot code loaded at $B700, and we find this: --v-- T00,S08 ($B700) ----------- DISASSEMBLY MODE ---------- 002D:4A LSR 002E:4A LSR 002F:AA TAX 0030:A9 00 LDA #$00 0032:9D F8 04 STA $04F8,X 0035:9D 78 04 STA $0478,X 0038:20 00 BC JSR $BC00 <-- ! 003B:A2 FF LDX #$FF 003D:9A TXS 003E:8E EB B7 STX $B7EB 0041:4C C8 BF JMP $BFC8 0044:20 89 FE JSR $FE89 0047:4C 84 9D JMP $9D84 --^-- Normally the JSR at $B738 calls $B793 to read DOS into memory. $BC00 is stored on T00,S0D. --v-- T00,S0D ($BC00) ----------- DISASSEMBLY MODE ---------- ; copy most of $BB00 to lower memory ; (but don't overwrite the page 3 ; vectors at $3D0+) 0000:A0 CF LDY #$CF 0002:B9 00 BB LDA $BB00,Y 0005:99 00 03 STA $0300,Y 0008:88 DEY 0009:C0 FF CPY #$FF 000B:D0 F5 BNE $0002 000D:EA NOP 000E:EA NOP 000F:EA NOP ; exit via $B793 to load DOS as usual 0010:4C 93 B7 JMP $B793 --^-- So we're stealthily putting some code at $0300 but not calling it. Presumably this is what gets called later. $BB00 is stored on T00,S0C. --v-- T00,S0C ($BB00, later $0300) ----------- DISASSEMBLY MODE ---------- ; set up RWTS parameter table with ; track 7 0000:A9 07 LDA #$07 0002:8D EC B7 STA $B7EC ; command 0 (seek) 0005:A9 00 LDA #$00 0007:8D F4 B7 STA $B7F4 000A:8D F2 B7 STA $B7F2 ; call RWTS to seek to track 7 000D:20 E3 03 JSR $03E3 0010:20 D9 03 JSR $03D9 ; turn on drive motor manually 0013:AE E9 B7 LDX $B7E9 0016:BD 89 C0 LDA $C089,X 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 ; find next available address field 002B:20 44 B9 JSR $B944 002E:B0 F1 BCS $0021 ; get sector number from address field 0030:A5 2D LDA $2D ; compare to the counter we initialized ; earlier (at $031B) 0032:C5 3C CMP $3C 0034:D0 EB BNE $0021 ; find $EB nibble 0036:BD 8C C0 LDA $C08C,X 0039:10 FB BPL $0036 003B:C9 EB CMP #$EB 003D:D0 6A BNE $00A9 ; call an "RTS" to burn CPU cycles ; (the JSR burns 6 cycles, and the RTS ; burns another 6 cycles) 003F:20 C3 BC JSR $BCC3 ; burn another 12 cycles 0042:20 C3 BC JSR $BCC3 ; burn another 10 cycles (2 x 5) 0045:EA NOP 0046:EA NOP 0047:EA NOP 0048:EA NOP 0049:EA NOP ; find next nibble 004A:BD 8C C0 LDA $C08C,X 004D:10 FB BPL $004A ; must be $F2 004F:C9 F2 CMP #$F2 0051:D0 56 BNE $00A9 ; increment sector counter 0053:E6 3C INC $3C ; do this for all 16 sectors on track 7 0055:A5 3C LDA $3C 0057:C9 10 CMP #$10 0059:D0 D0 BNE $002B ; now do it all over again for all ; tracks (down to 0) 005B:CE EC B7 DEC $B7EC 005E:10 AD BPL $000D --^-- So it's all about a timing bit. After finding the $EB nibble (at $0336), we intentionally burn 32 CPU cycles before we even start looking for the $F2 nibble. Looking at the raw track in Copy II Plus nibble editor, the $F2 nibble immediately follows the $EB. 32 CPU cycles is enough time that the $F2 ought to have come into and gone out of the data latch. We ought to miss it. Except... If there is a hidden timing bit after the $EB nibble, the data latch will hold its value 4 cycles longer. By the time we get around to checking for the $F2 nibble, it will still be there -- just barely, but the timing works out. Except on a copy which didn't preserve the timing bit. Then we'll miss the $F2 nibble at $034F, try a few more times, hit the same problem every time, and eventually give up and reboot. As far as I can tell, there is only 1 timing bit after the $EB nibble, so it could be possible to make a protected backup onto a physical floppy, given a sufficiently advanced bit copy program like EDD 4. But that doesn't get us any closer to the goal of seamless digital distribution. It is 2018, after all. It's .dsk or bust. ~ Chapter 1 But Wait, There's More So we disable this copy protection routine and move on with our lives, right? Not so fast. Continuing from $0360... --v-- ; set up an entirely new 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 ; find a custom nibble sequence on the ; final track we checked for the timing ; bit (track 0 at this point) ; $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 and decode a pair of 4-and-4 ; encoded values 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 ; store the values in zero page 009B:99 3C 00 STA $003C,Y 009E:88 DEY 009F:10 EB BPL $008C ; turn off drive motor and return 00A1:BD 88 C0 LDA $C088,X 00A4:A9 00 LDA #$00 00A6:85 48 STA $48 00A8:60 RTS --^-- Turning again to the Copy II Plus nibble editor, I can see this custom prologue and encoded values: --v-- COPY ][ PLUS BIT COPY PROGRAM 8.4 (C) 1982-9 CENTRAL POINT SOFTWARE, INC. --------------------------------------- TRACK: 00 START: 1800 LENGTH: 3DFF 3918: FC FF FF FF FF FF FF FF VIEW 3920: FF FF FF FF FF FF FF FF 3928: FF FF FF FF FF FF FF FF 3930: FF FF FF FF FF FF FF FF 3938: FF FF FF FF FF FF FF FF 3940: CA D2 A9 AA AA AA AA AA ^^^^^^^^ ^^^^^^^^^^^ prologue encoded zeroes 3948: AA AA AA AA AA AA AA F7 3950: E7 F9 FE FF FE FF FF FF 3958: FF FF FF FF FF D5 AA 96 --^-- So this protection check does have side effects after all. It sets zero page $3C and $3D to 0. ($AA $AA is the 4-and-4 encoded value 0.) Thus: T00,S0C,$00: -> A9 00 85 3C 85 3D 60 which looks like 0000:A9 00 LDA #$00 0002:85 3C STA $3C 0004:85 3D STA $3D 0006:60 RTS and unconditionally sets the two zero page locations to 0 and exits without checking timing bits or other stuff. ]PR#6 ...works, and it is glorious... Quod erat liberandum. --------------------------------------- A 4am crack No. 1698 ------------------EOF------------------