--------Facemaker Golden Edition------- A 4am crack 2017-06-10 --------------------------------------- Name: Facemaker Golden Edition Genre: educational Year: 1986 Credits: program by James Bach artwork by Bill Groetzinger Dale Disharoon, Inc. Publisher: Spinnaker Software Media: single-sided 5.25-inch floppy OS: DOS 3.3 Previous cracks: none Similar cracks: #279 The Math Busters ~ Chapter 0 In Which Various Automated Tools Fail In Interesting Ways COPYA no errors, but copy hangs after title screen Locksmith Fast Disk Backup ditto EDD 4 bit copy (no sync, no count) ditto Copy ][+ nibble editor nothing unusual Disk Fixer T00-T02 -> looks like DOS 3.3 T01,S09 -> startup program is "FACEMAKER" T11 -> DOS-style disk catalog Why didn't any of my copies work? I don't know. Maybe a nibble check called from the startup program? Next steps: 1. Trace startup program 2. Find nibble check and disable it 3. Declare victory (*) (*) go to the gym ~ Chapter 1 In Which We Quickly Descend Into A Hellish Nightmare Of Encryption And Obfuscation [S6,D1=original disk] ]PR#6 gets me a working prompt ]CATALOG DISK VOLUME 254 A 002 FACEMAKER A 003 LOADER B 033 TITLE B 005 TEXT FONT B 010 FONT B 055 PROGRAM B 005 TABLES ]LIST 10 HOME 20 POKE 104,65: POKE 16384 + 25 6,0 30 PRINT "RUN LOADER" ]LOAD LOADER ]LIST 5 HOME 15 PRINT "BLOAD TITLE,A$2000" 16 VTAB 22: HTAB 13: PRINT "COP YRIGHT 1986": HTAB 9: PRINT "SPINNAKER SOFTWARE CORP.": VTAB 24: HTAB 11: PRINT "ALL RIGH TS RESERVED"; 17 POKE - 16297,0: POKE - 163 00,0: POKE - 16301,0: POKE - 16304,0 20 VTAB 1: PRINT 30 PRINT "BLOAD TEXT FONT,A$140 0" 40 PRINT "BLOAD FONT,A$C00" 50 PRINT "BLOAD TABLES" 70 VTAB 1: PRINT 80 PRINT "BLOAD PROGRAM" 90 POKE - 16302,0 91 IF PEEK (49420) = 20 THEN 9 4 92 IF PEEK (49413) = 56 THEN 9 5 93 IF PEEK (49669) = 56 THEN 9 5 94 POKE 0,0: GOTO 100 95 PRINT CHR$ (4);"PR#1" 97 PRINT CHR$ (13); 99 PRINT CHR$ (4);"PR#0": POKE 0,255 100 CALL 24576 A quick test confirms that my non- working copy gets as far as line 100, so I'm guessing the problem is in the assembly language code at $6000 (24576 in decimal). ]100 END ]RUN ]CALL -151 *6000L ]BLOAD BOOTER.OBJ,A$92D0 ]CALL -151 *6000L 6000- 4C E7 94 JMP $94E7 *94E7L ; push an address to the stack 94E7- A9 94 LDA #$94 94E9- 48 PHA 94EA- A9 9F LDA #$9F 94EC- 48 PHA ; save some zero page values 94ED- A5 08 LDA $08 94EF- 8D 2D 95 STA $952D 94F2- A5 09 LDA $09 94F4- 8D 2E 95 STA $952E ; probably an address, so ($08) points ; to $9300 94F7- A9 00 LDA #$00 94F9- 85 08 STA $08 94FB- A9 93 LDA #$93 94FD- 85 09 STA $09 ; decryption loop 94FF- A9 96 LDA #$96 9501- 48 PHA 9502- A0 00 LDY #$00 9504- 68 PLA 9505- 51 08 EOR ($08),Y 9507- 91 08 STA ($08),Y 9509- 48 PHA 950A- C8 INY 950B- D0 F7 BNE $9504 950D- E6 09 INC $09 950F- A5 09 LDA $09 ; so 2 pages total, $9300 and $9400 9511- C9 95 CMP #$95 9513- D0 EF BNE $9504 ; more decryption 9515- 68 PLA 9516- 51 08 EOR ($08),Y 9518- 91 08 STA ($08),Y 951A- 48 PHA 951B- C8 INY 951C- 98 TYA 951D- C9 02 CMP #$02 951F- D0 F4 BNE $9515 9521- 68 PLA ; restore zero page 9522- AD 2D 95 LDA $952D 9525- 85 08 STA $08 9527- AD 2E 95 LDA $952E 952A- 85 09 STA $09 ; exit by "returning" to the address ; that we pushed earlier -- ; $94/$9F, so execution continues at ; $94A0 952C- 60 RTS This is self-contained. If I start at $94ED, I can run the decryption and exit gracefully (since I never pushed another address to the stack). *94ED Here's the decrypted code at $94A0: *94A0L ; push another address 94A0- A9 60 LDA #$60 94A2- 48 PHA 94A3- A9 21 LDA #$21 94A5- 48 PHA 94A6- 20 1A 94 JSR $941A *941AL ; get RWTS parameter address 941A- 20 E3 03 JSR $03E3 941D- 8C 38 94 STY $9438 9420- 8D 39 94 STA $9439 ; set up some RWTS parameters 9423- A2 04 LDX #$04 9425- 18 CLC 9426- BD 00 94 LDA $9400,X 9429- 6D 38 94 ADC $9438 942C- 8D 38 94 STA $9438 942F- 90 03 BCC $9434 9431- EE 39 94 INC $9439 9434- BD 05 94 LDA $9405,X 9437- 8D 00 00 STA $0000 943A- CA DEX 943B- 10 E8 BPL $9425 943D- 20 E3 03 JSR $03E3 9440- 20 D9 03 JSR $03D9 9443- 60 RTS *9400.9404 9400- 08 02 01 01 00 The loop works backwards, starting at $9404. Each value is the additional offset into the RWTS parameter table for the next value (also stored backwards, at $9405+). So we're setting RWTS+0, then +1, +2, +4, and +C. *9405.9409 9405- 00 00 01 60 01 RWTS+0 = #$01 (table type, always 1) RWTS+1 = #$60 (slot x16, so slot 6) RWTS+2 = #$01 (drive, so drive 1) RWTS+4 = #$00 (track, so track 0) RWTS+C = #$00 (command, seek) So we're seeking to track 0. Continuing from $94A9... ; turn on the drive motor manually ; (highly suspicious) 94A9- AD EA C0 LDA $C0EA 94AC- AD E9 C0 LDA $C0E9 ; some parameters (maybe nibbles?) 94AF- A9 AB LDA #$AB 94B1- 8D 0C 94 STA $940C 94B4- A9 AF LDA #$AF 94B6- 8D 0B 94 STA $940B 94B9- 20 44 94 JSR $9444 *9444L ; look for self-sync bytes ($FF nibble) 9444- AD EE C0 LDA $C0EE 9447- AD EC C0 LDA $C0EC 944A- 10 FB BPL $9447 944C- C9 FF CMP #$FF 944E- D0 F7 BNE $9447 ; look for a specific nibble sequence ; (including the ones set at $94AF) 9450- AD EC C0 LDA $C0EC 9453- 10 FB BPL $9450 9455- C9 FF CMP #$FF 9457- D0 EE BNE $9447 9459- AD EC C0 LDA $C0EC 945C- 10 FB BPL $9459 945E- C9 FF CMP #$FF 9460- F0 F7 BEQ $9459 9462- A2 07 LDX #$07 9464- 10 05 BPL $946B 9466- AD EC C0 LDA $C0EC 9469- 10 FB BPL $9466 946B- DD 0C 94 CMP $940C,X 946E- D0 D7 BNE $9447 9470- CA DEX 9471- D0 F3 BNE $9466 9473- A2 02 LDX #$02 9475- AD EC C0 LDA $C0EC 9478- 10 FB BPL $9475 947A- DD 0A 94 CMP $940A,X 947D- D0 C8 BNE $9447 947F- CA DEX 9480- D0 F3 BNE $9475 9482- 60 RTS Continuing from $94BC... ; change a value in the array at $9405 ; (the track to seek, was previously 0) 94BC- A9 01 LDA #$01 94BE- 8D 06 94 STA $9406 ; seek again (now to track 1) 94C1- 20 1A 94 JSR $941A ; turn on drive motor again (the RWTS ; call would have turned it off after ; seeking) 94C4- AD E9 C0 LDA $C0E9 ; oh, this is sneaky 94C7- A9 04 LDA #$04 94C9- 8D 7E 94 STA $947E That looks like initialization of some sort of counter or something, but it's really self-modifying code. The address $947E is part of this loop at the end of the subroutine at $9444: 9473- A2 02 LDX #$02 9475- AD EC C0 LDA $C0EC 9478- 10 FB BPL $9475 947A- DD 0A 94 CMP $940A,X 947D- D0 C8 BNE $9447 ^^ ++---- this is $947E 947F- CA DEX 9480- D0 F3 BNE $9475 9482- 60 RTS That means that, the second time $9444 is called, the penalty for failing the comparison loop is that you get booted out to $9483. What's at $9483? I'm guessing the answer is "bad things." *9483L ; not gonna return to the caller 9483- 68 PLA 9484- 68 PLA 9485- 68 PLA 9486- 68 PLA ; turn off drive motor manually 9487- AD E8 C0 LDA $C0E8 948A- 78 SEI ; wipe memory and hang 948B- A9 00 LDA #$00 948D- AA TAX 948E- 9D A0 94 STA $94A0,X 9491- E8 INX 9492- D0 FA BNE $948E 9494- 9D 8E 93 STA $938E,X 9497- CA DEX 9498- D0 FA BNE $9494 949A- CE 96 94 DEC $9496 949D- 4C 94 94 JMP $9494 ...which is exactly the behavior I saw on my non-working copy. Continuing from $94CC... ; set up different nibble parameters 94CC- A9 AB LDA #$AB 94CE- 8D 0D 94 STA $940D 94D1- A9 AB LDA #$AB 94D3- 8D 0C 94 STA $940C 94D6- A9 AB LDA #$AB 94D8- 8D 0B 94 STA $940B ; and call nibble check again, but this ; time it won't return unless the disk ; is an original 94DB- 20 44 94 JSR $9444 ; turn off drive motor and exit via the ; address we pushed at $94A0 -- so ; execution continues at $6022 94DE- AD E8 C0 LDA $C0E8 94E1- A0 00 LDY #$00 94E3- AD B3 FB LDA $FBB3 94E6- 60 RTS Now I see how this protection check works. It's a track synchronization protection. On the first call to $9444, it searches a nibble sequence including $AB $AF (set at $94AF). The rest of the sequence is taken from the 8 nibbles at $940D (in reverse order) and the two nibbles at $940B (also in reverse). *940D. 9408- .. .. .. .. .. AA AA FE 9410- FF 96 AA D5 AD AA D5 EB 9418- AA DE So the first time (on track 0), we find $D5 $AA $96 $FF $FE $AA $AA $AB $AF, which is part of the address prologue for track 0, sector 7. ($AA $AA as a 4-and-4 encoded value is 0. $AB $AF as a 4-and-4 encoded value is 7.) The second time (on track 1), we set $940B..$940D to $AB $AB $AB, so we end up looking for $D5 $AA $96 $FF $FE $AA $AB $AB $AB, which is part of the address prologue for track 1, sector 3. On the original disk, tracks 0 and 1 are physically synchronized so that, after finding sector 7 on track 0, a "blind" seek to track 1 will find sector 3 next. But this depends on the relative physical layout of the two tracks on disk; a copy program would not maintain this relative layout unless you specifically told it to. (Bit copiers did have a "synchronize tracks" option for just such an occasion.) If the physical layout of the sectors on tracks 0 and 1 does not match the expected pattern, the protection code knows it's not running on an original disk. ~ Chapter 2 In Which We Emerge From The Hellish Nightmare And Blink Our Eyes At The Simplicity Of The Solution There are no side effects to this protection check. (The instructions at $94E1 that set Y and A are ignored.) I can bypass it altogether by jumping directly to $6022 instead of $94E7 -- all the way back at $6000. Searching the disk for "4C E7 94", I find this code on track $07. T07,S0D,$05 E794 -> 2260 Quod erat liberandum. --------------------------------------- A 4am crack No. 1253 ------------------EOF------------------