---=== PalmPilot reversing Tutorial ===---
By AY ;)

Preface:

Yesterday, a few friends of mine asked me how did I cracked a game they were playing with on their PalmPilot PDA. I tried to explain in general the steps needed in order to crack a program, but my explanations sounded too vague. Since it's late at night and I'm board, I decided to write a small tutorial explaining the general process of reversing a program, and give example on a specific target. This tutorial is targeted to people with no experience with reversing at all, or for people who are not familiar with PalmPilot reversing.

I assume that the reader is somehow familiar with how computers works :)
You don't have to fully understand the structure of the Palm OS and the hardware architecture of the PalmPilot, but familiarization with a basic CPU (did anyone mentioned PDP11 ? :) and memory access can help.

General steps for cracking a program:

There are 2 main approaches for code reversing: dead-list and live. Live approach means that we use a debugger to debug
the program while it's running (similar to what we do with high-level languages like C++), and dead-list approach means
that we try to understand what the program is doing by looking at its assembler code.
When dealing with Windows cracking, most of the time we use both a debugger and a dead-list, but in the Palm environment, most of the time a debugger won't be necessary.
Here we will deal only with dead-list approach, working with PalmDebugger requires another tut :)
A note about assembler: when dealing with reverse code engineering, it is assumed that we don't have the source code of our target (otherwise we would simply change it and recompile). Since the only thing that we have is the executable (a prc file in the case of PalmOS), all we can do is disassemble it and get the program code in assembler. In our case, we deal with Motorola 68K assembler (unlike Intel's x86 assembler), since the Palmpilot CPU is based on Motorola's Draggonball series CPU (a variant of the 68K family).
We don't need to know by heart all the CPU instructions (although it helps), and later I'll describe the most important.
Here are the general steps we follow when we want to understand the protection mechanism and finally patch the program (change it's code in order to change its behavior):

1. Run the program on the emulator to understand its behavior and protection mechanism (function limitations, serial number, expire date etc...). Try to force the program to expire by altering the system date or by start it on and of several times.
At the end of step 1 you should know if the program is limited in any way, if it is, what are the limitations and if there any way to remove those limitation through the user interface (entering some serial number, installing additional files etc...).
There is a small chance that the program is only a DEMO and not a TRIAL, meaning that it is a crippled version (the full version is actually bigger, means it has additional code not included in the demo version). In this case, we have nothing to do with it, since we are not going to write the missing code by our self.
2. Write down the resource IDs that interest us (Alert or Forms).
3. Disassemble the program.
4. Try to locate the interesting part in the code (dead-list) usually by searching for the "interesting" IDs or by searching for suspected strings.
5. When you found the relevant code part - try to understand what it does (here we finally need to use our brain).
6. Patch the file where you think it will fix what we wanna fix (not before making a backup of the original file).
7. Test the patched file on the emulator (never on the real device!)
8. If successful - write down what you did and install the program to the real device
9. If not successful - restore the original file and go back to step 5.

Structure of PalmOS executable (prc):


Before we go into details about each step, I'll shortly describe how a palm program works.
Palm files are divided into resources. Those recourses can be Forms, Alert, Bitmaps, Icons, Buttons and actually everything you see on the Palm screen. Of course there is also the code section within the file as well as other data sections.
The most common resources are:

Form - a "window". for example, whenever you start a program, a new form is opened. A form contains form objects like buttons, bitmap, labels, fields (text or numeric) etc... each object has an ID.
Alert - a "Message". looks like a popup window that appear in response to some action you did, usually notice you about some error or any other info and allows you to press "OK" button.
Menu Bar - All the menu tree is kept in its own resource.
Bitmaps - all bitmaps (b/w and color and icons) are kept in a resource.
Strings - string can be kept in a resource and be referred from the code using a unique ID.

there may be some other resources, but those are the most common. Usually what interest us are Alerts.

To see what we are talking about, let's take a look at a screenshot of PalmdeMON (a resource editor and disassembler that will be discussed later):

The resource list on the upper left part.
On the lower left part we see a preview of an Alert that is currently highlighted.
On the right window we see the disassembly.

Tools we gonna need:

Before we start the real work, we gonna need some tools, find them on the internet (google is best place to start):

Palm Emulator - also known as POSE - Palm OS Emulator
hex editor - I use UltraEdit
disassembler - palmdeMON by carpathia and Pilotdis (pilotdis.exe)
resource editor - palmdeMON or PRCedit
debugger - Palmdebugger (not necessary at the moment since it won't be discussed in this tut)
paper and pen - most important :)

To the work:

The target we are dealing is a Monopoly game. You can get the original file here.

Step 1: getting familiar with the program

Load the file to POSE and run it. After the opening screen, you will see a form titled "New Game" with the buttons PLAY and CANCEL.
Press PLAY. Another window appear (note that this window might be a Form or a special alert called Custom Alert) with a title saying "Welcome". This window has 2 buttons: "Try the Demo" and "Enter Serial":

Press "Enter Serial":
A new form appears titled "Enter Serial Number". When you try to enter some number and press the OK button, you get an Alert saying "You have entered an invalid serial number".
Ok, at this point we have enough information to suspect that this program is protected by a serial number, and if we will enter the correct serial number, not only that it will not ask as for a serial number each time we start a new game, but probably other limitations, that we currently do not know what they are, are gonna disappear.
To finish this step, we will try to make the program expire. Goto the palm preferences panel and change the date to one year later from now (not the PC date, but in the emulator, goto Preferences from the main palm application browser and do it from there).
Try to run the program again - nothing has changed. So, this program must use some other kind of mechanism for expiration.
You don't have to try this, but let me tell you that if you will start 10 new games the program will expire. After 10 new games, the "Welcome" form/alert is changed to a "Demo Period Expired" Alert:

We can see that now we don't have any choice but enter a valid serial or exit the program. It won't be a wild guess to think that there is some counter that is being advanced every time we start a new game.

How are we going to deal with the protection ?
There are several things we can do in order to "fix" the program. For example, we can try to force it accept any random serial number we enter and by that register it, or, we can try to make the program not expire after 10 games without entering a serial number. In the last case, we will also want to make the "Welcome" form not coming up every time we start a new game.
Here I'll demonstrate the second option. At the end of it we will have a pre-registered program (since the Welcome form will never come up, and the user will never be asked to enter a serial number). In order to do that we will deal separately with the Expiration after 10 games, and with the Welcome form. Disabling the Welcome form only, will not necessarily make the program stop counting the allowed 10 games.

Step 2: digging out some resources

Run palmdeMON and open the prc file in it. Start browsing the Alerts looking for familiar stuff. Note that the status bar at the button of palmdeMON window shows the resource ID and the file offset (later on that). Numbers that begins with the $ sign or that looks like 0x123 are Hex numbers.
You can find the "Demo Period Expired" Alert easily in the alerts list. Note that the preview window looks a little bit different than the alert we got earlier (there are ^1 and ^2 signs). Those signs are simply text placeholders (something like string pointers) that are addressed during runtime. Write down this alert resource ID: Resource ID: 7904 (0x1EE0) File Offset 222090 (0x3638A).
6 Alerts bellow we see Alert named "Thank You!". Highlight it and look at the preview: "Thank you for purchasing ^1 ^2" with OK button. Hmm... this is probably something we gonna need (you can guess that this is the alert we should get if a valid serial is entered). Write down its resource ID: 7903 (0x1EDF) File Offset 223786 (0x36A2A).
A note about terminology: in reversers jargon, this is what we called "GoodGuy" and "BadGuy" - u can figure out which is which...
Look at the "Welcome" Alert, the preview says: "This limited demo of ^1 ^2 ^3". This looks just like our Welcome form. A quick look at the Forms list shows that there is no other form looking like this. This is a special case of what's called Custom Alert (an alert that has more design options than the standard alerts). It looks similiar to a Form, but in fact its Alert resource.
To conclude what we know so far:

"Welcome - This limited version..." Alert ID      = 7901    ($1EDD hex)
"Serial number failed..." (BadGuy) Alert ID       = 7902    ($1EDE hex)
"Thanks for purchasing..." (GoodGuy) Alert ID = 7903    ($1EDF hex)
"Program Expired" Alert ID                              = 7904    (@1EE0 hex)

As mentioned before, we will first take care of the expiration "problem", and later disable the "Welcome" Alert.

Step 3: Disassembling

At this point we gonna disassemble the program and start looking at the code. As palmdeMON can disassemble the file, I personally prefer the disassembly generated by Pilotdis (a command line utility). You can look at the disassembly in palmdeMON by highlighting a code part in the code section (on the resources list) and dbl-click it. You may notice that the code is divided to several parts - I will not go into the reasons for that here.
In order to generate the disassembly with Pilotdis, simply open a command window and type Pilotdis <name of file.prc>. You will get "name of file.prc.s" file. It is very convenient to associate .s files with your favorite text editor, since it's pure ASCII file.
Now that you have the disassembly, also known as "Dead-List", you can start exploring it.

Step 4: Find the relevant code section

There is no need (and no chance) to fully understand the whole dead-list. Actually, there is no point in trying to understand how the program works only by looking at the dead-list, since you can simply play with it on the emulator. The first thing to do, what mostly takes the longest time, is locating the relevant part in the code that interests us.
We will start looking for the "Program Expire" Alert. A good way to start looking for it is by searching for its resource ID.
Note: in the Pilotdis dead-list, numbers that starts with the # sign are Decimal, while number that starts with the $ sign are Hex.
A search for "$1EE0" shows several occurrences. The first occurrence is at address 12aa (this is the file offset that was mentioned earlier - not the real memory address that is used during runtime):

000012aa     3f3c1ee0              MOVE.W          #7904!$1ee0,-(A7)
000012ae     2f2f001a              MOVE.L            26(A7),-(A7)
000012b2     4e4fa180             TRAP                 #15
000012b6                                DC.W                 sysTrapFrmGetObjectIndex

On the left most part we see the address (file offset), next comes a hex number which is the opcode. This hex number is the binary interpretation of the English commands we see next.
The line MOVE.W #7904!$1ee0,-(A7) stands for Move Word (there is also move.b - byte, move.d - double, move.l - long etc...) valued 7904 (decimal) to data register A7 (one of the address registers).
The next line does similar thing (move.long a data found at 26 bytes offset of current A7 value and store it in A7).
Trap 15 is like Interrupt in PC. It commands the operating system to perform a build-in API (this API is built into the operating system and the program coder doesn't have to worry about its execution). From the Trap name, we learn that the above 4 code lines get some Object Index (something similar to Object ID) from an object whose Object ID is #7904 (Object ID and Object Index are different things).
If you scroll down a bit, you will see more traps like "sysTrapFldGetTextLength". For some reason, it doesn't "smell" right... what does text length has to do with our "Program Expired" Alert ? We can guess that this occurrence of "$1EE0" doesn't represent our Alert. This could simply be an object (most likely a text field) inside some form, which happens to have the same ID as out Alert. Note that there are no 2 Alerts with same ID, but it is possible to have same IDs for different kind of resources.

A Note about CPU registers:
The Motorola 68328 Draggonball CPU has 8 Data registers, numbered D0 through D7 and 8 Address registers numbered A0 through A7. There is also the PC (Program Counter) register which is same as the IP register in Intel based CPUs, the SR (Status Register) which is same as the Flags register in Intel's CPUs. There are 2 more registers which keeps data on the stack (USP and SSP).
In most of the cases you'll see that A7 is used for immediate instructions, while the rest of the address register usually keep pointers to relevant data. In the Data registers, D0 and D1 is most commonly in use.
If you are already familiar with x86 processors (Intel), you might know that x86 assembler is written in reverse, hence MOV AX,DX will move copy the data in register DX to register AX. In 68K assembler this is note the case. Operators appear the same way you read them (from left to right), hence MOVE.W D3,D0 will copy the data in register D3 to register D0.
For more information about the Palm CPU architecture, see this link.

Keep searching for out string ("$1EE0"). The next occurrence looks similar to the first. Keep searching until you reach the last occurrence of the string. Here are a few lines of code just before our ID (I manually added remarks to make the code more clear), note the Trap at the last line:

00001b72 0c430002         L165         CMPI.W    #2,D3                                       // check if the data in Data Register D3 is the decimal number "2".
00001b76 6632                                  BNE           L168                                        // jump to Label 168 if D3 =! 2
00001b78 48780000                          PEA            $0000.W                                 // push the data pointed by address 0000 (hex, size of a Word) to the stack.
00001b7c 486f0008                           PEA            8(A7)                                       // push the data pointed by the address stored at register A7+8 bytes (if the address is 0000, then push the data at 0008) to the stack.
00001b80 2f0a                                   MOVE.L    A2,-(A7)                                  // copy the data pointed by Adress Register A2 to register A7.
00001b82 3f3c1ee0                           MOVE.W   #7904!$1ee0,-(A7)                  // move the decimal number 7904 (the "Program Expire" resource ID) to register A7.
00001b86 4e4fa194                           TRAP          #15
00001b8a                                           DC.W         sysTrapFrmCustomAlert           // execute the system command "Form Custom Alert". This will display "Program Expired" Alert.

What we see here is that #7904 is pushed into register A7, and then the FrmCustomAlert API is called. Now this sound much closer to what we are looking for. Write down this address (1b82).

Step 5: Analyzing the code

Now lets take a look on the line before the Alert pops. On address 1b72 we see that the value in register D3 is compared with "2". Next line is conditional branch: BNE = Branch if Not Equal (same as JNZ in Intel CPU). The jump is to L168 which stands for Label 168. The label number is marked at the beginning of each code block (you can thing of each label as a start of a new function). Pilotdis marks those labels for us.
After the BNE we see 2 lines with PEA instructions. PEA = Push Effective Address. Remember those text placeholders (^1 and ^2) from the Alert as we saw it in palmdeMON ? Here they are filled with data.
We can suspect the line at address 1b76 to be our place to patch the program (change the branch condition). Before we do that, let's go to L168 to see what's there:

00001baa 7001              L168         MOVEQ              #1,D0
00001bac 4fef016c                          LEA                     364(A7),A7
00001bb0 4cdf5c18                        MOVEM.L           (A7)+,D3/D4/A2-A4/A6
00001bb4 4e75                               RTS

instructions summery:
MOVEQ     = Move Quick (move direct data to register)
LEA            = Load Effective Address
MOVEM.L = Move Memory Long
RTS            = Return from Sub-Routine (return to the place where this sub-routine/function was called)

We can see that very little happens here, "1" is moved to register D0. Registers data are restored to initial values (as they were before calling this function), and the program returns to the calling function (with the value "1" in Data Register D0).
You can see that if we will make the program perform the jump at address 1b76 in any case, we will never see the "Program Expired" alert again. But, will this thing actually remove the program limitations, or only remove the alert and keep the limitations (program will not run after 10 new games) ?
In order to check that, we will patch the program at address 1b76, run it and check what happens.
Before patching, load the original prc to POSE and start a new game 10 times until the "Demo Expired" Alert pops.

Step 6: Patching

What we are going to do is make the program jump at address 1b76 meaning that we are going to change the conditional jump to an un-conditional jump.
The instruction at 1b76 is BNE, which stands for Branch if Not-Equal. We can change it to BEQ (Branch if Equal), but better way will be to change it to BRA (Branch - no conditions). The opcode for the branch command is as follows:
BRA = 60
BEQ = 67
BNE = 66
In our case, at address 1b76 we see the opcode "6632". This opcode stands for "BNE    L168", where "66" is the BNE instruction and "32" is the bytes offset from the END of the current instruction location (our instruction start at 1b76 and it's 2 bytes long, hence offset is from 1b78), meaning 1B78+32=1BAA. And indeed, if you look at address 1BAA you'll that Label 168 starts there.
In order to change the code from BNE to BRA, we simply need to change the opcode from "6632" to "6032" (the offset doesn't change).
Before patching, make a backup of the original file.
Open the .prc file with UltraEdit and press CTRL+G (Hex goto). At the address type "0x1b76". this will put you in the right place in the .prc file. Now change to byte "66" to "60" and save the file.

Step 7: test the patched file on the emulator

Load the patched file into POSE and make it expire (start a new game 10 times). What happens after 10 games ? The "Demo Expired" Alert is gone, but, something is still wrong: Pressing the Play button does nothing... So, we can guess that this was NOT the right place to patch...

Motivation Pauses: RCE involves a lot of trial and error. We find many things by simply trying lots of combinations. As soon as you'll get more experience, you'll see things more clearly, and the code will come up between the lines without having to try so often.

Back to business:
The patch at 1b76 didn't work, so we will restore the original file and continue from the place we were. Let's try to figure out where does the program return to after the RTS in address 1bb4, and by that try to figure out what happens when we press the Play button (since we already forced the program to bypass the "Demo Expired" Alert, and reach that place).
How do we know where the program returns to ? Look at the line just before L165 starts, at address 1b70 you'll see also RTS. It means that there is no way the program simply continues from 1b70 to 1b72. So, 1b72 (L165) must have been called from somewhere. Search for "L165" and you'll find it here (I added my own comments):

00001a88 4a43 L152                    TST.W                             D3                               // test data in Data Register D3
00001a8a 660000e6                      BNE                                L165                            // jump to Label 165 if D3 != 0
00001a8e 4a14                              TST.B                             (A4)                             // test data in Address Register A4
00001a90 6650                             BNE                                L156                            // jump to Label 156 if A4 does not contain zeros
00001a92 2017                             MOVE.L                         (A7),D0                       // move the value pointed by the Address Register A7 to Data Register D0
00001a94 223c00015180             MOVE.L                          #86400!$15180,D1    // Put 86400 (decimal) in Data Register D1
00001a9a 4ebaed3e                      JSR                                   L9                              // perform sub-routine at Label 9
00001a9e 2600                             MOVE.L                          D0,D3                        // move data in D0 to register D3
00001aa0 6706                             BEQ                                 L153                          // jump to label L153 if ZERO was moved from D0 to D3.
00001aa2 5380                             SUBQ.L                           #1,D0                         // subtract 1 from the value in D0 and store the result in D0.
00001aa4 6714                             BEQ                                 L154                          // jump to Label 154 if D0 = 0.
00001aa6 6024                             BRA                                 L155                          // Jump to Label 155.

instructions summery:
TST.W       = Test Word
BNE           = Branch if Not Equal
JSR             = Jump to Sub-Routine
SUBQ.L     = Quick Subtraction of Long
BRA           = Branch (jump anyway with no conditions).

Look on address 1a8a: there's a conditional branch directly to L165, which by now, we already know that cause the "Demo Expired" Alert to show. What will happen if we cancel that conditional branch, and let the program continue anyway without jumping ? Let's try this out:
In order to cancel an instruction we use a special command named NOP (no operation). The opcode of NOP in 68k assembler is "4E71" (note it takes 2 bytes - unlike x86 where NOP's opcode is "90").
As you can see, the opcode at 1a8a is 8 bytes long, hence we will put to NOPs one after another. It is very important not to leave any "half-instructions" by patching half instruction. This will cause a crash when the program runs, because the hex numbers left there doesn't represent any reasonable code.
Repeat what we did earlier and replace the opcode in 1a8a with "4E71 4E71" (do that on the restored file, not what we patched earlier).
Load the file to POSE and start a new game 10 times, This is what happens when you try to start a new game for the 11 or 12 time:

You can see that the program keep counting the games (into negative range), but it doesn't expire ! This is good news. All we have to do now, is make that "Welcome" Form (actually a Custom Alert) never show up and nag us.

Remember the resource IDs we wrote down earlier ? The Resource ID of the "Welcome" Alert is "7901". Search in the dead-list for "#7901" (or for "$1edd").
You'll find it in address 1b34:

00001b26 7601                L160              MOVEQ           #1,D3
00001b28 6032                                      BRA                  L163
00001b2a 486f0004         L161              PEA                   4(A7)
00001b2e 486f009e                               PEA                   158(A7)
00001b32 2f0a                                       MOVE.L           A2,-(A7)
00001b34 3f3c1edd                               MOVE.W          #7901!$1edd,-(A7)        // "Welcome to this limited demo..." Alert
00001b38 4e4fa194                               TRAP                 #15
00001b3c                                               DC.W                sysTrapFrmCustomAlert  //execute a system API: Form Custom Alert
00001b3c 5340                                      SUBQ.W           #1,D0
00001b3e 4fef000e                                 LEA                   14(A7),A7
00001b42 660c                                      BNE                   L162
00001b44 7000                                      MOVEQ            #0,D0
00001b46 4fef016c                                 LEA                   364(A7),A7
00001b4a 4cdf5c18                                MOVEM.L        (A7)+,D3/D4/A2-A4/A6 //restore registers data before returning to calling function
00001b4e 4e75                                       RTS                                                           // return to calling function
00001b50 486f00fe           L162             PEA                    254(A7)
00001b54 4ebafa4e                                JSR                     L126                                // perform function at Label 126
00001b58 3600                                      MOVE.W           D0,D3
00001b5a 584f                                       ADDQ.W           #4,A7
00001b5c 3803                L163              MOVE.W           D3,D4
00001b5e 6706                                      BEQ                    L164                                 // jump if D4=0 - don't patch here
00001b60 0c440002                              CMPI.W             #2,D4
00001b64 66c4                                      BNE                    L161                               /// if branch - show the Welcome form (enter serial/try the demo)
00001b66 3003                L164              MOVE.W           D3,D0
00001b68 4fef016c                                 LEA                   364(A7),A7
00001b6c 4cdf5c18                                MOVEM.L         (A7)+,D3/D4/A2-A4/A6   //restore registers data before returning to calling function
00001b70 4e75                                      RTS                                                              // return to calling function

Look at Label 161: on the line before it, we see BRA (on address 1b28). That means that whenever the program gets to address 1b28, it jumps directly to L163 without showing our "Welcome" Alert. Since we know that the Alert appears (and let you even enter a new serial number, since we already patched the expiration), we must conclude that L161 is called from somewhere. A search for "L161" leads us to address 1b64. On 1b64 there is a conditional branch, and the most natural thing to do is patch it. I'll spare the time here, but this does not give us the solution (you can try patch it by yourself and see what happens).

Before we continue to guess, let's try to understand what's going on here: on address 1b5c, the data inside D3 is copied to D4. If that data was zero, the program jumps to L164 and return to the calling function on address 1b70. If D4 doesn't contain zero, then it is compared with "2", and if not equal it branches to L161 and we get the "Welcome" Alert.
Let's try to see where D3 gets its value: on address 1b54 we see that some function is executed and right after it, the value at D0 (which is probably something returned from the function) is copied to D3. On the other hand, we see, on address 1b26 (just before a branch to L163), that the value "1" is copied to D3. We know now that if D3=0 we will jump directly to L164 without displaying the "Welcome" Alert, and id D3=!0 - we will get that alert. This is why the "flag" on 1b26 should suspect this. We don't know what the function on L126 (called at 1b54) is doing, but we sure know that value of "1" inside D3 is not good.
In order to fix the "flag" at 1b26, and make sure that D3=0, we will copy "0" instead of "1" to D3, and see what happens.

The opcode for the flag at 1b26 is "7601". 76 represents MOVE.Q (quick move data to a register), while 01 represent the value to be moved to the register.
If we change the opcode to "7600" it'll put zero instead of "1" inside D3, so instead of
                     MOVEQ           #1,D3
we will get:    MOVEQ           #0,D3
Patch the file with the hex editor the same way we did it before and load to the emulator.
Now, when you start a new game, the nagging "Welcome" Alert doesn't appear, and the game simply starts. Also we are not limited by any number of games.

As you can see, by changing 9 bytes on the .prc files we made a pre-registered version of the game. In many other programs, only one byte is needed to be changed in order to bypass the protection. As there is always more than way to crack a file, we could also try and look for the function that calculates the serial number, and change it in a way that instead of accepting only valid serial numbers, it will accept only non-valid serial numbers (the odds the we randomly type an invalid serial numbers are near 100% if you think about it...)

Patch summery:

1A8A           660000e6                  BNE                L165 ------> NOP                  (opcode = 4E71 4E71)
0001b26      7601              L160    MOVEQ         #1,D3 -----> MOVEQ #0,D3 (opcode = 7600)

And that's really all I have to say about it...

AY ;)

p.s. for any comments/corrections - mail me.