------------Fun on the Farm------------ A 4am crack 2014-04-25 --------------------------------------- The Electric Crayon: Fun on the Farm is a 1986 educational program developed by Brian A. Rice and distributed by Polarware. The original disk copies with COPYA, but the copy does not work. It boots (ProDOS), shows a text-based title screen, loads some files from disk, then hangs. Booting another ProDOS disk with BASIC.SYSTEM, I start with a disk catalog to see what I'm working with (or against). ]CAT,S6,D1 /F NAME TYPE BLOCKS MODIFIED *PRODOS SYS 30 18-SEP-84 EC.SYSTEM SYS 12 27-AUG-86 EC.MAIN.OBJ BIN 13 19-AUG-86 NOMENUS.TK.ABS BIN 23 13-DEC-85 TEST.FONT BIN 6 7-NOV-85 EC.COLOR.DRIVER BIN 10 16-DEC-85 EC.IMAGE2.SETUP BIN 1 9-DEC-85 EC.SCRIBE.SETUP BIN 1 9-DEC-85 EC.ADSWITCH.OBJ BIN 1 9-DEC-85 EC.JDRIVER.OBJ BIN 3 9-DEC-85 EC.KDRIVER.OBJ BIN 4 9-DEC-85 EC.SCREEN.CLIP BIN 9 12-DEC-85 EC.PARMS BIN 1 25-APR-86 EC.COLORS BIN 17 27-FEB-86 EC.DIALOG.OBJ BIN 4 19-AUG-86 SHAPES BIN 1 *PICTURES DIR 3 BLOCKS FREE: 3 BLOCKS USED: 277 ProDOS always runs the first .SYSTEM file in the root directory, which would be EC.SYSTEM. Furthermore, it always loads it at address $2000. So let's do that. ]PREFIX /F ]BLOAD EC.SYSTEM,A$2000,TSYS ]CALL -151 *2000L OK, I see some standard unfriendly initialization of the reset vector and other things. Then lots of calls to $BF00, which is the ProDOS entry point. Presumably this is loading some of those BIN files. 2085- 20 00 BF JSR $BF00 2088- C7 ??? 2089- E1 22 SBC ($22,X) 208B- AD 91 03 LDA $0391 208E- F0 06 BEQ $2096 2090- 20 00 BF JSR $BF00 2093- C6 E4 DEC $E4 2095- 22 ??? 2096- 20 55 35 JSR $3555 2099- 20 19 24 JSR $2419 The ProDOS entry point at $BF00 pulls its parameters from the addresses AFTER the call, which is why the disassembly looks like it contains invalid opcodes. And in fact, they are invalid, but they are never executed. ProDOS grabs them by pulling the return address off the stack, looking at the bytes immediately after the stack pointer, then adjusting the stack pointer so that control returns to the (real) instruction after the parameters. So at $2085, it calls $BF00 with the three parameters $C7, $E1, and $22, then execution continues at $208B. And so forth. Anyway, I put "JMP $FF59" after each JSR until I found a subroutine that never returns: $7C00, which is called from $20C1. $7C00 isn't part of EC.SYSTEM. It must be loaded from one of the other files. Sector search finds it on T04,S05. T04,S05,$C1 change "20 00 7C" to "4C 59 FF" Now my non-working copy displays the text title page, beeps, and jumps to the monitor. *7C00L ; save some state from zero page 7C00- A2 03 LDX #$03 7C02- B5 00 LDA $00,X 7C04- 48 PHA 7C05- CA DEX 7C06- 10 FA BPL $7C02 ; put an "RTS" at $00 and call it 7C08- A9 60 LDA #$60 7C0A- 85 00 STA $00 7C0C- 20 00 00 JSR $0000 ; do some fancy stack pointer ; manipulation (why?) 7C0F- BA TSX 7C10- CA DEX 7C11- CA DEX 7C12- 9A TXS 7C13- 68 PLA 7C14- 85 00 STA $00 7C16- 68 PLA 7C17- 85 01 STA $01 ; this is a simple decryption loop 7C19- A0 89 LDY #$89 7C1B- A9 FF LDA #$FF 7C1D- 51 00 EOR ($00),Y 7C1F- 91 00 STA ($00),Y 7C21- 88 DEY 7C22- C0 17 CPY #$17 7C24- D0 F5 BNE $7C1B OK, I figured out what it's doing to stack, and why it put an "RTS" at $00 and called it. This entire routine is relocatable. There's no "program counter" variable on the Apple II. I mean, obviously there is a program counter, but it's not like a simple address that you can read to see what instruction is going to be executed next. But this routine figures it out, by creating a subroutine (that does nothing), calling it, and taking advantage of the fact that the "stack" is really just a page of memory (from $0100..$01FF) and a register that points to one of those memory locations. After a subroutine returns, the return address is still somewhere in the $0100..$01FF range (it's not overwritten until you call a different subroutine). This routine figures out where, and stores that address in $00/$01. Then the loop at $7C19 decrypts memory backwards from that address + $89, back to that address + $17, then stops. $17 bytes is the size of the code that manipulates the stack pointer, plus the decryption loop itself. Anyway, $7C26 and beyond is encrypted. The loop at $7C19 decrypts it. I'll need to decrypt it manually. *7C19:4C 59 FF *7C00G *00.01 0000- 0E 7C I need to reproduce the decryption loop somewhere else so I can stop after the loop completes. I'll put this at $0300: 0300- A9 0E LDA #$0E 0302- 85 00 STA $00 0304- A9 7C LDA #$7C 0306- 85 01 STA $01 0308- A0 89 LDY #$89 030A- A9 FF LDA #$FF 030C- 51 00 EOR ($00),Y 030E- 91 00 STA ($00),Y 0310- 88 DEY 0311- C0 17 CPY #$17 0313- D0 F5 BNE $030A 0315- 60 RTS *300G Now let's see what Electric Crayon was hiding from us. Oh look, it's a nibble check! What a surprise. (Not really.) *7C26L 7C26- AE 30 BF LDX $BF30 ; turning on the disk motor manually: ; never not suspicious 7C29- BD 89 C0 LDA $C089,X 7C2C- A9 56 LDA #$56 7C2E- 85 03 STA $03 7C30- A9 08 LDA #$08 7C32- C6 02 DEC $02 7C34- D0 04 BNE $7C3A 7C36- C6 03 DEC $03 ; looks like the failure path takes us ; to $7C93 7C38- F0 59 BEQ $7C93 ; nibble check 7C3A- BC 8C C0 LDY $C08C,X 7C3D- 10 FB BPL $7C3A 7C3F- C0 FB CPY #$FB 7C41- D0 ED BNE $7C30 7C43- F0 00 BEQ $7C45 7C45- EA NOP 7C46- EA NOP 7C47- BC 8C C0 LDY $C08C,X 7C4A- C0 08 CPY #$08 7C4C- 2A ROL 7C4D- B0 0B BCS $7C5A 7C4F- BC 8C C0 LDY $C08C,X 7C52- 10 FB BPL $7C4F 7C54- C0 FF CPY #$FF 7C56- D0 D8 BNE $7C30 7C58- F0 EB BEQ $7C45 7C5A- C9 0A CMP #$0A 7C5C- D0 D2 BNE $7C30 7C5E- BD 8C C0 LDA $C08C,X 7C61- 10 FB BPL $7C5E 7C63- C9 D5 CMP #$D5 7C65- D0 C9 BNE $7C30 7C67- F0 00 BEQ $7C69 7C69- BD 8C C0 LDA $C08C,X 7C6C- 10 FB BPL $7C69 7C6E- C9 AA CMP #$AA 7C70- D0 BE BNE $7C30 7C72- F0 00 BEQ $7C74 7C74- BD 8C C0 LDA $C08C,X 7C77- 10 FB BPL $7C74 7C79- C9 96 CMP #$96 7C7B- D0 B3 BNE $7C30 7C7D- F0 00 BEQ $7C7F 7C7F- BD 8C C0 LDA $C08C,X 7C82- 10 FB BPL $7C7F 7C84- C9 AA CMP #$AA 7C86- D0 A8 BNE $7C30 7C88- F0 00 BEQ $7C8A 7C8A- BD 8C C0 LDA $C08C,X 7C8D- 10 FB BPL $7C8A 7C8F- C9 AB CMP #$AB 7C91- D0 9D BNE $7C30 ; this was the failure path, but it ; looks like this is also part of the ; success path, which means it must be ; checking for side effects somewhere ; else later 7C93- DD 88 C0 CMP $C088,X ; re-encrypt the nibble check because ; f*** you, that's why 7C96- A0 89 LDY #$89 7C98- A9 FF LDA #$FF 7C9A- 51 00 EOR ($00),Y 7C9C- 91 00 STA ($00),Y 7C9E- 88 DEY 7C9F- C0 17 CPY #$17 7CA1- D0 F5 BNE $7C98 7CA3- A4 03 LDY $03 ; restore zero page state 7CA5- A2 00 LDX #$00 7CA7- 68 PLA 7CA8- 95 00 STA $00,X 7CAA- E8 INX 7CAB- E0 04 CPX #$04 7CAD- D0 F8 BNE $7CA7 ; ah! if the nibble check failed, zero ; page $03 will be 0, which means this ; will fall into an infinite loop at ; $7CB0 7CAF- 98 TYA 7CB0- F0 FE BEQ $7CB0 7CB2- A9 00 LDA #$00 7CB4- 8D D0 9A STA $9AD0 7CB7- A9 C6 LDA #$C6 7CB9- 8D D1 9A STA $9AD1 7CBC- 60 RTS The other side effect -- setting addresses $9AD0 and $9AD1, don't appear to be relevant. (There's nothing loaded there at this point.) Perhaps it's left over from another program that also used this protection code? It wouldn't be the first time I've seen it; copy protection routines got re-used all the time. Using my trusty Copy ][+ sector editor, I press "S" to scan and "H" for hex, and look for "A2 03 B5 00 48 CA 10 FA" (the first 8 opcodes at $7C00). There's only one hit, on T11,S03. T11,S03,$00 change "A2" to "60" Reboot. It displays the title screen and jumps to the monitor. Oops, I forgot to revert the unconditional jump that I added earlier. Restore T04,S05 from the original disk and reboot again. Success! The game boots and runs without complaint. Quod erat liberandum. --------------------------------------- A 4am crack No. 20 -------------------EOF-----------------