7#, k69i.0000@1#11821x )&2 24|*4 #4|24|4|694|4|4|4|4|4| The Hackers Handbook Part 1 - Cracks & Numbers Part 2 - The Cracking Guide ================================================================================ ====== Mac Cracking- A series on deprotection methods on the Macintosh ======= Part 1 Here it is. First in the series of the Infamous Atom's mac crack series. Some of you may have macs, others just wonder how you crack on the mac. In this series I'll attempt to show you the basics of cracking on a mac and hopefully give you an idea of the difficultly and difference between Apple ][ and Mac cracking. 1) Things that make Mac cracking easier than Apple ][: A) All code segments must be stored on the disk in normal format. No abnormal headers or anything that cannot be read with the normal ROM read routine.(data can be stored otherwise though) B) Protection on the mac has used fairly simple techniques since the programmers don't know all the tricks that they do on the II C) All disk I/O has to pass through the IWM chip and thus you can't have half and spiral tracking. 2) Things that make Mac cracking harder than Apple ][: A) No debugger(or monitor) in ROM.. Macsbug is a software debugger only and therefore can be destroyed by some nasty programs(EA does it on all of theirs) B) Programs have much more memory to play with. Instead of 0-$C000, you have $0-$400000 (of course most of the top is ROM.) C) Virtually no documentation on non-standard read routines. Basically have to figure it out yourself. The first thing you need to do before attempting a crack on the Mac is learn 68000 assembly (duuh) WELL!! don't just look at it and assume you know it!! You must really understand the addressing modes and especially how the stack is handled. EVERYTHING on the Mac uses the stack. The code you are following is constantly calculating addresses and placing them on the stack to RTS to. Also, pick up a copy of Inside Mac so you can get a basic Idea of the ROM traps (there are over 300). If you want to look for books, I suggest 68000 assembly by Leventhal and the Inside Mac phonebook or vol.1-3 from Apple. Next time, I'll start showing you some traps, and a little 68000, then we'll jump right in to the debugger and cracking a ware- Wizardry! ====== Mac Cracking- A series on deprotection methods on the Macintosh ======= Welcome to part 2 of The Atom's guide to Mac cracking- Today's Topic- 68000 assembly and Mac Traps By now, I assume you have looked at the 68000 assembly language somewhat and can at least understand small sections of code. Just to clarify things, and teach you some machine-dependent ideas (for the mac that is), I'll devote this part of the cracking series to the 68000 and traps/interrupts. First of all, the 68000 is a two instruction machine, unlike the 6502. This means that most commands have 2 arguments rather than one (as the 6502 has). IE> MOVE D0,#$1000 instead of STA #$1000. This makes life much easier, especially with the use of multiple registers. (68000 has 17, compared to the 4 on the 6502). These registers are labelled D0-7, A0-7, and the processor status reg. The registers starting with D are data registers which are 32 bits long. You can store any kind of 32 bit number in them. The address registers, (denoted by A), are also 32 bits long but can only refer to even numbered addresses and are not valid for all modes. (Don't worry if it doesn't make much sense, you'll get the hang of it.) Sooo Now we know about registers. How about operands? Unlike the 6502, which has a different command for each register, the 68000 has a standard set of commands which can work with all the regs. Instead of: STA $1000 or STY $1000, You would have: MOVE D0,$1000 or MOVE D1,$1000 The MOVE command is the basic operand to move data from one place to another. Be it from reg to reg (MOVE D0,D1) or memory to memory (MOVE $1000,$2000), or whatever. Most of the other commands are similar to 6502, and work pretty much the same (JSR, RTS, CMP, BEQ, BNE, etc.). There is one other addition to the syntax you should know: the .B, .W, and .L suffixes. These refer to byte, word, and long data commands. By adding any of these to the end of an operand, you limit the command to only that size of data. For instance, a MOVE.B D0,D1 would move the lowest 8 bits of D0 into the first 8 bits of D1. The word command uses 16 bits, and long word uses all 32. These can be added to most commands that manipulate data, like CMP, CLR, MOV, ROL, etc. If you ignore the syntax, it defaults to .W. Now we get to Addressing modes: On the 6502, you had different syntax for addressing, and its about the same on the 68000. An indirect jump (JMP ($1000)) would become JMP ($1000) really hard huh? You can use inderict addressing in MOVE commands also: MOVE (A0),D1. This would move the contents of whatever address was in A0 into D1. (IF $1000 was in A0, then the word at $1000 would go to D1). One note, the indirect mode can only be used with the address registers, not the data registers. Finally, there are the auto-increment and auto-decrement indirect modes. If you did a MOVE (A0)+,D1 (and A0 was $1000), it would the contents of $1000 into D1, and then automatically increment A0 so it now points to $1002. (It increments by 2 since it moved a word (2 bytes) and each address points to a byte (in effect, its now pointing to the next WORD)). Auto-decrement works basically the same way, a MOVE -(A0),D1, would decrement the address in A0 by 2, then move the contents of $FFE into D1. The placement of the - or + is the way its set up, so you can't do a MOVE +(A0),D1 or MOV (A0)-,D1. And a couple more sytnax things- there are no stack commands (PHA,etc.), they use the auto-inc and auto-dec modes to implement a stack with the A7 register. It's kind of complicated exactly how it works, so I won't go into it here, but just assume that MOVE D0,-(A7) pushes D0 onto the stack and MOVE (A7)+,D0 pops the value off the stack into D0. (the A7 is sometimes replaced by SP as in MOVE D0,-(SP)). So now we know everything there is to know about 68000 assembly, right? Wrong but we know enough to crack something! But before I start talking about the debugger, I'll mention something about the traps on the 68000. Since all ROM routines are called through these, it pays to know what they are. First of all, when the 68000 finds an opcode it doesnt know (some code that doesn't translate into an executable instruction), it will look in a trap table to see if there is a replacement code for it. This way, you can implement your own 68000 commands by putting the address of your routine into the trap table and simply issuing the command. On the Mac, the trap tables are in low memory and point to ROM routines. Since the routines are always in the same place with the same ROMs, the debugger keeps a table of these traps and will actually name them for you in the code. So while listing a section of memory, you may see something like this: _InitGraf _InitFonts _InitWindows MOVE #14,D0 MOVE D0,-(SP) _Read What it is doing is calling three rom routines to initialize different sections of the window management, executing a couple of 68000 instructions and then calling the Read ROM routine to read from the disk. Fairly simple, right? It does make cracking quite a bit easier, as long as you know what most of the traps do. So be sure and have a copy of Inside Mac at hand when you start to debug/crack something.(and lots of paper). Well, next time, we'll look at the debugger and start trying to crack a few warez ====== Mac Cracking- A series on deprotection methods on the Macintosh ======= Part 3 Here we are again, with the 3rd installment of "Mac Cracking- man or myth?" (or something like that). The topic of discussion in this section will be the DEBUGGER. Otherwise known as MacsBug. If you end up doing much cracking at all, you'll begin to love (and hate) some of MacsBug's commands, and you should get fairly familiar with reading other people's 68000 code.(or compiler code). First, a couple notes about MacsBug: To run it, you must have the MacsBug file on the disk you are booting up, and it must be named MacsBug exactly, (well, case doesnt matter, but otherwise, exactly like that). If you are using the HFS system, it must be in the system folder along with the system and finder files. Also, don't forget to install the little programmer's switch in the side of your mac. If you don't have it in, you can't even start up MacsBug! Ok, well, I'll start by talking about how MacsBug is loaded in, set up, and then list some commands and show you some examples. Booting up- When the mac boots up, it reads a bunch of system related stuff from the boot disk, initializes the Font, QuickDraw, Resource and other managers, and then throws up a Dialog saying "Welcome to Macintosh". It then looks for a file name MacsBug on the disk, and if it finds it, it allocates some extra memory for it and then loads it in and sets it all up. Macsbug takes up about 40k, so for large programs, running on a 128k, you may not be able to load it.(get a 512k!) Basically what MacsBug does when it sets up is change a few pointers in low RAM that used to point to error routines to point to it. Like the error when you hit the interrupt button on the side of the computer (plus a few more). So now, when you hit the interrupt button, instead of getting a bomb dialog, a new window will pop up, covering most of the screen, with a dump of all the registers (D0-D7 and A0-A7 as well as the PC and some other info). You are now in MacsBug! You have stolen control of the 68000 from the executing program and can now debug (or crack) to your hearts content but first, you need to know the MacsBug commands! Commands MacsBug has a lot of commands. At least 40. It basically works the same as the monitor on a II, but with different syntax, and a bunch of nice tracing functions. They are set up in a general format of a one or two word command, followed by a few numeric parameters. What follows is a partial list of the basic commands we will be using to crack Wizardry, and some explanation for each. To get a list of all the commands, look in your Inside Mac manual under the section called "INSIDE MACSBUG" (only in newer versions). Oh yea, forgot to mention, I'm using MacsBug V5.1. These commands work with all versions 5.0 and higher. (You can check your version of MacsBug by typing DV at the prompt.) (aaaa = address, nnnn = number) Memory Commands: DM aaaa nnnn Display memory- gives you hex dump of the bytes starting at aaaa and going up to address aaaa+nnnn. Ex. DM 0F00 10 would dump out hex from $F00 to $F10. If you leave out the nnnn, it lists the next 16 bytes. If you leave out aaaa, it starts at the current address. SM aaaa nn,nn1,nn2 Set memory- changes value in address aaaa to nn, then address aaaa+1 to nn1, etc. TD Total Display- dumps out all the registers, PC and disassembles the current line. Break Commands: BR aaaa Sets a break point at address aaaa. When the program executes the line at aaaa, it will be interrupted and the MacsBug window will pop up. G aaaa Just like the Apple ][, starts execution at aaaa. If you leave off aaaa, it starts where the PC last was. GT aaaa Very nice command, starts executing at the PC, and then stops and returns to MacsBug when it gets to address aaaa (easier than setting breakpoints). T Trace, executes one instruction, then dumps out the registers and disassembles the next line. MR Magic Return. If you are tracing along, and suddenly encounter a JSR, you can type MR and it will execute the subroutine and then return you to trace mode right after it gets a RTS. A Trap Commands: (these are probably the most important, be sure you understand them) AB TRAPNAME Causes the computer to halt and return to MacsBug when it sees a TRAP command that is referred to by TRAPNAME. Ex. AB READ would stop the next time the program does a READ call. AT TRAPNAME Traces and displays the address of each call to the trap TRAPNAME. Doesnt halt though. Ex. AT EJECT would show the address of each line that the program executed that called the EJECT trap, and then continue executing the program. AX Clear all trap commands. (so it won't stop everytime it does a READ anymore) Disassembler commands: IL aaaa nnnn List out disassembled code starting at address aaaa and going until address aaaa+nnnn. Just like the L command on the ][. If you just enter IL it will list out the next 10 or so instructions. So those are all the commands you need to know! there are a bunch more, but they aren't as powerful or as easy to understand as these, so you can learn them on your own. Just to give you and example of using the debugger, I'll show the steps you might use to find the starting address of a program: 1) put MacsBug on the disk with the program you are trying to find the starting address for. (Well, call it PROGRAM.) 2) Boot up the disk, and go to the desktop. 3) Press the interrupt button- you should see a big window pop up with a dump of the registers in it. 4) Type AB INITGRAF This tells MacsBug to stop the next time it encounters an _InitGraf call. (_InitGraf is usually one of the first instructions applications run, so it will close to the starting address.) 5) Hit G to start the finder running again. 6) Double click on PROGRAM and hope for the best! 7) If all goes ok, MacsBug should pop up in a little while, displaying the address of the instruction that called _InitGraf. 8) Type IL aaaa, where aaaa is the address that MacsBug said the _InitGraf instruction was at. 9) Thats it! the beginning of the code for our application! from there, we could trace on using the T command and watch the execution of the program. Or hit G to give the program back full control of the 68000. Don't forget to do an AX command when you are done, otherwise you will be jumping into macsbug everytime you run an application that calls _InitGraf. Finally, I'd like to say something about MacsBug alternatives: I know of one great debugger called MCBUG. it works a lot like MacsBug, but has a few extra features that help a lot, like a built in mini-assembler, some nice launch funtions, and a few other helpful commands. It's a shareware/public domain program, so if you look around you should be able to find a copy of it on CompuServe or from a user group. It comes with docs, and installs just like MacsBug; simply rename it and boot! ====== Mac Cracking- A series on deprotection methods on the Macintosh ======= Welcome to Part 4 of Mac Cracking- your guide to fame. In this, the fourth, and hopefully final part, we will look at Wizardry and actually remove its protection. Of course, as we all know, this is only for backup purposes, right? So first we need to set up a copy of the disk to work on. Wizardry is an unusual protection, in that you can copy all the files off the disk, but it asks you to put the master disk back in upon boot, and then reads some bad blocks off of the master disk. The nice thing about this method is that it does not crash the machine if it can't find the master, it simply continues with a semi-demo game of Wiz. This saves a lot of time when you are constantly backtracking and reloading the files to find the protection. Here we go! First, sector copy (or finder copy) the files from the Wizardry disk onto a blank. Then trash the Imagewriter file (we need more space for MacsBug). Copy MacsBug onto the Wizardry disk, and then select the Wizardry file, and install a MiniFinder with only Wizardry in the selection. This way, when the disk boots up, we can set up some breakpoints while the MiniFinder is running, and then execute Wizardry. If we let it boot straight into Wizardry, we would have to guess when to hit the interrupt switch and hope that we didn't miss something. Now we have a disk to crack. Boot up the disk, and when you see the MiniFinder, hit the interrupt switch. MacsBug should come up. Type AB INITGRAF . This will find the starting address of the program by halting when it starts initializing the managers. Now type G. You are back in the MiniFinder, so double click on the Wizardry file and wait for MacsBug to regain control. At this point, MacsBug should appear saying it halted on an _InitGraf call at location F200 (this address will be different depending on your memory size. F200 is on a 512 or plus). You can now type IL F200 to start listing the code. As we look at the code (hit return to see another 20 lines after you are done with a section) we see that the program contains no branching until address F222. The protection check is going to involve reading a sector from the disk and then branching on a result. So all we have to look for is a branch after some disk access. Type GT F222. Wizardry loads in some resources and sets up its menus and windows. If you booted up your copy of wizardry normally, this is right before it puts up a dialog and asks for the key disk. Now we have to narrow down the search to a specific JSR. If you try GT F322, you see that it goes through the check and comes back with a message that you did not insert the master disk. This means that the JSR to the protection routine is somewhere between F222 and F322. So now we look some more! (If you did try the GT F322, you can type EA to exit to the application, re-running Wizardry. It will abort again at the _InitGraf, and then you can type GT F222 to get back to where you belong.) By continuing this process (trying locations closer and closer to F222 in the GT command) you will eventually find that the JSR to the protection is at F31E. The program does not throw up a dialog saying you inserted the wrong disk before this JSR, but does draw a dialog at F322, the next statement. So we have tracked it down to a single JSR. Now we can have fun. If we trace, using the T command at this subroutine, we find that it immediately executes a _LoadSeg trap. And then for some mysterious reason, MacsBug never regains control. This is the tricky part- After the JSR to EFB7A (the _LoadSeg), the program loads the protection routine from disk AND loads code into EFB7A. Since the T command replaces the code at the next instruction with a break command in order to regain control after one step, this code is loaded on top of the break command, replacing it. This is why your MacsBug never comes back. There is not a break point to interrupt the program any more! What we can do though, is interrupt with the interrupt switch after it brings up a dialog saying we inserted the wrong disk, and disassemble the code that was loaded into EFB7A. When we look there, we see it did a JMP to 13828. The code at 13828 was also loaded in with this loadseg trap, so we didnt see it before. This is the main protection routine. But now we have a problem- how do we stop the execution of the program at 13828 so we can trace the protection and find the correct branch? We can't set a breakpoint at 13828 with GT, since it would get replaced with the code during the _LoadSeg. And we can't stop it after the _LoadSeg since it replaces itself and any breakpoints we set after it! What do we do?? Alas, MacsBug comes through with yet another amazing command. ST. This works like the GT, but does not set a breakpoint to stop the program. (I'm not sure what it does to do this, but it uses the 68000 step flag.) So we get back to F31E (using the EA command as before, then the GT). And type ST 13828. The reason we don't issue a ST right after F200 is that the S commands slows execution of the program noticeably. You will have to wait a couple of minutes for the ST 13828 to return to MacsBug. (The drive will make some strange noises, but don't worry, just be patient.) After this hard work, we are now in trace mode at 13828. Hurrah! Almost there. By repeating the method we used to find the first JSR, and by reading the code, you find that the last branch that seperates a key disk dialog from the bad disk dialog is at 139D0. The BEQ +90 is executed if the protection check comes out bad. If you search the code, you see that the good code continues 3 instructions from the address the BEQ branches to. So now we simply replace the BEQ with BRA +98, and no matter what the protection check returns, everything continues fine. Now to test it to be sure our patch works. Boot the disk from scratch and get to the trace mode at 13828 using the commands we used before. Now type SM 139D0 60 00 00 98. This is the code for a BRA +98, which we are replacing at 139D0. Hit G, and there it is! Your copy should continue to load, and no matter what disk you put in for the master, it will thank you for inserting your "master" and continue along its merry way. But we don't want to have to use MacsBug to do this EVERY time we boot up, so we'll change the program on the disk. First, dump the memory from the instructions before and after the BEQ +90 and write them down. Then run Fedit (its a sector editor program) and open the file Wizardry on your cracking disk. Do a hex search for the values- 0A 00 00 01 67 00 00 90 30 2E FF F2 (These are the bytes surrounding the instruction that you wrote down earlier. By searching for the whole string, we are sure we have the correct BEQ +92 in the program, in case there is more than one). Go in to hex edit mode and replace the 67 00 00 90 with 60 00 00 98 and write thesector back out. Congratulations, you have successfully cracked Wizardry. You can copy the Wizardry file you patched onto your master disk, or make the patch to a sector copy of the original to get the disk back looking like it normally did. (Auto boot into Wizardry, no MacsBug or MiniFinder, and with an ImageWriter file.) So, concluding this discussion, I'll say that these are the methods that work for me, but you are welcome to try anything else. Mac Nosy is a good program for disassembling code you are trying to unprotect. And other protection schemes are very different from Wizardry's. So practice on some other programs and with any luck, you'll be cracking everything you can get your hands on. Also, a few problems with the above crack I would like to note. Although it does work fine (I've killed Werdna on a cracked version), it is annoying to see the 3 dialogs at the beginning and also have it eject the disk twice. For further study, you might consider taking out the Ejects, so you don't have to re-insert the disk. (Hint: you will not only have to take out the _Eject's, but also the routine that waits for another disk to be inserted. Since it doesnt eject any more, there isn't any way to insert a disk!) Just to help you along, I'll give the patches to remove the ejects and wait for insert disk routines: Search for -> change to 20 5F A0 17 3E 80 4E D1 4E 56 -> 20 5F 3E BC 00 00 21 6E 00 0A 00 12 A0 17 3D 40 -> 21 6E 00 0A 00 12 4E 71 2F 2E 00 08 4E BA FF 66 A8 5E -> 2F 2E 00 08 4E 71 4E 71 FF F4 66 20 48 7A 06 7E 48 7A -> FF F4 4E 71 (Just search for the first part, then change the bytes that are different in the second part. 4 patches in all plus the main protection patch.) This introductory file is by no means the last word on assembly, cracking, etc. Some of the ideas of the Mac were simplified in order to bring you up to a good level of proficiency in a short time. Some of the functions do not work exactly as I outlined, but the ideas I presented are close enough for those who are beginners to the Mac world. If you are interested in the real inner workings of the Mac, I suggest getting the Macintosh Revealed books from Hayden. They explain the traps and ROM routines in greater detail. ================================================================================ Copy Info There are several programs currently that don't seem to be fully crackable. By bit copying the one or two protected tracks and making a couple of patches, you can make them easily copyable, even though the original can't be copied at all. These programs all use protection from the same company, and it works like this: A nibble read of the protected track is done, then a search is made for the string ABCDEFEF where a data marker should be. They also write over low memory pointers that the debugger uses, so that the debugger will crash. Programs like this are: HARRIER, ROUGE, GRID WARS, WINTER GAMES, ETC. All have the following strings, which if you NOP them, will not destroy the debugger Search for:2489 51C8 FFF2 46C3 Change to:4E71 Search for:12D8 51C8 FFFC 46C3 Change to:4E71 Once you find the code that must be changed to unprotect, it is found to be encoded on the disk, and decoded just before it is used. On the HIPPO ALMANAC, the data was not only encoded, but the block of memory that it was in was reversed end for end. ================================================================================ Eve Protection Scheme There is a way to get past the EVE protection scheme, but it's a bitch.From what I understand, you need to decompile the routine that checks for the dongle, and them re-assign it to check that the machine has a simple serial port as opposed to the info on the dongle itself. That info is virtually unreadable as it's encrypted, and pretty much useless to anything except the app that's looking for it because it's mainly using for app reference. ================================================================================ Hard Disk Ejects It has come to our attention that many games are obnoxious when run (in cracked form) on a hard disk. These games cause a warm reboot and bring down the hard disk. The solution -> use FEdit to scan the games for OS Trap A017 (_Eject) and replace it with ADF4 ( _ReturnToFinder ). For example Game Fedit File Block Byte# Was Replace With MacAttack 00C 00BE A017 ADF4 Frogger 00C 005C A017 ADF4 Frogger 00D 01B4 A017 ADF4 ================================================================================ MINIFINDER ZAP For 3390 byte MiniFinder created Feb. 19, 1904 4:40 AM by CCOM. (If you don't have this version don't even think of trying the following.) Important note! Use Fedit or an equivalent to zap the file. Block 1, positions 88 and 89 contain the number of file types that MiniFinder looks for on a disk. Change Block 1 Position 89 from $01 to $02. Block 5, positions 408 through 416 contain the type list and other apparently useless information (I zeroed almost everything out and it still worked). Change Block 5 Positions 408 to 416 From: $00 $00 $6D $46 $69 $6E $64 $65 $72 To: $46 $4E $44 $52 $00 $00 $00 $00 $00 And that's it! Now The Finder of other disks will appear in the scoll window of MiniFinder when it runs. One problem though, make sure you have two drives. When the Finder of another disk is chosen to be opened, MiniFinder begins to execute it and then asks for the disk MiniFinder is on to be inserted into a drive if it is not online. If you do this by having to eject the disk with the Finder that you wnat to boot, MiniFinder gets confused and crashes. ================================================================================ MultiDisk Partition Cracks Heres how to do a MultiDisk Partion Crack: The way you "crack" Performer is with MultiDisk. If you don't know what I mean by that, he is the routine. Install The MultiDisk INIT, and DA. Reboot. Go to the DA. Create a partition that is a little bit large than Pedrformer (and it's few extra files) needs. Mount it. Insert the Perfromer disk. Make sure it is NOT write protected. Launch off the floppy. It will take you first to the HARD DISK INSTALL screen. Install onto the partition. Quit the installer. Unmount the FLOPPY. Check out the partition, see if the install works (it will...). Drag the partition to the trash (unmount it.). Open MacTools, etc., or whatever program you have that will, 1- let you see invisible files, and, 2- let you "uncheck" tthe invisible box, so as to make them visible on the desktop. Find the MultiDisk partition. It will be called, "MultiDisk Partition000000xxxxx" (lots of numbers, no matter...). Make it visible. Quit MacTools. Back to Finder. Finder the partition FILE. Drag it into a folder. VERY IMPORTANT, you must get this file off the desktop, by dragging it into a folder. If you don't, it will not copy properly! Open the folder you put it in. Slect the file. Hit command-D, and enjoy watching it copy. Do it again, even. The last few numbers on the file will get replaced with the word copy. No worry, just delete the letters c-o-p-y, and all will be ok. Now, get your floppy install back, so you can return it to whoever let you borrow it. Drag one of these copies out of the folder, open MultiDisk DA, and mount it. Insert the orig floppy. Launch off the floppy, as before. This time, REMOVE the install. Quit installer. Take out the floppy, write protect it, and give back to friend. It is now as it was before. Drag the partition to the trash (unmount). Also, Drag the MultiDisk FILE (the one on with all those numbers) to the trash. After all, you just removed the install from it, it is now worthless. Drag another one of the copies out of the folder, use the DA to mount it, and you now have Performer (or Vision, or anything else you desire). Copy protection bites the dust!!!! NOW, the files here on the Nest, like Performer, Vision, etc., are already partitions, you only need to dl MultiDisk. Make sure you keep a backup of MultiDisk, it likes to corrupt itself once in a while, and it will freak out if you are one a network. ================================================================================ Network Protection Scheme Cracking a Network protection scheme is pretty easy. Registration on any LocalTalk network has to be done by a single ROM call NBPRegister. The passed parameters to NBPRegister consist of a pointer to what is called an ABusRecord and a Boolean. Ignore the Boolean and look in the ABusRecord for another pointer called the nbpEntityPtr. (The newer versions of Nosy should decode these data structures for you.) Now find the data that is being passed in with the nbpEntityPtr (look for a PEA instruction), go to that data which will consist of a packed set of bytes corresponding to name:object:zone. Change the name, leaving object and zone alone. Presto Jerry and the thing now cannot recognize itself as self. ================================================================================ Shutdown Code For you people with hard disks and are running MacBugs or apple debugger and when you crash and are not able to exit to shell (Finder) here is the code to type to unmounted all volume and then do a shutdown this will help to recover faster by not having to rebuilding the desktop after your crash: SM A78 3F3C 0002 A895 (THEN A RETURN)(YOU HAVE TO PUT THE SPACES IN) G A78 (THEN ANOTHER RETURN) (YOU JUST DID A SHUTDOWN NOW) ================================================================================  This document is a short tutorial designed to prepare the reader to use TMON and MacNosy to render protection schemes inoperative. It will not prepare the reader to begin programming in assembly language, in fact, I am not a programmer myself. Hopefully this will allow someone with a minimum programming background to learn how to quickly read assembly listings, and then quickly locate a give protection scheme. Actual cracking will not be covered in detail in this document. The following topics will be discussed in detail: Number Systems and Memory Basic Architecture and Addressing Schemes Instruction operands and parameters The Flags Register The Stack Traps Assembly Mnemonics How To: MacNosy Example Code How To: TMON 2.8.x How to Crack Sorcerer: A Test Cruise. THE BASICS Number Systems We will be dealing with three different number systems. The difference between the number systems is simply at which number one decides to carry into the next column. In Decimal (the first system), we carry at the 10th number. That is, any given digit can only hold 10 values, namely, the numbers 0 - 9. Once we get to the carry value, we carry a one into the next column and reset the previous column to zero which is precisely what happens when you go from 9 to 10 (or 99 to 100 in which you carry twice, etc). The second number system is called binary. In this system, the carry value is 2. This means that a given digit (called a bit in binary) can hold 2 values: 0 and 1. To add one to a number in binary, you use the same principle as in decimal, except that the carry is a different value. To add 1 to 8 in decimal, you just add 1 and there is no carry (because the ones column hasn't reached the carry value (10) yet). To add 1 to 9 in decimal, you have to carry the one to the next column (because you have passed the carry value) and reset the ones column to 0. So, counting in binary looks like this: 0 1 Add one to zero: we haven't reached the carry value (2) yet. 10 Add one to one: now we have 2 so we have to carry one to the next column and reset the first column. 11 Add one to zero (in the first column) and you just get one. 100 Add one to the first column and you get 2 so carry 1 to the second column and zero the first column. Add the carried one from the first column to the second column and you are adding 1 + 1 which is 2 - carry again. So, carry the one to the third column and zero the second column. 101 And so on... 110 111 1000 1001 1010 1011 1100 1101 1110 1111 And here we are at 15 decimal. OK, we refer to binary because it is the native numbering system of the computer and also because in some of the instructions, the individual bits represent different information. Unfortunately, binary is hell for us humans. That brings us to the third major numbering which is hell for the computer AND hell for us! But both sides can deal so it's not too bad. Hexadecimal is the third system and its carry value is, of all things, 16. Now, we don't have 15 digits so hexadecimal uses the letters A-F for its last values. Here is how to count in hexadecimal; Hex Decimal Binary 0 0 0 1 1 1 2 2 10 3 3 11 4 4 100 5 5 101 6 6 110 7 7 111 8 8 1000 9 9 1001 A 10 1010 B 11 1011 C 12 1100 D 13 1101 E 14 1110 F 15 1111 10 16 10000 And so on. You may be wondering what the hell is so great about hex numbering. Well, it turns out that one hex digit can account for 4 binary digits (whereas decimal cannot hold a whole number of binary digits). This makes it extremely easy to convert binary to hex and back. To convert to hex from binary, just take the right-most 4 digits and convert it to its equivalent hex digit with the above table. Then do the same for the next 4 binary digits and keep going until you are out of binary digits. For example: 1010111000110001101. Break it up as follows: 101 0111 0001 1000 1101 and convert each group of 4 into a hex digit: 6 7 1 8 D so the hex number is 6718D. Easy right? To go back to binary, take each individual hex digit and convert it to its equivalent binary code. Signed Numbers and 2's Complement. The basic binary system has no way of representing negative numbers. To accomodate this, we use what is called a sign bit. The sign bit is simply the leftmost bit we a talking about (meaning that often we have a 32 bit piece of data, but only care about 8 or 16 of the bits - so the sign bit is the 8th or 16th bit respectively), and is set to one for negative numbers. This means that if you want an 8 bit number to be negative, then it's eighth bit must be 1 (and 16th bit must be one for 16 bit numbers, etc.). Two's Complement is an operation (yes there is an assembly instruction to perform it) that converts a positive integer to its negative equivalent (e.g. 1 to -1, 5 to -5, etc). To perform it, simply invert every bit in the number, then add a binary 1 to it. Take the number 00000001 (the eight bit integer 1). To make this -1, invert every bit (11111110) and add binary 1 to it -> 11111111. This then is -1 (or FF hex) as an eight bit integer. What happens if we want to treat this as a 16 bit integer? Big trouble, because now the sign bit is bit 16 and god only knows what is in bit 16. So, assembly has an instruction called Extend that extends a number out any number of binary places to make sure that any bits to the left of the original number don't affect its value. All of this is relatively unimportant, since the assembly program you are trying to crack has already taken care of all these details and I have yet to see this type of information be critical to the cracking process. I simply wanted to get this out in the open so that you will have a better understanding of some of the instructions that will come up in the assembly instruction listings. Now let us start by talking about memory. You probably already know that there are two kinds: ROM - Read Only Memory - and RAM - Random Access Memory. As crackers, we don't care about ROM since we can't change it. Memory is one of the two things that we can move information into and out of (the other being CPU registers explained below). Each individual piece of memory has its own address which is simply one number from a sequential list of all available memory (i.e. it starts at zero, and goes up to the end of memory). The address is the means of telling the processor which piece of memory we are talking about. For example, if we want to execute a piece of code, we need to tell the processor the address of the memory that the code starts at. Basic Architecture and Addressing Schemes CPU Registers The 680X0 processors contain 8 data registers and 8 address registers. You can think of a register as a variable if you like; basically it is a storage unit that can hold up to 32 bits (binary digits) of information - or 4 bytes. Note that the programmer is not required to use all 32 bits; in fact most assembly operators can be used on 8, 16 or all 32 of the bits. The Data registers are labeled D0 through D7 and are used to hold data that will be operated upon. For example, mathematical operators (e.g ADD, SUB[tract] ,etc.) operate on data registers. The Address registers are labeled A0 through A7 and are used to hold memory addresses. This is how assembly language treats pointers. Pointers are simply a tool for easily dealing with a particular section of memory. If an address register contains an address, then that register can be used to move things into and out of the memory address that it contains (i.e. the memory that it points to). It is important to remember that ANY register simply contains 32 bits of information. There is actually no difference between what is contained in a data register and what is contained in an address register. In fact, information can be moved between the two directly. The reason we call D0-D7 data registers, is because there are no commands to deal with their contents as addresses. And we call A0-A7 address registers because all the address commands apply to them. Addressing Schemes: The idea here is to understand some of the ways that information can be moved into and out of registers and memory itself. I will give some very short programming examples to illustrate both the syntax and the use of a given scheme. I will be using the MOVE instruction which simply moves the first argument into the second argument: for example: MOVE 100,D1 moves the number 100 into the data register D1. You might be wondering whether 100 is binary, decimal, or hexidecimal. Well, right now we don't care, but as a general rule, we will assume that a number is decimal, unless it is prefixed by a dollar sign $. TMON and Nosy will be very explicit about telling you what type of number the command is using - but more on that when we talk about TMON and Nosy. BTW, this list is not the offical set of addressing schemes. I have grouped similar schemes into larger groups. For example, there is immediate addressing which means that you are moving a value (not a memory address or register). I have grouped immediate addressing with direct addressing since it does the same thing. Direct Addressing: This is simply the moving of information directly into a register or memory address. Examples: MOVE 100,D1 ;100 is in decimal MOVE D1,D2 MOVE D0,100 ;A little different here: since 100 is the receiving address (the second one) it will be treated as a memory address. So this instruction moves the contents of D0 into memory address 100. MOVE $55,D5 ;$ indicates 55 is in hexadecimal MOVE $97BA54,A1 ;moves the hex address 97BA54 into A1. Remember here that the last two instructions are essentially the same. They both move some number into a register. However, the last instruction - since it moves the number into an address register - is setting up a pointer and a whole host of new instructions become available to it that are not available to the D registers. Later we will note that there are several parameters that can be attached to the MOVE instruction (and many other instructions, for that matter). These will be covered later. This section is simply to show you how various kinds of information is manipulated. Note that in Direct Addressing, you see exactly what it is that is being moved: in the first example, you can see directly that the decimal number 100 is being moved into register D1. Any subsequent operations on D1 will involve the number 100. Indirect Addressing: (extremely important) This scheme involves moving some address into an address register and then operating not on the number in the address register, but rather on the address that is contained in the address register. Example: MOVE 100,(A1) ;moves the decimal number 100 into the address pointed to (or contained in) by A1. Re-examine the last example of Direct Addressing. The command moved the number $97BA54 into address register A1. Since it is an address register, we can think of $97BA54 as an address rather than just a number. It may well be just a number, but odds are it will eventually be used as an address. The instruction above moves the decimal number 100 into the address $97BA54. It does not move the number 100 into address register A1. The parentheses mean that whatever is in A1 is actually an address and that this memory address will now contain the number 100. Example: MOVE (A1),$1000 This instruction looks at the contents of A1, treats the contents as a memory address, and gets whatever is contained in that address and moves into hex address 1000. Example: MOVE (A1),(A2) This instruction looks at the contents of A1, grabs the contents of the address it contains, and places this value into the address pointed to by A2. Lets look at a simple program and examine the memory that it deals with: MOVE 100,D0 ;move 100 into D0 MOVE $5000,A1 ;move address $5000 into A1 MOVE D0,(A1) ;move D0 into address in A1 MOVE D0,A1 ;move D0 into register A1 Ok, let's analyze this sucker. First off, we move the decimal number 100 into data register D0. Any further references to D0 will also be references to the number 100. The second instruction moves the hexadecimal number 5000 into address register A1. Since we are dealing with an address register, we can think of $5000 as the memory address $5000. The third instruction says to move the contents of D0 (which is the number 100) into the address contained in A1 (which is the address $5000). So after this instruction, if you looked at memory address $5000, you would see the number 100. The last instruction serves to illustrate the difference between direct and indirect addressing. This instruction move the contents of D0 (still 100) directly into register A1 (and not into memory address $5000, as the previous instruction did). After this instruction, if you looked at the A1 register, you would see the number (or address since it is an address register) 100. After this last instruction, if you repeated the third instruction, the number 100 would be moved into memory address 100 (since we just changed the address contained in register A1). Consider an assembly program that needs to fill a block of memory - let's say from address 100 to 200 - with the number 10. To do this with direct addressing would require the following: MOVE 10,D0 ;D0 now contains the fill number. MOVE D0,100 ;put the number 10 into address 100. MOVE D0,101 MOVE D0,102 and 97 more move instructions to directly move the number 10 into the appropriate memory addresses. Now consider the same program using indirect addressing (here I will use some psuedo-code to fill the loop structure): MOVE 100,A0 ;put first address into A0. While A0 not equal to 200 do the following: MOVE 10,(A0) Increment A0 to next address End While Loop. Note that this program is much simpler. Once the address register is set to the correct address, we can move the number 10 into this address then just increment the value in A0 which effectively makes A0 point to the next address. Note also that we could have MOVEd the number 10 into D0 and then inside the loop MOVEd D0,(A0) which would have had the same result but with one more instruction. Auto Increment Addressing: This is not actually a distinct scheme, rather it is a slight modification of the indirect scheme. The idea is to automatically update a pointer simply by referencing it. There are two flavors of this: auto pre-decrement, and auto post-increment. Pre-decrement first decrements the register in question, while post-increment increments the register after the instruction is finished. It looks like this: MOVE D0,-(A1) ;decrement A1 to the previous address and put the contents of D0 into this new address. MOVE D0,(A0)+ ;move D0 into address pointed to by A0 and then increment A0 to point to the next address. MOVE (A0)+,(A1)+ ;move the contents of memory pointed to by A0 into the memory address pointed to by A1 and then increment both registers. Now lets look at the previous program to fill a block of memory: MOVE 100,A0 While A0 not equal to 200 do: MOVE 10,(A0)+ ;fill the address and increment to next address. end while loop. In this program, we use the auto post-increment to automatically increment register A0 to the next address that we will be using. This type of program structure is often used to move and compare passwords around in memory. Let's say the password is residing at memory address $A000 and that we need to move it to address $B000 before we call a routine that checks to see if is the correct one. Here is a program we might use: MOVE $A000,A0 ;put source address in A0. MOVE $B000,A1 ;put destination into A1. MOVE (A0)+,(A1)+ ;move one piece of password to destination and increment both pointers. MOVE (A0)+,(A1)+ ;move next piece of password to destination. The third line moves the first half of the information from $A000 to $B000. After both registers are incremented, the registers contain $A002 and $B002 respectively and are ready for the next piece of the password to be moved (assuming the password was 4 bytes long). Now why, you are asking, did the auto-increment add two to the two addresses instead of just one? Well, check out the next section on data size parameters to find out. This about wraps up addressing schemes and register introduction. Next I want to look at one instruction - MOVE - and consider all the parameters one might use with it. The first thing to consider is that there are several types of MOVE instruction. There is the basic MOVE that we have used up until now. This is used to move data around. MOVEA is used to move addresses. Example: MOVEA $5000,A0. Yes - we should have been using this in the above examples when moving addresses into address registers, but I wanted to show addressing types, not instruction types. The Move Address is used just like the Move command, but lets you know that it is an address that is being moved (which means simply that the destination is an Address register). MOVEQ Move Quick: A shortcut instruction that moves an eight bit signed integer into a data register. Two things to note: 1) a eight bit integer translates to -128 to +127 in decimal (the 8th bit is the sign so we only get to use 7 bits as actual data), and 2) all 32 bits of the destination register are affected. This means that even though only 8 bits are used to represent the integer, these four bits will be sign extended into a 32 bit integer (remember - sign extension means that the sign of the number will be preserved as we use all 32 bits of the register). Don't get too confused here. The MOVEQ instruction simply takes an 8 bit integer and turns it into a 32 bit integer before putting it into a register. We could certainly think of the eight bit integer as unsigned (always positive) even though the instruction says that it is signed. Signing the integer becomes important only when we remember that the sign (or 8th bit) will be extended across 32 bits - so if you use MOVEQ to put the unsigned number 255 (11111111 binary) into D0, the instruction says OK, here is the signed eight bit number -1 (in binary, -1 and 255 are the same), and it needs to be turned into a 32 bit signed number. Now we have problems with the 255 because -1 in 32 bits is 32 binary ones, but 255 in 32 bits is still only 8 binary ones. This will make more sense when we look at data sizes. This command is often used to load loop counters into D registers. A standard MOVE instruction could be used, but the MOVEQ is a shorter command and therefore takes up less memory and fewer machine cycles. Example: MOVEQ $50,D1 ;treat this instruction as a normal direct address MOVE. MOVEM Move Multiple: used to quickly move several registers to or from memory. Example: MOVEM D4-D7/A0-A5,$5000. Moves data registers D4,D5,D6 and D7, and address registers A0,A1,A2,A3,A4, and A5 into memory starting at $5000. This command is used primarily at the start and end of subroutines to save the contents of registers. Note that by reversing the arguments (so that $5000 comes first), the registers are restored to their original values which were saved in the above instruction. There are a couple of other forms of the MOVE instruction, but they are rare and unimportant for cracking. If you see one, you should be able to figure out what it is doing. Now, we look at modifying the operands of the MOVE instruction. Up until now, we have worked under the assumption that registers (and memory) contain 32 bits of information. This is not quite true. First of all, a memory address can hold 8 bits of information. Luckily, the Mac is smart enough to know that if we are moving a 32 bit register into memory, it needs to use 4 consecutive memory addresses. Secondly, we aren't limited to just 32 bit instructions. Consider: MOVE.L D0,(A0) MOVE.W D0,(A0) MOVE.B D0,(A0) These demonstrate the methods for referring to Long-words (all 32 bits), Words (16 bits) and Bytes (8 bits). The first instruction moves all 32 bits of D0 into the address pointed to by A0. Since the address in A0 can hold only 8 bits of information, the processor will put the remaining 24 bits of information into the three address following A0. The second instruction says to move the low 16 bits (I'll illustrate low bits in a second) into the address pointed to by A0 and the address following A0. The last instruction moves the low 8 bits of D0 into just the address pointed to by A0. OK: here is what all that really means. Consider: Instruction Memory Address Contents-> $5000 $5001 $5002 $5003 MOVE $5000,A0 ?? ?? ?? ?? MOVE $12345678,D0 ?? ?? ?? ?? MOVE.B D0,(A0) $78 ?? ?? ?? MOVE.W D0,(A0) $56 $78 ?? ?? MOVE.L D0,(A0) $12 $34 $56 $78 Question marks indicate that the instruction did not affect that memory address. Note that 1) when the information to be moved is longer than 8 bits it is automatically moved into successive memory addresses, and 2) the information is stored from most significant to least significant. The terms most and least significant (or high and low) are used to designate the higher vs lower portions of the number. In the number $1FF hex, the most significant byte is 01 and the least significant byte is FF. In the number $12345678, the MSB (most significant byte) is 12 and the LSB is 78. In that same number, the most significant word (2 bytes) is 1234 and the least significant word is 5678. I will often make references to both most/least significant bytes and most/least significant bits. One last thing before we move on is to note that when using the auto increment/decrment addressing modes, the amount of increment or decrement is dependent upon the size of the data being moved (which makes sense). If you say MOVE.W D0,(A0)+ then A0 will be incremented 2 bytes so that it then points one address past the data just moved into it. Likewise, if the instruction was MOVE.L D0,(A0)+, then A0 would be incremented by 4 bytes and would again point one address past the data just moved. Also, often the size identifier is left off the instruction (like in MOVE D0,D2). When this is the case, it means the instruction is using a word size operand or MOVE.W. If the instruction is referring to byte or long-word size operands, it will explicitly say so in the command - MOVE.B or MOVE.L. Special Registers: Program Counter, denoted PC. This register always points to the instruction to be executed. You won't usually care what is contained in the PC, but you will want to do your assembly listings from wherever it currently is. TMON makes it very simple to start dis-assembling from the current PC so that you can see on-screen the instructions that are going to be executed. The Status Register: very important. This guy is how the processor keeps track of what just happened. For example, anytime you compare two values, you need to know if they were equal, not equal, one was bigger, etc. All this type of information is contained in the Status register. Basically, the status register is a 16 bit register in which certain bits contain information that you will want to access. Don't worry about which bits mean what because assembly language has operators that refer to the bits with nice, easy to remember mnemonics. Here are the bits that you will care about: Z the zero flag. This flag is set if the result of an operation is zero, or if two compared values are the same - it is cleared otherwise. For example, ADD.B $FF,1 would result in the number $100. But since we specified a byte size operation, the byte result is 0 and the flag would be set. C the carry flag. This contains the carry from an arithmetic operation. If you add two 8 bit (.B) numbers, the carry flag contains the 9th bit. Say you add $FF and 1 again. The result is a byte valye of 0 with a carry into the next bit. This carry would show up in the c flag. This bit also receives bits that are shifted out of a number during shift or rotate instructions. (See commands list). N the negative flag. Set if the high bit (meaning the 8th bit when using the .B specifier, the 16th bit for the .W, etc) of an operation gets set. Also gets set if the result of an operation is negative. V the overflow flag. Set whenever an operation yields a result that cannot be properly represented. For example, when adding the bytes 7F and 01, the result - 80 - cannot be represented in 8 bits. In eight bits, the eighth bit is the sign bit (telling whether the number is posative or negative). Note that this only happens if you are adding bytes - if the command added words, then the result CAN be represented in 16 bits. This flag won't be used too much. X the extended flag. This is basically a copy of the carry bit, but not all operations affect it. The X flag is used to enable multi-precision instructions, that is, instructions can be intermixed without always affecting the X flag (in this case , the multi-precision carry bit). Once again, not used to much. This probably doesn't make too much sense. That's OK, because you will get the hang of it when we look at a batch of code listings. The only reason I am listing them here is because TMON can display these flags and their current values. This allows you to predict where the program is going when it decides to branch somewhere. These flags are used to control program flow and, as such, are the single most important element to cracking. This is how you tell a program that the password you just typed was equal (and not unequal) to the password the program is looking for. We will look at the branch instructions later on. These instructions almost all use the Status Register Flags. The final special register is actually just the A7 address register. The reason it is special is because it is used as the stack pointer on the Mac. The Stack is basically a chunk of memory that is used for special situations such as jumping to a subroutine and having to remember where the program jumped from so it can return when the subroutine is finished. The stack is also an excellent way to pass values to a subroutine. This will be illustrated later. All you need to understand is that the Stack is a piece of memory and can be manipulated as such. To refer to the stack, refer to the A7 register. Also, the stack moves backwards as it is used. Therefore, when a program wants to put a number on the stack it uses the pre-decrement indirect addressing mode: MOVE D0,-(A7) ;puts the value in D0 onto the stack and moves the stack pointer back one address. MOVE (A7)+,D0 ;puts the value on the stack into D0 and increments the stack pointer to the next stack value. And of course, to get the value back off the stack, you would use Post-Increment. These are not always used, but when they are used, it moves the stack pointer to the next available piece of stack space. When we begin working with Traps, you get a good workout with the stack so don't worry if this doesn't make complete (or any) sense yet. Traps Traps are a quick and easy method of accessing the 9 jillion built-in subroutines found in the Macintosh ROM. Traps do everything under the sun and are probably the main reason that all the Mac programs look alike. When a program wants do anything from drawing text to bringing up dialog boxes to putting up menus, traps are used. Why not just call the subroutines directly? Well, the problem is that every time Apple comes out with new system software, they change the addresses of one or more of these subroutines that almost all programs need. This would create chaos for applications, so Apple uses the idea of a trap table. The trap table is a means of associating the trap name (actually it's machine language code) with the proper address of the subroutine. So, no matter what the system version (within reason), an application can use the trap table to correctly call the subroutine it wants. These traps are easy to spot: they all start with an underscore and then the name of the trap, e.g. _GetNewDialog. A quick note about traps and viruses / anti-virus programs. If you were ever wondering how a virus program works, consider that a virus needs to be able to write portions of itself onto a disk. To do this, it needs to have access to an operating system that can do the actually writing. It could either pack an operating system around with itself (unwieldy and difficult to change when apple modifies the system) or use the trap table to call the traps that write resources. Now, the trap table can be patched by a program...i.e. a programmer can substitute his own subroutine into the trap table so that any program that calls the trap to do something, actually calls the new subroutine. Knowing this, an application could be written that patches the trap table and monitors the activity of any trap that writes resources. (I haven't de-compiled the newer virus programs, but I know that's how vaccine worked). The anti-viral program then just sits back and intercepts any of these traps, takes a look to see just what it is that is being written and where. If it looks suspicious (like writing an nVIR resource to the system!) then it lets you know. WDEF was a really great virus because the programmer figured a way to bypass this method. The first thing WDEF does is try to determine exactly which system it is operating under, and, if it is one that it recognizes (the 6.0x series I believe) it will re-patch the trap table with the original system values so that it can write to the disk without being monitored! The key is that this only works if WDEF knows the original values of the trap table and, since they often change, this means that WDEF is only effective on certain system versions. (Note that if it cannot re-patch the trap table, it will attempt to run and hope that there is no anti-virus program running). Well, back to assembly. Almost every trap needs some parameters to operate. For example, GetNewDialog needs several parameters, including the ID # of the dialog to load, and several other things; and it returns a pointer to the dialog. Here is where the stack becomes important. Most traps use the stack to pass parameters and return values. Consider the following code (which will probably be incomprehensible) CLR -(A7) ;put 0 (word length since there is no size specifier ) on the stack MOVE $2FF,-(A7) ;Put $2FF (word size again) on the stack. CLR.L -(A7) ;put a nil-pointer on stack _StopAlert MOVE (A7)+,D0 This little subprogram brings up an alert dialog. If we were to look in Inside Mac vol 1under StopAlert we would find that it requires 2 arguments and returns one result. If we were programming, we would care what types of information these parameters are (integer, pointer, etc.) but since we are cracking, we can assume that the program to be cracked has already figured all this out. Anytime a trap returns a value the calling code must allocate space on the stack before it puts the parameters on the stack. That is precisely what the CLR instruction does. (CLR or clear, puts zero into its operand so CLR.L D0 would put 32 zero bits in D0) This is a fast way to move the stack pointer back one byte...we don't actually care what get puts on the stack (zero in this case) because the trap is going to replace that number with its return result. Since Inside Mac says StopAlert returns an integer and that an integer is 2 bytes or 1 word, we first clear an integer's worth of space on the stack. Next we start putting arguments on the stack in the same order as Inside Mac says. The first thing is the alertID which is an integer. This is simply the number of the alert - i.e. the number you would see if you looked at the alerts in Resedit. So, this number ($2FF in my example) is moved onto the stack. The second argument is filterproc and is a procpointer (nothing more than a pointer). This argument is used only if the built-in dialog handlers don't quite cut it for you're application (maybe you have special command keys to watch for or something). If this is the case, you would pass a pointer to you're filtering procedure in this argument. Since I don't care about this, I will pass a nil pointer (one that points to nothing - this is defined as $00000000 [a long word] in assembly). Once I have put the proper information on the stack, I can call the trap. The final instruction moves the trap result from the stack into register D0. At this point I can test the result and branch accordingly. Finally, let's take at the branching structures. Branching is how a program makes decisions based upon tested values. For example, you type in a password. The program must compare what you typed in with what the true password is. Once it compares the two, it has to be able to go one place if you typed in the correct value, and someplace else if you typed in the wrong password. There are several ways to compare values, and I will cover all of them in a listing of the assembly commands. The most common is the CMP (compare) command. This compares its two operands, and sets the Z flag if the two are equal and clears the Z flag if the the two are different. Don't worry if the Z-flag doesn't quite register - it was one of the bits of the status register and you won't care too much about it...just note that the various branching instructions will be testing the status flags and jumping to a new chunk of the program accordingly. How about an example? MOVE.B 1,D0 MOVE.B 2,D1 CMP.B D0,D1 BEQ Code Section 1 BNE Code Section 2 OK, first we move two numbers into D0 and D1. The CMP instructiion compares the two values (actually it subtracts the second from the first - thus you can test for more things than just the two being equal) and sets the status register accordingly. From this example, we can see the the two are not equal and so the BEQ (branch if equal) will not be executed. However the BNE (branch if not equal) will be executed since the values are indeed not equal. The branch instructions cause program execution to actually jump to a new spot in memory. From this example, you can see that what flags in the status register actually get set is not of primary concern. All you have to know is that two values are being compared, and the program wants to know if they are equal - as opposed to wanting to see which was bigger...consider: MOVE.B 1,D0 MOVE.B 2,D1 CMP.B D0,D1 BGT Code Section 1 BLE Code section Here, the program wants to know which value is bigger. In this example, if D1 is bigger than D0, the the BGT (branch if greater than) will execute. The BLE (branch is less than or equal) will not execute. This is really easy to pick out in programs - as long as one of the various CMP instructions is used...note I say of the various; remember that most commands have several modes: consider CMP (compare), CMPA (compare address), CMPI (compare immediate), CMPM (compare memory). Once again, you don't care which of these is being used, you just care what the hell is being compared, and how they are being compared (are they equal?, is one bigger?, etc) Let me quickly mention that BEQ is not technically branch if equal (although functionally it certainly is). BEQ means branch if equal to zero (referring to the Z bit in the status register) and BNE means branch if not equal to zero. This is not critical, but it will help you to correlate the zero bit in the status register with the BEQ and BNE instructions. OK, now let's take this one step further. You know that a program can use the CMP instruction to test two values and you know that something happens to the status register - but you really don't care what - and you also know that you can jump to a new section of code based upon the result of the CMP. Consider for a moment the fact that the branch instructions depend entirely upon a bit in the status register. By this I mean that BEQ only executes if the Z (zero) bit is set, BCC (branch carry clear) only executes if the carry flag is clear, etc. From this it should be evident that ANY operation that changes the status register bits, could potentially be a reference for a branch. Consider the seemingly harmless enough CLR instruction. It serves to put the value zero into its operand. But, by its very definition, the CLR instruction sets the Z flag to 1 since it is setting something equal to zero. There are a slew of commands that set and clear the various bits in the status register. Refer to the command listing to see which commands affect which status flags. There are also several ways to change which section of code is currently executing. As you have seen, the branch instructions all cause the program to jump to another piece of code. Similarly, the BRA (branch with no test of status flags), JMP (jump), BSR (branch to subroutine) and JSR (jump to subroutine) all cause the program to jump to another location and begin executing. The BSR and JSR will cause the program to execute at its new loaction until an RTS (return from subroutine) is encountered at which point the program jumps back to the instruction following the original JSR or BSR. Finally, I want to quickly discuss two important instructions: PEA and LEA, which stand for Push effective address and Load effective address. Basically, LEA takes the first argument, computes the address at which that argument resides, and puts that address into the second argument. PEA computes the address of the argument and puts that address onto the stack. Many programs use PEA as a shortcut to putting trap arguments onto the stack. For example: LEA var1,A0 ;put the address of variable 1 into A0 MOVEA.L A0,-(A7) ;put address on stack. PEA var1 ;put address of variable1 on the stack. These two code listings do essentially the same thing. The first computes the address where variable 1 resides in memory and places that address in A0. At this point, we could use A0 to move information into an out of the variable var1 using indirect addressing (MOVE 1,(A0)). Then, the address in A0 is placed on the stack. The last line directly moves the address of variable onto the stack accomplishing the same thing as the previous instructions. A word about pointers and handles. You should be familiar with pointers by now. A pointer is simply an address which is used to access the memory that it points to. A handle is nothing more than a pointer to a pointer. That is, a handle is an address that points to some piece of memory, just like a pointer. The difference is that the memory the handle points to contains the address of yet another piece of memory. Many traps return handles to data rather than pointers. The reason is so that if the Mac's memory manager needs to move memory around, pointers can be moved without loosing the handle to the pointer. This isn't too important to cracking since, once again, the program knows how to handle its pointers. You will often find a section of code that looks like this: _GetNewDialog ;this trap returns a handle (according to IM) to the dialog in question. MOVE.L (A7)+,A0 ;Move the handle from the stack into A0. MOVE.L (A0),A0 ;A0 now contains the pointer. Basically, this turns a handle into a pointer. First, the handle is moved from the stack into A0. (Remember, traps pass return values via the stack). Next, using indirect addressing, the handle is turned into a pointer. The last line first looks at the value in A0 and treats it as an address. Then it looks at the contents of this address. This 32 bit value (which is actually the pointer that the handle points to) is then moved back into A0. Lets say A0 contains memory address 1000. At memory address 1000 is the value 2000. Now, 2000 is where the data we care about is actually located. So, we take the value in 1000 (which is 2000) and place that value back into A0. After this line, A0 contains the value (or address) 2000 and so A0 points to the data in question. I illustrate this because it is an often used technique. Following is a detailed description of all the 68000 instructions. Some day I will buy a book on 68030/68882 instructions and update this, but it should serve for now. COMMAND LISTING ABCD Add Binary Coded Decimal. Add two operands using BCD, result is in the second operand. Binary coded decimal is basically hexidecimal without the letter codes for the numbers 10-15. Using this, we get the flexibility of hexidecimal but the convience of decimal. I have yet to see this used. Flags affected: N: Undefined. Z Cleared if the result is not zero, otherwise unchanged. C Set by carry out of the most significant BCD digit. X Same as C. V Undefined. ADD Add two operands, result in the second operand. Flags affected: N Set if high-order bit of result was 1, otherwise cleared. Z Set if result was zero, cleared otherwise. C Set by the carry out of the most significant bit, cleared otherwise. X Same as C. V Set if operation results in an overflow (see definition of this bit). ADDA Add Address: add the contents of address registers, result in second operand. Flags affected: None ADDI Add Immediate: Add a constant to an effective address, result in second operand. Flags affected: N Set if high bit of result is set. Z Set result is zero. C Set on carry out of most significant bit. X Same as C. V Set on overflow. ADDQ Add Quick: Add a three bit value to the second argument, result in second argument. Flags affected: N Set if high bit of result is set. Z Set if result is zero. C Set of carry out of high bit. X Same as C. V Set on overflow. ADDX Add Extended: add two values but allowing for values that require more than 32 bits of information. Flags affected: N Set result was negative. Z Cleared if result is not zero. Else unchanged. C Set on carry out of high bit. X Same as C. V Set on overflow. AND Performs bit-wise and upon the two operands with the result in the second operand. This means that the two values are compared bit by bit. For every binary digit, if both operands contain a one, the result will contain a 1, otherwise the result will contain a zero. For example, consider 101 AND 110. The result would be 100 (only the third bit is set in both numbers. Flags affected: N Set if high bit of result is set. Z Set if result is zero, cleared otherwise. C Always cleared. V Always cleared. ANDI And Immediate: Performs bitwise and with a constand and an operand, result in second operand. Flags affected: same as AND instruction ASL Arithmetic Shift Left: Performs a bitwise shift left. If there are two arguments, then the first determines how may times to shift the bits to the left. The lowest bit is set to zero. X Set according to the last bit shifted out of the operand (that is, the most significant bit before the shift was executed. N Set according to the most significant bit in the result. Z Set if the result is equal to zero (all bits zeroed), cleared otherwise. C Same as the X bit. V Set if the most significant bit is changed at any time during the operation. (That is, if the ASL involves shifting more than one time, then if during any of the shifts, the msb is changed, V is set). NOTE - the msb does NOT mean the leftmost bit as I described way back when. It DOES mean the leftmost bit within the range of the operation. In other words, if it is a byte level shift, then the 8th bit is the msb, if the operation is at the word level, the the 16th bit is the msb, etc. A quick note about bit operations is probably in order. Basically, any register contains 32 bits, each of which is either a one or a zero. Assembly language contains several commands for directly manipulating the individual bits in a register - as opposed to manipulating the entire value contained in the register. For example, consider the ASL above. Basically, this command moves each bit in the register in question over one slot. Now, knowing how binary numbers work, you should be able to see that this operation serves to effectively multiply the value of the entire register by 2. Similarly, the ASR (shift right) will effectively divide the value in the register by 2. There are also commands to set and clear individual bits, as well as test to see if individual bits are set. More on these commands later in the listing... ASR Arithmetic Shift Right: Performs a bitwise shift right. If there are two arguments, then the first determines how may times to shift the bits to the right. The most significant bit is unchanged (and not zeroed as in the ASL); this is so that the sign bit remains unchanged. X Set according to the last bit shifted out of the operand (that is, the lowest bit before the shift was executed. N Set according to the most significant bit in the result. Z Set if the result is equal to zero (all bits zeroed), cleared otherwise. C Same as the X bit. V Always cleared. OK, next are the infamous branch instructions. Basically, all these operations will examine one or more of the flags and jump to a new section of code based on the result. None of these affect the status flags. Since these are the instructions that usually need to be altered to crack a program, I will list the actual hex codes associated with the instructions. This way you can go into Resedit and apply the patch. All the branches translate to 6X AA in hex where X is the status flag to check, and AA is the address to branch to. To modify the type of branch, just change the X, e.g. to change BEQ (67 hex) into BNE (66 hex) just go into Resedit, find the 67 in question, and replace it with 66. To change the address that the branch jumps to, you need to find the address you want the branch to jump to. Then start counting instructon bytes starting with the byte immediately following the branch instruction. Call that byte zero and count upwards to the spot to jump to. This number (the difference between the two addresses is the AA parameter. Note that you can start counting backwards also if you need to branch backwards. More on all of this in the actual cracking manual. Here they are: BCC Branch Carry Clear. Branch if the C flag is clear. 64 hex. BCS Branch Carry Set. Branch if the C flag is set. 65 hex. BEQ Branch if Equal. Branch if the Z flag is set. 67 hex. BNE Branch if Not-Equal. Branch if the Z flag is clear. 66 hex. BGE Branch if Greater Than or Equal. Branch if the N and V flags are either both set or both cleared. Basically, when dealing with these multi-flag branches (yes, there are several more coming up), look at the instruction that set the flags (usually a CMP) and ask yourself whether the relationship between the 2nd and 1st operands (the order is critical!) is true. So, for BGE, look at the CMP and say - is the 2nd operand greater than or equal to the first? If so, the branch will go. Or you can just step through this stupid command with TMON and see whether or not it branches. 6C hex. BGT Branch if Greater Than. Branch if 1) N and V are set and Z is clear, or 2) N, V, and Z are all clear. Basically the same as above but don't branch if the two are equal. 6E hex. BLE Branch if Less Than or Equal. Branch if 1) the Z bit is clear, 2) N is set and V is clear, or 3) N is clear and V is set. 6F hex. BLT Branch if Less Than. Branch if 1) N is set and V is clear, or 2) N is clear and V is set. 6D hex. BHI Branch if Higher Than. Branch if C and Z are both clear. Treat this as the same as BGT. 62 hex. BLS Branch if Lower or Same. Branch if either C or Z are set. Treat this as BLE. 63 hex. BMI Branch Minus. Branch if the N bit is set. 6B hex. BPL Branch Plus. Branch if the N bit is clear. 6A hex. BVC Branch V Clear. Branch if V is clear. 68 hex. BVS Branch V Set. Branch if V is set. 69 hex. BRA Branch. Branch regardless of what the hell is in the flags. This one is important...Imagine a program checking for an original disk, and then saying BEQ to the rest of the program. If Z is clear, the program continues and bombs. Now imagine changing that BEQ to BRA. All of a sudden, the dumb thing jumps to itself correctly no matter what happens! 60 hex. BCHG Bit test and Change. Inverts the nth bit (determined by the first operand) in the 2nd operand. Z is set according to the state of the bit BEFORE the inversion (by this I mean that if the bit was 0, Z is set and vice versa). No other flags are changed. BCLR Bit test and Clear. Same as above but clears the nth bit instead of inverting it. Flags are set the same. BSET Bit test and Set. Same as BCLR but sets the nth bit instead of clearing it. Flags are set the same. BSR Branch to Subroutine. This instruction first places the instruction following the BSR onto the stack. Next, operation is continued at the address specified by the BSR - called a subroutine. At the end of the subroutine will be a return instruction - covered later - at which point the original address is popped off the stack and execution continues from the instruction following the BSR. BSR is the same as JSR for all intents and purposes, except that BSR can first check any of the status flags the same way that the branch instructions did. BTST Bit Test. Test the nth bit of an operand and set the Z flag accordingly. CLR Clear. Sets its operand to zero. N Always cleared. Z Always set. CMP Compare. Compares two values. Actually, this command sets the status flags as if the second operand were subtracted from the first (but neither operand is actually changed). See the SUB command for more details. N Set if the result is negative. Cleared otherwise. Z Set if the result is zero - or if the operands are equal. Cleared otherwise. C Set if the result generates a borrow. Cleared otherwise. V Set on overflow in the subtract. Cleared otherwise. CMPA Compare Address. Same as above but this command will be used to compare address registers. CMPI Compare Immediate. Same as CMP, but this command will be used if the first operand is an actual number (instead of a register). CMPM Compare Memory. Once again, same as CMP, but this command always uses post-increment addressing and compares two memory addresses. Decrement and Branch instructions These commands make up part of assembly language's looping structures. Essentially, these commands decrement a loop counter (a specified data register) and branch back to the start of the loop. There are two ways that the loop may be terminated. First, if the condition is met, the loop will end, and second, if the loop counter reaches -1 then the loop will end. I am not going to list all the conditions for each command - for these, refer to the corresponding branch instruction. DBRA Decrement and Branch. No conditions are checked - terminate loop only when the loop counter reaches -1. DBCC Decrement and Branch unless Carry Clear. DBCS Decrement and Branch unless Carry Set. DBEQ Decrement and Branch unless Zero. DBNE Decrement and Branch unless Not Zero. DBGE Decrement and Branch unless Greater Than or Equal. DBGT Decrement and Branch unless Greater Than. DBHI Decrement and Branch unless Higher Than. DBLE Decrement and Branch unless Less Than or Equal. DBLS Decrement and Branch unless Less Than or Same. DBLT Decrement and Branch unless Less Than, DBMI Decrement and Branch unless Minus. DBPL Decrement and Branch unless Plus. DBVC Decrement and Branch unless V Clear. DBVS Decrement and Branch unless V Set. DIVS Divide Signed. Divides a 32 bit quantity (second operand) by a 16 bit quantity (first operand). The low order word of the 2nd operand gets the quotient and the upper word gets the remainder. N Set if quotient is negative cleared otherwise. Undefined if overflow. Z Set if result is zero, cleared otherwise. Undefined if overflow. C Always cleared. V Set on overflow. DIVU Divide Unsigned. Treat this as identical to the above instruction except that the result is treated as an unsigned integer. Flags are set the same. EOR Exclusive Or. Performs an exclusive or (which means that each bits are compared, if both are 1, the resultant bit is 0, if one of the two is 1, the result is 1, and if both are 0, the result is 0) on two operands. The result is in the 2nd operand. Example: EOR 1010,0011 (both binary) would yield 1001. This is not a valid instruction, but does show how exclusive or works. N Set if the most significant bit of the result is 1, cleared otherwise. Z Set if the entire result is zero (all bits zero), cleared otherwise. C Always cleared. V Always cleared. EORI Exclusive Or Immediate. Same as above, but the first operand will be an actual number. EXG Exchange. Exchanges all 32 bits of any two registers. No flags are affected. EXT Extend. Extends the sign bit into either a word or long word data size. N Set if result is negative, cleared otherwise. Z Set if result is zero, cleared otherwise. C Always cleared. V Always cleared. JMP Jump. Transfers control to another section of code. The address supplied (using any of the addressing modes) is put into the program counter and execution commences from that address. No flags are affected. JSR Jump Subroutine. Places the address of the next instruction on the stack, places the supplied address in the PC, and commences execution at the supplied address. When the subroutine executes a return instruction (below) the address on the stack is popped off and placed in the PC and execution commences at the address following the JSR. No flags are affected. LEA Load Effective Address. Computes the address of the first operand and places that address in the 2nd operand. No flags are affected. LINK Link. This command is a bitch to understand, but it is used a lot and is the method most compilers use to handle local variables for subroutines. Basically, Link creates what is called a Stack Frame on the Stack. Link takes two operands, an address register (A6 is almost always used), and the size of the stack frame to create. First, the address register is pushed on the stack and the resulting stack pointer is placed in the address register making it a new temporary stack pointer. Then the 2nd argument is added (note that this number is usually negative) to the original stack pointer. The memory between the stack pointer and the address register is then treated as a buffer to contain any local variables the subroutine may need. Don't worry to much about the dynamics of this command - just remember that usually, a subroutine will start with a LINK command, and end with an Unlink command and then return to the calling procedure. LSL Logical Shift Left. Performs a bitwise left shift on the second operand (or the first if there is only one). The first operand (if there are two) tells how many times to shift. The first bit is set to zero. X Set according to the most significant bit before the shift is executed. N Set a one is shifted into the most significant bit (indicating a negative result for signed numbers), cleared otherwise. Z Set if the entire result is zero, cleared otherwise. C Same as X. V Always cleared. LSR Logical Shift Right. Same as above, but shifts right. The most significant bit is set to zero (meaning the sign is lost. If this important, the program would use ASR instead). X Set according to the first bit before the shift was executed. N Always cleared (since zero is shifted into the sign bit). Z Set if the result is zero, cleared otherwise. C Same as X. V Always cleared. MOVE Move. Moves the first operand into the second operand. N Set if the most significant bit of the result is set, cleared otherwise. Z Set if the result is zero, cleared otherwise. V Always cleared. C Always cleared. MOVEA Move Address. Same as Move except that address regesters are being used. No flags are affected. MOVEM Move Multiple. Moves the specified register(s) onto or out of the stack to facilitate temporary storing of the registers. MOVEQ Move Quick. Moves an 8 bit signed integer into a register. The 8 bit integer is sign extended to 32 bits and then all 32 bits are placed into the destination register. N Set if result is negative, cleared otherwise. Z Set if result is zero, cleared otherwise. C Always cleared. V Always cleared. MULS Multiply Signed. Multiplys the first argument by the second with the result in the second operand. N Set if the result is negative, cleared otherwise. Z Set if the result is zero, cleared otherwise. C Always cleared. V Always cleared. MULU Multiply Signed. Same as above. I am not sure as to the exact difference between the two multiply command nor the two divide commands. I wouldn't worry about it. NBCD Negate Binary Coded Decimal. Converts a BCD number into its corresponding negative value, much the same as the NEG instruction (below). NEG Negative. Performs two's complement on the supplied operand converting it to its negative counterpart. X Cleared if the result is zero. Set otherwise. N Set if the result is negative. Cleared otherwise. Z Set if the result is zero. Cleared otherwise. V Set on overflow. cleared otherwise. C Same as X. NEGX Negative Extended. Same as NEG but used for multi-precision numbers. X Set on borrow. Cleared otherwise. N Set if result is negative. Cleared otherwise. Z Cleared if result is not zero. Otherwise unchanged. V Set on overflow. Cleared otherwise. C Same as X. NOP No Operation. A two byte instruction that does nothing. This is supposedly to allow programmers room for future expansion or something, but I suspect it is to allow crackers to remove instructions without fouling up the program. No flags are affected. The hex code is 4E71 and we will definately be using this to effectively remove offensive instructions from applications - note that it is a 2 byte instruction, the same size as a branch instruction... NOT One's Complement. Inverts every bit in the operand. N Set if result is negative. Cleared otherwise. Z Set if zero. Cleared otherwise. V Always cleared. C Always cleared. OR Binary OR. Compares bits one at a time from the two operands. Result bit is one unless both bits are zero. Result bits into second operand. Example: OR 0101,1100 yields 1101. N Set if most significant bit of the result is set, cleared otherwise. Z Set if result is zero, cleared otherwise. V Always cleared. C Always cleared. ORI OR Immediate. Same as OR but used when the first operand is a numeric constant. Same flags set as OR. PEA Push Effective Address. Pushes the address of the supplied operand onto the stack using auto post-decrement. This command is often used to pass pointers (VAR variables in Inside Mac) to traps and subroutines. No flags affected. ROL Rotate Left. Similar to the Left Shifts, except that not only is the leftmost bit shifted into the C flag, but it is also rotated back into the first bit of the operand (instead of a zero being shifted there). N Set if one is rotated into the most significant bit, cleared otherwise. Z Set if result is zero, cleared otherwise. C Set according to the last bit shifted out of the operand. V Always cleared. ROR Rotate Right. Same as ROL, but shift to the right. The bit shifted out of the lowest position is placed into the C flag, and also rotated back into the most significant bit. N Set if one is rotated into the msb, cleared otherwise. Z Set if the result is zero, cleared otherwise. C Set according to the last bit shifted out of the operand. V Always cleared. ROXL Rotate Left with Extend. Same as ROL, but the bit that gets shifted into the C flag is also shifted into the X flag. Flags identical to ROL except that the X flag will be the same as the C flag. ROXR Rotate Right with Extend. Same as ROR, but the bit that gets shifted into the C flag is also shifted into the X flag. flags identical to ROR except the the X flag will be the same as the C flag. RTS Return from Subroutine. Places the long word from the top of the stack into the program counter and resumes execution. This has the effect of returning execution at the end of a subroutine called with either BSR or JSR. No flags are affected. Set Instructions. This is a group of instructions that use the condition flags in an identical manner to the Branch instructions and therefore will not be listed out in full detail. Essentially, if the condition that the command is testing (for example, not equal) then the operand's low byte is set to all ones (hex FF), otherwise the byte is cleared to zero. Example: SEQ D0 This would set the low byte of D0 to hex FF if the Z flag was set. There are two special forms: SF will always clear the byte, and ST will always set the byte. SUB Subtract. Subtracts the first operand from the second - result in the second operand. X Set on borrow, cleared otherwise. N Set if msb is one, cleared otherwise. Z Set if result is zero, cleared otherwise. V Set on overflow, cleared otherwise. C Same as X. SUBA Subtract Address. Same as SUB, but used for address registers. No flags are affected. SUBI Subtract Immediate. Same as SUB, but used when the first operand is a numeric constant. Same flags set as SUB. SUBQ Subtract Quick. Same as SUBI except that the constant is limited to 3 bits. Same flags as SUB. SUBX Subtract Extended. Subtract for multi-precision numbers. Same flags set as SUB, except that Z will only be cleared by a non-zero result - it will not be set by zero. SWAP Swap. Swaps the words in a single operand. That is, bits 0-15 are swapped with bits 16-31. N Set if bit 31 of result is set, cleared otherwise. Z Set if entire result is zero, cleared otherwise. V Always cleared. C Always cleared. TAS Test and Set. Tests a byte specified by the operand and sets the high order bit of that byte to 1. Apparently this is used to prevent two processors from grabbing the same resource - but I have not seen it. N Set according to the high order bit of the specified byte before the TAS command is executed. Z Set if the byte is zero before the TAS is executed. V Always cleared. C Always cleared. TRAP Trap. All traps will be presented in their Inside Macintosh equivalent so you should never see this command. TRAPV Trap on Overflow. If V is clear, do nothing. If V is set, then the flags and the program counter are pushed on the stack, and the a new program counter is loaded from absolute location 1C hex. I have seen this instruction, but have ignored. Apparently some high-level languages use this to process overflow errors. TST Test. Tests an operand for negative or zero values. N Set if the msb is set, cleared otherwise. Z Set if zero, cleared otherwise. V Always cleared. C Always cleared. UNLK Unlink. Undoes a LINK command. The specified addres register is placed in the stack pointer (restoring it) and a long word is popped off the stack and placed in the address register (restoring it). No flags are affected. Using MacNosy Before looking at an actual assembly program listing, we need to look at MacNosy. The version I am using is 2.95 so if you have an older version, bear with me. What the Hell is it?? MacNosy is an incredible dissassembler. Instead of simply converting all the hex information in a program straight into assembler syntax, Nosy analyzes the program recursively, attempting to determine exactly where data is located, what types of information is being used and passed to and from procedures, etc. Once Nosy has attacked a program, it expects you to give it some hints about what you think is going on, then Nosy examines it again and so on until you like what you see. The two main types of information Nosy deals with are Code Blocks, and Data Blocks. Code blocks are what Nosy thinks can actually be executed while data is simply referred to by the code - but never actually executed. Often Nosy will be tricked into thinking that a code block is a data block. You will find out later how to show Nosy what is really going on. Starting Out. The first thing Nosy presents you with is an open dialog requesting the program to disassemble. All resource files will be available, but only something with executable code would make sense to decompile - such as applications, DAs, Inits, Cdevs, etc. Once the file to decompile has been selected, Nosy asks if you want to view the resources. Pressing y will list all resources and information pertaining to each. Pressing n or just will skip to the next question. Next Nosy wants to know what type of resource to decompile. Press return to decompile CODE resources (for applications, and any inits, cdevs, or DAs that use CODE resources). If CODE is not what you want, type in the resource type - INIT for inits, DRVR for DAs, and >cdev for Cdevs (the > is necessary). Finally, a dialog will come up asking how you want to decompile. Just leave all options as is, and hit return. Nosy will go through what it terms a TreeWalk which means that it will recursively analyze the program and generate it's decompiled code. Working with Nosy. Since I don't want to re-type the entire Nosy manual, I am going to list just the basics. There are some great features that I never use and don't even know how to initiate without referring to the manual and these will be omitted. Everything I use to crack software will be covered in detail. At this point, Nosy will present you with several windows: a Code Blks window, listing all procedures in the decompiled file; a Notes window which Nosy will use to display information; and a Mystery window, listing things that Nosy had trouble with during decompilation. Nosy can also display a list of all Data Blocks which are chunks of code that either did not make sense as executable code, or were referenced as data (Nosy looks for PEA and LEA to determine this and looks for JMP, JSR, and BSR to find individual procedures). Notes cannot be closed, so ignore it, and Mystery has things that - to date at least - don't matter that much to the cracker. When working with Nosy, you can at any time select the name of a code or data block and hit CMD-d to display it in a new window. Before examining the menu commands, lets look at a basic Nosy listing and see what Nosy tells us. This is the procedure called Eject from the file Font/DA Mover. This is the file I will describe in detail later. BD4: QUAL Eject ; b# =79 s#1 =proc47 vbt_1 VEQU -64 param2 VEQU 8 param1 VEQU 10 funRslt VEQU 14 BD4: VEND ;-refs - 2/CLOSEMYF BD4: 4E56 FFC0 'NV..' Eject LINK A6,#-$40 BD8: 41EE FFC0 200FFC0 LEA vbt_1(A6),A0 BDC: 316E 0008 0016 2000008 MOVE param2(A6),ioVRefNum(A0) BE2: 216E 000A 0012 200000A MOVE.L param1(A6),ioNamePtr(A0) BE8: A017 '..' _Eject ; (A0|IOPB:ParamBlockRec):D0\OSErr BEA: 3D40 000E 200000E MOVE D0,funRslt(A6) BEE: 4E5E 'N^' UNLK A6 BF0: 225F '"_' POP.L A1 BF2: 5C8F '\.' ADDQ.L #6,A7 BF4: 4ED1 'N.' JMP (A1) OK, The first column contains the code resource-relative address of the instructions. To the right of this is the hex listing of the instruction, followed by an ascii display, followed by the actual assembly instruction. The first line tells you the following: The name of the procedure (either a meaningful name Nosy found somewhere, or a generic procN where N tells where the procedure falls sequentially in the file), the block number (similar to proc number except this takes into account data blocks as well as procedure blocks), the segment or CODE resource ID #, and the actual procedure number. So in the above example, we are looking at Eject, it is the 79th block in the file (counting data blocks), it is in code resource ID 01, and it is the 47th procedure block in the file. So we could open CODE 01 in Resedit, skip down to BD8 and see the hex codes that Nosy lists. Whe BD8 and not BD4 like it says above? Well, on disk, a CODE resource has 4 header bytes (whose meaning escapes me at the moment) so we have to add 4 to the Nosy address to find the correct Resedit address. Below this information will be listed any local variables used (they will always contain an underscore) along with their relative offsets from the procedure, then any parameters passed along with their offsets. If there is a result that will be passed back to the calling procedure, it will be listed as funRslt (as it is here). Don't worry about all the offset information as Nosy will refer to parms and local variables by their symbolic names. VEND denotes the end of the variable list. Next comes any references to this procedure - any procedures that call this procedure. Finally comes the actual listing. Occasionally, Nosy will stick more than one procedure in a window. If this happens, each procedure will have the above header. Nosy also might include data blocks in a procedure's window and it will have the word dataXX to the left of it. Menu Commands  This is pretty straight forward and should require no explanation. Save As... will allow you to save as text a procedure or data window. This way you can type in your own comments and save them. I never use this feature, however. TTY mode allows some of Nosy's extra features. In previous version, TTY was the only mode and I imagine was hell to use. With the newer version, 2.0 or higher I believe, you can stay in the window mode that you are currently in and never use TTY mode.  OK, the first two sections are standard. Find will find the next occurence of whatever you have selected. For example, you can select a local variable name and hit cmd-f to find the next time it occurs in the current window. If nothing is selected, Nosy presents a dialog allowing you to enter a search string. Change brings up a standard search/replace dialog - similar to Word. Goto Line allows the user to goto a specified line number in the front window. Grab Clip & Find operates like Find except that the clipboard is used as the search string. Show Insert pt scrolls the window to display the cursor (if you had scrolled the cursor off the screen). insert pt to Top places the cursor at the top of the screen (line 1). sel to Notes copies the current selection into the Notes window.  Code|Data blk displays the currently selected code or data block in a new window. For example if you select a procedure from the code blocks window and hit cmd-d, the procedure will be displayed in a new window. If there is no current selection then Nosy will request a proc name via a dialog. Refs to Active only if a procedure name is selected. Displays all procedures that call the selected procedure. Using this, you can see any part of the program that is calling a particular procedure. Call chain Similar to Refs to. Any procedure that calls the selected procedure is also treated with Refs to. For example, you select a procedure called proc5. Doing a Refs to shows all procs that call proc5 - let us say for example, pro10 and proc 15. If you had selected proc5 and done a Call chain instead of Refs to, then first proc10 would be displayed along with any proc that called and so on backwards until the chain ends. Then proc15 would be listed along with any procs that call it, and so on until the chain ends. This is an excellent way of tracing a procedure that draws an error dialog back to the procedure that actually generated the error. Sys syms map Displays all system global variables along with any procedures that reference them. An example might be the system global MemErr which contains any OS errors. Nosy would display any procedures that reference MemErr - note that this command displays ALL system globals and their referencing procedures. Trap refs map This is a beauty. This will list all traps called by the program and the procedures that call them. If a program is asking for a key disk, use this command to search for procedures that call either ModalDialog or one of the Alert traps. Globals map Displays all program global variables and the procedures that use them. Rsrc map Lists all program resources, their lengths, and names if any. Strings Lists all strings and the procedures that reference them. I am not sure what Nosy defines as a string, but try it on Font/DA Mover to see. Data Blks Displays a window listing all data blocks. Case jumps Displays any procs containing a structure that resembles a case statement. Mystery procs Opens the Mystery Procs window showing any procedures that Nosy was unsure how to handle. ROM Patch Info Unknown. My outdated docs don't even mention this command. Bad Blks Displays information about any blocks that Nosy thought were code but contained illegal instructions so Nosy converted them to data blocks. Encrypted code would fit into this category. Blk tbl Displays the following for all blocks: name, segment number (resource ID #), start address, and length. Code Blks Displays the Code Blks window listing all code blocks.  Review... Lets you review data blocks, optionally converting them to code blocks. More on this later. Link Jmp to Tbl Defines the link between a mystery jump and and a data block. To use it, select the address of a mystery JMP and choose the command. More on Jump Tables later. Code to Data Converts a selected code block to a data block. The blocks name won't change until the next Explore (see below). Is Proc Converts a selected data block to a code block. The block's name won't change until the next Explore. JSR is JMP Tells Nosy that a JSR is really a JMP. Sometimes a JSR is followed by data - which will look like jiberish code. The called procedure then pops the return address off the stack and uses that address as a pointer to a data block with no intention of returning to the calling procedure. To use this command, select the destination of the JSR (e.g. for JSR proc100, select proc100) and choose this command. Explore Initiates a TreeWalk. This allows Nosy to re-examine the program using any changes you might have made (i.e. converting data blocks to code blocks, etc).  Extract Comments Extracts comments from the selection (usually an entire procedure window) into a window entitled Comments. See section on commenting below. Append to .aci Appends selected comments (created using the above) to an aci (additional comment information) file. Later, this file can be re-merged into the dissassembly. name cHange Changes the selected name to whatever you type in. Use this to rename procedures from the generic procN to a name that tells you what the procedure basically does. The change will be immediate and global. Addr to File pos Converts a selected address (the leftmost information for a procedure display) into file-relative information, displaying a file-relative offset (in hex), block or sector number, and the block offset to the start of the address. This might be handy if you were using a file editor (instead of Resedit which allows you to simply open the proper code resource) to edit the file. Convert to .asm fmt Converts the current procedure window to asm format by removing the addresses and hex and ascii data. Cannot be undone - you must close the window, not save changes, and re-open it. save .snt, reRead .aci Saves a .snt file for the dissassembled program and re-reads the comment file if it exists. .snt files (saved Nosy tables) are a means of saving your dissassembly mid-session. All changes are remembered so when you re-open the file later, you begin right where you left off instead of doing a TreeWalk all over again, etc. Journal commands A checkmark next to this indicates that all your commands are being saved to a text file. The file will not actually be saved unless you specify so when you Quit - see quitting later. Proc rel addresses When checked displays the address of each instruction as procedure relative (starting at zero for each procedure instead of starting at zero for each code resource). format Maps by Addr When checked, Nosy will change the way it displays its various maps, i.e. Sys syms, Trap refs, etc. under the Display menu. Instead of proc names, Nosy will display the segment number, and the segment offset. But - if Proc rel addresses is also checked, then Nosy will replace the proc names with a proc name followed by a + followed by the procedure relative offset. Try it out if this doesn't make sense. cmds to Notes Wind Not yet implementated - like a lot of great features (see below). Set Source Path Not in my manual - you're on your own. Extract Map Names Not in my manual - on you're own again. This doesn't seem to do anything, though, when I try it. Unfortunately, the Search_Rsrc menu is totally disabled. Maybe the next version...  Record/ALL names Lists all Macintosh data structures that Nosy currently knows. Fields of Depends on the selection: 1) a datatype (e.g. Dialog Record - or anything listed by the previous command) - describes all fields for the datatype. 2) datatype@address - displays the current values for the datatype if one exists at the specified address. 3) @address - displays hex/ascii dump at specified address. OS Traps Lists all known Operating System Traps and their parameters. TB Traps Lists all known ToolBox traps and their parameters. Sys Syms Lists all known System Symbols. Sys Errs Lists all known System Errors and their codes. Constants Lists all known Macintosh Constants. Note that my copy of Nosy contains an error in this window - it specifies that constants in brackets can be selected and viewed by pressing cmd-?. Use cmd- (or select Fields of from the Tables menu). Ascii Decimal/Hex/Ascii lookup table. Calculate Evaluates selected expressions which can contain mathematical operators, system globals, program globals, IM datatypes, registers etc. Use # to force decimal (#10) and $ for hex ($10). Convert Hex to Dec Converts a selected number to decimal, ascii, and system symbol equivalent if there is one. add/ZAP Type /Defs No idea. Note on commands requiring a selection (Calculate, etc.) You may have noticed that often there is no place to enter the text you want to select. In cases like these, type your text into the Notes window, select it, and choose the command you wish. Reviewing Data Blocks This is the process by which you tell Nosy that it has mistakenly made a piece of code a data block. Once you initiate the Review... command, Nosy will show you each data block in sequence and give you a chance to work on it. Note that you may never need to do this to crack a program - god knows I never use it unless I am really having problems. This section is to provide you with some idea of what Nosy can do. Here is a typical display after selecting Review... from the Reformat menu. The Data Blk window displays the data block, the window directly underneath this will display the section of code that references that data block (if their is one) and the Cmd window awaits your input. There are about a zillion things you can do, but the most important one is the c command.  If your press c and return, Nosy shows you what the data block looks like in assembly:  Notice that this looks like pretty good code! Also note that Nosy has placed i in the Cmd window anticipating that you will want to change this to code. Here are all the commands available: H takes 2 parameters: 1) either L,W, or B for Longword, Word, or Byte and 2) the number of entries per line. Formats the block as Hex bytes. Example: HL2 would format the block as hex longwords, 2 per line. D same as H (above) but formats block as decimal entries. A same as H (above) but formats the block as ascii entries. Z same as H (above) but formats the block as zero entries. W Formats the block as a word-aligned Pascal string. S Formats the block as a Pascal string. WZSTR Formats the block as a word-aligned zero-terminated string. Z Formats the block as a zero-terminated string. J Formats the block as a set of Jump Table entries - each word is taken as an offset from the beginning of the data block to a common procedure and these jumped to spots are marked as common blocks (a common block - denoted com_nn - is any procedure executed via JMP instead of JSR or BSR). If you do this, you need to use the Link Jmp to Tbl command to link the jump table to its jump command. See Jump Tables below. JUMPP Same as J except that the entry points are marked as procedures instead of common blocks. BRAL Formats the block as code. Any instructions that are BRAnched to are marked with local markers (as in a standard Nosy listing). BRAC Same as above except that instructions reached via BRA are marked as common blocks. U Undoes any formatting changes. C Changes the block to a code listing and brings up the code menu - discussed below. Q Exits Review mode. N takes an integer parameter X. Splits the block into two blocks, the first block getting X words (remember a word is two bytes). NB same as above except the parameter specifies bytes instead of words. NL same as above except the parameter is a segment-relative address specifying the end of the the first block. NU takes an optional search string as parameter. Splits the block with the first block ending upon finding the search string. If no string is supplied, Nosy searches for a logical procedure end (RTS, JMP(AX) ). The block is formatted as code and the code menu is displayed. NW Splits the block in two, the first block being made a word-aligned Pascal string. N* Splits the block in two. Uses the first longword to determine the length of the first block. O If the previous block is a data block, then combine it with the current one. ADDR Formats the block as a list of word-length procedure block addresses. LADRC Formats the block as a list of longword-length common block addresses. LADRP Formats the block as a list of longword-length procedure block addresses. Saves changes, and takes you to the next block. The Code Menu Commands: these come up if you use c or nu to change the block to a code listing. U Undo any changes to size or format and takes you back to the Review menu. I Tells Nosy to keep the block as code and return to the Review menu. Q Exits Review mode. R Changes block back to data, but retains any size changes you may have made. N Same as the N commands above. Same as above - returns to the Review menu. Once you have finished Reviewing data blocks, you must select Explore from the Reformat menu to have Nosy incorporate any changes into its lists. Working with Jump Tables A jump table is a means of efficiently transferring control to a procedure. An example of a jump table would be a program that receives an event (as most mac programs do) and then has to execute a procedure depending on what the event was. Font/DA Mover has an extermely simple jump table - actually it is not a true jump table - in which the button the user clicks is returned to its main event loop as an integer. The program then repeatedly subtracts one from the integer and branches to an appropriate procedure when the integer has been reduced to zero. A more common (and true) jump table consists of a list of offsets. The program then takes an integer which tells it which entry in the table to use, multiplies it by 2 (assuming each entry is two bytes in length) and then indirectly jumps to the correct procedure. Here is an example taken from the Nosy manual (this is from the System File's .MPP driver): LEA data4,A3 ADDA D3,A3 ADDA D3,A3 MOVEA (A3),A3 PEA .MPP ADDA.L (A7)+,A3 JMP (A3) data4 DC.W $82,$280,$26C,$3C, etc As gross as this looks, lets see what it is doing. At the start, D3 contains the selector that determines which entry in the table to use. A3 is loaded with the address of the jump table. D3 is added to it twice (we could have doubled D3, then added it) so that now A3 contains the address of the proper jump table entry. The instruction MOVEA (A3),A3 grabs the jump table entry (which is simply an offset from the start of the program to the correct procedure) and puts that entry back into A3. Next the address of the program start (.MPP) is pushed on the stack, and this value is added to A3 to produce the actual address of the procedure (the address is the start of the program plus the offset). Now A3 is setup, so the program Jumps to the address in A3. If you don't mind looking at this type of listing (and I don't since it probably is not the copy protection - although it might be jumping to the copy protection) then you need go no farther. But Nosy can set this up to look much nicer. To fix this up, select the address (the far left column) of the Jump instruction - in this case, the JMP (A3). Now choose Link Jmp to Tbl from the Reformat menu and a dialog box appears requesting the name of the jump table's block - in our case that would be data4. Click continue and a new dialog appears. The first thing we need is the table format. There are three choices: JUMPP - tells nosy to label the jumped to procedures as procedure blocks; JUMPC - tells Nosy to label the jumped to procedures as common blocs; JUMPL - tells Nosy to label the jumped to procedures with local labels in the same block as the jump table. To figure out which one to use (and it is really a matter of preference), decide if you want to break the whole thing up into many procedures, or keep it as one large procedure with tons of local labels. If the procedure is a massive one, you may want to break it up (and I would recommend JUMPP - but then, I like proc labels better than com labels), otherwise, use JUMPL. Next we need the number of jumps. Just count the number of entries - but be careful: you need to decide the size of each entry in the table. Note the DC.W next to data4. This means that Nosy is showing you individual words so you can just count the number of entries. But if Nosy is using DC.B, then it is showing you bytes, so you would have half as many word length entries. Finally, we need the Table Bias. Bias is a parameter that Nosy uses to determine the actual procedure address of a non-standard jump table. To calculate this, use this formula: Bias = Address of JumpTable + Offset - TargetAddress. The tricky thing is to deterine the TargetAddress. In the above example, it is easy, since the code clearly refers to the start of itself (it refers to the address of .MPP). JumpTable is the address (leftmost column in the listing) of the start of the jump table, and Offset is the first word-length offset in the table. Note that your calculations will result in a hex bias - Nosy needs you to change it to decimal. Click Accept, do another Explore, and that is it! Now the listing looks like: JMP (A3) JBIAS 92 data4 JUMP procA JUMP procB JUMP procC etc. Notice that Nosy uses JUMP to distinguish it from the instruction JMP. Once this is set up, it is a cinch to see where the jump table is jumping - provided you can deduce the selector. Most of the time Nosy works wonders with jump tables, and the few times it has problems (it will list these problems in the Mystery window) I have found it not worth the work to convert them to the above format. Commenting Your Listings There are a couple of cool features that I am not going to explain regarding commenting simply because I have never used them and the manual I have isn't the most verbose. Basically, you can put comments on any line that Nosy hasn't already commented. All comments must start with a semi-colon. Once you have all the comments you want, do a cmd-a to select all, and choose Extract Comments from the Misc menu. Nosy will extract all your comments into a comments window. Now hit cmd-a again, and choose Append to .aci from the Misc menu. This will save your comments. Now close the procedure window and don't save changes. Select Save .snt, reRead .aci from the Misc menu. Nosy may ask you if you want to delete something or other which it claims saves space in the Debugger. Since we are not using the Debugger, choose No. Now when you open the procedure again, you comments appear. There is one feature I will attempt to explain, because it could be a serious boon. There a several slash (/) commands Nosy understands. One in particular, /w, works like this: Anytime a register is setup to contain a pointer to a Mac structure, you can have Nosy automatically show the structure whenever the register is referenced. Here is an example: PEA data24 ; len = 206 _OpenPort ; (port:GrafPtr) LEA data24,A2 ; len = 206 MOVE #4,68(A2) MOVE #9,74(A2) Note in the 3rd line that A2 is given the GrafPtr. Since GrafPtr is a pointer to a valid Mac structure (GrafPort), we could use the /w command as follows: click at the end of the 3rd line and hit return (so we are not commenting on a line that Nosy has already commented). Enter /wGrafPort. Now save the comments as illustrated above. When the proc is re-opened it shows the following: PEA data24 ; len = 206 _OpenPort ; (port:GrafPtr) LEA data24,A2 /w GrafPort MOVE #4,txFont(A2) MOVE #9,txSize(A2) Using this technique, Nosy will use the fields of the structure instead of the actual offsets from the register. This will work for any valid Mac structure. I haven't used this feature (I just noticed it when compiling this manual) so that is all I will say - feel free to experiment. Well, I guess the next thing to do is start looking at some serius code listings pulled directly from Nosy (and not stuff I made up on the fly). Nosy has shorthand notations for certain operations, most commonly for stack operations. The two to watch out for are PUSH and POP (btw, TMON does not use this notation, but rather uses the standard notation found in any assembly book). PUSH is the equivalent of putting the operand onto the stack using auto pre-decrement. POP is the same as grabbing the operand off the stack using auto post-increment. Let's take a look at some code listings from Font/DA Mover 3.8. I selected this program so that you can pull up the same listings that I will refer to in Nosy. First take a look at the initial procedure: DA Mover. There will always be a procedure whose name is the same as the program you are de-compiling. This procedure (DA Mover in this case) is the first procedure the program executes when launched. 430: QUAL DA Mover ; b# =12 s#1 =proc4 OK, I will stick my notes right in the listing (below or to the right of what I am refering to), so bear with me. Note the first line (above). We are looking at block 12, segment (or CODE resource #) 1, and it is the 4th procedure in the program. The first few lines will do some startup stuff that I do not fully understand and so I will skip it. 430: 4EBA 0C48 100107A DA Mover JSR proc70 Proc 70 is just an RTS 434: 4E56 0000 'NV..' LINK A6,#0 438: 2C5F ',_' POP.L A6 43A: 4EBA 0C40 100107C JSR proc71 Proc 71 calls _RTINIT which seems to be a common step for MPW compiled programs. It then initializes some global variables. Let's skip all this. 43E: 486D 024A 3000000 PEA proc232(A5) 442: A9F1 '..' _UnLoadSeg ; (Proc:ProcPtr) Here we are dumping an un-needed procedure. Now, looking at the listing from here, notice the procedures that get called - two without labels, then SetUp, MakeAWin and FinderSE and finally MainEven which sounds suspicially like an event loop. Let's check out these procedures. 444: 4EBA 05E2 1000A28 JSR proc28 If you look at this procedure, you see several references to memory stuff like ApplHeap, ApplZone, Rom85, etc. Looks like this is checking for enough memory or something. Not too interesting. 448: 4EBA 021E 1000668 JSR proc10 This proc looks to be changing a few traps. Note that it allocates a new pointer (NewPtr trap) and then calls GetTrapAddress, and then SetTrapAddress. 44C: 4EAD 01F2 2005092 JSR SETUP(A5) Take a look at this listing below the current one: 450: 4EAD 01FA 2005852 JSR MAKEAWIN(A5) Draw the main dialog. 454: 4EBA FCC2 1000118 JSR FINDERSE Checks if user launched a suitcase and if so, opens it. 458: 4AAD F4F0 -$B10 TST.L glob26(A5) Verify the main memory handle. 45C: 6706 1000464 BEQ.S lae_1 Branch if it is empty. 45E: 4EBA FBA0 1000000 JSR MAINEVEN Do the actual program until user Quits. 462: 6008 100046C BRA.S lae_2 Exit without error. 464: 3F3C 0031 '?<.1' lae_1 PUSH #49 Out of Memory Error. 468: 4EAD 01CA 200023C JSR DOALERT(A5) Do an Out of Memory alert 46C: 4EBA FF86 10003F4 lae_2 JSR DOCLEANU From here, the program exits. 470: 4EAD 01D2 20002B2 JSR MYEXITTO(A5) 474: 4EBA 0C2A 10010A0 JSR %INITHEA 478: 4EBA 0C2C 10010A6 JSR proc73 47C: 4E75 'Nu' RTS 47E: 4E5E 4E75 C64F 4E54 data9 DNAME FONT_DA_,4 48A: '..' data10 DC.W 0 Here is the SetUp Procedure: 5092: QUAL SETUP ; b# =467 s#2 =proc199 vho_1 VEQU -12 Only one local variable, no parameters. 5092: VEND ;-refs - 1/DA Mover Only called via DA Mover. 5092: 4E56 FEE6 'NV..' SETUP LINK A6,#-$11A Setup a Stack frame for local variables 5096: 48E7 0118 'H...' MOVEM.L D7/A3-A4,-(A7) Save D7,A3 and A4 on stack 509A: 7E01 '~.' MOVEQ #1,D7 Loop coming up, this inits the loop counter. 509C: 6006 20050A4 BRA.S lho_2 And branch into the loop. 509E: 4EAD 00D2 1000AB0 lho_1 JSR MoreMasters(A5) 50A2: 5247 'RG' ADDQ #1,D7 Increment loop counter. 50A4: 700F 'p.' lho_2 MOVEQ #15,D0 50A6: B047 '.G' CMP.W D7,D0 Test if loop done... 50A8: 6CF4 200509E BGE lho_1 If not, branch back. OK, take a look at the above code. First, D7 is initialized to 1 and then the program branches down to lho_2. The loop test is setup here (15 is the end of the loop). At the compare, ask yourself, is D0 greater than or equal to D7? Well, the first time, D0 is 15 and D7 is 1 so the loop branch will execute. So, MoreMasters is called, 1 is added to the loop counter, and then the loop is checked again. This will loop 15 times (until D7 has 16 in it). MoreMasters is a trap (in this case, the procedure called MoreMasters will execute the trap) that causes a block of master pointers to be allocated in the current heap zone. See Inside Mac's (here on referred to as IM) Memory Manager section for a better description. 50AA: 486D F420 -$BE0 PEA glob3(A5) Push the address of glob3 on the stack. 50AE: A86E '.n' _InitGraf ; (globalPtr:Ptr) InitGraf, we see in IM, that InitGraf must be called once near the start of a program. It requires one parameter, a pointer to the first QD global variable. This parameter is first pushed on the stack in the previous instruction. 50B0: A8FE '..' _InitFonts These traps can all be found in IM. 50B2: A912 '..' _InitWindows 50B4: 2F3C 0000 FFFF '/<....' PUSH.L #$FFFF 50BA: 201F ' .' POP.L D0 50BC: A032 '.2' _FlushEvents ; (whichMask,stopMask:EventMask) 50BE: A9CC '..' _TeInit 50C0: 42A7 'B.' CLR.L -(A7) Note that InitDialogs needs a ProcPtr (a long word). The clr command here uses auto pre-decrement to push a NIL pointer onto the stack. 50C2: A97B '.{' _InitDialogs ; (resumeProc:ProcPtr) 50C4: A930 '.0' _InitMenus 50C6: 486E FFF4 200FFF4 PEA vho_1(A6) OK, notice the VAR in the trap below. This means that info will be returned via the parameter we push on the stack. So, after the trap, vho_1 will be a GrafPtr (whereas before the trap, god knows what is in it). 50CA: A910 '..' _GetWMgrPort ; (VAR wPort:GrafPtr) 50CC: 2F2E FFF4 200FFF4 PUSH.L vho_1(A6) Now, our GrafPtr is used to set the current Port. 50D0: A873 '.s' _SetPort ; (port:GrafPtr) 50D2: 206E FFF4 200FFF4 MOVEA.L vho_1(A6),A0 Now A0 contains our GrafPtr. 50D6: 4868 0008 'Hh..' PEA 8(A0) This instruction says to add 8 to A0,and push that address on the stack. 50DA: A87B '.{' _ClipRect ; (r:Rect) 50DC: 2F3C 000E 000C '/<....' PUSH.L #$E000C The MoveTo trap requires two integers to be passes, but only one value is being pushed on the stack. Since the instruction says to push long, 4 bytes are being put on the stack, and an integer is only two bytes. Even though one instruction is being used, there are actually two parameters being passed to the MoveTo trap. 50E2: A893 '..' _MoveTo ; (h,v:INTEGER) 50E4: 3F3C 0029 '?<.)' PUSH #41 50E8: 4EBA CE84 2001F6E JSR DRAWRESS It turns out that DRAWRESS will draw the 41st string in the STR# resource. If you look in Resedit, you will see that this is "3.8" the version number. 50EC: 3F3C 000C '?<..' PUSH #12 Note the lack of a size specifier. Remember that this means use the word (two bytes) size. Textsize needs an integer and IM tells us that an integer is two bytes - or one word. 50F0: A88A '..' _TextSize ; (size:INTEGER) This is pretty easy - sets the fontsize to 12 point. 50F2: 422D F4EF -$B11 CLR.B glob25(A5) Here is the .B size specifier, meaning clear only the low byte of glob25. 50F6: 42A7 'B.' CLR.L -(A7) 50F8: 3F3C 0004 '?<..' PUSH #4 50FC: A9B9 '..' _GetCursor ; (cursorID:INTEGER):CursHandle OK, this is a slightly different trap, since it returns something on the stack - as evidenced by the colon and description at the end of the trap parameter list (:CursHandle). Since this trap returns a value on the stack (and not with a passed pointer as with the GWMgrPort above), the program will first clear enough stack space to hold that value. Thus the CLR.L -(A7). The trap returns a handle which is 32 bits or a long word. The trap needs an integer, so the program pushes the word 4 onto the stack. Next, the program will pop the CursHandle returned by the trap off the stack into the variable glob24. 50FE: 2B5F F4EA -$B16 POP.L glob24(A5) This the CursorHandle. 5102: 1F3C 0002 '.<..' PUSH.B #2 5106: 4EBA AEF8 2000000 JSR SETTHECU This subroutine is setting the cursor. If you look at it, you will see that it looks at the parameter passed (2 in this case) as well as glob25 (0 in this case). When called from here, it will pass down to the 2nd SetCursor and use the CursorHandle in glob24. 510A: 42A7 'B.' CLR.L -(A7) Once again, clear space on the stack for a returned handle. 510C: 2F3A 0144 2005252 PUSH.L data260 ; 'PACK' 5110: 3F3C 0003 '?<..' PUSH #3 GetResource needs the resource type and the ID# to load. 5114: A9A0 '..' _GetResource ; (theType:ResType; ID:INTEGER):Handle 5116: 285F '(_' POP.L A4 Pop the handle (to the PACK resource) into A4. 5118: 2F0C '/.' PUSH.L A4 And push it back on the stack so HNoPurge can use it. 511A: 4EAD 00CA 1000AA6 JSR HNoPurge(A5) Once again we see a subroutine with the same name as a trap. You can bet that the trap will be called somewhere in the subroutine. 511E: 42A7 'B.' CLR.L -(A7) 5120: 2F3A 0130 2005252 PUSH.L data260 ; 'PACK' 5124: 3F3C 0006 '?<..' PUSH #6 5128: A9A0 '..' _GetResource ; (theType:ResType; ID:INTEGER):Handle 512A: 285F '(_' POP.L A4 512C: 2F0C '/.' PUSH.L A4 512E: 4EAD 00CA 1000AA6 JSR HNoPurge(A5) OK, the previous several lines have basically loaded two resources, PACK #3, and PACK #6. The handles to the two resources have been made non-purgeable meaning that the memory manager will not remove them to create free space. 5132: 42A7 'B.' CLR.L -(A7) 5134: 3F3C 0001 '?<..' PUSH #1 5138: 4EAD 0182 1000D8C JSR proc61(A5) This little gem invokes Pack6. My understanding of the package manager is less than it should be, but it looks to me like this says do a Pack6 with a selector of 1. Hell, lets just look at proc 61... D8C: 7406 't.' proc61 MOVEQ #6,D2 OK, here is the selector (and not the 1 passed from the above procedure). So we are going to be calling the IUGetIntl procedure (I think) with a parameter of 1 (passed from the calling procedure. Look in IM for details of this trap and its parameters. D8E: 205F ' _' POP.L A0 This pops the parameter passed, D90: 3F02 '?.' PUSH D2 so that the selector parameter can be put ahead of it on the stack. D92: 2F08 '/.' PUSH.L A0 Now the 2nd parm can be put back on the stack and the trap called. D94: ADED '..' _Pack6 AutoPop; (selector:INTEGER) 513C: 285F '(_' POP.L A4 proc 61 is returning a handle to the intl resource that it loaded, so save it in A4. 513E: 2F0C '/.' PUSH.L A4 5140: 4EAD 00CA 1000AA6 JSR HNoPurge(A5) 5144: 42A7 'B.' CLR.L -(A7) 5146: 2F3A 010A 2005252 PUSH.L data260 ; 'PACK' 514A: 3F3C 0007 '?<..' PUSH #7 514E: A9A0 '..' _GetResource ; (theType:ResType; ID:INTEGER):Handle 5150: 285F '(_' POP.L A4 A4 now has a handle to Pack #7. 5152: 2F0C '/.' PUSH.L A4 5154: 4EAD 00CA 1000AA6 JSR HNoPurge(A5) 5158: 4EAD 0172 1000D7C JSR proc59(A5) This proc calles Pack2 with a selector of 2. This reads the Disk Initialization package into memory. 515C: 42A7 'B.' CLR.L -(A7) Clear space on stack for a returned handle. 515E: 2F3A 00EE 200524E PUSH.L data259 ; 'ICON' 5162: 4267 'Bg' CLR -(A7) Push the integer 0. 5164: A9A0 '..' _GetResource ; (theType:ResType; ID:INTEGER):Handle 5166: 285F '(_' POP.L A4 A4 has a handle to Icon resource ID 0. 5168: 42A7 'B.' CLR.L -(A7) 516A: 2F3A 00E2 200524E PUSH.L data259 ; 'ICON' 516E: 3F3C 0001 '?<..' PUSH #1 5172: A9A0 '..' _GetResource ; (theType:ResType; ID:INTEGER):Handle 5174: 285F '(_' POP.L A4 A4 has a handle to Icon resource ID 1. 5176: 4267 'Bg' CLR -(A7) Make space for the returned RefNum. 5178: A994 '..' _CurResFile ; :RefNum Note - no parameters passed. 517A: 3B5F FFE0 -$20 POP glob58(A5) Pop off the returned RefNum. 517E: 486D FEDE -$122 PEA glob56(A5) 5182: 3F3C 000D '?<..' PUSH #13 5186: 4EAD 002A 100048C JSR proc5(A5) Here is proc5 again - the string getter. If you remember (from looking at DRAWRESS), the 1st parm is the string ptr, and the 2nd is the string # to get. This is returning a ptr to the string "The quick brown fox..." in glob56. 518A: 7000 'p.' MOVEQ #0,D0 518C: 2B40 FED4 -$12C MOVE.L D0,glob52(A5) 5190: 7000 'p.' MOVEQ #0,D0 5192: 2B40 FECC -$134 MOVE.L D0,glob50(A5) 5196: 7000 'p.' MOVEQ #0,D0 5198: 2B40 F61E -$9E2 MOVE.L D0,glob41(A5) 519C: 3B7C FFFF F616 -$9EA MOVE #$FFFF,glob38(A5) 51A2: 426D F614 -$9EC CLR glob37(A5) 51A6: 7000 'p.' MOVEQ #0,D0 51A8: 2B40 F610 -$9F0 MOVE.L D0,glob36(A5) 51AC: 7000 'p.' MOVEQ #0,D0 51AE: 2B40 F61A -$9E6 MOVE.L D0,glob40(A5) 51B2: 7034 'p4' MOVEQ #52,D0 51B4: 2B40 F5FE -$A02 MOVE.L D0,glob31(A5) The above instructions have simply initialized several global variables. We don't care what they mean at this point. If you like, you can write down what has been set to what, but I would only recommend this if later on you need to know explicitly what a global contains. 51B8: 42A7 'B.' CLR.L -(A7) 51BA: 7002 'p.' MOVEQ #2,D0 Note the MoveQ. Remember, this is the same as MOVE.L (except it executes faster). 51BC: 2F00 '/.' PUSH.L D0 51BE: 4EAD 009A 1000A5C JSR NewHandle(A5) NewHandle is a trap that returns a handle to a block of memory whose size is in D0. It makes sense to guess that this procedure will do essentially the same thing - and after checking, it certainly does. 51C2: 2B5F F622 -$9DE POP.L glob42(A5) So glob42 has a handle to a 2 byte chunk of memory. 51C6: 426D F626 -$9DA CLR glob43(A5) 51CA: 70FF 'p.' MOVEQ #-1,D0 Here is one of those cases where the sign bit is important. Remember that the -1 is sign extended to 32 bits so D0 is being set to all binary ones (-1 in binary). 51CC: 2B40 F602 -$9FE MOVE.L D0,glob32(A5) 51D0: 42A7 'B.' CLR.L -(A7) 51D2: 2EB8 02F0 $2F0 MOVE.L DoubleTime,(A7) 51D6: 7002 'p.' MOVEQ #2,D0 51D8: 2F00 '/.' PUSH.L D0 51DA: 4EAD 01A2 1001120 JSR proc76(A5) This is a gross looking (i.e. no Traps anywhere) procedure so I am not going to attempt to figure it out. You will want to use the technique a lot (the "Too Gross" technique) to determine which procedures to spend time with. 51DE: 2B5F F5F6 -$A0A POP.L glob29(A5) 51E2: 207C 0000 0AD8 $AD8 MOVEA.L #SysResName,A0 Put a pointer to the System File's name in A0. 51E8: 43ED F4F6 -$B0A LEA glob28(A5),A1 Put the address of glob28 in A1. 51EC: 703F 'p?' MOVEQ #63,D0 Set up D0 as a loop counter. 51EE: 22D8 '".' lho_3 MOVE.L (A0)+,(A1)+ This moves 4 bytes from A0 to A1. Note the use of auto post increment to automatically move the pointers to the next available data each time. This moves 4 bytes of the System name into glob28. Note that glob28 will not be a pointer to the Sys Name, but will rather contain the actual string data. 51F0: 51C8 FFFC 20051EE DBRA D0,lho_3 This decrements D0 (the loop counter) and branches back to the start of the loop until it is finished. 51F4: 422D F4F5 -$B0B CLR.B glob27(A5) 51F8: 267C 0000 028E $28E MOVEA.L #Rom85,A3 ROM85 is another of those variables that my old IMs are missing so god only knows what is going on here. I'll guess that it is looking for the 128K roms. 51FE: 4A53 'JS' TST (A3) 5200: 6D20 2005222 BLT.S lho_4 5202: 42A7 'B.' CLR.L -(A7) 5204: 3F3C 008F '?<..' PUSH #143 5208: 4EAD 00E2 1000AC6 JSR proc38(A5) Well, let's see here. Proc38 uses the passed parm as a trap number and returns that traps address on the stack. 520C: 42A7 'B.' CLR.L -(A7) Note that the trap address has not been popped off the stack. So when these next instructions are done, that address will still be on the stack. 520E: 3F3C 009F '?<..' PUSH #159 5212: 4EAD 00E2 1000AC6 JSR proc38(A5) Get another trap address on the stack, 5216: 201F ' .' POP.L D0 and put it in D0, leaving the first trap address on the stack. 5218: B09F '..' CMP.L (A7)+,D0 Now, compare the two trap addresses, 521A: 56C0 'V.' SNE D0 and set the low byte of D0 to FF hex if they are not the same. 521C: 4400 'D.' NEG.B D0 Do 2's complement - make the low byte of D0 its own negative. Since D0's byte is either 0 or FF (from the SNE), the NEG will make it either 0 (if it was 0) or 1 (if it was FF) - (for NEG, invert the bits, then add a binary 1). 521E: 1B40 F4F5 -$B0B MOVE.B D0,glob27(A5) And save this number. 5222: 42A7 'B.' lho_4 CLR.L -(A7) 5224: 2F3C 0001 0000 '/<....' PUSH.L #$10000 522A: 4EAD 009A 1000A5C JSR NewHandle(A5) Get a new Handle for a block of size 10000 hex. 522E: 2B5F F4F0 -$B10 POP.L glob26(A5) And save the handle. 5232: 6708 200523C BEQ.S lho_5 Branch if a NIL pointer (meaning the memory was not available) is popped off the stack. 5234: 487A FE26 200505C PEA MYGROWZO Otherwise setup a grow zone function. 5238: 4EAD 0092 1000A1E JSR SetGrowZone(A5) A grow zone procedure is a custom method for handling low memory conditions and overrides the memory managers routines. Not a great description, but we don't really care about this. 523C: 4CDF 1880 'L...' lho_5 MOVEM.L (A7)+,D7/A3-A4 Restore those saved regs, 5240: 4E5E 'N^' UNLK A6 Kill the stack frame, 5242: 4E75 'Nu' RTS And return to the caling proc. 5244: D345 5455 5020 2020 data257 DNAME SETUP ,0 524C: '..' data258 DC.W 8 ;-refs - 2/SETUP 524E: 4943 data259 DC.B 'ICON' ;-refs - 2/SETUP 5252: 5041 data260 DC.B 'PACK' The DRAWRESS Procedure 1F6E: QUAL DRAWRESS ; b# =284 s#2 =proc148 vfp_1 VEQU -256 One local variable. param1 VEQU 8 One parameter needed. 1F6E: VEND ;-refs - 2/DRAWFHIN 2/SETUP 2/DRAWNUM ;- 2/DRAWDHIN OK, you should be able to just look at this and see what happens. First off, look at the trap, DrawString. It takes one parameter, a pointer to a string. Now, the previous line says to push the address of the local variable so this has to be the string pointer. Go back a few lines and we see that proc5 is being called with two parameters: the string pointer, and the parameter from the calling procedure. You can deduce that proc5 has to get a string from somewhere, and probably will call the GetString trap or some equivalent. In fact, if you look at proc5, you will see that it calls GetResource (resource type STR#). This returns a handle to the STR# resource. Proc5 then uses the second parameter to figure out which string the calling procedure really wants. Proc5 loops through the STR# resource until it comes to the right string, then moves a pointer to the string into the first parameter and returns. When it gets back here, vfp_1 contains a pointer to the string. 1F6E: 4E56 FF00 'NV..' DRAWRESS LINK A6,#-$100 1F72: 486E FF00 200FF00 PEA vfp_1(A6) 1F76: 3F2E 0008 2000008 PUSH param1(A6) 1F7A: 4EAD 002A 100048C JSR proc5(A5) 1F7E: 486E FF00 200FF00 PEA vfp_1(A6) At this point, vfp_1 has the stringptr. 1F82: A884 '..' _DrawString ; (s:Str255) 1F84: 4E5E 'N^' UNLK A6 1F86: 205F ' _' POP.L A0 1F88: 544F 'TO' ADDQ #2,A7 1F8A: 4ED0 'N.' JMP (A0) Note that there is no RTS instruction to return. The subroutine uses a common substitute. First it pops the return address off the stack (which is actually what the RTS would have done anyways) and then does an indirect JMP (A0). This just means to jump to whatever A0 points to and A0 points to the return address. 1F8C: C452 4157 5245 5353 data125 DNAME DRAWRESS,0,0 The MAKEAWIN Procedure 5852: QUAL MAKEAWIN ; b# =490 s#2 =proc209 vhy_1 VEQU -12 Two local variables, no parms passed. vhy_2 VEQU -8 5852: VEND ;-refs - 1/DA Mover 5852: 4E56 FFF0 'NV..' MAKEAWIN LINK A6,#-$10 5856: 42A7 'B.' CLR.L -(A7) These instructions are setting up the GetNewDialog below. 1st, clear space for the DialogPtr. 5858: 3F3C 000A '?<..' PUSH #10 Push the Dialog ID #. 585C: 42A7 'B.' CLR.L -(A7) Push a NIL pointer for wStorage 585E: 70FF 'p.' MOVEQ #-1,D0 5860: 2F00 '/.' PUSH.L D0 Push a 32 bit -1 (IM says to do this to make the dialog the frontmost window). 5862: A97C '.|' _GetNewDialog ; (DlgID:INTEGER; wStorage:Ptr; behind:WindowPtr):DialogPtr 5864: 2B5F FFFA -6 POP.L glob67(A5) And pop off the dialogPtr. This will be used by proc MAKEBOX. 5868: 486D FEC4 -$13C PEA glob48(A5) 586C: 3F3C 000A '?<..' PUSH #10 This is the dialog item - the left list box if you check Resedit. 5870: 4EBA FF32 20057A4 JSR MAKEBOX Well, after inspecting this procedure, it looks like more can be determined by just looking at these few instructions here. Notice that MakeBox is being called with two parameters: The 1st being an unknown global variable, and the second being one of the two list boxes in Mover's main dialog. So it looks like MakeBox is just performing some housekeeping on these two list boxes. 5874: 486D FEC8 -$138 PEA glob49(A5) 5878: 3F3C 000B '?<..' PUSH #11 Now do the right list box. 587C: 4EBA FF26 20057A4 JSR MAKEBOX 5880: 206D FEC4 -$13C MOVEA.L glob48(A5),A0 Get the address in (not of) glob48 into A0, 5884: 2050 ' P' MOVEA.L (A0),A0 and dereference it - or get whatever glob48 was pointing at into A0. 5886: 216D FEC8 0004 -$138 MOVE.L glob49(A5),4(A0) Now move glob49 (a pointer I suspect) into 4 past A0. So glob48 contains a pointer which points four bytes behind the pointer in glob49. 588C: 206D FEC8 -$138 MOVEA.L glob49(A5),A0 Now do the exact opposite. Grab the pointer in glob49 and stick the pointer in glob48 4 bytes past it. 5890: 2050 ' P' MOVEA.L (A0),A0 5892: 216D FEC4 0004 -$13C MOVE.L glob48(A5),4(A0) These last few instructions were kind of a mess because we don't no anything about how globs 48 and 49 will be used. We will come back here after looking at MainEven and particularly HandleBu. It will turn out that these two globals are pointers (or maybe handles, we don't really care) to the two list boxes on the main dialog. In addition, each pointer as a way of referring to the other list box. At this point, this does not make any sense, but later on, glob 50 will be set to either glob48 or glob 49 (or NIL) depending on which list box - if any - has a selection made in it. The reason that glob48 and glob49 need to refer to each other, is that glob50 will be used to check both list boxes to see if their associated volumes are locked. See HandleBu for details. 5898: 2F2D FFFA -6 PUSH.L glob67(A5) 589C: 3F3C 0002 '?<..' PUSH #2 Item is the Copy button. 58A0: 486E FFF4 200FFF4 PEA vhy_1(A6) 58A4: 486D FFF6 -$A PEA glob66(A5) This will save a handle to it. 58A8: 486E FFF8 200FFF8 PEA vhy_2(A6) 58AC: A98D '..' _GetDItem ; (dlg:DialogPtr; itemNo:INTEGER; VAR kind:INTEGER; VAR item:Handle; VAR box:Rect) 58AE: 2F2D FFFA -6 PUSH.L glob67(A5) 58B2: 3F3C 0006 '?<..' PUSH #6 Item is the left Open button. 58B6: 486E FFF4 200FFF4 PEA vhy_1(A6) 58BA: 486D FFEC -$14 PEA glob63(A5) This will save a handle to it. 58BE: 486E FFF8 200FFF8 PEA vhy_2(A6) 58C2: A98D '..' _GetDItem ; (dlg:DialogPtr; itemNo:INTEGER; VAR kind:INTEGER; VAR item:Handle; VAR box:Rect) 58C4: 2F2D FFFA -6 PUSH.L glob67(A5) 58C8: 3F3C 0007 '?<..' PUSH #7 Item is the right Open button. 58CC: 486E FFF4 200FFF4 PEA vhy_1(A6) 58D0: 486D FFF0 -$10 PEA glob64(A5) This will save a handle to it. 58D4: 486E FFF8 200FFF8 PEA vhy_2(A6) 58D8: A98D '..' _GetDItem ; (dlg:DialogPtr; itemNo:INTEGER; VAR kind:INTEGER; VAR item:Handle; VAR box:Rect) Now the program is going to assign dialog procedures to various of its items. Items 12 and 13 - the two filename boxes are assigned the DrawName proecdure. Items 14 - the size selected box - gets DrawSize. Item 15 -the font text demo box - gets DrawHint. Items 16 through 18 - various lines in the dialog box - get DrawGray. And items 19 and 20 - the free space on disk boxes - get DrawFree. If you examine SetDProc, you will see that it simply invokes GetDItem to get a handle to the dialog item (passed from the list below) and then uses SetDItem to set the dialogProcPtr to the procedure passed from the list below. 58DA: 3F3C 000C '?<..' PUSH #12 58DE: 487A FB2E 200540E PEA DRAWNAME 58E2: 4EBA FE7E 2005762 JSR SETDPROC 58E6: 3F3C 000D '?<..' PUSH #13 58EA: 487A FB22 200540E PEA DRAWNAME 58EE: 4EBA FE72 2005762 JSR SETDPROC 58F2: 3F3C 000E '?<..' PUSH #14 58F6: 487A FC32 200552A PEA DRAWSIZE 58FA: 4EBA FE66 2005762 JSR SETDPROC 58FE: 3F3C 000F '?<..' PUSH #15 5902: 487A FA3A 200533E PEA DRAWHINT 5906: 4EBA FE5A 2005762 JSR SETDPROC 590A: 3F3C 0010 '?<..' PUSH #16 590E: 487A FE1C 200572C PEA DRAWGRAY 5912: 4EBA FE4E 2005762 JSR SETDPROC 5916: 3F3C 0011 '?<..' PUSH #17 591A: 487A FE10 200572C PEA DRAWGRAY 591E: 4EBA FE42 2005762 JSR SETDPROC 5922: 3F3C 0012 '?<..' PUSH #18 5926: 487A FE04 200572C PEA DRAWGRAY 592A: 4EBA FE36 2005762 JSR SETDPROC 592E: 3F3C 0013 '?<..' PUSH #19 5932: 487A FD12 2005646 PEA DRAWFREE 5936: 4EBA FE2A 2005762 JSR SETDPROC 593A: 3F3C 0014 '?<..' PUSH #20 593E: 487A FD06 2005646 PEA DRAWFREE 5942: 4EBA FE1E 2005762 JSR SETDPROC 5946: 2F2D FFFA -6 PUSH.L glob67(A5) Now the dialog is made the current Port 594A: A873 '.s' _SetPort ; (port:GrafPtr) 594C: 2F2D FFFA -6 PUSH.L glob67(A5) and make the dialog visible, 5950: A915 '..' _ShowWindow ; (theWindow:WindowPtr) 5952: 2F2D FFFA -6 PUSH.L glob67(A5) and make it the frontmost window. 5956: A91F '..' _SelectWindow ; (theWindow:WindowPtr) 5958: 3F3C 0002 '?<..' PUSH #2 595C: 4EBA A78A 20000E8 JSR DIMITEM These instructions dim the two Open buttons. 5960: 3F3C 0003 '?<..' PUSH #3 5964: 4EBA A782 20000E8 JSR DIMITEM 5968: 2F2D FFFA -6 PUSH.L glob67(A5) 596C: A981 '..' _DrawDialog ; (dlg:DialogPtr) And finally, draw the damn thing. 596E: 4E5E 'N^' UNLK A6 5970: 4E75 'Nu' RTS 5972: CD41 4B45 4157 494E data270 DNAME MAKEAWIN,0,0 The MAKEBOX Procedure. 57A4: QUAL MAKEBOX ; b# =488 s#2 =proc208 vhx_1 VEQU -14 vhx_2 VEQU -10 vhx_3 VEQU -8 vhx_4 VEQU -4 param2 VEQU 8 Parm 2 is the dialog item # param1 VEQU 10 57A4: VEND ;-refs - 2/MAKEAWIN 57A4: 4E56 FFF2 'NV..' MAKEBOX LINK A6,#-$E 57A8: 48E7 0018 'H...' MOVEM.L A3-A4,-(A7) 57AC: 266E 000A 200000A MOVEA.L param1(A6),A3 A3 gets whatever is in parm 1. 57B0: 2F2D FFFA -6 PUSH.L glob67(A5) Push the DialogPtr, 57B4: 3F2E 0008 2000008 PUSH param2(A6) And push the item #. 57B8: 486E FFF6 200FFF6 PEA vhx_2(A6) This will get the Kind. 57BC: 486E FFF2 200FFF2 PEA vhx_1(A6) This will get the ItemHandle. 57C0: 486E FFF8 200FFF8 PEA vhx_3(A6) This will get the Box. 57C4: A98D '..' _GetDItem ; (dlg:DialogPtr; itemNo:INTEGER; VAR kind:INTEGER; VAR item:Handle; VAR box:Rect) 57C6: 2F2D FFFA -6 PUSH.L glob67(A5) Now push the dialogPtr and item again... 57CA: 3F2E 0008 2000008 PUSH param2(A6) 57CE: 3F2E FFF6 200FFF6 PUSH vhx_2(A6) Push the item Kind 57D2: 487A F662 2004E36 PEA DRAWBOX See IM - this is a procPtr. 57D6: 486E FFF8 200FFF8 PEA vhx_3(A6) And push the Box 57DA: A98E '..' _SetDItem ; (dlg:DialogPtr; itemNo,kind:INTEGER; item:Handle; box:Rect) 57DC: 42A7 'B.' CLR.L -(A7) 57DE: 7064 'pd' MOVEQ #100,D0 57E0: 2F00 '/.' PUSH.L D0 57E2: 4EAD 009A 1000A5C JSR NewHandle(A5) 57E6: 269F '&.' POP.L (A3) Get a new handle - size 100 - and put it into parm1 (which A3 points to). 57E8: 2053 ' S' MOVEA.L (A3),A0 A0 gets the handle. 57EA: 2850 '(P' MOVEA.L (A0),A4 And A4 gets the pointer. OK, A0 is a handle meaning it points to a pointer which in turn points to whatever it is we care about (in this case, a free block of memory). That means that (A0) grabs what ever A0 points to which is (by definition of a handle) the pointer. 57EC: 28AD FFFA -6 MOVE.L glob67(A5),(A4) And now we put the dialogPtr into the block of memory gotten by NewHandle. 57F0: 426C 0060 'Bl.`' CLR 96(A4) Remember, A4 points (its a pointer, not a handle!) to a block of memory, 100 bytes long. So this instruction simply clears the 96 byte in that block. 57F4: 204C ' L' MOVEA.L A4,A0 Put the pointer into A0. 57F6: 5088 'P.' ADDQ.L #8,A0 Add 8 to A0. Previously we had stored the dialogPtr at the beginning of this block. Since a pointer is 8 bytes long, A0 no points to the first byte after the dialogPtr. 57F8: 43EE FFF8 200FFF8 LEA vhx_3(A6),A1 vhx_3 is a Box which is of type Rect which is 4 integers, or 4 words, or two long words. 57FC: 20D9 ' .' MOVE.L (A1)+,(A0)+ 57FE: 20D9 ' .' MOVE.L (A1)+,(A0)+ So move the Box information into the free memory right after the dialogPtr and increment A0 to the next free byte. 5800: 302E FFFC 200FFFC MOVE vhx_4(A6),D0 This is tough since we don't know what vhx_4 is to start with. 5804: 906E FFF8 200FFF8 SUB vhx_3(A6),D0 But whatever, subtrack vhx_3 from it, result in D0. 5808: 48C0 'H.' EXT.L D0 At this point, D0 is accurate to the word length (since that was all the SUB specified). This will make it's sign (negative or posative) accurate to all 32 bits. 580A: 81FC 0010 '....' DIVS #16,D0 Now, divide by 16. 580E: 3940 0062 '9@.b' MOVE D0,98(A4) And put this value (whatever it is) in the last two bytes (notice it is a word length instruction) of the memory block. 5812: 426C 0058 'Bl.X' CLR 88(A4) 5816: 397C FFFF 0056 '9|...V' MOVE #$FFFF,86(A4) 581C: 422C 0014 'B,..' CLR.B 20(A4) These last instructions are filling in various parts of the memory block. 5820: 206D FFFA -6 MOVEA.L glob67(A5),A0 Put the DialogPtr back in A0. 5824: 2153 0098 '!S..' MOVE.L (A3),152(A0) A3 still points to parm1. 5828: 2F13 '/.' PUSH.L (A3) So, this effectively pushes parm1 582A: 4EBA AEA4 20006D0 JSR MAKESBAR This is fairly complicated, but this procedure makes a scroll bar for the dialog item. 582E: 2053 ' S' MOVEA.L (A3),A0 5830: 2050 ' P' MOVEA.L (A0),A0 Can't tell what these instructions are doing. 5832: 2068 0010 ' h..' MOVEA.L 16(A0),A0 5836: 2050 ' P' MOVEA.L (A0),A0 5838: 2153 0024 '!S.$' MOVE.L (A3),36(A0) 583C: 4CDF 1800 'L...' MOVEM.L (A7)+,A3-A4 5840: 4E5E 'N^' UNLK A6 5842: 205F ' _' POP.L A0 Pop off the return address. 5844: 5C4F '\O' ADDQ #6,A7 5846: 4ED0 'N.' JMP (A0) And jump back to the calling procedure. 5848: CD41 4B45 424F 5820 data269 DNAME MAKEBOX ,0,0 The MAINEVEN Procedure Basically, the main loop consists of a set of housekeeping routines, a call to ModalDialog to read dialog events that take place, and a simple jump table to handle the various events. D7 needs to be zero for the loop to keep running. If an error occurs, or the user hits Quit, D7 is changed to one and the procedure exits. First, DA Mover attempts to allocate a large block of memory (10000 hex) into glob26. If this is successful (or glob26 already has a memory handle) then the program skips down to make some more checks - otherwise a memory error is generated. Next, the procedure checks to see if there are any files open and if so, calls FlushVol to write any changes to disk. 0: QUAL MAINEVEN ; b# =1 s#1 =proc1 vab_1 VEQU -6 0: VEND ;-refs - 1/DA Mover 0: 4E56 FFF8 'NV..' MAINEVEN LINK A6,#-8 4: 48E7 0308 'H...' MOVEM.L D6-D7/A4,-(A7) 8: 4207 'B.' CLR.B D7 Enable the Main Event Loop. A: 4AAD F4F0 -$B10 lab_1 TST.L glob26(A5) glob46 will (or does) contain a handle to a large block of memory. So, if glob26 already has the handle, branch down, otherwise try to get some memory. E: 661C 100002C BNE.S lab_2 10: 42A7 'B.' CLR.L -(A7) Clear stack space for the returned handle. 12: 2F3C 0001 0000 '/<....' PUSH.L #$10000 Size of memory block needed. 18: 4EBA 0A42 1000A5C JSR NewHandle 1C: 2B5F F4F0 -$B10 POP.L glob26(A5) And get the handle in glob26. 20: 660A 100002C BNE.S lab_2 Remember, a NIL handle or pointer is all zeroes. glob26 either has a valid handle or a NIL handle. If it is valid, branch. 22: 3F3C 0032 '?<.2' PUSH #50 26: 4EAD 01CA 200023C JSR DOALERT(A5) Otherwise do some memory alert (you can check this if you like.) 2A: 7E01 '~.' MOVEQ #1,D7 and disable the main event loop. 2C: 1007 '..' lab_2 MOVE.B D7,D0 2E: 6600 00D0 1000100 BNE lab_15 Go if loop disabled from above. 32: 206D FEC4 -$13C MOVEA.L glob48(A5),A0 Get reference to left list box. 36: 2850 '(P' MOVEA.L (A0),A4 38: 4A6C 0058 'Jl.X' TST 88(A4) Look at the descrpition of FlushVol (next paragraph) to see what this variable means. 3C: 670E 100004C BEQ.S lab_3 Seeing that 88(A4) is the VRefNum, then branch if it is zero (no volume available - i.e. the list box has no opened file in it). 3E: 4267 'Bg' CLR -(A7) Space for function result (OSErr). 40: 42A7 'B.' CLR.L -(A7) iovNamePtr parameter (NIL). 42: 3F2C 0058 '?,.X' PUSH 88(A4) iovRefNum parameter. 46: 4EBA 0BAE 1000BF6 JSR FlushVol If a volume is available, flush it. 4A: 3C1F '<.' POP D6 Pop off error code. 4C: 206D FEC8 -$138 lab_3 MOVEA.L glob49(A5),A0 Now do the same thing with the right list box. 50: 2850 '(P' MOVEA.L (A0),A4 52: 4A6C 0058 'Jl.X' TST 88(A4) 56: 670E 1000066 BEQ.S lab_4 58: 4267 'Bg' CLR -(A7) 5A: 42A7 'B.' CLR.L -(A7) 5C: 3F2C 0058 '?,.X' PUSH 88(A4) 60: 4EBA 0B94 1000BF6 JSR FlushVol 64: 3C1F '<.' POP D6 Lets take a quick look and FlushVol and we can see a couple of things. Fist of all, we can quickly see what the parameters are: Parm1 is a pointer to the Volume Name, Parm2 is the Volume Ref Number. Looking back at MainEven, we see that the PEA 88(A4) is referring to the Volume Reference Number. FlushVol "writes the contents of the associated volume buffer and descriptive informatin about the volume (if they've changed since the last time FlushVol was called)." [IM II pg 89]. The returned result of this procedure is the OSErr. ;-refs - 1/MAINEVEN 2/FLUSHRES 2/REMOVEST BF6: 4E56 FFC0 'NV..' FlushVol LINK A6,#-$40 BFA: 41EE FFC0 200FFC0 LEA vbu_1(A6),A0 BFE: 316E 0008 0016 2000008 MOVE param2(A6),ioVRefNum(A0) C04: 216E 000A 0012 200000A MOVE.L param1(A6),ioNamePtr(A0) C0A: A013 '..' _FlushVol ; (A0|IOPB:ParamBlockRec):D0\OSErr C0C: 3D40 000E 200000E MOVE D0,funRslt(A6) C10: 4E5E 'N^' UNLK A6 C12: 225F '"_' POP.L A1 C14: 5C8F '\.' ADDQ.L #6,A7 C16: 4ED1 'N.' JMP (A1) back to MainEven 66: 4EAD 020A 2005DCA lab_4 JSR HANDLEBU(A5) 6A: 486D 0212 2005EF8 PEA MYFILTER(A5) 6E: 486E FFFA 200FFFA PEA vab_1(A6) 72: A991 '..' _ModalDialog ; (filterProc:ProcPtr; VAR itemHit:INTEGER) ModalDialog is the all-purpose dialog handler. It will monitor events and wait for an event involving an active dialog item. Upon returning, the dialog item number is returned in ModalDialog's 2nd parameter - in this case, vab_1. Once the trap returns, the program has to figure out what to do now that an item has been activated. Below, is a simple jump table that repeatedly subtracts integers from vab_1 until it is zero, at which point the program knows that it has the proper dialog item. It then branches to the appropriate routine. ModalDialog also takes a parameter that specifies a special procedure that it can call whenever an event occurs. What that means, is that the line PEA MYFILTER is telling ModalDialog to execute the procedure MYFILTER anytime an event occurs. We can take a look at MYFILTER to see what it is doing (although in cracking, we probably don't care). Right now I will guess that MYFILTER is taking care of things like allowing multiple selections in the list boxes,. displaying the font string, and displaying the size of the selection. 74: 302E FFFA 200FFFA MOVE vab_1(A6),D0 78: 5540 'U@' SUBQ #2,D0 Copy button. 7A: 6736 10000B2 BEQ.S lab_7 7C: 5340 'S@' SUBQ #1,D0 Remove Button. 7E: 672C 10000AC BEQ.S lab_6 80: 5340 'S@' SUBQ #1,D0 Help Button. 82: 6734 10000B8 BEQ.S lab_8 84: 5340 'S@' SUBQ #1,D0 Quit Button. 86: 6720 10000A8 BEQ.S lab_5 88: 5340 'S@' SUBQ #1,D0 Left Open/Close Button. 8A: 673C 10000C8 BEQ.S lab_10 8C: 5340 'S@' SUBQ #1,D0 Right Open/Close Button. 8E: 6742 10000D2 BEQ.S lab_11 90: 5340 'S@' SUBQ #1,D0 Font Radio Button. 92: 672A 10000BE BEQ.S lab_9 94: 5340 'S@' SUBQ #1,D0 DA Radio Button. 96: 6726 10000BE BEQ.S lab_9 98: 5340 'S@' SUBQ #1,D0 Left List Box. 9A: 6740 10000DC BEQ.S lab_12 9C: 5340 'S@' SUBQ #1,D0 Right List Box. 9E: 674A 10000EA BEQ.S lab_13 A0: 0440 0028 '.@.(' SUBI #40,D0 We will have to check MyFilter to see what this is doing. A4: 6752 10000F8 BEQ.S lab_14 A6: 6058 1000100 BRA.S lab_15 A8: 7E01 '~.' lab_5 MOVEQ #1,D7 User hit Quit, so disable the loop and jump to the loop end. AA: 6054 1000100 BRA.S lab_15 AC: 4EAD 01E2 2004E98 lab_6 JSR REMOVEST(A5) Remove Button. B0: 604E 1000100 BRA.S lab_15 B2: 4EAD 01EA 2004F5C lab_7 JSR COPYSTUF(A5) Copy Button. B6: 6048 1000100 BRA.S lab_15 B8: 4EAD 023A 2006B0E lab_8 JSR DOHELP(A5) Help Button. BC: 6042 1000100 BRA.S lab_15 BE: 3F2E FFFA 200FFFA lab_9 PUSH vab_1(A6) Push the selected item number, C2: 4EAD 022A 20064F2 JSR SELCLICK(A5) and change to either Fonts or DAs. C6: 6038 1000100 BRA.S lab_15 C8: 2F2D FEC4 -$13C lab_10 PUSH.L glob48(A5) CC: 4EAD 0242 2006B50 JSR DOCFILE(A5) Left Open (or close) Button. D0: 602E 1000100 BRA.S lab_15 D2: 2F2D FEC8 -$138 lab_11 PUSH.L glob49(A5) D6: 4EAD 0242 2006B50 JSR DOCFILE(A5) Right Open (or close) Button. DA: 6024 1000100 BRA.S lab_15 DC: 2F2D FEC4 -$13C lab_12 PUSH.L glob48(A5) Remember this guy? Refers to the left box. E0: 2F2D FFE8 -$18 PUSH.L glob62(A5) E4: 4EAD 0202 2005CB8 JSR CONTENTC(A5) Handle a list box click. E8: 6016 1000100 BRA.S lab_15 EA: 2F2D FEC8 -$138 lab_13 PUSH.L glob49(A5) Refers to the right list box. EE: 2F2D FFE8 -$18 PUSH.L glob62(A5) F2: 4EAD 0202 2005CB8 JSR CONTENTC(A5) List box handler. F6: 6008 1000100 BRA.S lab_15 F8: 3F2D FEDC -$124 lab_14 PUSH glob55(A5) FC: 4EAD 0232 2006A6A JSR HANDLEIN(A5) 100: 1007 '..' lab_15 MOVE.B D7,D0 Here is the end of the main loop. This checks to see if the loop should terminate. If not, branch back to the beginning of the loop. 102: 6700 FF06 100000A BEQ lab_1 106: 4CDF 10C0 'L...' MOVEM.L (A7)+,D6-D7/A4 At this point, either an error occurred or the user has hit the Quit button. 10A: 4E5E 'N^' UNLK A6 10C: 4E75 'Nu' RTS 10E: CD41 494E 4556 454E data1 DNAME MAINEVEN,0,0 HANDLEBU Procedure 5DCA: QUAL HANDLEBU ; b# =501 s#2 =proc214 vid_1 VEQU -272 vid_2 VEQU -256 5DCA: VEND A quick observation here. After scanning the first few lines, you can notice some hereto unknown things. Look at the references to glob50. At this point, we know that globs 48 and 49 have been set up to refer (we don't know exactly how) to the two list boxes in the main dialog. A quick look down a ways reveals that D7 is used to pass an integer to our DrawString procedure (proc5). If we assume that D7 is the ID # of the STR# resource (since this is the parameter that proc5 requires), then that MOVEQ 1,D7 (line 5) must refer to STR# 1 which reads "Copy". The next two strings in the resource are "<>Copy>>" which are exactly the three strings that the copy button on the dialog can contain. So we might assume right now that glob50 refers to one of the list boxes, and can be used to determine whether the user has selected an item(s) in the list box. Based upon this information, the procedure will fill in the copy button with the proper string. ;-refs - 1/MAINEVEN 5DCA: 4E56 FEEE 'NV..' HANDLEBU LINK A6,#-$112 5DCE: 48E7 0308 'H...' MOVEM.L D6-D7/A4,-(A7) 5DD2: 4AAD FECC -$134 TST.L glob50(A5) Check the list box (it seems) 5DD6: 660C 2005DE4 BNE.S lid_1 Look at what this branch is skipping. 5DD8: 7E01 '~.' MOVEQ #1,D7 The string is "Copy". 5DDA: 3F3C 0003 '?<..' PUSH #3 DIMITEM needs the item number to dim as a parm. Item 3 is the Remove button. 5DDE: 4EBA A308 20000E8 JSR DIMITEM Take a quick look at DIMITEM and you will see that it takes an item number as a parameter, pushes the parameter, then pushes the number -1 (255 if we are talking about signed numbers) and calls HILITEIT which uses the 2nd parameter to either set or dim the desired button. 5DE2: 607A 2005E5E BRA.S lid_8 The Remove button is dimmed anytime there is no selection in one of the list boxes. How did this procedure know there was no selection? It checked to see if glob50 was blank (or possible a NIL pointer) and if so, there is no selection. 5DE4: 202D FECC -$134 lid_1 MOVE.L glob50(A5),D0 Here is the real key. glob50 is being compared to glob48. We know glob48 has something to do with the left list box, and look what happens if they are the same...D7 gets 3 which means string">>Copy>>" - the user has made a selection in the left list box. 5DE8: B0AD FEC4 -$13C CMP.L glob48(A5),D0 5DEC: 6604 2005DF2 BNE.S lid_2 5DEE: 7E03 '~.' MOVEQ #3,D7 5DF0: 6002 2005DF4 BRA.S lid_3 5DF2: 7E02 '~.' lid_2 MOVEQ #2,D7 Otherwise the user has made a selection in the right list box. A quick note: glob50 was not compared to glob49, but it was compared to glob48. We can deduce from this that glob50 had to contain either glob48 or glob49. What this means is that glob50 seems to indicate that something has been selected in one of the list boxes or is empty if there is no selection. 5DF4: 206D FECC -$134 lid_3 MOVEA.L glob50(A5),A0 This is a mess. We know that glob50 is a handle to a host of information about one of the list boxes, but we didn't bother to figure which bytes mean what. The best thing to do here is to analyze all the branches in the mess, see where they go, and look at what happens as a result of each branch. So... 5DF8: 2050 ' P' MOVEA.L (A0),A0 5DFA: 2068 0004 ' h..' MOVEA.L 4(A0),A0 5DFE: 2050 ' P' MOVEA.L (A0),A0 5E00: 3C28 0058 '<(.X' MOVE 88(A0),D6 Look familiar? Let's guess that this is a vRefNum for the list box containing the selection. 5E04: 206D FECC -$134 MOVEA.L glob50(A5),A0 5E08: 2050 ' P' MOVEA.L (A0),A0 5E0A: 2068 0004 ' h..' MOVEA.L 4(A0),A0 5E0E: 2050 ' P' MOVEA.L (A0),A0 5E10: 4A68 0056 'Jh.V' TST 86(A0) 5E14: 6C02 2005E18 BGE.S lid_4 OK, here is a branch. If it executes, D6 has something (which we guessed to be a vRefNum) in it which gets passed on to lid_4. 5E16: 4246 'BF' CLR D6 Otherewise, D6 is zeroed (no volume available). 5E18: 4A46 'JF' lid_4 TST D6 5E1A: 57C0 'W.' SEQ D0 D0=FF hex if there is no volume. 5E1C: 4A00 'J.' TST.B D0 5E1E: 6616 2005E36 BNE.S lid_5 This branch executes if D6 was zero and will cause 1 to moved into D7 - "Copy". 5E20: 2F00 '/.' PUSH.L D0 Save D0 on the stack (not a parameter) 5E22: 4267 'Bg' CLR -(A7) Create space on the stack for the return value. 5E24: 3F06 '?.' PUSH D6 Aha! We were right. proc6 needs a vRefNum and here is good old D6 being pushed as a parm. D6 is indeed the vRefNum. 5E26: 4EAD 0032 10004CA JSR proc6(A5) Takes a vRefNum as a parm, then does a GetVolInfo, and checks the iovAttributes to see if the disk is locked. Returns a 1 if locked, 0 if unlocked. 5E2A: 121F '..' POP.B D1 Pop off the locked status. 5E2C: 201F ' .' POP.L D0 Pop off the original D0. 5E2E: 8001 '..' OR.B D1,D0 Or them so that, in effect, the AND instruction below will be ANDing both D0 and D1 with 1. 5E30: 0240 0001 '.@..' ANDI #1,D0 Check to see if one of the two contains a non-zero value, 5E34: 6702 2005E38 BEQ.S lid_6 and if so, do not put a 1 in D7 (the string is not "Copy"). 5E36: 7E01 '~.' lid_5 MOVEQ #1,D7 String is "Copy" (meaning that DA Mover will not allow the Copy to proceed) and from the above code, we might guess that this is a result of the destination volume being locked so copying is impossible. 5E38: 4267 'Bg' lid_6 CLR -(A7) 5E3A: 206D FECC -$134 MOVEA.L glob50(A5),A0 And here is basically the same as above except that the other list box's volume is being checked 5E3E: 2050 ' P' MOVEA.L (A0),A0 5E40: 3F28 0058 '?(.X' PUSH 88(A0) Push the vRefNum of the volume from which the selection has been made. 5E44: 4EAD 0032 10004CA JSR proc6(A5) Locked Volume? 5E48: 101F '..' POP.B D0 5E4A: 670A 2005E56 BEQ.S lid_7 Go if not locked. 5E4C: 3F3C 0003 '?<..' PUSH #3 If the volume is locked, we cannot remove anything so dim the Remove Button. 5E50: 4EBA A296 20000E8 JSR DIMITEM 5E54: 6008 2005E5E BRA.S lid_8 5E56: 3F3C 0003 '?<..' lid_7 PUSH #3 Else activate the Remove Button (volume is not locked). 5E5A: 4EBA A2AE 200010A JSR UNDIMITE OK, let's re-cap for a minute. If you look back at MakeAWin, you will note that glob48 and glob49 are set up to refer to information about the left and right list boxes respectively. We also know that these globs contain information about the volume (and possibly the file) that is being displayed in the list boxes - since 88 bytes off the start of the pointer is the volume reference number. The above code can be broken into two pieces: from line 5DF4, to lid_5 and from lid_6 to one line past lid_7. The first piece is messy, but the end result is that the destination volume is tested to see if it is locked, and if so, the copy button text is set to "Copy". Therefore we can now assume that all that messy stuff beforehand was in essence setting a pointer to the destination list box information. Remember from MakeAWin there was a strange section of code that seemed to link the two globs to each other? Well, now we see that glob50 is set to one of these two (the one that contains a selection) but glob50 must also be able to access the other list box's volume to see if it is locked (or to see if copying to it is possible). The second section checks to see if the volume containing the selection is locked, and if so, Removing is not possible. 5E5E: BE6D FFF4 -$C lid_8 CMP.W glob65(A5),D7 Once again, we don't know what this glob means, but we can see what gets skipped if the branch executes. Once we know what gets skipped, we have a decent idea what the global means. Keep in mind that the global is being compared to D7 - the string resource ID #. 5E62: 6700 0082 2005EE6 BEQ lid_13 So if glob65 contains the ID # in D7, skip to the end of the procedure. 5E66: 42A7 'B.' CLR.L -(A7) 5E68: A8D8 '..' _NewRgn ; :RgnHandle 5E6A: 285F '(_' POP.L A4 5E6C: 2F0C '/.' PUSH.L A4 5E6E: A87A '.z' _GetClip ; (rgn:RgnHandle) 5E70: 486E FEF0 200FEF0 PEA vid_1(A6) 5E74: 42A7 'B.' CLR.L -(A7) 5E76: 42A7 'B.' CLR.L -(A7) 5E78: A8A7 '..' _SetRect ; (VAR r:Rect; left,top,right,bottom:INTEGER) 5E7A: 486E FEF0 200FEF0 PEA vid_1(A6) 5E7E: A87B '.{' _ClipRect ; (r:Rect) 5E80: 486E FF00 200FF00 PEA vid_2(A6) 5E84: 3F07 '?.' PUSH D7 5E86: 4EAD 002A 100048C JSR proc5(A5) Once again, the DrawString procedure. D7 is the string # and vid_2 returns a pointer to the string. 5E8A: 2F2D FFF6 -$A PUSH.L glob66(A5) Look at the trap below. glob66 HAS to be a CtlHdl (Handle to a control object on a dialog), 5E8E: 486E FF00 200FF00 PEA vid_2(A6) and vid_2 we already know has the string whose ID # is in D7. Since D7's string is "Copy", ">>Copy>>", or "<.' POP D7 428: 4267 'Bg' CLR -(A7) 42A: 486E FBFA 200FBFA PEA vem_5(A6) 42E: 3F2E FBF2 200FBF2 PUSH vem_2(A6) 432: 3F3C 0010 '?<..' PUSH #16 436: 2F0C '/.' PUSH.L A4 438: 4267 'Bg' CLR -(A7) 43A: 4EBA FDBE 20001FA JSR proc103 43E: 181F '..' POP.B D4 440: 4267 'Bg' CLR -(A7) 442: 3F2E FBF2 200FBF2 PUSH vem_2(A6) 446: 2F0C '/.' PUSH.L A4 448: 4EBA FED8 2000322 JSR proc105 44C: 101F '..' POP.B D0 44E: 0A00 0001 '....' EORI.B #1,D0 452: 6700 00A0 20004F4 BEQ lem_2 456: 4267 'Bg' CLR -(A7) 458: 3F3C 0002 '?<..' PUSH #2 45C: 3F2E FBF2 200FBF2 PUSH vem_2(A6) 460: 2F0C '/.' PUSH.L A4 462: 4EBA FF0C 2000370 JSR proc106 466: 101F '..' POP.B D0 468: 0A00 0001 '....' EORI.B #1,D0 46C: 6700 0086 20004F4 BEQ lem_2 470: 4267 'Bg' CLR -(A7) 472: 3F3C 0001 '?<..' PUSH #1 476: 3F2E FBF2 200FBF2 PUSH vem_2(A6) 47A: 2F0C '/.' PUSH.L A4 47C: 4EBA FEF2 2000370 JSR proc106 480: 101F '..' POP.B D0 482: 0A00 0001 '....' EORI.B #1,D0 486: 676C 20004F4 BEQ.S lem_2 488: 42A7 'B.' CLR.L -(A7) 48A: 3F3C 0101 '?<..' PUSH #257 48E: 42A7 'B.' CLR.L -(A7) 490: 70FF 'p.' MOVEQ #-1,D0 492: 2F00 '/.' PUSH.L D0 494: A9BD '..' _GetNewWindow ; (windowID:INTEGER; wStorage:Ptr; behind:WindowPtr):WindowPtr 496: 265F '&_' POP.L A3 498: 2F0B '/.' PUSH.L A3 49A: A873 '.s' _SetPort ; (port:GrafPtr) 49C: 3F3C 0010 '?<..' PUSH #16 4A0: 3F3C 001C '?<..' PUSH #28 4A4: A893 '..' _MoveTo ; (h,v:INTEGER) 4A6: 4267 'Bg' CLR -(A7) 4A8: A887 '..' _TextFont ; (font:FontCode) 4AA: 42A7 'B.' CLR.L -(A7) 4AC: 3F3C 0100 '?<..' PUSH #256 4B0: A9BA '..' _GetString ; (stringID:INTEGER):StringHandle 4B2: 2C1F ',.' POP.L D6 4B4: 2046 ' F' MOVEA.L D6,A0 4B6: 2F10 '/.' PUSH.L (A0) 4B8: A884 '..' _DrawString ; (s:Str255) 4BA: 4267 'Bg' CLR -(A7) 4BC: 42A7 'B.' CLR.L -(A7) 4BE: 3F3C 0001 '?<..' PUSH #1 4C2: 4EAD 002A 1000186 JSR Eject(A5) 4C6: 3E1F '>.' POP D7 4C8: 486E FBF4 200FBF4 lem_1 PEA vem_3(A6) 4CC: 4EBA FEEE 20003BC JSR proc107 4D0: 4267 'Bg' CLR -(A7) 4D2: 3F2E FBF4 200FBF4 PUSH vem_3(A6) 4D6: 2F0C '/.' PUSH.L A4 4D8: 4EBA FE48 2000322 JSR proc105 4DC: 1A1F '..' POP.B D5 4DE: 4267 'Bg' CLR -(A7) 4E0: 42A7 'B.' CLR.L -(A7) 4E2: 3F2E FBF4 200FBF4 PUSH vem_3(A6) 4E6: 4EAD 002A 1000186 JSR Eject(A5) 4EA: 3E1F '>.' POP D7 4EC: 1005 '..' MOVE.B D5,D0 4EE: 67D8 20004C8 BEQ lem_1 4F0: 2F0B '/.' PUSH.L A3 4F2: A914 '..' _DisposWindow ; (theWindow:WindowPtr) 4F4: 4CDF 18F0 'L...' lem_2 MOVEM.L (A7)+,D4-D7/A3-A4 4F8: 4E5E 'N^' UNLK A6 4FA: 4E75 'Nu' RTS 4FC: '............' data76 DC.W $8100,8,0,$4FC,$FC00,0 The first thing to do here is to quickly scan for trap names. There are quite a few, but one should stick out. Remember that we are looking for some reference to STR #256. Note the GetString trap. Immediately before the trap is a PUSH #256...that's our guy! So, at this point, we know where the string is being loaded and drawn. Since this procedure is called from the Main procedure, we can bet that the key-disk check is also in this proc. Note that this is not always the case - often when you find the procedure that loads the dialog or string, you need to back trace to find out where the actual error generator is located. That is where the Refs line (right below the VEND) in the listing comes in handy. Note that this proc is called by not only Sorcerer, but also by proc37. This might mean that the program checks the key-disk later in its execution. But if you load up proc37, you would find that it simply Unloads the segment so it is harmless. At this point, all we need to do is disable the disk-check. So, start scanning down the listing and ask yourself "Where is a branch that will skip over the GetString trap?". If you find that branch and make it always branch then odds are the program is cracked. Nosy will help out here. We are looking for a spot in the listing that a branch can jump to that will skip over the error. We have two choices in this listing: lem_1 and lem_2. Check out lem_1, and you will see a couple of problems with it. First of all, see what piece of code branches to it. There is a JSR Eject, then a test, and a BEQ lem_1. Also note that there is a DisposeWindow after it. We might guess that DisposeWindow is disposing the error dialog. We might also guess that lem_1 is being used as a loop to eject bad disks and request key-disks. Well, let's give lem_2 a shot. Now this one looks good - it is located right down at the procedures exit, so, if something is branching here, all the above stuff gets skipped. So, just select lem_2 and hit cmd-f to let Nosy find all the references to lem_2 in the listing. Line 452 is the key. Note, D0 gets a result from an unknow procedure, then is EORd with 1, and then the branch occurs. It sure looks like changing that branch from BEQ to BRA would gurrantee that the error never occured. Let's try it. From the assembly instruction listing, we see that BEQ is 67, and BRA is 60. So, look at the first line in the above listing and we see that it is segment 2. So, open CODE resource 2 in Resedit, and skip down to address 456 (remember, take the Nosy address and add 4 to find the Resedit address).  There it is, on line 450. See the 6700? That sure matches what we find in Nosy, so that is our guy. Change the 67 to 60 by clicking to the right of the 67, hitting backspace or delete, and typing 60. That's it! Now quit Resedit and save changes. Launch the program and the protection is gone! Let me quickly mention one last thing. The above crack involved looking for a branch that would skip over the problem area, and making damn sure that that branch always executed. But suppose that the program was setup so that after the disk check, the program branched to the error section. In this case, we would want to make sure the branch never executed. There are two ways to do this. First off, you can change the branch to its logical opposite - BCC to BCS, BNE to BEQ, etc. That way, the condition that triggers the error will now trigger the opposite, and run properly. The second method is to simply replace the trap with a NOP. That way, the branch never executes no matter what happens. Look for upcoming material on more specific cracking methods and more actual cracks. later - The Shepherd Beta Notes: 10/17/91 The following bold entries constitute a tentative outline for topics to dicuss in detail. Some of these topics will require a fair amount of research on my part - in particular, the Eve and Encryption sections will take some time. After this section come the live cracks. These represent an attempt to take a novice cracker through every step of the cracking process detailing choices and decisions that I would make as I go and why I would make them. Any feedback would be greatly appreciated - especially from any novice crackers who find parts of this document incomprehensible. Note that this is a rough draft - there are bound to be errors although hopefully no logical ones (just syntactical and/or spelling). Determining where to start looking 1) Types of protection a) Serial number schemes b) Registration codes c) Network serial checks [AppleTalk driver stuff] d) Hardware plugs - see below e) Encryption - see below f) Time stamps g) Key disk How to break into programs 1) Trap interrupts a) Dialog/Alert traps b) MenuSelect c) InitFonts etc. 2) Manual entrance of TMON [Good luck] 3) Automatic TMON entrance via code modification [_Debugger trap insertion] a) Determining an address with Nosy b) Determining an address from the Jump Table c) Using TMON, Nosy, and ResEdit together 1) Determining address offsets 2) Nosy vs TMON a) Why Nosy "feels better" b) Why TMON is virtually omniscient TMON Tricks 1) TMON tricks with register values, flags, and instruction modification 2) One step ModalDialog hassles [Serial number schemes] 3) TMON Pro shortcuts Determining the type of crack to apply 1) Bypasses vs cracks 2) Finding the key code 3) Branch switching a) Mention something about branch op-codes - 2 and 4 byte instructions and offsets 4) Flag/variable modification 5) Code modification Everything you always wanted to know about the CODE 0 Jump Table. 1) What it is and how it works 2) Locating an entry point 3) Modifications Hardware plugs 1) General tips [Device Manager stuff] 2) Eve bullshit Encrypted Code Unless you are one hell of a genius at cryptology and have lots of time to kill, the encrypted CODE resources will have to be de-crypted and written back to the program. Here is why: to decrypt itself, a program will usually either take a known seed number and use it on each encrypted byte of the code or else it will start with some byte in the code and do a forward decrypt, i.e. the first byte decrypts the second byte, the new second byte decrypts the third byte, and so on. A simple method might be to have some code that looks like this: MOVE #1000,D0 LEA encryptedshit,A0 LEA encryptedshit-1,A1 loop1 EOR.L (A1)+,(A0)+ DBRA D0,loop1 encryptedshit Here is where the encrypted gibberish begins. This is a simple example, but note how it functions. D0 gets the number of longwords to decrypt, A0 is the destination (where the decrypted stuff will go - which is right back over the encrypted stuff) and A1 gets the decrypting key which is the long word that was previously decrypted. Then the code simply loops D0 times writing over the encrypted code with the decrypted code. After this code has finished, the program continues execution right where the encrypted (and now decrypted) code begins. Now cosider: somewhere in the encrypted stuff is the error check that you have to modify. This will be simple enough to locate assuming that you can run the decryption routine and then immediately regain control in TMON. The problem is that when you go to modify the error check so that it always passes, the modification screws up the decryption routine. This is because the decryption routine requires the exact original values to run properly since these values are the keys that the code uses. So a crack using traditional methods requires that you not only change the error branch, but that you also change every other encrypted value such that the decryption routine still runs properly - no small feat! A much more feasable method would be to decrypt the code, make the necessary modifications to the error routine, and then disable the decryption routine (just branching around it would do) and writing the whole mess (un-encrypted) back to the original code resource. So much for the theory, now if I could just crack one of these suckers... Live Cracks MultiClip 2.0 This program uses a network checking algorithm to determine whether multiple copies with the same serial number are currently running - if you don't use this program on a network, you will never see the error. Step 1: Where to start looking. There are actually several good places to begin looking for the protection (especially if you have already cracked it - but I will assume that you have not). First of all, since the program scans the network, it is probably using the _Open Trap somewhere early in its code to to access the Appletalk driver. Second, it displays an error dialog (or alert) so we could open it up in Resedit, find the error dialog (and note its ID # for later use) and then Nosy it and look at procedures that call ModalDialog or one of the Alert traps to try and find the one that displays the dialog with the proper ID #. Third, we could have TMON trap either 1) ModalDialog if it is a dialog or 2) StopAlert, CautionAlert or NoteAlert if it is an Alert and begin tracing from that point backwords. Fourth, we could just Nosy it and start from the top (the slow way). Whenever a program displays an error dialog (not a serial number dialog which seems to be in vogue these days) I almost always find the ID # of the dialog or alert and begin looking at procs in Nosy, so let's start there. In Resedit, we note that it is Dialog (and not Alert) #128 that is the problem. On to Nosy. After Nosy analyzes the INIT resource, open up the Trap Refs List under the Display menu and scroll down to GetNewDialog. Here you will find two listings: ASKNAME and PUTREGISTERDLOG. Since there are only two we can quickly check them both out (if there were a bunch, I would probably try a different method). First let us look at ASKNAME - here is the listing down to the GetNewDialog: 42BA: QUAL ASKNAME ; b# =184 s#1 =proc54 vdu_1 VEQU -26 vdu_2 VEQU -18 vdu_3 VEQU -12 vdu_4 VEQU -10 vdu_5 VEQU -8 param1 VEQU 8 funRslt VEQU 12 42BA: VEND ;-refs - com_43 MYFILTERFORNAME 42BA: 4E56 FFE6 'NV..' ASKNAME LINK A6,#-$1A 42BE: 48E7 0318 'H...' MOVEM.L D6-D7/A3-A4,-(A7) 42C2: 2C2E 0008 2000008 MOVE.L param1(A6),D6 42C6: 42A7 'B.' CLR.L -(A7) 42C8: 4EBA E642 100290C JSR proc19 42CC: 285F '(_' POP.L A4 42CE: 486E FFF8 200FFF8 PEA vdu_5(A6) 42D2: A874 '.t' _GetPort ; (VAR port:GrafPtr) 42D4: 42A7 'B.' CLR.L -(A7) 42D6: 302C 001E '0,..' MOVE 30(A4),D0 42DA: D07C 0014 '.|..' ADD #20,D0 42DE: 3F00 '?.' PUSH D0 42E0: 42A7 'B.' CLR.L -(A7) 42E2: 70FF 'p.' MOVEQ #-1,D0 42E4: 2F00 '/.' PUSH.L D0 42E6: A97C '.|' _GetNewDialog ; (DlgID:INTEGER; wStorage:Ptr; behind:WindowPtr):DialogPtr The first thing to do is to locate the _GetNewDialog and determine its associated parameters: actually all we care about is the first parameter, the ID #. Tracing backwords, we see that -1 is the third parm, 0 is the second parm, and 30(A4) + #20 (from the ADD #20,D0) is the first parm. Well, we have a problem here. Instead of a nice plain ID # being passed to GetNewDialog, the ID # is hidden on the stack frame somewhere. At this point it is best to mark this proc as indeterminite and go on to the next one. If we must come back to this one then we will have to figure out if ID #128 is valid for this proc and go from there. So let us look at PUTREGISTERDLOG 33AC: QUAL PUTREGISTERDLOG ; b# =141 s#1 =proc35 vdb_1 VEQU -286 vdb_2 VEQU -278 vdb_3 VEQU -276 vdb_4 VEQU -274 vdb_5 VEQU -272 vdb_6 VEQU -270 vdb_7 VEQU -268 vdb_8 VEQU -264 vdb_9 VEQU -262 vdb_10 VEQU -256 param1 VEQU 8 33AC: VEND ;-refs - INIT1 PUTREGISTERDLOG 33AC: 4E56 FEE2 'NV..' LINK A6,#-$11E 33B0: 2F0C '/.' PUSH.L A4 33B2: 206E 0008 2000008 MOVEA.L param1(A6),A0 33B6: 43EE FF00 200FF00 LEA vdb_10(A6),A1 33BA: 703F 'p?' MOVEQ #63,D0 33BC: 22D8 '".' ldb_1 MOVE.L (A0)+,(A1)+ 33BE: 51C8 FFFC 10033BC DBRA D0,ldb_1 33C2: 42A7 'B.' CLR.L -(A7) 33C4: 3F3C 0080 '?<..' PUSH #128 33C8: 42A7 'B.' CLR.L -(A7) 33CA: 70FF 'p.' MOVEQ #-1,D0 33CC: 2F00 '/.' PUSH.L D0 33CE: A97C '.|' _GetNewDialog ; (DlgID:INTEGER; wStorage:Ptr; behind:WindowPtr):DialogPtr Once again, find the GetNewDialog and determine the parms. Here we have -1 for the third, 0 for the second, and lo and behold, 128 for the first. This is definately our procedure. Note that this is an extremely easy example as no attempt has been made to disguise the ID # - it is clearly 128, the value we have been looking for all along. Determining how to implement the crack. The obvious place to start looking is just before the error dialog has been loaded. Here is that section of code from the above procedure: LINK A6,#-$11E PUSH.L A4 MOVEA.L param1(A6),A0 LEA vdb_10(A6),A1 MOVEQ #63,D0 ldb 1 MOVE.L (A0)+,(A1)+ DBRA D0,ldb_1 Next comes the code we just looked at CLR.L -(A7) PUSH #128 CLR.L -(A7) MOVEQ #-1,D0 PUSH.L D0 _GetNewDialog As we look at this code, keep in mind what it is that we are looking for. We know that the program is capable of loading without this error, so somewhere it has to be checking the network and then either branching to the error code (if it detects a copy of itself) or else branching around the error code. So we need to find the branch that is causing this segment of code to execute. A quick scan of the code that precedes the error dialog code should reveal nothing of interest. A Link followed by a 63 word Move Loop - no branches of any consequence whatsoever. If you are wondering why we can immediately eliminate the DBRA D0,ldb1 (after all, it is a branch) then ask yourself this: 1st, where does the branch go? Answer: to the line above the branch instruction. 2nd, what (if any) conditions is it checking? Answer: it checks to see if D0 (an obvious loop counter in this case) is equal to zero. If the branch does not either 1) branch directly to the error code (in this case it would have to be branching to the CLR.L -(A7) ) or 2) branch around the error code (somewhere after the GetNewDialog and the ensuing ModalDialog and probably even an ensuing DisposeDialog) then the branch is almost certainly a bad candidate. You particulaly should be able to immediately eliminate loop terminator branches like the one above. Well, since we have eliminated the only branch in this procedure above the GetNewDialog, we will have to look elsewhere. The next obvious place to look is in the procedure that called this one. Again Nosy makes this a snap. Take a look at the line right above the code listing that read refs - INIT1. The refs line tells you every procedure that calls the one you are currently looking at. Luckily, there is only one, so let us look at it next. Since this is a long procedure, I am only listing the section that surrounds the JSR PUTREGISTERDLOG line. I should also mention that I am writing this with a copy that I cracked a while ago and in un-cracking it for this document, could not remember exactly what the changed code was. I will show you where your code listing might differ from mine below: 196: 4268 0004 'Bh..' CLR 4(A0) 19A: 4228 0006 'B(..' CLR.B 6(A0) 19E: 4228 0007 'B(..' CLR.B 7(A0) 1A2: 43FA 036E 1000512 LEA data2,A1 ; len= 1 1A6: 45E8 0009 'E...' LEA 9(A0),A2 1AA: 4EBA 0392 100053E JSR proc2 1AE: 43FA 03A2 1000552 LEA data4,A1 ; 'Multi' 1B2: 4EBA 038A 100053E JSR proc2 1B6: 43FA 03AC 1000564 LEA data7,A1 ; len= 2 1BA: 4EBA 0382 100053E JSR proc2 1BE: 4A6E FFEC 200FFEC TST vab_2(A6) 1C2: 6756 100021A BEQ.S lab_13 1C4: 4FEF FFFE 'O...' LEA -2(A7),A7 1C8: 2F2E FFEE 200FFEE PUSH.L vab_3(A6) 1CC: 4EBA 2C88 1002E56 JSR proc29 1D0: 301F '0.' POP D0 1D2: 6646 100021A BNE.S lab_13 1D4: 4FEF FFCE 'O...' LEA -50(A7),A7 1D8: 204F ' O' MOVEA.L A7,A0 1DA: 317C FFF6 0018 '1|....' MOVE #$FFF6,ioCRefNum(A0) 1E0: 216E FFEE 001E 200FFEE MOVE.L vab_3(A6),ioSEBlkPtr(A0) 1E6: 317C 00FC 001A '1|....' MOVE #252,CSCode(A0) 1EC: A004 '..' _Control ; (A0|IOPB:ParamBlockRec):D0\OSErr 1EE: 4FEF 0032 'O..2' LEA 50(A7),A7 1F2: 206E FFEE 200FFEE MOVEA.L vab_3(A6),A0 1F6: A01F '..' _DisposPtr ; (A0/p:Ptr) 1F8: 486D FFFC -4 PEA glob1(A5) 1FC: A86E '.n' _InitGraf ; (globalPtr:Ptr) 1FE: A8FE '..' _InitFonts 200: A912 '..' _InitWindows 202: A9CC '..' _TeInit 204: 42A7 'B.' CLR.L -(A7) 206: A97B '.{' _InitDialogs ; (resumeProc:ProcPtr) 208: A850 '.P' _InitCursor 20A: 42B8 0A6C $A6C CLR.L DeskHook 20E: 487A 0302 1000512 PEA data2 ; len= 1 212: 4EBA 3198 10033AC JSR PUTREGISTERDLOG 216: 4EFA 0316 100052E JMP com_2 21A: 4227 'B'' lab_13 CLR.B -(A7) 21C: A99B '..' _SetResLoad ; (AutoLoad:BOOLEAN) 21E: 42A7 'B.' CLR.L -(A7) 220: 2F3C 4452 5652 '/q7|%#?;8p0p8pa%#0A & `a0 $ ` @a<p@a>'`!a06  a0v  c|9 xxxa~//.  Њu0u0!HH\ff33̙ff33ff33ffffffffffff33ff33333333ff333333ff33ff33̙ff33̙̙̙̙ff̙33̙ffffffffffff33ff33333333ff333333ff33ff33̙ff33ff33ffffffffffff33ff33333333ff333333ff33ffffffffffff33ffffffff̙ffffff33ffffffffffffff33ffffffffffffffffffffffff33ffffff33ff33ff33ff33ffff3333ff33ffffffffffff33ff33333333ff333333333333̙33ff33333333333333ff33333333ff33ff33ff33ffff33ff3333ff3333333333333333ff333333333333333333ff333333ff33̙ff33ff33ffffffffffff33ff33333333ff333333ff33wwUUDD""wwUUDD""wwUUDD""wwwwwwUUUUUUDDDDDD""""""!99s                  ,  r  & )+% % ,        %19;4;K ;      *0567 \0 W< ]]W]F]WVV7WVVV VVVVVWVW WW{WW{{&{{'{{+{,%'%       .8 .//K 4        3513++-       =E     ;EEBAJI     /333''+       8 P H G G R 5    - ) N ] 1Y 1Z 1Z 1Y $B              @          -4OOQ NUW=    2<K^!Y!RX\I      000  Њu0u0EHH\ff33̙ff33ff33ffffffffffff33ff33333333ff333333ff33ff33̙ff33̙̙̙̙ff̙33̙ffffffffffff33ff33333333ff333333ff33ff33̙ff33ff33ffffffffffff33ff33333333ff333333ff33ffffffffffff33ffffffff̙ffffff33ffffffffffffff33ffffffffffffffffffffffff33ffffff33ff33ff33ff33ffff3333ff33ffffffffffff33ff33333333ff333333333333̙33ff33333333333333ff33333333ff33ff33ff33ffff33ff3333ff3333333333333333ff333333333333333333ff333333ff33̙ff33ff33ffffffffffff33ff33333333ff333333ff33wwUUDD""wwUUDD""wwUUDD""wwwwwwUUUUUUDDDDDD""""""E,/a    u   }   }     ,  `  %39.7575                   $"-/#\/W-]]W]+]WVV/WV V V VVW W W W{W{{/{{4{,52/3    ##;A071;3       #'%%!!%       2@ H I BC J V G                      %$+ ,-,44.        #* 6 DFG N! Q$ A      +-=HMK  Q  U  =        ;C]i  j m  t  w T     05X gg ajqV    00U k m  e  l  q V      *-F _  ^  QZg M     9<&9.&9& & Њu0u0j&HH\ff33̙ff33ff33ffffffffffff33ff33333333ff333333ff33ff33̙ff33̙̙̙̙ff̙33̙ffffffffffff33ff33333333ff333333ff33ff33̙ff33ff33ffffffffffff33ff33333333ff333333ff33ffffffffffff33ffffffff̙ffffff33ffffffffffffff33ffffffffffffffffffffffff33ffffff33ff33ff33ff33ffff3333ff33ffffffffffff33ff33333333ff333333333333̙33ff33333333333333ff33333333ff33ff33ff33ffff33ff3333ff3333333333333333ff333333333333333333ff333333ff33̙ff33ff33ffffffffffff33ff33333333ff333333ff33wwUUDD""wwUUDD""wwUUDD""wwwwwwUUUUUUDDDDDD""""""j&&'/[ z  o  x  x   z,  \  { {{D{P{{\ {ph {k  z{r  z{z {R {{{{{z{(*BF7F{@{B{@{{{{{W{'VVW!{W@{B{CzzWBzCWWG{WE{W{{{{{{{{W{W{{{{W]]|{{{{{{{{{{{{{{D{b  X {[z{[ z{e{I {{#{{{z{?[  R T U ] E      !5Q N T%T%W,>      /C 6 ;>C6      * 31125,              - >K5D  D  I 0        : S$JILV$8     T h`   ^  _  e B!    1ESl ^ jjf R        - :E 2> > C+        4B EA 8 >  E  H 7        .@ H PHE N ^=     tzfzVz z Њu0u0'HH\ff33̙ff33ff33ffffffffffff33ff33333333ff333333ff33ff33̙ff33̙̙̙̙ff̙33̙ffffffffffff33ff33333333ff333333ff33ff33̙ff33ff33ffffffffffff33ff33333333ff333333ff33ffffffffffff33ffffffff̙ffffff33ffffffffffffff33ffffffffffffffffffffffff33ffffff33ff33ff33ff33ffff3333ff33ffffffffffff33ff33333333ff333333333333̙33ff33333333333333ff33333333ff33ff33ff33ffff33ff3333ff3333333333333333ff333333333333333333ff333333ff33̙ff33ff33ffffffffffff33ff33333333ff333333ff33wwUUDD""wwUUDD""wwUUDD""wwwwwwUUUUUUDDDDDD"""""" z%Ea  S Y Y  `  L    5 =-9%5 7 L  5        41H^ [  ]  ]  b Q      $%LSR\RWO]]]WPW ]]WVLWV2VVVVVVWVVVVWWVWWVW{VW{*{WW{({W{AWW{{B{{={{7{{4{85       +.:@6>8C4          }y         ) +HG J @ LP;  ;;;  Њu0u0HH\ff33̙ff33ff33ffffffffffff33ff33333333ff333333ff33ff33̙ff33̙̙̙̙ff̙33̙ffffffffffff33ff33333333ff333333ff33ff33̙ff33ff33ffffffffffff33ff33333333ff333333ff33ffffffffffff33ffffffff̙ffffff33ffffffffffffff33ffffffffffffffffffffffff33ffffff33ff33ff33ff33ffff3333ff33ffffffffffff33ff33333333ff333333333333̙33ff33333333333333ff33333333ff33ff33ff33ffff33ff3333ff3333333333333333ff333333333333333333ff333333ff33̙ff33ff33ffffffffffff33ff33333333ff333333ff33wwUUDD""wwUUDD""wwUUDD""wwwwwwUUUUUUDDDDDD""""""    .6j w| |  k        -%wswsy}       QSUWS[S     _iamVVc\VeVVeVVWWWWWW WW WW {{ {{-{{'{{W{_{]{[WW{W[Q     "(Q{ u!  v  v   h              -          Z  lo  p  g          *          U 'z55Ch       #\| "j  a d z P       /7`  y        c     8 5 T  | vw [         &3Oe ZWXhG        )-Y| or w } a    )))z  Њu0u0z6HH\ff33̙ff33ff33ffffffffffff33ff33333333ff333333ff33ff33̙ff33̙̙̙̙ff̙33̙ffffffffffff33ff33333333ff333333ff33ff33̙ff33ff33ffffffffffff33ff33333333ff333333ff33ffffffffffff33ffffffff̙ffffff33ffffffffffffff33ffffffffffffffffffffffff33ffffff33ff33ff33ff33ffff3333ff33ffffffffffff33ff33333333ff333333333333̙33ff33333333333333ff33333333ff33ff33ff33ffff33ff3333ff3333333333333333ff333333333333333333ff333333ff33̙ff33ff33ffffffffffff33ff33333333ff333333ff33wwUUDD""wwUUDD""wwUUDD""wwwwwwUUUUUUDDDDDD""""""z/( 8 i 8  3 |1 |1 $ h  ! $- T x h l  l   w  _       *)8@>: B H7        / 71 5 6 ; (     08- 6 6 9)     *BF B@H T=     2 2 ,-- 7'    .I >B  B  L 9       %*"&&*%      +1 D ME F N  W' ?      .:i{z!jw    a      /?Yx  h h h  s  M   ZSZtSZdS S Њu0u0Z[HH\ff33̙ff33ff33ffffffffffff33ff33333333ff333333ff33ff33̙ff33̙̙̙̙ff̙33̙ffffffffffff33ff33333333ff333333ff33ff33̙ff33ff33ffffffffffff33ff33333333ff333333ff33ffffffffffff33ffffffff̙ffffff33ffffffffffffff33ffffffffffffffffffffffff33ffffff33ff33ff33ff33ffff3333ff33ffffffffffff33ff33333333ff333333333333̙33ff33333333333333ff33333333ff33ff33ff33ffff33ff3333ff3333333333333333ff333333333333333333ff333333ff33̙ff33ff33ffffffffffff33ff33333333ff333333ff33wwUUDD""wwUUDD""wwUUDD""wwwwwwUUUUUUDDDDDD""""""TS %{{{"&2...5. &RNFM< B.$Mj V Q U@.9 4G= 6 4( $- 6?9>  >  A 0  .)*&-NSJ**F** H,*)J..A1"J**  B&.5Z PZ PZ P P  Њu0u0XXHH\ff33̙ff33ff33ffffffffffff33ff33333333ff333333ff33ff33̙ff33̙̙̙̙ff̙33̙ffffffffffff33ff33333333ff333333ff33ff33̙ff33ff33ffffffffffff33ff33333333ff333333ff33ffffffffffff33ffffffff̙ffffff33ffffffffffffff33ffffffffffffffffffffffff33ffffff33ff33ff33ff33ffff3333ff33ffffffffffff33ff33333333ff333333333333̙33ff33333333333333ff33333333ff33ff33ff33ffff33ff3333ff3333333333333333ff333333333333333333ff333333ff33̙ff33ff33ffffffffffff33ff33333333ff333333ff33wwUUDD""wwUUDD""wwUUDD""wwwwwwUUUUUUDDDDDD""""""Q P %{{{$(400070 KN$  "+ 6?7<  <  ? .  ujynxgwy|z    vy~    f}x|     ***  Њu0u05wHH\ff33̙ff33ff33ffffffffffff33ff33333333ff333333ff33ff33̙ff33̙̙̙̙ff̙33̙ffffffffffff33ff33333333ff333333ff33ff33̙ff33ff33ffffffffffff33ff33333333ff333333ff33ffffffffffff33ffffffff̙ffffff33ffffffffffffff33ffffffffffffffffffffffff33ffffff33ff33ff33ff33ffff3333ff33ffffffffffff33ff33333333ff333333333333̙33ff33333333333333ff33333333ff33ff33ff33ffff33ff3333ff3333333333333333ff333333333333333333ff333333ff33̙ff33ff33ffffffffffff33ff33333333ff333333ff33wwUUDD""wwUUDD""wwUUDD""wwwwwwUUUUUUDDDDDD""""""5p   !/JYM  L C X<   '5T Yg h  OEXU  D : '9SPXF='?B;8+'834,FOMMMOG12 G  N , !AG H R&&Fc E :I; ; >1BGQ T""KQMQGIC2 -2 2!!!!!!!!!!!!!!!!)-1. 2515< 4em)B )4 )$    Њu0u0m9vHH\ff33̙ff33ff33ffffffffffff33ff33333333ff333333ff33ff33̙ff33̙̙̙̙ff̙33̙ffffffffffff33ff33333333ff333333ff33ff33̙ff33ff33ffffffffffff33ff33333333ff333333ff33ffffffffffff33ffffffff̙ffffff33ffffffffffffff33ffffffffffffffffffffffff33ffffff33ff33ff33ff33ffff3333ff33ffffffffffff33ff33333333ff333333333333̙33ff33333333333333ff33333333ff33ff33ff33ffff33ff3333ff3333333333333333ff333333333333333333ff333333ff33̙ff33ff33ffffffffffff33ff33333333ff333333ff33wwUUDD""wwUUDD""wwUUDD""wwwwwwUUUUUUDDDDDD""""""m9v  >]        w   JaQ  "*" ,*2899;3     Њu0u0$Y  8HHY  6UUU@ 1 @331 U"@331lg-U--.!.63466667G7l8w89 99V9u9:.:;;;<==> >>>?,?n?@6@DDEE<FkFFGHKHiHHR?RfRSSSTfTUUVDVFV]WWYY1YmYYYZ Z"Z]ZzZZ[[\+\=\v\\\^b^_0`<`}  _`}``aaabbcc[cdFddee3egf fAfSfgggh:hWhhhii?ijj3kkl l2lplppq!qr&rPrrss8sost\tttuauuuvNvvwZwwwxx=xQxxxxy5yc}} CQ7C5M:J a3B@NBN> Hsw L$[p*:q^!`u' pI} =SUfWe`9)md iY; 3gb+r,ƆƿS6sȹ:{ɻ̨̩Ί΋ΗЈЊզ"0(q} xgz-C}`a~EEGGIJLM M @5x @ 4 @@   TMMOO9OOPMPwQ?QQQRR([ [([\h[h\jq8rrsGsXsYscsdsyszsssssssssst t ttt(t)t3t4tA|<^  'G#Y@A?   ]?Gabrs͆͐͑͟͞ͅͱͲғ!_opނރލގޛޜޮޯ  j kI4>M^!"rsGbGSŵŶŷ 2:E   @ @  V1MNO56mn+HI` ) *   < = i j ef*N}$%z{ûѻѵѯѯѩѣѩђѵђѵ*0  *0  *0 *0 *0 *0 *0 *0 *0 *0 *0 0*0 II I!I$*0II I!I$*0 901;FS_mtu"#vw67 U V"#"$"""$$' ' ''(())))))**++++++++,|,},,----.....///001ļļļļļ*0 *0 *0 *0 *0 *0 *0 *0 *0 *0 J1111112233334$455Q56-77779999: : ::<<<<>>@k@lBBDDEUEVFFI(I)JJLLN8N9PiPjPRRTTUUWlWmXXYvYwYYZ(ZaZZ[W[X]d*0 *0 *0 *0 *0 *0 *0 *0 *0 M]d]]^_`"`w``abb5cdEdVepe}eeeefNf]hohhhjjjk"kmtuttwwxy1yvyzzzz{||2|\||||||||*0*0*0*0-*0*0 h*0 h*0 h*0 h*0 h h *0 h*0 h*0 h*0 h*0 8|}}}}+05ta*=CIPW_gow&P࢜*0*0*0*0 *0 *0 *0*0*00*00*0*0*0*0*09w8am7f=s4L,KvNO ;mzcd; GXŽ񙙙*0*0*0*0 *0*0*0 *0  @*0 @*0 @*0 @*0 @*0*0*0*0*0*06X0YT< +zÝǣDz$Yʘʙʵ3NDrх Pw ľ}u*00*00*0*0*0*04l#*0 @4l#*0*0*0*0 *0*0*0*0*0*0*0+ ګޙq)R_no_Z "/<I]q 9  Y [    ![|ٻ޶ԧ޻ٻ  @ @ *0*0*0*0*0*0 *0*0*0*0*0*0*00*00*00:IWef_m !     !c!d!""j"$o$p''(()I)))))..../7/8/t/u//2 2223O3P3  @  @ @S334 4!4}4~44445%5&5V5W6778<8=88::;#;$;J;K;^;m;n/>0>>>@@A2A3AaAAAAAABBBJBKByBzBBBBCCC:C;CbCcCCCCCD~DDEE!E5E6EEGQGRGGGH H HhHiHH_HI I I<IiI|IIJfJgKKLcLdPPPPQAQQRRRRRSSMS~SSSSST+T\ToTTTTUmUnVVVPV}VVVW WWCWtWWWXEXFXXYAYBYtYYZZZZ^Z_ZZZ[[%[&\\]/]0]b]]]]^e^f^^^___n_o`Z`[a2aa2a3a~aaaabbbccYclcmd7d8eeeegtghhhhzh{hhhii+i,iijjjgjhkkkxkykkkl l llmAmxmmmnnoYoZooooop p ppqquuy6yIzq}~_~`~~~Dv  @TU;zKL*'l  3"i2;nK]P+`ccdfgD<Xp pYXYSTk mS{xG\'aH34)I~,->?/!̟.~ωϊϔϥϱϽ  @  @  @8pLRklPQk֊ַ֦WXrؑءد#ݽݾ lߺ#)}L }$n;r/0  ]^}\)cUR   @WRb]6t1e6P>t%MDL  C        Z    E x  ( [ 9*\FTW?0f+a\a(!a"D !M!"]"""#3#$$%>%&&''(!(Y())**+T++++,*,+,,,^,_,,,,---<-=->-U---.7.h.i..233?3z344X4445)6h6i66666777m777888A_8A89 9v9:/:;;W;==>>F>?-?@@AAD"D]DDE=EwEF8FFGGWGHHjHHI8IL.LbLLMMAMzMMN NTNNNO3OlOOPPFPPPQ%Q^QQRRgRSSOSST.TTTU9UUVVVVDVEVFV]VVVWWGWyWW_WX)X*X`XaXXY2YYZ#Z{Z[R[[\>\\]N]]]^-^^`=`aabc\cdGde4f fTgg=g{ghXhiiij4jnjjkkQkkl3lplo7ooooop p!pXppqqrQrrs9st ttu*uuvvw[wxxRxxydyy\yz z?zuzz{{}6}7}}}}~F~~$W F?W RDzNKCz"YOO?v A| t<VMZTU1p%q;_H~";I =J gfa:std*`HN3jZ4cš8|ï_ïsĩKłŵƇ7Ⱥ|9:jk̨̩ΊΖΗUզdF "0(q} x6c1gz3FIQz-C @ @ p  @P  O {|}`b~LMN(f Cx!X  C   ! !W!!!!!$n$$$$%-%b%%&&5&i&&&&''T''( (C(}()-)c))* *H*{**+%+Y+++ @\+,-,c,,--;-q--..J.}..//T///0*0b001171l112 2223+3_334 4O4455C5y556&6\666767n7788I8|889"9\999:.:}::;#;$;%;l?3C%EEFIIIJJKKLLMM M M:MQMMMM_MMMMMN N"N1NDNkNNO OOO8O9OXOhOOOOOPP8PNPOPvPwPPPQ Q+Q@QAQQQQQQQQRRRR'R(TMTNT]TsTTTTTYYZZ[ [ [ [[[['[([[\\_t_ub9b:bbbbc+c`cccd.d/dsdt p [dtdde3ekeeff^ffg g@gxggh[h\jjkTkUkkkl%lYlllm)m]mmmmmn#n]nno oCooop#pYppq6q7q8rrrrsFsGsYsdszssssst tt)t4tBtCyy|||}}L}}~~F~|~~1hF\F}3|KH}i]I*]NO;<]^&' 6[z%Di^'0Jaz9Ph$?(@A@As 8j O QPCu9&YëZĒ0^p?@FGXbs͆͑͟Ͳ  Ғғ2iӛ/ԃԵ ;mթ-_ֳ-_ג*\؉ؾD٘Uڇ =m !^_pރގޜޯ00[WX@zF~&[A{)`!Y>s(f#tX D&aNJCMN&\>{ D|;ra,he=|,JN)p)v G O   9 t   1 f   $ kX?NCz"\lz~º*0 h*0 hh*0 h *0 h*0 h*0 I&programhang2hanghoserCodeList p   @( @ ( @  ================================================================================ Faces Search for:4240 4840 80FC 0030 4840 3D40 Change to:4280 etc. It will ask you who beat Napoleon at Waterloo. The answer is Welling. or This program was pretty tough to crack until I first did Welltris. Why? Because it takes your password, stores it in memory, then compares it to the correct answer (only the first 4 digits, making the password a nice simple Long). However, if you do a simple crack it will then say bring you up to the screen with the start game button. If then checks the password again - if it's wrong, then the program corrupts itself (thanks to SAM for telling me that!). This is very similar to Welltris, so I was armed for the job.So, after the _GetIText the program pushes the return address (A5-142A) onto the stack, then your password (A5-113). A JSR then stores your password with only the first 4 bytes, in lower-case at A5-142A. My simple crack simply pushes the correct password (A5-12C), instead of your password. Then, when ever the program compares what should be your password, it's actually comparing it to itself! Har har. KRAK PROCEDURE The protection in Faces is fairly typical. The password dialog does not affect your game, it just compares your passwords, and has a local variable to say whether to quit or not, and it also stores your password elsewhere to be checked later. The GetIText was also really close after the ModalDialog to make cracking quite simple. This double password thing had me confused for a while because occasionaly it would corrupt on me, so I had to open the damn archive again! I knew where to go in MacsBug, yet I just could figure how it knew to kill itself! This crack was not too advanced, yet not too simple. I thank it's creators for making the PEA so simple to find. Although this program had no definate crack point, such as an _ExitToShell, or a BNE/BEQ/TST, etc. it was fairly obvious with the BLT that it's only concerned with the first 4 chars, therefore making it easy to find where it is actually moving and comparing memory. I couldn't find the exact place to not show the Dialog, so I merely jumped over the ModalDialog routine, so you will see it flash onto the screen then disappear. That's OK. Now, since I don't have a color Mac, I had to absolutely guess at cracking that one. It looks almost exactly the same in the copy protection routine, except it's merely a few bytes down in CODE 3. If the crack doesn't work, don't blame me, just call me at christmas, and maybe I'll have a CQD machine. Here is the complete crack: CRACK PATCH Open Faces 1.0 with ResEdit Open CODE 3 Change CODE 3+$652 (just a few characters over from $650) from: 486D 01CA to: 603A 4E71 AND Change CODE 3+$694 (just a few characters over from $690) from: FEED to: FED4 Open Color Faces 1.0 with ResEdit Open CODE 3 Change CODE 3+$7C2 (just a few characters over from $7C0) from: 486D 017A to: 603A 4E71 AND Change CODE 3+$804 (just a few characters over from $800) from: FEED to: FED4 ================================================================================ Image Express Background ~~~~~~~~~~ Image Express uses the Eve Protection Scheme. Basically the protection scheme consists of 3 parts. 1) The Hardware device, 2) The Eve Init, and 3) the code inside each application which compares the values. The hardware device which hooks up to the ADB port houses a chip that I assume has a value or a resource on it. I say assume because I do not have one in my possession to check out. The Eve init is basically a driver that gets installed onto the Eve at bootup. And remains there until powerdown. The Apps which are protected with Eve will read from the Eve and compare values, if they match it will continue on with the program as normal if they don't then a dialog will surface telling you that you either hooked up Eve wrong or the init is not installed. If there is no Eve hooked up the same dialog will surface telling you there is no Hardware key hooked up. etc. Image Express is made up of 9 files in all. They are Camera, Demo Projector, Image Express, Transporter, Image Express Launcher, Camera Launcher, Projector Launcher, Transporter Launcher and Eve Init. All of these Apps except Demo Launcher are protected and have checks for Eve in there Apps at least once. The launchers all use the same exact protection scheme but I have not really worked on them too much and so I have not cracked any of them. The Eve Init is basically only values and some code in the crack I am working on the Eve Init will no longer be needed. The Camera application is somewhat tougher than the other applications; I have yet to crack it but I am slowly making progress. I have cracked the Image Express App, the Projector App and the Transporter app. Here are the hex changes: Image Express Search:3B5F F33A 4A6D F33A 6622 Change: 4E71 Search:4EAD 0B62 4EAD 166A 4EAD 16DA Change:4E71 4E71 4E71 4E71 4E71 4E71 Search:4EAD 257A A9F4 4E5E 4E75 Change: 4E71 Projector: Search:4A6D FDCE 664C 4EAD 056A Change: 4E71 Transporter: Search:3B5F FBB8 4A6D FBB8 6600 01E6 Change: 4E71 4E71 Search:57C0 4A00 6700 010E Change: 4E71 4E71 ================================================================================ Infini-D 1.0 01-1400-6350 31-9326-1679 Infini-D 1.02/1.1.1 31-9326-1679 Infini-D 1.02 Protection Scheme: Serial Number Supplier: Far Side Problem : Ok, a cool user on my board, Far Side, logged on new and since I was around spying on him, I decided to validated him before he got on. I should do, Infini-D1.02. I unpacked it and booted it sometime thereafter and noticed that the first thing that happens is a Serial Number Registration box comes up and asks me for my Name, Organization and Serial Number. Ug. Playing around with it, like I usually do, I noticed that if you try to hack out a Serial Number, it will just stay there like nothing happened. This is useful in determining how to go about cracking it because now I know I can not just search for an _ExitToShell Trap (Hex-A9F4). By the way, I tried putting in some earlier version's serial numbers but they do not work on the new version (1.02) at all. Solution : So I cancelled out of the registration thing and got back to the Finder where I jumped into MacsBug. Then I set a trap for _InitGraf (Hex-A86E) which is usually the first Trap executed by every program to init the graphics screen and what not. From here I double-clicked the App (Infini-D 1.02) and the cursor changed a little and found the trap. I scanned through a little, going nice and slow as to be careful noticing which JSR or JMP or branch would be the one to go to the Registration dialog. I went a little further, then it happened, so I went into MacsBug and found this as the last instruction before the dialog box appeared: Addr Instruction Hex Bytes Who Cares JSR $0798 4EBA 0796 Ahh! How easy? It's a JSR, so all we have to do is change those bytes (4EBA 0796) to two No Operations (4E71 4E71). This crack was extremely easy, to tell you the truth. But it was fun none the less. I hope you enjoy it. Hex Changes: Search for: 4EBA 0796 (You should find this 2 times, at Sectors 392 & 3FE) Change to : 4E71 4E71 ================================================================================ Painter 2.0 0011187QBO (The last 3 are letters) It's a bit of a tough one for you to try as a first crack, anyways i looked atit briefly the other day and this seemed to work, later i realized that saving and printing didn't work and so it must have gone to demo - i made no attempt to comprehend or decompile what the hash routine was, rather i simply tried to disable ever single check, so that wrong means right type thing. I'm sure this is very close to the real thing that is needed. Painter 2.0 GWIL GWIL main CHANGES +21A +FE 6718->6018 +286 +16A 6FB8->4E71 +2E2 +1C6 6FAC->4E71 +322 +206 6600 FEF8 -> 4E71 4E71 +3DA +2BE 6EB6->4E71 +40A +2EE 670C->4E71 CODE 2 +12EA 6600 FE46 -> 4E71 4E71 +131C 6FDB -> 4E71 +134C 6FD8 -> 4E71 oh and a tip , whenever using this type of crack info use resorcer instead of resedit as it has a better faster interface. Also whenever you encouter code in a non-"CODE" resource, simply go to the preferences in resorcerer and tell it that , in this case "GWIL" is a pseudonym for "CODE". ie add "GWIL" to the list, then you can view it as a code resource. ================================================================================ Panorama II Crack I jumped into MacsBug and set a trap for _InitGraf, which is normally how ever program starts off. It went fine and I traced the code, but unfortunately, after tracing through, it killed me back to the Finder. They obviously protected against tracing. No prob, I clear the trap table, and then rung up an _ExitToShell trap. Then I ran the program and hit return and MacsBug caught the _ExitToShell call being executed. It looked something like this: Addr Instruction Hex Bytes 004A55A4 _ExitToShell A9F4 004A55A6 CMPI.L #$00000001,-$6F52 (A4) 0CAC 0000 0001 004A55AE BNE.S +$001C 661A So, I wrote down the hex bytes for a backup and then went to DisAsm to scan for _ExitToShell traps (A9F4 in Hex). I found 2 right off the bat but they weren't the ones I was interested in because they didn't have the same bytes following them as the code above. So I kept on searching until DisAsm bombed into oblivion with an "Out of Memory xxxx" error or something or other. I hate that! In any case, I booted up my FEdit+ 3.21 and searched for the bytes I needed and found them on sector 494 of the Panorama Application. So I changed the ExitToShell Trap (hex A9F4) to a NOP (hex 4E71) and booted the program. The configuration dialog came up again and when I hit return, the program didn't quit but went on to the rest of the program the right way. The APP was CRACKED! Yes! But like every good cracker knows this crack was half ass because the dialog should not even come up in a well cracked ware. So I went back to FEdit and looked around at where I made the changes. And found out a couple of bytes before where I made changes was where the program put the dialog up. So here are the complete changes to the COMPLETE Panorama II Crack: Hex Changes Search for: 6730 4EBA 02E8 4AAC 90AE 6602 A9F4 (Found on sector 494) Change to : 6730 4E71 4E71 4E71 4E71 4E71 4E71 And the sn# is not the only thing you need to run panorama II. Your Name, Company, Phone #, and SN# are coded into the application so you need to use the crack. But if you want your name SN# etc. in the program just look for the Panorama II prefs in the Prefs folder and if it's there delete it. Then run a virgin copy of Panorama II and type in anything you want and say (OK or Cancel) then patch the Program and the patched Program uses the Pref file you created in the Prefs folder and Voila QuickFormat 7.0 Protection Scheme: Key Code Required to use special features Scenario Ok, I was calling around a few boards and as usual I make my rounds on the Buzzard's Nest(s). When calling BN Central I noticed someone posted asking me to remove the protection scheme from Quick Format 7.0. So I got it, and I have finally gotten around to cracking it today. Oh well, sue me! Problem When launching the program an annoying dialog box comes up asking you to register the program with a KEY CODE to use the advanced features of the package. If you typed in an INVLALID key code it will let you use the program with the few less sophisticated functions. Solution So I quit out of the program and then jumped into MacsBug. I set a trap for the _INITGRAF (Hex-A86E) and I double-clicked the Quick Format 7.0 Application. I traced through to a very suspicious part of code that looked something like this: Addr Instruction Hex Bytes 583834 JSR SETUPMEN 4EBA FE14 583838 JSR INITIALI 4EAD 02E2 58383C JSR INITGLOB 4EBA FEBE 583840 JSR VIRALCHE 4EBA FF22 583844 JSR CHECKMOD 4EBA F82A Now, its pretty obviouse from just looking at the labels that they used that you can determine what is going on. In most cases people would not use LABELS like the ones used above, but since it is shareware and not a $500 commercial package I can see why the author opted the easier route for programming ease. The first JSR would probably be him initializing his menus and stuff. The second JSR would be to initialize the screen and the fonts or whatever, the third JSR would be Initializing the global variables he would need and the fourth would be to check for any virus, persay. The fifth however is the routine he uses to check if the program has been Registered and brings up the dialog asking you to enter a key code. If it hasn't be registered with the correct keycode the program turns off some options. But, that is not necessary, as by omitting this JSR CHECKMOD you will remove the check and the program will run with all options available. Neat, eh? Byte Changes (You should find the SEARCH string only ONE TIME!) Search: 4EBA FE14 4EAD 02E2 4EBA FEBE 4EBA FF22 4EBA F82A Change: 4E71 4E71 QuickFormat 7.1 CODE 3 +$3B6 From $6606 to $4E71 ================================================================================ Railroad Tychoon Find Hex string 0684 6646 486E and replace it with 0684 4E71 486E. When you hit the choose the locomotive screen, any choice you make will be valid. I've since heard that the krak listed above allows the game to continue to limit your play to two trains at a time. A bad krak in other words. Protection Background I sat down here a few hours ago with the intention of blasting a quick hole in the protection. It's now five in the morning. This is a questionable krak. The first anonymous attempt reportedly didn't work. I'm not sure if this second attempt (mine) will work either. Understanding why means taking a much closer look at the logic of the program. Krak Procedure The protection in Railroad Tycoon is not typical. The screen that comes up asking for you to identify the type of locomotive is crucial to the operation of the protection. It's also very difficult to remove from the flow of execution - at least I haven't been able to sidestep it. Your choice sets up certain values in certain global variables for the program to use later. There are two separate response handling routines. One routine, called BCONTENT (accessed by a JSR CA(A5)), handles the response if you click the mouse. This routines highlights the name choices of the locomotives as you click them. It will also handle a click on the OK button. The other routine, called BKEY (accessed by JSR E2(A5)), handles keyboard input. I suppose it's possible, though I haven't tried, to choose the locomotive name under the direction of the arrow keys. This second routine also handles a return (hit OK). This double routine thing had me confused for at least an hour because I would randomly (and unconsciously) choose the OK button, sometimes with the mouse and sometimes with the keyboard. I kept setting breakpoints where I knew execution would occur (I knew this because I had painstakingly traced through the whole code block, picking out good breakpoints), but I had only done so for the mouse routine. I kept confusing myself by randomly selecting the OK button with the keyboard (sometimes TMON would break in, sometimes it wouldn't, on a seemingly random basis)! I began looking at the protection with the idea that the protection was a subroutine. It is not. The whole initialization of the game is all in one routine, with the protection at the leading edge of the code segment. This pre-misconception (at midnight) proved time consuming. (I'm telling you about all these traps I fell into so you can use the experience in your kraks.) The fact that the protection is built into the main initializing code segment makes the whole approach to kraking it much more involved. As I said above, I was looking for a "quickrak" [hmmm, a new word], but this just was not going to be the case. I stubbornly kept looking for a definitive krak point - one of those luscious, helpful, welcome conditional branches that, when satisfied, completely jumps around an undesirable bunch of code (the code bunch that says, "Nope. You're either blind or a pirate with no manual, so you will have only two trains"). At about four o'clock, it finally dawned on me (pardon the pun, the sun is just coming up now as I type this) that the protection was hard-wired into the logic of the program. When I say this, I mean that there is not any clear-cut result from your choice. In particular, this protection scheme (as I said above, or at least think I said above) takes your choice and puts a bunch of corresponding values into a bunch of global variables. Later on, the protection code then goes on and does a bunch of math operations on those values to come up with, not a condition, but data addresses and pointers for a dialog box. The protection always draws a dialog box (regardless of whether or not you make the right choice). The only thing that is different when you make the right choice is the contents of that dialog box, as computed from the numerical values assigned to your choice. Are you confused? The protection is not saying yes and it's not saying no. It's using your choice to figure out what to say in its dialog box (the gray one that pops up right after you make your choice). Because figuring out all that crumby math logic is way beyond my effort limits (especially for a game like this), the program always fills its dialog box with the message corresponding to an incorrect choice. The protection always tells you that you will be limited to two trains and there is nothing I am going to do about it. A bit farther down in the code from the _ModalDialog (the trap call which waits for you to acknowledge reading the dialog box's message with a return) is one of those luscious, helpful, welcome conditional branch statements I'm always looking for. Need I say that I was very happy to see it? I changed the BNE at "BSWITCH"+AC4 to an NOP. Here's where the ambiguity comes in. I think this modification forces the program to always act as if you've made the correct choice for the locomotive's name. (If this modification does in fact do this, then it constitutes a "krak"! If it does not, well, then it's your turn to take a shot at this thing.) In more words, despite the fact that the program's dialog box says you've made the wrong choice and will be limited to only two trains, this little "krak" makes the program a liar. You should be able to play without any software protection limitations to the number of trains. To be honest, I do not have the patience right now to figure out the game, to play it, or to test the krak. I'm way too tired. Additionally, I feel fairly certain that I will not have the slightest interest in figuring out the game, to play it, to test the krak tomorrow, or the next day, or even the next day. (I dunno. I'm hedonistic when it comes to computer games. People tell me Robot Wars and SSI games are great. I tell them they're nuts - I'm action arcade all the way. Besides, nothing turns me off faster to a game than a pathetically slow opening graphics presentation, and Railroad Tycoon definitely takes the cake (and whole damn cooking pan and spatula) when it comes to pathetically slow opening graphics presentations! Have you ever seen anything slower?!?) In all fairness (actually curiosity), I went back and looked through the game for the krak patch that Grimm posted. It's there all right, but I believe it is in the wrong place. If you search for what he says, but search for it twice, the second occurrence is remarkably close to where my krak patch is. Krak Patch Hex search for: 28 00 02 B0 69 06 84 66 08 08 AD Hex change the bold values to 4E 71 This should appear on sector 301, byte 25848 of the file. or Find Hex string 0684 6646 486E Replace it with 0684 4E71 486E When you hit the choose the locomotive screen, any choice you make will be valid The krak listed above allows the game to continue to limit your play to two trains at a time. ================================================================================ Claris Resolve 1.0 Protection: Date Expiration Supplied by: Zelig Problem: Resolve will expire August 10, 1991 unless a valid serial number is entered. Solution: I was having problems finding out where the check was taking place so I went the easy route and set a trap in MacsBug for an _ExitToShell (A9F4) and then back traced using "IL" to list the code before the _ExitToShell. And this is what was revealed: PEA $FFFC ;486E FFFC JSR Timebomb ;4EBA FCF6 Now, this is extremely suspicious. All you have to do is change the JSR Timebomb to NOPs and a branch right afterwards that will make Resolve work forever. Hex Changes: Search : 486E FFFC 4EBA FCF6 4A00 588F 670E Change : 4E71 4E71 600E ================================================================================ StuffIt Lite 3.0 If you have a registerred copy of Stuffit Lite 3.0 and you want to change the registration info, change the serial number that is in the data fork of the Stuffit Lite 3.0 application at the offset 836 ($344) from the start of the data fork (which is at the end). The registration name is in the middle of the data fork. If your copy of Stuffit Lite 3.0 has been patched by the Stuffit Lite 3.0 crack program, you can change it back to normal by changing CODE 13+$1208 from $4E71 to $6622. Unpatched Stuffit Lite programs won't have the same CODE 13 resource (see next paragraph). I tried this patch before I got the Stuffit Lite 3.0 crack and found that it didn't enable encryption or multiple open files so it was useless. I threw away the Stuffit Lite 3.0 crack because of this and it did the same thing I already tried. Stuffit Lite 3.0 contains some compressed resources that include most CODE resources (including CODE 13) and some DITL resources and probably others. (This is why trying to compress the file only gives about 3% saved.) This compression effectively scrambles them so they appear virtually meaningless. Stuffit uncompresses them when you run the program. The CODE 13 resource that was used in the patch was an expanded version of the CODE 13 resource in unpatched programs. The person who made the patch got the uncompressed resources from memory when the program was running and worked with them. (I made an FKEY that will retrieve resources from memory). You can't patch your unpatched program without the expanded CODE resource or the crack program. Surprisingly, Stuffit doesn't care if it's resources are expanded even though they should be compressed. Here are the serial numbers. They were created using numbers from 0 to 199. (most should work): L297000000 L347000001 L397000002 L447000003 L497000004 L547000005 L597000006 L647000007 L697000008 L747000009 L267000010 L317000011 L367000012 L417000013 L467000014 L517000015 L567000016 L617000017 L667000018 L717000019 L237000020 L287000021 L337000022 L387000023 L437000024 L487000025 L537000026 L587000027 L637000028 L687000029 L207000030 L257000031 L307000032 L357000033 L407000034 L457000035 L507000036 L557000037 L607000038 L657000039 L177000040 L227000041 L277000042 L327000043 L377000044 L427000045 L477000046 L527000047 L577000048 L627000049 L147000050 L197000051 L247000052 L297000053 L347000054 L397000055 L447000056 L497000057 L547000058 L597000059 ================================================================================ Fileguard How to beat Fileguard!h  xxbCODEPCODDLOG 2ALRTDITLSIZEVERSGRPHICONhfdr*PREC6dctbBversNSTR fCOLRSTR#helpFONDFONT&WDEFzSIEM>Yʹhw2=bdeg  ( . @  @  ~4DRVg=$r U5Zz(C^)<F<RĶ*0 h *0 hp *0 hp*0 h *0 h*0 h*0 h*0 *0 hB 5Qmt"sGb}GSØhŶŷ 2:EN>Ywɕɳ˾மমvvvvv *0 hTx *0 hTx*0 h*0 h*0 *0 h*0 h *0 h *0 h *0 h*0 h*0 h*0 *0 h*0 h*0 h.ʹ.@Prhw7٨GMa2=Mn   <Or(9ypͽͭ͵𝝝՝ͽ *0 *0 h*0 *0 h *0 h*0 h*0 h*0 h*0 *0 h*0  *0 T@ *0 hT@*0 h *0 h3(j0r8z 8q*0 h*0 h*0  Step one: Make a system disk with norton util, AND HDT Restart machine holding down command-option-shift-delete Run HDT, select "Install HDT Driver" (on the protected, but unmmounted volume) Restart, File guard wont bother you anymore. i@ f7(-4Q=JRVm_io%u{n} ٌٴ*S}s +<$u&1?MU`7er~t!yz_ENȇD͕ڐ 3  &)C+5S@MY{e{nzBM1τ՛rXP&y+/7DvJR\mfoRu"xr.I(Mܷ i } |   {P !@M"1,B' ~i !I"#$%g&S'4(')*)+,w-U./0123456789=:2;<Y=>?s@@:s;<=>?@ABCWDYEFGOHImJ+PKg@HlPI;@JnPK]@L5PM@NPO9@PXPQ@RYPSm@T=PUU@VaPW@XPY#@Z3P[ @\P]@^ P_@` Pa@bqPc@d-Pe6@f3PgM@h9Pi@jPk@lPm@nPo@pbPq3@rJPs@tPu@vPw@xbcEcKcccgog~hshk&kmmm*mFmRmmmmmmmnn*ndntnnnnno/oFo}opp2ppqqqrr r;rrrrrsJsst#tBttuuUuuv5v6wiyXyfyyyyzz&z1zQzgztzzzz{E{n{{{{~c~z9HZu X`1MubGWz%CaDSd}8oĒ"\_~ƝLǝǰ59Nf;gʸ5o`ӬDԆ LՎT֥֦֨֩؊ؼ$7AGZkxٌٿ02q7HLPUZ_dins $-7AKU_iu0JKuB/~/Bw1j      +   1 ^ m  F<MN  A!"$G$V$e$t$u&'*,..)//1345}7O8;=?@l@rDrKNORU(Z]`7ah3jlXp-swt1ulu{uuuv v vNvPvvwwwZw[wwx-x.xSxjxxxxy%y&yKyeyyyyz$z%zAzszzz|A|B|g||||}G}H~~~~$^_xy)u[\[\jk\]!"Z[jnoLMwx%uLM]^gh12YZST12Z[\"#muv@  `a  34  {|ars"0C&'!4GH+>?xyN)[*=PQ  R"OTU¢£bDmŚ-.Ƥƥ  ǹǺSȇȚȭȮɃɄ/Bʷʸ78ë̛̮̯ͣDZѬѺQRۅ۶KL܂܃ܼB݋Qބ޺7_$j8ur ,STVWp^FZ%K>  [%N q      #}?@Ka6u:OT<uv$j"#kly#')C,",#,-,.,8,I,U,a,g-..12233.3J3[3l3m445555E5S5h5}5~68:a:b::;<<^<<==>>?(@!@A]AB!B{BC)CsCDDiDDEEGEHEEEEEF#F$FFFGGGdGH!HqHrHII7IILMN2NNNO,OOPtPPRRRRSS[STUUV VWX9i*0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0*0-*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0 *0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0 *0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0 *0*0*0*0*0*0*0*0*0*0 *0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0 *0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0 *0*0*0*0*0*0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0&*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0 *0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0*0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0*0*0*0*0 *0 *0 *0 *0 *0 *0 *0*0*0*0*0*0*0 *0 *0 *0 *0 *0*0*0*0 *0 *0 *0 *0 *0 *0*0*0*0*0*0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0*0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0*0*0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0*0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 *0 +S`}M?E%&'()*+,-|1]d|X 3Ha2XRa8AWyï+MdtF~./0123456789:;<=>?@ABCDEFGHIJKL}~ +|+}3g333889:1:3::KHP(HPP  '=/B@P -:StyleWriter ChicagoNew YorkGenevaMonacoLondonPalatinoTimes HelveticaCourierSymbolBelmontArnold Boecklin Fette Fraktur MT Extra+|InsigniaLQmono+}InsigniaLQprop3gShelley Allegro Script3 MediciScript3Parisian3Umbra8Utopia8 Utopia Black9Linotext:1Hobo:3 Cooper Black: Zapf Chancery:N Helvetica NarrowK Chicago NTSCo.o.o.\bo$o.o/oEp2pprrz{D{E9FGRSǜǝʸ֥ii dE^fNrsŵyŶŷ  w(z j