------------Grammar Gremlins----------- A 4am crack 2015-11-26 --------------------------------------- Name: Grammar Gremlins Genre: educational Year: 1987 Authors: Santa Barbara Softworks Publisher: Davidson and Associates Media: 3.5-inch floppy (800K) OS: Apple Pascal 1.3 Previous cracks: none Similar cracks: #490 Grammar Gremlins 1987 (5.25") #291 Grammar Gremlins 1986 (5.25") ~ Chapter 0 In Which Various Automated Tools Fail In Interesting Ways Copy ][+ 9.1 ("COPY" > "DISK") read error on block $0308; copy boots and hangs with the drive motor on CFFA 3000 import read error on block 776 (= $0308); booting the disk image in an emulator exhibits the same behavior as the backup I made with Copy ][+ disk copy The original disk boots without complaint, so either this bad block is part of the protection check, or it's unrelated and I got incredibly lucky. The program appears to be written in Apple Pascal. The boot process starts with several sequential disk reads, then clears the screen and displays a block cursor in the upper-left corner. (I also recognize the hi-res graphics font and the way the program draws it. I'll take "Skills I Never Thought I'd Use" for $200, Alex, but here we are.) Next steps: 1. Use the tools on an Apple Pascal system disk to verify that this disk really uses Apple Pascal 2. Find the HELLO program (usually SYSTEM.STARTUP) and decompile it 3. Hack the p-code to disable the copy protection ~ Chapter 1 In Which We Choose The Right Tools For The Job The first thing I'll need is an Apple Pascal work disk, instead of my usual (DOS 3.3-compatible) Diversi-DOS disk. The second thing is a p-code decompiler from [S5,D1=original disk] [S6,D1=my work disk] ]PR#6 ... Command: F(ile, E(dit, R(un, C(omp, L(ink, X(ecute, A(ssem, ? [1.3] --> "F" [runs the Apple Pascal file management utility] Filer: L(dir E(dir R(em T(rans C(hng D(ate P(refix K(rnch Z(ero V(ols Q(uit? --> "V" [shows which disks are online] Volume # - Volume Name - # Blocks 1 CONSOLE: 2 SYSTERM: 4 GGWORK: 280 6 PRINTER: 11 GRAMLIN: 1600 System volume is - GGWORK: Prefix volume is - GGWORK: Confirmed: the original disk is written in Apple Pascal, and it's readable by the standard Apple Pascal filer. Apple Pascal volumes can be referenced by name, like ProDOS volumes. This disk's name is GRAMLIN:. --> "L" [lists a volume] Directory listing of what volume ? --> "GRAMLIN:" [name of original disk] Press to continue GRAMLIN: SYSTEM.PASCAL 28 3-Sep-85 PRETEST1 7 7-Nov-84 CAPITALIZATION1 7 7-Nov-84 PLURALS1 7 7-Nov-84 CONTRACTIONS1 7 7-Nov-84 PUNCTUATION1 7 7-Nov-84 PARTSOFSPEECH1 7 7-Nov-84 SENTENCES1 7 7-Nov-84 REVIEW_TEST1 7 7-Nov-84 PRETEST2 7 7-Nov-84 CAPITALIZATION2 7 7-Nov-84 PLURALS2 7 7-Nov-84 CONTRACTIONS2 7 7-Nov-84 ABBREVIATIONS2 7 7-Nov-84 COMMAS2 7 7-Nov-84 PARTSOFSPEECH2 7 7-Nov-84 SENTENCES2 7 7-Nov-84 REVIEW_TEST2 7 7-Nov-84 PRETEST3 7 7-Nov-84 CAPITALIZATION3 7 7-Nov-84 PLURALS3 7 7-Nov-84 ABBREVIATIONS3 7 7-Nov-84 POSSESSIVES3 7 7-Nov-84 PUNCTUATION3 7 7-Nov-84 PARTSOFSPEECH3 7 7-Nov-84 AGREEMENT3 7 7-Nov-84 REVIEW_TEST3 7 7-Nov-84 PRETEST4 7 7-Nov-84 CAPITALIZATION4 7 7-Nov-84 PLURALS4 7 7-Nov-84 ABBREVIATIONS4 7 7-Nov-84 POSSESSIVES4 7 7-Nov-84 COMMAS4 7 7-Nov-84 PUNCTUATION4 7 7-Nov-84 PARTSOFSPEECH4 7 7-Nov-84 AGREEMENT4 7 7-Nov-84 REVIEW_TEST4 7 7-Nov-84 SYSTEM.APPLE 32 3-Sep-85 GG.CODE 95 16-Sep-87 SYSTEM.CHARSET 2 14-Jun-79 SYSTEM.MISCINFO 1 4-Mar-86 ANIMALS.DATA 17 4-Mar-86 TITLE1.PIC 17 27-Mar-86 SYSTEM.LIBRARY 15 2-May-86 EDITOR.HELP 2 5-Apr-86 FULLHOUSE.PIC 17 8-Apr-86 SYSTEM.STARTUP 2 3-Nov-87 TITLE2.PIC 17 4-Nov-87 48/48 files , 503 blocks used, 1097 unused, 1080 in largest Under Apple Pascal, "SYSTEM.STARTUP" is the program that runs automatically on boot, like HELLO under DOS 3.3. I'm hoping that's where the copy protection routine lives. --> "Q" [return to main menu] Command: F(ile, E(dit, R(un, C(omp, L(ink, X(ecute, A(ssem, ? [1.3] --> "X" [execute a program] Execute what file ( to exit) ? --> "DISASSM" [this is the program I downloaded from callapple.org] APPLE PASCAL/6502 DIS-ASSEMBLER VERSION: July 27, 1982 Name of OUTPUT file (default is CONSOLE:): --> "GG800.TEXT" Name of code file: --> "GRAMLIN:SYSTEM.STARTUP" Decode PROTECT machine code? Hmm, that's interesting. Not sure what it means, but yes, let's do that. --> "Y" ...a surprisingly short time later... Success! Here is the complete output file, GG800.TEXT: --v-- Code file = GRAMLIN:SYSTEM.STARTUP The following library bytes are non- zero: PROTECT is a linked segment (P-code vers.5), length = 70 bytes These SYSTEM.LIBRARY intrinsics are used: 28 (Chainstuff) ** # PROCS = 1, SEGMENT # = 1 Disassembly for procedure # 1; Lex level = 0 P-code procedure 1 Code = 51, parameters = 4, data = 512 bytes; Jump table = 2 words 0: B9 2A UJP 42 2: CD 1C 01 CXP 28,1 5: 04 SLDC 4 6: A5 03 LAO 3 8: 00 SLDC 0 9: C7 00 02 LDCI 512 12: C7 10 01 LDCI 272 15: 00 SLDC 0 16: 9E 05 CSP 5 18: 9E 22 CSP 34 20: 00 SLDC 0 21: CB NEQI 22: A1 0A FJP 10 24: D7 NOP 25: A6 02 47 47 LSA 2,"GG" 29: CD 1C 02 CXP 28,2 32: B9 05 UJP 5 34: 01 SLDC 1 35: A1 02 FJP 2 37: B9 F6 UJP -10 39: 1C SLDC 28 40: 9E 16 CSP 22 42: B9 05 UJP 5 44: 1C SLDC 28 45: 9E 15 CSP 21 47: B9 F4 UJP -12 49: C1 00 RBP 0 Jump table: 52: 32 00 .WORD R2 (Entry # -12) 54: 14 00 .WORD R34 (Entry # -10) --^-- Let's go through this line by line. ~ Chapter 2 P-Code Is Best Code The numbers along the left of the disassembly are byte offsets in decimal, not hex. It looks like the numbers along the right are also in decimal, but the opcodes are in hex. 0: B9 2A UJP 42 According to Appendix A in the "Apple Pascal Operating System Reference Manual" (hereafter known as "the f---ing manual"), "UJP" stands for "unconditional jump." With a positive parameter, it functions like a relative forward jump, similar to branch opcodes in 6502 assembly. The offset is added to the PC of the next instruction, not the current one. (Assembly language branches also work this way.) In other words, this two-byte instruction at offset 0 branches forward to offset 0 + 2 + 42 = 44. 44: 1C SLDC 28 45: 9E 15 CSP 21 According to the f---ing manual, "SLDC 28" pushes the decimal value 28 ($1C) to the stack. "CSP" stands for "call standard procedure." Apple Pascal has a number of built-in functions like "cos" (calculate a cosine) or "readln" (read a line of input). But what is standard procedure #21? The f---ing manual does not say. But I found a reference to it in "P-Source: A Guide to the Apple Pascal System" by Randall Hyde. On page 311 is the entire CSP table, and the 21st entry is "LDS" ("Load Segment"). Quoting Hyde: "[LDS] pops the segment number off of the stack, gets it into the 6502 accumulator, and then calls the load segment subroutine. Upon return from load segment, control is returned to the main interpreter loop." But what segment are we loading? #28, which was just pushed to the stack in the previous instruction. But what is segment #28? According to Appendix A in the f---ing manual, segment 0 and segments 2-6 are reserved for the operating system. Segment 1 is the main program that is currently executing (SYSTEM.STARTUP in this case). Segments 7-21 are for segments and units defined within the program. And segments 22-31 are predefined intrinsic units. Apple Pascal includes several libraries like TURTLEGRAPHICS for Logo-like graphic commands and APPLESTUFF for access to joystick buttons and other hardware. But which one is #28? I finally found the answer in a post by "Tommy" on comp.sys.apple2 in a thread called "Wizardry re-engineering." Using a tool called LIBMAP, Tommy dug into the SYSTEM.LIBRARY file (part of the Apple Pascal operating system) and found the internal segment numbers for each intrinsic unit in Apple Pascal: 20 TURTLEGRAPHICS (Logo-like graphics) 22 APPLESTUFF (hardware access) 28 CHAINSTUFF (launching programs) 29 TRANSCEND (geometry functions) 30 LONGINTIO (long integer support) 31 PASCALIO (parsing decimals) Thus, the p-code instruction "SLDC 28", followed by "CSP 21", will load the CHAINSTUFF intrinsic unit from SYSTEM.LIBRARY. 47: B9 F4 UJP -12 This is another unconditional jump, but according to the f---ing manual, negative parameters are not simply backward branches. Instead, they are referenced by their position in the "jump table" at the end of the procedure. This is the jump table: Jump table: 52: 32 00 .WORD R2 (Entry # -12) 54: 14 00 .WORD R34 (Entry # -10) So the instruction "UJP -12" jumps to the address listed as "Entry # -12", which is R2. "2" refers to the instruction at byte offset 2, which is back near the beginning: 2: CD 1C 01 CXP 28,1 "CXP" stands for "Call eXternal Procedure", and the f---ing manual says it takes two arguments, a segment # and a procedure # (in that order). According to Tommy, procedure #28 in segment #1 is "uses chainstuff". That makes sense, I suppose, since we just got finished loading that library into memory. Now we're signaling that we want to use it. That's it. Four hours of research, and "uses chainstuff" is as far as I've gotten. Did I mention I have no idea what I'm doing? This is fun. Are you having fun? I'm having fun. ~ Chapter 3 Standard Procedure Is Best Procedure Continuing at offset 5: 5: 04 SLDC 4 6: A5 03 LAO 3 8: 00 SLDC 0 9: C7 00 02 LDCI 512 12: C7 10 01 LDCI 272 15: 00 SLDC 0 16: 9E 05 CSP 5 According to the f---ing manual, "SLDC 4" just pushes the value 4 to the stack. (To conserve space, p-code has lots of one-byte instructions that all mean "push me to the stack.") "LAO" is "load global address". The f---ing manual has this to say about that: "LAO B. Fetch the word with offset 'B' in BASE activation record and push it." Then more pushing, then "CSP 5". According to Hyde, standard procedure #5 is "UREAD" (unit read). According to Tommy, the unit read procedure is a low-level disk read, like calling the RWTS directly under DOS 3.3. It takes 5 parameters. The first parameter is a volume number. In Apple Pascal, the volume number of the disk you're booting from is always 4. (I saw this earlier when I listed all online volumes from the Filer.) That explains the "SLDC 4" and "LAO 3" instructions. It's getting a reference to volume #4 and pushing it to the stack, in preparation for the unit read procedure to pull it off the stack and use the value as its first parameter. The second and third parameters to the UREAD function are BUFFER (0) and BUFFERSIZE (512). So we're storing the result of the read in a 512-byte buffer, which makes sense. Like ProDOS, Pascal organizes disks into "blocks" of two sectors each. There are 280 blocks on a 5.25-inch floppy. The 4th parameter is the block number to read. Since a block is two sectors, there are 8 blocks per track. Counting from 0, block 272 would be the first block of track $22. According to the "Comparison of Sector Skewing" figure in "Beneath Apple DOS" (p. 3-23), this block includes track $22, sector $00, which is was the only unreadable sector on the original disk. We are definitely on the right track. The "CSP 5" actually calls the UREAD function. Continuing at offset 18: 18: 9E 22 CSP 34 According to Hyde, standard procedure #34 is "IOR". It pushes the value of the IORESULT global (set by UREAD) to the stack. In other words, we're checking whether the read worked. But remember! On the original disk, this block was unreadable, because T22,S00 was intentionally bad. There's no special logic here to change the RWTS to make it readable. The original disk is just reading the block like any other and checking to ensure that the read failed. 20: 00 SLDC 0 21: CB NEQI "SLDC" pushes 0 to the stack. "NQI" is an "is-not-equals" test. It compares the top value on the stack with the next-to-top value on the stack. The top value is 0, since the instruction just before it (at offset 28) was "SLDC 0". The next-to-top value is the return value from calling standard procedure #34. So I think this is saying "if UREAD did not return 0, then..." Then what? 22: A1 0A FJP 10 "FJP" stands for "false jump," or less confusingly, "jump if false." If the previous test evaluated to false, then jump. The previous test was "did UREAD not return 0?" So I had it backwards in the previous paragraph. The proper prose description of this code is "if UREAD *did* return 0, then jump." Jump to where? Forward 10 bytes, to offset 22 + 2 + 10 = 34. Without decompiling every single instruction between 24 and 34, it seems pretty clear that this is the branch that my copy is taking and the original disk is not. The program expects block 272 to be unreadable, but on my copy, block 272 reads just fine. (This also explains why my EDD bit copy worked.) If I change that "NEQI" (is-not-equals) instruction at offset 21 to an "EQUI" (is-equals) instruction, that would reverse the logic entirely. Instead of saying, "if UREAD returned no error, branch to the failure path," it would say, "if UREAD returned an error, branch to the failure path." I like this solution for two reasons: first, it's the simplest thing that could possibly work. It's only a one- byte change, so it doesn't require inserting or removing any bytes of the p-code (which is convoluted and intimidating, to say the least). And second, I enjoy the irony of turning a copy protection check into a copy deprotection check. Turning to my trusty Block Warden block editor, I searched the disk for the first few opcodes ("B9 2A CD 1C 01", from the very beginning of the procedure) and found this code on block $1F6. The f---ing manual tells me that the opcode for "NEQI" is $C3. Block $01F6, byte $015 change CB to C3 ]PR#5 ...works... Quod erat liberandum. ~ References Here are some references on Apple Pascal hacking that I found helpful: Dave Tribby's Apple II Pages: Apple Pascal The "Apple Pascal Operating System Reference Manual" (a.k.a. "the f---ing manual") Appendices A and B of that manual are available in plain text at "P-Source: A Guide to the Apple Pascal System" by Randall Hyde Starting in 2012, "Tommy" has written a series of posts on comp.sys.apple2 about reverse engineering Wizardry, which was written in Apple Pascal. "Re-engineered: Wizardry III, Legacy of Llylgamyn" "Wizardry re-engineering" "Wizardry IV bootstrap bug in SYSTEM. INTERP" --------------------------------------- A 4am crack No. 504 ------------------EOF------------------