------------Alpine Tram Ride----------- A 4am crack 2015-07-15 -------------------. updated 2015-09-06 |___________________ Name: Alpine Tram Ride Genre: educational Year: 1985 Publisher: Litag Media: single-sided 5.25-inch floppy OS: DOS 3.3 Other cracks: none Similar cracks: Sliding Block (crack no. 290) ~ Chapter 0 In Which Various Automated Tools Fail In Interesting Ways COPYA no errors, but copy loads DOS, clears hi-res graphics screen, then reboots Locksmith Fast Disk Backup ditto EDD 4 bit copy (no sync, no count) ditto Disk Fixer T00 -> looks like DOS 3.3 RWTS T00-T02 -> looks like full DOS 3.3 T01,S09 -> startup program is "L.T.I." Why didn't any of my copies work? probably a nibble check in the startup program A well-timed gets me a prompt, but any command reboots again. Next steps: 1. Load up the startup program from my work disk 2. Find and disable the nibble check 3. There is no step 3 (I hope) ~ Chapter 1 Hide Your Disk Catalog With This One Weird Trick! Crackers Hate It! Booting from my work disk, my non- working copy ought to have a catalog, but it has suspiciously vanished. [S6,D1=non-working copy from COPYA] [S5,D1=my work disk] ]PR#5 ... ]CATALOG,S6,D1 C1983 DSR^C#254 294 FREE ] Wait, what? [Disk Fixer] --> T11,S00 Looks like a standard DOS 3.3 VTOC. It points to T11,S0F as the first catalog sector. --> T11,S0F Blank. In fact, the entire rest of the track is blank. --> T10,S0F nope --> T0F,S0F nope --> T0E,S0F nope ... --> T03,S0F Ah, there it is! --v-- -------------- DISK EDIT -------------- TRACK $03/SECTOR $0F/VOLUME $FE/BYTE$00 --------------------------------------- $00:>00<03 0E 00 00 00 00 00 @CN@@@@@ $08: 00 00 00 12 0F 80 CC 8C @@@RO.L. $10: D4 94 C9 89 A0 A0 A0 A0 T.I. $18: A0 A0 A0 A0 A0 A0 A0 A0 $20: A0 A0 A0 A0 A0 A0 A0 A0 $28: A0 A0 A0 A0 03 00 12 0C C@RL $30: 84 CC C9 D4 C1 C7 CD A0 .LITAGM $38: A0 A0 A0 A0 A0 A0 A0 A0 $40: A0 A0 A0 A0 A0 A0 A0 A0 $48: A0 A0 A0 A0 A0 A0 A0 15 U $50: 00 13 07 84 D0 D2 C5 D3 @SG.PRES $58: D3 A0 A0 A0 A0 A0 A0 A0 S $60: A0 A0 A0 A0 A0 A0 A0 A0 $68: A0 A0 A0 A0 A0 A0 A0 A0 $70: A0 A0 02 00 13 05 84 CC B@SE.L $78: D4 C9 CD A0 A0 A0 A0 A0 TIM --------------------------------------- BUFFER 0/SLOT 6/DRIVE 1/MASK OFF/NORMAL --------------------------------------- COMMAND : _ --^-- But how does the original disk know where to look? I scoured "Beneath Apple DOS" until I found the answer on p8-28: --v-- B011-B036 Read a directory sector ; (If CARRY flag is zero on entry, read first directory sector. If CARRY is one, read next) ; Memorize entry code. ; Set buffer pointers (B045). ; First or next? ; If first, get track/sector of directory sector from VTOC at offset +1,+2. ; Otherwise, get track/sector from directory sector at offset +1,+2. If track is zero, exit with error code (end of directory). ; Call RWTS to read sector. ; Exit with normal return code. --^-- So, to read the first sector of file names and other metadata, this routine is supposed to look at the VTOC sector buffer (read from T11,S00 and stored at $B3BB..$B4BA). The VTOC says "hey, the first sector of files and stuff is in T11,S0F" so this routine is supposed to read T11,S0F. But the DOS on this disk made one small modification to that routine. (This is on T01,S0F.) B011- 08 PHP B012- 20 45 B0 JSR $B045 B015- 28 PLP B016- B0 08 BCS $B020 B018- AC BD B3 LDY $B3BD ------ B01B- A2 03 LDX #$03 << hey B01D- EA NOP << now ------ B01E- D0 0A BNE $B02A B020- AE BC B4 LDX $B4BC B023- D0 02 BNE $B027 B025- 38 SEC B026- 60 RTS B027- AC BD B4 LDY $B4BD B02A- 8E 97 B3 STX $B397 B02D- 8C 98 B3 STY $B398 B030- A9 01 LDA #$01 B032- 20 52 B0 JSR $B052 B035- 18 CLC B036- 60 RTS Instead of getting the track number from the VTOC, it hard-codes track $03. Now that I've identified the problem, the fix is straightforward. If I change the VTOC header (T11,S00) to point to the actual first directory sector (T03,S0F), DOS 3.3 or any other copy utility should be able to read the disk catalog. T11,S00,$01 change "11" to "03" ]PR#5 ... ]CATALOG,S6,D1 C1983 DSR^C#254 294 FREE *T 003 LTI *B 021 LITAGM *B 002 PRESS *B 013 LTIM *A 007 *B 043 *A 011 *B 034 DIR *B 032 TITLE A 007 JELLO A 011 JAME B 002 MELLO Ah! Looking more closely at T01,S09 in Disk Fixer, it appears that the startup program is not "L.T.I." but rather "LTI". Lovely. ~ Chapter 2 In Which We Discover That All That Glitters Is Not Suspicious The startup program is LTI (plus some control characters which I'm already tired of typing). More important than the name is the fact that it's a text file. Not something you see every day, but DOS 3.3 supports it. It EXECs the file, treating it as a series of typed commands. [Copy II Plus 8.4] [VIEW FILES] [TEXT] [select "LTI"] VIEW FILES TEXT SLOT 6 DRIVE 1 DISK VOLUME 254 FILE: LTI PRESS [RETURN] 512 --------------------------------------- NOMONI,O,C HGR:POKE49234,0:D$=CHR$(4):HOME:CALL-15 1 400:A9 06 8D 31 04 A9 1D 8D 35 04 20 20 04 EE 31 04 EE 35 04 20 20 04 EE 31 04 EE 35 04 20 20 04 60 A9 04 A0 2C 20 D9 03 A9 00 85 48 60 01 60 01 00 02 06 3D 04 00 1D 00 00 01 00 FE 60 01 00 01 FF D8 N 400G D43CG H$="MELLO" PRINTD$;"BRUN";H$; Well, that certainly looks suspicious. Typing in those bytes at $2400 gives me this short routine: 2400- A9 06 LDA #$06 2402- 8D 31 04 STA $0431 2405- A9 1D LDA #$1D 2407- 8D 35 04 STA $0435 240A- 20 20 04 JSR $0420 240D- EE 31 04 INC $0431 2410- EE 35 04 INC $0435 2413- 20 20 04 JSR $0420 2416- EE 31 04 INC $0431 2419- EE 35 04 INC $0435 241C- 20 20 04 JSR $0420 241F- 60 RTS 2420- A9 04 LDA #$04 2422- A0 2C LDY #$2C 2424- 20 D9 03 JSR $03D9 2427- A9 00 LDA #$00 2429- 85 48 STA $48 242B- 60 RTS 242C- 01 60 01 00 02 06 3D 04 00 1D 00 00 01 00 FE 60 01 00 01 FF D8 Taking into account that this will execute at $0400, it looks like a standard multi-sector read loop. The data structure at $042C is a standard RWTS parameter table. It doesn't call the code it loads from disk; it just loads it and exits. Next is a call to $D43C, which is the Applesoft BASIC "warm start" routine. Next it BRUNs MELLO. ~ Chapter 3 In Which We Stare Into Infinity And Infinity Stares Back And Executes A DOS Command ]BLOAD MELLO ]CALL -151 *BF55.BF56 BF55- 00 90 (Diversi-DOS 64K stores the program start location in $BF55. In standard DOS 3.3, it's at $AA72.) *9000L ; get address of RWTS parameter table 9000- 20 E3 03 JSR $03E3 9003- 85 FB STA $FB 9005- 84 FA STY $FA ; hmm 9007- A9 C5 LDA #$C5 9009- 48 PHA ; copy RWTS parameters 900A- A9 00 LDA #$00 900C- 85 FC STA $FC 900E- A2 03 LDX #$03 9010- BC 49 90 LDY $9049,X 9013- 91 FA STA ($FA),Y 9015- CA DEX 9016- 10 F8 BPL $9010 9018- 8A TXA ; hmm 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 3C 90 JSR $903C *903CL ; call the RWTS (most likely just to ; move the drive head to the proper ; position for an impending nibble ; check) 903C- 20 E3 03 JSR $03E3 903F- 20 D9 03 JSR $03D9 9042- A9 00 LDA #$00 9044- 85 48 STA $48 ; if that fails, off to The Badlands 9046- B0 5E BCS $90A6 9048- 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 59 90 JSR $9059 *9059L ; turning on the drive motor manually ; is always suspicious 9059- BD 89 C0 LDA $C089,X ; initialize death counters 905C- A9 56 LDA #$56 905E- 85 FD STA $FD 9060- A9 08 LDA #$08 9062- C6 FC DEC $FC 9064- D0 04 BNE $906A 9066- C6 FD DEC $FD ; if death counter hits 0, give up 9068- F0 3C BEQ $90A6 ; look for an $FB nibble 906A- BC 8C C0 LDY $C08C,X 906D- 10 FB BPL $906A 906F- C0 FB CPY #$FB 9071- D0 ED BNE $9060 9073- F0 00 BEQ $9075 ; 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) 9075- EA NOP 9076- EA NOP ; read data latch (note: no BPL loop ; here, we're just reading it once) 9077- 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) 907A- C0 08 CPY #$08 ; rotate the carry into the low bit of ; the accumulator 907C- 2A ROL ; if we just rolled a "1" bit out of ; the high bit of the accumulator, take ; this branch 907D- B0 0B BCS $908A ; next nibble needs to be $FF 907F- BC 8C C0 LDY $C08C,X 9082- 10 FB BPL $907F ; ...otherwise we start over 9084- C0 FF CPY #$FF 9086- D0 D8 BNE $9060 ; loop back to get next nibble 9088- F0 EB BEQ $9075 ; execution continues here (from $907D) ; get another nibble 908A- BC 8C C0 LDY $C08C,X 908D- 10 FB BPL $908A ; stash it in zero page 908F- 84 FC STY $FC ; if the accumulator is anything but ; %00001010, start over 9091- C9 0A CMP #$0A 9093- D0 CB BNE $9060 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 9095- BD 8C C0 LDA $C08C,X 9098- 10 FB BPL $9095 ; more bit twiddling 909A- 38 SEC 909B- 2A ROL ; AND it with the previously stashed ; nibble 909C- 25 FC AND $FC 909E- 49 FF EOR #$FF ; branch to failure path 90A0- D0 04 BNE $90A6 ; success path falls through to here -- ; turn off the drive motor and return ; to the caller 90A2- DD 88 C0 CMP $C088,X 90A5- 60 RTS ; failure path (a.k.a. "The Badlands", ; from which there is no return) 90A6- A8 TAY 90A7- 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) 90AA- 68 PLA 90AB- 68 PLA ; destroy all trace of this program in ; memory 90AC- 99 00 90 STA $9000,Y 90AF- C8 INY 90B0- C0 8B CPY #$8B 90B2- D0 F8 BNE $90AC ; "return" to $C5FF+1, i.e. reboot 90B4- 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 ; pop the bogus return value ($C5FF) ; off the stack 9025- 68 PLA 9026- 68 PLA ; Route a series of bytes through the ; DOS output vector at ($36). This is ; how binary files "execute" DOS 3.3 ; commands, like PRINT CHR$(4)"..." in ; Applesoft BASIC. This is totally ; legitimate code, cleverly disguised ; as an infinite loop. 9027- A0 00 LDY #$00 9029- 84 FE STY $FE 902B- B9 4D 90 LDA $904D,Y 902E- 09 80 ORA #$80 9030- 20 39 90 JSR $9039 9033- A4 FE LDY $FE 9035- C8 INY 9036- 4C 29 90 JMP $9029 9039- 6C 36 00 JMP ($0036) Let's see what it's printing. *FC58G N 400<904D.9058M DRUN JELLOM@ ^ ^^ control characters Aha! It's not an infinite loop after all. Well, it is, technically, but it won't actually run forever. It's not easy to show in plain text, but the initial "D" and the final "M@" are in inverse. So that string starts with Ctrl-D and ends with Ctrl-M and a null byte. Since DOS is already loaded, printing this through the DOS vector will execute that command, as if you typed it from a prompt yourself. Since that program never returns to the caller, it will break out of the seemingly "infinite" loop. Since there are no side effects to the copy protection routine, I can change this file to simply skip the call to $9059 and continue on to running the real startup program. Using my trusty Disk Fixer sector editor, I press "D" for directory mode, select "MELLO", and I find this code on T1D,S06. T1D,S06,$26 change "20" to "2C" 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. 2015-07-15 - initial release --------------------------------------- A 4am crack No. 369 ------------------EOF------------------