--------------Microzine 25------------- A 4am crack 2014-08-14 -------------------. updated 2015-09-06 |___________________ "Microzine" was an educational disk- based magazine published by Scholastic between 1983 and 1992. Issue no. 25 is a double-sided disk that features "Cosmic Heroes" and "The Learning Machine." Side 1 is bootable; side 2 just says "put in side 1." COPYA copies both sides, but the copy does not work. It loads DOS, displays a prompt, runs a startup program, then swings the drive head out to a high- numbered track and reboots. In my experience, computers do not spontaneously reboot unless someone tells them to. Booting my non-working copy, I can press to break to the BASIC prompt with the HELLO program in memory. ]LIST 0 REM Hmm. ]LIST (nothing) Lovely. Annoying BASIC tricks from the 1980s are still annoying. ]LOAD HELLO ]LIST 1, 2 ONERR GOTO 500 3 PRINT CHR$ (4);"BRUN TOC.6" 7 HOME :D$ = CHR$ (4) 8 PRINT D$;"MAXFILES 1" 10 REM IF PEEK (28676) = 39 TH EN POKE 767,1 13 REM 767 = FLAG THAT TITLE H AS BEEN SHOWN 14 IF PEEK (767) = 1 THEN GOTO 30 15 POKE - 16302,0 16 PRINT D$;"BLOAD PACK.MICRO7, A$7000" 17 PRINT D$;"BLOAD UNPACKDATA.1 ,A$1000" 18 POKE (4098),39: POKE (4099), 192: POKE (252),0: POKE (253 ),16: CALL 4168 23 POKE - 16304,0: POKE - 162 97,0 BREAK There we go. Let's start with line 3, which runs an external program. ]BLOAD TOC.6 ]CALL -151 *AA72.AA73 ; location of last BLOAD AA72- 00 90 *9000L ; get RWTS parameter table address ; and store it 9000- 20 E3 03 JSR $03E3 9003- 85 FB STA $FB 9005- 84 FA STY $FA ; pushing bytes to the stack out of ; nowhere is a little suspicious 9007- A9 C5 LDA #$C5 9009- 48 PHA ; copy some local values into the RWTS ; parameter table 900A- A9 00 LDA #$00 900C- 85 FC STA $FC 900E- A2 03 LDX #$03 9010- BC 35 90 LDY $9035,X 9013- 91 FA STA ($FA),Y 9015- CA DEX 9016- 10 F8 BPL $9010 ; more stack fiddling 9018- 8A TXA 9019- 48 PHA The X register will be $FF after the loop, so this means we've pushed $C5FF to the stack. At this point, a bare RTS will "return" to $C5FF+1, i.e. reboot. 901A- 20 28 90 JSR $9028 *9028L ; call the RWTS (most likely just to ; move the drive head to the proper ; position for an impending nibble ; check) 9028- 20 E3 03 JSR $03E3 902B- 20 D9 03 JSR $03D9 902E- A9 00 LDA #$00 9030- 85 48 STA $48 ; if that fails, off to The Badlands 9032- B0 52 BCS $9086 9034- 60 RTS Caller was $901A, so resuming at $901D: *901DL 901D- A0 01 LDY #$01 901F- B1 FA LDA ($FA),Y 9021- AA TAX 9022- 20 39 90 JSR $9039 *9039L ; turning on the drive motor manually ; is always suspicious 9039- BD 89 C0 LDA $C089,X 903C- A9 56 LDA #$56 903E- 85 FD STA $FD 9040- A9 08 LDA #$08 9042- C6 FC DEC $FC 9044- D0 04 BNE $904A 9046- C6 FD DEC $FD ; if zero page $FD hits 0, give up 9048- F0 3C BEQ $9086 ; look for a specific nibble ($FB) 904A- BC 8C C0 LDY $C08C,X 904D- 10 FB BPL $904A 904F- C0 FB CPY #$FB 9051- D0 ED BNE $9040 9053- F0 00 BEQ $9055 ; kill a few cycles (not pointless, ; because the disk spins independently ; of the CPU, so all of these low-level ; disk reads are highly time-sensitive) 9055- EA NOP 9056- EA NOP ; read data latch (note: no BPL loop ; here, we're just reading it once) 9057- BC 8C C0 LDY $C08C,X ; do a compare to set or clear the ; carry bit (among other things, but ; it's the carry bit we care about) 905A- C0 08 CPY #$08 ; rotate the carry into the low bit of ; the accumulator 905C- 2A ROL ; if we just rolled a "1" bit out of ; the high bit of the accumulator, take ; this branch 905D- B0 0B BCS $906A ; next nibble needs to be $FF 905F- BC 8C C0 LDY $C08C,X 9062- 10 FB BPL $905F ; ...otherwise we start over 9064- C0 FF CPY #$FF 9066- D0 D8 BNE $9040 ; loop back to get next nibble 9068- F0 EB BEQ $9055 ; execution continues here (from $905D) ; get another nibble 906A- BC 8C C0 LDY $C08C,X 906D- 10 FB BPL $906A ; stash it in zero page 906F- 84 FC STY $FC ; if the accumulator is anything but ; %00001010, start over 9071- C9 0A CMP #$0A 9073- D0 CB BNE $9040 I got lost several times trying to follow this routine. I think the easiest way to explain it is to show the difference between the original disk and my non-working copy. Here is the original disk, as seen by the Copy II+ nibble editor. Nibbles with extra "0" bits (timing bits) after them are displayed in inverse on an original machine, marked here with a "+" after the nibble. --v-- COPY ][ PLUS BIT COPY PROGRAM 8.4 (C) 1982-9 CENTRAL POINT SOFTWARE, INC. --------------------------------------- TRACK: START: 1B1E LENGTH: 17C1 1C70: 9F EB E5 FC D7 D7 D7 EE VIEW 1C78: FA E6 E6 FF FE F2 ED FD 1C80: FF EF ED BA BB DD AF E6 1C88: B7 A7 CB B7 DE AA EB FF 1C90: FF FF FF FB+FF FF+FF FF+ 1C98: FD FF+FF+FF+FF+FF+FF+FF+ 1CA0: FF+FF+D5 AA 96 AA AB AA 1CA8: AA AA AB AA AA DE AA EB+ 1CB0: FF+FF+FF+FF+FF+FF D5 AA --------------------------------------- A TO ANALYZE DATA ESC TO QUIT ? FOR HELP SCREEN / CHANGE PARMS Q FOR NEXT TRACK SPACE TO RE-READ --^-- It's easy to understand why a simple sector copy failed. The sequence that this code is looking for starts at offset $1C93, which is between the end of one sector and the beginning of the next. (The data epilogue is at $1C8C; the next address prologue is at $1CA2.) Sector copiers discard everything between those delimiters and rebuild the track with a default pattern of sync bytes. That pattern doesn't include an $FB nibble, so the nibble check fails. But the EDD bit copy also failed. Here is the original disk's pattern at offset $1C93: - $FB + timing bit - $FF - $FF + timing bit - $FF - $FF + timing bit And here is what the same part of the track looks like on my failed EDD copy: --v-- COPY ][ PLUS BIT COPY PROGRAM 8.4 (C) 1982-9 CENTRAL POINT SOFTWARE, INC. --------------------------------------- TRACK: START: 1B1E LENGTH: 17C1 1C70: 9F EB E5 FC D7 D7 D7 EE VIEW 1C78: FA E6 E6 FF FE F2 ED FD 1C80: FF EF ED BA BB DD AF E6 1C88: B7 A7 CB B7 DE AA EB FF 1C90: FF FF FF FB+FF FF FF+FF+ 1C98: FD FF+FF+FF+FF+FF+FF+FF+ 1CA0: FF+FF+D5 AA 96 AA AB AA 1CA8: AA AA AB AA AA DE AA EB+ 1CB0: FF+FF+FF+FF+FF+FF D5 AA --------------------------------------- A TO ANALYZE DATA ESC TO QUIT ? FOR HELP SCREEN / CHANGE PARMS Q FOR NEXT TRACK SPACE TO RE-READ --^-- A subtle difference! The sequence at offset $1C93 now looks like this: - $FB + timing bit - $FF - $FF - $FF + timing bit - $FF + timing bit This code is looking for $FF bytes with an alternating pattern of timing bit, no timing bit, timing bit, no timing bit. It doesn't find that on the bit copy, so it knows it's not running on an original disk. Continuing the code listing... ; get a nibble 9075- BD 8C C0 LDA $C08C,X 9078- 10 FB BPL $9075 ; more bit twiddling 907A- 38 SEC 907B- 2A ROL ; AND it with the previously stashed ; nibble 907C- 25 FC AND $FC 907E- 49 FF EOR #$FF ; branch to failure path 9080- D0 04 BNE $9086 ; success path is here -- just turn off ; the drive motor and return to the ; immediate caller 9082- DD 88 C0 CMP $C088,X 9085- 60 RTS ; failure path (a.k.a. "The Badlands", ; from which there is no return) 9086- A8 TAY 9087- DD 88 C0 CMP $C088,X ; manually pop the return address of ; the immediate caller (which leaves ; the manually pushed $C5FF address on ; the top of the stack to "return" to) 908A- 68 PLA 908B- 68 PLA ; destroy all trace of this program in ; memory 908C- 99 00 90 STA $9000,Y 908F- C8 INY 9090- C0 8B CPY #$8B 9092- D0 F8 BNE $908C ; "return" to $C5FF+1, i.e. reboot 9094- 60 RTS That explains the behavior that I saw on my non-working copy (endlessly rebooting). This was called from $9022, so let's continue from $9025. *9025L ; remove the dummy address that we ; manually pushed to the stack earlier ; (which would reboot if "returned" to) 9025- 68 PLA 9026- 68 PLA 9027- 60 RTS This program literally does nothing I want. I could probably just delete the line in HELLO that BRUNs it, except I'm afraid to disturb a BASIC program by modifying it and re-saving it. (I've seen other disks where the HELLO program fakes its own size and hides some critical assembly language routine in the free space that follows. I'm not saying that that's happening here, but life is short and re-saving a BASIC program can be surprisingly invasive.) Instead, I turn to my trusty Copy ][+ sector editor (version 5.5, because later versions removed the handy "follow file" function). I press "F" to follow a file, select "TOC.6" from the directory listing, and I find the first sector on T22,S0A. The first four bytes are the address and length (as with any DOS 3.3 binary file), so the fifth byte is where I need to put an "RTS". T22,S0A,$04 change "20" to "60" Success! The disk boots and loads with no complaint. There doesn't appear to be any further protection. Quod erat liberandum. ~ Changelog 2015-09-06 - Vastly improved explanation of the actual protection routine. Thanks to qkumba for pointing out that my original explanation was inaccurate. 2014-08-14 - initial release --------------------------------------- A 4am crack No. 112 ------------------EOF------------------