---------The Great Number Chase-------- A 4am crack 2017-05-09 --------------------------------------- Name: The Great Number Chase Genre: educational Year: 1984 Credits: Eric Soldan Publisher: Milliken Publishing Company Platform: Apple ][+ or later Media: single-sided 5.25-inch floppy OS: Diversi-DOS C1982 Previous cracks: none ~ Chapter 0 In Which The Tools Do Not Save Us This disk was automatically cracked by Passport. Here is the transcript: --v-- READING FROM S6,D1 T00,S00 FOUND DIVERSI-DOS BOOTLOADER USING DISK'S OWN RWTS WRITING TO S5,D2 T00,S03,$91: DF -> DE T00,S03,$35: DF -> DE T00,S06,$AE: DF -> DE T00,S02,$9E: DF -> DE CRACK COMPLETE. --^-- [Narrator] But the crack was not complete. The copy that Passport produces is in a standard format, fully readable by third-party tools. But when I boot it, the disk sounds like it loads DOS as usual, but then it reboots. Which is odd, because disks generally do not reboot unless someone tells them to. ~ Chapter 1 In Which We Learn The How But Not The Why Turning to my trusty Disk Fixer sector editor, I start my investigation as any investigation should start: on track 0, sector 0. It is identical to an unprotected DOS disk. On to sector 1, then. --v-- T00,S01 ----------- DISASSEMBLY MODE ---------- ; totally normal -- setting up the RWTS ; parameters to read DOS from tracks ; $00-$02 0000:8E E9 B7 STX $B7E9 0003:8E F7 B7 STX $B7F7 0006:A9 01 LDA #$01 0008:8D F8 B7 STA $B7F8 000B:8D EA B7 STA $B7EA 000E:AD E0 B7 LDA $B7E0 0011:8D E1 B7 STA $B7E1 0014:A9 02 LDA #$02 0016:8D EC B7 STA $B7EC 0019:A9 04 LDA #$04 001B:8D ED B7 STA $B7ED 001E:AC E7 B7 LDY $B7E7 0021:88 DEY 0022:8C F1 B7 STY $B7F1 0025:A9 01 LDA #$01 0027:8D F4 B7 STA $B7F4 002A:8A TXA 002B:4A LSR 002C:4A LSR 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 ; call RWTS to load DOS from T00-T02 ; (my non-working copy sounded like it ; got this far) 0038:20 93 B7 JSR $B793 003B:A2 FF LDX #$FF 003D:9A TXS 003E:8E EB B7 STX $B7EB ; nothing suspicious, just sets up some ; machine stuff and jumps back to next ; instruction (not shown) 0041:4C C8 BF JMP $BFC8 0044:20 89 FE JSR $FE89 ; hey ho, hey ho, this custom code has ; got to go 0047:4C 0E BF JMP $BF0E Page $BF is loaded from T00,S09, so let's jump over there and see what's going on. This is right about where my non-working copy stopped, you know, working. --v-- T00,S09 ----------- DISASSEMBLY MODE ---------- ; set up RWTS for an unusual sector ; read from T02,S00 000E:A9 02 LDA #$02 0010:8D EC B7 STA $B7EC 0013:A9 00 LDA #$00 0015:8D ED B7 STA $B7ED 0018:8D EB B7 STA $B7EB ; no wait, we're just seeking (RWTS ; command 0 = seek), which is totally ; suspicious 001B:8D F4 B7 STA $B7F4 001E:A9 B7 LDA #$B7 0020:A0 E8 LDY #$E8 0022:20 00 BD JSR $BD00 ; turn on drive motor manually (the ; RWTS would have turned it off before ; returning) 0025:AE F8 05 LDX $05F8 0028:BD 8C C0 LDA $C08C,X 002B:BD 89 C0 LDA $C089,X ; possibly two different counters? not ; sure yet 002E:A9 12 LDA #$12 0030:85 02 STA $02 0032:A0 14 LDY #$14 ; look for an $FB nibble (why?) 0034:BD 8C C0 LDA $C08C,X 0037:10 FB BPL $0034 0039:C9 FB CMP #$FB 003B:D0 F7 BNE $0034 ; burn some CPU cycles (I may get to ; count these later) 003D:48 PHA 003E:68 PLA 003F:48 PHA 0040:68 PLA 0041:EA NOP 0042:EA NOP 0043:EA NOP 0044:EA NOP 0045:EA NOP 0046:EA NOP ; check the data latch again (note: no ; BPL here, so we're just fetching the ; current value at this moment in time) 0047:BD 8C C0 LDA $C08C,X ; check if data latch is still $FB 004A:C9 FB CMP #$FB ; nope -> skip next instruction 004C:D0 02 BNE $0050 ; decrement one counter (in zp$02) 004E:C6 02 DEC $02 ; decrement the other counter (in Y) 0050:88 DEY ; loop back until done 0051:D0 E1 BNE $0034 ; check the first counter 0053:24 02 BIT $02 ; if it's still positive, that's bad 0055:10 03 BPL $005A ; if it's negative, continue with the ; rest of the boot process 0057:4C 84 9D JMP $9D84 ; failure path ends up here -- ; decrement what appears to be an ; uninitialized counter and loop back ; to try again some number of times 005A:CE A1 81 DEC $81A1 005D:D0 CF BNE $002E ; eventually fall through to here and ; reboot 005F:AD E9 B7 LDA $B7E9 0062:4A LSR 0063:4A LSR 0064:4A LSR 0065:4A LSR 0066:09 C0 ORA #$C0 ; pretty sure this is a bug and it's ; supposed to be altering the next JMP ; instruction, but the address is wrong 0068:8D A0 81 STA $81A0 ; anyway, reboot from slot 6 (always, ; since we didn't actually change it) 006B:4C 00 C6 JMP $C600 OK, well that's definitely a protection check, and it's definitely the cause of my non-working copy not, you know, working. But what's going on? To answer that, I turn to my trusty Copy II Plus nibble editor to look at track $02 on the original disk. ~ Chapter 2 In Which We Learn The Why (Copy II Plus shows timing bits after a nibble by showing them in inverse. To represent this in plain text, I've replaced them with "+" signs instead.) --v-- COPY ][ PLUS BIT COPY PROGRAM 8.4 (C) 1982-9 CENTRAL POINT SOFTWARE, INC. --------------------------------------- TRACK: 02 START: 1800 LENGTH: 3DFF 3F38: FF+FF+FF+FF+FF+FF+FF+FF+ VIEW 3F40: D5 AA 96 FF FE AB AA AF ^^^^^^^^ ^^^^^ ^^^^^ ^^ addr prologue V=254 T=$02 S=$0F 3F48: AF FB FB DF AA EB FF+9F ^^ ^^^^^ ^^^^^^^^ chksm epilogue 3F50: F9 FE+FF+FF+FF+D5 AA AD+ ^^^^^^^^ data prologue 3F58: FB+FB+FB+FB+FB+FB+FB+FB+ 3F60: FB+FB+FB+FB+FB+FB+FB+FB+ 3F68: FB+FB+FB+FB+FB+FB+FB+FB+ 3F70: FB+FB+FB+FB+FB+FB+FB+FB+ 3F78: FB+FB+FB+FB+FB+FB+FB+FB+ 3F80: FB+FB+FB+FB+FB+FB+FB+FB+ 3F88: FB+FB+FB+FB+FB+FB+FB+FB+ 3F90: FB+FB+FB+FB+FB+FB+FB+FB+ 3F98: FB+FB+FB+FB+FB+FB+FB+FB+ 3FA0: FB+FB+FB+FB+FB+FB+FB+FB+ 3FA8: FB+FB+FB+FB+FB+FB+96 96 3FB0: 96 96 96 96 96 96 96 96 --^-- > > Look at all those timing bits there > > are at least ten. > Well, you're not wrong. So we're looking for $FB nibbles and counting how many of them have a timing bit after them. How can we tell? By wasting just the right amount of time. Returning to this code at $BF34, this time with cycle counts in the margin: --v-- 0034:BD 8C C0 LDA $C08C,X ; 4 0037:10 FB BPL $0034 ; 2 0039:C9 FB CMP #$FB ; 2 003B:D0 F7 BNE $0034 ; 2 003D:48 PHA ; 3 003E:68 PLA ; 4 003F:48 PHA ; 3 0040:68 PLA ; 4 0041:EA NOP ; 2 0042:EA NOP ; 2 0043:EA NOP ; 2 0044:EA NOP ; 2 0045:EA NOP ; 2 0046:EA NOP ; 2 0047:BD 8C C0 LDA $C08C,X 004A:C9 FB CMP #$FB --^-- The way the data latch ($C08C,X) works is that it accumulates bits as the disk spins until it has a "full" nibble value (with the high bit set). Each bit on disk takes 4 CPU cycles to come around as the disk is spinning. That means we need to read an 8-bit nibble every 32 cycles. The data latch will hold the "full" value for 4 more cycles before it resets itself back to zero, so we can spend an absolute maximum of 36 cycles on any one nibble before it's gone. Including the legitimate branches and compares, combined with the "pointless" PHA/PLA/NOP instructions, we spend 36 cycles before checking the data latch again. That's too long; the data latch will have reset itself to zero! Except... If there's an extra "0" bit after the nibble, the data latch holds its value for an extra 4 cycles. So this code burns exactly the right amount of time to distinguish whether a nibble has a timing bit after it. Then it does it 20 times to make sure it wasn't a fluke. Advanced nibble copiers like EDD4 and Copy II Plus will preserve one timing bit per nibble -- even inside the data field, as they are on this disk. But of course Passport didn't preserve any nibbles -- it read sector data with the original disk's own RWTS, then wrote out new sector data. That sector data doesn't have any timing bits at all, so the check fails. There's no consequence to skipping the check. I can change T00,S01 to jump directly to $9D84 and bypass the check altogether. T00,S01,$48: 0EBF -> 849D ]PR#6 ...works, and it is glorious... Quod erat liberandum. --------------------------------------- A 4am crack No. 1196 ------------------EOF------------------