----------The Kingdom of Facts--------- A 4am crack 2017-12-01 --------------------------------------- Name: The Kingdom of Facts Genre: educational Year: 1984 Credits: Sheilla Morrell (graphics), Anders Beitnes (programming), Rita and Larry Levin, Wendy Barels, Cindy Taylor (questions), Santa Barbara Softworks Publisher: Adventure International Platform: Apple ][+ or later Media: double-sided 5.25-inch disk OS: DOS 3.3 Previous cracks: none ~ Chapter 0 In Which Various Automated Tools Fail In Interesting Ways COPYA read error on last pass Locksmith Fast Disk Backup unable to read track $22 disk boots and game loads up to the initial play screen, but does not respond to user input thereafter (game is unplayable) EDD 4 bit copy (no sync, no count) ditto Copy ][+ nibble editor track $22 is weeeeeeeird, mostly #$FF and #$DD nibbles, like hundreds and hundreds of them in a row, and timing bits between each nibble Disk Fixer bootloader is standard DOS 3.3 RWTS is standard T01,S09 -> startup program is "HELLO" no way to read track $22 (no standard structure) Why didn't any of my copies work? probably a runtime check on that weird track $22 Next steps: 1. Find the runtime protection check 2. Disable it 3. Declare victory (*) (*) go to the gym ~ Chapter 1 In Which We Quickly Get To The Point Booting my non-working copy, I press and drop to a prompt with DOS in memory. ]LIST ; ; some interesting options (press "E" ; at the title screen to access the ; game editor, or "D" to initialize a ; data disk) but otherwise harmless ; 170 PRINT : PRINT CHR$ (4);"BR UN GAME" ]BLOAD GAME ]CALL -151 *AA72.AA73 ; address of last BLOAD AA72- 00 20 *2000L ; ensure DOS I/O vectors are hooked up 2000- 20 51 A8 JSR $A851 ; "print" a DOS command to execute it 2003- A0 00 LDY #$00 2005- B9 3C 20 LDA $203C,Y 2008- F0 06 BEQ $2010 200A- 20 ED FD JSR $FDED 200D- C8 INY 200E- D0 F5 BNE $2005 *FC58G N 400<203C.204FM MDBLOADFACTSM@ This is equivalent to the BASIC command PRINT CHR$(13);CHR$(4);"BLOADFACTS" or simply typing "BLOAD FACTS" at the prompt. Let's do that. *BLOAD FACTS *AA72.AA73 ; address of last BLOAD AA72- 00 40 Continuing from $2010... 2010- 2C 20 2C BIT $2C20 ; weird, we're modifying code at $204A ; then immediately calling it 2013- A9 EA LDA #$EA 2015- 8D 4A 20 STA $204A 2018- 20 4A 20 JSR $204A ; set 6 other addresses (these are all ; within the range of the "FACTS" file ; we just BLOADed) 201B- A9 4A LDA #$4A 201D- 8D 94 5C STA $5C94 2020- A9 0A LDA #$0A 2022- 8D 9D 5C STA $5C9D 2025- A9 AA LDA #$AA 2027- 8D 20 5D STA $5D20 202A- A9 60 LDA #$60 202C- 8D 9C 5B STA $5B9C 202F- A9 30 LDA #$30 2031- 8D DE 5B STA $5BDE 2034- A9 05 LDA #$05 2036- 8D DF 5B STA $5BDF ; jump to $4000 to start the game 2039- 4C 00 40 JMP $4000 *204AL 204A- 60 RTS But wait! We just modified this (at $2015). $EA is the NOP opcode; the caller made this fall through to the next instruction. ; counter? 204B- A9 02 LDA #$02 204D- 8D 54 20 STA $2054 ; unconditional branch 2050- D0 03 BNE $2055 ... ; more self-modifying code, now an RTS 2055- A9 60 LDA #$60 2057- 8D 55 20 STA $2055 ; get address of RWTS parameter table ; (aha!) 205A- 20 E3 03 JSR $03E3 205D- 84 F8 STY $F8 205F- 85 F9 STA $F9 ; save last-used slot 2061- A0 0F LDY #$0F 2063- B1 F8 LDA ($F8),Y 2065- 8D 52 20 STA $2052 ; RWTS command 0 = seek 2068- A9 00 LDA #$00 206A- A0 0C LDY #$0C 206C- 91 F8 STA ($F8),Y ; set track to seek 206E- A0 04 LDY #$04 2070- AD 53 20 LDA $2053 2073- 91 F8 STA ($F8),Y *2053 2053- 22 Aha! Track $22, the unreadable track. This is definitely the copy protection. ; call the RWTS to execute the seek 2075- 20 E3 03 JSR $03E3 2078- 20 D9 03 JSR $03D9 ; recalibrate the drive head by ; claiming to be on track $28 and ; seeking back to track 0 207B- AE 52 20 LDX $2052 207E- A9 50 LDA #$50 2080- 8D 78 04 STA $0478 2083- A9 00 LDA #$00 2085- 20 A0 B9 JSR $B9A0 ; then back to track $22 2088- AE 52 20 LDX $2052 208B- A9 00 LDA #$00 208D- 8D 78 04 STA $0478 2090- AD 53 20 LDA $2053 2093- 0A ASL 2094- 20 A0 B9 JSR $B9A0 We're definitely on track $22 now, and it's definitely the unreadable track. ; these are used as raw disk nibbles 2097- A9 DD LDA #$DD 2099- 85 F9 STA $F9 209B- A9 FF LDA #$FF 209D- 85 F8 STA $F8 ; turn on drive motor manually 209F- AE 52 20 LDX $2052 20A2- BD 89 C0 LDA $C089,X ; initialize counters 20A5- A9 00 LDA #$00 20A7- 85 F0 STA $F0 20A9- 85 F1 STA $F1 20AB- 85 F2 STA $F2 20AD- 85 F3 STA $F3 20AF- 85 F4 STA $F4 20B1- 85 F5 STA $F5 20B3- 85 F6 STA $F6 20B5- 85 F7 STA $F7 20B7- A8 TAY 20B8- AE 52 20 LDX $2052 ; look for #$DD or #$FF nibble 20BB- BD 8E C0 LDA $C08E,X 20BE- BD 8C C0 LDA $C08C,X 20C1- 10 FB BPL $20BE 20C3- C5 F8 CMP $F8 ; #$FF 20C5- F0 62 BEQ $2129 20C7- C5 F9 CMP $F9 ; #$DD 20C9- F0 05 BEQ $20D0 20CB- C8 INY 20CC- D0 F0 BNE $20BE 20CE- F0 6E BEQ $213E There are three ways out of this loop. Two of them are OK -- we find an #$FF nibble and branch to $2129, or we find a #$DD nibble and branch to $20D0. Or we don't find either of those nibbles within 256 tries, and we give up and branch to $213E. If we find a #$DD nibble first, we continue to $20D0: ; look for #$FF nibble 20D0- BD 8C C0 LDA $C08C,X 20D3- 10 FB BPL $20D0 ; branch forward once we find it 20D5- C5 F8 CMP $F8 ; #$FF 20D7- F0 0E BEQ $20E7 ; count the non-#$FF nibbles 20D9- E6 F6 INC $F6 20DB- D0 F3 BNE $20D0 20DD- E6 F7 INC $F7 20DF- A5 F7 LDA $F7 ; give up if we've seen $1000 nibbles ; without finding #$FF 20E1- C9 10 CMP #$10 20E3- 90 EB BCC $20D0 20E5- B0 57 BCS $213E ; success path continues here (from the ; BEQ at $20D7) -- ; now look for non-#$FF nibble 20E7- BD 8C C0 LDA $C08C,X 20EA- 10 FB BPL $20E7 20EC- C5 F8 CMP $F8 ; #$FF 20EE- D0 0E BNE $20FE ; and give up after $1000 wrong nibbles 20F0- E6 F0 INC $F0 20F2- D0 F3 BNE $20E7 20F4- E6 F1 INC $F1 20F6- A5 F1 LDA $F1 20F8- C9 10 CMP #$10 20FA- 90 EB BCC $20E7 20FC- B0 40 BCS $213E ; if the first non-#$FF nibble is #$DD, ; that's good 20FE- C5 F9 CMP $F9 ; #$DD 2100- F0 06 BEQ $2108 ; otherwise try again (but eventually ; give up after $100 non-#$DD nibbles) 2102- E6 F4 INC $F4 2104- D0 E1 BNE $20E7 2106- F0 36 BEQ $213E ; execution continues here (from "BEQ" ; at $2100) -- ; count #$DD nibbles (zero page $F2/3) ; but give up after $1000 of them, ; terminate on #$FF nibble, tolerate up ; to 256 other values (zero page $F5) 2108- BD 8C C0 LDA $C08C,X 210B- 10 FB BPL $2108 210D- C5 F9 CMP $F9 ; #$DD 210F- F0 0A BEQ $211B 2111- C5 F8 CMP $F8 ; #$FF 2113- F0 6E BEQ $2183 2115- E6 F5 INC $F5 2117- D0 EF BNE $2108 2119- F0 23 BEQ $213E 211B- E6 F2 INC $F2 211D- D0 E9 BNE $2108 211F- E6 F3 INC $F3 2121- A5 F3 LDA $F3 2123- C9 10 CMP #$10 2125- 90 E1 BCC $2108 2127- B0 15 BCS $213E This path continues at $2183. This next line is the start of the other major path, from the "BEQ" at $20C5, if we initially found an #$FF nibble instead of #$DD. (Tracks are just endless circles, and there's no way to know where in the circle we'll start. Rather than burn through half the track looking for one or the other, this protection check is optimized to start counting either way.) ~ Chapter 2 In Which We Enter The Upside Down This path is just the inverse of the other path. Everything that was #$FF is now #$DD, and vice versa. ; look for #$DD nibble 2129- BD 8C C0 LDA $C08C,X 212C- 10 FB BPL $2129 212E- C5 F9 CMP $F9 ; #$DD 2130- F0 0F BEQ $2141 ; count the non-#$DD nibbles 2132- E6 F6 INC $F6 2134- D0 F3 BNE $2129 2136- E6 F7 INC $F7 2138- A5 F7 LDA $F7 213A- C9 10 CMP #$10 213C- 90 EB BCC $2129 ; Besides this loop falling through, ; several failure paths end up here, ; because branch instructions can only ; jump $80 bytes in either direction. ; It just continues to the real ; Badlands further down. 213E- 4C B8 21 JMP $21B8 ; execution continues here (from "BEQ" ; at $2130) -- ; now look for non-#$DD nibble 2141- BD 8C C0 LDA $C08C,X 2144- 10 FB BPL $2141 2146- C5 F9 CMP $F9 ; #$DD 2148- D0 0E BNE $2158 ; and give up after $1000 wrong nibbles 214A- E6 F2 INC $F2 214C- D0 F3 BNE $2141 214E- E6 F3 INC $F3 2150- A5 F3 LDA $F3 2152- C9 10 CMP #$10 2154- 90 EB BCC $2141 2156- B0 E6 BCS $213E ; if the first non-#$DD nibble is #$FF, ; that's good 2158- C5 F8 CMP $F8 ; #$FF 215A- F0 06 BEQ $2162 ; otherwise try again (but eventually ; give up after $100 non-#$FF nibbles) 215C- E6 F5 INC $F5 215E- D0 E1 BNE $2141 2160- F0 DC BEQ $213E ; execution continues here (from "BEQ" ; at $215A) -- ; count #$FF nibbles (zero page $F0/1) ; but give up after $1000 of them, ; terminate on #$DD nibble, tolerate up ; to $100 other nibbles (zero page $F4) 2162- BD 8C C0 LDA $C08C,X 2165- 10 FB BPL $2162 2167- C5 F8 CMP $F8 ; #$FF 2169- F0 0A BEQ $2175 216B- C5 F9 CMP $F9 ; #$DD 216D- F0 14 BEQ $2183 216F- E6 F4 INC $F4 2171- D0 EF BNE $2162 2173- F0 C9 BEQ $213E 2175- E6 F0 INC $F0 2177- D0 E9 BNE $2162 2179- E6 F1 INC $F1 217B- A5 F1 LDA $F1 217D- C9 10 CMP #$10 217F- 90 E1 BCC $2162 2181- B0 BB BCS $213E ; execution continues here (from "BEQ" ; at $216D) -- ; if we found too many of either nibble ; then the protection check has failed 2183- A5 F4 LDA $F4 2185- C9 10 CMP #$10 2187- B0 B5 BCS $213E 2189- A5 F5 LDA $F5 218B- C9 10 CMP #$10 218D- B0 AF BCS $213E ; count of #$FF nibbles needs to be ; between #$0E70 and #$0E90 218F- A5 F1 LDA $F1 2191- C9 0E CMP #$0E 2193- D0 A9 BNE $213E 2195- A5 F0 LDA $F0 2197- C9 70 CMP #$70 2199- 90 A3 BCC $213E 219B- C9 90 CMP #$90 219D- B0 9F BCS $213E ; count of #$DD nibbles needs to be ; between #$0570 and #$0590 219F- A5 F3 LDA $F3 21A1- C9 05 CMP #$05 21A3- D0 99 BNE $213E 21A5- A5 F2 LDA $F2 21A7- C9 70 CMP #$70 21A9- 90 93 BCC $213E 21AB- C9 90 CMP #$90 21AD- B0 8F BCS $213E ; Success! All nibble counts were ; within acceptable ranges. Now turn ; off the drive motor. 21AF- BD 88 C0 LDA $C088,X ; restore the code we self-modified ; earlier (at $2057) 21B2- A9 A9 LDA #$A9 21B4- 8D 55 20 STA $2055 ; and return to the caller 21B7- 60 RTS ; failure path is here (from $213E) -- ; decrement the master counter (set at ; $204B) and try it all again 21B8- CE 54 20 DEC $2054 21BB- D0 12 BNE $21CF ; if we're tried it all and failed too ; many times, give up -- ; beep twice 21BD- 20 DD FB JSR $FBDD 21C0- 20 DD FB JSR $FBDD ; don't return to the caller 21C3- 68 PLA 21C4- 68 PLA ; and... continue to the game anyway?!? 21C5- 4C 00 40 JMP $4000 Note what happens here: on success, we return gracefully to the caller, where we change 6 seemingly innocuous memory locations, then jump to $4000 to start the game: 201B- A9 4A LDA #$4A 201D- 8D 94 5C STA $5C94 2020- A9 0A LDA #$0A 2022- 8D 9D 5C STA $5C9D 2025- A9 AA LDA #$AA 2027- 8D 20 5D STA $5D20 202A- A9 60 LDA #$60 202C- 8D 9C 5B STA $5B9C 202F- A9 30 LDA #$30 2031- 8D DE 5B STA $5BDE 2034- A9 05 LDA #$05 2036- 8D DF 5B STA $5BDF 2039- 4C 00 40 JMP $4000 On failure, we beep twice (at $21BD), pop the stack, and jump to $4000 to start the game anway -- but we never set those 6 "innocuous" addresses. How much could that matter, really? ~ Chapter 3 In Which It Turns Out That In A General Purpose Computing Device Every Instruction Matters Here's the rub: this game is shipped on disk with intentional bugs. Those six patches at $201B fix the bugs. Here, for example, is the surrounding code around the first two patches, at $5C94 and $5C9D. (Again, this is part of the "FACTS" file we BLOADed.) *5C7EL 5C7E- 98 TYA 5C7F- 48 PHA 5C80- 29 07 AND #$07 5C82- 0A ASL 5C83- 0A ASL 5C84- 09 20 ORA #$20 5C86- 85 90 STA $90 5C88- 98 TYA 5C89- 29 38 AND #$38 5C8B- 0A ASL 5C8C- 0A ASL 5C8D- 0A ASL 5C8E- 0A ASL 5C8F- 85 8F STA $8F 5C91- 98 TYA 5C92- 29 38 AND #$38 5C94- EA NOP <-- ! 5C95- 4A LSR 5C96- 4A LSR 5C97- 4A LSR 5C98- 05 90 ORA $90 5C9A- 85 90 STA $90 5C9C- 98 TYA 5C9D- EA NOP <-- ! 5C9E- 90 09 BCC $5CA9 5CA0- A5 8F LDA $8F 5CA2- 09 50 ORA #$50 5CA4- 85 8F STA $8F 5CA6- 4C B2 5C JMP $5CB2 5CA9- 0A ASL 5CAA- 90 06 BCC $5CB2 5CAC- A5 8F LDA $8F 5CAE- 09 28 ORA #$28 5CB0- 85 8F STA $8F 5CB2- 68 PLA 5CB3- A8 TAY 5CB4- 68 PLA 5CB5- AA TAX 5CB6- 68 PLA 5CB7- 60 RTS Those patches aren't random; they're vital instructions in the middle of a vital subroutine. If the protection routine fails, those "NOP" instructions are never fixed ($5C94 never becomes LSR, $5C9D never becomes ASL), and the game is unplayable. Since the game itself patches out its own protection check the first time it runs -- putting an "RTS" at $2055 -- the safest patch would be to make that "RTS" permanent. The protection check will properly return to the caller, the intentional bugs will get patched in memory, and the game will be playable. T16,S03,$59: A9 -> 60 ]PR#6 ...works, and it is glorious... Side B is unprotected. Quod erat liberand one more thing... ~ Chapter 4 I Mean, It's One Bit, Michael. What Could It Cost? Ten Dollars? The sequence of bits on track $22 is very specific. It was designed to fool the best bit copiers, and it does. But why? How does it work? Why can't bit copiers duplicate it? This game was published by Adventure International, better known for its Scott Adams adventure games, and it shares the protection bitstream with those famous adventures. The earliest sample in my collection is "Graphic Adventure #1; Adventureland," dated 1982, but the protection may date back even further. That's a long time to reuse a copy protection. Heck, six months was a long time to reuse a copy protection. This one is obviously durable. Here is the original disk, as seen through the Copy II Plus nibble editor. (The editor indicates timing bits after a nibble with inverse. I've displayed them here as "+" signs.) --v-- COPY ][ PLUS BIT COPY PROGRAM 8.4 (C) 1982-9 CENTRAL POINT SOFTWARE, INC. --------------------------------------- TRACK: 22 START: 1800 LENGTH: 3DFF 1CB0: DD+DD+DD+DD+DD+DD+DD+DD+ 1CB8: FF 9F E7 F9 FE+FF+FF+FF+ 1CC0: FF+FF+FF+FF+FF+FF+FF+FF+ ... 2B30: FF+FF+FF+FF+FF+FF+FF+FF+ 2B38: FF+FF+FF+FF+FF+FF+E9 BA+ 2B40: DD+DD+DD+DD+DD+DD+DD+DD+ ... 30B0: DD+DD+DD+DD+DD+DD+DD+DD+ 30B8: DD+DD+DD+DD+DD+DD+FF 9F 30C0: E7 F9 FE+FF+FF+FF+FF+FF+ --^-- The offsets ($1CB0, $2B30, $30B0) are not important here; in fact, they will vary on every read. There's no defined "start" to a track; it's an unending circle of bits. The offsets depend on the physical rotation of the disk when Copy II Plus started reading. It's the nibble counts that matter. That's what the protection check cares about. - $DD (repeated $057E times) - $FF $9F $E7 $F9 $FE - $FF (repeated $0E81 times) - $E9 $BA And, as indicated, there are timing bits between most nibbles. But that's it; in this view, the $9F nibble at offset $1CB9 is the same as the $9F nibble at offset $30BF. Those are the same bits. That's the point at which, pardon the pun, we've literally come full circle. Now let's copy the disk with a bit copy program. I used EDD 4.4 for this test, one of the most advanced bit copiers even developed. --v-- TRACK: 22 START: 1800 LENGTH: 3DFF 24C0: FF+FF+FF+FF+FF+FF+FF+FF+ 24C8: E9 BA DD DD+DD+DD+DD+DD+ 24D0: DD+DD+DD+DD+DD+DD+DD+DD+ ... 2A40: DD+DD+DD+DD+DD+DD+DD+DD+ 2A48: FF 9F E7 F9 FE FE DB AD 2A50: D6 EB EB+EB+EB+EB+EB+EB+ 2A58: EB+EB+EB+EB+EB+EB+EB+EB+ ... 2C90: EB+EB+EB+EB+EB+EB+EB+EB+ 2C98: FE+FF+FF+FF+FF+FF+FF+FF+ 2CA0: FF+FF+FF+FF+FF+FF+FF+FF+ ... 3B10: FF+FF+FF+FF+FF+FF+FF+FF+ 3B18: FF+FF+FF+FF+FF+E9 BA DD 3B20: DD+DD+DD+DD+DD+DD+DD+DD+ --^-- $057E $DD nibbles -- exactly the same as the original disk. (The acceptable range was $0570..$0590.) And $0E84 $FF nibbles -- a few more than the original disk, but still within acceptable range ($0E70..$0E90). But what's all that in between? After the $DD nibbles, we see the sequence $FF $9F $E7 $F9 $FE, but then it goes off into the weeds. $FE $DB $AD $D6, then a long sequence of... $EB? Where did $EB come from? And by the way, how can all these extra nibbles fit on a track that is supposed to be the same size as the original disk? In retrospect, that last question points the way to the real answer, but I could not see it at the time. For me, the answer did not come from within the Apple II, because, like so many copy protection questions, the answer is not directly visible from within the Apple II. I had to jump out of the system. With modern tools, we can now create bit-perfect images of physical Apple II floppy disks, then examine those images on modern systems. Here is what I found on my bit-perfect image of this 35-year old floppy disk: /--DD--\ /--DD--\ /--DD--\ 110111010011011101001101110100 ... Over and over and over and over. Those are the $DD nibbles we saw earlier in Copy II Plus, the ones that EDD 4 was able to count and copy successfully... or did it? On the original disk, each $DD nibble is followed by 2 "timing" (0) bits. Since each valid nibble needs to start with a "1" bit, those "0" bits between nibbles are ignored. Even the copy protection code ignores them. But that doesn't mean they're useless. Looking at the failed bit copy (again from outside the system, via a disk image that I could only examine on a modern PC), those $DD nibbles look like this: /--DD--\ /--DD--\ /--DD--\ 110111010110111010110111010 ... Each $DD nibble is followed by only 1 "timing" (0) bit! They will still be read as $DD nibbles; in fact, the copy protection code successfully reads and counts them. But the underlying bits on the disk are very different. The original disk had a 10-bit pattern; the copy has a 9-bit pattern. What's going on? Disks spin independently from the Apple II's CPU, which is very slow (1 Mhz). There is barely enough time to read the bits as they spin by the drive head, and there is no way to slow down the disk to read them more accurately. The drive controller card (the thing in slot 6) had to "help" by accumulating the bits into an 8-bit value which could be read by the main CPU. Standard floppy disks were designed to compensate for the limits of the hardware; protected disks were designed to exploit them. EDD 4 devised a clever algorithm to detect the presence or absence of timing bits after a nibble. But it couldn't tell *how many* timing bits are after any particular nibble: 1 or 2. There just wasn't enough time to tell the difference before the next bits started coming in. EDD 4 isn't making a perfect copy of this disk, because no Apple II program could make a perfect copy of any disk. It's making a reconstruction. EDD 4 knows there is at least 1 timing bit after each $FF nibble. It decides to write out 2 timing bits after the first four $FF nibbles, because $FF is often used as a "self-synchronizing" nibble value with the 10-bit pattern 1111111100 repeated four times. (See p. 3-8 of "Beneath Apple DOS" for a more detailed explanation of how this works, and why four 10-bit nibbles is sufficient to synchronize.) For the rest of the $FF nibbles, EDD 4 guesses 1 timing bit, creating a 9-bit pattern 111111110. This is wrong, by the way; the original disk had a 10-bit pattern 1111111100. But in the absence of perfect information, EDD 4 decides that it's better to drop bits and have too few than to add bits and have too many. If it guessed 2 timing bits where the original had only 1, too often, it could end up overwriting the start of the track. The original disk also used 2 timing bits after each $DD nibble. This is an unusual nibble value, so EDD 4 guesses, incorrectly, that there is only one timing bit after every $DD nibble. It writes the 9-bit repeating pattern 110111010, which is different than the original disk's 10-bit pattern of 1101110100. Now EDD 4 has a problem: the track it's reconstructed is too short, and it doesn't know why. With the benefit of hindsight and being able to jump out of the system, we can see why: EDD 4 is writing out 9-bit $DD nibbles instead of 10-bit, and, with few exceptions, 9-bit $FF nibbles instead of 10-bit. That's $057E + $0E80 bits short! Then, like it always does, EDD 4 writes out the track data again, from where it ended the first time (more or less). Now our copy has the wrong data twice, part of which gets overwritten by the wrong data from the first time and part of which doesn't. I believe the technical term for this is "a complete mess." Wait, it gets worse. ~ Chapter 5 Now You See It, Now You Don't There's only one thing you can put on a disk that will change every time you read it: nothing. And by "nothing," I mean "a long sequence of zero bits." And that's what is on the original disk between each of these long groups of nibbles: nothing. A bit of background. When we say a "zero bit," we really mean "the lack of a magnetic state change." The Disk II drive isn't digital; it's analog. If it doesn't see a state change in a certain period of time, it calls that a "0". If it does see a change, it calls that a "1". But the drive can only tolerate a lack of state changes for so long -- about as long as it takes for two bits to go by. Fun fact(*): this is why you need to use nibbles as an intermediate on-disk format in the first place. No valid nibble contains more than two zero bits consecutively, when written from most- significant to least-significant bit. (*) not guaranteed, actual fun may vary So what happens when a drive doesn't see a state change after the equivalent of two consecutive zero bits? The drive thinks the disk is weak, and it starts increasing the amplification to try to compensate, looking for a valid signal. But there is no signal. There is no data. There is just a yawning abyss of nothingness. Eventually, the drive gets desperate and amplifies so much that it starts returning random bits based on ambient noise from the disk motor and the magnetism of the Earth. Seriously. It's trivial to write zero bits to a disk; just write a #$00 nibble to write 8 zero bits -- like any other 8-bit nibble. You can write whatever you want to a disk; it doesn't need to be what DOS would consider a "valid" nibble. But when you read that nibble back, the drive can't handle 8 zero bits in a row, so it will actually return some random bits. Which is why no one does that. Returning random bits doesn't sound very useful for a storage device, but it's exactly what the developer wanted, and that's exactly what this copy protection scheme depends on. Here's why: Bit copiers can't duplicate a long sequence of zero bits. Why? Because that's not what they see. What they see is some random bits -- the real zero bits interspersed with phantom "1" bits. So that's what they write to the target disk. Whatever randomness they get when they read the original disk will essentially get "frozen" onto the copy. As far as I can tell, the sequence of 10-bit $FF nibbles is followed by 12 extra zero bits, for a total of 14 after the final $FF nibble. (All others have 2 zero bits.) Here's what's on the disk between the last half of the last $FF nibbles and the first two $DD nibbles: F--\ /--DD--\ /--DD--\ 11110000000000000011011101001101110100 ^^^^^^^^^^^^ More than two consecutive zero bits will form an "abyss" that the floppy drive will fill with randomness. Some of the zero bits in the abyss (shown here above the "^^^^^^^^^^^" line) will randomly transform into "1" bits. And it will be a different set every time you read the disk. Seriously. Here's one of many possible bitstreams that might come out of the abyss: F--\ /--E9--\/--BA--\ /--DD--\ 11110000000111010011011101001101110100 ^^^^^^^^^^^^ That's what I saw the first time I read the original disk with the Copy II Plus nibble editor: the abyss coalesces into one full nibble ($E9). But if you look closely, you'll see that the $E9 nibble is stealing a bit from the first $DD nibble. The number of $DD nibbles will be one fewer this time, because the $E9 nibble swallowed the first one. Now we're out of phase from the start of the $DD nibbles, because the $E9 nibble stole a bit. After $E9, we see a $BA nibble made up of bits 2-8 of what ought to be the first $DD nibble, plus one of the timing bits as its least significant bit. Then we ignore one timing bit after $BA, and now we're back in phase, reading 10-bit $DD nibbles again. Then I read the original disk again and got a completely different bitstream between the last $FF and the first $DD. It looked like this: F--\ /--ED--\ /--9B--\/--A6--\/--E9--\ 111100111011010100110111010011011101001 ^^^^^^^^^^^^ /--BA--\ /--DD--\ /--DD--\ /--DD--\ 101110100110111010011011101001101110100 Again, the random bits in the abyss are interpreted as real bits, and they fool the disk reading code into reading a long sequence of out-of-phase nibbles, $ED $9B $A6 $E9 $BA, before we finally get back in phase and start reading 10-bit $DD nibbles again. I say "finally" get back in phase, but it really didn't take that long. No matter how many bits get swallowed by the abyss, the 10-bit $DD pattern will always resynchronize. We'll always get back in phase in time to find at least $0570 $DD nibbles. Now let's consider what happens on our attempted copy. First of all, there is no randomness, no abyss. Whatever random bits it read from the original disk, it writes those bits to the copy, followed by the 9-bit $DD nibbles. F--\ /--ED--\ /--9B--\/--AD--\/--D6--\ 11110111011010100110111010110111010110 ^^^^^^^^^^^^ /--EB--\ /--EB--\ /--EB--\ /--EB--\ 111010110111010110111010110111010110 The initial sequence of out-of-phase nibbles is different, between the number of timing bits is different. But we never get back to $DD. In fact, we quickly reach a steady state where we're reading out-of-phase $EB nibbles over and over again. The 9-bit $DD pattern will never resynchronize itself. If you ever get out of phase, you're going to stay out of phase. Of course, you might not ever get out of phase; it depends on which bits come out of the abyss. But that's where the $EB comes from: it's the 9-bit $DD, shifted by 3 bits. Wait, it gets worse. ~ Chapter 6 In Which Everything Is Designed To Be As Bad As Possible, And Succeeds Track $22 on the original disk is split up roughly 70/30, like this: 1111111100 1101110100 |-----------------------|?|----------|? (10-bit $FF) (10-bit $DD) The first, longer section is full of 10-bit $FF nibbles. The second, shorter section is full of 10-bit $DD nibbles. The two "?" areas are the "weak" bits, the abyss, which appear different every time you read the disk and consume a small number of the nibbles that follow (but only a small number, because both 10-bit $FF and 10-bit $DD nibbles will resynchronize). The failed bit copy looks like this: 111111110 110111010 |------------------|?|---------|?|///// (9-bit $FF) (9-bit $DD) The "/////" at the end of the track is not modified (yet), because EDD 4 is writing 9-bit nibbles instead of 10-bit nibbles, so the track is shorter than the original. But then EDD 4 writes the data again, starting in the "/////" region and wrapping around to overwrite most (but not all) of the track it just wrote: 111111110 110111010 1101 |------------------|?|---------|?|----| (9-bit $FF) (9-bit $DD) That last section is detritus, leftover 9-bit $DD nibbles from the previous write. Of course, these may also end up out of phase, depending on how the bits in the abyss come out. And as we've seen, once a 9-bit $DD nibble gets out of phase, it stays out of phase. All of which explains what I initially saw when I examined my failed bit copy in the Copy II Plus nibble editor: --v-- TRACK: 22 START: 1800 LENGTH: 3DFF : FF+FF+FF+FF+FF+FF+FF+FF+ : E9 BA DD DD+DD+DD+DD+DD+ | 9-bit $DD : DD+DD+DD+DD+DD+DD+DD+DD+ | nibbles ... : DD+DD+DD+DD+DD+DD+DD+DD+ : FF 9F E7 F9 FE FE DB AD | abyss : D6 EB EB+EB+EB+EB+EB+EB+ : EB+EB+EB+EB+EB+EB+EB+EB+ | detritus ... : EB+EB+EB+EB+EB+EB+EB+EB+ : FE+FF+FF+FF+FF+FF+FF+FF+ | 9-bit $FF : FF+FF+FF+FF+FF+FF+FF+FF+ | nibbles ... : FF+FF+FF+FF+FF+FF+FF+FF+ : FF+FF+FF+FF+FF+E9 BA DD | abyss : DD+DD+DD+DD+DD+DD+DD+DD+ --^-- Astute readers may complain that I've oversimplified, and they would be technically correct (the best kind of correct). There's no guarantee that the detritus is $DD nibbles; it could be any part of the track, depending on where the bit copier decides the track starts and ends. There's no guarantee that another bit copier would write the track data twice. Other bit copiers might make different guesses about the number of timing bits. It appears that one of the choices EDD 4 made -- adding 2 timing bits after the first few $FF nibbles -- fixed the desynchronization caused by the random bits after the $DD nibbles. But this countermeasure was not enough on its own. This protection check is very strict. It expects a track with a range of $DD nibbles and a range of $FF nibbles and very little else. To fool it, you'd need to make a perfect bit copy. To do that, you'd need to make a perfect bit copier. No one ever did. Quod erat liberandum. --------------------------------------- A 4am crack No. 1563 ------------------EOF------------------