From DaphneWiki

Jump to: navigation, search


Microzine #9, #12, #18 (and more)

The following information applies to all of these versions too.

Microzine #14 (Funhouse Caper)

This floppy disk sat in my garage for probably years and before that it sat in my mom's basement. It survived several winters amazingly enough and I just recently re-assembled the family Apple //gs and tried booting it up. To my amazement, it still worked!

The Goals

  1. To be able to enjoy this software in a modern emulator instead of needing to have a real Apple ][ to play it.
  2. To preserve the copy protection so that it can be studied in the future if anyone is curious.

The second goal has not been fully reached (yet).

Recovering the data

Most of the hard work was done by Phil Pattengale in Computist magazine, issue 27, pages 12 and 13. (see http://victa.jamtronix.com/display/page/958 )

The disk has non-standard formatting but can still be read properly as long as a copy program ignores non-fatal errors. To copy the disk,

  1. Boot a DOS 3.3 system master disk
  2. type the following at the BASIC prompt:
    1. "call -151"
    2. "b942: 18 60"
    3. "q"
    4. "run copya"
  3. copy the microzine disk to a duplicate

We now have a standard DOS 3.3 disk with all the sectors recovered from the copy protected disk. But during the boot sequence of the disk, it will do a check against the raw nibbles on one of the tracks (I believe track 1) and if it doesn't find what it is looking for, it will know it's a copy. How does it know? Because the nibbles it checks for are not part of the sector data, and only the sector data is copied by programs like COPYA.

Transferring the image to an emulator

This is mostly beyond the scope of this article, but what I usually do is use shrinkit to compress the 5.25" disk image to an .SHK file. I then use ProTERM to upload the file (using zmodem) to a modern PC using a null modem cable and a term program such as hyperterminal (for windows) or minicom (for linux). Once the .SHK file is on the modern PC, you can use nulib or ciderpress to extract it to a disk image and then run it inside KEGS or AppleWin or whatever.

The Copy Protection Routine

A really handy way to examine the copy protection routine is to use KEGS' debugger. This is a much more powerful technique for seeing what's really going on than trying to do it on a real Apple // where you can't set breakpoints easily.

  • The copy protection routine starts with a JSR to $BB00, so boot up KEGS, hit Shift-F6 to break into the debugger, and type "bb00B", then "G" to resume exeuction. Then boot the disk image ("PR#6" or whatever). Your breakpoint will get a few false positive "hits" but you know that you're in the right place when the program counter (PC) is near BB00 (bb02 for me).
  • type "bb00L" in the KEGS debugger and check out what's going on.
00/bb00: a0 00          LDY     #$00
00/bb02: b9 00 bb       LDA     $bb00,Y
00/bb05: 99 00 02       STA     $0200,Y
00/bb08: 88             DEY
00/bb09: d0 f7          BNE     $bb02
00/bb0b: 60             RTS

This routine copies the code from $BB00-BBFF to $200 and then returns. So the actual copy protection routine is at $bb0c-$bbff (or so) but is designed to run at $20C-$2FF.

Since we have the luxury of being able to set breakpoints, we know that $20C must eventually get called in order to run the copy protection routine, so set a breakpoint.

  • First, get rid of the BB00 breakpoint because it will waste your time if you don't. Type "bb00D" to get rid of it.
  • Then type "20cB", then "g" to resume execution.

You will break at 2CF due to KEGS breaking you after the instruction you wanted has executed (I'd love to change that).

  • Type "20CL" to check out the copy protection routine. You can hit L a few times to see all of it.
  • The copy protection routine will branch to $293 if the copy protection check has failed. You will see a lot of those branches here.
00/020c: 20 cf 02       JSR     $02cf
00/020f: a9 0a          LDA     #$0a
00/0211: 85 2a          STA     $2a

$2a contains how many failures the copy protection routine will tolerate before assuming that the disk is indeed a copy. In this case, it will try 10 times to look for the special signature on the disk that will not be on the copy.

00/0213: ae e9 b7       LDX     $b7e9
00/0216: bd 89 c0       LDA     $c089,X
00/0219: bd 8e c0       LDA     $c08e,X

Reading from $c089,X turns on the drive motor. Reading from $c08e,X puts us in input mode.

00/021c: a9 c7          LDA     #$c7
00/021e: 85 48          STA     $48
00/0220: a9 02          LDA     #$02
00/0222: 85 49          STA     $49
00/0224: a9 80          LDA     #$80
00/0226: 85 29          STA     $29
00/0228: c6 29          DEC     $29
00/022a: f0 67          BEQ     $0293
00/022c: 20 44 b9       JSR     $b944
00/022f: b0 62          BCS     $0293
00/0231: a5 2d          LDA     $2d
00/0233: c9 0d          CMP     #$0d
00/0235: d0 f1          BNE     $0228

$b944 is the DOS "RDADR" routine. It reads an address field. On success, the carry is clear and $2f will contain the volume number, $2e the track number, $2d the sector number, and $2c the checksum. Notice that $233 checks to see if we've found sector $D and if we haven't, it loops until we do find it.

So that means that the special nibble signature is on track $11, sector $D.

The nibble header for track $11, sector $D looks like this:

D5 AA 96 FF FE AA BB AE AF FB EA FF FF EB (12 sync bytes) D5 AA AD 96 96 ...

An astute observer will notice that the address field ends in "FF FF EB" instead of "DE AA EB". This is a non-standard change on the copy protected disk to throw off copy programs.

Right now the drive head is in the middle of the sync bytes.

00/0237: a0 00          LDY     #$00
00/0239: bd 8c c0       LDA     $c08c,X
00/023c: 10 fb          BPL     $0239
00/023e: 88             DEY
00/023f: f0 52          BEQ     $0293
00/0241: c9 d5          CMP     #$d5
00/0243: d0 f4          BNE     $0239

$241 now starts looking for the next #$D5 bytes, which marks the start of the sector data.

00/0245: a0 00          LDY     #$00
00/0247: bd 8c c0       LDA     $c08c,X
00/024a: 10 fb          BPL     $0247
00/024c: 88             DEY
00/024d: f0 44          BEQ     $0293
00/024f: c9 e7          CMP     #$e7
00/0251: d0 f4          BNE     $0247
00/0253: bd 8c c0       LDA     $c08c,X
00/0256: 10 fb          BPL     $0253
00/0258: c9 e7          CMP     #$e7
00/025a: d0 37          BNE     $0293
00/025c: bd 8c c0       LDA     $c08c,X
00/025f: 10 fb          BPL     $025c
00/0261: c9 e7          CMP     #$e7
00/0263: d0 2e          BNE     $0293

$245 now looks for three consecutive nibbles all with a value of #$E7, and it will search the next 256 bytes before it gives up. As it turns out, the end of the sector data is full of #$E7's, even on a non-copy-protected disk.

00/0265: bd 8d c0       LDA     $c08d,X
00/0268: a0 10          LDY     #$10
00/026a: 24 06          BIT     $06
00/026c: bd 8c c0       LDA     $c08c,X
00/026f: 10 fb          BPL     $026c
00/0271: 88             DEY
00/0272: f0 1f          BEQ     $0293
00/0274: c9 ee          CMP     #$ee
00/0276: d0 f4          BNE     $026c

This is where the actual check for the copy-protected disk is. After the #$E7 section has been found, the code touches $C08D,X which throws the disk drive sync off. It then expects to find a #$EE within the next 10 bytes as the drive tries to regain sync.

00/0278: a0 07          LDY     #$07
00/027a: bd 8c c0       LDA     $c08c,X
00/027d: 10 fb          BPL     $027a
00/027f: d1 48          CMP     ($48),Y
00/0281: d0 10          BNE     $0293
00/0283: 88             DEY
00/0284: 10 f4          BPL     $027a

Once it finds #$EE, it looks for specific nibble pattern, 7 bytes long, matching what we see at $2C7 (going backward). In other words, "e7 fc ee e7 fc ee ee fc". Experimenting on a real disk shows that the chances of this happening consistently are rare, which is why it attempts this routine over and over again.

It should be mentioned at this point, that a nibble copier cannot defeat this type of protection because the results returned will be different with each read. The only way to defeat this protection without cracking the disk is to know exactly what bits the copy protection routine will be looking for and somehow replicate that on a new disk.

00/0286: a9 80          LDA     #$80
00/0288: 8d 4e 9e       STA     $9e4e
00/028b: a9 a1          LDA     #$a1
00/028d: 8d 4f 9e       STA     $9e4f
00/0290: 4c 4d 9e       JMP     $9e4d

If we get to $286, it means the copy protection routine has passed.

00/0293: c6 2a          DEC     $2a
00/0295: d0 8d          BNE     $0224
00/0297: a2 22          LDX     #$22
00/0299: bd a4 02       LDA     $02a4,X
00/029c: 95 00          STA     $00,X
00/029e: ca             DEX
00/029f: 10 f8          BPL     $0299
00/02a1: 4c 00 00       JMP     $0000

$293 is where the program goes if the copy protection has failed. The copy protection routine will try again a number of times to succeed ($293 and $295). Memory location $2a contains how many attempts we have left before failing. If we do fail, it will zero out $2a4-$2c6, and then jump to $0000 which causes a random crash. I really can't see how $2a4-$2c6 is used but the copy protection engineer obviously thought it contained some serious clues that he didn't want anyone to know about. :)

02a4: a0 01 84 4f 88 84 4e 98 91 4e 88 d0    ..O..N..N.P
02b0: fb e6 4f f0 0c a5 4f c9 c0 d0 f1 a9 d0 85 4f d0   {fOp.%OI@Pq)P.OP
02c0: eb ad 81 c0 6c fc ff   k-.@l|.

Not sure what $2a4-$2c6 is for but it doesn't seem to get executed. It does, however, seem to be important because it gets cleared right before the copy protection routine causes a crash. Interesting.

02c7: fc ee ee fc e7 ee fc e7   |nn|gn|g

$2c7-$2ce is a nibble pattern on the copy protected disk that will not show up on a copied disk. (see $21C and $27F)

00/02cf: ad e9 b7       LDA     $b7e9
00/02d2: 4a             LSR
00/02d3: 4a             LSR
00/02d4: 4a             LSR
00/02d5: 4a             LSR
00/02d6: 09 c0          ORA     #$c0
00/02d8: 8d f3 03       STA     $03f3
00/02db: 49 a5          EOR     #$a5
00/02dd: 8d f4 03       STA     $03f4
00/02e0: a9 00          LDA     #$00
00/02e2: 8d f2 03       STA     $03f2
00/02e5: 8d f4 b7       STA     $b7f4
00/02e8: a9 11          LDA     #$11
00/02ea: 8d ec b7       STA     $b7ec
00/02ed: a9 b7          LDA     #$b7
00/02ef: a0 e8          LDY     #$e8
00/02f1: 4c b5 b7       JMP     $b7b5

$2cf sets up an IOB and calls RWTS. The IOB is stored at $b7e8 (standard place). To learn about IOB and RWTS, see the book "Beneath Apple DOS" which is how I learned about it.

The IOB looks like this:

b7e8: 01 60 01 ff 11 09 fb b7   .`....{7
b7f0: 00 9c 00 01 00 00 fe 60 01   ......~`.
  • b7e8: table type (must be 1)
  • b7e9: slot * 16 (0x60)
  • b7ea: drive number (1 in this case)
  • b7eb: volume number expected (0xFF in this case)
  • b7ec: track number (0x11 in this case)
  • b7ed: sector number (9 in this case)
  • b7ee: pointer to device characteristics table (DCT), $b7fb in this case
  • b7f0: pointer to buffer to read/write ($9c00 in this case)
  • b7f2: unused
  • b7f3: byte count for partial sector ($1 in this case)
  • b7f4: command code (0 in this case, which means SEEK)
  • b7f5-b7f8 are return codes (from what I can tell)

So in other words, this function performs a seek to track $11, sector $9. (unless I've read it wrong)

The Device Characteristics Table (DBT) referenced contains standard DOS 3.3 values, so it doesn't demand attention.

The Crack

The crack is to make this copy protection routine end up at $286 instead of $293. There are a few ways this can be done:

  • Change all branches that go to $293 so they go to $286 instead.
  • Change location $293 to branch to $286 which feels cleaner.
  • Change location $293 to jump to $286.

Phil Pattengale decided to go with the third option and code in a jump. Just to show that I can actually contribute something beyond what Phil has done (ok let's face it, he did 90% of the work hehe), I'm going to present the second option instead.

Change 293 from c6 2a d0 8d to a9 00 10 ef which changes the "DEC $2A, BNE $224" to "LDA #$0, BPL $286" which is a long-winded way of saying ALWAYS branch to $286, but the BRA instruction isn't available on the 6502 last I recall so I gotta use something that I know is there (BPL).

With a sector editor

Search for "c6 2a d0". I found mine on track $0, sector $5, byte $93 which is the same place Phil found it on his disk (although he was not doing Microzine #14).

Personal tools