---------------Time Zone--------------- A 4am and san inc crack 2018-02-03 --------------------------------------- Name: Time Zone Version: 1.1 Genre: adventure Year: 1982 Credits: by Ken and Roberta Williams; produced by Bob Davis and Jeff Stephenson; graphics by Terry Pierce and Michelle Pritchard Publisher: On-Line Systems Platform: Apple ][+ or later (48K) Media: 6 double-sided 5.25-inch disks (12 sides total) OS: DOS 3.3 This disk was automatically cracked by Passport, using qkumba's universal Sierra patcher. Here is the transcript for disk A: --v-- READING FROM S6,D1 T00,S00 FOUND DOS 3.3 BOOTLOADER USING DISK'S OWN RWTS WRITING TO S5,D2 T03,S0F FOUND SIERRA PROTECTION CHECK RESTARTING SCAN T03,S0B,$F0: 20 -> 2C CRACK COMPLETE. PRESS ANY KEY --^-- More information and source code is available at https://archive.org/details/Passport4am Other sides are unprotected. Quod erat liberand one more thing... ~ Every DOS 3.3-formatted Apple II floppy has a "disk volume number." It is set when you format the disk, is stored in the address field of every sector, and is displayed when you issue the CATALOG command. The disk volume number is almost always ignoreable, except when it isn't. In this game, it isn't. All 12 sides, including the boot disk, and even the data disks you create to save and restore games, have non- standard disk volume numbers. - disk A: 002 - disk B: 003 - disk C: 004 - disk D: 005 - disk E: 006 - disk F: 007 - disk G: 008 - disk H: 009 - disk I: 010 - disk J: 011 - disk K: 012 - disk L: 013 - data disks (created within the game by pressing "4"): 252 And they matter. The game uses the disk volume number to know whether the right disk is in the drive at any particular time. Why is this a problem? In the physical world, it isn't. Unprotected disks can have any disk volume number and still be copyable with third-party tools. But it's 2018, and we don't traffic in physical objects anymore; we traffic in disk images. Due to poor historical choices, the dominant format for disk images, .DSK, does not store the disk volume number. This presents us with a modern dilemma of our own design: either release disk images in some other format (easy, but could reduce compatibility or require more disk space or both), or change the game code to distinguish disks in some other way (hard, game-specific, insane, why would you do that). Obviously, we chose the hard way. To distinguish disks that are really .DSK files, we need to store a unique marker somewhere on the disk that will actually be stored in the .DSK file. Since .DSK files only contain sector data from track $00 to track $22, that means we need to store a byte in a sector. That part is non-negotiable. Data must be stored somewhere, and if .DSK files only store sector data, we need to store the fake volume number as sector data. Of course, the game doesn't make this easy for us. There isn't actually one sector that is unused across all disks. (Trust me, we checked.) But that's not even the worst part. This game has an in-game option to format a data disk. The data disk is formatted with -- you guessed it -- a nonstandard disk volume number, which is later checked to ensure the data disk is in the drive during the "SAVE GAME" or "RESTORE GAME" command. Here's what we're going to do. We're going to store the disk volume number in a sector that has real data, AND intercept the game's low-level disk reading routine so that instead of getting the disk volume from the address field, it gets it from that magic byte in that magic sector, AND intercept low-level format commands to write the disk volume number to that magic byte of that magic sector, AND intercept low-level read commands of that magic sector to return the real sector data that belongs there instead of the magic byte, AND cache the magic byte from the magic sector so we're not actually reading the magic sector for every single disk access (which would totally destroy the performance of an already slow medium). I said it was insane. You were warned. ~ Our code begins at $BEB0. This region is supposed to be part of the format disk routine, but it is now unused for reasons that will be explained shortly. [Note: source code for this routine was provided along with this write-up. If you didn't get it, well, I don't know what to tell you. In the long run we are all dead.] [Note the second: qkumba wrote this code. I mostly just cheered.] *BEB0L ; Save address of RWTS parameter table BEB0- 84 48 STY $48 BEB2- 85 49 STA $49 ; Check the requested disk volume BEB4- A0 03 LDY #$03 BEB6- B1 48 LDA ($48),Y ; 0 = wildcard = always continue BEB8- F0 4A BEQ $BF04 ; Otherwise check if it's $FE BEBA- AA TAX BEBB- E8 INX BEBC- E0 FF CPX #$FF ; No, it's a non-standard disk volume BEBE- 90 02 BCC $BEC2 ; Yes, it was $FE -- change it to $02 ; to fake out the caller (the game will ; refuse to boot on a disk with the ; wrong disk volume, because f--- you) BEC0- A9 02 LDA #$02 ; Either way, store the requested disk ; volume because we'll need it later BEC2- 8D FE BE STA $BEFE ; If we're trying to read the boot disk, ; skip ahead (the carry is still set ; from the CMP at $BEBC) BEC5- B0 3D BCS $BF04 ; The other disks need special handling ; because they can be requested at any ; time. First off, fake out the real ; RWTS by putting a 0 in the requested ; disk volume BEC7- A9 00 LDA #$00 BEC9- 91 48 STA ($48),Y BECB- CA DEX ; X holds the requested disk volume -- ; check if it's the same as the last ; RWTS command BECC- EC 00 BF CPX $BF00 ; Yes, they match, which means we can ; go ahead and execute the RWTS command BECF- F0 33 BEQ $BF04 ; No, this RWTS command is requesting a ; different disk volume than the last ; RWTS command. It's time for our magic ; act! BED1- 20 30 BF JSR $BF30 Track $22, sector $0F is the best choice to store our fake disk volume byte, for one reason: it is unused on the save game disk. None of the other disks are writeable, so we won't have to intercept writes to the sector, which simplifies the code enormously. *BF30L ; swap out the requested track/sector/ ; command with ; track = $22 ; sector = $0F ; address = $BF00 ; command = $01 (read) ; and save the original values so we ; can restore them later BF30- A2 08 LDX #$08 BF32- BC 47 BF LDY $BF47,X BF35- B1 48 LDA ($48),Y BF37- 48 PHA BF38- BD 46 BF LDA $BF46,X BF3B- 91 48 STA ($48),Y BF3D- 68 PLA BF3E- 9D 46 BF STA $BF46,X BF41- CA DEX BF42- CA DEX BF43- 10 ED BPL $BF32 BF45- 60 RTS BF46- 01 0C BF 09 00 08 0F 05 22 04 Continuing from $BED4... ; After swapping the RWTS parameters, ; the accumulator has the original RWTS ; command. Check if it's a format ; command -- i.e. the game is trying to ; initialize a disk for saved games. BED4- C9 04 CMP #$04 ; no (whew) BED6- D0 08 BNE $BEE0 ; Yes, this is a format command. Ugh. ; We're not really going to format the ; disk. Instead, we're going to assume ; the disk is already formatted, and ; write the disk volume as regular data ; in our special sector (T22,S0F). ; First, convert $04 (format command) ; to $02 (write command). BED8- 4A LSR BED9- 91 48 STA ($48),Y ; The expected disk volume of a save ; game disk is $0A, so set that as the ; first byte in the buffer BEDB- A9 0A LDA #$0A BEDD- 8D 00 BF STA $BF00 ; alter underlying RWTS code so it only ; reads the first byte of the sector BEE0- EE 3D BE INC $BE3D BEE3- 20 2D BF JSR $BF2D *BF2DL ; call the real RWTS BF2D- 20 04 BD JSR $BD04 After the real RWTS returns, $BF2D will fall through to $BF30, which is the routine we called earlier to swap the track/sector/command in the RWTS parameter table. So now they've swapped back to their original values, and we have the first byte of T22,S0F in $BF00 (which is actually right in the middle of our code, which is totally the kind of thing that qkumba would do). Continuing from $BEE6... ; restore underlying RWTS code so it ; reads full sectors again BEE6- CE 3D BE DEC $BE3D ; Restore read command BEE9- A9 01 LDA #$01 BEEB- 8D 46 BF STA $BF46 ; If the real RWTS came back with an ; error, cancel all the magic and ; propagate the error back to the ; caller BEEE- B0 2E BCS $BF1E ; Set the error in the RWTS parameter ; table to "disk volume mismatch." ; (This will only be checked by the ; caller if we return with the carry ; bit set, which we haven't decided ; yet, so it's safe to do this now.) BEF0- B1 48 LDA ($48),Y BEF2- AA TAX BEF3- C8 INY BEF4- A9 20 LDA #$20 BEF6- 91 48 STA ($48),Y ; If the original RWTS command was $04 ; (format), we've done all the magic ; we're going to do today, so tell the ; caller that it worked and be happy. BEF8- E0 04 CPX #$04 BEFA- 18 CLC BEFB- F0 21 BEQ $BF1E ; These next two instructions are self- ; modifying code. $BEFE is the disk ; volume that was originally requested ; (set at $BEC2), and $BF00 is the disk ; volume that we want the caller to ; believe is currently in the drive ; (set by the underlying RWTS after we ; hacked it to read only one byte from ; T22,S0F). BEFD- A9 01 LDA #$01 BEFF- C9 01 CMP #$01 ; If they don't match, set the carry to ; indicate an error (we already set the ; RWTS error code to "disk volume ; mismatch, at $BEF6), then exit via ; the cleanup routine at $BF1E which ; will complete the illusion by setting ; the requested and found disk volume ; in the RWTS parameter table. BF01- 38 SEC BF02- D0 1A BNE $BF1E There are 4 possible ways we can end up here. 1) The caller requested a wildcard disk volume ($00), so we branched from $BEB8. 2) The caller requested disk volume $FE, so we branched from $BEC5. 3) The caller requested a disk volume of one of the game disks ($02-$0D, or $FC to read from the save game disk), but it was the same disk volume as the previous RWTS call. We assume the correct disk is already in the drive and branched here from $BECF. 4) The caller requested a disk volume of one of the game disks, it was different from the previous RWTS call, we did our magic to get the fake volume from T22,S0F and discovered that the correct disk is now in the drive, so we fell through from $BF02. ; Execute the original RWTS command BF04- 20 04 BD JSR $BD04 ; Didn't work, propagate the error and ; exit BF07- B0 15 BCS $BF1E Hooray! The original RWTS command succeeded! Just one last thing... ~ ; Was this the game trying to read ; T22,S0F? BF09- A0 04 LDY #$04 BF0B- B1 48 LDA ($48),Y BF0D- C9 22 CMP #$22 BF0F- D0 0C BNE $BF1D BF11- C8 INY BF12- B1 48 LDA ($48),Y BF14- C9 0F CMP #$0F BF16- D0 05 BNE $BF1D ; Yes, which means the first byte of ; the sector data we just read is the ; fake disk volume. Change that to the ; real data before returning to the ; caller. Presto-chango! BF18- A9 00 LDA #$00 BF1A- A8 TAY BF1B- 91 3E STA ($3E),Y [In this game, all disks have a #$00 as the first byte of T22,S0F, so swapping out the real sector data is easy -- it's always #$00! But if that weren't the case, we could keep a map of expected data indexed by disk volume. That may be required if we use this code for other games.] [This technique is called 4shadowing.] ; Reset the carry to tell the caller ; that the RWTS command succeeded. (The ; CMPs to check the track and sector ; messed it up.) BF1D- 18 CLC All code paths lead here. This is the final cleanup that we always do before returning to the caller: setting the requested and found disk volumes in the RWTS parameter table. This does not affect the carry bit. BF1E- A0 03 LDY #$03 BF20- AD FE BE LDA $BEFE BF23- 91 48 STA ($48),Y BF25- A0 0E LDY #$0E BF27- AD 00 BF LDA $BF00 BF2A- 91 48 STA ($48),Y BF2C- 60 RTS Now, each disk gets its own magic byte on track $22, sector $0F: disk A: T22,S0F,$00: 00 -> 02 disk B: T22,S0F,$00: 00 -> 03 disk C: T22,S0F,$00: 00 -> 04 disk D: T22,S0F,$00: 00 -> 05 disk E: T22,S0F,$00: 00 -> 06 disk F: T22,S0F,$00: 00 -> 07 disk G: T22,S0F,$00: 00 -> 08 disk H: T22,S0F,$00: 00 -> 09 disk I: T22,S0F,$00: 00 -> 0A disk J: T22,S0F,$00: 00 -> 0B disk K: T22,S0F,$00: 00 -> 0C disk L: T22,S0F,$00: 00 -> 0D Finally, we need one single patch. All RWTS calls go through $B7B5, which in turn calls $BD00. If we change that to call $BEB0 instead, the illusion will be complete. T00,S01,$B8: 00BD -> B0BE ~ Are we done? Well, yes and no. The next patch is not protection-related. It's not even disk-format-related. It's more of an aesthetic patch. This game has a "main menu" of sorts where you can play the game, initialize a save game disk, or back up the disks. Playing the game works -- Passport took care of that. Initializing a save game disk works, in the sense that our RWTS patch intercepts the format command and writes out the magic byte on T22,S0F of the save game disk. But the built-in backup program does not work. It does some sort of custom checksumming to ensure that the disks are not damaged. Of course, they are damaged -- we've removed the copy protection. ("Damage" is relative.) There is really no point in having disk specific backup functions inside a game that's being released as a series of .dsk files in 2018. Rather than trying to fix up a program that no one is ever going to use, we decided to remove it. The menu text is on T03,S0C. We simply changed "Initialize save game disk" from option 3 to option 2, and blanked out the rest of the space. The code that drives the menu is also on track $03. It is straightforward. --v-- T03,S00 ----------- DISASSEMBLY MODE ---------- ; get key from input buffer 0039:AD 00 02 LDA $0200 ; handle "1" 003C:C9 B1 CMP #$B1 ; "1" 003E:D0 03 BNE $0043 0040:4C BC 08 JMP $08BC ; handle "2" 0043:C9 B2 CMP #$B2 ; "2" 0045:D0 03 BNE $004A 0047:20 00 0A JSR $0A00 ; handle "3" 004A:C9 B3 CMP #$B3 ; "3" 004C:D0 03 BNE $0051 004E:20 84 08 JSR $0884 --^-- The exact patches were ; disable old "2" handler (JSR $0A00) T03,S00,$47: 20 -> 2C ; change "3" handler to trigger on "2" T03,S00,$4B: B3 -> B2 Quod erat liberandum. --------------------------------------- A 4am and san inc crack No. 1677 ------------------EOF------------------